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
13method = options.method;
14source_ist = sn.nodeToStation(sn.nodetype == NodeType.Source);
15queue_ist = sn.nodeToStation(sn.nodetype == NodeType.Queue);
16lambda = sn.rates(source_ist)*sn.visits{source_ist}(sn.stationToStateful(queue_ist));
17k = sn.nservers(queue_ist);
18mu = sn.rates(queue_ist);
19ca = sqrt(sn.scv(source_ist));
20cs = sqrt(sn.scv(queue_ist));
22line_debug(
'MVA qsys analyzer starting: method=%s, lambda=%g, mu=%g, k=%d', method, lambda, mu, k);
24% Check
for BMAP arrivals (batch Markovian)
25arvproc = sn.proc{source_ist};
26if iscell(arvproc) && length(arvproc) >= 1
30if isa(arvproc,
'BMAP')
31 line_debug('BMAP arrival process detected');
32 srvproc = sn.proc{queue_ist};
33 if iscell(srvproc) && length(srvproc) >= 1
37 % Check
if service
is exponential
38 if isa(srvproc,
'Exp')
39 line_debug('Service
is exponential, using MX/M/1 queue model');
40 lambda_batch = arvproc.getRate();
41 E_X = arvproc.getMeanBatchSize();
43 % Compute second moment of batch size
44 % For BMAP, we need to extract this from the D matrices
45 batch_rates = arvproc.getBatchRates();
46 total_rate = sum(batch_rates);
49 for bsize = 1:length(batch_rates)
50 E_X2 = E_X2 + (bsize^2) * batch_rates(bsize) / total_rate;
53 E_X2 = E_X^2; % Fallback to deterministic batch size
56 [W, Wq, U_mxm1, Q_mxm1] = qsys_mxm1(lambda_batch, mu, E_X, E_X2);
57 R(queue_ist,1) = W * sn.
visits{1}(sn.stationToStateful(queue_ist));
58 C(queue_ist,1) = R(1,1);
59 X(queue_ist,1) = lambda_batch * E_X; % Job arrival rate
60 U(queue_ist,1) = U_mxm1;
61 T(source_ist,1) = lambda_batch * E_X;
62 T(queue_ist,1) = lambda_batch * E_X;
63 Q(queue_ist,1) = Q_mxm1;
71if strcmpi(method,
'exact')
72 if ca == 1 && cs == 1 && k==1
74 line_debug('Exact method selected: M/M/1 (ca=1, cs=1, k=1)');
75 elseif ca == 1 && cs == 1 && k>1
77 line_debug('Exact method selected: M/M/k (ca=1, cs=1, k=%d)', k);
78 elseif ca == 1 && k==1
80 line_debug('Exact method selected: M/G/1 (ca=1, k=1)');
81 elseif cs == 1 && k==1
83 line_debug('Exact method selected: G/M/1 (cs=1, k=1)');
85 line_error(mfilename,'MVA exact method unavailable for this model.');
91 if ca == 1 && cs == 1 && k == 1
93 line_debug('Default method: using M/M/1 exact solution\n');
94 elseif ca == 1 && cs == 1 && k > 1
96 line_debug('Default method: using M/M/k exact solution (k=%d)\n', k);
97 elseif ca == 1 && k == 1
99 line_debug('Default method: using M/G/1 exact solution\n');
100 elseif cs == 1 && k == 1
102 line_debug('Default method: using G/M/1 exact solution\n');
105 line_debug('Default method: using G/G/k approximation (k=%d)\n', k);
108 line_debug('Default method: using G/G/1 KLB approximation\n');
114 line_debug('Using M/M/1 exact solution');
115 R = qsys_mm1(lambda,mu);
117 line_debug('Using M/M/k exact solution (k=%d)', k);
118 R = qsys_mmk(lambda,mu,k);
119 case {
'mg1',
'mgi1'} % verified
120 line_debug(
'Using M/G/1 exact solution');
121 R = qsys_mg1(lambda,mu,cs);
123 line_debug(
'Using G/G/k approximation (k=%d)', k);
124 R = qsys_gigk_approx(lambda,mu,ca,cs,k);
125 case {
'gigk.kingman_approx'}
126 line_debug(
'Using G/G/k Kingman approximation (k=%d)', k);
127 R = qsys_gigk_approx_kingman(lambda,mu,ca,cs,k);
128 case {
'gig1',
'gig1.kingman'} % verified
129 line_debug(
'Using G/G/1 Kingman upper bound');
130 R = qsys_gig1_ubnd_kingman(lambda,mu,ca,cs);
132 line_debug(
'Using G/G/1 Heyman approximation');
133 R = qsys_gig1_approx_heyman(lambda,mu,ca,cs);
135 line_debug(
'Using G/G/1 Allen-Cunneen approximation');
136 R = qsys_gig1_approx_allencunneen(lambda,mu,ca,cs);
137 case 'gig1.kobayashi'
138 line_debug(
'Using G/G/1 Kobayashi approximation');
139 R = qsys_gig1_approx_kobayashi(lambda,mu,ca,cs);
141 line_debug(
'Using G/G/1 KLB approximation');
142 R = qsys_gig1_approx_klb(lambda,mu,ca,cs);
143 if strcmpi(options.method,
'default')
144 method = sprintf('default [%s]','gig1.klb');
146 case 'gig1.marchal' % verified
147 line_debug('Using G/G/1 Marchal approximation');
148 R = qsys_gig1_approx_marchal(lambda,mu,ca,cs);
150 line_debug(
'Using G/M/1 exact solution');
151 % sigma = Load at arrival instants (Laplace transform of the inter-arrival times)
152 LA = @(s) sn.lst{source_ist}{1}(s);
153 mu = sn.rates(queue_ist);
154 sigma = fzero(@(x) LA(mu-mu*x)-x,0.5);
155 R = qsys_gm1(sigma,mu);
157 line_error(mfilename,
'Unsupported method for a model with 1 station and 1 class.');
160R(queue_ist,1) = R *sn.visits{1}(sn.stationToStateful(queue_ist));
161C(queue_ist,1) = R(1,1);
162X(queue_ist,1) = lambda;
163U(queue_ist,1) = lambda/mu/k;
164T(source_ist,1) = lambda;
165T(queue_ist,1) = lambda;
166Q(queue_ist,1) = X(queue_ist,1) * R(queue_ist,1);