LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Loading...
Searching...
No Matches
ldes_spn.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_SOLVERS_LDES_LDES_SPN_H
6#define LINE_SOLVERS_LDES_LDES_SPN_H
7
8/**
9 * @file
10 * @ingroup line_solvers
11 * The Petri-net layer of the native LDES engine: Places and Transitions.
12 *
13 * A PLACE holds tokens per class and a TRANSITION moves them. A mode of a
14 * transition is ENABLED when every input place holds at least its enabling
15 * count and no inhibiting count is met; firing consumes the enabling tokens and
16 * deposits the firing counts.
17 *
18 * IMMEDIATE MODES FIRE BEFORE ANY TIMED ONE, and among themselves in priority
19 * order, weighted at equal priority. That is not an optimization: an immediate
20 * transition takes zero time, so a marking that enables one is not a marking
21 * the net ever rests in, and letting a timed mode fire from it would visit a
22 * state the model does not have.
23 *
24 * THE FIRING RATE MAY DEPEND ON THE MARKING. `firingdep` multiplies the mode's
25 * nominal rate by g(marking), and the clock has to be RESAMPLED whenever the
26 * marking changes, not merely scaled: an exponential clock drawn under one
27 * rate and left to run carries the old rate into the new marking.
28 */
29
30#include <algorithm>
31#include <cstddef>
32#include <functional>
33#include <limits>
34#include <vector>
35
37
38namespace line {
39namespace ldes {
40namespace engine {
41
42/**
43 * One mode of a transition, resolved to what the event loop needs.
44 *
45 * THE ARC VECTORS ARE INDEXED BY (place slot, class), flattened `p * K + r`,
46 * and the marking handed to `spn_enabled` and `spn_fire` uses the same layout.
47 * A Petri net whose arcs carry a class is a COLOURED net and its behaviour is
48 * not that of the class-summed one: a mode needing two Class1 tokens must not
49 * be enabled by two Class2 tokens sitting at the same place.
50 */
51struct SpnMode {
52 std::vector<double> enabling; ///< per (place, class), tokens required
53 std::vector<double> inhibiting; ///< per (place, class), tokens that block (inf = never)
54 std::vector<double> firing; ///< per (place, class), tokens moved on a firing
55 double servers = 1.0;
56 double priority = 0.0;
57 double weight = 1.0;
58 bool immediate = false;
59 double rate = 1.0; ///< nominal firing rate of a timed mode
60 std::function<double(const std::vector<double>&)> dep; ///< marking multiplier
61};
62
63/** One Transition node. */
65 std::size_t node = 0;
66 std::vector<SpnMode> modes;
67 std::vector<std::size_t> places; ///< the place NODES its modes reference, in order
68 std::vector<double> fired; ///< per mode
69};
70
71/**
72 * True when `m` is enabled by the marking `tok`, which is indexed the same way
73 * as the mode's own vectors -- (place slot, class), flattened.
74 *
75 * An INHIBITING arc is a bound, not a requirement: the mode is blocked once the
76 * place reaches the count IN THAT CLASS, and an infinite count never blocks.
77 */
78inline bool spn_enabled(const SpnMode& m, const std::vector<double>& tok) {
79 for (std::size_t p = 0; p < m.enabling.size() && p < tok.size(); ++p)
80 if (tok[p] < m.enabling[p]) return false;
81 for (std::size_t p = 0; p < m.inhibiting.size() && p < tok.size(); ++p)
82 if (std::isfinite(m.inhibiting[p]) && m.inhibiting[p] > 0.0 && tok[p] >= m.inhibiting[p])
83 return false;
84 return true;
85}
86
87/** The rate of a timed mode under the current marking. */
88inline double spn_rate(const SpnMode& m, const std::vector<double>& tok) {
89 double r = m.rate;
90 if (m.dep) {
91 const double g = m.dep(tok);
92 r *= (g > 0.0) ? g : 0.0;
93 }
94 return r;
95}
96
97/** Move the tokens of one firing: consume the enabling counts, deposit the firing ones. */
98inline void spn_fire(const SpnMode& m, std::vector<double>& tok) {
99 for (std::size_t p = 0; p < m.enabling.size() && p < tok.size(); ++p)
100 tok[p] -= m.enabling[p];
101 for (std::size_t p = 0; p < m.firing.size() && p < tok.size(); ++p) tok[p] += m.firing[p];
102}
103
104/**
105 * Pick among the enabled IMMEDIATE modes: the highest priority first (lowest
106 * value), then weighted at random among the ties. Returns -1 when none is
107 * enabled.
108 */
109inline int spn_pick_immediate(const std::vector<SpnMode>& modes, const std::vector<double>& tok,
110 double u) {
111 double best_prio = std::numeric_limits<double>::infinity();
112 for (const SpnMode& m : modes)
113 if (m.immediate && spn_enabled(m, tok)) best_prio = std::min(best_prio, m.priority);
114 if (!std::isfinite(best_prio)) return -1;
115 double total = 0.0;
116 for (const SpnMode& m : modes)
117 if (m.immediate && m.priority == best_prio && spn_enabled(m, tok)) total += m.weight;
118 if (!(total > 0.0)) {
119 for (std::size_t k = 0; k < modes.size(); ++k)
120 if (modes[k].immediate && modes[k].priority == best_prio && spn_enabled(modes[k], tok))
121 return static_cast<int>(k);
122 return -1;
123 }
124 double acc = 0.0;
125 const double x = u * total;
126 for (std::size_t k = 0; k < modes.size(); ++k)
127 if (modes[k].immediate && modes[k].priority == best_prio && spn_enabled(modes[k], tok)) {
128 acc += modes[k].weight;
129 if (x < acc) return static_cast<int>(k);
130 }
131 return -1;
132}
133
134} // namespace engine
135} // namespace ldes
136} // namespace line
137
138#endif // LINE_SOLVERS_LDES_LDES_SPN_H
Enumerations and the minimal distribution descriptor shared by the model layer of the C++ port.
double spn_rate(const SpnMode &m, const std::vector< double > &tok)
The rate of a timed mode under the current marking.
Definition ldes_spn.h:88
int spn_pick_immediate(const std::vector< SpnMode > &modes, const std::vector< double > &tok, double u)
Pick among the enabled IMMEDIATE modes: the highest priority first (lowest value),...
Definition ldes_spn.h:109
bool spn_enabled(const SpnMode &m, const std::vector< double > &tok)
True when m is enabled by the marking tok, which is indexed the same way as the mode's own vectors – ...
Definition ldes_spn.h:78
void spn_fire(const SpnMode &m, std::vector< double > &tok)
Move the tokens of one firing: consume the enabling counts, deposit the firing ones.
Definition ldes_spn.h:98
One mode of a transition, resolved to what the event loop needs.
Definition ldes_spn.h:51
std::vector< double > enabling
per (place, class), tokens required
Definition ldes_spn.h:52
std::function< double(const std::vector< double > &)> dep
marking multiplier
Definition ldes_spn.h:60
double rate
nominal firing rate of a timed mode
Definition ldes_spn.h:59
std::vector< double > inhibiting
per (place, class), tokens that block (inf = never)
Definition ldes_spn.h:53
std::vector< double > firing
per (place, class), tokens moved on a firing
Definition ldes_spn.h:54
One Transition node.
Definition ldes_spn.h:64
std::vector< double > fired
per mode
Definition ldes_spn.h:68
std::vector< std::size_t > places
the place NODES its modes reference, in order
Definition ldes_spn.h:67
std::vector< SpnMode > modes
Definition ldes_spn.h:66