1function [Q,U,R,T,C,X,lG,runtime,totiter] = solver_mva_qsys_analyzer(sn, options)
2% [Q,U,R,T,C,X,LG,RUNTIME,ITER] = SOLVER_MVA_QSYS_ANALYZER(QN, OPTIONS)
4% Copyright (c) 2012-2026, Imperial College London
10Q = zeros(M,K); U = zeros(M,K);
11R = zeros(M,K); T = zeros(M,K);
12C = zeros(1,K); X = zeros(M,K);
15method = options.method;
16source_ist = sn.nodeToStation(sn.nodetype == NodeType.Source);
17queue_ist = sn.nodeToStation(sn.nodetype == NodeType.Queue);
18lambda = sn.rates(source_ist)*sn.visits{source_ist}(sn.stationToStateful(queue_ist));
19k = sn.nservers(queue_ist);
20mu = sn.rates(queue_ist);
21ca = sqrt(sn.scv(source_ist));
22cs = sqrt(sn.scv(queue_ist));
24line_debug(
'MVA qsys analyzer starting: method=%s, lambda=%g, mu=%g, k=%d', method, lambda, mu, k);
26% Check
for BMAP arrivals (batch Markovian)
27arvproc = sn.proc{source_ist};
28if iscell(arvproc) && length(arvproc) >= 1
32if isa(arvproc,
'BMAP')
33 line_debug('BMAP arrival process detected');
34 srvproc = sn.proc{queue_ist};
35 if iscell(srvproc) && length(srvproc) >= 1
39 % Check
if service
is exponential
40 if isa(srvproc,
'Exp')
41 line_debug('Service
is exponential, using MX/M/1 queue model');
42 lambda_batch = arvproc.getRate();
43 E_X = arvproc.getMeanBatchSize();
45 % Compute second moment of batch size
46 % For BMAP, we need to extract this from the D matrices
47 batch_rates = arvproc.getBatchRates();
48 total_rate = sum(batch_rates);
51 for bsize = 1:length(batch_rates)
52 E_X2 = E_X2 + (bsize^2) * batch_rates(bsize) / total_rate;
55 E_X2 = E_X^2; % Fallback to deterministic batch size
58 [W, Wq, U_mxm1, Q_mxm1] = qsys_mxm1(lambda_batch, mu, E_X, E_X2);
59 R(queue_ist,1) = W * sn.
visits{1}(sn.stationToStateful(queue_ist));
60 C(1,1) = R(queue_ist,1);
61 X(queue_ist,1) = lambda_batch * E_X; % Job arrival rate
62 U(queue_ist,1) = U_mxm1;
63 T(source_ist,1) = lambda_batch * E_X;
64 T(queue_ist,1) = lambda_batch * E_X;
65 Q(queue_ist,1) = Q_mxm1;
73if strcmpi(method,
'exact')
74 if ca == 1 && cs == 1 && k==1
76 line_debug('Exact method selected: M/M/1 (ca=1, cs=1, k=1)');
77 elseif ca == 1 && cs == 1 && k>1
79 line_debug('Exact method selected: M/M/k (ca=1, cs=1, k=%d)', k);
80 elseif ca == 1 && k==1
82 line_debug('Exact method selected: M/G/1 (ca=1, k=1)');
83 elseif cs == 1 && k==1
85 line_debug('Exact method selected: G/M/1 (cs=1, k=1)');
87 line_error(mfilename,'MVA exact method unavailable for this model.');
93 if ca == 1 && cs == 1 && k == 1
95 line_debug('Default method: using M/M/1 exact solution\n');
96 elseif ca == 1 && cs == 1 && k > 1
98 line_debug('Default method: using M/M/k exact solution (k=%d)\n', k);
99 elseif ca == 1 && k == 1
101 line_debug('Default method: using M/G/1 exact solution\n');
102 elseif cs == 1 && k == 1
104 line_debug('Default method: using G/M/1 exact solution\n');
107 line_debug('Default method: using G/G/k approximation (k=%d)\n', k);
110 line_debug('Default method: using G/G/1 KLB approximation\n');
116 line_debug('Using M/M/1 exact solution');
117 R = qsys_mm1(lambda,mu);
119 line_debug('Using M/M/k exact solution (k=%d)', k);
120 R = qsys_mmk(lambda,mu,k);
121 case {
'mg1',
'mgi1'} % verified
122 line_debug(
'Using M/G/1 exact solution');
123 R = qsys_mg1(lambda,mu,cs);
125 line_debug(
'Using G/G/k approximation (k=%d)', k);
126 R = qsys_gigk_approx(lambda,mu,ca,cs,k);
127 case {
'gigk.kingman_approx'}
128 line_debug(
'Using G/G/k Kingman approximation (k=%d)', k);
129 R = qsys_gigk_approx_kingman(lambda,mu,ca,cs,k);
130 case {
'gig1',
'gig1.kingman'} % verified
131 line_debug(
'Using G/G/1 Kingman upper bound');
132 R = qsys_gig1_ubnd_kingman(lambda,mu,ca,cs);
134 line_debug(
'Using G/G/1 Heyman approximation');
135 R = qsys_gig1_approx_heyman(lambda,mu,ca,cs);
137 line_debug(
'Using G/G/1 Allen-Cunneen approximation');
138 R = qsys_gig1_approx_allencunneen(lambda,mu,ca,cs);
139 case 'gig1.kobayashi'
140 line_debug(
'Using G/G/1 Kobayashi approximation');
141 R = qsys_gig1_approx_kobayashi(lambda,mu,ca,cs);
143 line_debug(
'Using G/G/1 KLB approximation');
144 R = qsys_gig1_approx_klb(lambda,mu,ca,cs);
145 if strcmpi(options.method,
'default')
146 method = sprintf('default [%s]','gig1.klb');
148 case 'gig1.marchal' % verified
149 line_debug('Using G/G/1 Marchal approximation');
150 R = qsys_gig1_approx_marchal(lambda,mu,ca,cs);
152 line_debug(
'Using G/M/1 exact solution');
153 % sigma = Load at arrival instants (Laplace transform of the inter-arrival times)
154 LA = @(s) sn.lst{source_ist}{1}(s);
155 mu = sn.rates(queue_ist);
156 sigma = fzero(@(x) LA(mu-mu*x)-x,0.5);
157 R = qsys_gm1(sigma,mu);
159 line_error(mfilename,
'Unsupported method for a model with 1 station and 1 class.');
162Rscalar = R; % save scalar from qsys function
164R(queue_ist,1) = Rscalar * sn.visits{1}(sn.stationToStateful(queue_ist));
165C(1,1) = R(queue_ist,1);
166X(queue_ist,1) = lambda;
167U(queue_ist,1) = lambda/mu/k;
168T(source_ist,1) = lambda;
169T(queue_ist,1) = lambda;
170Q(queue_ist,1) = X(queue_ist,1) * R(queue_ist,1);