LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Toggle main menu visibility
Loading...
Searching...
No Matches
lsn_max_multiplicity.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_LQN_LSN_MAX_MULTIPLICITY_H
6
#define LINE_API_LQN_LSN_MAX_MULTIPLICITY_H
7
8
/**
9
* @file
10
* @ingroup api_lqn
11
* Maximum sustainable multiplicity (concurrency level) of every element of a
12
* layered software network.
13
*
14
* Templated port of matlab/src/api/lsn/lsn_max_multiplicity.m, cross-checked
15
* against jar/src/main/java/jline/api/lsn/LsnMaxMultiplicity.java. It lives
16
* under api/lqn/ because the port adds no api/lsn/ directory of its own; the
17
* namespace is line::lsn, matching the MATLAB domain.
18
*
19
* Concurrency is propagated along the call graph in topological order (Kahn):
20
* a reference task seeds its own multiplicity, an entry with open arrivals
21
* seeds one thread, and every element passes on min(what reaches it, what it
22
* can hold). A setup task is exempt from the caller bound: its instances
23
* are provisioned by the platform rather than spawned by its callers, so it
24
* passes on its declared multiplicity. A non-reference task with infinite
25
* multiplicity ends up unbounded.
26
*
27
* SCOPE: the port takes the six plain-data fields the algorithm actually
28
* reads -- the call graph, the multiplicities, the element types, the
29
* reference and setup-task flags, and the per-entry open-arrival flag --
30
* rather than a LayeredNetworkStruct, so no model layer is needed.
31
*
32
* DIVERGENCE, MATLAB vs JAR: MATLAB also seeds inflow(i) = 1 for an ENTRY that
33
* has an open arrival (lsn.arrival{i} non-empty); the JAR omits that branch
34
* entirely, so an open-arrival entry reachable from no reference task gets
35
* outflow 0 there and 1 in MATLAB. This port follows MATLAB, the reference
36
* implementation, and exposes the flag as entry_has_arrival.
37
*
38
* ARITHMETIC: comparisons and additions only, so a finite field computation,
39
* exact in the exact instantiation. Infinite multiplicity is carried by an
40
* explicit flag instead of a floating infinity, both because Rational has no
41
* infinity and because Inf + Inf and min(Inf, Inf) are then decided by the
42
* algorithm rather than by the number type.
43
*/
44
45
#include <cstddef>
46
#include <deque>
47
#include <vector>
48
49
#include "
line/num/number.h
"
50
#include "
line/util/error.h
"
51
#include "
line/util/matrix.h
"
52
53
namespace
line
{
54
namespace
lsn
{
55
56
/** Element kinds, with the values of MATLAB's LayeredNetworkElement. */
57
enum class
LsnElementType
{
HOST
= 0,
TASK
= 1,
ENTRY
= 2,
ACTIVITY
= 3,
CALL
= 4 };
58
59
/** A multiplicity, possibly infinite; MATLAB's mult(i) = Inf. */
60
template
<
class
T>
61
struct
Multiplicity
{
62
bool
infinite
=
false
;
63
T
value
=
num_traits<T>::from_int
(0);
64
65
static
Multiplicity
finite
(
const
T& v) {
66
Multiplicity
r;
67
r.
infinite
=
false
;
68
r.
value
= v;
69
return
r;
70
}
71
static
Multiplicity
of
(
long
v) {
return
finite
(
num_traits<T>::from_int
(v)); }
72
static
Multiplicity
inf
() {
73
Multiplicity
r;
74
r.
infinite
=
true
;
75
return
r;
76
}
77
78
bool
positive
()
const
{
return
infinite
||
value
>
num_traits<T>::from_int
(0); }
79
};
80
81
/** a + b, with infinity absorbing. */
82
template
<
class
T>
83
Multiplicity<T>
mult_add
(
const
Multiplicity<T>
& a,
const
Multiplicity<T>
& b) {
84
if
(a.
infinite
|| b.
infinite
)
return
Multiplicity<T>::inf
();
85
return
Multiplicity<T>::finite
(T(a.
value
+ b.
value
));
86
}
87
88
/** min(a,b), with infinity as the top element. */
89
template
<
class
T>
90
Multiplicity<T>
mult_min
(
const
Multiplicity<T>
& a,
const
Multiplicity<T>
& b) {
91
if
(a.
infinite
)
return
b;
92
if
(b.
infinite
)
return
a;
93
return
a.
value
< b.
value
? a : b;
94
}
95
96
/** The plain-data fields of a layered software network read by the algorithm. */
97
template
<
class
T>
98
struct
LsnInput
{
99
Matrix<T>
dag
;
///< (n x n) call graph; an edge is a strictly positive entry
100
std::vector<Multiplicity<T>>
mult
;
///< (n) declared multiplicity; short vectors are padded with Inf
101
std::vector<LsnElementType>
type
;
///< (n) element kind
102
std::vector<bool>
isref
;
///< (n) reference task flag
103
std::vector<bool>
hassetup
;
///< (n) setup task flag; may be empty
104
std::vector<bool>
entry_has_arrival
;
///< (n) entry with an open arrival; may be empty
105
};
106
107
namespace
detail {
108
109
/**
110
* Kahn topological sort of an adjacency matrix, port of matlab/util/kahn.m.
111
* The queue is FIFO, as in both MATLAB and the JAR, so the order is identical.
112
* A cycle leaves the order short, which the caller must reject: MATLAB then
113
* indexes with a zero and the JAR runs off the end of its list, so neither
114
* reference implementation defines an answer there.
115
*/
116
template
<
class
T>
117
std::vector<std::size_t> kahn(
const
Matrix<T>
& adj) {
118
const
std::size_t n = adj.
rows
();
119
const
T zero =
num_traits<T>::from_int
(0);
120
std::vector<std::size_t> indeg(n, 0);
121
for
(std::size_t c = 0; c < n; ++c)
122
for
(std::size_t r = 0; r < n; ++r)
123
if
(adj(r, c) > zero) ++indeg[c];
124
125
std::deque<std::size_t> q;
126
for
(std::size_t v = 0; v < n; ++v)
127
if
(indeg[v] == 0) q.push_back(v);
128
129
std::vector<std::size_t> order;
130
order.reserve(n);
131
while
(!q.empty()) {
132
const
std::size_t i = q.front();
133
q.pop_front();
134
order.push_back(i);
135
for
(std::size_t j = 0; j < n; ++j)
136
if
(adj(i, j) > zero && --indeg[j] == 0) q.push_back(j);
137
}
138
return
order;
139
}
140
141
}
// namespace detail
142
143
/**
144
* @brief Maximum sustainable multiplicity (concurrency level) of every
145
* element of a layered software network.
146
*
147
* @param lsn the call graph and the per-element attributes
148
* @return (n) maximum multiplicity sustainable by each element
149
*/
150
template
<
class
T>
151
std::vector<Multiplicity<T>>
lsn_max_multiplicity
(
const
LsnInput<T>
&
lsn
) {
152
const
std::size_t n =
lsn
.dag.rows();
153
if
(
lsn
.dag.cols() != n)
throw
InputError
(
"lsn_max_multiplicity: the call graph is not square"
);
154
if
(
lsn
.type.size() != n ||
lsn
.isref.size() != n)
155
throw
InputError
(
"lsn_max_multiplicity: type/isref disagree with the graph size"
);
156
if
(!
lsn
.hassetup.empty() &&
lsn
.hassetup.size() != n)
157
throw
InputError
(
"lsn_max_multiplicity: hassetup disagrees with the graph size"
);
158
if
(!
lsn
.entry_has_arrival.empty() &&
lsn
.entry_has_arrival.size() != n)
159
throw
InputError
(
"lsn_max_multiplicity: entry_has_arrival disagrees with the graph size"
);
160
161
// an edge is any strictly positive weight, MATLAB's ag = lsn.dag > 0
162
const
T zero =
num_traits<T>::from_int
(0);
163
Matrix<T>
ag
(n, n, zero);
164
for
(std::size_t i = 0; i < n; ++i)
165
for
(std::size_t j = 0; j < n; ++j)
166
if
(
lsn
.dag(i, j) > zero)
ag
(i, j) =
num_traits<T>::from_int
(1);
167
168
const
std::vector<std::size_t> order = detail::kahn(
ag
);
169
if
(order.size() != n)
170
throw
InputError
(
"lsn_max_multiplicity: the call graph has a cycle, no topological order"
);
171
172
// multiplicities shorter than the graph are unbounded, as MATLAB pads with Inf
173
std::vector<Multiplicity<T>> mult =
lsn
.mult;
174
if
(mult.size() > n)
throw
InputError
(
"lsn_max_multiplicity: more multiplicities than elements"
);
175
while
(mult.size() < n) mult.push_back(
Multiplicity<T>::inf
());
176
177
std::vector<Multiplicity<T>> inflow(n,
Multiplicity<T>::of
(0));
178
for
(std::size_t i = 0; i < n; ++i) {
179
if
(
lsn
.type[i] ==
LsnElementType::TASK
&&
lsn
.isref[i]) {
180
inflow[i] = mult[i];
181
}
else
if
(
lsn
.type[i] ==
LsnElementType::ENTRY
&& !
lsn
.entry_has_arrival.empty() &&
182
lsn
.entry_has_arrival[i]) {
183
// an entry fed by an open arrival needs at least one thread of its
184
// parent task (MATLAB only; the JAR has no such branch)
185
inflow[i] =
Multiplicity<T>::of
(1);
186
}
187
}
188
189
std::vector<Multiplicity<T>> outflow(n,
Multiplicity<T>::of
(0));
190
for
(std::size_t k = 0; k < n; ++k) {
191
const
std::size_t i = order[k];
192
const
bool
has_setup = !
lsn
.hassetup.empty() &&
lsn
.hassetup[i];
193
if
(has_setup && inflow[i].positive()) {
194
// setup-task multiplicity rationale: see _kb/03-api-layer.md (cpp port notes: lsn)
195
outflow[i] = mult[i];
196
}
else
{
197
outflow[i] =
mult_min
(inflow[i], mult[i]);
198
}
199
for
(std::size_t j = 0; j < n; ++j)
200
if
(j != i &&
ag
(i, j) > zero) inflow[j] =
mult_add
(inflow[j], outflow[i]);
201
}
202
203
for
(std::size_t i = 0; i < n; ++i)
204
if
(
lsn
.type[i] ==
LsnElementType::TASK
&& mult[i].infinite && !
lsn
.isref[i])
205
outflow[i] =
Multiplicity<T>::inf
();
206
207
return
outflow;
208
}
209
210
}
// namespace lsn
211
}
// namespace line
212
213
#endif
// LINE_API_LQN_LSN_MAX_MULTIPLICITY_H
line::InputError::InputError
InputError(const std::string &what)
Definition
error.h:39
line::Matrix
Definition
matrix.h:56
line::Matrix::rows
std::size_t rows() const
Definition
matrix.h:89
error.h
The exception types the port throws.
matrix.h
Dense matrix and non-owning view.
line::ag
Definition
ag_dispatch.h:30
line::lsn
Definition
lsn_max_multiplicity.h:54
line::lsn::LsnElementType
LsnElementType
Element kinds, with the values of MATLAB's LayeredNetworkElement.
Definition
lsn_max_multiplicity.h:57
line::lsn::LsnElementType::TASK
@ TASK
Definition
lsn_max_multiplicity.h:57
line::lsn::LsnElementType::ACTIVITY
@ ACTIVITY
Definition
lsn_max_multiplicity.h:57
line::lsn::LsnElementType::ENTRY
@ ENTRY
Definition
lsn_max_multiplicity.h:57
line::lsn::LsnElementType::HOST
@ HOST
Definition
lsn_max_multiplicity.h:57
line::lsn::LsnElementType::CALL
@ CALL
Definition
lsn_max_multiplicity.h:57
line::lsn::lsn_max_multiplicity
std::vector< Multiplicity< T > > lsn_max_multiplicity(const LsnInput< T > &lsn)
Maximum sustainable multiplicity (concurrency level) of every element of a layered software network.
Definition
lsn_max_multiplicity.h:151
line::lsn::mult_add
Multiplicity< T > mult_add(const Multiplicity< T > &a, const Multiplicity< T > &b)
a + b, with infinity absorbing.
Definition
lsn_max_multiplicity.h:83
line::lsn::mult_min
Multiplicity< T > mult_min(const Multiplicity< T > &a, const Multiplicity< T > &b)
min(a,b), with infinity as the top element.
Definition
lsn_max_multiplicity.h:90
line
Definition
aoi_dist2ph.h:52
number.h
Number-type abstraction for the templated API port.
line::lsn::LsnInput
The plain-data fields of a layered software network read by the algorithm.
Definition
lsn_max_multiplicity.h:98
line::lsn::LsnInput::hassetup
std::vector< bool > hassetup
(n) setup task flag; may be empty
Definition
lsn_max_multiplicity.h:103
line::lsn::LsnInput::entry_has_arrival
std::vector< bool > entry_has_arrival
(n) entry with an open arrival; may be empty
Definition
lsn_max_multiplicity.h:104
line::lsn::LsnInput::type
std::vector< LsnElementType > type
(n) element kind
Definition
lsn_max_multiplicity.h:101
line::lsn::LsnInput::isref
std::vector< bool > isref
(n) reference task flag
Definition
lsn_max_multiplicity.h:102
line::lsn::LsnInput::dag
Matrix< T > dag
(n x n) call graph; an edge is a strictly positive entry
Definition
lsn_max_multiplicity.h:99
line::lsn::LsnInput::mult
std::vector< Multiplicity< T > > mult
(n) declared multiplicity; short vectors are padded with Inf
Definition
lsn_max_multiplicity.h:100
line::lsn::Multiplicity
A multiplicity, possibly infinite; MATLAB's mult(i) = Inf.
Definition
lsn_max_multiplicity.h:61
line::lsn::Multiplicity::finite
static Multiplicity finite(const T &v)
Definition
lsn_max_multiplicity.h:65
line::lsn::Multiplicity::positive
bool positive() const
Definition
lsn_max_multiplicity.h:78
line::lsn::Multiplicity::infinite
bool infinite
Definition
lsn_max_multiplicity.h:62
line::lsn::Multiplicity::inf
static Multiplicity inf()
Definition
lsn_max_multiplicity.h:72
line::lsn::Multiplicity::value
T value
Definition
lsn_max_multiplicity.h:63
line::lsn::Multiplicity::of
static Multiplicity of(long v)
Definition
lsn_max_multiplicity.h:71
line::num_traits
Definition
number.h:111
include
line
api
lqn
lsn_max_multiplicity.h
Generated by
1.18.0