1classdef SolverCTMC < NetworkSolver
2 % Continuous-Time Markov Chain solver
for exact state-space analysis
4 % Implements exact analysis of queueing networks via CTMC formulation.
6 % Copyright (c) 2012-2026, Imperial College London
10 function self = SolverCTMC(model,varargin)
11 % SOLVERCTMC Create a CTMC solver instance
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
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']);
27 self@NetworkSolver(model, mfilename);
28 self.setOptions(Solver.parseOptions(varargin, self.defaultOptions));
32 runtime = runAnalyzer(self, options)
33 Pnir = getProb(self, node, state)
35 Pnir = getProbAggr(self, ist)
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)
45 [stateSpace,nodeStateSpace] = getStateSpace(self, options)
46 stateSpaceAggr = getStateSpaceAggr(self)
48 % Reward computation methods
49 [Rt, t, names] = getTranReward(self, rewardName)
50 [R, names] = getAvgReward(self)
51 [V, t, names, stateSpace] = runRewardAnalyzer(self)
53 function [state_space, local_states] = stateSpace(self)
54 % STATESPACE Kotlin-style alias for getStateSpace
56 state_space = self.getStateSpace();
58 [state_space, local_states] = self.getStateSpace();
62 function Q = generator(self)
63 % GENERATOR Kotlin-style alias for getGenerator
64 Q = self.getGenerator();
67 [infGen, eventFilt, synchInfo, stateSpace, nodeStateSpace] = getSymbolicGenerator(self, invertSymbol)
68 [infGen, eventFilt, synchInfo] = getInfGen(self, options)
69 [infGen, eventFilt, synchInfo] = getGenerator(self, options)
71 tstate = sampleSys(self, numevents)
72 sampleAggr = sampleAggr(self, node, numEvents)
74 function MCTMC = getMarkedCTMC(self, options)
75 % MCTMC = GETMARKEDCTMC(options)
78 [infGen, eventFilt, synchInfo] = self.getInfGen();
80 [infGen, eventFilt, synchInfo] = getInfGen(self, options);
83 MCTMC = MarkedMarkovProcess(infGen, eventFilt, synchInfo);
86 function sn = getStruct(self)
89 % Get data structure summarizing the model
90 sn = self.model.getStruct(true);
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'};
102 function featSupported = getMethodFeatureSet(self, method) %#ok<INUSD>
103 % All CTMC methods share the solver-level feature envelope.
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
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')
118 featSupported = SolverCTMC.getFeatureSet();
126 function featSupported = getFeatureSet()
127 % FEATSUPPORTED = GETFEATURESET()
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.
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'});
170 function [bool, featSupported, featUsed] = supports(model)
171 % [BOOL, FEATSUPPORTED, FEATUSED] = SUPPORTS(MODEL)
173 featUsed = model.getUsedLangFeatures();
174 featSupported = SolverCTMC.getFeatureSet();
175 bool = SolverFeatureSet.supports(featSupported, featUsed);
178 function options = defaultOptions()
179 % OPTIONS = DEFAULTOPTIONS()
180 options = SolverOptions('CTMC');
183 function printInfGen(Q,SS)
191 line_printf('\n%s->%s: %f',mat2str(SS(s,:)),mat2str(SS(sp,:)),
double(Q(s,sp)));
198 function printEventFilt(sync,D,SS,myevents)
199 % PRINTEVENTFILT(SYNC,D,SS,MYEVENTS)
201 if nargin<4 %~exist('events','var')
202 myevents = 1:length(sync);
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)));
217 function libs = getLibrariesUsed(sn, options) %#ok<INUSD>
218 % GETLIBRARIESUSED Get list of external libraries used by CTMC solver
222 function backend = symbolicBackend(self)
223 % BACKEND = SYMBOLICBACKEND(SELF)
225 % Value of options.config.symbolic, or
'auto' when the solver
226 % carries no such option (an older options
struct, or none at all).
229 if isprop(self,
'options') && isfield(self.options,
'config') && ...
230 isfield(self.options.config,
'symbolic')
231 backend = self.options.config.symbolic;
237 function entries = symbolicEntries(infGenTerms, symbols, invertSymbol, n)
238 % ENTRIES = SYMBOLICENTRIES(INFGENTERMS, SYMBOLS, INVERTSYMBOL, N)
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);
248 for e = 1:numel(symbols)
249 if isempty(symbols{e})
252 c = infGenTerms{e}(i, j);
262 s = [s,
' - ']; %#ok<AGROW>
264 s = [s,
' + ']; %#ok<AGROW>
269 s = [s, SolverCTMC.symbolicCoeff(absC),
'/', symbols{e}]; %#ok<AGROW>
272 s = [s, SolverCTMC.symbolicCoeff(absC),
'*']; %#ok<AGROW>
274 s = [s, symbols{e}]; %#ok<AGROW>
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));
290 s = sprintf(
'%.17g', c);