LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Loading...
Searching...
No Matches
sn_gd_balance.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_SN_SN_GD_BALANCE_H
6#define LINE_API_SN_SN_GD_BALANCE_H
7
8/**
9 * @file
10 * @ingroup api_sn
11 * Whittle balance check for a globally state-dependent rate scaling.
12 *
13 * For every state n of the lattice 0..cutoffs and every pair of stations (s,t)
14 * populated in n, the balance property requires
15 *
16 * phi_s(n) phi_t(n - e_s) = phi_t(n) phi_s(n - e_t).
17 *
18 * When it holds, the chain is reversible with pi(n) ~ Phi(n) prod rho^n for the
19 * balance function Phi implied by phi, and the stationary law is insensitive to
20 * the service-time distribution beyond its mean. When it fails the model is
21 * still solvable by SolverCTMC, but it has no product form and IS sensitive --
22 * which is exactly the distinction this routine exists to make checkable, since
23 * nothing in a `set_global_dependence` declaration announces it.
24 *
25 * Twin of matlab/src/api/sn/sn_gd_balance.m, python
26 * line_solver.api.sn.sn_gd_balance and jline.api.sn.SnGdBalance.
27 *
28 * Reference: P. Whittle, "Partial balance and insensitivity", J. Appl. Prob.
29 * 22(1), 1985; T. Bonald, A. Proutiere, "Insensitivity in processor-sharing
30 * networks", Perf. Eval. 49, 2002.
31 */
32
33#include <cmath>
34#include <cstddef>
35#include <functional>
36#include <vector>
37
38#include "line/util/error.h"
39#include "line/num/number.h"
40
41namespace line {
42namespace sn {
43
44/**
45 * Worst relative violation of the balance property over the given lattice.
46 *
47 * @param phi scaling evaluated on an (nstations) population vector,
48 * returning one scalar (broadcast) or one entry per station
49 * @param cutoffs per-station lattice bound, one entry per station
50 * @return the worst relative violation; 0 to rounding when phi is balanced
51 */
52template <class T>
53T sn_gd_balance(const std::function<std::vector<T>(const std::vector<T>&)>& phi,
54 const std::vector<std::size_t>& cutoffs) {
55 if (!phi) throw InputError("sn_gd_balance: phi must be callable");
56 const std::size_t S = cutoffs.size();
57 if (S < 2)
58 throw InputError(
59 "sn_gd_balance: cutoffs must have one entry per station (at least two stations are "
60 "needed for a balance pair)");
61
62 std::size_t total = 1;
63 for (std::size_t s = 0; s < S; ++s) total *= cutoffs[s] + 1;
64
65 const T zero = num_traits<T>::from_int(0);
66 T viol = zero;
67 std::vector<T> n(S, zero);
68 const std::function<std::vector<T>(const std::vector<T>&)> ev =
69 [&phi, S](const std::vector<T>& x) {
70 std::vector<T> v = phi(x);
71 if (v.size() == 1) return std::vector<T>(S, v[0]);
72 if (v.size() != S)
73 throw InputError("sn_gd_balance: phi must return a scalar or one entry per station");
74 return v;
75 };
76
77 for (std::size_t idx = 0; idx < total; ++idx) {
78 std::size_t rem = idx;
79 for (std::size_t s = 0; s < S; ++s) {
80 n[s] = num_traits<T>::from_int(static_cast<long>(rem % (cutoffs[s] + 1)));
81 rem /= cutoffs[s] + 1;
82 }
83 for (std::size_t s = 0; s < S; ++s) {
84 if (num_traits<T>::to_double(n[s]) == 0) continue;
85 for (std::size_t t = s + 1; t < S; ++t) {
86 if (num_traits<T>::to_double(n[t]) == 0) continue;
87 const std::vector<T> xn = ev(n);
88 std::vector<T> m = n;
89 m[s] = T(m[s] - num_traits<T>::from_int(1));
90 const std::vector<T> xs = ev(m);
91 m = n;
92 m[t] = T(m[t] - num_traits<T>::from_int(1));
93 const std::vector<T> xt = ev(m);
94 const double lhs = num_traits<T>::to_double(T(xn[s] * xs[t]));
95 const double rhs = num_traits<T>::to_double(T(xn[t] * xt[s]));
96 const double scale = std::max(std::fabs(lhs), std::fabs(rhs));
97 if (scale > 0) {
98 const double v = std::fabs(lhs - rhs) / scale;
100 }
101 }
102 }
103 }
104 return viol;
105}
106
107} // namespace sn
108} // namespace line
109
110#endif // LINE_API_SN_SN_GD_BALANCE_H
InputError(const std::string &what)
Definition error.h:39
The exception types the port throws.
T sn_gd_balance(const std::function< std::vector< T >(const std::vector< T > &)> &phi, const std::vector< std::size_t > &cutoffs)
Worst relative violation of the balance property over the given lattice.
Number-type abstraction for the templated API port.