LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Loading...
Searching...
No Matches
pfqn_comomrm_ms.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_PFQN_COMOMRM_MS_H
6#define LINE_API_PFQN_COMOMRM_MS_H
7
8/**
9 * @file
10 * @ingroup api_pfqn
11 * CoMoM for the MULTISERVER repairman model: one queueing station with S
12 * servers (optionally replicated m times), plus a delay.
13 *
14 * Templated port of matlab/src/api/pfqn/pfqn_comomrm_ms.m, and the home of the
15 * bidiagonal transfer-matrix recursion shared with pfqn_comomrm_ld.
16 *
17 * The basis is the vector of normalizing constants indexed by the queue
18 * occupancy, h_k = G(k jobs at the queue), and adding one class-r job applies
19 * the bidiagonal transfer matrix
20 *
21 * T_r = Z_r I + superdiag_k ( L_r (Nt+1-k) / mu(Nt+1-k) ),
22 * h <- T_r h / n_r,
23 *
24 * once per job, for n_r = 1, ..., N_r. G(N) is the sum of the resulting vector
25 * and the queue-length marginal is its reversal, normalized.
26 *
27 * SCALING. The reference renormalizes h to unit 1-norm after every step and
28 * accumulates the discarded factors in log space, because in IEEE double the
29 * unscaled vector underflows. That renormalization is a pure change of
30 * representation: the discarded factors multiply back to exactly the sum of
31 * the unscaled vector, so this port drops it and returns
32 *
33 * G = Gremaind * sum_k h_k
34 *
35 * with h_k the UNSCALED basis. In an exact field the two agree identically; in
36 * double they agree to rounding. The marginal is unaffected either way, since
37 * it is a ratio within one vector.
38 *
39 * Arithmetic: EXACT-CAPABLE. Only additions, multiplications and divisions in
40 * the field of the inputs. The multiserver rate lattice comes from pfqn_mu_ms
41 * (m > 1) or is min(k, S) (m = 1), both exact.
42 */
43
44#include <cstddef>
45#include <vector>
46
50#include "line/num/number.h"
51#include "line/util/error.h"
52#include "line/util/matrix.h"
53
54namespace line {
55namespace pfqn {
56
57template <class T>
59 T G; ///< normalizing constant
60 double lG; ///< its logarithm
61 std::vector<T> prob; ///< (Nt+1) queue-length marginal, prob[k] = P(n = k)
62};
63
64namespace detail {
65
66/**
67 * Bidiagonal CoMoM recursion for a single queueing station with rate lattice
68 * mu(1..Nt) and think times Z, over the sanitized classes.
69 *
70 * @param L (R) per-class demand at the queueing station
71 * @param N (R) per-class population
72 * @param Z (R) per-class think time
73 * @param mu (Nt) rate lattice, mu[k-1] the rate with k jobs present
74 * @return the unnormalized basis h of length Nt+1, h[j] indexed as in the
75 * reference (h[Nt] is the empty-network seed)
76 */
77template <class T>
78std::vector<T> comomrm_bidiag(const std::vector<T>& L, const std::vector<int>& N,
79 const std::vector<T>& Z, const std::vector<T>& mu) {
80 const std::size_t R = N.size();
81 int Nt = 0;
82 for (int v : N) Nt += v;
83 if (static_cast<int>(mu.size()) < Nt)
84 throw InputError("comomrm_bidiag: the rate lattice is shorter than the total population");
85
86 const T zero = num_traits<T>::from_int(0);
87 std::vector<T> h(static_cast<std::size_t>(Nt) + 1, zero);
88 h[static_cast<std::size_t>(Nt)] = num_traits<T>::from_int(1);
89
90 for (std::size_t r = 0; r < R; ++r) {
91 for (int nr = 1; nr <= N[r]; ++nr) {
92 // 0-based row indexing rationale: see _kb/03-api-layer.md (cpp port notes: pfqn)
93 std::vector<T> hn(static_cast<std::size_t>(Nt) + 1, zero);
95 for (std::size_t k = 0; k <= static_cast<std::size_t>(Nt); ++k) {
96 T acc = Z[r] * h[k];
97 if (k < static_cast<std::size_t>(Nt)) {
98 const std::size_t j = static_cast<std::size_t>(Nt) - k; // 1 .. Nt
99 if (mu[j - 1] == zero)
100 throw NumericError("comomrm_bidiag: a load-dependent rate is zero");
101 acc += L[r] * num_traits<T>::from_int(static_cast<long>(j)) / mu[j - 1] *
102 h[k + 1];
103 }
104 hn[k] = acc * inv;
105 }
106 h.swap(hn);
107 }
108 }
109 return h;
110}
111
112/** Assemble G and the marginal from the unscaled basis and the sanitize factor. */
113template <class T>
114ComomRmResult<T> comomrm_finish(const std::vector<T>& h, const T& Gremaind) {
115 const T zero = num_traits<T>::from_int(0);
116 T s = zero;
117 for (const T& x : h) s += x;
118 ComomRmResult<T> res;
119 res.G = Gremaind * s;
120 res.lG = num_traits<T>::log_as_double(res.G);
121 res.prob.assign(h.size(), zero);
122 if (s != zero)
123 for (std::size_t k = 0; k < h.size(); ++k) res.prob[k] = h[h.size() - 1 - k] / s;
124 return res;
125}
126
127} // namespace detail
128
129/**
130 * @brief CoMoM for the MULTISERVER repairman model: one queueing station with
131 * S servers (optionally replicated m times), plus a delay.
132 *
133 * @param L (1 x R) demands at the single queueing station
134 * @param N (R) populations
135 * @param Z (1 x R) think times
136 * @param m replication factor of the queueing station
137 * @param S number of servers per replica
138 */
139template <class T>
140ComomRmResult<T> pfqn_comomrm_ms(const Matrix<T>& L, const std::vector<int>& N, const Matrix<T>& Z,
141 int m, int S) {
142 if (!L.empty() && L.rows() != 1)
143 throw InputError("pfqn_comomrm_ms: the solver accepts at most a single queueing station");
144 if (m < 1) throw InputError("pfqn_comomrm_ms: the replication factor must be at least one");
145 if (S < 1) throw InputError("pfqn_comomrm_ms: the server count must be at least one");
146
147 const NcSanitizeResult<T> san = pfqn_nc_sanitize(L, N, Z);
148 int Nt = 0;
149 for (int v : san.N) Nt += v;
150 if (Nt == 0) {
152 res.G = san.Gremaind;
153 res.lG = san.lGremaind;
154 res.prob.assign(1, num_traits<T>::from_int(1));
155 return res;
156 }
157
158 std::vector<T> mu;
159 if (m > 1) {
160 mu = pfqn_mu_ms<T>(Nt, m, S);
161 } else {
162 mu.assign(static_cast<std::size_t>(Nt), num_traits<T>::from_int(1));
163 for (int k = 1; k <= Nt; ++k)
164 mu[static_cast<std::size_t>(k - 1)] = num_traits<T>::from_int(k < S ? k : S);
165 }
166
167 const std::size_t Rk = san.N.size();
168 std::vector<T> Lv(Rk, num_traits<T>::from_int(0)), Zv(Rk, num_traits<T>::from_int(0));
169 for (std::size_t r = 0; r < Rk; ++r) {
170 if (!san.L.empty()) Lv[r] = san.L(0, r);
171 for (std::size_t k = 0; k < san.Z.rows(); ++k) Zv[r] += san.Z(k, r);
172 }
173 return detail::comomrm_finish(detail::comomrm_bidiag(Lv, san.N, Zv, mu), san.Gremaind);
174}
175
176/** Overload with the single-replica default. */
177template <class T>
178ComomRmResult<T> pfqn_comomrm_ms(const Matrix<T>& L, const std::vector<int>& N, const Matrix<T>& Z,
179 int S) {
180 return pfqn_comomrm_ms(L, N, Z, 1, S);
181}
182
183} // namespace pfqn
184} // namespace line
185
186#endif // LINE_API_PFQN_COMOMRM_MS_H
InputError(const std::string &what)
Definition error.h:39
std::size_t rows() const
Definition matrix.h:89
bool empty() const
Definition matrix.h:92
NumericError(const std::string &what)
Definition error.h:45
The exception types the port throws.
Dense matrix and non-owning view.
NcSanitizeResult< T > pfqn_nc_sanitize(const std::vector< T > &lambda, const Matrix< T > &L, const std::vector< int > &N, const Matrix< T > &Z, const T &atol)
Preprocessing shared by the normalizing-constant solvers: drop the classes that cannot contribute,...
std::vector< T > pfqn_mu_ms(int N, int m, int c)
Aggregate load-dependent rate of m identical c-server FCFS stations.
Definition pfqn_mu_ms.h:56
ComomRmResult< T > pfqn_comomrm_ms(const Matrix< T > &L, const std::vector< int > &N, const Matrix< T > &Z, int m, int S)
CoMoM for the MULTISERVER repairman model: one queueing station with S servers (optionally replicated...
Number-type abstraction for the templated API port.
Convolution algorithm for the exact normalizing constant of a closed product-form network (Buzen 1973...
Aggregate load-dependent rate of m identical c-server FCFS stations.
Preprocessing shared by the normalizing-constant solvers: drop the classes that cannot contribute,...
T G
normalizing constant
std::vector< T > prob
(Nt+1) queue-length marginal, prob[k] = P(n = k)
double lG
its logarithm
T Gremaind
multiplicative factor removed from G
Matrix< T > Z
retained think times, rescaled and reordered
Matrix< T > L
retained demands, rescaled and reordered
double lGremaind
its logarithm, for the log-space callers
std::vector< int > N
retained populations, reordered