LINE Solver
MATLAB API documentation
Loading...
Searching...
No Matches
solver_mva_qsys_analyzer.m
1function [Q,U,R,T,C,X,lG,runtime,totiter,actualmethod] = solver_mva_qsys_analyzer(sn, options)
2% [Q,U,R,T,C,X,LG,RUNTIME,ITER,ACTUALMETHOD] = SOLVER_MVA_QSYS_ANALYZER(QN, OPTIONS)
3
4% Copyright (c) 2012-2026, Imperial College London
5% All rights reserved.
6
7T0=tic;
8M = sn.nstations;
9K = sn.nclasses;
10Q = zeros(M,K); U = zeros(M,K);
11R = zeros(M,K); T = zeros(M,K);
12C = zeros(1,K); X = zeros(M,K);
13totiter = 1;
14
15method = options.method;
16actualmethod = method; % resolved algorithm name, reported in the solver banner
17source_ist = sn.nodeToStation(sn.nodetype == NodeType.Source);
18queue_ist = sn.nodeToStation(sn.nodetype == NodeType.Queue);
19lambda = sn.rates(source_ist)*sn.visits{source_ist}(sn.stationToStateful(queue_ist));
20k = sn.nservers(queue_ist);
21mu = sn.rates(queue_ist);
22ca = sqrt(sn.scv(source_ist));
23cs = sqrt(sn.scv(queue_ist));
24
25line_debug('MVA qsys analyzer starting: method=%s, lambda=%g, mu=%g, k=%d', method, lambda, mu, k);
26
27% Check for BMAP arrivals (batch Markovian)
28if sn.procid(source_ist) == ProcessType.BMAP
29 line_debug('BMAP arrival process detected');
30
31 % Check if service is exponential
32 if sn.procid(queue_ist) == ProcessType.EXP
33 line_debug('Service is exponential, using MX/M/1 queue model');
34 % sn.proc holds {D0, D1, D_batch1, ..., D_batchK} (JAR MatrixCell
35 % layout); batch rates are weighted by the stationary vector of
36 % the underlying CTMC, as in BMAP.getBatchRates
37 proc_src = sn.proc{source_ist};
38 if iscell(proc_src) && ~isempty(proc_src) && iscell(proc_src{1})
39 proc_src = proc_src{1};
40 end
41 pie_b = ctmc_solve(proc_src{1} + proc_src{2});
42 nbatch = length(proc_src) - 2;
43 batch_rates = zeros(1, nbatch);
44 for bsize = 1:nbatch
45 batch_rates(bsize) = pie_b * proc_src{2+bsize} * ones(size(proc_src{1},1),1);
46 end
47 total_rate = sum(batch_rates);
48 lambda_batch = total_rate; % batch event rate
49 if total_rate > 0
50 E_X = sum((1:nbatch) .* batch_rates) / total_rate;
51 E_X2 = sum(((1:nbatch).^2) .* batch_rates) / total_rate;
52 else
53 E_X = 1;
54 E_X2 = 1;
55 end
56
57 [W, Wq, U_mxm1, Q_mxm1] = qsys_mxm1(lambda_batch, mu, E_X, E_X2);
58 R(queue_ist,1) = W * sn.visits{1}(sn.stationToStateful(queue_ist));
59 C(1,1) = R(queue_ist,1);
60 X(queue_ist,1) = lambda_batch * E_X; % Job arrival rate
61 U(queue_ist,1) = U_mxm1;
62 T(source_ist,1) = lambda_batch * E_X;
63 T(queue_ist,1) = lambda_batch * E_X;
64 Q(queue_ist,1) = Q_mxm1;
65 lG = 0;
66 runtime=toc(T0);
67 actualmethod = 'mxm1';
68 return;
69 end
70end
71
72if strcmpi(method,'exact')
73 if ca == 1 && cs == 1 && k==1
74 method = 'mm1';
75 line_debug('Exact method selected: M/M/1 (ca=1, cs=1, k=1)');
76 elseif ca == 1 && cs == 1 && k>1
77 method = 'mmk';
78 line_debug('Exact method selected: M/M/k (ca=1, cs=1, k=%d)', k);
79 elseif ca == 1 && k==1
80 method = 'mg1';
81 line_debug('Exact method selected: M/G/1 (ca=1, k=1)');
82 elseif cs == 1 && k==1
83 method = 'gm1';
84 line_debug('Exact method selected: G/M/1 (cs=1, k=1)');
85 else
86 line_error(mfilename,'MVA exact method unavailable for this model.');
87 end
88end
89
90switch method
91 case 'default'
92 if ca == 1 && cs == 1 && k == 1
93 method = 'mm1';
94 line_debug('Default method: using M/M/1 exact solution\n');
95 elseif ca == 1 && cs == 1 && k > 1
96 method = 'mmk';
97 line_debug('Default method: using M/M/k exact solution (k=%d)\n', k);
98 elseif ca == 1 && k == 1
99 method = 'mg1';
100 line_debug('Default method: using M/G/1 exact solution\n');
101 elseif cs == 1 && k == 1
102 method = 'gm1';
103 line_debug('Default method: using G/M/1 exact solution\n');
104 elseif k > 1
105 method = 'gigk';
106 line_debug('Default method: using G/G/k approximation (k=%d)\n', k);
107 else
108 method = 'gig1.klb';
109 line_debug('Default method: using G/G/1 KLB approximation\n');
110 end
111end
112
113switch method
114 case 'mm1'
115 line_debug('Using M/M/1 exact solution');
116 R = qsys_mm1(lambda,mu);
117 case 'mmk'
118 line_debug('Using M/M/k exact solution (k=%d)', k);
119 R = qsys_mmk(lambda,mu,k);
120 case {'rqna'}
121 line_debug('Using RQNA (robust queueing) single-queue solution');
122 arvMAP = sn.proc{source_ist}{1};
123 rho1 = lambda/mu;
124 IaFun1 = @(x) map_count_idc(arvMAP, x);
125 [~, W1] = qsys_gig1_rq(rho1, mu, cs^2, IaFun1);
126 R = W1 + 1/mu;
127 case {'mg1', 'mgi1'} % verified
128 line_debug('Using M/G/1 exact solution');
129 R = qsys_mg1(lambda,mu,cs);
130 case {'gigk'}
131 line_debug('Using G/G/k approximation (k=%d)', k);
132 R = qsys_gigk_approx(lambda,mu,ca,cs,k);
133 case {'gigk.kingman_approx'}
134 line_debug('Using G/G/k Kingman approximation (k=%d)', k);
135 R = qsys_gigk_approx_kingman(lambda,mu,ca,cs,k);
136 case 'gig1.kingman' % verified
137 line_debug('Using G/G/1 Kingman upper bound');
138 R = qsys_gig1_ubnd_kingman(lambda,mu,ca,cs);
139 case 'gig1.heyman'
140 line_debug('Using G/G/1 Heyman approximation');
141 R = qsys_gig1_approx_heyman(lambda,mu,ca,cs);
142 case {'gig1', 'gig1.allen'}
143 line_debug('Using G/G/1 Allen-Cunneen approximation');
144 R = qsys_gig1_approx_allencunneen(lambda,mu,ca,cs);
145 case 'gig1.kobayashi'
146 line_debug('Using G/G/1 Kobayashi approximation');
147 R = qsys_gig1_approx_kobayashi(lambda,mu,ca,cs);
148 case 'gig1.klb'
149 line_debug('Using G/G/1 KLB approximation');
150 R = qsys_gig1_approx_klb(lambda,mu,ca,cs);
151 case 'gig1.marchal' % verified
152 line_debug('Using G/G/1 Marchal approximation');
153 R = qsys_gig1_approx_marchal(lambda,mu,ca,cs);
154 case {'gm1', 'gim1'}
155 line_debug('Using G/M/1 exact solution');
156 mu = sn.rates(queue_ist);
157 % Prefer the exact PH/M/1 sigma-root via the source's PH representation.
158 % Falls back to the LST-based fzero if that's available, else to the
159 % two-moment sigma-root fit of qsys_gg1.
160 R = [];
161 % A deterministic arrival has no exact finite PH -- sn.proc stores an
162 % Erlang-n moment approximation, so the PH path returns an approximate
163 % sigma (D/M/1 0.64477 instead of the exact 0.6275). Skip PH for Det and
164 % use the exact LST sigma-root below (Erlang/HyperExp/Coxian/APH keep PH,
165 % which is exact for them).
166 if sn.procid(source_ist) ~= ProcessType.DET
167 try
168 phPair = sn.proc{source_ist}{1};
169 if iscell(phPair) && numel(phPair) >= 2
170 D0_ph = phPair{1};
171 D1_ph = phPair{2};
172 if size(D0_ph, 1) == size(D0_ph, 2) && all(size(D0_ph) == size(D1_ph))
173 pie_src = map_pie({D0_ph, D1_ph});
174 res_phm1 = qsys_phm1(pie_src, D0_ph, mu);
175 R = res_phm1.meanSojournTime;
176 end
177 end
178 catch
179 R = [];
180 end
181 end
182 if isempty(R)
183 try
184 LA = @(s) sn.lst{source_ist}{1}(s);
185 sigma = fzero(@(x) LA(mu-mu*x)-x, 0.5);
186 R = qsys_gm1(sigma, mu);
187 catch
188 R = qsys_gg1(lambda, mu, ca^2, 1);
189 end
190 end
191 otherwise
192 line_error(mfilename,'Unsupported method for a model with 1 station and 1 class.');
193end
194actualmethod = method;
195
196Rscalar = R; % save scalar from qsys function
197% lambda is the effective (per-visit) arrival rate to the queue = external
198% source rate times the queue visit ratio; Rscalar is the per-visit sojourn
199% time. For feedback models the queue visit ratio Vq > 1, so the per-visit
200% and per-job (residence) quantities must be kept distinct: RespT/QLen are
201% per-visit, while ResidT (=RespT*Vq downstream) and the system response C
202% are per-job. Source throughput is the external arrival rate, not lambda.
203Vq = sn.visits{1}(sn.stationToStateful(queue_ist));
204srcRate = sn.rates(source_ist);
205R = zeros(M,K);
206R(queue_ist,1) = Rscalar; % per-visit response time
207C(1,1) = Rscalar * Vq; % system (per-job) response time
208X(queue_ist,1) = srcRate; % system throughput = external arrival rate
209U(queue_ist,1) = lambda/mu/k;
210T(source_ist,1) = srcRate; % source throughput = external arrival rate
211T(queue_ist,1) = lambda; % queue throughput = effective arrival rate
212Q(queue_ist,1) = lambda * Rscalar; % Little's law at the queue (per-visit)
213lG = 0;
214runtime=toc(T0);
215end
Definition Station.m:245