LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Loading...
Searching...
No Matches
network_generators.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_NETWORK_GENERATORS_H
6#define LINE_LANG_QN_NETWORK_GENERATORS_H
7
8/**
9 * @file
10 * @ingroup line_lang
11 * @ingroup line_public
12 * The static model factories of MATLAB's `Network`: `tandem*` and `cyclic*`.
13 *
14 * Ports of `MNetwork.m`'s `tandem`, `tandemPs`, `tandemPsInf`, `tandemFcfs`,
15 * `tandemFcfsInf`, `cyclic`, `cyclicPs`, `cyclicPsInf`, `cyclicFcfs` and
16 * `cyclicFcfsInf`. They build the SAME model the reference builds, node names
17 * and class names included, so a table keyed by name lines up across
18 * codebases:
19 *
20 * tandem Source, `Station1..StationM`, Sink, serial routing; class r is an
21 * open class `Class<r>` arriving at rate `lambda[r]` (an exponential
22 * of mean 1/lambda[r]) and served with mean `D(i,r)` at station i.
23 * An INF station is a Delay, any other a Queue under its strategy.
24 * cyclic `Delay<k>` / `Queue<k>` numbered within their kind, cyclic routing
25 * 1 -> 2 -> ... -> M -> 1; class r is a closed class `Class<r>` of
26 * `N[r]` jobs referencing the FIRST station, served with mean
27 * `D(i,r)`; a Queue gets `S[i]` servers.
28 *
29 * The `*Inf` variants put the rows of Z FIRST as delays and the rows of D after
30 * them, which is the reference's `[Z; D]`; the delays of a cyclic model carry
31 * an infinite server count in the stacked S vector, as `[Inf*ones(Mz,1); S]`.
32 */
33
34#include <cmath>
35#include <limits>
36#include <string>
37#include <vector>
38
40#include "line/util/error.h"
41#include "line/util/matrix.h"
42
43namespace line {
44namespace qn {
45
46namespace gen_detail {
47
48/** `[Z; D]`: the rows of Z stacked on top of the rows of D. */
49template <class T>
51 if (Z.rows() == 0) return D;
52 if (Z.cols() != D.cols())
53 throw InputError("Network generator: Z and D must have one column per class");
54 Matrix<T> out(Z.rows() + D.rows(), D.cols(), num_traits<T>::from_int(0));
55 for (std::size_t i = 0; i < Z.rows(); ++i)
56 for (std::size_t r = 0; r < Z.cols(); ++r) out(i, r) = Z(i, r);
57 for (std::size_t i = 0; i < D.rows(); ++i)
58 for (std::size_t r = 0; r < D.cols(); ++r) out(Z.rows() + i, r) = D(i, r);
59 return out;
60}
61
62/** Mz INF strategies, then M of `sched`. */
63inline std::vector<SchedStrategy> inf_then(std::size_t Mz, std::size_t M, SchedStrategy sched) {
64 std::vector<SchedStrategy> st(Mz, SchedStrategy::INF);
65 st.insert(st.end(), M, sched);
66 return st;
67}
68
69} // namespace gen_detail
70
71/**
72 * `Network.tandem(lambda, D, strategy)`: an open tandem line.
73 *
74 * @param lambda arrival rate per class
75 * @param D D(i,r), mean service time of class r at station i
76 * @param strategy scheduling strategy of each station
77 */
78template <class T>
79Network<T> tandem(const std::vector<T>& lambda, const Matrix<T>& D,
80 const std::vector<SchedStrategy>& strategy) {
81 const std::size_t M = D.rows(), R = D.cols();
82 if (lambda.size() != R)
83 throw InputError("Network.tandem: lambda must have one entry per column of D");
84 if (strategy.size() != M)
85 throw InputError("Network.tandem: strategy must have one entry per row of D");
86 Network<T> model("Model");
87 std::vector<std::size_t> node;
88 node.push_back(model.add_source("Source"));
89 for (std::size_t i = 0; i < M; ++i) {
90 const std::string nm = "Station" + std::to_string(i + 1);
91 node.push_back(strategy[i] == SchedStrategy::INF ? model.add_delay(nm)
92 : model.add_queue(nm, strategy[i]));
93 }
94 node.push_back(model.add_sink("Sink"));
96 std::vector<std::size_t> cls(R);
97 for (std::size_t r = 0; r < R; ++r) {
98 cls[r] = model.add_open_class("Class" + std::to_string(r + 1), 0);
99 for (std::size_t k = 0; k + 1 < node.size(); ++k)
100 P.set(cls[r], cls[r], node[k], node[k + 1], num_traits<T>::from_int(1));
101 }
102 const T one = num_traits<T>::from_int(1);
103 for (std::size_t r = 0; r < R; ++r) {
104 model.set_arrival(node[0], cls[r], Distrib<T>::exp_mean(T(one / lambda[r])));
105 for (std::size_t i = 0; i < M; ++i)
106 model.set_service(node[1 + i], cls[r], Distrib<T>::exp_mean(D(i, r)));
107 }
108 model.link(P);
109 return model;
110}
111
112/** `Network.tandemPsInf(lambda, D, Z)`: delays for the rows of Z, then PS queues for D. */
113template <class T>
114Network<T> tandem_ps_inf(const std::vector<T>& lambda, const Matrix<T>& D, const Matrix<T>& Z) {
115 return tandem(lambda, gen_detail::stack_rows(Z, D),
116 gen_detail::inf_then(Z.rows(), D.rows(), SchedStrategy::PS));
117}
118
119/** `Network.tandemPs(lambda, D)`: PS queues only. */
120template <class T>
121Network<T> tandem_ps(const std::vector<T>& lambda, const Matrix<T>& D) {
122 return tandem_ps_inf(lambda, D, Matrix<T>(0, D.cols(), num_traits<T>::from_int(0)));
123}
124
125/** `Network.tandemFcfsInf(lambda, D, Z)`: delays for the rows of Z, then FCFS queues for D. */
126template <class T>
127Network<T> tandem_fcfs_inf(const std::vector<T>& lambda, const Matrix<T>& D, const Matrix<T>& Z) {
128 return tandem(lambda, gen_detail::stack_rows(Z, D),
129 gen_detail::inf_then(Z.rows(), D.rows(), SchedStrategy::FCFS));
130}
131
132/** `Network.tandemFcfs(lambda, D)`: FCFS queues only. */
133template <class T>
134Network<T> tandem_fcfs(const std::vector<T>& lambda, const Matrix<T>& D) {
135 return tandem_fcfs_inf(lambda, D, Matrix<T>(0, D.cols(), num_traits<T>::from_int(0)));
136}
137
138/**
139 * `Network.cyclic(N, D, strategy, S)`: a closed cyclic network.
140 *
141 * @param N population per class
142 * @param D D(i,r), mean service time of class r at station i
143 * @param strategy scheduling strategy of each station
144 * @param S number of servers of each station (read at the Queues only)
145 */
146template <class T>
147Network<T> cyclic(const std::vector<double>& N, const Matrix<T>& D,
148 const std::vector<SchedStrategy>& strategy, const std::vector<double>& S) {
149 const std::size_t M = D.rows(), R = D.cols();
150 if (N.size() != R)
151 throw InputError("Network.cyclic: N must have one entry per column of D");
152 if (strategy.size() != M || S.size() != M)
153 throw InputError("Network.cyclic: strategy and S must have one entry per row of D");
154 Network<T> model("Model");
155 std::vector<std::size_t> node(M);
156 std::size_t nQ = 0, nD = 0;
157 for (std::size_t i = 0; i < M; ++i) {
158 if (strategy[i] == SchedStrategy::INF) {
159 node[i] = model.add_delay("Delay" + std::to_string(++nD));
160 } else {
161 node[i] = model.add_queue("Queue" + std::to_string(++nQ), strategy[i]);
162 model.set_number_of_servers(node[i], S[i]);
163 }
164 }
166 std::vector<std::size_t> cls(R);
167 for (std::size_t r = 0; r < R; ++r) {
168 cls[r] = model.add_closed_class("Class" + std::to_string(r + 1), N[r], node[0], 0);
169 // circul(M): i -> i+1, M -> 1; a single station loops on itself
170 for (std::size_t i = 0; i < M; ++i)
171 P.set(cls[r], cls[r], node[i], node[(i + 1) % M], num_traits<T>::from_int(1));
172 }
173 for (std::size_t i = 0; i < M; ++i)
174 for (std::size_t r = 0; r < R; ++r)
175 model.set_service(node[i], cls[r], Distrib<T>::exp_mean(D(i, r)));
176 model.link(P);
177 return model;
178}
179
180/**
181 * `Network.cyclicPsInf(N, D, Z, S)`: delays for the rows of Z, then PS queues
182 * for the rows of D with `S` servers each (one server each when S is empty).
183 */
184template <class T>
185Network<T> cyclic_ps_inf(const std::vector<double>& N, const Matrix<T>& D, const Matrix<T>& Z,
186 std::vector<double> S = std::vector<double>()) {
187 if (S.empty()) S.assign(D.rows(), 1.0);
188 std::vector<double> Sall(Z.rows(), std::numeric_limits<double>::infinity());
189 Sall.insert(Sall.end(), S.begin(), S.end());
190 return cyclic(N, gen_detail::stack_rows(Z, D),
191 gen_detail::inf_then(Z.rows(), D.rows(), SchedStrategy::PS), Sall);
192}
193
194/** `Network.cyclicPs(N, D, S)`: PS queues only. */
195template <class T>
196Network<T> cyclic_ps(const std::vector<double>& N, const Matrix<T>& D,
197 const std::vector<double>& S = std::vector<double>()) {
198 return cyclic_ps_inf(N, D, Matrix<T>(0, D.cols(), num_traits<T>::from_int(0)), S);
199}
200
201/** `Network.cyclicFcfsInf(N, D, Z, S)`: delays for Z, then FCFS queues for D. */
202template <class T>
203Network<T> cyclic_fcfs_inf(const std::vector<double>& N, const Matrix<T>& D, const Matrix<T>& Z,
204 std::vector<double> S = std::vector<double>()) {
205 if (S.empty()) S.assign(D.rows(), 1.0);
206 std::vector<double> Sall(Z.rows(), std::numeric_limits<double>::infinity());
207 Sall.insert(Sall.end(), S.begin(), S.end());
208 return cyclic(N, gen_detail::stack_rows(Z, D),
209 gen_detail::inf_then(Z.rows(), D.rows(), SchedStrategy::FCFS), Sall);
210}
211
212/** `Network.cyclicFcfs(N, D, S)`: FCFS queues only. */
213template <class T>
214Network<T> cyclic_fcfs(const std::vector<double>& N, const Matrix<T>& D,
215 const std::vector<double>& S = std::vector<double>()) {
216 return cyclic_fcfs_inf(N, D, Matrix<T>(0, D.cols(), num_traits<T>::from_int(0)), S);
217}
218
219} // namespace qn
220} // namespace line
221
222#endif // LINE_LANG_QN_NETWORK_GENERATORS_H
InputError(const std::string &what)
Definition error.h:105
std::size_t cols() const
Definition matrix.h:90
std::size_t rows() const
Definition matrix.h:89
A queueing network under construction.
std::size_t add_source(const std::string &nm)
The external arrival station.
std::size_t add_open_class(const std::string &nm, int prio=0)
An open class.
std::size_t add_delay(const std::string &nm)
An infinite-server station (a Delay, MATLAB's Delay / DelayStation).
void set_number_of_servers(std::size_t node, double n)
queue.setNumberOfServers(n).
std::size_t add_queue(const std::string &nm, SchedStrategy sched=SchedStrategy::FCFS)
A queueing station.
std::size_t add_closed_class(const std::string &nm, double njobs, std::size_t refstat_node, int prio=0)
A closed class of the given population, referencing a station node.
std::size_t add_sink(const std::string &nm)
The external departure node.
void link(const RoutingMatrix< T > &Pm)
model.link(P): install the routing.
void set_service(std::size_t node, std::size_t cls, const Distrib< T > &d)
station.setService(class, dist).
void set_arrival(std::size_t node, std::size_t cls, const Distrib< T > &d)
source.setArrival(class, dist): the same table, at the Source.
The routing matrix a model script fills in, MATLAB's P cell array.
The exception types the port throws.
Dense matrix and non-owning view.
SchedStrategy
Scheduling disciplines, with the values of MATLAB SchedStrategy.
Definition lang_types.h:185
std::vector< SchedStrategy > inf_then(std::size_t Mz, std::size_t M, SchedStrategy sched)
Mz INF strategies, then M of sched.
Matrix< T > stack_rows(const Matrix< T > &Z, const Matrix< T > &D)
[Z; D]: the rows of Z stacked on top of the rows of D.
Network< T > cyclic_fcfs_inf(const std::vector< double > &N, const Matrix< T > &D, const Matrix< T > &Z, std::vector< double > S=std::vector< double >())
Network.cyclicFcfsInf(N, D, Z, S): delays for Z, then FCFS queues for D.
Network< T > tandem_ps(const std::vector< T > &lambda, const Matrix< T > &D)
Network.tandemPs(lambda, D): PS queues only.
Network< T > cyclic_ps_inf(const std::vector< double > &N, const Matrix< T > &D, const Matrix< T > &Z, std::vector< double > S=std::vector< double >())
Network.cyclicPsInf(N, D, Z, S): delays for the rows of Z, then PS queues for the rows of D with S se...
Network< T > cyclic_fcfs(const std::vector< double > &N, const Matrix< T > &D, const std::vector< double > &S=std::vector< double >())
Network.cyclicFcfs(N, D, S): FCFS queues only.
Network< T > tandem_fcfs(const std::vector< T > &lambda, const Matrix< T > &D)
Network.tandemFcfs(lambda, D): FCFS queues only.
Network< T > tandem_ps_inf(const std::vector< T > &lambda, const Matrix< T > &D, const Matrix< T > &Z)
Network.tandemPsInf(lambda, D, Z): delays for the rows of Z, then PS queues for D.
Network< T > cyclic(const std::vector< double > &N, const Matrix< T > &D, const std::vector< SchedStrategy > &strategy, const std::vector< double > &S)
Network.cyclic(N, D, strategy, S): a closed cyclic network.
Network< T > cyclic_ps(const std::vector< double > &N, const Matrix< T > &D, const std::vector< double > &S=std::vector< double >())
Network.cyclicPs(N, D, S): PS queues only.
Network< T > tandem(const std::vector< T > &lambda, const Matrix< T > &D, const std::vector< SchedStrategy > &strategy)
Network.tandem(lambda, D, strategy): an open tandem line.
Network< T > tandem_fcfs_inf(const std::vector< T > &lambda, const Matrix< T > &D, const Matrix< T > &Z)
Network.tandemFcfsInf(lambda, D, Z): delays for the rows of Z, then FCFS queues for D.
Conservation laws of a layered queueing network, enumerated from its structure.
Definition aoi_dist2ph.h:52
The Network constructor API: Queue, Delay, Source, Sink, Router, ClassSwitch, Cache,...
static Distrib exp_mean(const T &m)
Definition lang_types.h:940