LINE Solver
MATLAB API documentation
Loading...
Searching...
No Matches
SolverMVA.m
1classdef SolverMVA < NetworkSolver
2 % Mean Value Analysis solver for queueing networks
3 %
4 % Implements MVA algorithms for analyzing closed and open queueing networks.
5 %
6 % Copyright (c) 2012-2026, Imperial College London
7 % All rights reserved.
8
9 methods
10 function self = SolverMVA(model,varargin)
11 % SOLVERMVA Create an MVA solver instance
12 %
13 % @brief Creates a Mean Value Analysis solver for the given model
14 % @param model Network model to be analyzed
15 % @param varargin Optional solver options (method, tolerance, etc.)
16 % @return self SolverMVA instance configured with specified options
17
18 self@NetworkSolver(model, mfilename);
19 self.setOptions(Solver.parseOptions(varargin, SolverMVA.defaultOptions));
20 self.setLang();
21 end
22
23 function sn = getStruct(self)
24 % GETSTRUCT Get model data structure for analysis
25 %
26 % @brief Returns the internal data structure representing the model
27 % @return sn Structured data representing the queueing network
28 sn = self.model.getStruct(false);
29 end
30
31 function tf = supportsExactSensitivity(self) %#ok<MANU>
32 % TF = SUPPORTSEXACTSENSITIVITY()
33 % MVA differentiates its own recursion: getSensitivityTable uses
34 % the analytic branch (pfqn_sens) wherever the model is in scope.
35 tf = true;
36 end
37
38 [runtime, analyzer] = runAnalyzer(self, options);
39 [lNormConst] = getProbNormConstAggr(self);
40 [Pnir,logPnir] = getProbAggr(self, ist);
41 [Pnir,logPn] = getProbSysAggr(self);
42
43 function [allMethods] = listValidMethods(self)
44 % LISTVALIDMETHODS Get all valid MVA solution methods
45 %
46 % @brief Returns cell array of valid MVA methods for the current model
47 % @return allMethods Cell array of method names available for this model
48
49 sn = self.model.getStruct;
50 % base set of methods
51 allMethods = {'default',...
52 'mva','exact','amva', ...
53 'sum','esum', ...
54 'qdlin','amva.qdlin', ...
55 'bs','amva.bs', ...
56 'sqni', ...
57 'qd','amva.qd', ...
58 'qli','amva.qli', ...
59 'fli','amva.fli', ...
60 'ab','amva.ab', ...
61 'schmidt','amva.schmidt', ...
62 'schmidt-ext','amva.schmidt-ext'};
63
64 % QNA/RQNA advertised for fully open models only; see
65 % _kb/06-solver-catalog.md (MVA section) for the default-dispatch rules
66 if sn_is_open_model(sn)
67 allMethods = [allMethods(1:4), {'qna','rqna'}, allMethods(5:end)];
68 end
69
70 % SQD (Smith Queue Decomposition) is only valid for closed
71 % single-chain Blocking-After-Service networks; kept at this
72 % position to preserve BAS regression-baseline ordering.
73 if sn_is_bas_model(sn)
74 allMethods{end+1} = 'sqd'; %#ok<AGROW>
75 end
76
77 % MVAC (exact mean value analysis by chain, pfqn_mvac): closed
78 % single-server product-form networks only; rejects open/mixed and
79 % multiserver at solve time.
80 if ~any(isinf(sn.njobs))
81 allMethods{end+1} = 'mvac'; %#ok<AGROW>
82 end
83
84 % Marie withheld for open models and for class-dependent routing;
85 % see _kb/06-solver-catalog.md (MVA section) for the rationale
86 if ~sn_is_open_model(sn) && ~sn_has_classdep_routing(sn)
87 allMethods = [allMethods, {'marie','amva.marie'}]; %#ok<AGROW>
88 end
89
90 allMethods = {allMethods{:}, ...
91 'lin','egflin','gflin','amva.lin'}; %#ok<CCAT>
92
93 % Bound methods (aba/bjb/gb/pb/sb/mwba) are NOT listed here: they
94 % moved to SolverBA, and runAnalyzer raises line_error for the whole
95 % family. Listing them made this a false claim, since every listed
96 % name errored when run. See SolverBA.listValidMethods.
97
98 if sn_is_open_model(sn) && sn.nstations == 2 && sn.nclasses == 1
99 % methods to add for queueing systems
100 qsys = {'mm1','mmk','mg1','mgi1','gm1','gig1','gim1','gig1.kingman', ...
101 'gigk','gigk.kingman_approx', ...
102 'gig1.gelenbe','gig1.heyman','gig1.kimura','gig1.allen', ...
103 'gig1.kobayashi','gig1.klb','gig1.marchal'};
104 % append, keeping original order and avoiding duplicates
105 allMethods = {allMethods{:}, qsys{:}}; %#ok<CCAT>
106 end
107 end
108
109 function method = resolveMethod(self, options)
110 % Feature-driven resolution of options.method='default'. A bursty
111 % single-class open network has a non-renewal (MAP/MMPP) arrival
112 % process whose autocorrelation a two-moment method cannot capture,
113 % so the default dispatch selects RQNA (robust queueing network
114 % analyzer, indices of dispersion). All other cases keep 'default',
115 % which the analyzer expands with its own heuristics. Expressed as
116 % RQNA's MAP-family coverage rather than a bespoke gate.
117 method = options.method;
118 if strcmp(options.method, 'default')
119 sn = self.model.getStruct();
120 if (sn.nclasses == 1) && all(isinf(sn.njobs)) && sn_has_bursty_arrival(sn)
121 method = 'rqna';
122 end
123 end
124 end
125
126 function featSupported = getMethodFeatureSet(self, method) %#ok<INUSL>
127 % Per-method feature deltas applied to the base MVA envelope.
128 % RQNA natively consumes non-renewal MAP/MMPP/RAP arrival and
129 % service processes (open only); QNA is a two-moment open-network
130 % method. The queueing-system and bounds methods are already
131 % structurally restricted by listValidMethods, so they inherit the
132 % base envelope unchanged.
133 featSupported = SolverMVA.getFeatureSet();
134 switch method
135 case 'rqna'
136 featSupported.setTrue({'MAP','MMPP2','MMAP','RAP'});
137 featSupported.setFalse({'ClosedClass','SelfLoopingClass'});
138 case 'qna'
139 featSupported.setFalse({'ClosedClass','SelfLoopingClass'});
140 end
141 end
142
143 function [bool, reason] = supportsModelMethod(self, method)
144 % Finite station/class capacity has no registry feature name, so
145 % the coarse per-method feature gate cannot see it. Apply the
146 % structural capacity check on top of it, otherwise MVA silently
147 % returns the unconstrained product-form answer for models built
148 % with setCapacity / a finite classCap (BUG-39).
149 [bool, reason] = supportsModelMethod@NetworkSolver(self, method);
150 if bool && isa(self.model, 'Network')
151 [bool, reason] = SolverMVA.supportsFiniteCapacity(self.model, method);
152 end
153 end
154
155 end
156
157 methods(Static)
158 function [bool, reason] = supportsFiniteCapacity(model, method)
159 % [BOOL, REASON] = SUPPORTSFINITECAPACITY(MODEL, METHOD)
160 % MVA-specific finite-capacity gate: Blocking-After-Service models
161 % are exempt because MVA offers the Smith queue-decomposition
162 % method 'sqd', and solver_mva_analyzer routes a BAS model to
163 % solver_sqd under the default method too, so the finite buffers
164 % ARE honoured on every MVA path. Everything else defers to the
165 % shared product-form gate.
166 if nargin < 2
167 method = '';
168 end
169 bool = true;
170 reason = '';
171 if ~isa(model, 'Network')
172 return
173 end
174 if sn_is_bas_model(model.getStruct())
175 return
176 end
177 % Single-station M/M/1/K with tail drop is handled by the
178 % moment-based (MacGregor Smith) qsys_mg1k_loss_mgs branch in
179 % solver_mva_qsys_analyzer, exact only at scv=1. It is an
180 % approximation in general, so method='exact' is NOT exempted (it
181 % must reject); every other method is.
182 if ~strcmp(method, 'exact') && sn_is_mm1k_loss(model.getStruct())
183 return
184 end
185 [bool, reason] = NetworkSolver.checkBindingCapacity(model, 'SolverMVA');
186 end
187
188 function featSupported = getFeatureSet()
189 % FEATSUPPORTED = GETFEATURESET()
190
191 featSupported = SolverFeatureSet;
192 featSupported.setTrue({'Sink','Source',...
193 'ClassSwitch','Delay','DelayStation','Queue',...
194 'APH','Coxian','Erlang','Exp','HyperExp','BMAP',...
195 'Pareto','Weibull','Lognormal','Uniform','Det', ...
196 'StatelessClassSwitcher','InfiniteServer','SharedServer','Buffer','Dispatcher',...
197 'CacheClassSwitcher','Cache', ...
198 'CacheRetrieval', ...
199 'Server','JobSink','RandomSource','ServiceTunnel',...
200 'SchedStrategy_INF','SchedStrategy_PS',...
201 'SchedStrategy_DPS','SchedStrategy_FCFS','SchedStrategy_SIRO','SchedStrategy_HOL',...
202 'SchedStrategy_LCFS','SchedStrategy_LCFSPR','SchedStrategy_POLLING',...
203 'SchedStrategy_OI','SchedStrategy_PAS',... % exact order-independent path only (solver_mva_oi_analyzer)
204 'Fork','Forker','Join','Joiner',...
205 'RoutingStrategy_PROB','RoutingStrategy_RAND',...
206 'ReplacementStrategy_RR', 'ReplacementStrategy_FIFO', 'ReplacementStrategy_LRU',...
207 'ReplacementStrategy_HLRU',...
208 'MMAP',... % marked MAP sources (cache LRU via cache_ttl_lrum_map)
209 'ClosedClass','SelfLoopingClass','OpenClass','Replayer',...
210 'LoadDependence','ClassDependence','JointDependence'});
211 end
212
213 function [bool, featSupported] = supports(model)
214 % [BOOL, FEATSUPPORTED] = SUPPORTS(MODEL)
215
216 featUsed = model.getUsedLangFeatures();
217 featSupported = SolverMVA.getFeatureSet();
218 bool = SolverFeatureSet.supports(featSupported, featUsed);
219 if bool
220 % Registry inclusion cannot see finite capacity (no feature
221 % name); apply the structural gate too, so that the static
222 % supports() agrees with the runAnalyzer feature gate.
223 [bool, reason] = SolverMVA.supportsFiniteCapacity(model);
224 if ~bool
225 line_warning(mfilename, '%s\n', reason);
226 end
227 end
228 end
229
230 function options = defaultOptions
231 % OPTIONS = DEFAULTOPTIONS()
232
233 options = SolverOptions('MVA');
234 end
235
236 function libs = getLibrariesUsed(sn, options)
237 % GETLIBRARIESUSED Get list of external libraries used by MVA solver
238 % MVA uses internal algorithms, no external library attribution needed
239 libs = {};
240 end
241
242 end
243end