LINE Solver
MATLAB API documentation
Loading...
Searching...
No Matches
SolverCTMC.m
1classdef SolverCTMC < NetworkSolver
2 % Continuous-Time Markov Chain solver for exact state-space analysis
3 %
4 % Implements exact analysis of queueing networks via CTMC formulation.
5 %
6 % Copyright (c) 2012-2026, Imperial College London
7 % All rights reserved.
8
9 methods
10 function self = SolverCTMC(model,varargin)
11 % SOLVERCTMC Create a CTMC solver instance
12 %
13 % @brief Creates a CTMC solver for exact Markov chain analysis
14 % @param model Network model to be analyzed via CTMC formulation
15 % @param varargin Optional parameters (cutoff, method, etc.)
16 % @return self SolverCTMC instance configured for exact analysis
17
18 % Redirect a LayeredNetwork to the solvers that accept one. This
19 % must precede the NetworkSolver constructor: that constructor calls
20 % model.getAvgHandles(), which a LayeredNetwork does not implement,
21 % so the redirect that used to live in runAnalyzer was unreachable
22 % and the user saw "Unrecognized method 'getAvgHandles'" instead.
23 if isa(model, 'LayeredNetwork')
24 line_error(mfilename, ['This model is a LayeredNetwork. Use SolverLN ', ...
25 '(iterative) or SolverLQNS (external LQNS) instead of SolverCTMC.\n']);
26 end
27 self@NetworkSolver(model, mfilename);
28 self.setOptions(Solver.parseOptions(varargin, self.defaultOptions));
29 self.setLang();
30 end
31
32 runtime = runAnalyzer(self, options)
33 Pnir = getProb(self, node, state)
34 Pn = getProbSys(self)
35 Pnir = getProbAggr(self, ist)
36
37 Pn = getProbSysAggr(self)
38 [Pi_t, SSsysa] = getTranProbSysAggr(self)
39 [Pi_t, SSnode_a] = getTranProbAggr(self, node)
40 [Pi_t, SSsys] = getTranProbSys(self)
41 [Pi_t, SSnode] = getTranProb(self, node)
42 RD = getCdfRespT(self, R)
43 RD = getCdfSysRespT(self)
44
45 [stateSpace,nodeStateSpace] = getStateSpace(self, options)
46 stateSpaceAggr = getStateSpaceAggr(self)
47
48 % Reward computation methods
49 [Rt, t, names] = getTranReward(self, rewardName)
50 [R, names] = getAvgReward(self)
51 [V, t, names, stateSpace] = runRewardAnalyzer(self)
52
53 function [state_space, local_states] = stateSpace(self)
54 % STATESPACE Kotlin-style alias for getStateSpace
55 if nargout <= 1
56 state_space = self.getStateSpace();
57 else
58 [state_space, local_states] = self.getStateSpace();
59 end
60 end
61
62 function Q = generator(self)
63 % GENERATOR Kotlin-style alias for getGenerator
64 Q = self.getGenerator();
65 end
66
67 [infGen, eventFilt, synchInfo, stateSpace, nodeStateSpace] = getSymbolicGenerator(self, invertSymbol)
68 [infGen, eventFilt, synchInfo] = getInfGen(self, options)
69 [infGen, eventFilt, synchInfo] = getGenerator(self, options)
70
71 tstate = sampleSys(self, numevents)
72 sampleAggr = sampleAggr(self, node, numEvents)
73
74 function MCTMC = getMarkedCTMC(self, options)
75 % MCTMC = GETMARKEDCTMC(options)
76
77 if nargin < 2
78 [infGen, eventFilt, synchInfo] = self.getInfGen();
79 else
80 [infGen, eventFilt, synchInfo] = getInfGen(self, options);
81 end
82
83 MCTMC = MarkedMarkovProcess(infGen, eventFilt, synchInfo);
84 end
85
86 function sn = getStruct(self)
87 % QN = GETSTRUCT()
88
89 % Get data structure summarizing the model
90 sn = self.model.getStruct(true);
91 end
92
93
94 function [allMethods] = listValidMethods(self)
95 % allMethods = LISTVALIDMETHODS()
96 % List valid methods for this solver
97 sn = self.model.getStruct();
98 % QRF (Quadratic/Linear Reduction) LP-based bounds were moved to
99 % SolverBA (methods qr/lr/qrf.*); SolverCTMC no longer serves them.
100 allMethods = {'default','gpu'};
101 end
102 function featSupported = getMethodFeatureSet(self, method) %#ok<INUSD>
103 % All CTMC methods share the solver-level feature envelope.
104 %
105 % Defining this is what lets NetworkSolver.supportsModelMethod name
106 % the offending features: with no method feature set it falls back
107 % to the coarse supports(model), which returns an empty reason, so
108 % the gate could only report "features not supported" without
109 % saying which ones.
110 %
111 % A non-Network model (e.g. a LayeredNetwork) has no
112 % getUsedLangFeatures, so it keeps the coarse path and any
113 % structural checks or redirects that operate on such models.
114 if ~isa(self.model, 'Network')
115 featSupported = [];
116 return;
117 end
118 featSupported = SolverCTMC.getFeatureSet();
119 end
120
121
122 end
123
124 methods (Static)
125
126 function featSupported = getFeatureSet()
127 % FEATSUPPORTED = GETFEATURESET()
128
129 featSupported = SolverFeatureSet;
130 featSupported.setTrue({'Source','Sink',...
131 'ClassSwitch','Delay','DelayStation','Queue','Router',...
132 'MAP','APH','MMPP2','MMAP','PH','Coxian','Erlang','Exp','HyperExp',...
133 'Det','Gamma','Lognormal','Pareto','Uniform','Weibull',...
134 'StatelessClassSwitcher','InfiniteServer','SharedServer','Buffer','Dispatcher',...
135 ... % Finite capacity regions: CTMC represents them exactly (the
136 ... % blocked-job overflow buffer is part of the chain), verified by
137 ... % convergence to the simulators as the cutoff grows.
138 'Region', ...
139 'Cache','CacheClassSwitcher', ...
140 'CacheRetrieval', ...
141 'Server','JobSink','RandomSource','ServiceTunnel',...
142 'SchedStrategy_INF','SchedStrategy_PS',...
143 'SchedStrategy_DPS','SchedStrategy_GPS',...
144 'SchedStrategy_SIRO','SchedStrategy_SEPT',...
145 'SchedStrategy_LEPT','SchedStrategy_FCFS',...
146 'SchedStrategy_HOL','SchedStrategy_LCFS',...
147 'SchedStrategy_LCFSPR','SchedStrategy_LCFSPRPRIO','SchedStrategy_FCFSPRPRIO',...
148 'SchedStrategy_PSPRIO','SchedStrategy_DPSPRIO','SchedStrategy_GPSPRIO',...
149 'SchedStrategy_LPS',...
150 'SchedStrategy_PAS',...
151 'SchedStrategy_OI',...
152 'SchedStrategy_POLLING',...
153 'RoutingStrategy_RROBIN',...
154 'RoutingStrategy_WRROBIN',...
155 'RoutingStrategy_JSQ',...
156 'RoutingStrategy_SQ',...
157 'RoutingStrategy_PROB','RoutingStrategy_RAND',...
158 'ReplacementStrategy_RR', 'ReplacementStrategy_FIFO','ReplacementStrategy_SFIFO','ReplacementStrategy_LRU',...
159 'ReplacementStrategy_HLRU','ReplacementStrategy_CLIMB','ReplacementStrategy_QLRU',...
160 'ClosedClass','SelfLoopingClass','OpenClass','Replayer',...
161 'OpenSignal','ClosedSignal',...
162 'SignalType_NEGATIVE','SignalType_CATASTROPHE',...
163 'SignalBatchRemoval','SignalRemovalPolicy',...
164 'Place','Transition','Linkage','Enabling','Inhibiting','Timing','Firing','Storage',...
165 'Fork','Join','Forker','Joiner',...
166 'Balking','Reneging','Retrial','Breakdown',...
167 'LoadDependence','ClassDependence','JointDependence'});
168 end
169
170 function [bool, featSupported, featUsed] = supports(model)
171 % [BOOL, FEATSUPPORTED, FEATUSED] = SUPPORTS(MODEL)
172
173 featUsed = model.getUsedLangFeatures();
174 featSupported = SolverCTMC.getFeatureSet();
175 bool = SolverFeatureSet.supports(featSupported, featUsed);
176 end
177
178 function options = defaultOptions()
179 % OPTIONS = DEFAULTOPTIONS()
180 options = SolverOptions('CTMC');
181 end
182
183 function printInfGen(Q,SS)
184 % PRINTINFGEN(Q,SS)
185
186 SS=full(SS);
187 Q=full(Q);
188 for s=1:size(SS,1)
189 for sp=1:size(SS,1)
190 if Q(s,sp)>0
191 line_printf('\n%s->%s: %f',mat2str(SS(s,:)),mat2str(SS(sp,:)),double(Q(s,sp)));
192 end
193 end
194 end
195 line_printf('\n');
196 end
197
198 function printEventFilt(sync,D,SS,myevents)
199 % PRINTEVENTFILT(SYNC,D,SS,MYEVENTS)
200
201 if nargin<4 %~exist('events','var')
202 myevents = 1:length(sync);
203 end
204 SS=full(SS);
205 for e=myevents
206 D{e}=full(D{e});
207 for s=1:size(SS,1)
208 for sp=1:size(SS,1)
209 if D{e}(s,sp)>0
210 line_printf('\n%s-- %d: (%d,%d) => (%d,%d) -->%s: %f',mat2str(SS(s,:)),e,sync{e}.active{1}.node,sync{e}.active{1}.class,sync{e}.passive{1}.node,sync{e}.passive{1}.class,mat2str(SS(sp,:)),double(D{e}(s,sp)));
211 end
212 end
213 end
214 end
215 end
216
217 function libs = getLibrariesUsed(sn, options) %#ok<INUSD>
218 % GETLIBRARIESUSED Get list of external libraries used by CTMC solver
219 libs = {};
220 end
221
222 function backend = symbolicBackend(self)
223 % BACKEND = SYMBOLICBACKEND(SELF)
224 %
225 % Value of options.config.symbolic, or 'auto' when the solver
226 % carries no such option (an older options struct, or none at all).
227 backend = 'auto';
228 try
229 if isprop(self, 'options') && isfield(self.options, 'config') && ...
230 isfield(self.options.config, 'symbolic')
231 backend = self.options.config.symbolic;
232 end
233 catch
234 end
235 end
236
237 function entries = symbolicEntries(infGenTerms, symbols, invertSymbol, n)
238 % ENTRIES = SYMBOLICENTRIES(INFGENTERMS, SYMBOLS, INVERTSYMBOL, N)
239 %
240 % Renders the per-event coefficient matrices as expression strings,
241 % e.g. '2*x1 - 3*x2'. This is the wire form the symbolic backend
242 % consumes and the exact format the JAR's getSymbolicEntry emits,
243 % which is what lets the two be compared at all.
244 entries = cell(n, n);
245 for i = 1:n
246 for j = 1:n
247 s = '';
248 for e = 1:numel(symbols)
249 if isempty(symbols{e})
250 continue
251 end
252 c = infGenTerms{e}(i, j);
253 if c == 0
254 continue
255 end
256 if isempty(s)
257 if c < 0
258 s = '-';
259 end
260 else
261 if c < 0
262 s = [s, ' - ']; %#ok<AGROW>
263 else
264 s = [s, ' + ']; %#ok<AGROW>
265 end
266 end
267 absC = abs(c);
268 if invertSymbol
269 s = [s, SolverCTMC.symbolicCoeff(absC), '/', symbols{e}]; %#ok<AGROW>
270 else
271 if absC ~= 1
272 s = [s, SolverCTMC.symbolicCoeff(absC), '*']; %#ok<AGROW>
273 end
274 s = [s, symbols{e}]; %#ok<AGROW>
275 end
276 end
277 if isempty(s)
278 s = '0';
279 end
280 entries{i, j} = s;
281 end
282 end
283 end
284
285 function s = symbolicCoeff(c)
286 % S = SYMBOLICCOEFF(C) Coefficient as text the backend reads exactly.
287 if c == round(c) && abs(c) < 1e15
288 s = sprintf('%d', round(c));
289 else
290 s = sprintf('%.17g', c);
291 end
292 end
293 end
294end