LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Loading...
Searching...
No Matches
sn_map_modulation.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_SN_SN_MAP_MODULATION_H
6#define LINE_API_SN_SN_MAP_MODULATION_H
7
8/**
9 * @file
10 * @ingroup api_sn
11 * Port of matlab/src/api/sn/sn_map_modulation.m.
12 *
13 * Every MAP-like process in the model, collected as the modulating environment
14 * it really is: `map2renv` turns this list into a random-environment model, in
15 * which the MAP's phase process is the environment stage and the network runs
16 * at the rate that stage selects.
17 *
18 * MARKED PROCESSES. When a station's arrival is a marked MAP (an MMAP), the
19 * classes it marks share ONE modulating phase process, so they are reported as
20 * a SINGLE entry carrying (D0, D1^(1), ..., D1^(C)) over the marked class set,
21 * not as one entry per class. Splitting them would give each class its own
22 * independent environment and lose exactly the correlation the MMAP encodes.
23 *
24 * `is_mmpp` records whether every mark block is diagonal: a diagonal D1 means
25 * an arrival never changes the phase, which is the MMPP special case whose
26 * environment is the phase process alone.
27 *
28 * ARITHMETIC: field. A Frobenius norm comparison, no transcendental.
29 */
30
31#include <cmath>
32#include <cstddef>
33#include <string>
34#include <vector>
35
37#include "line/num/number.h"
38
39namespace line {
40namespace api {
41
42/** One modulating process: which station and classes it drives, and its blocks. */
43template <class T>
45 std::size_t ist = 0; ///< 1-based station
46 std::size_t node = 0; ///< 1-based node
47 bool arrival = false; ///< true at a Source, false for a service process
48 std::vector<std::size_t> classes; ///< 1-based classes this process drives
50 std::vector<Matrix<T>> D1; ///< one block per entry of `classes`
51 std::size_t order = 0; ///< phases of the modulating process
52 bool is_mmpp = false; ///< every D1 block is diagonal
53};
54
55namespace detail {
56
57/** `norm(D1 - diag(diag(D1)),'fro') <= Zero * max(1, norm(D1,'fro'))`. */
58template <class T>
59bool sn_map_is_diagonal(const Matrix<T>& D1) {
60 double off = 0.0, all = 0.0;
61 for (std::size_t a = 0; a < D1.rows(); ++a)
62 for (std::size_t b = 0; b < D1.cols(); ++b) {
63 const double v = num_traits<T>::to_double(D1(a, b));
64 all += v * v;
65 if (a != b) off += v * v;
66 }
67 return std::sqrt(off) <= lang::GlobalConstants::Zero * std::max(1.0, std::sqrt(all));
68}
69
70} // namespace detail
71
72template <class T>
73std::vector<SnMapModulation<T>> sn_map_modulation(const qn::NetworkStruct<T>& sn) {
74 std::vector<SnMapModulation<T>> mods;
75 for (std::size_t ist = 1; ist <= sn.nstations; ++ist) {
76 const std::size_t nd = sn.station_to_node[ist - 1];
77 const bool arrival =
78 nd != 0 && sn.nodes[nd - 1].nodetype == qn::NodeType::Source;
79 const std::vector<std::size_t>& marks = sn.stations[ist - 1].marked_classes;
80 std::vector<bool> done(sn.nclasses, false);
81 for (std::size_t r = 1; r <= sn.nclasses; ++r) {
82 if (done[r - 1]) continue;
83 const lang::ProcessType pt = sn.service[ist - 1][r - 1].type;
86 continue;
87 const lang::Distrib<T>& d = sn.service[ist - 1][r - 1];
88 if (d.disabled || d.D0.rows() == 0) continue;
89 bool is_marked = false;
90 for (std::size_t c : marks)
91 if (c == r) is_marked = true;
93 m.ist = ist;
94 m.node = nd;
95 m.arrival = arrival;
96 if (is_marked && !marks.empty()) {
97 // one entry for the whole marked set, carried by the block list
98 // of the FIRST marked class -- the reference's "carrier"
99 const lang::Distrib<T>& carrier = sn.service[ist - 1][marks[0] - 1];
100 if (carrier.Dmark.size() < marks.size())
101 throw InputError(
102 "sn_map_modulation: the marked arrival process at station " +
103 std::to_string(ist) + " carries " +
104 std::to_string(carrier.Dmark.size()) + " mark matrices for " +
105 std::to_string(marks.size()) +
106 " marked classes; the (D0,D1,D1^(1),...,D1^(C)) form is required");
107 m.classes = marks;
108 m.D0 = carrier.D0;
109 m.is_mmpp = true;
110 for (std::size_t k = 0; k < marks.size(); ++k) {
111 m.D1.push_back(carrier.Dmark[k]);
112 if (!detail::sn_map_is_diagonal(carrier.Dmark[k])) m.is_mmpp = false;
113 done[marks[k] - 1] = true;
114 }
115 m.order = carrier.D0.rows();
116 } else {
117 m.classes.push_back(r);
118 m.D0 = d.D0;
119 m.D1.push_back(d.D1);
120 m.order = d.D0.rows();
121 m.is_mmpp = detail::sn_map_is_diagonal(d.D1);
122 done[r - 1] = true;
123 }
124 mods.push_back(m);
125 }
126 }
127 return mods;
128}
129
130} // namespace api
131} // namespace line
132
133#endif // LINE_API_SN_SN_MAP_MODULATION_H
InputError(const std::string &what)
Definition error.h:39
std::size_t cols() const
Definition matrix.h:90
std::size_t rows() const
Definition matrix.h:89
A network plus its refreshed NetworkStruct.
std::vector< SnMapModulation< T > > sn_map_modulation(const qn::NetworkStruct< T > &sn)
ProcessType
Distribution kinds, with the values of MATLAB ProcessType.
Definition lang_types.h:483
A queueing network and its refreshed NetworkStruct.
Number-type abstraction for the templated API port.
One modulating process: which station and classes it drives, and its blocks.
std::vector< std::size_t > classes
1-based classes this process drives
std::size_t node
1-based node
bool arrival
true at a Source, false for a service process
std::vector< Matrix< T > > D1
one block per entry of classes
std::size_t order
phases of the modulating process
bool is_mmpp
every D1 block is diagonal
std::size_t ist
1-based station
Matrix< T > D0
The (D0,D1) pair when the type carries one directly.
Definition lang_types.h:759
std::vector< Matrix< T > > Dmark
MMAP per-class D1 blocks / BMAP per-batch-size blocks; empty otherwise.
Definition lang_types.h:761
static constexpr double Zero
Definition lang_types.h:670