LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Loading...
Searching...
No Matches
spn_reachprops.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_REACHPROPS_H
6#define LINE_API_SPN_SPN_REACHPROPS_H
7
8/**
9 * @file
10 * @ingroup api_spn
11 * Behavioural properties of a stochastic Petri net from its reachability
12 * graph (Murata 1989, sec. IV; Haverkort 1998, sec. 14.2), under the GSPN
13 * firing rule of spn_reachgraph, so inhibitor arcs, priorities and immediate
14 * events are honoured. Verdicts are tri-state: 1 true, 0 false, -1 unknown.
15 * On a complete graph:
16 * bounded = 1 and bound(l) is the largest token count of level l;
17 * deadlocks are the markings where nothing may fire;
18 * live(e) = 1 (L4-live) iff e labels an edge leaving a marking of every
19 * bottom strongly connected component;
20 * dead(e) = 1 iff e never fires;
21 * reversible = 1 iff there is one bottom SCC and it holds m0.
22 * On an unbounded or capped graph, bounded is 0 or -1, bound is NaN (Inf on
23 * the proven unbounded levels), a found deadlock makes deadlockfree, every
24 * live(e) and (when it is not m0) reversible 0, and the other verdicts are
25 * -1. MATLAB twin: matlab/src/api/spn/spn_reachprops.m.
26 */
27
28#include <algorithm>
29#include <cstddef>
30#include <limits>
31#include <string>
32#include <vector>
33
35
36namespace line {
37namespace spn {
38
40 std::string status; ///< the graph status
41 std::size_t nstates = 0;
42 int bounded = -1;
43 std::vector<double> bound; ///< per level; NaN unknown, Inf unbounded
44 int deadlockfree = -1;
45 std::vector<std::size_t> deadlocks; ///< 0-based marking indices
46 std::vector<std::vector<long long>> deadmarkings;
47 std::vector<int> live, dead; ///< per event, tri-state
48 int reversible = -1;
49 int nbscc = -1; ///< number of bottom SCCs (-1 unknown)
51};
52
53namespace detail {
54
55/** Strongly connected components (iterative Tarjan); comp[i] is a 0-based label. */
56inline std::vector<std::size_t> spn_scc(std::size_t ns, const std::vector<std::vector<std::size_t>>& Ed,
57 std::size_t& ncomp) {
58 std::vector<std::vector<std::size_t>> adj(ns);
59 for (std::size_t r = 0; r < Ed.size(); ++r) adj[Ed[r][0]].push_back(Ed[r][1]);
60 const std::size_t none = std::numeric_limits<std::size_t>::max();
61 std::vector<std::size_t> index(ns, none), low(ns, 0), comp(ns, none), stack, it(ns, 0);
62 std::vector<bool> on(ns, false);
63 std::size_t idx = 0;
64 ncomp = 0;
65 for (std::size_t s = 0; s < ns; ++s) {
66 if (index[s] != none) continue;
67 std::vector<std::size_t> call(1, s);
68 index[s] = low[s] = idx++;
69 stack.push_back(s);
70 on[s] = true;
71 while (!call.empty()) {
72 const std::size_t v = call.back();
73 if (it[v] < adj[v].size()) {
74 const std::size_t w = adj[v][it[v]++];
75 if (index[w] == none) {
76 index[w] = low[w] = idx++;
77 stack.push_back(w);
78 on[w] = true;
79 call.push_back(w);
80 } else if (on[w]) {
81 low[v] = std::min(low[v], index[w]);
82 }
83 continue;
84 }
85 call.pop_back();
86 if (!call.empty()) low[call.back()] = std::min(low[call.back()], low[v]);
87 if (low[v] == index[v]) {
88 for (;;) {
89 const std::size_t w = stack.back();
90 stack.pop_back();
91 on[w] = false;
92 comp[w] = ncomp;
93 if (w == v) break;
94 }
95 ++ncomp;
96 }
97 }
98 }
99 return comp;
100}
101
102} // namespace detail
103
104template <class T>
106 const std::vector<double>& init = std::vector<double>(),
107 std::size_t maxstates = 0) {
108 SpnReachProps rp;
109 rp.graph = spn_reachgraph(sn, init, maxstates);
110 const SpnReachGraph& g = rp.graph;
111 const detail::SpnNet net = detail::spn_build_net(sn, "spn_reachprops");
112 const std::size_t ns = g.M.size(), E = net.E, n = net.n;
113 rp.status = g.status;
114 rp.nstates = ns;
115 for (std::size_t i = 0; i < ns; ++i) {
116 const std::vector<bool> f = net.fireable(g.M[i]);
117 if (std::find(f.begin(), f.end(), true) == f.end()) {
118 rp.deadlocks.push_back(i);
119 rp.deadmarkings.push_back(g.M[i]);
120 }
121 }
122 std::vector<bool> fired(E, false);
123 for (std::size_t r = 0; r < g.E.size(); ++r) fired[g.E[r][2]] = true;
124 if (g.status == "complete") {
125 rp.bounded = 1;
126 rp.bound.assign(n, 0.0);
127 for (std::size_t i = 0; i < ns; ++i)
128 for (std::size_t l = 0; l < n; ++l)
129 rp.bound[l] = std::max(rp.bound[l], static_cast<double>(g.M[i][l]));
130 rp.deadlockfree = rp.deadlocks.empty() ? 1 : 0;
131 std::size_t nc = 0;
132 const std::vector<std::size_t> comp = detail::spn_scc(ns, g.E, nc);
133 std::vector<bool> bottom(nc, true);
134 for (std::size_t r = 0; r < g.E.size(); ++r)
135 if (comp[g.E[r][0]] != comp[g.E[r][1]]) bottom[comp[g.E[r][0]]] = false;
136 rp.live.assign(E, 1);
137 std::size_t nb = 0;
138 for (std::size_t b = 0; b < nc; ++b) {
139 if (!bottom[b]) continue;
140 ++nb;
141 std::vector<bool> lab(E, false);
142 for (std::size_t r = 0; r < g.E.size(); ++r)
143 if (comp[g.E[r][0]] == b) lab[g.E[r][2]] = true;
144 for (std::size_t e = 0; e < E; ++e)
145 if (!lab[e]) rp.live[e] = 0;
146 }
147 rp.dead.assign(E, 0);
148 for (std::size_t e = 0; e < E; ++e) rp.dead[e] = fired[e] ? 0 : 1;
149 rp.reversible = (nb == 1 && bottom[comp[0]]) ? 1 : 0;
150 rp.nbscc = static_cast<int>(nb);
151 } else {
152 rp.bounded = g.status == "unbounded" ? 0 : -1;
153 rp.bound.assign(n, std::numeric_limits<double>::quiet_NaN());
154 for (std::size_t l = 0; l < n; ++l)
155 if (g.unboundedlevels[l]) rp.bound[l] = std::numeric_limits<double>::infinity();
156 if (rp.deadlocks.empty()) {
157 rp.deadlockfree = -1;
158 rp.live.assign(E, -1);
159 rp.reversible = -1;
160 } else {
161 rp.deadlockfree = 0;
162 rp.live.assign(E, 0);
163 bool other = false;
164 for (std::size_t i = 0; i < rp.deadlocks.size(); ++i) other = other || rp.deadlocks[i] != 0;
165 rp.reversible = other ? 0 : -1;
166 }
167 rp.dead.assign(E, -1);
168 for (std::size_t e = 0; e < E; ++e)
169 if (fired[e]) rp.dead[e] = 0;
170 rp.nbscc = -1;
171 }
172 return rp;
173}
174
175} // namespace spn
176} // namespace line
177
178#endif // LINE_API_SPN_SPN_REACHPROPS_H
A network plus its refreshed NetworkStruct.
SpnReachProps spn_reachprops(const qn::NetworkStruct< T > &sn, const std::vector< double > &init=std::vector< double >(), std::size_t maxstates=0)
SpnReachGraph spn_reachgraph(const qn::NetworkStruct< T > &sn, const std::vector< double > &init=std::vector< double >(), std::size_t maxstates=0)
Conservation laws of a layered queueing network, enumerated from its structure.
Definition aoi_dist2ph.h:52
Reachability graph of a stochastic Petri net under the GSPN firing rule (Haverkort 1998,...
std::vector< bool > unboundedlevels
std::string status
complete, cap or unbounded
std::vector< std::vector< std::size_t > > E
edges (from, to, event), 0-based
std::vector< std::vector< long long > > M
markings, discovery order
std::vector< std::size_t > deadlocks
0-based marking indices
std::vector< int > dead
per event, tri-state
int nbscc
number of bottom SCCs (-1 unknown)
std::vector< double > bound
per level; NaN unknown, Inf unbounded
std::vector< std::vector< long long > > deadmarkings
std::vector< int > live
std::string status
the graph status