159 const double inf = std::numeric_limits<double>::infinity();
165 "spn_lpbnd: the net has " + std::to_string(
sn.nclasses) +
166 " classes, and this translation puts ONE LEVEL PER PLACE; a coloured net needs a "
167 "level per (place, class) pair, which the moment relaxation would then be written "
168 "over. Solve the single-class net, or use the MATLAB, JAR or python twin, which "
169 "carry the class dimension.");
171 std::vector<std::size_t> places, transitions;
172 for (std::size_t i = 1; i <=
sn.nodes.size(); ++i) {
176 if (places.empty() || transitions.empty())
177 throw InputError(
"spn_lpbnd: the model holds no Place or no Transition node");
178 const std::size_t P = places.size();
179 const std::size_t L = P;
184 std::vector<SpnLpMode> md;
185 for (std::size_t t = 0; t < transitions.size(); ++t) {
186 const std::size_t ind = transitions[t];
187 const typename std::map<std::size_t, qn::TransitionParam<T>>::const_iterator it =
188 sn.transparam.find(ind);
189 if (it ==
sn.transparam.end())
continue;
191 for (std::size_t m = 0; m < tp.
nmodes; ++m) {
193 throw UnsupportedError(
"spn_lpbnd: mode " + std::to_string(m + 1) +
" of node " +
194 std::to_string(ind) +
195 " is IMMEDIATE; the moment relaxation is written for a net "
196 "whose transitions all have finite rates, so vanishing "
197 "states must be eliminated first");
199 throw UnsupportedError(
"spn_lpbnd: mode " + std::to_string(m + 1) +
" of node " +
200 std::to_string(ind) +
201 " has a marking-dependent firing rate; the uniformization "
202 "step needs one rate per mode");
205 throw UnsupportedError(
"spn_lpbnd: mode " + std::to_string(m + 1) +
" of node " +
206 std::to_string(ind) +
" has " + std::to_string(srv) +
207 " servers; the relaxation is derived under single-server "
208 "semantics, where the firing rate is mu*q. Its "
209 "infinite-server form needs the K-fold transition "
210 "expansion of the reference's Section 7, which is not "
213 throw InputError(
"spn_lpbnd: mode " + std::to_string(m + 1) +
" of node " +
214 std::to_string(ind) +
" has no firing process");
218 e.
enab.assign(L, 0.0);
219 e.
inhib.assign(L, inf);
220 e.
fire.assign(L, 0.0);
226 for (std::size_t pp = 0; pp < P; ++pp) {
227 const std::size_t q = places[pp] - 1;
237 const std::size_t nph = proc.
order();
238 if (options.markovian && nph > 1)
239 throw UnsupportedError(
"spn_lpbnd: mode " + std::to_string(m + 1) +
" of node " +
240 std::to_string(ind) +
241 " has a phase-type firing time; the relaxation is written "
242 "over the marking alone, and a phase-type mode needs the "
243 "state-machine expansion of the reference's Section 7, "
244 "which is not implemented. Use the operational bound, "
245 "which needs only the mean");
248 if (!(e.
rate > 0) || !std::isfinite(e.
rate))
249 throw InputError(
"spn_lpbnd: mode " + std::to_string(m + 1) +
" of node " +
250 std::to_string(ind) +
" has mean firing rate " +
251 std::to_string(e.
rate) +
"; a bound needs a finite positive one");
255 if (md.empty())
throw InputError(
"spn_lpbnd: the net has no firing mode");
256 const std::size_t E = md.size();
258 std::vector<double> mu(E, 0.0);
259 std::vector<std::vector<double>> net(E, std::vector<double>(L, 0.0));
260 for (std::size_t e = 0; e < E; ++e) {
262 for (std::size_t l = 0; l < L; ++l) net[e][l] = md[e].fire[l] - md[e].enab[l];
271 const std::vector<std::vector<long long>>& S = inv.
S;
272 const std::vector<long long>& V = inv.
V;
273 const std::vector<long long>& m0 = inv.
m0;
274 std::vector<double> B(L, inf);
275 for (std::size_t i = 0; i < S.size(); ++i)
276 for (std::size_t l = 0; l < L; ++l)
278 B[l] = std::min(B[l], std::floor(
static_cast<double>(V[i]) /
279 static_cast<double>(S[i][l])));
282 const std::size_t ix = 0;
283 const std::size_t iq = L;
284 const std::size_t ith = L + E;
285 const std::size_t iu = L + 2 * E;
286 const std::size_t iy = L + 3 * E;
287 const std::size_t nv = options.markovian ? L + 3 * E + L * E : L + 3 * E;
290 for (std::size_t e = 0; e < E; ++e) lpm.
set_bounds(iq + e, 0.0, 1.0);
291 for (std::size_t l = 0; l < L; ++l) {
292 if (std::isfinite(B[l])) {
294 if (options.markovian)
295 for (std::size_t e = 0; e < E; ++e) lpm.
set_bounds(iy + l * E + e, 0.0, B[l]);
299 const double tol = options.tol;
302 for (std::size_t e = 0; e < E; ++e) {
311 for (std::size_t l = 0; l < L; ++l) {
313 for (std::size_t e = 0; e < E; ++e) lpm.
row_add(iq + e, mu[e] * net[e][l]);
324 if (options.markovian) {
325 for (std::size_t l1 = 0; l1 < L; ++l1)
326 for (std::size_t l2 = l1; l2 < L; ++l2) {
328 for (std::size_t e = 0; e < E; ++e) {
332 lpm.
row_add(iy + l1 * E + e, mu[e] * net[e][l2]);
333 lpm.
row_add(iy + l2 * E + e, mu[e] * net[e][l1]);
334 lpm.
row_add(iq + e, mu[e] * net[e][l1] * net[e][l2]);
341 if (options.assumelive) {
343 for (std::size_t e = 0; e < E; ++e) lpm.
row_add(iq + e, 1.0);
345 if (options.markovian)
346 for (std::size_t l = 0; l < L; ++l) {
349 for (std::size_t e = 0; e < E; ++e) lpm.
row_add(iy + l * E + e, -1.0);
356 for (std::size_t e1 = 0; e1 < E; ++e1)
357 for (std::size_t e2 = 0; e2 < E; ++e2) {
358 if (e1 == e2)
continue;
359 bool dominated =
true;
360 for (std::size_t l = 0; l < L && dominated; ++l)
361 if (!(md[e1].enab[l] <= md[e2].enab[l] && md[e1].inhib[l] >= md[e2].inhib[l]))
373 if (options.markovian)
374 for (std::size_t l = 0; l < L; ++l) {
375 if (!std::isfinite(B[l]))
continue;
376 for (std::size_t e = 0; e < E; ++e) {
378 lpm.
row_add(iy + l * E + e, 1.0);
383 lpm.
row_add(iy + l * E + e, -1.0);
388 lpm.
row_add(ix + l, 1.0 - 1.0 / B[l]);
389 lpm.
row_add(iy + l * E + e, -1.0);
395 for (std::size_t i = 0; i < S.size(); ++i) {
397 for (std::size_t l = 0; l < L; ++l) lpm.
row_add(ix + l,
static_cast<double>(S[i][l]));
398 lpm.
emit_eq(
static_cast<double>(V[i]));
399 if (options.markovian)
400 for (std::size_t e = 0; e < E; ++e) {
402 for (std::size_t l = 0; l < L; ++l)
403 lpm.
row_add(iy + l * E + e,
static_cast<double>(S[i][l]));
404 lpm.
row_add(iq + e, -
static_cast<double>(V[i]));
410 for (std::size_t l = 0; l < L; ++l) {
413 for (std::size_t e = 0; e < E; ++e) lpm.
row_add(iu + e, -net[e][l]);
414 lpm.
emit_eq(
static_cast<double>(m0[l]));
418 if (options.markovian) {
420 for (std::size_t e = 0; e < E; ++e) mutot += mu[e];
421 for (std::size_t l = 0; l < L; ++l) {
422 for (std::size_t e = 0; e < E; ++e) {
424 lpm.
row_add(iy + l * E + e, 1.0);
427 if (md[e].enab[l] > 0) {
429 lpm.
row_add(iy + l * E + e, 1.0);
430 lpm.
row_add(iq + e, -md[e].enab[l]);
433 if (std::isfinite(md[e].inhib[l])) {
435 lpm.
row_add(iy + l * E + e, 1.0);
436 lpm.
row_add(iq + e, -(md[e].inhib[l] - 1.0));
442 for (std::size_t e = 0; e < E; ++e) lpm.
row_add(iy + l * E + e, -mu[e]);
445 for (std::size_t e = 0; e < E; ++e) {
447 std::size_t nent = 0;
448 bool inhibited =
false;
449 for (std::size_t l = 0; l < L; ++l) {
450 if (md[e].enab[l] > 0) {
454 if (std::isfinite(md[e].inhib[l])) inhibited =
true;
456 if (nent == 1 && !inhibited) {
459 lpm.
row_add(iy + ent * E + e, -1.0);
460 lpm.
emit_le(md[e].enab[ent] - 1.0 + tol);
466 for (std::size_t e = 0; e < E; ++e) {
467 std::vector<std::size_t> ent, inh;
468 for (std::size_t l = 0; l < L; ++l) {
469 if (md[e].enab[l] > 0) ent.push_back(l);
470 if (std::isfinite(md[e].inhib[l])) inh.push_back(l);
472 const std::size_t d = ent.size() + inh.size();
473 if (d == 0)
continue;
474 bool entBounded = !ent.empty();
475 for (std::size_t j = 0; j < ent.size(); ++j)
476 if (!std::isfinite(B[ent[j]])) entBounded =
false;
482 for (std::size_t j = 0; j < ent.size() && ok; ++j) {
483 const std::size_t l = ent[j];
484 const double den = B[l] - md[e].enab[l] + 1.0;
489 lpm.
row_add(ix + l, -1.0 / den);
493 for (std::size_t j = 0; j < inh.size(); ++j)
494 lpm.
row_add(ix + inh[j], 1.0 / md[e].inhib[inh[j]]);
506 for (std::size_t j = 0; j < inh.size(); ++j) {
507 const std::size_t l = inh[j];
508 if (!std::isfinite(B[l]) || B[l] - md[e].inhib[l] + 1.0 <= 0) ok =
false;
511 const std::size_t
nc = ent.size();
512 std::vector<unsigned long> combos;
514 for (
unsigned long c = 0; c < (1UL <<
nc); ++c) combos.push_back(c);
517 combos.push_back((1UL <<
nc) - 1);
519 for (std::size_t ci = 0; ci < combos.size(); ++ci) {
520 const unsigned long c = combos[ci];
522 lpm.
row_add(iq + e,
static_cast<double>(d));
524 for (std::size_t j = 0; j < inh.size(); ++j) {
525 const std::size_t l = inh[j];
526 const double den = B[l] - md[e].inhib[l] + 1.0;
528 lpm.
row_add(ix + l, 1.0 / den);
530 for (std::size_t j = 0; j <
nc; ++j) {
531 const std::size_t l = ent[j];
532 if (((c >> j) & 1UL) == 0) {
533 lpm.
row_add(ix + l, -1.0 / md[e].enab[l]);
544 if (options.markovian)
545 for (std::size_t l = 0; l < L; ++l) {
547 for (std::size_t e = 0; e < E; ++e)
548 if (md[e].enab[l] > 0) out += mu[e];
549 if (out <= 0)
continue;
552 for (std::size_t e = 0; e < E; ++e) lpm.
row_add(iq + e, -mu[e] * md[e].fire[l]);
561 std::vector<double> c(nv, 0.0);
562 for (std::size_t l = 0; l < L; ++l) {
563 std::fill(c.begin(), c.end(), 0.0);
565 out.
tokens_lo[l] = detail::spn_lp_opt(lpm, c,
true);
566 out.
tokens_hi[l] = detail::spn_lp_opt(lpm, c,
false);
567 std::fill(c.begin(), c.end(), 0.0);
569 for (std::size_t e = 0; e < E; ++e)
570 if (md[e].enab[l] > 0) {
571 c[iq + e] = mu[e] * md[e].enab[l];
574 out.
place_tput_lo[l] = any ? detail::spn_lp_opt(lpm, c,
true) : 0.0;
575 out.
place_tput_hi[l] = any ? detail::spn_lp_opt(lpm, c,
false) : 0.0;
581 for (std::size_t e = 0; e < E; ++e) {
582 std::fill(c.begin(), c.end(), 0.0);
585 out.
mode_tput_hi[e] = detail::spn_lp_opt(lpm, c,
false);
586 std::fill(c.begin(), c.end(), 0.0);
589 out.
mode_util_hi[e] = detail::spn_lp_opt(lpm, c,
false);
594 for (std::size_t pp = 0; pp < P; ++pp) out.
levelname[pp] =
sn.nodes[places[pp] - 1].name;