5#ifndef LINE_API_SPN_SPN_NET_H
6#define LINE_API_SPN_SPN_NET_H
45 std::vector<std::size_t> places;
50 std::vector<std::size_t> transitions, modes;
52 std::vector<std::vector<long long>> pre, post, C;
53 std::vector<std::vector<double>> inh;
54 std::vector<bool> immediate;
55 std::vector<double> prio, weight;
57 std::vector<std::string> ignored;
59 bool monotone()
const {
return ignored.empty(); }
61 std::vector<bool> enabled(
const std::vector<long long>& m)
const {
62 std::vector<bool> en(E,
true);
63 for (std::size_t e = 0; e < E; ++e)
64 for (std::size_t l = 0; l < n && en[e]; ++l)
65 if (pre[l][e] > m[l] || inh[l][e] <=
static_cast<double>(m[l])) en[e] =
false;
73 std::vector<bool> fireable(
const std::vector<long long>& m)
const {
74 std::vector<bool> en = enabled(m);
76 double best = -std::numeric_limits<double>::infinity();
77 for (std::size_t e = 0; e < E; ++e)
78 if (en[e] && immediate[e]) {
80 best = std::max(best, prio[e]);
82 if (!anyimm)
return en;
83 std::vector<bool> f(E,
false);
84 for (std::size_t e = 0; e < E; ++e) f[e] = en[e] && immediate[e] && prio[e] == best;
89inline long long spn_net_integer(
double x,
const char* what,
const std::string& caller) {
90 const double r = std::floor(x + 0.5);
91 if (std::fabs(x - r) > 1e-9)
92 throw InputError(ErrorKind{
"Model"}, caller +
": " + what +
93 " is not integral; a fractional arc multiplicity "
94 "has no Petri-net meaning");
95 return static_cast<long long>(r);
99SpnNet spn_build_net(
const qn::NetworkStruct<T>& sn,
const std::string& caller) {
100 std::vector<std::size_t> places,
trans;
101 for (std::size_t i = 1; i <= sn.nodes.size(); ++i) {
106 throw InputError(ErrorKind{
"Model"}, caller +
": the model holds no Place or no Transition node");
107 const std::size_t R = sn.nclasses;
108 for (std::size_t k = 0; k < R && k < sn.classes.size(); ++k)
109 if (!std::isfinite(sn.classes[k].population))
111 caller +
": class " + std::to_string(k + 1) +
112 " is open: its arrivals enter a place through a Source "
113 "routing rather than a transition, so the incidence "
114 "matrix does not describe the token game");
118 const std::size_t
P = places.size();
120 const std::size_t n = net.n;
121 net.pre.assign(n, std::vector<long long>());
122 net.post.assign(n, std::vector<long long>());
123 net.inh.assign(n, std::vector<double>());
124 const double inf = std::numeric_limits<double>::infinity();
125 for (std::size_t t = 0; t <
trans.
size(); ++t) {
126 const typename std::map<std::size_t, qn::TransitionParam<T>>::const_iterator it =
127 sn.transparam.find(trans[t]);
128 if (it == sn.transparam.end())
continue;
129 const qn::TransitionParam<T>& tp = it->second;
130 for (std::size_t m = 0; m < tp.nmodes; ++m) {
131 const Matrix<T>* en = m < tp.enabling.size() ? &tp.enabling[m] :
nullptr;
132 const Matrix<T>* ih = m < tp.inhibiting.size() ? &tp.inhibiting[m] :
nullptr;
133 const Matrix<T>* fi = m < tp.firing.size() ? &tp.firing[m] :
nullptr;
134 for (std::size_t pp = 0; pp <
P; ++pp) {
135 const std::size_t q = places[pp] - 1;
136 for (std::size_t k = 0; k < R; ++k) {
137 const std::size_t l = pp * R + k;
138 double a = 0, b = 0, h = inf;
139 if (en && q < en->rows() && k < en->cols())
140 a = std::max(0.0, num_traits<T>::to_double((*en)(q, k)));
141 if (fi && q < fi->rows() && k < fi->cols())
142 b = std::max(0.0, num_traits<T>::to_double((*fi)(q, k)));
143 if (ih && q < ih->rows() && k < ih->cols())
144 h = num_traits<T>::to_double((*ih)(q, k));
145 net.pre[l].push_back(spn_net_integer(a,
"an enabling arc", caller));
146 net.post[l].push_back(spn_net_integer(b,
"a firing arc", caller));
147 net.inh[l].push_back(h);
150 net.transitions.push_back(trans[t]);
151 net.modes.push_back(m);
152 net.immediate.push_back(m < tp.timing.size() &&
154 net.prio.push_back(m < tp.firingprio.size() ? tp.firingprio[m] : 1.0);
155 net.weight.push_back(m < tp.fireweight.size()
156 ? num_traits<T>::to_double(tp.fireweight[m])
160 net.E = net.transitions.size();
161 if (net.E == 0)
throw InputError(ErrorKind{
"Model"}, caller +
": the net has no firing mode");
162 net.C.assign(n, std::vector<long long>(net.E, 0));
164 for (std::size_t l = 0; l < n; ++l)
165 for (std::size_t e = 0; e < net.E; ++e) {
166 net.C[l][e] = net.post[l][e] - net.pre[l][e];
167 anyinh = anyinh || std::isfinite(net.inh[l][e]);
169 bool anyimm =
false, anytimed =
false;
170 std::vector<double> ip;
171 for (std::size_t e = 0; e < net.E; ++e) {
172 if (net.immediate[e]) {
174 if (std::find(ip.begin(), ip.end(), net.prio[e]) == ip.end()) ip.push_back(net.prio[e]);
179 if (anyinh) net.ignored.push_back(
"inhibitor");
180 if (anyimm && anytimed) net.ignored.push_back(
"immediate");
181 if (ip.size() > 1) net.ignored.push_back(
"priority");
185inline long long spn_marking_integer(
double x,
const std::string& caller) {
186 const double r = std::floor(x + 0.5);
187 if (std::fabs(x - r) > 1e-9 || r < 0)
189 caller +
": an initial marking must be a non-negative integer");
190 return static_cast<long long>(r);
195std::vector<long long> spn_initmarking(
const qn::NetworkStruct<T>& sn,
const SpnNet& net,
196 const std::vector<double>& init,
const std::string& caller) {
197 const std::size_t n = net.n, R = net.R,
P = net.places.size();
198 std::vector<long long> m0(n, 0);
200 if (init.size() != n)
202 caller +
": init must hold one token count per place level");
203 for (std::size_t i = 0; i < n; ++i) m0[i] = spn_marking_integer(init[i], caller);
207 std::vector<double> dec(n, 0.0);
208 for (std::size_t pp = 0; pp <
P; ++pp) {
209 const typename std::map<std::size_t, std::vector<T>>::const_iterator it =
210 sn.initmarking.find(net.places[pp]);
211 if (it == sn.initmarking.end())
continue;
212 for (std::size_t k = 0; k < R && k < it->second.size(); ++k) {
213 dec[pp * R + k] = num_traits<T>::to_double(it->second[k]);
214 anyset = anyset || dec[pp * R + k] > 0;
218 for (std::size_t i = 0; i < n; ++i) m0[i] = spn_marking_integer(dec[i], caller);
221 for (std::size_t k = 0; k < R; ++k) {
222 const std::size_t rs = sn.classes[k].refstat;
223 if (rs < 1 || rs > sn.station_to_node.size())
continue;
224 const std::size_t ref_node = sn.station_to_node[rs - 1];
225 for (std::size_t pp = 0; pp <
P; ++pp)
226 if (net.places[pp] == ref_node)
227 m0[pp * R + k] += spn_marking_integer(sn.classes[k].population, caller);
236inline std::vector<std::vector<long long>> spn_farkas(
const std::vector<std::vector<long long>>& A,
238 const std::size_t nr = A.size();
239 const std::size_t w = nc + nr;
240 std::vector<std::vector<long long>> rows(nr, std::vector<long long>(w, 0));
241 for (std::size_t p = 0; p < nr; ++p) {
242 for (std::size_t c = 0; c < nc; ++c) rows[p][c] = A[p][c];
245 for (std::size_t c = 0; c < nc; ++c) {
246 std::vector<std::vector<long long>> nxt;
247 for (std::size_t r = 0; r < rows.size(); ++r)
248 if (rows[r][c] == 0) nxt.push_back(rows[r]);
249 for (std::size_t a = 0; a < rows.size(); ++a) {
250 if (rows[a][c] <= 0)
continue;
251 for (std::size_t b = 0; b < rows.size(); ++b) {
252 if (rows[b][c] >= 0)
continue;
253 const long long pa = rows[a][c], nb = -rows[b][c];
254 const long long d = spn_gcd(pa, nb);
255 std::vector<long long> combo(w, 0);
257 for (std::size_t k = 0; k < w; ++k) {
258 combo[k] = (nb / d) * rows[a][k] + (pa / d) * rows[b][k];
259 g = spn_gcd(g, combo[k]);
262 for (std::size_t k = 0; k < w; ++k) combo[k] /= g;
264 for (std::size_t k = 0; k < nr; ++k) nz = nz || combo[nc + k] != 0;
265 if (nz) nxt.push_back(combo);
268 std::vector<bool> keep(nxt.size(),
true);
269 for (std::size_t r = 0; r < nxt.size(); ++r)
270 for (std::size_t s = 0; s < nxt.size(); ++s) {
271 if (s == r || !keep[r])
continue;
272 if (!spn_support_subset(nxt[s], nxt[r], nc, nr))
continue;
273 if (!spn_support_equal(nxt[s], nxt[r], nc, nr) || s < r) keep[r] =
false;
276 for (std::size_t r = 0; r < nxt.size(); ++r)
277 if (keep[r]) rows.push_back(nxt[r]);
279 std::vector<std::vector<long long>> X;
280 for (std::size_t r = 0; r < rows.size(); ++r) {
281 std::vector<long long> x(rows[r].begin() +
static_cast<std::ptrdiff_t
>(nc), rows[r].end());
283 for (std::size_t k = 0; k < nr; ++k) nonneg = nonneg && x[k] >= 0;
284 if (nonneg) X.push_back(x);
290inline std::vector<bool> spn_maxsiphon(
const std::vector<std::vector<long long>>& pre,
291 const std::vector<std::vector<long long>>& post,
292 std::vector<bool> X) {
293 const std::size_t n = pre.size();
294 const std::size_t E = n == 0 ? 0 : pre[0].size();
297 for (std::size_t l = 0; l < n; ++l) any = any || X[l];
299 std::vector<bool> fr(E,
true);
300 for (std::size_t e = 0; e < E; ++e)
301 for (std::size_t l = 0; l < n && fr[e]; ++l)
302 if (X[l] && pre[l][e] > 0) fr[e] =
false;
304 for (std::size_t l = 0; l < n; ++l) {
306 for (std::size_t e = 0; e < E; ++e)
307 if (fr[e] && post[l][e] > 0) {
317inline bool spn_isminimal(
const std::vector<std::vector<long long>>& pre,
318 const std::vector<std::vector<long long>>& post,
319 const std::vector<bool>& S) {
320 for (std::size_t p = 0; p < S.size(); ++p) {
322 std::vector<bool> X = S;
324 const std::vector<bool> Y = spn_maxsiphon(pre, post, X);
325 for (std::size_t l = 0; l < Y.size(); ++l)
326 if (Y[l])
return false;
335inline std::vector<std::vector<bool>> spn_minsiphons(
const std::vector<std::vector<long long>>& pre,
336 const std::vector<std::vector<long long>>& post) {
337 const std::size_t n = pre.size();
338 std::vector<std::vector<bool>> found;
339 std::vector<std::vector<bool>> stI(1, std::vector<bool>(n,
false));
340 std::vector<std::vector<bool>> stO(1, std::vector<bool>(n,
false));
341 while (!stI.empty()) {
342 const std::vector<bool> I = stI.back();
343 const std::vector<bool> O = stO.back();
346 std::vector<bool> notO(n);
347 for (std::size_t l = 0; l < n; ++l) notO[l] = !O[l];
348 const std::vector<bool> S = spn_maxsiphon(pre, post, notO);
349 bool anyS =
false, miss =
false;
350 for (std::size_t l = 0; l < n; ++l) {
352 miss = miss || (I[l] && !S[l]);
354 if (!anyS || miss)
continue;
356 for (std::size_t f = 0; f < found.size() && !inside; ++f) {
357 bool sub =
true,
nz =
false;
358 for (std::size_t l = 0; l < n; ++l) {
359 nz =
nz || found[f][l];
360 if (found[f][l] && !I[l]) sub =
false;
364 if (inside)
continue;
365 if (spn_isminimal(pre, post, S)) {
370 for (std::size_t l = 0; l < n; ++l)
375 if (q == n)
continue;
376 std::vector<bool> Oq = O, Iq = I;
384 std::stable_sort(found.begin(), found.end(),
385 [](
const std::vector<bool>& a,
const std::vector<bool>& b) {
386 std::vector<std::size_t> ia, ib;
387 for (std::size_t l = 0; l < a.size(); ++l) {
388 if (a[l]) ia.push_back(l);
389 if (b[l]) ib.push_back(l);
391 if (ia.size() != ib.size())
return ia.size() < ib.size();
398inline std::vector<std::vector<int>> spn_sets_to_int(
const std::vector<std::vector<bool>>& L) {
399 std::vector<std::vector<int>> out(L.size());
400 for (std::size_t i = 0; i < L.size(); ++i) {
401 out[i].resize(L[i].size());
402 for (std::size_t l = 0; l < L[i].size(); ++l) out[i][l] = L[i][l] ? 1 : 0;
408inline std::vector<std::vector<long long>> spn_transpose(
const std::vector<std::vector<long long>>& A,
410 std::vector<std::vector<long long>> B(nc, std::vector<long long>(A.size(), 0));
411 for (std::size_t i = 0; i < A.size(); ++i)
412 for (std::size_t j = 0; j < nc; ++j) B[j][i] = A[i][j];
UnsupportedError(const std::string &what)
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.
@ IMMEDIATE
fires with zero delay, resolved by weight and priority
Matrix< double > trans(const Matrix< double > &A)
Conservation laws of a layered queueing network, enumerated from its structure.
A queueing network and its refreshed NetworkStruct.
Number-type abstraction for the templated API port.
Minimal-support S-invariants (P-invariants) of a stochastic Petri net, and the load vector V = S m0.