LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Loading...
Searching...
No Matches
polling_info.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_LANG_QN_POLLING_INFO_H
6#define LINE_LANG_QN_POLLING_INFO_H
7
8/**
9 * @file
10 * @ingroup line_lang
11 * `State.pollingInfo` and the controller description it returns.
12 *
13 * IT LIVES IN ITS OWN HEADER because both sides of the State package need it
14 * and neither can include the other: `from_marginal` (state.h) has to ENUMERATE
15 * the controller configurations a station can occupy, and the event handlers
16 * (state_events.h) have to MOVE between them. Leaving it in state_events.h left
17 * `from_marginal` unable to emit the controller columns at all, so a polling
18 * station's states were built one block too narrow and the enumerated space
19 * contained no controller -- the station then behaved as a capacity-one queue.
20 */
21
22#include <cstddef>
23#include <vector>
24
28#include "line/util/error.h"
29#include "line/util/matrix.h"
30
31namespace line {
32namespace qn {
33
36
37/**
38 * Port of `State.pollingInfo`: the derived description of a polling controller.
39 *
40 * The controller lives in the shared node block of the local variables and
41 * holds, in order, [pos, swk, ctr]. Each column is materialized ONLY when the
42 * discipline needs it, so a polling station never carries state its dynamics
43 * cannot distinguish:
44 *
45 * pos the buffer the server is at or heading to. Needed only when some
46 * switchover is non-immediate: while a job is in service pos equals
47 * that job's class, and while the station is empty with immediate
48 * switchovers the position is unobservable.
49 * swk 0 when the server sits at pos, else the phase of the switchover into
50 * it. Same condition as pos.
51 * ctr the visit budget. Every discipline except EXHAUSTIVE bounds a visit.
52 *
53 * An Immediate switchover is deliberately NOT a state: its rate is ~1e8, and
54 * taking that literally would make the generator stiff and add a spurious
55 * state per buffer. `polling_next` folds it into the enclosing transition.
56 *
57 * EVERY POLLING STATION HAS A CONTROLLER, whichever API declared it and even
58 * when none did. The reference returns [] only for a node that is not a polling
59 * station and otherwise defaults to EXHAUSTIVE with every switchover immediate,
60 * so `NetworkStruct::effective_polling` resolves the two C++ spellings --
61 * `set_polling` writing `sn.pollingparam`, and setPollingType / setSwitchover
62 * writing the Station fields -- into one description.
63 *
64 * BUILDING NO CONTROLLER WAS NOT A DEGRADED MODEL BUT A CRASH. Reading only
65 * `pollingparam` left every station declared the other way (the JSON reader
66 * included) with `valid = false` and, with it, an EMPTY `polled`. `polling_next`
67 * indexes `polled[pos-1]` with no size test, and an empty std::vector<bool>
68 * holds a NULL word pointer, so that index segfaults rather than returning a
69 * garbage bit -- which is why the failure surfaced as a SIGSEGV inside
70 * `after_event_station_arv` and not as a wrong number.
71 */
72template <class T>
75 std::size_t pk = 1;
76 std::vector<bool> polled, has_sw;
77 std::vector<std::size_t> ksw;
78 std::vector<Matrix<T>> sw_d0, sw_d1;
79 std::vector<std::vector<T>> sw_pie;
80 std::size_t off = 0, width = 0;
81 // 0-based columns inside the local-variable block, or npos when absent.
82 std::size_t ipos = static_cast<std::size_t>(-1);
83 std::size_t iswk = static_cast<std::size_t>(-1);
84 std::size_t ictr = static_cast<std::size_t>(-1);
85 bool valid = false;
86};
87
88template <class T>
90 const std::size_t R = sn.nclasses;
92 const std::size_t ist = sn.nodes[ind - 1].station;
93 if (ist == 0 || sn.stations[ist - 1].sched != SchedStrategy::POLLING) return pi;
94 // Whichever API declared the controller, and a default when neither did.
95 const typename NetworkStruct<T>::PollingParam pp = sn.effective_polling(ist);
96 pi.valid = true;
97 pi.ptype = pp.ptype;
98 pi.pk = pp.pk;
99
100 // A class disabled at this station can never hold a job, so it is dropped
101 // from the cyclic order rather than polled forever.
102 pi.polled.assign(R, true);
103 for (std::size_t r = 1; r <= R; ++r)
104 if (sn.disabled[ist - 1][r - 1] || sn.phases_of(ist, r) == 0) pi.polled[r - 1] = false;
105 bool any = false;
106 for (std::size_t r = 0; r < R; ++r) any = any || pi.polled[r];
107 if (!any)
108 throw InputError("polling_info: the polling station has no class with an enabled service");
109
110 pi.has_sw.assign(R, false);
111 pi.ksw.assign(R, 0);
112 pi.sw_d0.resize(R);
113 pi.sw_d1.resize(R);
114 pi.sw_pie.resize(R);
115 // The switchover of the leg ENTERING each polled buffer is read off the
116 // buffer the server leaves to get there.
117 std::vector<std::size_t> polled_list;
118 for (std::size_t r = 1; r <= R; ++r)
119 if (pi.polled[r - 1]) polled_list.push_back(r);
120 for (std::size_t j = 0; j < polled_list.size(); ++j) {
121 const std::size_t q = polled_list[j];
122 const std::size_t prev = polled_list[(j + polled_list.size() - 1) % polled_list.size()];
123 if (pp.switchover.size() < prev) continue;
124 const lang::Distrib<T>& d = pp.switchover[prev - 1];
125 if (d.disabled || d.D0.rows() == 0) continue;
126 if (d.type == ProcessType::IMMEDIATE) continue; // folded, never a state
127 pi.has_sw[q - 1] = true;
128 pi.ksw[q - 1] = d.D0.rows();
129 pi.sw_d0[q - 1] = d.D0;
130 pi.sw_d1[q - 1] = d.D1;
131 mam::Map<T> mp;
132 mp.D0 = d.D0;
133 mp.D1 = d.D1;
134 pi.sw_pie[q - 1] = mam::map_pie(mp);
135 }
136
137 bool anysw = false;
138 for (std::size_t r = 0; r < R; ++r) anysw = anysw || pi.has_sw[r];
139 // pos and swk exist only to encode SWITCHING(p); with every switchover
140 // immediate the server is either serving (pos = the class in service) or
141 // parked (pos unobservable), so neither column carries information.
142 std::size_t c = 0;
143 if (anysw) {
144 pi.ipos = c++;
145 pi.iswk = c++;
146 }
147 if (pi.ptype != lang::PollingType::EXHAUSTIVE) pi.ictr = c++;
148 pi.width = c;
149 // Guarded for the same reason nvars_of is: a hand-built struct can carry a
150 // shorter nvars than the node index, and sn.nvars[ind-1] is evaluated before
151 // the inner size() test would ever run.
152 std::size_t off = 0;
153 if (ind <= sn.nvars.size())
154 for (std::size_t j = 0; j < 2 * R && j < sn.nvars[ind - 1].size(); ++j)
155 off += sn.nvars[ind - 1][j];
156 pi.off = off;
157 return pi;
158}
159} // namespace qn
160} // namespace line
161
162#endif // LINE_LANG_QN_POLLING_INFO_H
InputError(const std::string &what)
Definition error.h:39
A network plus its refreshed NetworkStruct.
The exception types the port throws.
Enumerations and the minimal distribution descriptor shared by the model layer of the C++ port.
Markovian arrival process descriptors: stationary vectors, rate, moments, autocorrelation and the ind...
Dense matrix and non-owning view.
SchedStrategy
Scheduling disciplines, with the values of MATLAB SchedStrategy.
Definition lang_types.h:181
PollingType
Polling service disciplines, with the values of MATLAB PollingType.
Definition lang_types.h:370
@ EXHAUSTIVE
serve until the queue empties
Definition lang_types.h:372
ProcessType
Distribution kinds, with the values of MATLAB ProcessType.
Definition lang_types.h:483
std::vector< T > map_pie(const Map< T > &m)
Phase distribution seen by an arriving job, pie = pi D1 / (pi D1 e).
Definition map_moment.h:89
PollingInfo< T > polling_info(const NetworkStruct< T > &sn, std::size_t ind)
A queueing network and its refreshed NetworkStruct.
Matrix< T > D0
The (D0,D1) pair when the type carries one directly.
Definition lang_types.h:759
A MAP as the pair of matrices (D0, D1).
Definition map_moment.h:53
Matrix< T > D1
Definition map_moment.h:55
Matrix< T > D0
Definition map_moment.h:54
The polling controller of a POLLING station, keyed by station index.
std::size_t pk
the K of K-LIMITED
std::vector< lang::Distrib< T > > switchover
Port of State.pollingInfo: the derived description of a polling controller.
std::vector< bool > has_sw
std::vector< std::size_t > ksw
std::vector< std::vector< T > > sw_pie
std::vector< Matrix< T > > sw_d0
std::vector< Matrix< T > > sw_d1
std::vector< bool > polled
lang::PollingType ptype