LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Loading...
Searching...
No Matches
npfqn_traffic_split_rr.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_NPFQN_TRAFFIC_SPLIT_RR_H
6#define LINE_API_NPFQN_TRAFFIC_SPLIT_RR_H
7
8/**
9 * @file
10 * @ingroup api_npfqn
11 * Deterministic (round-robin) split degrees of every station-class departure
12 * stream.
13 *
14 * Templated port of matlab/src/api/npfqn/npfqn_traffic_split_rr.m,
15 * cross-checked against
16 * jar/src/main/java/jline/api/npfqn/Npfqn_traffic_split_rr.java and
17 * python/line_solver/api/npfqn/split_rr.py (identical).
18 *
19 * kRR(i,r) = k > 1 says the class-r departures of station i are dispatched
20 * one-in-k by a round-robin node, so a downstream flow carrying a fraction p of
21 * them is the k-fold convolution thinned at q = k p and has SCV 1 + p (d2 - k),
22 * against the Markovian 1 + p (d2 - 1). kRR(i,r) = 1 is an ordinary
23 * probabilistic split, which is what every entry stays at on a model with no
24 * round-robin dispatcher; the QNA/MNA equations then reduce exactly to the
25 * Bernoulli-thinning form they had before this factor existed.
26 *
27 * Only two topologies admit the deterministic rule: the station dispatches
28 * round-robin itself, or it feeds WITH PROBABILITY ONE a non-station node that
29 * does and whose pointer no other flow advances. A router shared by two
30 * upstream streams interleaves them, so neither sees a clean one-in-k split and
31 * both fall back to k = 1.
32 *
33 * THE DEGREE IS READ OFF `rtnodes`, NOT off `connmatrix` and not off an
34 * `outlinks` list. The reference prefers `sn.nodeparam{ind}{r}.outlinks`, the
35 * per-class destination list of the dispatcher, and falls back to the row of
36 * `sn.connmatrix`. This port's NetworkStruct carries neither field: `rtnodes`
37 * is the same graph once refresh_routing has expanded the strategies (the same
38 * substitution downstream_stations documents in network_struct.h). Counting the
39 * DISTINCT destination NODES of row (ind,r) reproduces the per-class `outlinks`
40 * semantics, which is the branch the reference takes whenever a dispatcher was
41 * actually built, and differs from the connmatrix fallback only on a link the
42 * model declares and then routes no mass over.
43 *
44 * ARITHMETIC: field, and integral in fact. The result is a matrix of counts;
45 * only comparisons against zero and against 1 - FineTol are taken, so this is
46 * exact at T = Rational.
47 */
48
49#include <cstddef>
50#include <vector>
51
54#include "line/num/number.h"
55#include "line/util/matrix.h"
56
57namespace line {
58namespace npfqn {
59
60namespace detail {
61
62/** The routing strategy of (1-based node `ind`, 1-based class `r`). */
63template <class T>
64qn::RoutingStrategy routing_of(const qn::NetworkStruct<T>& sn, std::size_t ind, std::size_t r) {
65 const std::vector<qn::RoutingStrategy>& rs = sn.nodes[ind - 1].routing;
66 return rs.size() >= r ? rs[r - 1] : qn::RoutingStrategy::PROB;
67}
68
69/** Number of destinations the round-robin pointer of (`ind`, `r`) cycles through. */
70template <class T>
71std::size_t rr_degree(const qn::NetworkStruct<T>& sn, std::size_t ind, std::size_t r) {
72 const T zero = num_traits<T>::from_int(0);
73 const std::size_t K = sn.nclasses, I = sn.nodes.size();
74 std::size_t k = 0;
75 for (std::size_t j = 1; j <= I; ++j) {
76 bool linked = false;
77 for (std::size_t s = 1; s <= K && !linked; ++s)
78 if (sn.rtnodes((ind - 1) * K + (r - 1), (j - 1) * K + (s - 1)) > zero) linked = true;
79 if (linked) ++k;
80 }
81 return k < 1 ? 1 : k;
82}
83
84} // namespace detail
85
86/**
87 * Port of `npfqn_traffic_split_rr.m`.
88 *
89 * @param sn the model, after refresh(): `rtnodes` carries the expanded routing
90 * @return an (nstations x nclasses) matrix of split degrees, 1 where the split
91 * is Markovian
92 */
93template <class T>
95 const T zero = num_traits<T>::from_int(0);
96 const T one = num_traits<T>::from_int(1);
97 const std::size_t M = sn.nstations, K = sn.nclasses, I = sn.nodes.size();
98 Matrix<T> kRR(M, K, one);
99
100 bool any_rr = false;
101 for (const qn::NodeDef& nd : sn.nodes)
102 for (qn::RoutingStrategy rs : nd.routing)
103 if (rs == qn::RoutingStrategy::RROBIN) any_rr = true;
104 if (!any_rr) return kRR;
105 if (sn.rtnodes.rows() < I * K) return kRR;
106
108 for (std::size_t ist = 1; ist <= M; ++ist) {
109 const std::size_t ind = sn.node_of_station(ist);
110 if (ind == 0) continue;
111 for (std::size_t r = 1; r <= K; ++r) {
112 if (detail::routing_of(sn, ind, r) == qn::RoutingStrategy::RROBIN) {
113 kRR(ist - 1, r - 1) =
114 num_traits<T>::from_int(static_cast<long>(detail::rr_degree(sn, ind, r)));
115 continue;
116 }
117 // a sure transition into a node that dispatches round-robin
118 const std::size_t row = (ind - 1) * K + (r - 1);
119 std::size_t dest = 0, ndest = 0;
120 for (std::size_t col = 0; col < sn.rtnodes.cols(); ++col)
121 if (sn.rtnodes(row, col) > zero) {
122 dest = col;
123 ++ndest;
124 }
125 if (ndest != 1) continue;
126 const std::size_t jnd = dest / K + 1, s = dest % K + 1;
127 if (sn.nodes[jnd - 1].station != 0) continue;
128 if (detail::routing_of(sn, jnd, s) != qn::RoutingStrategy::RROBIN) continue;
129 if (sn.rtnodes(row, dest) < sure) continue;
130 // the round-robin pointer must be advanced by this stream alone
131 std::size_t nfeed = 0;
132 for (std::size_t a = 0; a < sn.rtnodes.rows(); ++a)
133 if (sn.rtnodes(a, dest) > zero) ++nfeed;
134 if (nfeed != 1) continue;
135 kRR(ist - 1, r - 1) =
136 num_traits<T>::from_int(static_cast<long>(detail::rr_degree(sn, jnd, s)));
137 }
138 }
139 return kRR;
140}
141
142} // namespace npfqn
143} // namespace line
144
145#endif // LINE_API_NPFQN_TRAFFIC_SPLIT_RR_H
A network plus its refreshed NetworkStruct.
Enumerations and the minimal distribution descriptor shared by the model layer of the C++ port.
Dense matrix and non-owning view.
RoutingStrategy
Routing strategies, with the values of MATLAB RoutingStrategy.
Definition lang_types.h:389
Matrix< T > npfqn_traffic_split_rr(const qn::NetworkStruct< T > &sn)
Port of npfqn_traffic_split_rr.m.
A queueing network and its refreshed NetworkStruct.
Number-type abstraction for the templated API port.
static constexpr double FineTol
Definition lang_types.h:668
A node of the network.
std::vector< RoutingStrategy > routing
sn.routing, per class.