LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Loading...
Searching...
No Matches
spn_metrics.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_METRICS_H
6#define LINE_API_SPN_SPN_METRICS_H
7
8/**
9 * @file
10 * @ingroup api_spn
11 * Stationary measures of a product-form stochastic Petri net from the MDD-rec
12 * masses.
13 *
14 * S. Balsamo, A. Marin, I. Stojic, FGCS 111 (2020) 475-490, Sec. 3.1 for the
15 * definitions and Sec. 5.3 for the recursions they are read off.
16 *
17 * n(P_j) = sum_k k P(m_j = k) mean tokens
18 * u(P_j) = 1 - P(m_j = 0) place utilization
19 * u(T_j) = P(e_j >= 1) transition utilization
20 * x(T_j) = sum_k min(k, c_j) W(T_j) P(e_j = k) throughput
21 * x(P_j) = sum_T I_j(T) x(T) tokens removed per unit time
22 *
23 * ONE DEVIATION FROM THE PAPER'S x(T_j), AND IT IS A GENERALISATION. The paper
24 * writes x(T_j) = sum_k k W(T_j) P(e_j = k), which is INFINITE-SERVER firing
25 * semantics -- every enabling set fires in parallel. LINE's own rate law is
26 * min(enabling degree, nmodeservers) * W(T), so `c_j` above is the mode's server
27 * count: c_j = 1 recovers single-server semantics, x = W(T) P(e >= 1), and
28 * c_j = infinity recovers the paper's formula exactly. Using the paper's form
29 * for a single-server mode would report a throughput that grows with the token
30 * population of a net whose transition can only fire one set at a time.
31 *
32 * The measures come out of ONE reachable set and ONE set of g_l, so they are
33 * mutually consistent by construction: no per-measure fixed point, no iteration.
34 */
35
36#include <cmath>
37#include <cstddef>
38#include <limits>
39#include <vector>
40
41#include "line/api/mdd/mdd.h"
45#include "line/num/number.h"
46#include "line/util/error.h"
47
48namespace line {
49namespace spn {
50
51/** The stationary measures of Sec. 3.1, per place level and per mode. */
52template <class T>
53struct SpnMetrics {
54 /** The normalising constant G the measures are taken against. */
55 T G;
56 /** Mean tokens per place level. */
57 std::vector<T> tokens;
58 /** Place utilization, P(m_j > 0). */
59 std::vector<T> place_util;
60 /** Place throughput, tokens removed per unit time. */
61 std::vector<T> place_tput;
62 /** Transition (mode) utilization, P(e_j >= 1). */
63 std::vector<T> mode_util;
64 /** Transition (mode) throughput. */
65 std::vector<T> mode_tput;
66 /** marginal[l][k] = P(m_l = k). */
67 std::vector<std::vector<T>> marginal;
68};
69
70/**
71 * Every measure of Sec. 3.1 from one diagram and one product form.
72 *
73 * @param mdds the reachable set built by `spn_mdd`
74 * @param g per-level product-form factors g_l(v)
75 * @param info the metadata `spn_mdd` returned alongside the diagram
76 */
77template <class T>
78SpnMetrics<T> spn_metrics(const mdd::MddStruct& mdds, const std::vector<std::vector<T>>& g,
79 const SpnInfo<T>& info) {
80 const T zero = num_traits<T>::from_int(0), one = num_traits<T>::from_int(1);
81 const std::size_t L = info.nplacelevels;
82
83 SpnMetrics<T> out;
84 out.G = mdd::mdd_rec(mdds, g);
85 if (!(num_traits<T>::to_double(out.G) > 0))
86 throw NumericError("spn_metrics: the normalising constant is not positive; the g_l passed "
87 "do not describe a product form over this reachable set");
88
89 out.marginal.assign(L, std::vector<T>());
90 out.tokens.assign(L, zero);
91 out.place_util.assign(L, zero);
92 out.place_tput.assign(L, zero);
93 for (std::size_t l = 0; l < L; ++l) {
94 const std::vector<T> mass = mdd::mdd_rec_marginal(mdds, g, l);
95 out.marginal[l].assign(mass.size(), zero);
96 for (std::size_t k = 0; k < mass.size(); ++k) {
97 out.marginal[l][k] = T(mass[k] / out.G);
98 out.tokens[l] += T(num_traits<T>::from_int(static_cast<long>(k)) * out.marginal[l][k]);
99 }
100 out.place_util[l] = T(one - out.marginal[l][0]);
101 }
102
103 out.mode_util.assign(info.modes.size(), zero);
104 out.mode_tput.assign(info.modes.size(), zero);
105 for (std::size_t e = 0; e < info.modes.size(); ++e) {
106 const SpnMode<T>& mde = info.modes[e];
107 const SpnEnabling<T> en = spn_rec_enabled(mdds, g, mde, L);
108 out.mode_util[e] = T(en.ge[1] / out.G);
109 // W(T) is the scalar firing rate of the mode; a phase-type firing time
110 // has no single rate, so its throughput is left to the phase-level
111 // marginal rather than reported through this formula.
112 if (mde.nph > 1)
113 throw UnsupportedError(
114 "spn_metrics: mode " + std::to_string(mde.mode + 1) + " of node " +
115 std::to_string(mde.trans) +
116 " has a phase-type firing time, whose throughput is not W(T) times an enabling "
117 "probability; read it from the phase-level marginal instead");
118 // The formula below is W(T)*E[min(enabling degree, servers)], which is
119 // the rate law only when no marking-dependent multiplier is in play.
120 // With one, the firing rate is not a function of the enabling degree at
121 // all, so the enabling-degree law is the wrong summary to take it from.
122 // The MARGINALS above are unaffected -- they come from the product form,
123 // not the rates.
124 if (mde.dep)
125 throw UnsupportedError(
126 "spn_metrics: mode " + std::to_string(mde.mode + 1) + " of node " +
127 std::to_string(mde.trans) +
128 " has a marking-dependent firing rate, so its throughput is not W(T) times a "
129 "function of the enabling degree and cannot be read from the enabling-degree "
130 "law. The token marginals are still exact");
131 const T rate = mde.D1(0, 0);
132 T x = zero;
133 for (std::size_t k = 1; k < en.eq.size(); ++k) {
134 const double kd = static_cast<double>(k);
135 const double served = std::isinf(mde.srv) ? kd : (kd < mde.srv ? kd : mde.srv);
136 x += T(num_traits<T>::from_double(served) * rate * T(en.eq[k] / out.G));
137 }
138 out.mode_tput[e] = x;
139 for (std::size_t l = 0; l < L; ++l)
140 if (mde.enab[l] > 0)
141 out.place_tput[l] += T(num_traits<T>::from_double(mde.enab[l]) * x);
142 }
143 return out;
144}
145
146} // namespace spn
147} // namespace line
148
149#endif // LINE_API_SPN_SPN_METRICS_H
NumericError(const std::string &what)
Definition error.h:45
UnsupportedError(const std::string &what)
Definition error.h:51
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...
T mdd_rec(const MddStruct &mdds, const std::vector< std::vector< T > > &g)
The normalising constant G = sum_{s in S} prod_l g_l(s_l).
Definition mdd_rec.h:139
std::vector< T > mdd_rec_marginal(const MddStruct &mdds, const std::vector< std::vector< T > > &g, std::size_t l)
Unnormalised masses of {s in S : s_l = k}, one per local value k of level l.
Definition mdd_rec.h:150
SpnMetrics< T > spn_metrics(const mdd::MddStruct &mdds, const std::vector< std::vector< T > > &g, const SpnInfo< T > &info)
Every measure of Sec.
Definition spn_metrics.h:78
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,...
Enabling-degree distribution of one mode of a product-form stochastic Petri net, by the masked MDD-re...
Plain-array export of an MDD, the input contract of mdd_mcd.
Definition mdd.h:57
Unnormalised enabling-degree masses of one mode.
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.
Everything the caller needs alongside the descriptor.
Definition spn_mdd.h:108
std::size_t nplacelevels
Definition spn_mdd.h:122
std::vector< SpnMode< T > > modes
Definition spn_mdd.h:115
The stationary measures of Sec.
Definition spn_metrics.h:53
std::vector< T > mode_util
Transition (mode) utilization, P(e_j >= 1).
Definition spn_metrics.h:63
std::vector< T > place_tput
Place throughput, tokens removed per unit time.
Definition spn_metrics.h:61
std::vector< T > mode_tput
Transition (mode) throughput.
Definition spn_metrics.h:65
std::vector< std::vector< T > > marginal
marginal[l][k] = P(m_l = k).
Definition spn_metrics.h:67
std::vector< T > place_util
Place utilization, P(m_j > 0).
Definition spn_metrics.h:59
T G
The normalising constant G the measures are taken against.
Definition spn_metrics.h:55
std::vector< T > tokens
Mean tokens per place level.
Definition spn_metrics.h:57
One (transition, mode) pair of the net, in level coordinates.
Definition spn_mdd.h:86
std::size_t trans
1-based node index of the transition.
Definition spn_mdd.h:88
Matrix< T > D1
Definition spn_mdd.h:98
std::size_t nph
Definition spn_mdd.h:100
std::vector< double > enab
Enabling multiplicity per place level.
Definition spn_mdd.h:92
std::function< T(const std::vector< T > &)> dep
Marking-dependent firing-rate multiplier; empty for the unit one.
Definition spn_mdd.h:103
std::size_t mode
Mode index within the transition, 0-based.
Definition spn_mdd.h:90