LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Loading...
Searching...
No Matches
pfqn_oi_fnc.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_PFQN_OI_FNC_H
6#define LINE_API_PFQN_PFQN_OI_FNC_H
7
8/**
9 * @file
10 * @ingroup api_pfqn
11 * Order-independent (OI) functional server: the balance function Psi and the
12 * rate mu_f of an auxiliary station whose insertion turns the mean of a
13 * queue-dependent function into a ratio of normalizing constants.
14 *
15 * Templated port of matlab/src/api/pfqn/pfqn_oi_fnc.m, the OI generalization
16 * of pfqn_fnc (Casale, QEST 2006, Theorem 3 and Corollary 1). Psi is defined
17 * by the lattice convolution identity
18 *
19 * (Psi * Phi)(n) = (1 + f(n)) Phi(n), (Psi * Phi)(n) = sum_{0<=k<=n} Psi(k) Phi(n-k)
20 *
21 * deconvolved triangularly in column-major order, and then inverted to the
22 * rate by balanced fairness,
23 *
24 * mu_f(n) = ( sum_{r: n_r > 0} Psi(n - e_r) ) / Psi(n).
25 *
26 * With that station in the model, E[f(n)] = G+/G - 1, with no probabilities
27 * and no Little's law. For f(n) = sum(n) this is the exact total mean queue
28 * length of the station.
29 *
30 * SIGNED BALANCE. Psi and mu_f may be negative or otherwise non-physical, as
31 * the reference notes; this is immaterial because only the normalizing-constant
32 * ratio is used. A state with Psi(n) = 0 has no defined rate and returns
33 * infinity, exactly as in MATLAB.
34 *
35 * ARITHMETIC. Deconvolution and division only, no transcendental function
36 * anywhere, so the routine is EXACT in rational arithmetic and is deliberately
37 * left ungated. That matters here more than usual: the deconvolution is a
38 * triangular solve with alternating signs, which is exactly the pattern where
39 * floating point loses digits, and the exact instantiation is a real check on
40 * a double one.
41 */
42
43#include <cmath>
44#include <limits>
45#include <cstddef>
46#include <functional>
47#include <vector>
48
50#include "line/num/number.h"
51#include "line/util/error.h"
52
53namespace line {
54namespace pfqn {
55
56/** Return value of pfqn_oi_fnc, mirroring [muf, Psi, mu] flattened. */
57template <class T>
59 std::vector<T> Psi; ///< column-major over the lattice
60 std::vector<T> mu; ///< column-major over the lattice
61 std::vector<std::size_t> stride; ///< column-major strides, for indexing
62};
63
64/**
65 * @brief Order-independent (OI) functional server: the balance function Psi
66 * and the rate mu_f of an auxiliary station whose insertion turns the
67 * mean of a queue-dependent function into a ratio of normalizing
68 * constants.
69 *
70 * @param Phi balance function of the existing OI station, column-major over
71 * the lattice 0 <= n <= N (length prod(N+1))
72 * @param N (R) closed population vector
73 * @param f target queue-dependent function with f(0) = 0; empty for sum(n)
74 */
75template <class T>
76OiFncResult<T> pfqn_oi_fnc(const std::vector<T>& Phi, const std::vector<int>& N,
77 const std::function<T(const std::vector<int>&)>& f) {
78 const std::size_t R = N.size();
79 if (R == 0) throw InputError("pfqn_oi_fnc: empty population vector");
80 std::vector<std::size_t> shp(R), stride(R, 1);
81 std::size_t total = 1;
82 for (std::size_t d = 0; d < R; ++d) {
83 if (N[d] < 0) throw InputError("pfqn_oi_fnc: negative population");
84 shp[d] = static_cast<std::size_t>(N[d]) + 1;
85 }
86 for (std::size_t d = 1; d < R; ++d) stride[d] = stride[d - 1] * shp[d - 1];
87 for (std::size_t d = 0; d < R; ++d) total *= shp[d];
88 if (Phi.size() != total) throw InputError("pfqn_oi_fnc: numel(Phi) must equal prod(N+1)");
89
90 const T zero = num_traits<T>::from_int(0), one = num_traits<T>::from_int(1);
91 std::vector<std::vector<int>> subs(total, std::vector<int>(R, 0));
92 for (std::size_t i = 0; i < total; ++i) {
93 std::size_t li = i;
94 for (std::size_t d = 0; d < R; ++d) {
95 subs[i][d] = static_cast<int>(li % shp[d]);
96 li /= shp[d];
97 }
98 }
99
100 // Step 1: deconvolve (Psi * Phi)(n) = (1 + f(n)) Phi(n).
101 std::vector<T> Psi(total, zero);
102 for (std::size_t i = 0; i < total; ++i) {
103 const std::vector<int>& n = subs[i];
104 const T fv = f ? f(n) : [&]() {
105 T s = zero;
106 for (int v : n) s += num_traits<T>::from_int(v);
107 return s;
108 }();
109 T acc = T(T(one + fv) * Phi[i]);
110 for (std::size_t j = 0; j < i; ++j) {
111 const std::vector<int>& k = subs[j];
112 bool le = true;
113 for (std::size_t d = 0; d < R && le; ++d)
114 if (k[d] > n[d]) le = false;
115 if (!le) continue;
116 std::size_t idx = 0;
117 for (std::size_t d = 0; d < R; ++d)
118 idx += static_cast<std::size_t>(n[d] - k[d]) * stride[d];
119 acc -= Psi[j] * Phi[idx];
120 }
121 Psi[i] = acc;
122 }
123
124 // Step 2: balanced-fairness inversion to the rate.
125 const T inf = detail::num_inf_marker<T>();
126 std::vector<T> mu(total, inf);
127 for (std::size_t i = 0; i < total; ++i) {
128 const std::vector<int>& n = subs[i];
129 int tot = 0;
130 for (int v : n) tot += v;
131 if (tot == 0) {
132 mu[i] = zero;
133 continue;
134 }
135 if (Psi[i] == zero) continue; // non-physical / undefined rate
136 T num = zero;
137 for (std::size_t r = 0; r < R; ++r)
138 if (n[r] > 0) num += Psi[i - stride[r]];
139 mu[i] = T(num / Psi[i]);
140 }
141
142 OiFncResult<T> res;
143 res.Psi = Psi;
144 res.mu = mu;
145 res.stride = stride;
146 return res;
147}
148
149template <class T>
150OiFncResult<T> pfqn_oi_fnc(const std::vector<T>& Phi, const std::vector<int>& N) {
151 return pfqn_oi_fnc(Phi, N, std::function<T(const std::vector<int>&)>());
152}
153
154} // namespace pfqn
155} // namespace line
156
157#endif // LINE_API_PFQN_PFQN_OI_FNC_H
InputError(const std::string &what)
Definition error.h:39
The exception types the port throws.
OiFncResult< T > pfqn_oi_fnc(const std::vector< T > &Phi, const std::vector< int > &N, const std::function< T(const std::vector< int > &)> &f)
Order-independent (OI) functional server: the balance function Psi and the rate mu_f of an auxiliary ...
Definition pfqn_oi_fnc.h:76
Number-type abstraction for the templated API port.
Shared scalar machinery for the integration / asymptotic members of the pfqn family (pfqn_le,...
Return value of pfqn_oi_fnc, mirroring [muf, Psi, mu] flattened.
Definition pfqn_oi_fnc.h:58
std::vector< std::size_t > stride
column-major strides, for indexing
Definition pfqn_oi_fnc.h:61
std::vector< T > Psi
column-major over the lattice
Definition pfqn_oi_fnc.h:59
std::vector< T > mu
column-major over the lattice
Definition pfqn_oi_fnc.h:60