LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Loading...
Searching...
No Matches
spn_structprops.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_STRUCTPROPS_H
6#define LINE_API_SPN_SPN_STRUCTPROPS_H
7
8/**
9 * @file
10 * @ingroup api_spn
11 * Structural properties of a stochastic Petri net, from its incidence matrix
12 * C alone (Murata 1989, sec. VI-B; Silva, Teruel and Colom 1998; Haverkort
13 * 1998, sec. 14.2). Each LP property is decided by one covering linear
14 * program over [z; t] with 0 <= t <= 1, t <= z, maximising sum(t): t_j = 1
15 * exactly when some solution has z_j > 0, so the program both decides the
16 * property and names the levels or events that satisfy it.
17 * bounded: exists y > 0 with y' C <= 0 (structurally bounded)
18 * conservative: exists y > 0 with y' C = 0
19 * consistent: exists x > 0 with C x = 0
20 * repetitive: exists x > 0 with C x >= 0
21 * The net classes (ordinary, state machine, marked graph, free choice,
22 * extended free choice, asymmetric choice) follow Murata's definitions on
23 * the arc supports. All of these ignore inhibitor arcs, priorities and the
24 * timed/immediate split; `ignored` lists those of the three the net uses.
25 * MATLAB twin: matlab/src/api/spn/spn_structprops.m.
26 */
27
28#include <cmath>
29#include <cstddef>
30#include <string>
31#include <vector>
32
34#include "line/util/lp_highs.h"
35#include "line/util/simplex.h"
36
37namespace line {
38namespace spn {
39
41 std::vector<std::size_t> places; ///< 1-based node indices, in level order
42 std::vector<std::size_t> transitions; ///< 1-based node index of each event
43 std::vector<std::size_t> modes; ///< 0-based mode index of each event
44 bool bounded = false, conservative = false, consistent = false, repetitive = false;
46 bool ordinary = false, statemachine = false, markedgraph = false;
47 bool freechoice = false, efc = false, asymmetric = false;
48 std::vector<std::string> ignored; ///< inhibitor, immediate, priority (when used)
49};
50
51namespace detail {
52
53/** Covering LP: which columns of z admit z_j > 0 with A z <= 0 (le) or A z = 0 (eq), z >= 0. */
54inline std::vector<bool> spn_cover(const std::vector<std::vector<long long>>& A, std::size_t N,
55 bool eq) {
56 lp::LpModel<double> lpm(2 * N);
57 for (std::size_t j = 0; j < N; ++j) {
58 lpm.set_cost(j, 0.0);
59 lpm.set_cost(N + j, 1.0);
60 lpm.set_bounds(N + j, 0.0, 1.0);
61 }
62 lpm.set_maximize(true);
63 for (std::size_t i = 0; i < A.size(); ++i) {
64 for (std::size_t j = 0; j < N; ++j)
65 if (A[i][j] != 0) lpm.row_add(j, static_cast<double>(A[i][j]));
66 lpm.emit(eq ? lp::LpSense::EQ : lp::LpSense::LE, 0.0);
67 }
68 for (std::size_t j = 0; j < N; ++j) {
69 lpm.row_add(N + j, 1.0);
70 lpm.row_add(j, -1.0);
71 lpm.emit(lp::LpSense::LE, 0.0);
72 }
73 const lp::LpSolution<double> sol = lp::lp_solve(lpm);
74 bool fin = sol.ok() && sol.x.size() == 2 * N;
75 for (std::size_t j = 0; fin && j < 2 * N; ++j) fin = std::isfinite(sol.x[j]);
76 if (!fin)
77 throw NumericError(ErrorKind{"Infeasible"},
78 "spn_structprops: the covering linear program returned no solution, "
79 "although z = 0 is feasible");
80 std::vector<bool> cov(N);
81 for (std::size_t j = 0; j < N; ++j) cov[j] = sol.x[N + j] > 0.5;
82 return cov;
83}
84
85inline bool spn_all(const std::vector<bool>& v) {
86 for (std::size_t i = 0; i < v.size(); ++i)
87 if (!v[i]) return false;
88 return true;
89}
90
91} // namespace detail
92
93template <class T>
95 const detail::SpnNet net = detail::spn_build_net(sn, "spn_structprops");
96 const std::size_t n = net.n, E = net.E;
97 const std::vector<std::vector<long long>> Ct = detail::spn_transpose(net.C, E);
98 std::vector<std::vector<long long>> negC(n, std::vector<long long>(E));
99 for (std::size_t l = 0; l < n; ++l)
100 for (std::size_t e = 0; e < E; ++e) negC[l][e] = -net.C[l][e];
102 sp.places = net.places;
103 sp.transitions = net.transitions;
104 sp.modes = net.modes;
105 sp.boundedlevels = detail::spn_cover(Ct, n, false);
106 sp.conservedlevels = detail::spn_cover(Ct, n, true);
107 sp.consistentevents = detail::spn_cover(net.C, E, true);
108 sp.repetitiveevents = detail::spn_cover(negC, E, false);
109 sp.bounded = detail::spn_all(sp.boundedlevels);
110 sp.conservative = detail::spn_all(sp.conservedlevels);
111 sp.consistent = detail::spn_all(sp.consistentevents);
112 sp.repetitive = detail::spn_all(sp.repetitiveevents);
113
114 bool ordinary = true;
115 std::vector<std::size_t> intake(E, 0), outfeed(E, 0), lout(n, 0), lin(n, 0);
116 for (std::size_t l = 0; l < n; ++l)
117 for (std::size_t e = 0; e < E; ++e) {
118 if (net.pre[l][e] > 0) {
119 ordinary = ordinary && net.pre[l][e] == 1;
120 ++intake[e];
121 ++lout[l];
122 }
123 if (net.post[l][e] > 0) {
124 ordinary = ordinary && net.post[l][e] == 1;
125 ++outfeed[e];
126 ++lin[l];
127 }
128 }
129 bool smach = ordinary, mgraph = ordinary;
130 for (std::size_t e = 0; e < E; ++e) smach = smach && intake[e] == 1 && outfeed[e] == 1;
131 for (std::size_t l = 0; l < n; ++l) mgraph = mgraph && lout[l] == 1 && lin[l] == 1;
132 bool fc = ordinary, efc = ordinary, ac = ordinary;
133 for (std::size_t p = 0; p < n; ++p) {
134 if (lout[p] > 1)
135 for (std::size_t e = 0; e < E; ++e)
136 if (net.pre[p][e] > 0 && intake[e] != 1) fc = false;
137 for (std::size_t q = 0; q < n; ++q) {
138 bool share = false, aonly = false, bonly = false;
139 for (std::size_t e = 0; e < E; ++e) {
140 const bool a = net.pre[p][e] > 0, b = net.pre[q][e] > 0;
141 share = share || (a && b);
142 aonly = aonly || (a && !b);
143 bonly = bonly || (b && !a);
144 }
145 if (share) {
146 if (aonly || bonly) efc = false;
147 if (aonly && bonly) ac = false;
148 }
149 }
150 }
151 sp.ordinary = ordinary;
152 sp.statemachine = smach;
153 sp.markedgraph = mgraph;
154 sp.freechoice = fc;
155 sp.efc = efc;
156 sp.asymmetric = ac;
157 sp.ignored = net.ignored;
158 return sp;
159}
160
161} // namespace spn
162} // namespace line
163
164#endif // LINE_API_SPN_SPN_STRUCTPROPS_H
NumericError(const std::string &what)
Definition error.h:112
Sparse LP in the natural form, with per-variable bounds.
Definition simplex.h:112
void set_maximize(bool m)
true to maximize c'x (the default), false to minimize.
Definition simplex.h:174
void emit(LpSense sense, const T &rhs)
Emit the accumulated row with the given relation and right-hand side.
Definition simplex.h:200
void set_cost(std::size_t j, const T &v)
Definition simplex.h:164
void set_bounds(std::size_t j, const T &lo, const T &hi)
Definition simplex.h:139
void row_add(std::size_t j, const T &v)
row(j) += v, the accumulation the MATLAB reference performs.
Definition simplex.h:188
A network plus its refreshed NetworkStruct.
A sparse LP backend for line::lp::LpModel, on HiGHS (MIT).
LpSolution< T > lp_solve(const LpModel< T > &model, std::size_t dense_max_cols=512)
Solve, choosing the backend by arithmetic and size.
Definition lp_highs.h:176
SpnStructProps spn_structprops(const qn::NetworkStruct< T > &sn)
Conservation laws of a layered queueing network, enumerated from its structure.
Definition aoi_dist2ph.h:52
Templated primal simplex with Bland's rule.
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
bool ok() const
Definition simplex.h:99
std::vector< T > x
primal solution in the ORIGINAL variable space
Definition simplex.h:96
std::vector< bool > repetitiveevents
std::vector< bool > boundedlevels
std::vector< bool > conservedlevels
std::vector< bool > consistentevents
std::vector< std::size_t > transitions
1-based node index of each event
std::vector< std::size_t > places
1-based node indices, in level order
std::vector< std::size_t > modes
0-based mode index of each event
std::vector< std::string > ignored
inhibitor, immediate, priority (when used)