LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Toggle main menu visibility
Loading...
Searching...
No Matches
ssa_event_cache.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_SSA_SSA_EVENT_CACHE_H
6
#define LINE_SOLVERS_SSA_SSA_EVENT_CACHE_H
7
8
/**
9
* @file
10
* @ingroup line_solvers
11
* Port of `EventCache.m` and of the lookup that `State.afterEvent` performs
12
* against it (afterEvent.m lines 24-44 and its write-back sites).
13
*
14
* WHAT IS BEING MEMOIZED, AND WHY IT IS SOUND. `after_event` is a pure function
15
* of `(sn, ind, inspace, event, cls, no_promote, aux_rate)`: it reads no
16
* mutable state, draws no random number and touches nothing outside its
17
* arguments. So its value may be stored against those arguments and returned
18
* again, and a hit is EXACTLY what recomputation would produce. That is the
19
* whole correctness condition, and it is the one the tests assert directly,
20
* state by state, rather than inferring it from the metrics.
21
*
22
* THE KEY IS THE WHOLE ARGUMENT LIST. The reference keys on
23
* `mat2str([ind, event, class, noPromote, inspace])`, and each of those five
24
* pieces earns its place: `noPromote` distinguishes the departure half of an
25
* immediate-feedback self-loop (which leaves the vacated server held) from an
26
* ordinary departure at the same station in the same state, and dropping it
27
* would return one where the other was asked for. This port keys on the same
28
* five plus `aux_rate`, which the C++ handler takes and MATLAB does not: it is
29
* the rate carried into the RENEGE, RETRY, FAILURE and REPAIR branches, so two
30
* calls that differ only in it have different answers.
31
*
32
* A HIT AND A MISS ARE INDISTINGUISHABLE HERE, WHICH IS STRONGER THAN THE
33
* REFERENCE. MATLAB stores the full enumeration and then, on a hit, SAMPLES one
34
* successor row from it, spending a `rand`. This port's `after_event` is the
35
* enumeration-mode handler in every case and the sampling happens downstream in
36
* the engine, so enabling the cache changes neither the value nor the random
37
* stream. The seed-fixed sample path is therefore identical with and without
38
* caching, which is what lets a test compare the two runs bit for bit.
39
*
40
* THE CACHE IS BOUND TO ONE `sn`. The reference's `create` takes `sn` and
41
* ignores it (its loops are commented out). Here it is retained and checked:
42
* the memoized value is computed from the rates, capacities and routing of one
43
* network struct, so serving it to a query about a different struct would
44
* return another model's successors. The binding is a pointer identity test
45
* because the engines hold `sn` by const reference for their whole lifetime.
46
*
47
* A DISABLED CACHE IS NOT AN ABSENT ONE. `EventCache.create(false, sn)` returns
48
* `[]` and `afterEvent` then takes the uncached path. Here a disabled cache is
49
* an object that computes on every call and stores nothing, so the caller has
50
* one code path rather than two and cannot accidentally diverge between them.
51
*/
52
53
#include <cstddef>
54
#include <map>
55
#include <vector>
56
57
#include "
line/lang/lang_types.h
"
58
#include "
line/lang/qn/network_struct.h
"
59
#include "
line/lang/qn/state_events.h
"
60
#include "
line/num/number.h
"
61
#include "
line/util/error.h
"
62
63
namespace
line
{
64
namespace
ssa
{
65
66
/**
67
* The memoization key: the argument list of `after_event`, in fields.
68
*
69
* The pieces are kept SEPARATE rather than flattened into one vector because a
70
* flatten lets a wide state row of one node impersonate a narrow row of another
71
* with a different index prefix; separate fields cannot collide by
72
* construction, so no separator sentinel is needed.
73
*/
74
struct
SsaEventKey
{
75
std::size_t
ind
= 0;
///< 1-based node index
76
int
event
= 0;
///< `EventType`, as its underlying value
77
std::size_t
cls
= 0;
///< 1-based class index
78
bool
no_promote
=
false
;
79
double
aux_rate
= 0.0;
80
std::vector<double>
inspace
;
///< the node's local state row
81
82
bool
operator<
(
const
SsaEventKey
& o)
const
{
83
if
(
ind
!= o.
ind
)
return
ind
< o.
ind
;
84
if
(
event
!= o.
event
)
return
event
< o.
event
;
85
if
(
cls
!= o.
cls
)
return
cls
< o.
cls
;
86
if
(
no_promote
!= o.
no_promote
)
return
!
no_promote
;
87
if
(
aux_rate
!= o.
aux_rate
)
return
aux_rate
< o.
aux_rate
;
88
return
inspace
< o.
inspace
;
89
}
90
};
91
92
/** `EventCache`: the per-state enabled-event memo of the serial SSA engine. */
93
template
<
class
T>
94
class
SsaEventCache
{
95
public
:
96
/**
97
* `EventCache.create(enabled, sn)`.
98
*
99
* The struct is held by pointer, so the cache must not outlive it. Every
100
* caller in this port constructs the cache inside the scope that already
101
* holds `sn` by const reference for the run.
102
*/
103
static
SsaEventCache
create
(
bool
enabled
,
const
qn::NetworkStruct<T>
&
sn
) {
104
return
SsaEventCache
(
enabled
, &
sn
);
105
}
106
107
SsaEventCache
() : enabled_(false), sn_(0) {}
108
SsaEventCache
(
bool
enabled
,
const
qn::NetworkStruct<T>
*
sn
) : enabled_(
enabled
), sn_(
sn
) {}
109
110
bool
enabled
()
const
{
return
enabled_; }
111
112
/**
113
* `State.afterEvent` with the lookup in front of it.
114
*
115
* `sn` is passed rather than taken from the binding so the signature is the
116
* free function's and a caller can switch between them by changing one
117
* token; the binding is only there to catch the mistake of reusing a cache
118
* across structs.
119
*/
120
qn::EventOutcome<T>
after_event
(
const
qn::NetworkStruct<T>
&
sn
, std::size_t ind,
121
const
std::vector<T>& inspace,
lang::EventType
event,
122
std::size_t cls,
bool
no_promote =
false
,
123
const
T& aux_rate =
num_traits<T>::from_int
(0)) {
124
if
(!enabled_)
125
return
qn::after_event
(
sn
, ind, inspace, event, cls, no_promote, aux_rate);
126
if
(sn_ != 0 && sn_ != &
sn
)
127
throw
InputError
(
128
"SsaEventCache: queried with a different NetworkStruct from the one it was "
129
"created against. The memoized successors are a function of that struct's rates, "
130
"capacities and routing, so serving them here would answer about another model"
);
131
132
SsaEventKey
key;
133
key.
ind
= ind;
134
key.
event
=
static_cast<
int
>
(event);
135
key.
cls
= cls;
136
key.
no_promote
= no_promote;
137
key.
aux_rate
=
num_traits<T>::to_double
(aux_rate);
138
key.
inspace
.resize(inspace.size());
139
for
(std::size_t j = 0; j < inspace.size(); ++j)
140
key.
inspace
[j] =
num_traits<T>::to_double
(inspace[j]);
141
142
const
typename
std::map<SsaEventKey, qn::EventOutcome<T> >::const_iterator it =
143
memo_.find(key);
144
if
(it != memo_.end()) {
145
++hits_;
146
return
it->second;
147
}
148
++misses_;
149
const
qn::EventOutcome<T>
out =
150
qn::after_event
(
sn
, ind, inspace, event, cls, no_promote, aux_rate);
151
memo_[key] = out;
152
return
out;
153
}
154
155
/**
156
* Hits and misses, so a caller can report the memo's effect.
157
*
158
* They are counters and not a hit RATIO because the ratio alone hides the
159
* denominator, and a 100% hit rate over three lookups says nothing.
160
*/
161
std::size_t
hits
()
const
{
return
hits_; }
162
std::size_t
misses
()
const
{
return
misses_; }
163
std::size_t
size
()
const
{
return
memo_.size(); }
164
165
void
clear
() {
166
memo_.clear();
167
hits_ = 0;
168
misses_ = 0;
169
}
170
171
private
:
172
bool
enabled_;
173
const
qn::NetworkStruct<T>
* sn_;
174
std::map<SsaEventKey, qn::EventOutcome<T> > memo_;
175
std::size_t hits_ = 0, misses_ = 0;
176
};
177
178
}
// namespace ssa
179
}
// namespace line
180
181
#endif
// LINE_SOLVERS_SSA_SSA_EVENT_CACHE_H
line::InputError::InputError
InputError(const std::string &what)
Definition
error.h:39
line::qn::NetworkStruct
A network plus its refreshed NetworkStruct.
Definition
network_struct.h:838
line::ssa::SsaEventCache::size
std::size_t size() const
Definition
ssa_event_cache.h:163
line::ssa::SsaEventCache::hits
std::size_t hits() const
Hits and misses, so a caller can report the memo's effect.
Definition
ssa_event_cache.h:161
line::ssa::SsaEventCache::enabled
bool enabled() const
Definition
ssa_event_cache.h:110
line::ssa::SsaEventCache::after_event
qn::EventOutcome< T > after_event(const qn::NetworkStruct< T > &sn, std::size_t ind, const std::vector< T > &inspace, lang::EventType event, std::size_t cls, bool no_promote=false, const T &aux_rate=num_traits< T >::from_int(0))
State.afterEvent with the lookup in front of it.
Definition
ssa_event_cache.h:120
line::ssa::SsaEventCache::SsaEventCache
SsaEventCache()
Definition
ssa_event_cache.h:107
line::ssa::SsaEventCache::create
static SsaEventCache create(bool enabled, const qn::NetworkStruct< T > &sn)
EventCache.create(enabled, sn).
Definition
ssa_event_cache.h:103
line::ssa::SsaEventCache::misses
std::size_t misses() const
Definition
ssa_event_cache.h:162
line::ssa::SsaEventCache::clear
void clear()
Definition
ssa_event_cache.h:165
line::ssa::SsaEventCache::SsaEventCache
SsaEventCache(bool enabled, const qn::NetworkStruct< T > *sn)
Definition
ssa_event_cache.h:108
error.h
The exception types the port throws.
lang_types.h
Enumerations and the minimal distribution descriptor shared by the model layer of the C++ port.
line::lang::EventType
EventType
The events a state can undergo, with the values of MATLAB EventType.
Definition
lang_types.h:111
line::qn::after_event
EventOutcome< T > after_event(const NetworkStruct< T > &sn, std::size_t ind, const std::vector< T > &inspace, EventType event, std::size_t cls, bool no_promote=false, const T &aux_rate=num_traits< T >::from_int(0))
Port of State.afterEvent: the successors of one event at one NODE.
Definition
state_events.h:1948
line::sn
Definition
sn_gd_balance.h:42
line::ssa
Definition
solver_ssa_getters.h:58
line
Definition
aoi_dist2ph.h:52
network_struct.h
A queueing network and its refreshed NetworkStruct.
number.h
Number-type abstraction for the templated API port.
state_events.h
Port of the event half of MATLAB's +State package: the successor states an event produces at one node...
line::num_traits
Definition
number.h:111
line::qn::EventOutcome
What one event produces at one node: the successor rows, their rates and their probabilities,...
Definition
state_events.h:53
line::ssa::SsaEventKey
The memoization key: the argument list of after_event, in fields.
Definition
ssa_event_cache.h:74
line::ssa::SsaEventKey::event
int event
EventType, as its underlying value
Definition
ssa_event_cache.h:76
line::ssa::SsaEventKey::no_promote
bool no_promote
Definition
ssa_event_cache.h:78
line::ssa::SsaEventKey::inspace
std::vector< double > inspace
the node's local state row
Definition
ssa_event_cache.h:80
line::ssa::SsaEventKey::cls
std::size_t cls
1-based class index
Definition
ssa_event_cache.h:77
line::ssa::SsaEventKey::aux_rate
double aux_rate
Definition
ssa_event_cache.h:79
line::ssa::SsaEventKey::operator<
bool operator<(const SsaEventKey &o) const
Definition
ssa_event_cache.h:82
line::ssa::SsaEventKey::ind
std::size_t ind
1-based node index
Definition
ssa_event_cache.h:75
include
line
solvers
ssa
ssa_event_cache.h
Generated by
1.18.0