LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Toggle main menu visibility
Loading...
Searching...
No Matches
ldes_busyperiod.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_BUSYPERIOD_H
6
#define LINE_SOLVERS_LDES_LDES_BUSYPERIOD_H
7
8
/**
9
* @file
10
* @ingroup line_solvers
11
* Busy period measurement for the native LDES engine.
12
*
13
* A busy period of ORDER n for a set of stations runs from the instant an
14
* arrival raises the jobs the set holds to n, up to the instant it falls back
15
* below n (Daduna, J. ACM 35(3), 1988). Order 1 is the ordinary busy period;
16
* higher orders measure how long the set stays at least n deep, which is what
17
* the analytical reference `pfqn_busyp` returns.
18
*
19
* ONE TARGET PER (set, class filter). Every station is a set of its own, every
20
* declared subnetwork is a set, and each is measured both aggregated over
21
* classes and per class, exactly as `initializeBusyPeriods` builds them.
22
*
23
* THE EXIT IS DEFERRED TO THE END OF THE INSTANT, and that is the whole
24
* difficulty. A job moving from one member of a target to another DROPS the
25
* count and RESTORES it at the same simulated instant; treating the drop as an
26
* exit ends a busy period that never ended and starts a new one, which roughly
27
* HALVES the reported mean on any multi-station target. So a drop only records
28
* a PENDING exit, an arrival at the same instant cancels it, and the pending
29
* exits are committed once the clock actually advances. A period of zero
30
* duration is an artifact of a simultaneous pair of events, not an
31
* observation, and is dropped.
32
*/
33
34
#include <cmath>
35
#include <cstddef>
36
#include <limits>
37
#include <string>
38
#include <vector>
39
40
namespace
line
{
41
namespace
ldes
{
42
namespace
engine
{
43
44
/** One measured target: a station set, optionally filtered to one class. */
45
struct
BpTarget
{
46
std::vector<std::size_t>
stations
;
///< 0-based station indexes
47
int
job_class
= -1;
///< -1 aggregates every class
48
std::string
name
;
49
};
50
51
/**
52
* The busy-period tracker of one run.
53
*
54
* `orders` is the highest order measured; 0 disables the measurement and every
55
* entry point below returns immediately, so the engine pays nothing for it.
56
*/
57
class
BusyPeriods
{
58
public
:
59
BusyPeriods
() {}
60
61
void
init
(
const
std::vector<BpTarget>&
targets
,
int
orders
, std::size_t nstations) {
62
orders_ =
orders
;
63
if
(orders_ <= 0)
return
;
64
targets_ =
targets
;
65
const
std::size_t nt = targets_.size();
66
const
std::size_t no =
static_cast<
std::size_t
>
(orders_);
67
jobs_.assign(nt, 0);
68
entry_.assign(nt, std::vector<double>(no, nan()));
69
pending_.assign(nt, std::vector<double>(no, nan()));
70
sum_.assign(nt, std::vector<double>(no, 0.0));
71
count_.assign(nt, std::vector<double>(no, 0.0));
72
by_station_.assign(nstations, std::vector<std::size_t>());
73
for
(std::size_t ti = 0; ti < nt; ++ti)
74
for
(std::size_t s : targets_[ti].stations)
75
if
(s < nstations) by_station_[s].push_back(ti);
76
}
77
78
bool
enabled
()
const
{
return
orders_ > 0; }
79
80
/**
81
* Record a population change of `delta` (+1 or -1) for `cls` at `station`.
82
*
83
* `now` must be non-decreasing across calls: the first call at a strictly
84
* later instant is what commits the exits pending from the previous one.
85
*/
86
void
track
(std::size_t station, std::size_t cls,
int
delta,
double
now) {
87
if
(orders_ <= 0 || station >= by_station_.size())
return
;
88
if
(now > last_time_) {
89
commit
();
90
last_time_ = now;
91
}
92
for
(std::size_t ti : by_station_[station]) {
93
const
BpTarget
& t = targets_[ti];
94
if
(t.
job_class
>= 0 &&
static_cast<
std::size_t
>
(t.
job_class
) != cls)
continue
;
95
jobs_[ti] += delta;
96
// A rise to n opens the period of order n; a fall FROM n closes it,
97
// and after the decrement the count is n-1, hence the +1.
98
const
int
order = (delta > 0) ? jobs_[ti] : jobs_[ti] + 1;
99
if
(order < 1 || order > orders_)
continue
;
100
const
std::size_t oi =
static_cast<
std::size_t
>
(order - 1);
101
if
(delta > 0) {
102
if
(std::isnan(pending_[ti][oi]))
103
entry_[ti][oi] = now;
104
else
105
// The level came back within the same instant: the drop was
106
// a move inside the target and the period never ended.
107
pending_[ti][oi] = nan();
108
}
else
{
109
pending_[ti][oi] = now;
110
pending_list_.push_back(ti);
111
pending_list_.push_back(oi);
112
}
113
}
114
}
115
116
/** Close every exit no longer contradicted by a same-instant arrival. */
117
void
commit
() {
118
for
(std::size_t p = 0; p + 1 < pending_list_.size(); p += 2) {
119
const
std::size_t ti = pending_list_[p], oi = pending_list_[p + 1];
120
const
double
exit = pending_[ti][oi];
121
if
(std::isnan(exit))
continue
;
122
const
double
entry = entry_[ti][oi];
123
// A NaN entry belongs to a period straddling the warmup reset: its
124
// start is unknown, so the period is dropped rather than guessed.
125
if
(!std::isnan(entry) && exit > entry) {
126
sum_[ti][oi] += exit - entry;
127
count_[ti][oi] += 1.0;
128
}
129
entry_[ti][oi] = nan();
130
pending_[ti][oi] = nan();
131
}
132
pending_list_.clear();
133
}
134
135
/**
136
* Discard everything observed during the warmup.
137
*
138
* A period IN PROGRESS at the reset has an unknown start, so its entry is
139
* invalidated and the period is dropped when it ends -- counting it from
140
* the reset instant would report a truncated period as a whole one and
141
* bias every mean downwards.
142
*/
143
void
reset
() {
144
if
(orders_ <= 0)
return
;
145
for
(std::size_t ti = 0; ti < targets_.size(); ++ti)
146
for
(std::size_t oi = 0; oi < static_cast<std::size_t>(orders_); ++oi) {
147
sum_[ti][oi] = 0.0;
148
count_[ti][oi] = 0.0;
149
entry_[ti][oi] = nan();
150
pending_[ti][oi] = nan();
151
}
152
pending_list_.clear();
153
}
154
155
const
std::vector<BpTarget>&
targets
()
const
{
return
targets_; }
156
int
orders
()
const
{
return
orders_; }
157
/** Mean duration of order `oi`+1 at target `ti`; 0 when nothing was observed. */
158
double
mean
(std::size_t ti, std::size_t oi)
const
{
159
return
(count_[ti][oi] > 0.0) ? sum_[ti][oi] / count_[ti][oi] : 0.0;
160
}
161
double
count
(std::size_t ti, std::size_t oi)
const
{
return
count_[ti][oi]; }
162
163
private
:
164
static
double
nan() {
return
std::numeric_limits<double>::quiet_NaN(); }
165
166
int
orders_ = 0;
167
std::vector<BpTarget> targets_;
168
std::vector<int> jobs_;
169
std::vector<std::vector<double>> entry_, pending_, sum_, count_;
170
std::vector<std::vector<std::size_t>> by_station_;
171
std::vector<std::size_t> pending_list_;
172
double
last_time_ = 0.0;
173
};
174
175
}
// namespace engine
176
}
// namespace ldes
177
}
// namespace line
178
179
#endif
// LINE_SOLVERS_LDES_LDES_BUSYPERIOD_H
line::ldes::engine::BusyPeriods::mean
double mean(std::size_t ti, std::size_t oi) const
Mean duration of order oi+1 at target ti; 0 when nothing was observed.
Definition
ldes_busyperiod.h:158
line::ldes::engine::BusyPeriods::targets
const std::vector< BpTarget > & targets() const
Definition
ldes_busyperiod.h:155
line::ldes::engine::BusyPeriods::enabled
bool enabled() const
Definition
ldes_busyperiod.h:78
line::ldes::engine::BusyPeriods::reset
void reset()
Discard everything observed during the warmup.
Definition
ldes_busyperiod.h:143
line::ldes::engine::BusyPeriods::commit
void commit()
Close every exit no longer contradicted by a same-instant arrival.
Definition
ldes_busyperiod.h:117
line::ldes::engine::BusyPeriods::orders
int orders() const
Definition
ldes_busyperiod.h:156
line::ldes::engine::BusyPeriods::init
void init(const std::vector< BpTarget > &targets, int orders, std::size_t nstations)
Definition
ldes_busyperiod.h:61
line::ldes::engine::BusyPeriods::count
double count(std::size_t ti, std::size_t oi) const
Definition
ldes_busyperiod.h:161
line::ldes::engine::BusyPeriods::track
void track(std::size_t station, std::size_t cls, int delta, double now)
Record a population change of delta (+1 or -1) for cls at station.
Definition
ldes_busyperiod.h:86
line::ldes::engine::BusyPeriods::BusyPeriods
BusyPeriods()
Definition
ldes_busyperiod.h:59
line::ldes::engine
Definition
ldes_busyperiod.h:42
line::ldes
Definition
ldes_busyperiod.h:41
line
Definition
aoi_dist2ph.h:52
line::ldes::engine::BpTarget
One measured target: a station set, optionally filtered to one class.
Definition
ldes_busyperiod.h:45
line::ldes::engine::BpTarget::job_class
int job_class
-1 aggregates every class
Definition
ldes_busyperiod.h:47
line::ldes::engine::BpTarget::stations
std::vector< std::size_t > stations
0-based station indexes
Definition
ldes_busyperiod.h:46
line::ldes::engine::BpTarget::name
std::string name
Definition
ldes_busyperiod.h:48
include
line
solvers
ldes
ldes_busyperiod.h
Generated by
1.18.0