LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Loading...
Searching...
No Matches
spn_ispn.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_API_SPN_SPN_ISPN_H
6#define LINE_API_SPN_SPN_ISPN_H
7
8/**
9 * @file
10 * @ingroup api_spn
11 * Infinite-state stochastic Petri nets (iSPN) by the matrix-geometric method.
12 *
13 * A net whose single unbounded place P0 is the level of a quasi-birth-death
14 * process (B. R. Haverkort, "Performance of Computer Communication Systems: A
15 * Model-Based Approach", Wiley, 1998, ch. 17; B. R. Haverkort, A. Ost, "Steady-
16 * state analysis of infinite stochastic Petri nets: comparing the spectral
17 * expansion and the matrix-geometric method", PNPM 1997, 36-45).
18 *
19 * The iSPN requirements of Haverkort sec. 17.2 are checked, and a violation is
20 * refused with `LINE:unsupported:Structure` naming the condition: every firing
21 * moves P0 by at most one token (also across a chain of immediate firings),
22 * every other place is bounded, and above a threshold level K the levels repeat
23 * with the same phase markings and level-independent rates. The threshold is
24 * found by exploring a window of levels and comparing the generator blocks.
25 *
26 * Vanishing markings of immediate transitions are eliminated as
27 * Q = Q_TT + Q_TV (I - P_VV)^-1 P_VT. R solves A0 + R A1 + R^2 A2 = 0 by
28 * logarithmic reduction (G. Latouche, V. Ramaswami, J. Appl. Probab. 30(3),
29 * 1993) or successive substitution, the drift condition pi A0 1 < pi A2 1
30 * (Haverkort (17.7)) is checked, and the boundary levels 0..K solve x B = 0
31 * with the R A2 correction and the normalisation x_K (I-R)^-1 1 = 1 - sum.
32 *
33 * NO TRUNCATION: a measure r of the marking that is affine in the level above
34 * K, r(K+j) = a + (j-1) b for j >= 1, has the closed form
35 * E[r] = sum_{i<=K} x_i r_i + x_K R N a + x_K R^2 N^2 b, N = (I-R)^-1,
36 * which covers the mean marking of every place, the marginals of the bounded
37 * places, the firing rates and the user rewards. A measure that is not affine
38 * is refused rather than truncated.
39 *
40 * DOUBLE ONLY: R is a fixed point of a quadratic matrix equation, irrational in
41 * general, and the stability test needs the spectral radius of R.
42 *
43 * Indices are 0-based: p0 is the row of the unbounded place in pre/post.
44 * MATLAB twin: matlab/src/api/spn/spn_ispn.m (1-based P0); Java twin:
45 * jline.api.spn.Spn_ispn; Python twin: line_solver.api.spn.ispn.
46 */
47
48#include <algorithm>
49#include <cctype>
50#include <cmath>
51#include <cstddef>
52#include <cstdio>
53#include <functional>
54#include <limits>
55#include <map>
56#include <string>
57#include <vector>
58
59#include "line/api/mam/qbd_r.h"
60#include "line/util/eig.h"
61#include "line/util/error.h"
62#include "line/util/matrix.h"
63
64namespace line {
65namespace spn {
66
67/** Options of spn_ispn. */
69 int extra = 3; ///< levels explored above the structural threshold (at least 3)
70 int maxlevel = 200; ///< largest explored window before the net is refused
71 std::size_t maxstates = 200000; ///< largest number of markings explored
72 double tol = 1e-9; ///< tolerance of the block-repetition and affinity tests
73 double distmax = 1e5; ///< largest level of the reported distribution of P0
74 /** reward functions of the marking (length P), affine in the level above K */
75 std::vector<std::function<double(const std::vector<double>&)>> rewards;
76 std::string qbdmethod = "logred"; ///< "logred" or "successive"
77};
78
79/** Result of spn_ispn; fields as in the MATLAB help. */
80struct IspnResult {
81 int K = 0; ///< first repeating level
82 std::size_t L = 0; ///< phases per level
83 std::vector<std::vector<double>> A0, A1, A2, R, B;
84 std::vector<std::vector<double>> x; ///< stationary vectors of levels 0..K
85 std::vector<std::vector<std::vector<double>>> levelmarkings; ///< P x n markings of levels 0..K
86 std::vector<double> drift; ///< {up, down} mean drift above K
87 double spR = 0;
88 std::vector<double> meanmarking;
89 double varlevel = 0;
90 std::vector<double> leveldist;
91 double leveltail = 0;
92 std::vector<std::vector<double>> marginal; ///< per place (P0: leveldist)
93 std::vector<double> tput;
94 std::vector<double> reward;
95 std::size_t ntangible = 0;
96 std::size_t nvanishing = 0;
97};
98
99namespace ispn_detail {
100
101typedef std::vector<double> Vec;
102typedef std::vector<Vec> Mat;
103
104inline std::string fmt(const char* f, double a = 0, double b = 0, double c = 0, double d = 0) {
105 char buf[512];
106 std::snprintf(buf, sizeof buf, f, a, b, c, d);
107 return std::string(buf);
108}
109
110[[noreturn]] inline void refuse(const std::string& m) {
111 throw UnsupportedError(ErrorKind{"Structure"}, "spn_ispn: " + m);
112}
113[[noreturn]] inline void unstable(const std::string& m) {
114 throw NumericError(ErrorKind{"Instability"}, "spn_ispn: " + m);
115}
116[[noreturn]] inline void badarg(const std::string& m) {
117 throw InputError(ErrorKind{"Argument"}, "spn_ispn: " + m);
118}
119
120inline Mat zeros(std::size_t n, std::size_t m) { return Mat(n, Vec(m, 0.0)); }
121inline Mat eye(std::size_t n) {
122 Mat I = zeros(n, n);
123 for (std::size_t i = 0; i < n; ++i) I[i][i] = 1.0;
124 return I;
125}
126inline std::size_t ncols(const Mat& A) { return A.empty() ? 0 : A[0].size(); }
127inline Mat sub(const Mat& A, const std::vector<std::size_t>& r, const std::vector<std::size_t>& c) {
128 Mat B = zeros(r.size(), c.size());
129 for (std::size_t i = 0; i < r.size(); ++i)
130 for (std::size_t j = 0; j < c.size(); ++j) B[i][j] = A[r[i]][c[j]];
131 return B;
132}
133inline bool all_zero(const Mat& A) {
134 for (const Vec& r : A)
135 for (double v : r)
136 if (v != 0) return false;
137 return true;
138}
139inline double max_abs(const Vec& a) {
140 double m = 0;
141 for (double v : a) m = std::max(m, std::fabs(v));
142 return m;
143}
144inline double max_abs(const Mat& A) {
145 double m = 0;
146 for (const Vec& r : A) m = std::max(m, max_abs(r));
147 return m;
148}
149inline double max_abs_diff(const Mat& A, const Mat& B) {
150 double m = 0;
151 for (std::size_t i = 0; i < A.size(); ++i)
152 for (std::size_t j = 0; j < A[i].size(); ++j) m = std::max(m, std::fabs(A[i][j] - B[i][j]));
153 return m;
154}
155inline Mat plus(const Mat& A, const Mat& B) {
156 Mat C = A;
157 for (std::size_t i = 0; i < A.size(); ++i)
158 for (std::size_t j = 0; j < A[i].size(); ++j) C[i][j] += B[i][j];
159 return C;
160}
161inline Mat minus(const Mat& A, const Mat& B) {
162 Mat C = A;
163 for (std::size_t i = 0; i < A.size(); ++i)
164 for (std::size_t j = 0; j < A[i].size(); ++j) C[i][j] -= B[i][j];
165 return C;
166}
167inline Mat scale(const Mat& A, double s) {
168 Mat C = A;
169 for (Vec& r : C)
170 for (double& v : r) v *= s;
171 return C;
172}
173inline Mat mult(const Mat& A, const Mat& B) {
174 const std::size_t n = A.size(), m = B.size(), p = ncols(B);
175 Mat C = zeros(n, p);
176 for (std::size_t i = 0; i < n; ++i)
177 for (std::size_t k = 0; k < m; ++k) {
178 const double a = A[i][k];
179 if (a == 0) continue;
180 for (std::size_t j = 0; j < p; ++j) C[i][j] += a * B[k][j];
181 }
182 return C;
183}
184inline Vec vec_mult(const Vec& x, const Mat& A) {
185 const std::size_t p = ncols(A);
186 Vec y(p, 0.0);
187 for (std::size_t k = 0; k < x.size(); ++k)
188 for (std::size_t j = 0; j < p; ++j) y[j] += x[k] * A[k][j];
189 return y;
190}
191inline double sum(const Vec& a) {
192 double s = 0;
193 for (double v : a) s += v;
194 return s;
195}
196inline Vec row_sums(const Mat& A) {
197 Vec s(A.size());
198 for (std::size_t i = 0; i < A.size(); ++i) s[i] = sum(A[i]);
199 return s;
200}
201inline double dot(const Vec& a, const Vec& b) {
202 double s = 0;
203 for (std::size_t i = 0; i < a.size(); ++i) s += a[i] * b[i];
204 return s;
205}
206
207/** LU with partial pivoting in place; returns the row permutation. */
208inline std::vector<std::size_t> lu(Mat& A) {
209 const std::size_t n = A.size();
210 std::vector<std::size_t> piv(n);
211 for (std::size_t i = 0; i < n; ++i) piv[i] = i;
212 for (std::size_t k = 0; k < n; ++k) {
213 std::size_t p = k;
214 for (std::size_t i = k + 1; i < n; ++i)
215 if (std::fabs(A[i][k]) > std::fabs(A[p][k])) p = i;
216 if (p != k) {
217 std::swap(A[p], A[k]);
218 std::swap(piv[p], piv[k]);
219 }
220 const double d = A[k][k];
221 if (d == 0) continue;
222 for (std::size_t i = k + 1; i < n; ++i) {
223 const double f = A[i][k] / d;
224 A[i][k] = f;
225 if (f == 0) continue;
226 for (std::size_t j = k + 1; j < n; ++j) A[i][j] -= f * A[k][j];
227 }
228 }
229 return piv;
230}
231
232/** Solves A X = B. */
233inline Mat solve(const Mat& A0, const Mat& B) {
234 const std::size_t n = A0.size(), m = ncols(B);
235 Mat A = A0;
236 const std::vector<std::size_t> piv = lu(A);
237 Mat X = zeros(n, m);
238 for (std::size_t c = 0; c < m; ++c) {
239 Vec y(n, 0.0);
240 for (std::size_t i = 0; i < n; ++i) {
241 double s = B[piv[i]][c];
242 for (std::size_t j = 0; j < i; ++j) s -= A[i][j] * y[j];
243 y[i] = s;
244 }
245 for (std::size_t ii = n; ii-- > 0;) {
246 double s = y[ii];
247 for (std::size_t j = ii + 1; j < n; ++j) s -= A[ii][j] * X[j][c];
248 X[ii][c] = s / A[ii][ii];
249 }
250 }
251 return X;
252}
253inline Mat inv(const Mat& A) { return solve(A, eye(A.size())); }
254
255/** Solves x A = b for the row vector x. */
256inline Vec solve_left(const Mat& A, const Vec& b) {
257 const std::size_t n = A.size();
258 Mat At = zeros(n, n), bb = zeros(n, 1);
259 for (std::size_t i = 0; i < n; ++i) {
260 bb[i][0] = b[i];
261 for (std::size_t j = 0; j < n; ++j) At[j][i] = A[i][j];
262 }
263 const Mat X = solve(At, bb);
264 Vec y(n);
265 for (std::size_t i = 0; i < n; ++i) y[i] = X[i][0];
266 return y;
267}
268inline double norm1(const Mat& A) {
269 double m = 0;
270 for (std::size_t j = 0; j < ncols(A); ++j) {
271 double s = 0;
272 for (std::size_t i = 0; i < A.size(); ++i) s += std::fabs(A[i][j]);
273 m = std::max(m, s);
274 }
275 return m;
276}
277inline double cond1(const Mat& A) {
278 const Mat Ai = inv(A);
279 for (const Vec& r : Ai)
280 for (double v : r)
281 if (!std::isfinite(v)) return std::numeric_limits<double>::infinity();
282 return norm1(A) * norm1(Ai);
283}
284inline Matrix<double> to_matrix(const Mat& A) {
285 Matrix<double> M(A.size(), ncols(A));
286 for (std::size_t i = 0; i < A.size(); ++i)
287 for (std::size_t j = 0; j < A[i].size(); ++j) M(i, j) = A[i][j];
288 return M;
289}
291 Mat A = zeros(M.rows(), M.cols());
292 for (std::size_t i = 0; i < M.rows(); ++i)
293 for (std::size_t j = 0; j < M.cols(); ++j) A[i][j] = M(i, j);
294 return A;
295}
296
297struct Net {
298 const Mat* pre;
299 const Mat* post;
300 const Mat* inh;
301 const Vec* rate;
302 const std::vector<bool>* imm;
303 const Vec* srv;
304 std::size_t p0, P, T;
305 std::vector<std::size_t> other;
306};
307
308struct Arc {
309 std::size_t src; ///< 1-based source marking
310 std::size_t dst; ///< 1-based destination marking, 0 = escape above the window
311 std::size_t trn;
312 double val;
313};
314
315struct Walk {
316 std::vector<Vec> S;
317 std::vector<Arc> arcs;
318 std::vector<bool> isvan;
319};
320
321struct Elim {
322 Mat Q; ///< nT x nT generator of the tangible markings
323 Mat S; ///< P x nT markings
324 std::vector<std::vector<std::size_t>> levidx; ///< tangible indices by level 0..Ktop+1
325 Mat trate; ///< nT x T firing rates
326 std::size_t nvan = 0;
327};
328
329inline Vec enabled_weights(const Net& net, const Vec& m, std::vector<bool>& en) {
330 Vec w(net.T, 0.0);
331 for (std::size_t t = 0; t < net.T; ++t) {
332 bool ok = true;
333 for (std::size_t p = 0; p < net.P && ok; ++p)
334 if (m[p] < (*net.pre)[p][t] || m[p] >= (*net.inh)[p][t]) ok = false;
335 if (!ok) continue;
336 double dg = std::numeric_limits<double>::infinity();
337 for (std::size_t p = 0; p < net.P; ++p)
338 if ((*net.pre)[p][t] > 0) dg = std::min(dg, std::floor(m[p] / (*net.pre)[p][t]));
339 if (std::isinf(dg)) dg = 1;
340 en[t] = true;
341 w[t] = (*net.rate)[t] * std::min(dg, (*net.srv)[t]);
342 }
343 return w;
344}
345
346inline std::size_t lookup(Walk& W, std::map<std::vector<long long>, std::size_t>& keys, const Vec& m,
347 const Net& net, int cap, const IspnOptions& opt) {
348 std::vector<long long> k(m.size());
349 for (std::size_t i = 0; i < m.size(); ++i) k[i] = static_cast<long long>(m[i]);
350 auto it = keys.find(k);
351 if (it != keys.end()) return it->second;
352 if (W.S.size() + 1 > opt.maxstates)
353 refuse(fmt("iSPN requirement 1 violated: more than %.0f markings below level %.0f; a place other "
354 "than %.0f appears unbounded. Raise OPTIONS.maxstates if the net is just large",
355 static_cast<double>(opt.maxstates), cap, static_cast<double>(net.p0)));
356 W.S.push_back(m);
357 keys[k] = W.S.size();
358 return W.S.size();
359}
360
361inline Walk explore(const Net& net, const Vec& m0, int cap, const IspnOptions& opt) {
362 Walk W;
363 std::map<std::vector<long long>, std::size_t> keys;
364 if (m0[net.p0] > cap) badarg("the initial marking of P0 exceeds the explored window");
365 lookup(W, keys, m0, net, cap, opt);
366 std::size_t head = 0;
367 while (head < W.S.size()) {
368 const Vec m = W.S[head];
369 ++head;
370 std::vector<bool> en(net.T, false);
371 Vec w = enabled_weights(net, m, en);
372 bool v = false;
373 for (std::size_t t = 0; t < net.T; ++t) v = v || (en[t] && (*net.imm)[t]);
374 W.isvan.push_back(v);
375 std::vector<std::size_t> fire;
376 double tot = 0;
377 for (std::size_t t = 0; t < net.T; ++t)
378 if (en[t] && (*net.imm)[t] == v && w[t] > 0) {
379 fire.push_back(t);
380 tot += w[t];
381 }
382 if (v) {
383 if (tot <= 0) badarg("an enabled immediate transition has zero weight");
384 for (double& x : w) x /= tot;
385 }
386 for (std::size_t t : fire) {
387 Vec mn(net.P);
388 for (std::size_t p = 0; p < net.P; ++p) mn[p] = m[p] - (*net.pre)[p][t] + (*net.post)[p][t];
389 const std::size_t id = mn[net.p0] > cap ? 0 : lookup(W, keys, mn, net, cap, opt);
390 W.arcs.push_back(Arc{head, id, t, w[t]});
391 }
392 }
393 return W;
394}
395
396inline Elim eliminate(const Walk& W, const Net& net, int Ktop) {
397 const std::size_t n = W.S.size(), p0 = net.p0, T = net.T;
398 std::vector<std::size_t> tgl, vnl;
399 for (std::size_t i = 0; i < n; ++i) (W.isvan[i] ? vnl : tgl).push_back(i);
400 const std::vector<std::size_t>& other = net.other;
401 std::stable_sort(tgl.begin(), tgl.end(), [&](std::size_t a, std::size_t b) {
402 if (W.S[a][p0] != W.S[b][p0]) return W.S[a][p0] < W.S[b][p0];
403 for (std::size_t p : other)
404 if (W.S[a][p] != W.S[b][p]) return W.S[a][p] < W.S[b][p];
405 return false;
406 });
407 const std::size_t nT = tgl.size(), nV = vnl.size();
408 std::vector<std::size_t> mapT(n, 0), mapV(n, 0);
409 for (std::size_t i = 0; i < nT; ++i) mapT[tgl[i]] = i;
410 for (std::size_t i = 0; i < nV; ++i) mapV[vnl[i]] = i;
411 const std::size_t esc = nT;
412 Mat QTT = zeros(nT, nT + 1), QTV = zeros(nT, nV), PVT = zeros(nV, nT + 1), PVV = zeros(nV, nV);
413 Mat trate = zeros(nT, T), Fimm = zeros(nV, T);
414 for (const Arc& arc : W.arcs) {
415 const std::size_t s0 = arc.src - 1, d = arc.dst, t = arc.trn;
416 const double v = arc.val;
417 if (!W.isvan[s0]) {
418 const std::size_t i = mapT[s0];
419 trate[i][t] += v;
420 if (d == 0) QTT[i][esc] += v;
421 else if (W.isvan[d - 1]) QTV[i][mapV[d - 1]] += v;
422 else QTT[i][mapT[d - 1]] += v;
423 } else {
424 const std::size_t i = mapV[s0];
425 Fimm[i][t] += v;
426 if (d == 0) PVT[i][esc] += v;
427 else if (W.isvan[d - 1]) PVV[i][mapV[d - 1]] += v;
428 else PVT[i][mapT[d - 1]] += v;
429 }
430 }
431 Mat Q;
432 if (nV > 0) {
433 const Mat IP = minus(eye(nV), PVV);
434 if (cond1(IP) > 1e14)
435 refuse("the immediate transitions contain a loop of vanishing markings that is never left "
436 "(a timeless trap)");
437 const Mat IPinv = inv(IP);
438 Q = plus(QTT, mult(QTV, mult(IPinv, PVT)));
439 trate = plus(trate, mult(QTV, mult(IPinv, Fimm)));
440 } else {
441 Q = QTT;
442 }
443 Vec levT(nT + 1);
444 for (std::size_t i = 0; i < nT; ++i) levT[i] = W.S[tgl[i]][p0];
445 levT[nT] = Ktop + 3;
446 for (std::size_t a = 0; a < nT; ++a)
447 for (std::size_t b = 0; b <= nT; ++b)
448 if (std::fabs(Q[a][b]) > 1e-14 && std::fabs(levT[b] - levT[a]) > 1 && levT[a] <= Ktop)
449 refuse(fmt("iSPN requirement 2 violated: a timed firing followed by immediate firings moves "
450 "the unbounded place from level %.0f to level %.0f, but a level may change by at "
451 "most one",
452 levT[a], levT[b]));
453 Elim E;
454 E.Q = zeros(nT, nT);
455 for (std::size_t i = 0; i < nT; ++i) {
456 double rs = 0;
457 for (std::size_t j = 0; j <= nT; ++j)
458 if (j != i) rs += Q[i][j];
459 for (std::size_t j = 0; j < nT; ++j)
460 if (j != i) E.Q[i][j] = Q[i][j];
461 E.Q[i][i] = -rs;
462 }
463 E.S = zeros(net.P, nT);
464 for (std::size_t p = 0; p < net.P; ++p)
465 for (std::size_t i = 0; i < nT; ++i) E.S[p][i] = W.S[tgl[i]][p];
466 E.levidx.assign(static_cast<std::size_t>(Ktop + 2), std::vector<std::size_t>());
467 for (std::size_t i = 0; i < nT; ++i) {
468 const double l = levT[i];
469 if (l >= 0 && l < Ktop + 2) E.levidx[static_cast<std::size_t>(l)].push_back(i);
470 }
471 E.trate = trate;
472 E.nvan = nV;
473 return E;
474}
475
476inline int find_K(const Elim& E, int Ktop, const IspnOptions& opt, std::size_t p0) {
477 const auto& lv = E.levidx;
478 const Mat& S = E.S;
479 for (int c = 0; c < Ktop - 2; ++c) {
480 const std::vector<std::size_t>& ref = lv[c];
481 if (ref.empty()) continue;
482 bool ok = true;
483 for (int k = c + 1; k < Ktop + 2 && ok; ++k) {
484 if (lv[k].size() != ref.size()) {
485 ok = false;
486 break;
487 }
488 for (std::size_t p = 0; p < S.size() && ok; ++p) {
489 if (p == p0) continue;
490 for (std::size_t j = 0; j < ref.size(); ++j)
491 if (S[p][lv[k][j]] != S[p][ref[j]]) {
492 ok = false;
493 break;
494 }
495 }
496 }
497 if (!ok) continue;
498 const Mat F0 = sub(E.Q, lv[c], lv[c + 1]);
499 const Mat L1 = sub(E.Q, lv[c + 1], lv[c + 1]);
500 const Mat D1 = sub(E.Q, lv[c + 1], lv[c]);
501 const double sc = std::max(1.0, std::max(max_abs(F0), std::max(max_abs(L1), max_abs(D1))));
502 for (int k = c + 1; k <= Ktop && ok; ++k)
503 if (max_abs_diff(sub(E.Q, lv[k], lv[k + 1]), F0) > opt.tol * sc ||
504 max_abs_diff(sub(E.Q, lv[k], lv[k]), L1) > opt.tol * sc ||
505 max_abs_diff(sub(E.Q, lv[k], lv[k - 1]), D1) > opt.tol * sc)
506 ok = false;
507 if (ok) return c;
508 }
509 return -1;
510}
511
512inline double expect(const Vec& r, const std::string& what, const Mat& x,
513 const std::vector<std::vector<std::size_t>>& lv, int K, int Ktop, const Vec& c1,
514 const Vec& c2, double tol) {
515 double y = 0;
516 for (int i = 0; i <= K; ++i)
517 for (std::size_t j = 0; j < x[i].size(); ++j) y += x[i][j] * r[lv[i][j]];
518 const std::size_t n = lv[K + 1].size();
519 Vec a(n), b(n);
520 for (std::size_t j = 0; j < n; ++j) {
521 a[j] = r[lv[K + 1][j]];
522 b[j] = r[lv[K + 2][j]] - a[j];
523 }
524 const double sc = std::max(1.0, std::max(max_abs(a), max_abs(b)));
525 for (int j = 3; j <= Ktop - K; ++j)
526 for (std::size_t i = 0; i < n; ++i)
527 if (std::fabs(r[lv[K + j][i]] - a[i] - (j - 1) * b[i]) > tol * sc * j)
528 badarg("the " + what + " is not affine in the level above K = " + std::to_string(K) +
529 ", so it has no closed form");
530 return y + dot(c1, a) + dot(c2, b);
531}
532
533inline long long gcd_ll(long long a, long long b) {
534 while (b != 0) {
535 const long long t = a % b;
536 a = b;
537 b = t;
538 }
539 return a;
540}
541
542inline std::vector<Vec> minimal(const std::vector<Vec>& D, std::size_t T, std::size_t P) {
543 const std::size_t n = D.size();
544 if (n <= 1) return D;
545 std::vector<std::vector<bool>> supp(n, std::vector<bool>(P));
546 for (std::size_t i = 0; i < n; ++i)
547 for (std::size_t p = 0; p < P; ++p) supp[i][p] = D[i][T + p] > 1e-12;
548 std::vector<bool> drop(n, false);
549 for (std::size_t i = 0; i < n; ++i) {
550 if (drop[i]) continue;
551 for (std::size_t j = 0; j < n; ++j) {
552 if (i == j || drop[j]) continue;
553 bool le = true, lt = false;
554 for (std::size_t p = 0; p < P; ++p) {
555 if (supp[j][p] && !supp[i][p]) le = false;
556 if (!supp[j][p] && supp[i][p]) lt = true;
557 }
558 if (le && (lt || j < i)) {
559 drop[i] = true;
560 break;
561 }
562 }
563 }
564 std::vector<Vec> out;
565 for (std::size_t i = 0; i < n; ++i)
566 if (!drop[i]) out.push_back(D[i]);
567 return out;
568}
569
570} // namespace ispn_detail
571
572/**
573 * Places covered by a non-negative S-invariant (P-semiflow) y' C = 0, by
574 * Farkas' algorithm on [C | I] keeping minimal supports. The uncovered places
575 * are the candidates for the unbounded place of an iSPN.
576 *
577 * @param C P x T incidence matrix POST - PRE
578 */
579inline std::vector<bool> spn_ispn_covered(const std::vector<std::vector<double>>& C) {
580 using namespace ispn_detail;
581 const std::size_t P = C.size(), T = P > 0 ? C[0].size() : 0;
582 std::vector<Vec> D;
583 for (std::size_t p = 0; p < P; ++p) {
584 if (C[p].size() != T) throw InputError(ErrorKind{"Argument"}, "spn_ispn_covered: C must be P x T");
585 Vec row(T + P, 0.0);
586 for (std::size_t t = 0; t < T; ++t) row[t] = C[p][t];
587 row[T + p] = 1.0;
588 D.push_back(row);
589 }
590 for (std::size_t t = 0; t < T; ++t) {
591 std::vector<Vec> nd, pos, neg;
592 for (const Vec& r : D) {
593 if (std::fabs(r[t]) < 1e-12) nd.push_back(r);
594 else if (r[t] > 0) pos.push_back(r);
595 else neg.push_back(r);
596 }
597 for (const Vec& a : pos)
598 for (const Vec& b : neg) {
599 Vec row(T + P);
600 long long g = 0;
601 for (std::size_t j = 0; j < T + P; ++j) {
602 row[j] = -b[t] * a[j] + a[t] * b[j];
603 const long long rv = std::llabs(std::llround(row[j]));
604 if (rv != 0) g = gcd_ll(g, rv);
605 }
606 if (g > 1)
607 for (double& v : row) v /= static_cast<double>(g);
608 nd.push_back(row);
609 }
610 D = minimal(nd, T, P);
611 }
612 std::vector<bool> covered(P, false);
613 for (const Vec& r : D)
614 for (std::size_t p = 0; p < P; ++p)
615 if (r[T + p] > 1e-12) covered[p] = true;
616 return covered;
617}
618
619/**
620 * Steady state of an infinite-state SPN.
621 *
622 * @param pre P x T input multiplicities
623 * @param post P x T output multiplicities
624 * @param inh P x T inhibitor thresholds (t disabled when m(p) >= inh), +inf for none; empty: none
625 * @param rate length-T timed rate, or the weight of an immediate transition
626 * @param imm length-T, true for an immediate transition; empty: all timed
627 * @param srv length-T servers (+inf allowed); empty: all 1. A transition with no input fires at rate(t)
628 * @param m0 length-P initial marking
629 * @param p0 0-based index of the unbounded place
630 */
631inline IspnResult spn_ispn(const std::vector<std::vector<double>>& pre,
632 const std::vector<std::vector<double>>& post,
633 std::vector<std::vector<double>> inh, const std::vector<double>& rate,
634 std::vector<bool> imm, std::vector<double> srv, const std::vector<double>& m0,
635 std::size_t p0, const IspnOptions& opt = IspnOptions()) {
636 using namespace ispn_detail;
637 const double Inf = std::numeric_limits<double>::infinity();
638 if (opt.extra < 3) badarg("OPTIONS.extra must be at least 3");
639 const std::size_t P = pre.size(), T = P > 0 ? pre[0].size() : 0;
640 if (post.size() != P) badarg("PRE and POST must both be P x T");
641 for (std::size_t p = 0; p < P; ++p)
642 if (pre[p].size() != T || post[p].size() != T) badarg("PRE and POST must both be P x T");
643 if (inh.empty()) inh.assign(P, Vec(T, Inf));
644 if (imm.empty()) imm.assign(T, false);
645 if (srv.empty()) srv.assign(T, 1.0);
646 bool shape = inh.size() == P && rate.size() == T && imm.size() == T && srv.size() == T && m0.size() == P;
647 for (std::size_t p = 0; shape && p < P; ++p) shape = inh[p].size() == T;
648 if (!shape) badarg("INH must be P x T, RATE, IMM and SRV of length T and M0 of length P");
649 for (std::size_t p = 0; p < P; ++p) {
650 if (m0[p] < 0 || m0[p] != std::rint(m0[p]))
651 badarg("arc multiplicities and the initial marking must be non-negative integers");
652 for (std::size_t t = 0; t < T; ++t)
653 if (pre[p][t] < 0 || post[p][t] < 0 || pre[p][t] != std::rint(pre[p][t]) ||
654 post[p][t] != std::rint(post[p][t]))
655 badarg("arc multiplicities and the initial marking must be non-negative integers");
656 }
657 for (std::size_t t = 0; t < T; ++t)
658 if (rate[t] < 0 || srv[t] < 1)
659 badarg("arc multiplicities, the initial marking and the rates must be non-negative, servers >= 1");
660 if (p0 >= P) badarg("P0 must be the index of a place");
661
662 for (std::size_t t = 0; t < T; ++t) {
663 const double d = post[p0][t] - pre[p0][t];
664 if (std::fabs(d) > 1)
665 refuse(fmt("iSPN requirement 2 violated: transition %.0f changes the unbounded place %.0f by %.0f "
666 "tokens, but a firing may move the level by at most one",
667 static_cast<double>(t), static_cast<double>(p0), d));
668 }
669 std::vector<std::size_t> other;
670 for (std::size_t p = 0; p < P; ++p)
671 if (p != p0) other.push_back(p);
672
673 int Kstr = 0;
674 std::vector<bool> infdeg(T, false);
675 for (std::size_t t = 0; t < T; ++t) {
676 if (pre[p0][t] > 0) {
677 Kstr = std::max(Kstr, static_cast<int>(pre[p0][t]));
678 if (!std::isinf(srv[t])) {
679 Kstr = std::max(Kstr, static_cast<int>(pre[p0][t] * srv[t]));
680 } else {
681 bool any_other = false;
682 for (std::size_t p : other) any_other = any_other || pre[p][t] > 0;
683 if (any_other)
684 infdeg[t] = true;
685 else
686 refuse(fmt("iSPN requirement 3 violated: transition %.0f is infinite-server and consumes "
687 "only from the unbounded place %.0f, so its rate grows with the level and never "
688 "becomes level independent",
689 static_cast<double>(t), static_cast<double>(p0)));
690 }
691 }
692 if (!std::isinf(inh[p0][t])) Kstr = std::max(Kstr, static_cast<int>(inh[p0][t]));
693 }
694
695 Net net;
696 net.pre = &pre;
697 net.post = &post;
698 net.inh = &inh;
699 net.rate = &rate;
700 net.imm = &imm;
701 net.srv = &srv;
702 net.p0 = p0;
703 net.P = P;
704 net.T = T;
705 net.other = other;
706
707 int Kneed = Kstr;
708 int Ktop = Kneed + opt.extra;
709 Elim E;
710 int K;
711 while (true) {
712 if (Ktop > opt.maxlevel)
713 refuse(fmt("iSPN requirement 1/3 violated: the levels of place %.0f do not become identical (same "
714 "phase markings and level-independent rates) up to level %.0f; either another place is "
715 "unbounded or the rates depend on the level. Raise OPTIONS.maxlevel if the threshold is "
716 "higher",
717 static_cast<double>(p0), opt.maxlevel));
718 const Walk W = explore(net, m0, Ktop + 2, opt);
719 for (std::size_t t = 0; t < T; ++t) {
720 if (!infdeg[t]) continue;
721 double mx = 0;
722 for (const Vec& m : W.S) {
723 double dg = Inf;
724 for (std::size_t p : other)
725 if (pre[p][t] > 0) dg = std::min(dg, std::floor(m[p] / pre[p][t]));
726 mx = std::max(mx, dg);
727 }
728 Kneed = std::max(Kneed, static_cast<int>(pre[p0][t] * mx));
729 }
730 if (Ktop < Kneed + opt.extra) {
731 Ktop = Kneed + opt.extra;
732 continue;
733 }
734 E = eliminate(W, net, Ktop);
735 K = find_K(E, Ktop, opt, p0);
736 if (K >= 0) break;
737 Ktop = 2 * Ktop + 1;
738 }
739
740 const auto& lv = E.levidx;
741 const Mat& Qt = E.Q;
742 const Mat A0 = sub(Qt, lv[K], lv[K + 1]);
743 const Mat A1 = sub(Qt, lv[K + 1], lv[K + 1]);
744 const Mat A2 = sub(Qt, lv[K + 1], lv[K]);
745 const std::size_t nL = lv[K].size();
746 if (all_zero(A0))
747 refuse(fmt("place %.0f is bounded: no transition raises its marking above level %.0f. The net has a "
748 "finite state space; solve it with SolverCTMC",
749 static_cast<double>(p0), K));
750 if (all_zero(A2))
751 unstable(fmt("the iSPN is unstable: no transition lowers the marking of place %.0f above level %.0f, "
752 "so it grows without bound",
753 static_cast<double>(p0), K));
754
755 // drift (Haverkort (17.7))
756 Mat Aa = plus(plus(A0, A1), A2);
757 for (std::size_t i = 0; i < nL; ++i) Aa[i][nL - 1] = 1.0;
758 Vec eLast(nL, 0.0);
759 eLast[nL - 1] = 1.0;
760 const Vec piA = solve_left(Aa, eLast);
761 const double up = dot(piA, row_sums(A0));
762 const double down = dot(piA, row_sums(A2));
763 if (!(up < down * (1 - 1e-12)))
764 unstable(fmt("the iSPN is unstable: the mean drift of place %.0f above level %.0f is up %.6g >= down "
765 "%.6g (Haverkort (17.7)); no steady state exists",
766 static_cast<double>(p0), K, up, down));
767
768 std::string qm = opt.qbdmethod;
769 std::transform(qm.begin(), qm.end(), qm.begin(), [](unsigned char c) { return std::tolower(c); });
770 Matrix<double> Rmat;
771 if (qm == "logred")
772 Rmat = mam::qbd_R_logred(to_matrix(A2), to_matrix(A1), to_matrix(A0), 100000u, 1e-12);
773 else if (qm == "successive")
774 Rmat = mam::qbd_R(to_matrix(A2), to_matrix(A1), to_matrix(A0));
775 else
776 badarg("unknown OPTIONS.qbdmethod '" + opt.qbdmethod + "'; use 'logred' or 'successive'");
777 const Mat Rm = from_matrix(Rmat);
778 const double spR = spectral_radius(Rmat);
779 if (!(spR < 1)) unstable(fmt("the iSPN is unstable: the spectral radius of R is %.6g >= 1", spR));
780 const Mat I = eye(nL);
781 const Mat Ni = inv(minus(I, Rm));
782
783 // boundary
784 std::vector<std::size_t> bidx;
785 for (int i = 0; i <= K; ++i) bidx.insert(bidx.end(), lv[i].begin(), lv[i].end());
786 const std::size_t nb = bidx.size();
787 const Mat B = sub(Qt, bidx, bidx);
788 Mat Bm = B;
789 const Mat RA2 = mult(Rm, A2);
790 for (std::size_t i = 0; i < nL; ++i)
791 for (std::size_t j = 0; j < nL; ++j) Bm[nb - nL + i][nb - nL + j] += RA2[i][j];
792 const Vec NiOne = row_sums(Ni);
793 for (std::size_t i = 0; i < nb; ++i) Bm[i][nb - 1] = 1.0;
794 for (std::size_t i = 0; i < nL; ++i) Bm[nb - nL + i][nb - 1] = NiOne[i];
795 Vec rhs(nb, 0.0);
796 rhs[nb - 1] = 1.0;
797 Vec xb = solve_left(Bm, rhs);
798 for (double& v : xb)
799 if (std::fabs(v) < 1e-300) v = 0.0;
800 IspnResult res;
801 for (int i = 0, o = 0; i <= K; ++i) {
802 res.x.push_back(Vec(xb.begin() + o, xb.begin() + o + static_cast<long>(lv[i].size())));
803 o += static_cast<int>(lv[i].size());
804 }
805 const Vec& xK = res.x[K];
806 const Mat RN = mult(Rm, Ni);
807 const Vec c1 = vec_mult(xK, RN);
808 const Vec c2 = vec_mult(xK, mult(mult(Rm, Rm), mult(Ni, Ni)));
809
810 const Mat& S = E.S;
811 const std::size_t nT = S[0].size();
812 Vec mm(P);
813 for (std::size_t p = 0; p < P; ++p)
814 mm[p] = expect(S[p], "marking of place " + std::to_string(p), res.x, lv, K, Ktop, c1, c2, opt.tol);
815 double EL2 = 0;
816 for (int i = 0; i <= K; ++i) EL2 += static_cast<double>(i) * i * sum(res.x[i]);
817 const Mat N2 = mult(Ni, Ni);
818 const Mat N3 = mult(N2, Ni);
819 const Mat term = plus(plus(scale(RN, static_cast<double>(K) * K), scale(mult(Rm, N2), 2.0 * K)),
820 mult(mult(Rm, plus(I, Rm)), N3));
821 EL2 += dot(xK, row_sums(term));
822 res.varlevel = std::max(EL2 - mm[p0] * mm[p0], 0.0);
823
824 Vec dist;
825 double tail = 1.0;
826 for (int i = 0; i <= K; ++i) {
827 dist.push_back(sum(res.x[i]));
828 tail -= dist.back();
829 }
830 Vec v = vec_mult(xK, Rm);
831 double lvl = K;
832 while (lvl < opt.distmax && tail > 1e-12) {
833 lvl += 1;
834 const double s = sum(v);
835 dist.push_back(s);
836 tail -= s;
837 v = vec_mult(v, Rm);
838 }
839 if (!std::isinf(opt.distmax))
840 while (static_cast<double>(dist.size()) > std::floor(opt.distmax) + 1) dist.pop_back();
841 res.leveldist = dist;
842 res.leveltail = std::max(1.0 - sum(dist), 0.0);
843
844 for (std::size_t p = 0; p < P; ++p) {
845 if (p == p0) {
846 res.marginal.push_back(res.leveldist);
847 continue;
848 }
849 std::size_t nmax = 0;
850 for (std::size_t s = 0; s < nT; ++s) nmax = std::max(nmax, static_cast<std::size_t>(S[p][s]));
851 Vec mg(nmax + 1);
852 for (std::size_t n = 0; n <= nmax; ++n) {
853 Vec ind(nT);
854 for (std::size_t s = 0; s < nT; ++s) ind[s] = S[p][s] == static_cast<double>(n) ? 1.0 : 0.0;
855 mg[n] = expect(ind, "marginal of place " + std::to_string(p), res.x, lv, K, Ktop, c1, c2, opt.tol);
856 }
857 res.marginal.push_back(mg);
858 }
859
860 res.tput.assign(T, 0.0);
861 for (std::size_t t = 0; t < T; ++t) {
862 Vec col(nT);
863 for (std::size_t s = 0; s < nT; ++s) col[s] = E.trate[s][t];
864 res.tput[t] = expect(col, "firing rate of transition " + std::to_string(t), res.x, lv, K, Ktop, c1, c2,
865 opt.tol);
866 }
867
868 res.reward.assign(opt.rewards.size(), 0.0);
869 for (std::size_t j = 0; j < opt.rewards.size(); ++j) {
870 Vec rv(nT);
871 for (std::size_t s = 0; s < nT; ++s) {
872 Vec m(P);
873 for (std::size_t p = 0; p < P; ++p) m[p] = S[p][s];
874 rv[s] = opt.rewards[j](m);
875 }
876 res.reward[j] = expect(rv, "reward " + std::to_string(j), res.x, lv, K, Ktop, c1, c2, opt.tol);
877 }
878
879 res.K = K;
880 res.L = nL;
881 res.A0 = A0;
882 res.A1 = A1;
883 res.A2 = A2;
884 res.R = Rm;
885 res.B = B;
886 for (int i = 0; i <= K; ++i) {
887 Mat lm = zeros(P, lv[i].size());
888 for (std::size_t p = 0; p < P; ++p)
889 for (std::size_t j = 0; j < lv[i].size(); ++j) lm[p][j] = S[p][lv[i][j]];
890 res.levelmarkings.push_back(lm);
891 }
892 res.drift = {up, down};
893 res.spR = spR;
894 res.meanmarking = mm;
895 res.ntangible = nT;
896 res.nvanishing = E.nvan;
897 return res;
898}
899
900} // namespace spn
901} // namespace line
902
903#endif // LINE_API_SPN_SPN_ISPN_H
InputError(const std::string &what)
Definition error.h:105
std::size_t cols() const
Definition matrix.h:90
std::size_t rows() const
Definition matrix.h:89
NumericError(const std::string &what)
Definition error.h:112
UnsupportedError(const std::string &what)
Definition error.h:119
Eigenvalues and singular values, backed by LAPACK.
The exception types the port throws.
Dense matrix and non-owning view.
Matrix< T > qbd_R_logred(const Matrix< T > &B, const Matrix< T > &L, const Matrix< T > &F, unsigned iter_max, const T &tol)
R by logarithmic reduction (qbd_R_logred.m).
Definition qbd_r.h:217
Matrix< T > qbd_R(const Matrix< T > &B, const Matrix< T > &L, const Matrix< T > &F, unsigned iter_max, const T &tol)
R by successive substitutions (qbd_R.m): iterate R <- -(F + R^2 B) L^-1.
Definition qbd_r.h:184
Vec enabled_weights(const Net &net, const Vec &m, std::vector< bool > &en)
Definition spn_ispn.h:329
double expect(const Vec &r, const std::string &what, const Mat &x, const std::vector< std::vector< std::size_t > > &lv, int K, int Ktop, const Vec &c1, const Vec &c2, double tol)
Definition spn_ispn.h:512
Mat eye(std::size_t n)
Definition spn_ispn.h:121
std::string fmt(const char *f, double a=0, double b=0, double c=0, double d=0)
Definition spn_ispn.h:104
Walk explore(const Net &net, const Vec &m0, int cap, const IspnOptions &opt)
Definition spn_ispn.h:361
Mat plus(const Mat &A, const Mat &B)
Definition spn_ispn.h:155
Mat inv(const Mat &A)
Definition spn_ispn.h:253
double sum(const Vec &a)
Definition spn_ispn.h:191
std::vector< double > Vec
Definition spn_ispn.h:101
Vec solve_left(const Mat &A, const Vec &b)
Solves x A = b for the row vector x.
Definition spn_ispn.h:256
void refuse(const std::string &m)
Definition spn_ispn.h:110
void badarg(const std::string &m)
Definition spn_ispn.h:116
std::vector< Vec > Mat
Definition spn_ispn.h:102
bool all_zero(const Mat &A)
Definition spn_ispn.h:133
double max_abs_diff(const Mat &A, const Mat &B)
Definition spn_ispn.h:149
Mat from_matrix(const Matrix< double > &M)
Definition spn_ispn.h:290
Elim eliminate(const Walk &W, const Net &net, int Ktop)
Definition spn_ispn.h:396
double cond1(const Mat &A)
Definition spn_ispn.h:277
double max_abs(const Vec &a)
Definition spn_ispn.h:139
Mat solve(const Mat &A0, const Mat &B)
Solves A X = B.
Definition spn_ispn.h:233
int find_K(const Elim &E, int Ktop, const IspnOptions &opt, std::size_t p0)
Definition spn_ispn.h:476
double dot(const Vec &a, const Vec &b)
Definition spn_ispn.h:201
Mat sub(const Mat &A, const std::vector< std::size_t > &r, const std::vector< std::size_t > &c)
Definition spn_ispn.h:127
std::vector< std::size_t > lu(Mat &A)
LU with partial pivoting in place; returns the row permutation.
Definition spn_ispn.h:208
std::vector< Vec > minimal(const std::vector< Vec > &D, std::size_t T, std::size_t P)
Definition spn_ispn.h:542
long long gcd_ll(long long a, long long b)
Definition spn_ispn.h:533
Vec row_sums(const Mat &A)
Definition spn_ispn.h:196
Mat mult(const Mat &A, const Mat &B)
Definition spn_ispn.h:173
std::size_t ncols(const Mat &A)
Definition spn_ispn.h:126
Mat zeros(std::size_t n, std::size_t m)
Definition spn_ispn.h:120
Matrix< double > to_matrix(const Mat &A)
Definition spn_ispn.h:284
Vec vec_mult(const Vec &x, const Mat &A)
Definition spn_ispn.h:184
Mat minus(const Mat &A, const Mat &B)
Definition spn_ispn.h:161
double norm1(const Mat &A)
Definition spn_ispn.h:268
Mat scale(const Mat &A, double s)
Definition spn_ispn.h:167
std::size_t lookup(Walk &W, std::map< std::vector< long long >, std::size_t > &keys, const Vec &m, const Net &net, int cap, const IspnOptions &opt)
Definition spn_ispn.h:346
void unstable(const std::string &m)
Definition spn_ispn.h:113
std::vector< bool > spn_ispn_covered(const std::vector< std::vector< double > > &C)
Places covered by a non-negative S-invariant (P-semiflow) y' C = 0, by Farkas' algorithm on [C | I] k...
Definition spn_ispn.h:579
IspnResult spn_ispn(const std::vector< std::vector< double > > &pre, const std::vector< std::vector< double > > &post, std::vector< std::vector< double > > inh, const std::vector< double > &rate, std::vector< bool > imm, std::vector< double > srv, const std::vector< double > &m0, std::size_t p0, const IspnOptions &opt=IspnOptions())
Steady state of an infinite-state SPN.
Definition spn_ispn.h:631
Conservation laws of a layered queueing network, enumerated from its structure.
Definition aoi_dist2ph.h:52
double spectral_radius(const Matrix< double > &A)
Largest modulus over the spectrum, i.e.
Definition eig.h:84
Matrix< T > eye(std::size_t n)
Identity of order n.
Definition linalg.h:28
Quasi-birth-death processes: the rate matrix R, the fundamental matrix G, the caudal characteristic,...
The <kind> part of a LINE:<category>:<kind> identifier (see _kb/17-error-vocabulary....
Definition error.h:37
Options of spn_ispn.
Definition spn_ispn.h:68
std::vector< std::function< double(const std::vector< double > &)> > rewards
reward functions of the marking (length P), affine in the level above K
Definition spn_ispn.h:75
double tol
tolerance of the block-repetition and affinity tests
Definition spn_ispn.h:72
int extra
levels explored above the structural threshold (at least 3)
Definition spn_ispn.h:69
std::string qbdmethod
"logred" or "successive"
Definition spn_ispn.h:76
double distmax
largest level of the reported distribution of P0
Definition spn_ispn.h:73
int maxlevel
largest explored window before the net is refused
Definition spn_ispn.h:70
std::size_t maxstates
largest number of markings explored
Definition spn_ispn.h:71
Result of spn_ispn; fields as in the MATLAB help.
Definition spn_ispn.h:80
int K
first repeating level
Definition spn_ispn.h:81
std::vector< std::vector< double > > A2
Definition spn_ispn.h:83
std::vector< std::vector< double > > B
Definition spn_ispn.h:83
std::size_t L
phases per level
Definition spn_ispn.h:82
std::vector< double > drift
{up, down} mean drift above K
Definition spn_ispn.h:86
std::vector< double > meanmarking
Definition spn_ispn.h:88
std::vector< double > tput
Definition spn_ispn.h:93
std::vector< std::vector< double > > A1
Definition spn_ispn.h:83
std::vector< std::vector< double > > R
Definition spn_ispn.h:83
std::vector< std::vector< std::vector< double > > > levelmarkings
P x n markings of levels 0..K.
Definition spn_ispn.h:85
std::size_t nvanishing
Definition spn_ispn.h:96
std::vector< std::vector< double > > x
stationary vectors of levels 0..K
Definition spn_ispn.h:84
std::vector< std::vector< double > > A0
Definition spn_ispn.h:83
std::vector< std::vector< double > > marginal
per place (P0: leveldist)
Definition spn_ispn.h:92
std::vector< double > reward
Definition spn_ispn.h:94
std::size_t ntangible
Definition spn_ispn.h:95
std::vector< double > leveldist
Definition spn_ispn.h:90
std::size_t src
1-based source marking
Definition spn_ispn.h:309
std::size_t dst
1-based destination marking, 0 = escape above the window
Definition spn_ispn.h:310
Mat trate
nT x T firing rates
Definition spn_ispn.h:325
std::vector< std::vector< std::size_t > > levidx
tangible indices by level 0..Ktop+1
Definition spn_ispn.h:324
Mat S
P x nT markings.
Definition spn_ispn.h:323
Mat Q
nT x nT generator of the tangible markings
Definition spn_ispn.h:322
std::vector< std::size_t > other
Definition spn_ispn.h:305
const std::vector< bool > * imm
Definition spn_ispn.h:302
std::vector< Arc > arcs
Definition spn_ispn.h:317
std::vector< Vec > S
Definition spn_ispn.h:316
std::vector< bool > isvan
Definition spn_ispn.h:318