LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Toggle main menu visibility
Loading...
Searching...
No Matches
fluid_mfq_admits.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_FLUID_FLUID_MFQ_ADMITS_H
6
#define LINE_SOLVERS_FLUID_FLUID_MFQ_ADMITS_H
7
8
/**
9
* @file
10
* @ingroup line_solvers
11
* Port of `fluid_mfq_admits.m` and `fluid_is_single_queue.m`: can the
12
* `mfq` fluid method (and its alias `aoi`) run as itself on this model?
13
*
14
* The Markovian fluid queue answers ONE open station, Source -> Queue -> Sink.
15
* Off that shape `mfq` RESOLVES to `matrix` (`SolverFLD.resolveMethod`), which
16
* is the fallback the analyzer has always taken: stating it before the gate is
17
* what makes the gate check, and the result report, the method that answers.
18
* One predicate, two callers (`fluid_resolve_method` and the `mfq` arm of
19
* `fluid_dispatch`), as in MATLAB and native python.
20
*
21
* Three arms, tried in the analyzer's own order:
22
* - the age-of-information shape (`aoi_is_aoi`);
23
* - the priority branch, taken when the classes carry distinct priorities: a
24
* class-independent finite positive service rate, a MAP {D0,D1} arrival for
25
* every open class, at least one of them modulated;
26
* - the plain branch, stated for ONE open class.
27
* Closed classes are refused by the feature set rather than here.
28
*
29
* The topology test is NODE-level, as the reference's is: a Delay or Router
30
* node does not disqualify the model, only a second Source, Queue or Sink
31
* does. That differs from `mfq_is_single_queue`, the topology the solver
32
* itself builds, which is stricter; a model admitted here and refused there is
33
* answered by `matrix` in the dispatch, exactly as before.
34
*/
35
36
#include <algorithm>
37
#include <cmath>
38
#include <cstddef>
39
#include <set>
40
#include <string>
41
#include <vector>
42
43
#include "
line/lang/qn/network_struct.h
"
44
#include "
line/solvers/fluid/fluid_aoi.h
"
45
46
namespace
line
{
47
namespace
fluid
{
48
49
/** The single-queue topology the reference reads, in 0-based indices. */
50
struct
FluidSingleQueue
{
51
bool
ok
=
false
;
52
std::string
error
;
///< the reference's errorMsg when !ok
53
std::size_t
source
= 0;
///< 0-based station index of the Source
54
std::size_t
queue
= 0;
///< 0-based station index of the Queue
55
};
56
57
/** Port of `fluid_is_single_queue.m`. */
58
template
<
class
T>
59
FluidSingleQueue
fluid_is_single_queue
(
const
qn::NetworkStruct<T>
&
sn
) {
60
FluidSingleQueue
t;
61
const
std::size_t K =
sn
.nclasses;
62
bool
any_open =
false
;
63
for
(std::size_t r = 0; r < K; ++r)
64
if
(!std::isfinite(
sn
.classes[r].population)) any_open =
true
;
65
if
(!any_open) {
66
t.
error
=
"Not an open model - all classes are closed"
;
67
return
t;
68
}
69
std::size_t nsrc = 0, nq = 0, nsink = 0, qnode = 0, srcnode = 0;
70
for
(std::size_t ind = 0; ind <
sn
.nodes.size(); ++ind) {
71
const
qn::NodeType
nt =
sn
.nodes[ind].nodetype;
72
if
(nt == qn::NodeType::Source) { ++nsrc; srcnode = ind + 1; }
73
else
if
(nt == qn::NodeType::Queue) { ++nq; qnode = ind + 1; }
74
else
if
(nt == qn::NodeType::Sink) ++nsink;
75
}
76
if
(nsrc == 0) { t.
error
=
"No source node found"
;
return
t; }
77
if
(nsrc > 1) { t.
error
=
"Multiple source nodes found ("
+ std::to_string(nsrc) +
")"
;
return
t; }
78
if
(nsink == 0) { t.
error
=
"No sink node found"
;
return
t; }
79
if
(nsink > 1) { t.
error
=
"Multiple sink nodes found ("
+ std::to_string(nsink) +
")"
;
return
t; }
80
if
(nq == 0) { t.
error
=
"No queue node found"
;
return
t; }
81
if
(nq > 1) {
82
t.
error
=
"Multiple queue nodes found ("
+ std::to_string(nq) +
83
") - MFQ supports single queue only"
;
84
return
t;
85
}
86
const
std::size_t sst =
sn
.nodes[srcnode - 1].station;
// 1-based, 0 = not a station
87
const
std::size_t qst =
sn
.nodes[qnode - 1].station;
88
if
(sst == 0 || qst == 0) {
89
t.
error
=
"MFQ requires source and queue to be stations"
;
90
return
t;
91
}
92
const
double
c =
sn
.stations[qst - 1].nservers;
93
if
(c > 1 && std::isfinite(c)) {
94
t.
error
=
"Multi-server queue (c="
+ std::to_string(
static_cast<
long
long
>
(c)) +
95
") not supported - MFQ requires c=1 or c=Inf"
;
96
return
t;
97
}
98
// No self-loop at the queue. rt is over the STATEFUL nodes; the reference
99
// indexes it with the station index, which coincides on this shape.
100
const
auto
it = std::lower_bound(
sn
.stateful_nodes.begin(),
sn
.stateful_nodes.end(), qnode);
101
if
(it !=
sn
.stateful_nodes.end() && *it == qnode) {
102
const
std::size_t isf =
static_cast<
std::size_t
>
(it -
sn
.stateful_nodes.begin());
103
for
(std::size_t k = 0; k < K; ++k) {
104
const
std::size_t idx = isf * K + k;
105
if
(idx <
sn
.rt.rows() && idx <
sn
.rt.cols() &&
106
num_traits<T>::to_double
(
sn
.rt(idx, idx)) > 0) {
107
t.
error
=
"Self-loop detected at queue - violates independence assumption"
;
108
return
t;
109
}
110
}
111
}
112
t.
source
= sst - 1;
113
t.
queue
= qst - 1;
114
t.
ok
=
true
;
115
return
t;
116
}
117
118
/** Verdict of `fluid_mfq_admits`. */
119
struct
FluidMfqAdmits
{
120
bool
ok
=
false
;
121
std::string
reason
;
///< the refusal, empty when ok
122
bool
is_aoi
=
false
;
///< the age-of-information arm is the one that runs
123
};
124
125
/** Port of `fluid_mfq_admits.m`. */
126
template
<
class
T>
127
FluidMfqAdmits
fluid_mfq_admits
(
const
qn::NetworkStruct<T>
&
sn
) {
128
FluidMfqAdmits
a;
129
if
(
aoi_is_aoi
(
sn
).ok) {
130
a.
ok
=
true
;
131
a.
is_aoi
=
true
;
132
return
a;
133
}
134
const
FluidSingleQueue
top =
fluid_is_single_queue
(
sn
);
135
if
(!top.
ok
) {
136
a.
reason
=
"The 'mfq' method is a single-queue fluid model (Source -> Queue -> Sink, "
137
"one or infinitely many servers): "
+ top.
error
+
"."
;
138
return
a;
139
}
140
std::vector<std::size_t> open;
141
for
(std::size_t r = 0; r <
sn
.nclasses; ++r)
142
if
(!std::isfinite(
sn
.classes[r].population)) open.push_back(r);
143
// the same test the analyzer switches on: distinct priorities over ALL classes
144
std::set<double> prios;
145
for
(std::size_t r = 0; r <
sn
.nclasses; ++r) prios.insert(
static_cast<
double
>
(
sn
.classes[r].prio));
146
if
(prios.size() > 1) {
147
const
char
* rate_msg =
"The priority branch of 'mfq' needs a finite positive service rate "
148
"for every open class."
;
149
if
(open.empty()) { a.
reason
= rate_msg;
return
a; }
150
std::vector<double> mu;
151
for
(std::size_t k : open) mu.push_back(
num_traits<T>::to_double
(
sn
.rates(top.
queue
, k)));
152
for
(
double
m : mu)
153
if
(!std::isfinite(m) || m <= 0) { a.
reason
= rate_msg;
return
a; }
154
const
double
fine_tol = 1e-8;
// GlobalConstants.FineTol
155
for
(
double
m : mu)
156
if
(std::fabs(m - mu[0]) > fine_tol * std::max(1.0, mu[0])) {
157
a.
reason
=
"The priority branch of 'mfq' is a fluid priority queue drained at one "
158
"rate, so it needs a class-independent service rate."
;
159
return
a;
160
}
161
std::size_t nph = 1;
162
for
(std::size_t k : open) {
163
const
lang::Distrib<T>
& arr =
sn
.service[top.
source
][k];
164
const
std::size_t n = arr.
D0
.rows();
165
if
(n == 0 || arr.
D1
.rows() != n) {
166
a.
reason
=
"The priority branch of 'mfq' needs a MAP {D0,D1} arrival process for "
167
"class "
+ std::to_string(k + 1) +
"."
;
168
return
a;
169
}
170
nph *= n;
171
}
172
if
(nph < 2) {
173
a.
reason
=
"The priority branch of 'mfq' needs at least one Markov-modulated "
174
"(multi-phase) arrival process: with exponential arrivals its fluid model "
175
"degenerates."
;
176
return
a;
177
}
178
a.
ok
=
true
;
179
return
a;
180
}
181
if
(open.size() != 1) {
182
a.
reason
=
"The 'mfq' method reads the arrival and service processes of one open class "
183
"and this model has "
+ std::to_string(open.size()) +
184
"; several classes are served only by its priority branch (distinct class "
185
"priorities)."
;
186
return
a;
187
}
188
a.
ok
=
true
;
189
return
a;
190
}
191
192
}
// namespace fluid
193
}
// namespace line
194
195
#endif
// LINE_SOLVERS_FLUID_FLUID_MFQ_ADMITS_H
line::qn::NetworkStruct
A network plus its refreshed NetworkStruct.
Definition
network_struct.h:1013
fluid_aoi.h
Age of Information by Markovian fluid queues: a port of solver_mfq_aoi.m (identical to solver_fluid_a...
line::fluid
Definition
fluid_aoi.h:81
line::fluid::fluid_is_single_queue
FluidSingleQueue fluid_is_single_queue(const qn::NetworkStruct< T > &sn)
Port of fluid_is_single_queue.m.
Definition
fluid_mfq_admits.h:59
line::fluid::fluid_mfq_admits
FluidMfqAdmits fluid_mfq_admits(const qn::NetworkStruct< T > &sn)
Port of fluid_mfq_admits.m.
Definition
fluid_mfq_admits.h:127
line::fluid::aoi_is_aoi
AoiTopology aoi_is_aoi(const qn::NetworkStruct< T > &sn)
Port of aoi_is_aoi.m.
Definition
fluid_aoi.h:102
line::lang::NodeType
NodeType
Node kinds, with the values of MATLAB NodeType.
Definition
lang_types.h:331
line::sn
Definition
sn_arvr_state_dependent_balance.h:25
line
Conservation laws of a layered queueing network, enumerated from its structure.
Definition
aoi_dist2ph.h:52
network_struct.h
A queueing network and its refreshed NetworkStruct.
line::fluid::FluidMfqAdmits
Verdict of fluid_mfq_admits.
Definition
fluid_mfq_admits.h:119
line::fluid::FluidMfqAdmits::is_aoi
bool is_aoi
the age-of-information arm is the one that runs
Definition
fluid_mfq_admits.h:122
line::fluid::FluidMfqAdmits::reason
std::string reason
the refusal, empty when ok
Definition
fluid_mfq_admits.h:121
line::fluid::FluidMfqAdmits::ok
bool ok
Definition
fluid_mfq_admits.h:120
line::fluid::FluidSingleQueue
The single-queue topology the reference reads, in 0-based indices.
Definition
fluid_mfq_admits.h:50
line::fluid::FluidSingleQueue::source
std::size_t source
0-based station index of the Source
Definition
fluid_mfq_admits.h:53
line::fluid::FluidSingleQueue::ok
bool ok
Definition
fluid_mfq_admits.h:51
line::fluid::FluidSingleQueue::error
std::string error
the reference's errorMsg when !ok
Definition
fluid_mfq_admits.h:52
line::fluid::FluidSingleQueue::queue
std::size_t queue
0-based station index of the Queue
Definition
fluid_mfq_admits.h:54
line::lang::Distrib
Definition
lang_types.h:818
line::lang::Distrib::D0
Matrix< T > D0
The (D0,D1) pair when the type carries one directly.
Definition
lang_types.h:863
line::lang::Distrib::D1
Matrix< T > D1
Definition
lang_types.h:863
line::num_traits
Definition
number.h:123
include
line
solvers
fluid
fluid_mfq_admits.h
Generated by
1.18.0