LINE Solver
MATLAB API documentation
Loading...
Searching...
No Matches
solver_mfq_prio.m
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.
3%
4% [QN,UN,RN,TN,xvec_it,QNt,UNt,TNt,xvec_t,t,iters,runtime] = SOLVER_MFQ_PRIO(sn, options)
5%
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).
12%
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.
18%
19% See also: solver_mfq, mfq_prio_queue, fluid_is_single_queue
20
21runtime = tic;
22iters = 1;
23
24M = sn.nstations;
25K = sn.nclasses;
26QN = zeros(M,K); UN = zeros(M,K); RN = zeros(M,K); TN = zeros(M,K);
27
28t = [0; options.timespan(2)];
29QNt = cell(M,K); UNt = cell(M,K); TNt = cell(M,K);
30for ist = 1:M
31 for k = 1:K
32 QNt{ist,k} = zeros(2,1); UNt{ist,k} = zeros(2,1); TNt{ist,k} = zeros(2,1);
33 end
34end
35xvec_t = [];
36xvec_it = {zeros(size(sn.state{1}, 2), 1)};
37
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);
42 end
43
44% ---- topology ----
45[isSingleQueue, fluidInfo] = fluid_is_single_queue(sn);
46if ~isSingleQueue
47 line_error(mfilename, 'MFQ-prio requires single-queue topology: %s', fluidInfo.errorMsg);
48end
49sourceIdx = fluidInfo.sourceStation;
50queueIdx = fluidInfo.queueStation;
51
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);
58Kc = numel(ordC);
59
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;
64end
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;
67end
68d = mu(1);
69
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);
72for i = 1:Kc
73 k = ordC(i);
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;
77 end
78 D0 = proc{1}; D1 = proc{2};
79 Qk{i} = D0 + D1;
80 Rk{i} = sum(D1, 2);
81 Nk(i) = size(D0, 1);
82 lambda(i) = sn.rates(sourceIdx, k);
83end
84
85if prod(Nk) < 2
86 [QN,UN,RN,TN,xvec_it,QNt,UNt,TNt,xvec_t,t] = fallback_matrix('non-modulated (exponential) arrivals'); return;
87end
88
89% ---- joint background CTMC (Kronecker sum) and per-class fluid rates ----
90Njoint = prod(Nk);
91Qjoint = zeros(Njoint);
92for i = 1:Kc
93 term = 1;
94 for j = 1:Kc
95 if j == i, term = kron(term, Qk{i}); else, term = kron(term, eye(Nk(j))); end
96 end
97 Qjoint = Qjoint + term;
98end
99Rjoint = zeros(Kc, Njoint);
100for i = 1:Kc
101 v = 1;
102 for j = 1:Kc
103 if j == i, v = kron(v, Rk{i}); else, v = kron(v, ones(Nk(j),1)); end
104 end
105 Rjoint(i,:) = v(:)';
106end
107
108prec = 1e-14;
109if isfield(options, 'tol') && ~isempty(options.tol), prec = options.tol; end
110
111% ---- fluid priority queue (last row = highest priority) ----
112fl = cell(1,Kc); st = cell(1,Kc);
113try
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);
116catch ME
117 [QN,UN,RN,TN,xvec_it,QNt,UNt,TNt,xvec_t,t] = fallback_matrix(ME.message); return;
118end
119
120% ---- map fluid measures to LINE metrics ----
121for i = 1:Kc
122 k = ordC(i);
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)];
134end
135
136runtime = toc(runtime);
137end
Definition Station.m:245