LINE Solver
MATLAB API documentation
Loading...
Searching...
No Matches
solver_mva_analyzer.m
1function [Q,U,R,T,C,X,lG,runtime,iter,method] = solver_mva_analyzer(sn, options)
2% [Q,U,R,T,C,X,LG,RUNTIME] = SOLVER_MVA_ANALYZER(QN, OPTIONS)
3
4% Copyright (c) 2012-2026, Imperial College London
5% All rights reserved.
6
7iter = NaN;
8% Convergence flag as reported by the handler; [] means it reports none,
9% in which case the count-vs-budget test below applies instead.
10converged = [];
11Tstart = tic;
12method = options.method;
13method = regexprep(method, '^amva\.', '');
14
15line_debug(options, 'MVA analyzer starting: method=%s, nclasses=%d, njobs=%s, nchains=%d', method, sn.nclasses, mat2str(sn.njobs), sn.nchains);
16
17switch method
18 case {'exact','mva'}
19 line_debug(options, 'Using exact MVA method, calling solver_mva');
20 [Q,U,R,T,C,X,lG] = solver_mva(sn, options);
21 case {'mvac'}
22 line_debug(options, 'Using exact MVAC method, calling solver_mvac');
23 [Q,U,R,T,C,X,lG] = solver_mvac(sn, options);
24 case {'sqd'}
25 line_debug(options, 'Using BAS method, calling solver_sqd');
26 [Q,U,R,T,C,X,lG,iter] = solver_sqd(sn, options);
27 case {'qna'}
28 line_debug(options, 'Using QNA method, calling solver_qna');
29 [Q,U,R,T,C,X] = solver_qna(sn, options);
30 lG = NaN;
31 case {'sum','esum'}
32 line_debug(options, 'Using summation method, calling solver_mva_sum');
33 [Q,U,R,T,C,X,lG,iter] = solver_mva_sum(sn, options);
34 case {'rqna'}
35 line_debug(options, 'Using RQNA method, calling solver_rqna');
36 [Q,U,R,T,C,X] = solver_rqna(sn, options);
37 lG = NaN;
38 case {'default'}
39 % for non-exponential open queueing networks, use qna
40 % (commented as not ready yet, it fails on example_cacheModel_3.m)
41 %if all(isinf(sn.njobs)) && any(any(sn.scv ~= 1.0))
42 % line_warning(mfilename,'QNA implementation is still in beta version.')
43 % [Q,U,R,T,C,X] = solver_qna(sn, options);
44 % lG = NaN;
45 %else
46 % see _kb/06-solver-catalog.md (MVA section) for the default-dispatch rules
47 if sn.nclasses == 1 && all(isinf(sn.njobs)) && sn_has_bursty_arrival(sn)
48 line_debug('Default method: bursty single-class open network, using RQNA\n');
49 line_debug(options, 'Non-renewal arrivals detected, calling solver_rqna');
50 [Q,U,R,T,C,X] = solver_rqna(sn, options);
51 lG = NaN;
52 method = 'rqna';
53 % Closed single-chain Blocking-After-Service network (finite buffers)
54 elseif isBasModel(sn)
55 line_debug('Default method: using BAS for blocking-after-service model\n');
56 line_debug(options, 'Model has Blocking-After-Service finite buffers, calling solver_sqd');
57 [Q,U,R,T,C,X,lG,iter] = solver_sqd(sn, options);
58 method = 'sqd';
59 % Force AMVA for class- or joint-dependent models - exact MVA doesn't support them
60 elseif ~isempty(sn.cdscaling) || ~isempty(sn.jdscaling)
61 line_debug('Default method: using AMVA for class-/joint-dependent model\n');
62 line_debug(options, 'Model has class/joint dependence, calling solver_amva');
63 [Q,U,R,T,C,X,lG,iter,method,converged] = solver_amva(sn, options);
64 elseif any(isinf(sn.njobs)) && any(isfinite(sn.njobs) & sn.njobs > 0) && max(sn.nservers(isfinite(sn.nservers))) == 1 && sn_has_product_form(sn) && all(sn.njobs(isfinite(sn.njobs)) == floor(sn.njobs(isfinite(sn.njobs))))
65 % see _kb/06-solver-catalog.md (MVA section) for the default-dispatch rules
66 line_debug('Default method: using exact mixed MVA\n');
67 [Q,U,R,T,C,X,lG] = solver_mva(sn, options);
68 method = 'exact';
69 elseif sn.nchains <= 4 && sum(sn.njobs) <= 20 && sn_has_product_form(sn) && ~sn_has_fractional_populations(sn)
70 line_debug('Default method: using exact MVA\n');
71 % The parameters above take in the worst case a handful of ms
72 line_debug(options, 'Model qualifies for exact MVA (nchains=%d, njobs=%d, product-form=%d), calling solver_mva', sn.nchains, sum(sn.njobs), sn_has_product_form(sn));
73 [Q,U,R,T,C,X,lG] = solver_mva(sn, options);
74 method = 'exact';
75 else
76 line_debug('Default method: using approximate MVA\n');
77 line_debug(options, 'Model requires approximation (nchains=%d, njobs=%d, product-form=%d), calling solver_amva', sn.nchains, sum(sn.njobs), sn_has_product_form(sn));
78 [Q,U,R,T,C,X,lG,iter,method,converged] = solver_amva(sn, options);
79 end
80 %end
81 case {'amva','bs','qd','qli','fli','lin','qdlin','sqni','egflin','gflin','ab','schmidt','schmidt-ext'} %,'aql','qdaql'
82 line_debug(options, 'Using approximate MVA method: %s, calling solver_amva', method);
83 [Q,U,R,T,C,X,lG,iter,~,converged] = solver_amva(sn, options);
84 otherwise
85 % An unsupported method must error by name (returning empty metrics let the
86 % caller index into [] with an opaque message). 'aql'/'qdaql' exist in
87 % solver_amva but are deliberately not dispatched here.
88 line_error(mfilename, sprintf(['The ''%s'' method is not dispatched by ' ...
89 'solver_mva_analyzer. Supported: default, exact, mva, mvac, amva, bs, qd, qli, fli, ' ...
90 'lin, qdlin, sqni, egflin, gflin, ab, schmidt, schmidt-ext.'], method));
91end
92runtime = toc(Tstart);
93
94% see _kb/06-solver-catalog.md (MVA section) for AMVA convergence flag-vs-count
95iterBudget = min(options.iter_max, 10000);
96if ~isempty(converged)
97 nonConverged = ~converged;
98else
99 nonConverged = ~isempty(iter) && isscalar(iter) && ~isnan(iter) && isfinite(iter) && iter >= iterBudget;
100end
101if nonConverged
102 line_warning_always(mfilename, ...
103 'AMVA method ''%s'' did not meet the convergence tolerance %g after %d iterations; the returned metrics may not be converged. Try another method (e.g. ''qd'' or ''bs''), raise options.iter_max, or loosen options.iter_tol.', ...
104 method, options.iter_tol, iter);
105end
106
107if options.verbose
108 %line_printf('\nMVA analysis completed. Runtime: %f seconds.\n',runtime);
109end
110
111end
112
113function tf = isBasModel(sn)
114% Detect a closed single-chain network with Blocking-After-Service (BAS) finite-buffer
115% blocking, which solver_sqd handles but exact/AMVA MVA does not.
116tf = false;
117if sn.nchains ~= 1 || sn.nclosedjobs <= 0 || isempty(sn.droprule)
118 return;
119end
120if any(isinf(sn.njobs))
121 return; % open class present
122end
123tf = any(sn.droprule(:) == DropStrategy.BAS);
124end