LINE Solver
MATLAB API documentation
Loading...
Searching...
No Matches
SolverMAM.m
1classdef SolverMAM < NetworkSolver
2 % Matrix-Analytic and RCAT methods solver
3 %
4 % Implements matrix-analytic methods and RCAT for structured Markov chain analysis.
5 %
6 % Copyright (c) 2012-2026, Imperial College London
7 % All rights reserved.
8
9 methods
10 function self = SolverMAM(model,varargin)
11 % SOLVERMAM Create a Matrix-Analytic Methods solver instance
12 %
13 % @brief Creates a MAM solver for structured Markov chain analysis
14 % @param model Network model to be analyzed via matrix-analytic methods
15 % @param varargin Optional parameters (method, tolerance, etc.)
16 % @return self SolverMAM instance configured for MAM analysis
17
18 self@NetworkSolver(model, mfilename);
19 self.setOptions(Solver.parseOptions(varargin, self.defaultOptions));
20 self.setLang();
21 end
22
23 function sn = getStruct(self)
24 % QN = GETSTRUCT()
25
26 % Get data structure summarizing the model
27 sn = self.model.getStruct(true);
28 end
29
30 runtime = runAnalyzer(self, options);
31 RD = getCdfRespT(self, R);
32
33 function [allMethods] = listValidMethods(self)
34 % allMethods = LISTVALIDMETHODS()
35 % List valid methods for this solver
36 sn = self.model.getStruct();
37 % Note: Method order must match expected values in test files.
38 % New methods should be added at the end to preserve index alignment.
39 % 'exact' method removed - autocat moved to line-legacy.git
40 allMethods = {'default','dec.source','dec.mmap','dec.poisson','mna','inap','ldqbd','inapplus','dec.source.mmap','inapinf'};
41 end
42
43 function featSupported = getMethodFeatureSet(self, method) %#ok<INUSD>
44 % Every MAM method shares the solver-level feature envelope; the
45 % genuine per-method restrictions ('mna', 'ldqbd') are structural
46 % and are applied in supportsModelMethod below.
47 %
48 % Defining this is what lets NetworkSolver.supportsModelMethod name
49 % the offending features: with no method feature set it falls back
50 % to the coarse supports(model) and returns an empty reason, so the
51 % gate could only report "features not supported" without saying
52 % which ones.
53 featSupported = SolverMAM.getFeatureSet();
54 end
55
56 function [bool, reason] = supportsModelMethod(self, method)
57 % Method-aware gate for the genuine per-method restrictions of the
58 % MAM analyzer (mirrors the inline guards in solver_mam_analyzer):
59 % 'mna' does not support mixed open/closed models, and 'ldqbd'
60 % requires a single-class model. All other methods rely on the
61 % coarse MAM feature set and the analyzer's topology-based routing
62 % (e.g. a Fork-Join model on 'default'/'dec.source' is routed to the
63 % FJ solver, not rejected).
64 sn = self.model.getStruct();
65 switch method
66 case 'mna'
67 if ~sn_is_open_model(sn) && ~sn_is_closed_model(sn)
68 bool = false;
69 reason = 'The mna method does not support mixed open/closed models.';
70 return;
71 end
72 % mna rejects self-looping classes (no inter-station flow);
73 % see _kb/06-solver-catalog.md for rationale
74 Vmna = cellsum(sn.visits);
75 for kmna = 1:sn.nclasses
76 % Only a CLOSED class is self-looping (njobs<Inf); see _kb/06-solver-catalog.md for rationale
77 if ~isfinite(sn.njobs(kmna))
78 continue;
79 end
80 vis = find(Vmna(:, kmna) > GlobalConstants.FineTol);
81 if isscalar(vis) && sn.sched(vis) ~= SchedStrategy.INF ...
82 && sn.sched(vis) ~= SchedStrategy.EXT
83 bool = false;
84 reason = sprintf(['The mna method does not support self-looping ' ...
85 'classes (class %d is confined to station %d with no ' ...
86 'inter-station flow to decompose). Use the dec.source method.'], ...
87 kmna, vis);
88 return;
89 end
90 end
91 case 'ldqbd'
92 if sn.nclasses ~= 1
93 bool = false;
94 reason = 'The ldqbd method requires a single-class model.';
95 return;
96 end
97 case {'inap','inapplus','inapinf','exact'}
98 % RCAT collapses each process to its mean rate: reject
99 % non-exponential; see _kb/06-solver-catalog.md for rationale
100 nonExp = (sn.procid ~= ProcessType.EXP) & isfinite(sn.rates) & (sn.rates > 0);
101 % Exempt signal service entries from the nonExp mask;
102 % see _kb/06-solver-catalog.md for rationale
103 if isfield(sn, 'issignal') && ~isempty(sn.issignal) && any(sn.issignal)
104 issource = false(size(nonExp, 1), 1);
105 for ist = 1:numel(issource)
106 issource(ist) = sn.nodetype(sn.stationToNode(ist)) == NodeType.Source;
107 end
108 nonExp(~issource, logical(sn.issignal(:))') = false;
109 end
110 if any(nonExp(:))
111 [ist, r] = find(nonExp, 1);
112 bool = false;
113 reason = sprintf(['The %s method supports exponential processes only ' ...
114 '(RCAT models each station-class by its mean rate, with no service-phase ' ...
115 'dimension), but station %d class %d is %s. Use the dec.source method ' ...
116 'for non-exponential models.'], method, ist, r, ...
117 ProcessType.toText(sn.procid(ist, r)));
118 return;
119 end
120 % RCAT models every station single-server: reject multi-server;
121 % see _kb/06-solver-catalog.md for rationale
122 multi = isfinite(sn.nservers) & (sn.nservers > 1);
123 if any(multi(:))
124 ist = find(multi, 1);
125 bool = false;
126 reason = sprintf(['The %s method supports single-server stations only ' ...
127 '(RCAT does not model sn.nservers, so a multiserver station is driven ' ...
128 'at rho = lambda/mu instead of lambda/(c*mu)), but station %d has %d ' ...
129 'servers. Use the dec.source method for multiserver models.'], ...
130 method, ist, sn.nservers(ist));
131 return;
132 end
133 end
134 [bool, reason] = supportsModelMethod@NetworkSolver(self, method);
135 end
136end
137
138 methods (Static)
139
140
141 function featSupported = getFeatureSet()
142 % FEATSUPPORTED = GETFEATURESET()
143
144 featSupported = SolverFeatureSet;
145 % MAM features
146 featSupported.setTrue({'Sink','Source',...
147 'Fork','Join','Forker','Joiner',... % Fork-Join support (via FJ_codes)
148 'Delay','DelayStation','Queue',...
149 'APH','Coxian','Erlang','Exp','HyperExp','MMPP2','MAP','MMAP','DMAP','ME','RAP',...
150 'Det','Gamma','Lognormal','Pareto','Uniform','Weibull',...
151 'StatelessClassSwitcher','InfiniteServer',...
152 'ClassSwitch', ...
153 'SharedServer','Buffer','Dispatcher',...
154 'Server','JobSink','RandomSource','ServiceTunnel',...
155 'SchedStrategy_INF','SchedStrategy_PS','SchedStrategy_HOL',...
156 'SchedStrategy_FCFSPRPRIO',... % solver_mam_basic: MMAPPH1PRPR
157 'SchedStrategy_FCFS',...
158 'RoutingStrategy_PROB','RoutingStrategy_RAND',...
159 'ClosedClass','SelfLoopingClass',...
160 'OpenClass'});
161 % Add RCAT (AG) features
162 featSupported.setTrue({'Sink', 'Source', ...
163 'Fork','Join','Forker','Joiner',... % Fork-Join support (via FJ_codes)
164 'Delay', 'DelayStation', 'Queue', ...
165 'APH', 'Coxian', 'Erlang', 'Exp', 'HyperExp', ...
166 'Det','Gamma','Lognormal','Pareto','Uniform','Weibull',...
167 'StatelessClassSwitcher', 'InfiniteServer', ...
168 'SharedServer', 'Buffer', 'Dispatcher', ...
169 'Server', 'JobSink', 'RandomSource', 'ServiceTunnel', ...
170 'SchedStrategy_INF', 'SchedStrategy_PS', ...
171 'SchedStrategy_FCFS', ...
172 'RoutingStrategy_PROB', 'RoutingStrategy_RAND', ...
173 'ClosedClass', ...
174 'OpenClass', ...
175 'OpenSignal', 'ClosedSignal', ... % G-network signals (solver_mam_ag)
176 'SignalType_NEGATIVE', 'SignalType_CATASTROPHE', ...
177 'SignalBatchRemoval'}); % AG reads sn.signalremdist
178 % Add BMAP/PH/N/N retrial queue features
179 featSupported.setTrue({'Retrial', 'BMAP', 'PH'});
180 % Setup/delay-off: open stations are solved exactly by
181 % qbd_setupdelayoff; closed stations use the per-instance
182 % cold-start race of the isfunction branch.
183 featSupported.setTrue({'SetupDelayOff'});
184 end
185
186 function [bool, featSupported] = supports(model)
187 % [BOOL, FEATSUPPORTED] = SUPPORTS(MODEL)
188
189 featUsed = model.getUsedLangFeatures();
190 featSupported = SolverMAM.getFeatureSet();
191 bool = SolverFeatureSet.supports(featSupported, featUsed);
192 end
193
194 function options = defaultOptions()
195 % OPTIONS = DEFAULTOPTIONS()
196 options = SolverOptions('MAM');
197 end
198
199 function libs = getLibrariesUsed(sn, options)
200 % GETLIBRARIESUSED Get list of external libraries used by MAM solver
201 % Detect libraries used by MAM solver based on topology and method
202 libs = {};
203
204 % MAMSolver used for matrix-analytic methods (M/G/1, GI/M/1 types)
205 % This includes default and decomposition methods
206 if ismember(options.method, {'default', 'dec.source', 'dec.mmap', 'dec.poisson', 'dec.source.mmap'})
207 libs{end+1} = 'MAMSolver';
208 end
209
210 % Q-MAM used for specific RCAT-based methods
211 if ismember(options.method, {'mna', 'inap', 'inapplus', 'inapinf'})
212 libs{end+1} = 'Q-MAM';
213 end
214
215 % SMCSolver used for QBD (Quasi-Birth-Death) analysis
216 % Currently available but not actively used in default paths
217 % Uncomment when QBD methods are activated:
218 % if ismember(options.method, {'qbd'})
219 % libs{end+1} = 'SMCSolver';
220 % end
221
222 % BUTools usage detection (via KPCToolbox MAP functions)
223 % BUTools is used when analyzing MAP/PH distributions
224 if ~isempty(sn) && isfield(sn, 'proc') && ~isempty(sn.proc) && any(~cellfun(@isempty, sn.proc(:)))
225 libs{end+1} = 'BUTools';
226 end
227
228 % Remove duplicates and maintain order
229 libs = unique(libs, 'stable');
230 end
231 end
232end