LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Loading...
Searching...
No Matches
solver_ctmc_mdd_analyzer.h
Go to the documentation of this file.
1/*
2 * Copyright (c) 2012-2026, QORE Lab, Imperial College London
3 * All rights reserved.
4 */
5#ifndef LINE_SOLVERS_CTMC_SOLVER_CTMC_MDD_ANALYZER_H
6#define LINE_SOLVERS_CTMC_SOLVER_CTMC_MDD_ANALYZER_H
7
8/**
9 * @file
10 * @ingroup line_solvers
11 * The `mdd` method of SolverCTMC: stationary analysis of a closed single-class
12 * network whose state space is held in a decision diagram and solved by level
13 * aggregation.
14 *
15 * Port of matlab/src/solvers/CTMC/solver_ctmc_mdd_analyzer.m, after
16 * A.S. Miner, G. Ciardo, S. Donatelli, "Using the exact state space of a Markov
17 * model to compute approximate stationary measures", SIGMETRICS 2000.
18 *
19 * IT NEVER FORMS THE |S|-STATE GENERATOR, which is the whole point and also why
20 * it sits beside `solver_ctmc_analyzer` rather than inside it: the reachable set
21 * lives in an MDD and K coupled level-CTMCs are iterated to a fixed point, so
22 * the memory cost is O(sum_k |M_k|) rather than O(|S|). The saving grows with
23 * the number of stations and is NEGATIVE at K = 3, where the diagram compresses
24 * nothing.
25 *
26 * EXACTNESS. The single approximation is Pr{i_k | alpha} = Pr{i_k | p}. It is
27 * EXACT on product-form networks (paper Sec. 5), which covers exponential
28 * service under any work-conserving discipline and general service at PS or IS
29 * stations (BCMP types 2 and 3). It is an approximation otherwise, notably
30 * phase-type service at FCFS or LCFS, where errors of a fraction of a percent on
31 * the mean queue lengths have been observed. `no_aggregation` additionally
32 * certifies exactness STRUCTURALLY: when no diagram node is shared, conditioning
33 * on the node equals conditioning on the whole path and the approximation is an
34 * identity. False means "not certified", never "approximate" -- a product-form
35 * model is exact however much its diagram shares.
36 *
37 * WHICH ENCODING. Exponential service is discipline-insensitive for the
38 * queue-length law, so the compact count encoding of `mdd_descriptor` serves any
39 * work-conserving station. Phase-type service is not: `mdd_descriptor`'s
40 * count-plus-one-phase local state is NON-preemptive, while a shared server has
41 * every job present in service holding its own phase and needs the per-phase
42 * counts of `mdd_ps`. A model that mixes the two cases is refused rather than
43 * modelled under whichever encoding happens to be picked.
44 */
45
46#include <cmath>
47#include <cstddef>
48#include <string>
49#include <vector>
50
51#include "line/api/mdd/mdd.h"
54#include "line/api/mdd/mdd_ps.h"
60#include "line/num/number.h"
63#include "line/util/error.h"
64#include "line/util/matrix.h"
65
66namespace line {
67namespace ctmc {
68
69// NetworkStruct, NodeType and SchedStrategy are already in scope from
70// solver_ctmc.h; re-declaring them here would be a redundant using.
71
72/** What one `mdd` solve produces beside the means, i.e. the reference's INFO. */
73template <class T>
76 /** |S|, counted in the diagram without ever listing a state. */
77 long long num_states = 0;
78 /** |M_k| per paper level; their sum is what the diagram actually holds. */
79 std::vector<std::size_t> level_sizes;
80 /** Coupled fixed-point sweeps performed. */
81 int iters = 0;
82 /** max |A(p)| per paper level; 1 everywhere means no node is shared. */
83 std::vector<double> paths_per_level;
84 /** True certifies the answer is exact structurally; see the file header. */
85 bool no_aggregation = false;
86 /** Which local encoding was picked, "np" or "ps". */
87 std::string encoding;
88 std::string actualmethod = "mdd";
89};
90
91namespace mdd_analyzer_detail {
92
93/**
94 * Station-to-station routing of the single class, read off `sn.rt`.
95 *
96 * `rt` is indexed by STATEFUL node and class-major, so the row of station i is
97 * `(stateful_of_station(i) - 1) * R + 0` with R = 1 here. A row that does not
98 * sum to one means a completion can leave the network or stay put, neither of
99 * which the closed descriptor represents.
100 */
101template <class T>
102Matrix<T> mdd_station_routing(const NetworkStruct<T>& sn) {
103 const T zero = num_traits<T>::from_int(0);
104 const std::size_t M = sn.nstations, R = sn.nclasses;
105 Matrix<T> P(M, M, zero);
106 for (std::size_t i = 0; i < M; ++i) {
107 const std::size_t si = sn.stateful_of_station(i + 1) - 1;
108 for (std::size_t j = 0; j < M; ++j) {
109 const std::size_t sj = sn.stateful_of_station(j + 1) - 1;
110 P(i, j) = sn.rt(si * R, sj * R);
111 }
112 }
113 for (std::size_t i = 0; i < M; ++i) {
114 T s = zero;
115 for (std::size_t j = 0; j < M; ++j) s += P(i, j);
116 if (std::fabs(num_traits<T>::to_double(s) - 1.0) > 1e-8)
117 throw InputError(
118 "SolverCTMC(mdd): the station-to-station routing chain is not stochastic at "
119 "station " +
120 std::to_string(i + 1) +
121 "; the mdd method needs every completion to move the job to another station");
122 }
123 return P;
124}
125
126/** Disciplines under which every job present is in service, each with its own phase. */
127inline bool mdd_is_shared(SchedStrategy s) {
128 return s == SchedStrategy::PS || s == SchedStrategy::DPS || s == SchedStrategy::GPS ||
129 s == SchedStrategy::INF;
130}
131
132/** Disciplines the count-plus-one-phase local state represents, single server only. */
133inline bool mdd_is_nonpreemptive(SchedStrategy s, double servers) {
134 return (s == SchedStrategy::FCFS || s == SchedStrategy::LCFS || s == SchedStrategy::SIRO ||
135 s == SchedStrategy::HOL) &&
136 servers == 1.0;
137}
138
139
140/**
141 * The Petri-net route of the `mdd` method: `spn_mdd` supplies the reachable set
142 * and the Kronecker descriptor, `mdd_mcd` aggregates, and the measures are read
143 * back per place.
144 *
145 * The approximation is the same Eq. 5 as for a queueing network, and it is exact
146 * on a product-form net, which `solver_nc_spn_analyzer` solves exactly and far
147 * more cheaply -- the aggregation earns its place on the nets that have NO
148 * product form.
149 *
150 * A net carries no per-station service rate, so `mdd_mcd` returns only the level
151 * marginals. The token throughput is assembled here from the mode rates and
152 * those marginals, under the SAME independence across levels that the
153 * aggregation already assumes: it is the method's own approximation applied once
154 * more, not a second one layered on top. TN counts FIRING EVENTS, which is what
155 * the explicit CTMC path reports and what `sn_pn_avg_rates` converts to a token
156 * rate afterwards.
157 */
158template <class T>
159CtmcMddSolution<T> spn_route(const NetworkStruct<T>& sn, const CtmcOptions& opt,
160 const mdd::MddMcdOptions& mcdopt) {
161 const T zero = num_traits<T>::from_int(0);
162 const std::size_t M = sn.nstations, R = sn.nclasses;
163
164 spn::SpnOptions mddopt;
165 const spn::SpnResult<T> net = spn::spn_mdd<T>(sn, mddopt);
166 const mdd::MddMcdResult<T> out = mdd::mdd_mcd<T>(net.mdds, net.desc, mcdopt);
167 const std::size_t L = net.info.nplacelevels;
168
169 CtmcMddSolution<T> sol;
170 sol.avg.QN = Matrix<T>(M, R, zero);
171 sol.avg.UN = Matrix<T>(M, R, zero);
172 sol.avg.RN = Matrix<T>(M, R, zero);
173 sol.avg.TN = Matrix<T>(M, R, zero);
174 sol.avg.XN.assign(R, zero);
175 sol.avg.CN.assign(R, zero);
176 sol.avg.StartN = Matrix<T>(M, R, zero);
177 sol.avg.PreemptN = Matrix<T>(M, R, zero);
178
179 // place levels are place-major, l = pp * R + k (spn_mdd)
180 const std::size_t Rl = net.info.nclasses > 0 ? net.info.nclasses : 1;
181 for (std::size_t l = 0; l < L; ++l) {
182 const std::size_t pp = l / Rl, k = l % Rl;
183 const std::size_t ist = sn.nodes[net.info.places[pp] - 1].station;
184 if (ist < 1) continue;
185 sol.avg.QN(ist - 1, k) = out.QLen[l];
186 sol.avg.UN(ist - 1, k) = out.QLen[l]; // a Place is an INF station: U = Q
187 }
188
189 // P(level l = v) from the converged level chains; mdd_mcd works in the
190 // paper's orientation, paper level k = level K+1-l.
191 std::vector<std::vector<T>> pl(L);
192 for (std::size_t l = 0; l < L; ++l) {
193 const std::size_t k = net.mdds.K - 1 - l;
194 pl[l].assign(net.mdds.domain[l], zero);
195 for (std::size_t r = 0; r < out.Mrows[k].size(); ++r)
196 pl[l][out.Mrows[k][r].second] += out.pik[k][r];
197 T tot = zero;
198 for (std::size_t v = 0; v < pl[l].size(); ++v) tot += pl[l][v];
199 if (tot > zero)
200 for (std::size_t v = 0; v < pl[l].size(); ++v) pl[l][v] = T(pl[l][v] / tot);
201 }
202
203 for (std::size_t e = 0; e < net.info.modes.size(); ++e) {
204 const spn::SpnMode<T>& mde = net.info.modes[e];
205 if (mde.nph > 1) continue; // no single rate; read the phase level
206 // E[min(enabling degree, servers)] under independence across the inputs
207 std::vector<std::size_t> lv;
208 for (std::size_t l = 0; l < L; ++l)
209 if (mde.enab[l] > 0) lv.push_back(l);
210 T nsrv = num_traits<T>::from_int(1);
211 if (!lv.empty()) {
212 double kmax = std::numeric_limits<double>::infinity();
213 for (std::size_t i = 0; i < lv.size(); ++i)
214 kmax = std::min(kmax, std::floor((pl[lv[i]].size() - 1) / mde.enab[lv[i]]));
215 if (std::isfinite(mde.srv)) kmax = std::min(kmax, mde.srv);
216 nsrv = zero;
217 for (int k = 1; k <= static_cast<int>(kmax); ++k) {
218 T ge = num_traits<T>::from_int(1);
219 for (std::size_t i = 0; i < lv.size(); ++i) {
220 const std::size_t l = lv[i];
221 const std::size_t thr = static_cast<std::size_t>(k * mde.enab[l]);
222 if (thr >= pl[l].size()) {
223 ge = zero;
224 break;
225 }
226 T s = zero;
227 for (std::size_t v = thr; v < pl[l].size(); ++v) s += pl[l][v];
228 ge = T(ge * s);
229 }
230 nsrv += ge; // E[min] = sum_k P(min >= k)
231 }
232 }
233 const T x = T(mde.D1(0, 0) * nsrv);
234 for (std::size_t l = 0; l < L; ++l) {
235 if (mde.enab[l] <= 0) continue;
236 const std::size_t ist = sn.nodes[net.info.places[l / Rl] - 1].station;
237 if (ist >= 1) sol.avg.TN(ist - 1, l % Rl) = T(sol.avg.TN(ist - 1, l % Rl) + x);
238 }
239 }
240 for (std::size_t i = 0; i < M; ++i)
241 for (std::size_t k = 0; k < R; ++k)
242 if (sol.avg.TN(i, k) > zero)
243 sol.avg.RN(i, k) = T(sol.avg.QN(i, k) / sol.avg.TN(i, k));
244
245 const std::size_t ref = sn.classes.empty() ? 0 : sn.classes[0].refstat;
246 if (ref >= 1 && ref <= M) sol.avg.XN[0] = sol.avg.TN(ref - 1, 0);
247 T Nk = zero;
248 for (std::size_t i = 0; i < M; ++i) Nk += sol.avg.QN(i, 0);
249 if (sol.avg.XN[0] > zero && Nk > zero) sol.avg.CN[0] = T(Nk / sol.avg.XN[0]);
250
251 sol.num_states = static_cast<long long>(net.info.diagram.cardinality());
252 sol.level_sizes = out.level_sizes;
253 sol.iters = out.iters;
254 sol.paths_per_level = out.paths_per_level;
255 sol.no_aggregation = out.no_aggregation;
256 sol.encoding = "spn";
257 return sol;
258}
259
260} // namespace mdd_analyzer_detail
261
262/**
263 * Can the `mdd` decision-diagram method be asked for this model?
264 *
265 * The model-shape gate asked as a predicate rather than thrown.
266 * `solver_ctmc_mdd_analyzer` refuses with it before it builds anything, and a
267 * REPORT reaches the same call, so a pair the report offers is a pair the
268 * method runs. One predicate with two callers is what stops the two from
269 * disagreeing about which models the method serves.
270 *
271 * A STOCHASTIC PETRI NET IS EXEMPT: a Place model is read through
272 * `spn_route`, which builds the reachable set and the Kronecker descriptor from
273 * the marking rather than from the (station,class) encoding, so neither the
274 * single-class rule nor the closed-population rule applies to it.
275 *
276 * The deeper refusals the analyzer still raises -- a station-to-station chain
277 * that is not stochastic, and a phase-type law at a discipline neither local
278 * encoding represents -- are not restated here: they are decided from
279 * quantities the analyzer computes on its way through, not from the model
280 * shape, so a caller cannot be told about them without doing the work.
281 *
282 * @param sn the refreshed struct of the model
283 * @return an empty string when the method may run, else the refusal
284 */
285template <class T>
287 for (std::size_t ind = 0; ind < sn.nodes.size(); ++ind)
288 if (sn.nodes[ind].nodetype == NodeType::Place) return std::string();
289 // A FORK-JOIN MODEL IS NEITHER of the two shapes this method serves. The
290 // tag augmentation a fork needs adds one auxiliary class per branch, so the
291 // struct that reaches the analyzer is never single-class however the model
292 // was written, and the level decomposition has no meaning for a firing that
293 // does not conserve the per-chain population. Also stated in
294 // `qn::ctmc_feature_set("mdd")`, which drops the Fork/Join names.
295 for (std::size_t ind = 0; ind < sn.nodes.size(); ++ind)
296 if (sn.nodes[ind].nodetype == NodeType::Fork || sn.nodes[ind].nodetype == NodeType::Join)
297 return "SolverCTMC(mdd): the mdd method analyses closed single-class networks and "
298 "stochastic Petri nets; a fork-join model is neither, and its tag "
299 "augmentation adds one auxiliary class per branch";
300 if (sn.nclasses != 1)
301 return "SolverCTMC(mdd): the mdd method analyses single-class networks; this model has " +
302 std::to_string(sn.nclasses) +
303 " classes. The Kronecker descriptor would need one level per (station,class)";
304 for (std::size_t ind = 0; ind < sn.nodes.size(); ++ind)
305 if (sn.nodes[ind].nodetype == NodeType::Source || sn.nodes[ind].nodetype == NodeType::Sink)
306 return "SolverCTMC(mdd): the mdd method analyses CLOSED networks; an open stream "
307 "makes the marking unbounded, so the reachable set has no finite decision "
308 "diagram";
309 const std::vector<double> njobs_gate = sn.njobs();
310 if (!std::isfinite(njobs_gate[0]) || njobs_gate[0] <= 0.0)
311 return "SolverCTMC(mdd): the mdd method needs a finite positive closed population";
312 return std::string();
313}
314
315/**
316 * Solve with the `mdd` method.
317 *
318 * @param sn the refreshed struct of a CLOSED single-class network
319 * @param opt the SolverCTMC knobs; only `method` is read, the state-space ones
320 * having no meaning for a solve that enumerates nothing
321 * @param mcdopt the level-iteration knobs, an INNER numerical solve whose
322 * tolerance must stay far tighter than any solver-level `iter_tol`
323 */
324template <class T>
326 const mdd::MddMcdOptions& mcdopt =
328 // EVERY STEP HERE IS A FIELD OPERATION, including the level solve.
329 // `mcd_solve_stat` picks its backend by the arithmetic: Householder QR in
330 // floating point, where the reflector norms are square roots and the point
331 // is to avoid squaring the condition number, and `line::lstsq` under exact
332 // arithmetic, where there is no condition number to square. So `--method
333 // mdd` runs under `--arith exact` like `--method default`, and returns the
334 // exact rational fixed point of the level system. The one thing exactness
335 // does NOT buy is agreement with the enumerated chain: the level
336 // aggregation is an approximation away from product form whatever the
337 // arithmetic, and `no_aggregation` is what certifies otherwise.
338 const T zero = num_traits<T>::from_int(0);
339 const std::size_t M = sn.nstations, R = sn.nclasses;
340
341 // The model-shape rules live in `solver_ctmc_mdd_supports`, which a report
342 // asks too: the analyzer must refuse exactly what the report refuses, and
343 // one predicate with two callers is what keeps the two from drifting apart.
344 // All three come back as one exception type now; the population case used to
345 // be an InputError, and no catch site anywhere distinguishes the two.
346 const std::string shape_reason = solver_ctmc_mdd_supports(sn);
347 if (!shape_reason.empty()) throw UnsupportedError(ErrorKind{"Model"}, shape_reason);
348
349 // A net holding Places takes the SPN translation instead of mdd_descriptor:
350 // the levels are places, the measures come back per place, and the same Eq. 5
351 // approximation applies. See mdd_analyzer_detail::spn_route.
352 for (std::size_t ind = 0; ind < sn.nodes.size(); ++ind)
353 if (sn.nodes[ind].nodetype == NodeType::Place)
354 return mdd_analyzer_detail::spn_route<T>(sn, opt, mcdopt);
355
356 const std::vector<double> njobs = sn.njobs();
357 const int N = static_cast<int>(njobs[0]);
358
359 // ---- service laws: the rate by LINE convention, the (D0,D1) pair when the
360 // station is phase-type. A one-phase entry stays exponential so that the
361 // descriptor takes its compact index = population encoding.
362 std::vector<T> mu(M, zero);
363 std::vector<double> servers(M, 1.0);
364 std::vector<mdd::MddServiceLaw<T>> proc(M);
365 std::vector<SchedStrategy> sched(M, SchedStrategy::FCFS);
366 bool any_ph = false;
367 for (std::size_t i = 0; i < M; ++i) {
368 mu[i] = sn.rates(i, 0);
369 servers[i] = sn.stations[i].nservers;
370 sched[i] = sn.stations[i].sched;
371 const lang::Distrib<T>& d = sn.service[i][0];
372 if (d.phases() > 1 && d.D0.rows() > 0) {
373 proc[i].D0 = d.D0;
374 proc[i].D1 = d.D1;
375 proc[i].present = true;
376 any_ph = true;
377 }
378 }
379
380 // ---- which local encoding represents these disciplines exactly
381 std::string encoding = "np";
382 if (any_ph) {
383 bool all_shared = true;
384 for (std::size_t i = 0; i < M; ++i)
385 if (!mdd_analyzer_detail::mdd_is_shared(sched[i])) all_shared = false;
386 if (all_shared) {
387 encoding = "ps";
388 } else {
389 // Every PHASE-TYPE station must be non-preemptive for the compact
390 // encoding; a shared-server one among them is the mixed case, which
391 // no single encoding covers.
392 bool all_np_at_ph = true;
393 std::size_t first_ph = M, bad = M;
394 for (std::size_t i = 0; i < M; ++i) {
395 if (!proc[i].present) continue;
396 if (first_ph == M) first_ph = i;
397 if (mdd_analyzer_detail::mdd_is_nonpreemptive(sched[i], servers[i])) continue;
398 all_np_at_ph = false;
399 if (bad == M && !mdd_analyzer_detail::mdd_is_shared(sched[i])) bad = i;
400 }
401 if (!all_np_at_ph) {
402 if (bad == M) bad = first_ph;
403 throw UnsupportedError(ErrorKind{"SchedStrategy"},
404 "SolverCTMC(mdd): station " + std::to_string(bad + 1) +
405 " combines a phase-type service law with a discipline that neither local "
406 "encoding represents: the count-plus-phase encoding is non-preemptive, and "
407 "the per-phase-count encoding covers only shared servers (PS/DPS/GPS/INF). "
408 "Mixing a shared and a non-preemptive phase-type station in one model is "
409 "likewise unsupported");
410 }
411 }
412 }
413
414 const Matrix<T> P = mdd_analyzer_detail::mdd_station_routing(sn);
415
416 // ---- descriptor, reachable set, level aggregation
418 if (encoding == "ps") {
419 desc = mdd::mdd_ps(mu, P, servers, N, proc);
420 } else {
421 std::vector<std::string> schedname(M);
422 for (std::size_t i = 0; i < M; ++i) schedname[i] = lang::sched_to_text(sched[i]);
423 desc = mdd::mdd_descriptor(mu, P, servers, N, proc, schedname);
424 }
425 const mdd::MDD diagram = mdd::mdd_reachset(desc.domain, desc.init, desc.nextfun);
426 const mdd::MddMcdResult<T> out = mdd::mdd_mcd(diagram.to_struct(), desc, mcdopt);
427
428 // ---- pack the analyzer contract
430 s.avg.QN = Matrix<T>(M, R, zero);
431 s.avg.UN = Matrix<T>(M, R, zero);
432 s.avg.RN = Matrix<T>(M, R, zero);
433 s.avg.TN = Matrix<T>(M, R, zero);
434 s.avg.XN.assign(R, zero);
435 s.avg.CN.assign(R, zero);
436 for (std::size_t i = 0; i < M; ++i) {
437 s.avg.QN(i, 0) = out.QLen[i];
438 s.avg.UN(i, 0) = out.U[i];
439 s.avg.TN(i, 0) = out.X[i];
440 if (out.X[i] > zero) s.avg.RN(i, 0) = T(out.QLen[i] / out.X[i]); // Little's law
441 }
442
443 // System throughput at the reference station, per unit visit. `visits` is
444 // indexed by STATEFUL node, so the station index has to be converted; a
445 // model with a Router has more nodes than stations and reading the station
446 // index straight into that array would charge the wrong row.
447 const std::size_t ref = sn.classes[0].refstat;
448 T vis = num_traits<T>::from_int(1);
449 if (!sn.visits.empty() && sn.visits[0].rows() > 0) {
450 const std::size_t rsf = sn.stateful_of_station(ref) - 1;
451 if (sn.visits[0](rsf, 0) > zero) vis = sn.visits[0](rsf, 0);
452 }
453 s.avg.XN[0] = T(s.avg.TN(ref - 1, 0) / vis);
454 if (s.avg.XN[0] > zero) s.avg.CN[0] = T(num_traits<T>::from_int(N) / s.avg.XN[0]);
455
456 s.num_states = diagram.cardinality();
457 s.level_sizes = out.level_sizes;
458 s.iters = out.iters;
461 s.encoding = encoding;
462 s.actualmethod = opt.method.empty() ? std::string("mdd") : opt.method;
463 return s;
464}
465
466/** Solve with `mdd` and format, so a caller with no use for the diagram has one call. */
467template <class T>
476
477} // namespace ctmc
478} // namespace line
479
480#endif // LINE_SOLVERS_CTMC_SOLVER_CTMC_MDD_ANALYZER_H
InputError(const std::string &what)
Definition error.h:105
UnsupportedError(const std::string &what)
Definition error.h:119
The diagram: insert / member / index / enumerate / cardinality.
Definition mdd.h:97
long long cardinality() const
|S|, the number of stored states.
Definition mdd.h:145
MddStruct to_struct() const
Export the diagram as plain arrays for downstream algorithms.
Definition mdd.h:181
A network plus its refreshed NetworkStruct.
The exception types the port throws.
Enumerations and the minimal distribution descriptor shared by the model layer of the C++ port.
Dense matrix and non-owning view.
Quasi-reduced ordered Multi-valued Decision Diagram.
Kronecker rate descriptor of a single-class closed queueing network.
Miner-Ciardo-Donatelli approximate stationary analysis.
Kronecker rate descriptor for shared-server stations with phase-type service.
Reachability set generation into a decision diagram.
The rate side of the decision-diagram domain: local matrices, events, the Kronecker descriptor,...
std::string solver_ctmc_mdd_supports(const NetworkStruct< T > &sn)
Can the mdd decision-diagram method be asked for this model?
mva::AvgResult< T > solver_ctmc_avg_table(const NetworkStruct< T > &sn, const CtmcSolution< T > &d, const std::string &method)
Port of @@SolverCTMC/runAnalyzer.m's result assembly: solve, then apply the metric filter @@NetworkSo...
CtmcMddSolution< T > solver_ctmc_mdd_analyzer(const NetworkStruct< T > &sn, const CtmcOptions &opt, const mdd::MddMcdOptions &mcdopt=mdd::MddMcdOptions())
Solve with the mdd method.
mva::AvgResult< T > solver_ctmc_mdd_run_analyzer(const NetworkStruct< T > &sn, const CtmcOptions &opt, const mdd::MddMcdOptions &mcdopt=mdd::MddMcdOptions())
Solve with mdd and format, so a caller with no use for the diagram has one call.
SchedStrategy
Scheduling disciplines, with the values of MATLAB SchedStrategy.
Definition lang_types.h:185
const char * sched_to_text(SchedStrategy s)
Definition lang_types.h:235
double tot(const std::vector< double > &pops)
MddDescriptor< T > mdd_descriptor(const std::vector< T > &mu, const Matrix< T > &P, const std::vector< double > &servers, int N, const std::vector< MddServiceLaw< T > > &proc=std::vector< MddServiceLaw< T > >(), const std::vector< std::string > &sched=std::vector< std::string >())
Build the descriptor.
MDD mdd_reachset(const std::vector< int > &domain, const std::vector< int > &init, const MddNextState &nextfun)
Generate and store the reachability set into a quasi-reduced ordered MDD.
MddDescriptor< T > mdd_ps(const std::vector< T > &mu, const Matrix< T > &P, const std::vector< double > &servers, int N, const std::vector< MddServiceLaw< T > > &proc=std::vector< MddServiceLaw< T > >())
Build the descriptor.
Definition mdd_ps.h:242
MddMcdResult< T > mdd_mcd(const MddStruct &mdds, const MddDescriptor< T > &desc, const MddMcdOptions &options=MddMcdOptions())
Approximate stationary measures by decision-diagram-guided aggregation.
Definition mdd_mcd.h:384
SpnResult< T > spn_mdd(const qn::NetworkStruct< T > &sn, const SpnOptions &options=SpnOptions())
Build the reachable set and Kronecker descriptor of a stochastic Petri net.
Definition spn_mdd.h:403
Conservation laws of a layered queueing network, enumerated from its structure.
Definition aoi_dist2ph.h:52
A queueing network and its refreshed NetworkStruct.
Number-type abstraction for the templated API port.
Port of solver_ctmc.m: the infinitesimal generator of a queueing network, assembled from the enumerat...
Port of solver_ctmc_analyzer.m and the parts of @@SolverCTMC/runAnalyzer.m that surround one solve: t...
Decision-diagram reachable set and Kronecker rate descriptor of a stochastic Petri net,...
The <kind> part of a LINE:<category>:<kind> identifier (see _kb/17-error-vocabulary....
Definition error.h:37
The mean performance metrics a stationary vector maps to.
What one mdd solve produces beside the means, i.e.
long long num_states
|S|, counted in the diagram without ever listing a state.
int iters
Coupled fixed-point sweeps performed.
bool no_aggregation
True certifies the answer is exact structurally; see the file header.
std::vector< double > paths_per_level
max |A(p)| per paper level; 1 everywhere means no node is shared.
std::string encoding
Which local encoding was picked, "np" or "ps".
std::vector< std::size_t > level_sizes
|M_k| per paper level; their sum is what the diagram actually holds.
The SolverCTMC knobs this port honours.
Everything one CTMC solve produces.
Matrix< T > D0
The (D0,D1) pair when the type carries one directly.
Definition lang_types.h:863
std::size_t phases() const
The order of the representation, MATLAB's sn.phases.
Kronecker rate descriptor of a structured model, the input of mdd_mcd.
Definition mdd_types.h:165
MddNextState nextfun
Successor function over local indices.
Definition mdd_types.h:191
std::vector< int > domain
Local domain per level.
Definition mdd_types.h:171
std::vector< int > init
Initial local index per level.
Definition mdd_types.h:189
Knobs of the level iteration in mdd_mcd.
Definition mdd_types.h:212
Result of the Miner-Ciardo-Donatelli level aggregation.
Definition mdd_types.h:229
std::vector< std::size_t > level_sizes
|M_k| per paper level.
Definition mdd_types.h:241
bool no_aggregation
True certifies the result is EXACT with no reference solve needed; false means "not certified by this...
Definition mdd_types.h:255
int iters
Fixed-point iterations performed.
Definition mdd_types.h:243
std::vector< T > QLen
Mean occupancy per station (or place), in station order.
Definition mdd_types.h:231
std::vector< double > paths_per_level
max |A(p)| per paper level: the largest number of distinct root-to-node paths at that level.
Definition mdd_types.h:249
std::vector< T > X
Per-station throughput; empty when the descriptor carries no queueing parameters.
Definition mdd_types.h:233
std::vector< T > U
Per-station utilization; empty when the descriptor carries no queueing parameters.
Definition mdd_types.h:235
The metrics getAvg returns, after filtering.