LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Loading...
Searching...
No Matches
me_types.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_ME_ME_TYPES_H
6#define LINE_API_ME_ME_TYPES_H
7
8/**
9 * @file
10 * @ingroup api_me
11 * Shared declarations for the maximum-entropy (Kouvatsos) queueing network
12 * algorithms.
13 *
14 * Templated port of matlab/src/api/me/, cross-checked against
15 * jar/src/main/java/jline/api/nc/Me_oqn.java, Me_cqn.java and Me_mqn.java.
16 *
17 * Reference: D.D. Kouvatsos, "Entropy Maximisation and Queueing Network
18 * Models", Annals of Operations Research 48:63-126, 1994.
19 *
20 * CONVENTIONS
21 * - Stations are indexed 0..M-1, classes 0..R-1.
22 * - Per-station, per-class data (arrival rates, service rates, scvs) are
23 * (M x R) matrices; routing is a vector of R (M x M) matrices with
24 * P[r](j,i) the probability that a class-r job moves from j to i.
25 * - The server count is a vector of longs with 0 standing for an
26 * INFINITE-SERVER station. MATLAB and the JAR use Inf for this; a
27 * templated port cannot rely on T having an infinity (Rational does not),
28 * and the algorithms only ever test isinf(c(i)), never arithmetic on it.
29 * - `insens` marks stations with an insensitive discipline (PS, LCFS-PR),
30 * which are solved by the product-form mean queue length instead of the
31 * GE-type FCFS formula.
32 *
33 * ARITHMETIC
34 * Every function in this domain is a damped fixed-point iteration stopped by
35 * a relative tolerance, and me_cqn additionally evaluates its Lagrangian
36 * coefficient functions through log-gamma and exp. They therefore all carry
37 * static_assert(num_traits<T>::has_transcendental)
38 * and are instantiated for double and Real50 only. Raising the precision is
39 * a legitimate use here: the ME coefficients of (3.8) are products of up to
40 * sum(N) factors, and the convolution that normalizes them cancels heavily
41 * at high population, which is precisely where the double solution starts to
42 * lose its population constraint sum_i L(i,r) = N(r).
43 */
44
45#include <algorithm>
46#include <cmath>
47#include <cstddef>
48#include <vector>
49
50#include "line/num/number.h"
51#include "line/util/error.h"
52#include "line/util/matrix.h"
53
54namespace line {
55namespace me {
56
57/** Iteration control, mirroring the MATLAB options struct. */
58struct MeOptions {
59 double tol = 1e-6;
60 long maxiter = 1000;
61};
62
63namespace detail {
64
65/** exp(v), resolved by ADL. */
66template <class T>
67inline T num_exp(const T& v) {
68 using std::exp;
69 return exp(v);
70}
71
72/** log(v), resolved by ADL. */
73template <class T>
74inline T num_log(const T& v) {
75 using std::log;
76 return log(v);
77}
78
79/** sqrt(v), resolved by ADL. */
80template <class T>
81inline T num_sqrt(const T& v) {
82 using std::sqrt;
83 return sqrt(v);
84}
85
86/** log(k!), the gammaln(k+1) of the MATLAB source. */
87template <class T>
88inline T log_factorial(long k) {
89 T s = num_traits<T>::from_int(0);
90 for (long j = 2; j <= k; ++j) s += num_log(num_traits<T>::from_int(j));
91 return s;
92}
93
94/** true when station i has an infinite number of servers. */
95inline bool is_is(const std::vector<long>& c, std::size_t i) { return c[i] <= 0; }
96
97/** Solve A x = b by Gaussian elimination with partial pivoting. */
98template <class T>
99std::vector<T> linear_solve(Matrix<T> A, std::vector<T> b) {
100 const std::size_t n = A.rows();
101 if (A.cols() != n || b.size() != n)
102 throw InputError("me: linear solve with inconsistent dimensions");
103 const T zero = num_traits<T>::from_int(0);
104 for (std::size_t k = 0; k < n; ++k) {
105 std::size_t p = k;
106 T amax = num_abs(A(k, k));
107 for (std::size_t i = k + 1; i < n; ++i) {
108 const T a = num_abs(A(i, k));
109 if (a > amax) {
110 amax = a;
111 p = i;
112 }
113 }
114 if (amax == zero) throw NumericError("me: singular flow balance equations");
115 if (p != k) {
116 for (std::size_t j = 0; j < n; ++j) std::swap(A(k, j), A(p, j));
117 std::swap(b[k], b[p]);
118 }
119 for (std::size_t i = k + 1; i < n; ++i) {
120 const T f = A(i, k) / A(k, k);
121 if (f == zero) continue;
122 for (std::size_t j = k; j < n; ++j) A(i, j) -= f * A(k, j);
123 b[i] -= f * b[k];
124 }
125 }
126 std::vector<T> x(n, zero);
127 for (std::size_t ii = n; ii-- > 0;) {
128 T s = b[ii];
129 for (std::size_t j = ii + 1; j < n; ++j) s -= A(ii, j) * x[j];
130 x[ii] = s / A(ii, ii);
131 }
132 return x;
133}
134
135/** Validates the shared (M x R) / routing / server arguments. */
136template <class T>
137inline void check_dims(std::size_t M, std::size_t R, const Matrix<T>& mu, const Matrix<T>& Cs,
138 const std::vector<Matrix<T>>& P, const std::vector<long>& c,
139 const std::vector<char>& insens, const char* who) {
140 if (M == 0 || R == 0) throw InputError(std::string(who) + ": M and R must be positive");
141 if (mu.rows() != M || mu.cols() != R || Cs.rows() != M || Cs.cols() != R)
142 throw InputError(std::string(who) + ": mu and Cs must be M x R");
143 if (P.size() != R) throw InputError(std::string(who) + ": one routing matrix per class");
144 for (std::size_t r = 0; r < R; ++r)
145 if (P[r].rows() != M || P[r].cols() != M)
146 throw InputError(std::string(who) + ": each routing matrix must be M x M");
147 if (c.size() != M) throw InputError(std::string(who) + ": one server count per station");
148 if (insens.size() != M) throw InputError(std::string(who) + ": one insens flag per station");
149 // insens is consulted only at single-server stations, exactly as in the
150 // references, so a flag set at a multiserver station is simply inert.
151}
152
153} // namespace detail
154
155/** Mean-value results shared by the open, closed and mixed algorithms. */
156template <class T>
157struct MeResult {
158 Matrix<T> L; ///< mean queue lengths (M x R)
159 Matrix<T> W; ///< mean response times (M x R)
160 Matrix<T> Ca; ///< arrival scvs (M x R)
161 Matrix<T> Cd; ///< departure scvs (M x R)
162 Matrix<T> lambda; ///< per-station throughputs (M x R)
163 Matrix<T> rho; ///< utilizations (M x R)
164 std::vector<T> X; ///< class throughputs (R)
165 long iter = 0; ///< fixed-point iterations performed
166 bool converged = false;
167};
168
169} // namespace me
170} // namespace line
171
172#endif // LINE_API_ME_ME_TYPES_H
InputError(const std::string &what)
Definition error.h:39
NumericError(const std::string &what)
Definition error.h:45
The exception types the port throws.
Dense matrix and non-owning view.
T num_abs(const T &v)
Definition number.h:172
Number-type abstraction for the templated API port.
Iteration control, mirroring the MATLAB options struct.
Definition me_types.h:58
Mean-value results shared by the open, closed and mixed algorithms.
Definition me_types.h:157
Matrix< T > rho
utilizations (M x R)
Definition me_types.h:163
Matrix< T > L
mean queue lengths (M x R)
Definition me_types.h:158
Matrix< T > W
mean response times (M x R)
Definition me_types.h:159
Matrix< T > Ca
arrival scvs (M x R)
Definition me_types.h:160
Matrix< T > Cd
departure scvs (M x R)
Definition me_types.h:161
std::vector< T > X
class throughputs (R)
Definition me_types.h:164
long iter
fixed-point iterations performed
Definition me_types.h:165
Matrix< T > lambda
per-station throughputs (M x R)
Definition me_types.h:162