LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Loading...
Searching...
No Matches
spn_reachgraph.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_REACHGRAPH_H
6#define LINE_API_SPN_SPN_REACHGRAPH_H
7
8/**
9 * @file
10 * @ingroup api_spn
11 * Reachability graph of a stochastic Petri net under the GSPN firing rule
12 * (Haverkort 1998, sec. 14.2; Ajmone Marsan et al.). An event is enabled in
13 * m when m covers its enabling arcs and no inhibitor threshold is reached;
14 * when any immediate event is enabled only the enabled immediate events of
15 * the highest priority may fire, otherwise every enabled event may. The
16 * markings are found breadth first from m0 and numbered in discovery order;
17 * E lists the edges (from, to, event), 0-based.
18 *
19 * status is "complete" when the graph is finite and fully explored, "cap"
20 * when maxstates markings were found and another one appeared, and
21 * "unbounded" when a new marking strictly covers one of its ancestors
22 * (Karp-Miller), which proves unboundedness only for a monotone net: the
23 * covering test runs only without inhibitor arcs, priorities and mixed
24 * timed/immediate events. unboundedlevels marks the strictly larger levels.
25 * tangible(i) is false when an immediate event may fire in marking i.
26 * QUEUEING places carry state the marking does not hold and are refused.
27 * MATLAB twin: matlab/src/api/spn/spn_reachgraph.m.
28 */
29
30#include <cstddef>
31#include <map>
32#include <string>
33#include <vector>
34
36
37namespace line {
38namespace spn {
39
40/** Default cap on the number of explored markings. */
41constexpr std::size_t SPN_REACH_MAXSTATES = 100000;
42
44 std::vector<std::size_t> places; ///< 1-based node indices, in level order
45 std::vector<std::size_t> transitions; ///< 1-based node index of each event
46 std::vector<std::size_t> modes; ///< 0-based mode index of each event
47 std::vector<std::vector<long long>> M; ///< markings, discovery order
48 std::vector<std::vector<std::size_t>> E; ///< edges (from, to, event), 0-based
49 std::vector<bool> tangible;
50 std::string status; ///< complete, cap or unbounded
51 std::vector<bool> unboundedlevels;
52 std::vector<long long> m0;
53};
54
55namespace detail {
56
57template <class T>
58void spn_check_places(const qn::NetworkStruct<T>& sn, const SpnNet& net, const std::string& caller) {
59 for (std::size_t pp = 0; pp < net.places.size(); ++pp) {
60 const std::size_t nd = net.places[pp];
61 const std::size_t ist = sn.nodes[nd - 1].station;
62 if (ist >= 1 && ist <= sn.stations.size() &&
63 sn.stations[ist - 1].sched != lang::SchedStrategy::INF)
64 throw UnsupportedError(ErrorKind{"QueueingPlace"},
65 caller + ": place " + sn.nodes[nd - 1].name +
66 " is a QUEUEING place: its embedded service is state "
67 "that the marking does not carry");
68 }
69}
70
71inline SpnReachGraph spn_explore(const SpnNet& net, const std::vector<long long>& m0,
72 std::size_t maxstates) {
73 const std::size_t n = net.n;
74 SpnReachGraph g;
75 g.places = net.places;
76 g.transitions = net.transitions;
77 g.modes = net.modes;
78 g.m0 = m0;
79 g.status = "complete";
80 g.unboundedlevels.assign(n, false);
81 std::vector<std::size_t> parent;
82 std::map<std::vector<long long>, std::size_t> key;
83 g.M.push_back(m0);
84 parent.push_back(0); // the root has no parent; guarded by a == 0 below
85 key[m0] = 0;
86 const bool monotone = net.monotone();
87 for (std::size_t head = 0; head < g.M.size() && g.status == "complete"; ++head) {
88 const std::vector<long long> m = g.M[head];
89 const std::vector<bool> f = net.fireable(m);
90 for (std::size_t e = 0; e < net.E; ++e) {
91 if (!f[e]) continue;
92 std::vector<long long> mp(n);
93 for (std::size_t l = 0; l < n; ++l) mp[l] = m[l] - net.pre[l][e] + net.post[l][e];
94 const std::map<std::vector<long long>, std::size_t>::const_iterator it = key.find(mp);
95 if (it != key.end()) {
96 g.E.push_back(std::vector<std::size_t>{head, it->second, e});
97 continue;
98 }
99 if (g.M.size() >= maxstates) {
100 g.status = "cap";
101 break;
102 }
103 const std::size_t id = g.M.size();
104 g.M.push_back(mp);
105 parent.push_back(head);
106 key[mp] = id;
107 g.E.push_back(std::vector<std::size_t>{head, id, e});
108 if (monotone) {
109 std::size_t a = head;
110 for (;;) {
111 bool le = true, lt = false;
112 for (std::size_t l = 0; l < n; ++l) {
113 le = le && g.M[a][l] <= mp[l];
114 lt = lt || g.M[a][l] < mp[l];
115 }
116 if (le && lt) {
117 for (std::size_t l = 0; l < n; ++l) g.unboundedlevels[l] = mp[l] > g.M[a][l];
118 g.status = "unbounded";
119 break;
120 }
121 if (a == 0) break;
122 a = parent[a];
123 }
124 if (g.status != "complete") break;
125 }
126 }
127 }
128 g.tangible.assign(g.M.size(), true);
129 for (std::size_t i = 0; i < g.M.size(); ++i) {
130 const std::vector<bool> f = net.fireable(g.M[i]);
131 for (std::size_t e = 0; e < net.E; ++e)
132 if (f[e] && net.immediate[e]) g.tangible[i] = false;
133 }
134 return g;
135}
136
137} // namespace detail
138
139/**
140 * @param init initial marking per level; empty: the declared Place
141 * state, else the reference station populations
142 * @param maxstates cap on the number of markings (0: SPN_REACH_MAXSTATES)
143 */
144template <class T>
146 const std::vector<double>& init = std::vector<double>(),
147 std::size_t maxstates = 0) {
148 const detail::SpnNet net = detail::spn_build_net(sn, "spn_reachgraph");
149 detail::spn_check_places(sn, net, "spn_reachgraph");
150 const std::vector<long long> m0 = detail::spn_initmarking(sn, net, init, "spn_reachgraph");
151 return detail::spn_explore(net, m0, maxstates == 0 ? SPN_REACH_MAXSTATES : maxstates);
152}
153
154} // namespace spn
155} // namespace line
156
157#endif // LINE_API_SPN_SPN_REACHGRAPH_H
UnsupportedError(const std::string &what)
Definition error.h:119
A network plus its refreshed NetworkStruct.
constexpr std::size_t SPN_REACH_MAXSTATES
Default cap on the number of explored markings.
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
Shared net model of the Petri-net property functions (spn_tinvariants, spn_siphons,...
The <kind> part of a LINE:<category>:<kind> identifier (see _kb/17-error-vocabulary....
Definition error.h:37
std::vector< bool > unboundedlevels
std::vector< std::size_t > modes
0-based mode index of each event
std::vector< std::size_t > places
1-based node indices, in level order
std::vector< long long > m0
std::vector< std::size_t > transitions
1-based node index of each event
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< bool > tangible