1function runtime = runAnalyzer(self, options)
8 options = self.getOptions;
11if ~isinf(options.timespan(1)) && (options.timespan(1) == options.timespan(2))
12 line_warning(mfilename,
'%s: timespan is a single point, spacing by options.tol (%e).\n',mfilename, options.tol);
13 options.timespan(2) = options.timespan(1) + options.tol;
17self.runAnalyzerChecks(options);
18Solver.resetRandomGeneratorSeed(options.seed);
20% Show library attribution
if verbose and not yet shown
21if options.verbose ~= VerboseLevel.SILENT && ~GlobalConstants.isLibraryAttributionShown()
22 libs = SolverCTMC.getLibrariesUsed([], options);
24 line_printf(
'The solver will leverage %s.\n', strjoin(libs,
', '));
25 GlobalConstants.setLibraryAttributionShown(
true);
29if self.enableChecks && ~self.supports(self.model)
30 line_error(mfilename,
'This model contains features not supported by the solver.\n');
33% Inform user about reducible routing handling
35 [isErg, ergInfo] = self.model.isRoutingErgodic();
36 if ~isErg && ~isempty(ergInfo.absorbingStations)
37 absNames = strjoin(ergInfo.absorbingStations, ', ');
39 'Note: Model has reducible routing with absorbing stations: %s\n' ...
40 ' Results represent limiting/absorption probabilities.\n' ...
41 ' Use model.getReducibilityInfo() for detailed analysis.\n'], ...
47line_debug(options, 'CTMC: using lang=matlab');
49% Convert non-Markovian distributions to PH
50sn = sn_nonmarkov_toph(sn, options);
51line_debug(options, 'CTMC: converted non-Markovian distributions to PH (nstations=%d, nclasses=%d)', sn.nstations, sn.nclasses);
58 sizeEstimator = sizeEstimator + gammaln(1+NK(k)+M-1) - gammaln(1+M-1) - gammaln(1+NK(k)); % worst-case estimate of the state space
61if any(isinf(sn.njobs))
62 if isinf(options.cutoff)
63 line_warning(mfilename,sprintf('The model has open chains, it
is recommended to specify a finite cutoff value, e.g., SolverCTMC(model,''cutoff'',1).\n'));
64 self.options.cutoff= ceil(6000^(1/(M*K)));
65 options.cutoff= ceil(6000^(1/(M*K)));
66 line_debug(options, 'Open/mixed model: auto-setting cutoff=%d for %d stations, %d
classes', options.cutoff, M, K);
67 line_warning(mfilename,sprintf('Setting cutoff=%d.\n',self.options.cutoff));
69 % Mandatory truncation warning for open/mixed models
70 line_printf('CTMC solver using state space cutoff = %d for open/mixed model.\n', options.cutoff);
71 line_warning(mfilename,'State space truncation may cause inaccurate results. Consider varying cutoff to assess sensitivity.\n');
75 line_debug(options, 'State space size estimate: exp(%f), may be too large', sizeEstimator);
76 if ~isfield(options,'force') || options.force == false
77 % line_error(mfilename,'CTMC size may be too large to solve. Stopping SolverCTMC. Set options.force=true to bypass this control.');
78 line_error(mfilename,'CTMC size may be too large to solve. Stopping SolverCTMC. Set options.force=true or use SolverCTMC(...,''force'',true) to bypass this control.\n');
83% we compute all metrics anyway because CTMC has essentially
85if isinf(options.timespan(1))
86 if startsWith(options.method, 'qrf')
87 line_debug(options, 'Using QRF method for steady-state CTMC analysis');
88 [QN,UN,RN,TN,CN,XN,~] = solver_ctmc_qrf_analyzer(sn, options);
90 T = getAvgTputHandles(self);
91 AN = sn_get_arvr_from_tput(sn, TN, T);
92 self.setAvgResults(QN,UN,RN,TN,AN,[],CN,XN,runtime,options.method);
94 line_debug(options, 'Using standard CTMC method for steady-state analysis');
96 s0prior = sn.stateprior;
99 isf = sn.nodeToStateful(ind);
100 sn.state{isf} = s0{isf}(maxpos(s0prior{1}),:); % pick one particular initial state
103 [QN,UN,RN,TN,CN,XN,Q,SS,SSq,Dfilt,~,~,sn] = solver_ctmc_analyzer(sn, options);
104 % update initial state
if this has been corrected by the state space
106 for isf=1:sn.nstateful
107 ind = sn.statefulToNode(isf);
108 self.model.nodes{ind}.setState(sn.state{isf});
109 switch class(self.model.nodes{sn.statefulToNode(isf)})
111 self.model.nodes{sn.statefulToNode(isf)}.setResultHitProb(sn.nodeparam{ind}.actualhitprob);
112 self.model.nodes{sn.statefulToNode(isf)}.setResultMissProb(sn.nodeparam{ind}.actualmissprob);
113 self.model.refreshChains();
117 self.result.infGen = Q;
118 self.result.space = SS;
119 self.result.spaceAggr = SSq;
120 self.result.nodeSpace = sn.space;
121 self.result.eventFilt = Dfilt;
124 T = getAvgTputHandles(self);
125 AN=sn_get_arvr_from_tput(sn, TN, T);
126 self.setAvgResults(QN,UN,RN,TN,AN,[],CN,XN,runtime,options.method);
127 end %
if startsWith(options.method,
'qrf')
129 line_debug(options, 'Transient analysis: timespan=[%f,%f]', options.timespan(1), options.timespan(2));
132 s0prior = sn.stateprior;
134 s0_sz = cellfun(@(x) size(x,1), s0)';
135 s0_id = pprod(s0_sz-1);
136 cur_state = sn.state;
137 while s0_id>=0 % for all possible initial states
140 if sn.isstateful(ind)
141 isf = sn.nodeToStateful(ind);
142 s0prior_val = s0prior_val * s0prior{isf}(1+s0_id(isf)); % update prior
143 sn.state{isf} = s0{isf}(1+s0_id(isf),:); % assign initial state to network
147 [t,pit,QNt,UNt,~,TNt,~,~,Q,SS,SSq,Dfilt,runtime_t] = solver_ctmc_transient_analyzer(sn, options);
148 self.result.space = SS;
149 self.result.spaceAggr = SSq;
150 self.result.infGen = Q;
151 self.result.eventFilt = Dfilt;
153 setTranProb(self,t,pit,SS,runtime_t);
154 if isempty(self.result) || ~isfield(self.result,
'Tran') || ~isfield(self.result.Tran,
'Avg') || ~isfield(self.result.Tran.Avg,
'Q')
155 self.result.Tran.Avg.Q = cell(M,K);
156 self.result.Tran.Avg.U = cell(M,K);
157 self.result.Tran.Avg.T = cell(M,K);
160 self.result.Tran.Avg.Q{ist,r} = [QNt{ist,r} * s0prior_val,t];
161 self.result.Tran.Avg.U{ist,r} = [UNt{ist,r} * s0prior_val,t];
162 self.result.Tran.Avg.T{ist,r} = [TNt{ist,r} * s0prior_val,t];
168 tunion =
union(self.result.Tran.Avg.Q{ist,r}(:,2), t);
169 dataOld = interp1(self.result.Tran.Avg.Q{ist,r}(:,2),self.result.Tran.Avg.Q{ist,r}(:,1),tunion);
170 dataNew = interp1(t,QNt{ist,r},tunion);
171 self.result.Tran.Avg.Q{ist,r} = [dataOld+s0prior_val*dataNew,tunion];
172 dataOld = interp1(self.result.Tran.Avg.U{ist,r}(:,2),self.result.Tran.Avg.U{ist,r}(:,1),tunion);
173 dataNew = interp1(t,UNt{ist,r},tunion);
174 self.result.Tran.Avg.U{ist,r} = [dataOld+s0prior_val*dataNew,tunion];
176 dataOld = interp1(self.result.Tran.Avg.T{ist,r}(:,2),self.result.Tran.Avg.T{ist,r}(:,1),tunion);
177 dataNew = interp1(t,TNt{ist,r},tunion);
178 self.result.Tran.Avg.T{ist,r} = [dataOld+s0prior_val*dataNew,tunion];
183 s0_id=pprod(s0_id,s0_sz-1); % update initial state
185 % Now we restore the original state
187 if sn.isstateful(ind)
188 isf = sn.nodeToStateful(ind);
189 self.model.nodes{ind}.setState(cur_state{isf});
195 self.result.(
'solver') = getName(self);
196 self.result.runtime = runtime;
197 self.result.solverSpecific = lastSol;