1function [QN,UN,RN,TN,xvec_it,QNt,UNt,TNt,xvec_t,t,iters,runtime] = solver_mfq_prio(sn, options)
2%SOLVER_MFQ_PRIO Single-queue open priority model via
the fluid priority queue.
4% [QN,UN,RN,TN,xvec_it,QNt,UNt,TNt,xvec_t,t,iters,runtime] = SOLVER_MFQ_PRIO(sn, options)
6% Analyzes a single-queue open system with
class priorities using
the fluid
7% priority queue mfq_prio_queue (G. Horvath,
"Efficient analysis of the
8% MMAP[K]/PH[K]/1 priority queue", EJOR 246(1):128-139, 2015). The per-class
9% Markovian arrival processes are superposed into a joint background CTMC that
10% modulates
the per-class fluid input rates;
the single server drains fluid at
11%
the constant rate d = mu, serving higher-priority fluid first (preemptive).
13% The fluid level
is interpreted as
the (per-
class) queue length and
the fluid
14% sojourn time as
the response time, consistent with solver_mfq. The method
15% applies when arrivals are Markov-modulated (MAP) and
the service rate
is
16% class-independent; simple-exponential arrivals or class-dependent service
17% degenerate
the fluid model and fall back to
the matrix fluid method.
19% See also: solver_mfq, mfq_prio_queue, fluid_is_single_queue
26QN = zeros(M,K); UN = zeros(M,K); RN = zeros(M,K); TN = zeros(M,K);
28t = [0; options.timespan(2)];
29QNt = cell(M,K); UNt = cell(M,K); TNt = cell(M,K);
32 QNt{ist,k} = zeros(2,1); UNt{ist,k} = zeros(2,1); TNt{ist,k} = zeros(2,1);
36xvec_it = {zeros(size(sn.state{1}, 2), 1)};
38 function [QN,UN,RN,TN,xvec_it,QNt,UNt,TNt,xvec_t,t] = fallback_matrix(reason)
39 line_warning(mfilename,
'MFQ-prio not applicable (%s); falling back to matrix method.', reason);
40 opts = options; opts.method =
'matrix';
41 [QN,UN,RN,TN,xvec_it,QNt,UNt,TNt,xvec_t,t] = solver_fluid_matrix(sn, opts);
45[isSingleQueue, fluidInfo] = fluid_is_single_queue(sn);
47 line_error(mfilename,
'MFQ-prio requires single-queue topology: %s', fluidInfo.errorMsg);
49sourceIdx = fluidInfo.sourceStation;
50queueIdx = fluidInfo.queueStation;
52% ---- classes ordered
for mfq_prio_queue (LINE classprio: lower value = higher
53% priority; FluidPrioQueue uses a higher row index
for higher priority, so
the
54% highest-priority
class must occupy
the last row) ----
55openClasses = find(isinf(sn.njobs));
56[~, ord] = sort(sn.classprio(openClasses),
'descend');
57ordC = openClasses(ord);
60% ---- constant service rate (
class-independent required) ----
61mu = sn.rates(queueIdx, ordC);
62if Kc == 0 || any(~isfinite(mu)) || any(mu <= 0)
63 [QN,UN,RN,TN,xvec_it,QNt,UNt,TNt,xvec_t,t] = fallback_matrix(
'invalid service rates');
return;
65if any(abs(mu - mu(1)) > GlobalConstants.FineTol * max(1, mu(1)))
66 [QN,UN,RN,TN,xvec_it,QNt,UNt,TNt,xvec_t,t] = fallback_matrix(
'class-dependent service');
return;
70% ---- per-
class arrival fluid representations (Q_k = D0+D1, R_k = D1*1) ----
71Qk = cell(1,Kc); Rk = cell(1,Kc); Nk = zeros(1,Kc); lambda = zeros(1,Kc);
74 proc = sn.proc{sourceIdx}{k};
75 if ~iscell(proc) || numel(proc) < 2
76 [QN,UN,RN,TN,xvec_it,QNt,UNt,TNt,xvec_t,t] = fallback_matrix(
'non-MAP arrival');
return;
78 D0 = proc{1}; D1 = proc{2};
82 lambda(i) = sn.rates(sourceIdx, k);
86 [QN,UN,RN,TN,xvec_it,QNt,UNt,TNt,xvec_t,t] = fallback_matrix(
'non-modulated (exponential) arrivals');
return;
89% ---- joint background CTMC (Kronecker sum) and per-
class fluid rates ----
91Qjoint = zeros(Njoint);
95 if j == i, term = kron(term, Qk{i});
else, term = kron(term, eye(Nk(j))); end
97 Qjoint = Qjoint + term;
99Rjoint = zeros(Kc, Njoint);
103 if j == i, v = kron(v, Rk{i});
else, v = kron(v, ones(Nk(j),1)); end
109if isfield(options, 'tol
') && ~isempty(options.tol), prec = options.tol; end
111% ---- fluid priority queue (last row = highest priority) ----
112fl = cell(1,Kc); st = cell(1,Kc);
114 [fl{:}] = mfq_prio_queue(Qjoint, Rjoint, d, 'classes
', 1:Kc, 'flMoms
', 1, 'prec
', prec);
115 [st{:}] = mfq_prio_queue(Qjoint, Rjoint, d, 'classes
', 1:Kc, 'stMoms
', 1, 'prec
', prec);
117 [QN,UN,RN,TN,xvec_it,QNt,UNt,TNt,xvec_t,t] = fallback_matrix(ME.message); return;
120% ---- map fluid measures to LINE metrics ----
123 QN(queueIdx, k) = fl{i}(1); % mean queue length = mean fluid level
124 RN(queueIdx, k) = st{i}(1); % mean response time = mean fluid sojourn
125 TN(queueIdx, k) = lambda(i); % throughput = arrival rate
126 TN(sourceIdx, k) = lambda(i);
127 UN(queueIdx, k) = min(1, lambda(i) / d);
128 QNt{queueIdx, k} = [0; QN(queueIdx, k)];
129 UNt{queueIdx, k} = [0; UN(queueIdx, k)];
130 TNt{queueIdx, k} = [0; TN(queueIdx, k)];
131 QNt{sourceIdx, k} = [0; 0];
132 UNt{sourceIdx, k} = [0; 0];
133 TNt{sourceIdx, k} = [0; TN(sourceIdx, k)];
136runtime = toc(runtime);