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 properties (Hidden)
10 % Cached MMT fork-join transformation, reused across the outer
11 % iterations of SolverLN so that each layer is not deep-copied once per
12 % iteration. Tagged with the model's structVersion and only reused while
13 % that still matches; its rates are re-fed from the base model on every
14 % reuse (ModelAdapter.refreshServicesFromBase). Deliberately NOT cleared
15 % by reset(): correctness lives at the point of use, and clearing it
16 % there would throw the transformation away on every outer iteration.
17 mmtCache = [];
18 end
19
20 properties (Access = public)
21 % Auxiliary-class arrival rates of the fork-join (MMT) fixed point,
22 % retained across runAnalyzer calls so that an outer iteration (e.g.
23 % SolverLN) restarts the MMT loop from the previous converged point
24 % instead of from GlobalConstants.FineTol. Like options.init_sol it
25 % survives reset(); use resetForkWarmStart to discard it.
26 fjForkLambda = [];
27 end
28
29 methods
30 function self = SolverMVA(model,varargin)
31 % SOLVERMVA Create an MVA solver instance
32 %
33 % @brief Creates a Mean Value Analysis solver for the given model
34 % @param model Network model to be analyzed
35 % @param varargin Optional solver options (method, tolerance, etc.)
36 % @return self SolverMVA instance configured with specified options
37
38 self@NetworkSolver(model, mfilename);
39 self.setOptions(Solver.parseOptions(varargin, SolverMVA.defaultOptions));
40 self.setLang();
41 end
42
43 function sn = getStruct(self)
44 % GETSTRUCT Get model data structure for analysis
45 %
46 % @brief Returns the internal data structure representing the model
47 % @return sn Structured data representing the queueing network
48 sn = self.model.getStruct(false);
49 end
50
51 function tf = supportsExactSensitivity(self) %#ok<MANU>
52 % TF = SUPPORTSEXACTSENSITIVITY()
53 % MVA differentiates its own recursion: getSensitivityTable uses
54 % the analytic branch (pfqn_sens) wherever the model is in scope.
55 tf = true;
56 end
57
58 function resetForkWarmStart(self)
59 % RESETFORKWARMSTART()
60 % Discard the retained MMT fixed point. Mirrors options.init_sol:
61 % the iterate survives reset() and is invalidated explicitly by the
62 % caller when the chain basis changes.
63 self.fjForkLambda = [];
64 end
65
66 [runtime, analyzer] = runAnalyzer(self, options);
67 [lNormConst] = getProbNormConstAggr(self);
68 [Pnir,logPnir] = getProbAggr(self, ist);
69 [Pnir,logPn] = getProbSysAggr(self);
70
71 function [allMethods] = listValidMethods(self)
72 % LISTVALIDMETHODS Get all valid MVA solution methods
73 %
74 % @brief Returns cell array of valid MVA methods for the current model
75 % @return allMethods Cell array of method names available for this model
76
77 sn = self.model.getStruct;
78 % base set of methods
79 allMethods = {'default',...
80 'mva','exact','amva', ...
81 'sum','esum', ...
82 'qdlin','amva.qdlin', ...
83 'bs','amva.bs', ...
84 'sqni', ...
85 'qd','amva.qd', ...
86 'qli','amva.qli', ...
87 'fli','amva.fli', ...
88 'ab','amva.ab', ...
89 'schmidt','amva.schmidt', ...
90 'schmidt-ext','amva.schmidt-ext'};
91
92 % QNA and RQNA are open-network decomposition analyzers driven by
93 % arrival (Poisson/MAP) flows; they have no meaningful fixed point
94 % on a model with closed chains (the mixed path is disabled and a
95 % fully closed model has no exogenous arrivals), and previously
96 % returned degenerate queue lengths there. Advertise them only for
97 % fully open models (matching the RQNA default dispatch and the
98 % native Python gate), inserting between 'amva' and 'qdlin' to
99 % preserve the open regression-baseline ordering.
100 if sn_is_open_model(sn)
101 allMethods = [allMethods(1:4), {'qna','rqna'}, allMethods(5:end)];
102 end
103
104 % SQD (Smith Queue Decomposition) is only valid for closed
105 % single-chain Blocking-After-Service networks; kept at this
106 % position to preserve BAS regression-baseline ordering.
107 if sn_is_bas_model(sn)
108 allMethods{end+1} = 'sqd'; %#ok<AGROW>
109 end
110
111 % Marie's aggregation-decomposition (method 'marie') is a closed-
112 % network mean-value method for FCFS non-exponential service; it is
113 % not defined for open models (no exogenous arrivals to decompose).
114 if ~sn_is_open_model(sn)
115 allMethods = [allMethods, {'marie','amva.marie'}]; %#ok<AGROW>
116 end
117
118 allMethods = {allMethods{:}, ...
119 'lin','egflin','gflin','amva.lin'}; %#ok<CCAT>
120
121 % Bound methods (aba/bjb/gb/pb/sb/mwba) are NOT listed here: they
122 % moved to SolverBA, and runAnalyzer raises line_error for the whole
123 % family. Listing them made this a false claim, since every listed
124 % name errored when run. See SolverBA.listValidMethods.
125
126 if sn_is_open_model(sn) && sn.nstations == 2 && sn.nclasses == 1
127 % methods to add for queueing systems
128 qsys = {'mm1','mmk','mg1','mgi1','gm1','gig1','gim1','gig1.kingman', ...
129 'gigk','gigk.kingman_approx', ...
130 'gig1.gelenbe','gig1.heyman','gig1.kimura','gig1.allen', ...
131 'gig1.kobayashi','gig1.klb','gig1.marchal'};
132 % append, keeping original order and avoiding duplicates
133 allMethods = {allMethods{:}, qsys{:}}; %#ok<CCAT>
134 end
135 end
136
137 function method = resolveMethod(self, options)
138 % Feature-driven resolution of options.method='default'. A bursty
139 % single-class open network has a non-renewal (MAP/MMPP) arrival
140 % process whose autocorrelation a two-moment method cannot capture,
141 % so the default dispatch selects RQNA (robust queueing network
142 % analyzer, indices of dispersion). All other cases keep 'default',
143 % which the analyzer expands with its own heuristics. Expressed as
144 % RQNA's MAP-family coverage rather than a bespoke gate.
145 method = options.method;
146 if strcmp(options.method, 'default')
147 sn = self.model.getStruct();
148 if (sn.nclasses == 1) && all(isinf(sn.njobs)) && sn_has_bursty_arrival(sn)
149 method = 'rqna';
150 end
151 end
152 end
153
154 function featSupported = getMethodFeatureSet(self, method) %#ok<INUSL>
155 % Per-method feature deltas applied to the base MVA envelope.
156 % RQNA natively consumes non-renewal MAP/MMPP/RAP arrival and
157 % service processes (open only); QNA is a two-moment open-network
158 % method. The queueing-system and bounds methods are already
159 % structurally restricted by listValidMethods, so they inherit the
160 % base envelope unchanged.
161 featSupported = SolverMVA.getFeatureSet();
162 switch method
163 case 'rqna'
164 featSupported.setTrue({'MAP','MMPP2','MMAP','RAP'});
165 featSupported.setFalse({'ClosedClass','SelfLoopingClass'});
166 case 'qna'
167 featSupported.setFalse({'ClosedClass','SelfLoopingClass'});
168 end
169 end
170
171 function [bool, reason] = supportsModelMethod(self, method)
172 % Finite station/class capacity has no registry feature name, so
173 % the coarse per-method feature gate cannot see it. Apply the
174 % structural capacity check on top of it, otherwise MVA silently
175 % returns the unconstrained product-form answer for models built
176 % with setCapacity / a finite classCap (BUG-39).
177 [bool, reason] = supportsModelMethod@NetworkSolver(self, method);
178 if bool && isa(self.model, 'Network')
179 [bool, reason] = SolverMVA.supportsFiniteCapacity(self.model);
180 end
181 end
182
183 end
184
185 methods(Static)
186 function [bool, reason] = supportsFiniteCapacity(model)
187 % [BOOL, REASON] = SUPPORTSFINITECAPACITY(MODEL)
188 % MVA-specific finite-capacity gate: Blocking-After-Service models
189 % are exempt because MVA offers the Smith queue-decomposition
190 % method 'sqd', and solver_mva_analyzer routes a BAS model to
191 % solver_sqd under the default method too, so the finite buffers
192 % ARE honoured on every MVA path. Everything else defers to the
193 % shared product-form gate.
194 bool = true;
195 reason = '';
196 if ~isa(model, 'Network')
197 return
198 end
199 if sn_is_bas_model(model.getStruct())
200 return
201 end
202 [bool, reason] = NetworkSolver.checkBindingCapacity(model, 'SolverMVA');
203 end
204
205 function featSupported = getFeatureSet()
206 % FEATSUPPORTED = GETFEATURESET()
207
208 featSupported = SolverFeatureSet;
209 featSupported.setTrue({'Sink','Source',...
210 'ClassSwitch','Delay','DelayStation','Queue',...
211 'APH','Coxian','Erlang','Exp','HyperExp','BMAP',...
212 'Pareto','Weibull','Lognormal','Uniform','Det', ...
213 'StatelessClassSwitcher','InfiniteServer','SharedServer','Buffer','Dispatcher',...
214 'CacheClassSwitcher','Cache', ...
215 'CacheRetrieval', ...
216 'Server','JobSink','RandomSource','ServiceTunnel',...
217 'SchedStrategy_INF','SchedStrategy_PS',...
218 'SchedStrategy_DPS','SchedStrategy_FCFS','SchedStrategy_SIRO','SchedStrategy_HOL',...
219 'SchedStrategy_LCFS','SchedStrategy_LCFSPR','SchedStrategy_POLLING',...
220 'SchedStrategy_OI','SchedStrategy_PAS',... % exact order-independent path only (solver_mva_oi_analyzer)
221 'Fork','Forker','Join','Joiner',...
222 'RoutingStrategy_PROB','RoutingStrategy_RAND',...
223 'ReplacementStrategy_RR', 'ReplacementStrategy_FIFO', 'ReplacementStrategy_LRU',...
224 'ReplacementStrategy_HLRU',...
225 'MMAP',... % marked MAP sources (cache LRU via cache_ttl_lrum_map)
226 'ClosedClass','SelfLoopingClass','OpenClass','Replayer',...
227 'LoadDependence','ClassDependence'});
228 end
229
230 function [bool, featSupported] = supports(model)
231 % [BOOL, FEATSUPPORTED] = SUPPORTS(MODEL)
232
233 featUsed = model.getUsedLangFeatures();
234 featSupported = SolverMVA.getFeatureSet();
235 bool = SolverFeatureSet.supports(featSupported, featUsed);
236 if bool
237 % Registry inclusion cannot see finite capacity (no feature
238 % name); apply the structural gate too, so that the static
239 % supports() agrees with the runAnalyzer feature gate.
240 [bool, reason] = SolverMVA.supportsFiniteCapacity(model);
241 if ~bool
242 line_warning(mfilename, '%s\n', reason);
243 end
244 end
245 end
246
247 function options = defaultOptions
248 % OPTIONS = DEFAULTOPTIONS()
249
250 options = SolverOptions('MVA');
251 end
252
253 function libs = getLibrariesUsed(sn, options)
254 % GETLIBRARIESUSED Get list of external libraries used by MVA solver
255 % MVA uses internal algorithms, no external library attribution needed
256 libs = {};
257 end
258
259 end
260end
Definition Station.m:245