160 const double inf = std::numeric_limits<double>::infinity();
162 std::vector<std::size_t> places, transitions;
163 for (std::size_t i = 1; i <=
sn.nodes.size(); ++i) {
167 if (places.empty() || transitions.empty())
168 throw InputError(
"spn_lpbnd: the model holds no Place or no Transition node");
169 const std::size_t P = places.size();
170 const std::size_t R =
sn.nclasses;
171 const std::size_t L = P * R;
176 std::vector<SpnLpMode> md;
177 for (std::size_t t = 0; t < transitions.size(); ++t) {
178 const std::size_t ind = transitions[t];
179 const typename std::map<std::size_t, qn::TransitionParam<T>>::const_iterator it =
180 sn.transparam.find(ind);
181 if (it ==
sn.transparam.end())
continue;
183 for (std::size_t m = 0; m < tp.
nmodes; ++m) {
186 std::to_string(ind) +
187 " is IMMEDIATE; the moment relaxation is written for a net "
188 "whose transitions all have finite rates, so vanishing "
189 "states must be eliminated first");
192 std::to_string(ind) +
193 " has a marking-dependent firing rate; the uniformization "
194 "step needs one rate per mode");
198 std::to_string(ind) +
" has " + std::to_string(srv) +
199 " servers; the relaxation is derived under single-server "
200 "semantics, where the firing rate is mu*q. Its "
201 "infinite-server form needs the K-fold transition "
202 "expansion of the reference's Section 7, which is not "
205 throw InputError(
"spn_lpbnd: mode " + std::to_string(m + 1) +
" of node " +
206 std::to_string(ind) +
" has no firing process");
210 e.
enab.assign(L, 0.0);
211 e.
inhib.assign(L, inf);
212 e.
fire.assign(L, 0.0);
218 for (std::size_t pp = 0; pp < P; ++pp) {
219 const std::size_t q = places[pp] - 1;
220 for (std::size_t k = 0; k < R; ++k) {
221 const std::size_t l = pp * R + k;
222 if (en && q < en->rows() && k < en->cols())
224 if (ih && q < ih->rows() && k < ih->cols())
226 if (fi && q < fi->rows() && k < fi->cols())
235 const std::size_t nph = proc.
order();
236 if (options.markovian && nph > 1)
238 std::to_string(ind) +
239 " has a phase-type firing time; the relaxation is written "
240 "over the marking alone, and a phase-type mode needs the "
241 "state-machine expansion of the reference's Section 7, "
242 "which is not implemented. Use the operational bound, "
243 "which needs only the mean");
246 if (!(e.
rate > 0) || !std::isfinite(e.
rate))
247 throw InputError(
"spn_lpbnd: mode " + std::to_string(m + 1) +
" of node " +
248 std::to_string(ind) +
" has mean firing rate " +
249 std::to_string(e.
rate) +
"; a bound needs a finite positive one");
253 if (md.empty())
throw InputError(
"spn_lpbnd: the net has no firing mode");
254 const std::size_t E = md.size();
256 std::vector<double> mu(E, 0.0);
257 std::vector<std::vector<double>> net(E, std::vector<double>(L, 0.0));
258 for (std::size_t e = 0; e < E; ++e) {
260 for (std::size_t l = 0; l < L; ++l) net[e][l] = md[e].fire[l] - md[e].enab[l];
271 std::vector<double> init = options.init;
273 std::vector<double> declared(L, 0.0);
275 for (std::size_t pp = 0; pp < P; ++pp) {
276 const typename std::map<std::size_t, std::vector<T>>::const_iterator it =
277 sn.initmarking.find(places[pp]);
278 if (it ==
sn.initmarking.end())
continue;
279 for (std::size_t k = 0; k < R && k < it->second.size(); ++k) {
281 declared[pp * R + k] = v > 0 ? v : 0.0;
282 anyset = anyset || v > 0;
285 if (anyset) init = declared;
288 const std::vector<std::vector<long long>>& S = inv.
S;
289 const std::vector<long long>& V = inv.
V;
290 const std::vector<long long>& m0 = inv.
m0;
291 std::vector<double> B(L, inf);
292 for (std::size_t i = 0; i < S.size(); ++i)
293 for (std::size_t l = 0; l < L; ++l)
295 B[l] = std::min(B[l], std::floor(
static_cast<double>(V[i]) /
296 static_cast<double>(S[i][l])));
299 const std::size_t ix = 0;
300 const std::size_t iq = L;
301 const std::size_t ith = L + E;
302 const std::size_t iu = L + 2 * E;
303 const std::size_t iy = L + 3 * E;
304 const std::size_t nv = options.markovian ? L + 3 * E + L * E : L + 3 * E;
307 for (std::size_t e = 0; e < E; ++e) lpm.
set_bounds(iq + e, 0.0, 1.0);
308 for (std::size_t l = 0; l < L; ++l) {
309 if (std::isfinite(B[l])) {
311 if (options.markovian)
312 for (std::size_t e = 0; e < E; ++e) lpm.
set_bounds(iy + l * E + e, 0.0, B[l]);
316 const double tol = options.tol;
319 for (std::size_t e = 0; e < E; ++e) {
328 for (std::size_t l = 0; l < L; ++l) {
330 for (std::size_t e = 0; e < E; ++e) lpm.
row_add(iq + e, mu[e] * net[e][l]);
341 if (options.markovian) {
342 for (std::size_t l1 = 0; l1 < L; ++l1)
343 for (std::size_t l2 = l1; l2 < L; ++l2) {
345 for (std::size_t e = 0; e < E; ++e) {
349 lpm.
row_add(iy + l1 * E + e, mu[e] * net[e][l2]);
350 lpm.
row_add(iy + l2 * E + e, mu[e] * net[e][l1]);
351 lpm.
row_add(iq + e, mu[e] * net[e][l1] * net[e][l2]);
358 if (options.assumelive) {
360 for (std::size_t e = 0; e < E; ++e) lpm.
row_add(iq + e, 1.0);
362 if (options.markovian)
363 for (std::size_t l = 0; l < L; ++l) {
366 for (std::size_t e = 0; e < E; ++e) lpm.
row_add(iy + l * E + e, -1.0);
373 for (std::size_t e1 = 0; e1 < E; ++e1)
374 for (std::size_t e2 = 0; e2 < E; ++e2) {
375 if (e1 == e2)
continue;
376 bool dominated =
true;
377 for (std::size_t l = 0; l < L && dominated; ++l)
378 if (!(md[e1].enab[l] <= md[e2].enab[l] && md[e1].inhib[l] >= md[e2].inhib[l]))
390 if (options.markovian)
391 for (std::size_t l = 0; l < L; ++l) {
392 if (!std::isfinite(B[l]))
continue;
393 for (std::size_t e = 0; e < E; ++e) {
395 lpm.
row_add(iy + l * E + e, 1.0);
400 lpm.
row_add(iy + l * E + e, -1.0);
405 lpm.
row_add(ix + l, 1.0 - 1.0 / B[l]);
406 lpm.
row_add(iy + l * E + e, -1.0);
412 for (std::size_t i = 0; i < S.size(); ++i) {
414 for (std::size_t l = 0; l < L; ++l) lpm.
row_add(ix + l,
static_cast<double>(S[i][l]));
415 lpm.
emit_eq(
static_cast<double>(V[i]));
416 if (options.markovian)
417 for (std::size_t e = 0; e < E; ++e) {
419 for (std::size_t l = 0; l < L; ++l)
420 lpm.
row_add(iy + l * E + e,
static_cast<double>(S[i][l]));
421 lpm.
row_add(iq + e, -
static_cast<double>(V[i]));
427 for (std::size_t l = 0; l < L; ++l) {
430 for (std::size_t e = 0; e < E; ++e) lpm.
row_add(iu + e, -net[e][l]);
431 lpm.
emit_eq(
static_cast<double>(m0[l]));
435 if (options.markovian) {
437 for (std::size_t e = 0; e < E; ++e) mutot += mu[e];
438 for (std::size_t l = 0; l < L; ++l) {
439 for (std::size_t e = 0; e < E; ++e) {
441 lpm.
row_add(iy + l * E + e, 1.0);
444 if (md[e].enab[l] > 0) {
446 lpm.
row_add(iy + l * E + e, 1.0);
447 lpm.
row_add(iq + e, -md[e].enab[l]);
450 if (std::isfinite(md[e].inhib[l])) {
452 lpm.
row_add(iy + l * E + e, 1.0);
453 lpm.
row_add(iq + e, -(md[e].inhib[l] - 1.0));
459 for (std::size_t e = 0; e < E; ++e) lpm.
row_add(iy + l * E + e, -mu[e]);
462 for (std::size_t e = 0; e < E; ++e) {
464 std::size_t nent = 0;
465 bool inhibited =
false;
466 for (std::size_t l = 0; l < L; ++l) {
467 if (md[e].enab[l] > 0) {
471 if (std::isfinite(md[e].inhib[l])) inhibited =
true;
473 if (nent == 1 && !inhibited) {
476 lpm.
row_add(iy + ent * E + e, -1.0);
477 lpm.
emit_le(md[e].enab[ent] - 1.0 + tol);
483 for (std::size_t e = 0; e < E; ++e) {
484 std::vector<std::size_t> ent, inh;
485 for (std::size_t l = 0; l < L; ++l) {
486 if (md[e].enab[l] > 0) ent.push_back(l);
487 if (std::isfinite(md[e].inhib[l])) inh.push_back(l);
489 const std::size_t d = ent.size() + inh.size();
490 if (d == 0)
continue;
491 bool entBounded = !ent.empty();
492 for (std::size_t j = 0; j < ent.size(); ++j)
493 if (!std::isfinite(B[ent[j]])) entBounded =
false;
499 for (std::size_t j = 0; j < ent.size() && ok; ++j) {
500 const std::size_t l = ent[j];
501 const double den = B[l] - md[e].enab[l] + 1.0;
506 lpm.
row_add(ix + l, -1.0 / den);
510 for (std::size_t j = 0; j < inh.size(); ++j)
511 lpm.
row_add(ix + inh[j], 1.0 / md[e].inhib[inh[j]]);
523 for (std::size_t j = 0; j < inh.size(); ++j) {
524 const std::size_t l = inh[j];
525 if (!std::isfinite(B[l]) || B[l] - md[e].inhib[l] + 1.0 <= 0) ok =
false;
528 const std::size_t
nc = ent.size();
529 std::vector<unsigned long> combos;
531 for (
unsigned long c = 0; c < (1UL <<
nc); ++c) combos.push_back(c);
534 combos.push_back((1UL <<
nc) - 1);
536 for (std::size_t ci = 0; ci < combos.size(); ++ci) {
537 const unsigned long c = combos[ci];
539 lpm.
row_add(iq + e,
static_cast<double>(d));
541 for (std::size_t j = 0; j < inh.size(); ++j) {
542 const std::size_t l = inh[j];
543 const double den = B[l] - md[e].inhib[l] + 1.0;
545 lpm.
row_add(ix + l, 1.0 / den);
547 for (std::size_t j = 0; j <
nc; ++j) {
548 const std::size_t l = ent[j];
549 if (((c >> j) & 1UL) == 0) {
550 lpm.
row_add(ix + l, -1.0 / md[e].enab[l]);
561 if (options.markovian)
562 for (std::size_t l = 0; l < L; ++l) {
564 for (std::size_t e = 0; e < E; ++e)
565 if (md[e].enab[l] > 0) out += mu[e];
566 if (out <= 0)
continue;
569 for (std::size_t e = 0; e < E; ++e) lpm.
row_add(iq + e, -mu[e] * md[e].fire[l]);
573 if (options.verbose) {
576 for (std::size_t i = 0; i < lpm.
num_rows(); ++i)
578 std::printf(
"\nSPN -> LP: %zu place levels, %zu modes, %zu variables, %zu equalities, "
579 "%zu inequalities\n",
580 L, E, nv, neq, lpm.
num_rows() - neq);
588 std::vector<double> c(nv, 0.0);
589 for (std::size_t l = 0; l < L; ++l) {
590 std::fill(c.begin(), c.end(), 0.0);
592 out.
tokens_lo[l] = detail::spn_lp_opt(lpm, c,
true);
593 out.
tokens_hi[l] = detail::spn_lp_opt(lpm, c,
false);
594 std::fill(c.begin(), c.end(), 0.0);
596 for (std::size_t e = 0; e < E; ++e)
597 if (md[e].enab[l] > 0) {
598 c[iq + e] = mu[e] * md[e].enab[l];
601 out.
place_tput_lo[l] = any ? detail::spn_lp_opt(lpm, c,
true) : 0.0;
602 out.
place_tput_hi[l] = any ? detail::spn_lp_opt(lpm, c,
false) : 0.0;
608 for (std::size_t e = 0; e < E; ++e) {
609 std::fill(c.begin(), c.end(), 0.0);
612 out.
mode_tput_hi[e] = detail::spn_lp_opt(lpm, c,
false);
613 std::fill(c.begin(), c.end(), 0.0);
616 out.
mode_util_hi[e] = detail::spn_lp_opt(lpm, c,
false);
621 for (std::size_t pp = 0; pp < P; ++pp)
622 for (std::size_t k = 0; k < R; ++k)
623 out.
levelname[pp * R + k] =
sn.nodes[places[pp] - 1].name +
"." +
sn.classes[k].name;