LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Loading...
Searching...
No Matches
spn_rec_enabled.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_REC_ENABLED_H
6#define LINE_API_SPN_SPN_REC_ENABLED_H
7
8/**
9 * @file
10 * @ingroup api_spn
11 * Enabling-degree distribution of one mode of a product-form stochastic Petri
12 * net, by the masked MDD-rec recursion.
13 *
14 * S. Balsamo, A. Marin, I. Stojic, FGCS 111 (2020) 475-490, Sec. 5.3.
15 *
16 * The enabling degree of a mode in marking m is
17 *
18 * e(m) = min_{l : I_l > 0} floor(m_l / I_l),
19 *
20 * zero when any inhibitor threshold is met. P(e >= k) is therefore the mass of
21 * the marking subset in which EVERY input level holds at least k*I_l tokens and
22 * no inhibitor fires, which is a per-level restriction and so exactly what
23 * `mdd::mdd_rec_masked` computes: the paper's second modified recurrence is the
24 * same walk under a different mask, not a second algorithm.
25 *
26 * The masses returned are UNNORMALISED, as in the paper; divide by G from
27 * `mdd::mdd_rec` for probabilities. `spn_metrics` does that and turns them into
28 * the transition measures.
29 */
30
31#include <cmath>
32#include <cstddef>
33#include <vector>
34
35#include "line/api/mdd/mdd.h"
38#include "line/num/number.h"
39#include "line/util/error.h"
40
41namespace line {
42namespace spn {
43
44/** Unnormalised enabling-degree masses of one mode. */
45template <class T>
47 /** ge[k] is the mass of {e >= k}; ge[0] is the whole reachable set. */
48 std::vector<T> ge;
49 /** eq[k] is the mass of {e == k}, i.e. ge[k] - ge[k+1]. */
50 std::vector<T> eq;
51 /** The largest enabling degree the place bounds permit, E_j in the paper. */
52 std::size_t max_degree = 0;
53};
54
55/**
56 * Enabling-degree masses of mode `mde` over the reachable set in `mdds`.
57 *
58 * @param mdds the reachable set built by `spn_mdd`
59 * @param g per-level product-form factors, one vector per level
60 * @param mde the mode, as returned in `SpnInfo::modes`
61 * @param nplacelevels how many leading levels are place levels
62 */
63template <class T>
64SpnEnabling<T> spn_rec_enabled(const mdd::MddStruct& mdds, const std::vector<std::vector<T>>& g,
65 const SpnMode<T>& mde, std::size_t nplacelevels) {
66 if (nplacelevels > mdds.K)
67 throw InputError("spn_rec_enabled: more place levels than diagram levels");
68 const T zero = num_traits<T>::from_int(0);
69
70 // E_j: the enabling degree cannot exceed what the tightest input place bound
71 // allows. A mode with no input place has no bound and is refused rather than
72 // silently truncated, matching spn_mdd's own refusal.
73 std::size_t emax = 0;
74 bool has_input = false;
75 for (std::size_t l = 0; l < nplacelevels; ++l) {
76 if (!(mde.enab[l] > 0)) continue;
77 const double top = static_cast<double>(mdds.domain[l] - 1);
78 const std::size_t cap = static_cast<std::size_t>(std::floor(top / mde.enab[l]));
79 emax = has_input ? (cap < emax ? cap : emax) : cap;
80 has_input = true;
81 }
82 if (!has_input)
83 throw InputError("spn_rec_enabled: the mode consumes from no place, so its enabling "
84 "degree is unbounded");
85
87 out.max_degree = emax;
88 out.ge.assign(emax + 2, zero);
89 out.eq.assign(emax + 2, zero);
90 for (std::size_t k = 0; k <= emax; ++k) {
91 mdd::MddMask mask(mdds.K);
92 for (std::size_t j = 0; j < mdds.K; ++j)
93 mask[j].assign(static_cast<std::size_t>(mdds.domain[j]), true);
94 for (std::size_t l = 0; l < nplacelevels; ++l) {
95 const double need = mde.enab[l] * static_cast<double>(k);
96 for (int v = 0; v < mdds.domain[l]; ++v) {
97 const bool short_of_tokens = static_cast<double>(v) < need;
98 const bool inhibited = static_cast<double>(v) >= mde.inhib[l];
99 // k = 0 asks only that the marking exist, so the inhibitor test
100 // belongs to k >= 1: e = 0 covers the inhibited markings too.
101 if (short_of_tokens || (k > 0 && inhibited))
102 mask[l][static_cast<std::size_t>(v)] = false;
103 }
104 }
105 out.ge[k] = mdd::mdd_rec_masked(mdds, g, mask);
106 }
107 for (std::size_t k = 0; k <= emax; ++k) out.eq[k] = T(out.ge[k] - out.ge[k + 1]);
108 return out;
109}
110
111} // namespace spn
112} // namespace line
113
114#endif // LINE_API_SPN_SPN_REC_ENABLED_H
InputError(const std::string &what)
Definition error.h:39
The exception types the port throws.
Quasi-reduced ordered Multi-valued Decision Diagram.
MDD-rec: the normalising constant of a product-form model whose reachable set is held in a decision d...
std::vector< std::vector< bool > > MddMask
Per-level admissible local values, the restriction of Sec.
Definition mdd_rec.h:65
T mdd_rec_masked(const MddStruct &mdds, const std::vector< std::vector< T > > &g, const MddMask &mask)
Unnormalised mass of the masked subset of the reachable set (Algorithm 1).
Definition mdd_rec.h:123
SpnEnabling< T > spn_rec_enabled(const mdd::MddStruct &mdds, const std::vector< std::vector< T > > &g, const SpnMode< T > &mde, std::size_t nplacelevels)
Enabling-degree masses of mode mde over the reachable set in mdds.
Number-type abstraction for the templated API port.
Decision-diagram reachable set and Kronecker rate descriptor of a stochastic Petri net,...
Plain-array export of an MDD, the input contract of mdd_mcd.
Definition mdd.h:57
std::vector< int > domain
domain[k] is the number of local states at level k.
Definition mdd.h:61
std::size_t K
Number of variable levels.
Definition mdd.h:59
Unnormalised enabling-degree masses of one mode.
std::size_t max_degree
The largest enabling degree the place bounds permit, E_j in the paper.
std::vector< T > eq
eq[k] is the mass of {e == k}, i.e.
std::vector< T > ge
ge[k] is the mass of {e >= k}; ge[0] is the whole reachable set.
One (transition, mode) pair of the net, in level coordinates.
Definition spn_mdd.h:86
std::vector< double > enab
Enabling multiplicity per place level.
Definition spn_mdd.h:92
std::vector< double > inhib
Inhibition threshold per place level; infinite when absent.
Definition spn_mdd.h:94