LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Loading...
Searching...
No Matches
pfqn_amva_common.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_AMVA_COMMON_H
6#define LINE_API_PFQN_AMVA_COMMON_H
7
8/**
9 * @file
10 * @ingroup api_pfqn
11 * Scaffolding shared by the approximate-MVA family. This header is NOT a port
12 * of a MATLAB function: it collects the small utilities that
13 * matlab/src/util/ provides to every AMVA routine (oner, pprod, sprod,
14 * multichoose, enorm) plus the scheduling-discipline tag those routines branch
15 * on, so that each pfqn_*.h below stays a 1:1 port of its own MATLAB file.
16 *
17 * Scheduling disciplines. The MATLAB routines take sn.sched-valued vectors but
18 * only ever compare them against SchedStrategy.PS, SchedStrategy.FCFS and
19 * SchedStrategy.INF, so the full enum is not part of the porting surface: the
20 * three tags below are what the algorithms actually distinguish. Anything else
21 * in a model is handled upstream, in the NetworkStruct layer, which is out of
22 * scope here.
23 */
24
25#include <cstddef>
26#include <vector>
27
28#include "line/num/number.h"
29#include "line/util/error.h"
30#include "line/util/matrix.h"
32
33namespace line {
34namespace pfqn {
35
36/** The three scheduling disciplines the AMVA and Schmidt recursions branch on. */
37enum class SchedStrategy { PS, FCFS, INF };
38
39/**
40 * matlab/src/util/oner.m: decrement position r of N, with r given 1-based and
41 * r == 0 meaning "leave N alone" (the s == 0 arm of every `for s=0:R` loop).
42 * The result may go negative, exactly as in MATLAB; callers that cannot cope
43 * with a negative population guard it themselves, as the MATLAB ones do.
44 */
45inline std::vector<int> oner(const std::vector<int>& N, std::size_t r) {
46 std::vector<int> n(N);
47 if (r >= 1) {
48 if (r > n.size()) throw InputError("oner: class index out of range");
49 n[r - 1] -= 1;
50 }
51 return n;
52}
53
54/** matlab/src/util/enorm.m: Frobenius norm of a matrix. */
55template <class T>
56T enorm(const Matrix<T>& A) {
58 for (std::size_t i = 0; i < A.rows(); ++i)
59 for (std::size_t j = 0; j < A.cols(); ++j) s += A(i, j) * A(i, j);
60 using std::sqrt;
61 return sqrt(s);
62}
63
64/**
65 * Frobenius norm of the difference of two equally shaped matrices, AS A DOUBLE.
66 *
67 * The only use of this quantity anywhere in the AMVA family is the stopping
68 * test `enorm_diff(Q, Qlast) < tol`, and tol is a double. Returning a double
69 * therefore loses nothing and buys the exact backend the whole family: sqrt is
70 * not an operation of the rational field, so a T-valued version cannot be
71 * instantiated there at all. The sum of squares is accumulated in T, exactly,
72 * and only the final square root drops to double, so for T == double the
73 * result is bit-identical to sqrt of the T-valued sum.
74 */
75template <class T>
76double enorm_diff(const Matrix<T>& A, const Matrix<T>& B) {
78 for (std::size_t i = 0; i < A.rows(); ++i)
79 for (std::size_t j = 0; j < A.cols(); ++j) {
80 const T d = A(i, j) - B(i, j);
81 s += d * d;
82 }
83 return std::sqrt(num_traits<T>::to_double(s));
84}
85
86/**
87 * Sum the rows of a think-time matrix into a length-R vector, the `sum(Z,1)`
88 * that every AMVA entry point performs on its Z argument. An empty Z gives
89 * the all-zero vector.
90 */
91template <class T>
92std::vector<T> sum_rows(const Matrix<T>& Z, std::size_t R) {
93 std::vector<T> z(R, num_traits<T>::from_int(0));
94 if (Z.empty()) return z;
95 if (Z.cols() != R) throw InputError("think-time matrix has the wrong class count");
96 for (std::size_t k = 0; k < Z.rows(); ++k)
97 for (std::size_t r = 0; r < R; ++r) z[r] += Z(k, r);
98 return z;
99}
100
101/**
102 * matlab/src/util/multichoose.m and sprod.m, as an in-place odometer: the
103 * compositions of `c` into R non-negative parts, i.e. the vectors n with
104 * sum(n) == c. Start from first_composition and iterate until this returns
105 * false. The enumeration order differs from MATLAB's recursive one; every use
106 * site sums over the whole set, so only completeness matters.
107 */
108inline void first_composition(std::vector<int>& n, int c) {
109 n.assign(n.size(), 0);
110 if (!n.empty()) n[0] = c;
111}
112
113inline bool next_composition(std::vector<int>& n) {
114 const long R = static_cast<long>(n.size());
115 if (R < 2) return false;
116 long i = R - 2;
117 while (i >= 0 && n[i] == 0) --i;
118 if (i < 0) return false;
119 // Every entry strictly between i and the last is zero by the choice of i,
120 // so the whole remainder is carried in the last slot.
121 const int tail = n[R - 1];
122 n[R - 1] = 0;
123 n[i] -= 1;
124 n[i + 1] = tail + 1;
125 return true;
126}
127
128/** Multinomial coefficient sum(m)!/prod_i m_i!, exact in any arithmetic. */
129template <class T>
130T num_multinomial(const std::vector<int>& m) {
131 int tot = 0;
132 for (int v : m) tot += v;
133 T r = num_factorial<T>(static_cast<unsigned>(tot));
134 for (int v : m) r /= num_factorial<T>(static_cast<unsigned>(v));
135 return r;
136}
137
138} // namespace pfqn
139} // namespace line
140
141#endif // LINE_API_PFQN_AMVA_COMMON_H
InputError(const std::string &what)
Definition error.h:39
std::size_t cols() const
Definition matrix.h:90
std::size_t rows() const
Definition matrix.h:89
bool empty() const
Definition matrix.h:92
The exception types the port throws.
Dense matrix and non-owning view.
T num_multinomial(const std::vector< int > &m)
Multinomial coefficient sum(m)!
T enorm(const Matrix< T > &A)
matlab/src/util/enorm.m: Frobenius norm of a matrix.
std::vector< T > sum_rows(const Matrix< T > &Z, std::size_t R)
Sum the rows of a think-time matrix into a length-R vector, the sum(Z,1) that every AMVA entry point ...
std::vector< int > oner(const std::vector< int > &N, std::size_t r)
matlab/src/util/oner.m: decrement position r of N, with r given 1-based and r == 0 meaning "leave N a...
SchedStrategy
The three scheduling disciplines the AMVA and Schmidt recursions branch on.
bool next_composition(std::vector< int > &n)
void first_composition(std::vector< int > &n, int c)
matlab/src/util/multichoose.m and sprod.m, as an in-place odometer: the compositions of c into R non-...
double enorm_diff(const Matrix< T > &A, const Matrix< T > &B)
Frobenius norm of the difference of two equally shaped matrices, AS A DOUBLE.
T num_factorial(unsigned n)
Factorial as a value of T.
Definition number.h:184
Number-type abstraction for the templated API port.
Population-vector enumeration and combinatorics.