LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Loading...
Searching...
No Matches
lossn_asy.h
Go to the documentation of this file.
1#ifndef LINE_API_LOSSN_LOSSN_ASY_H
2#define LINE_API_LOSSN_LOSSN_ASY_H
3
4/**
5 * @file lossn_asy.h
6 * @ingroup api_lossn
7 * @brief Large-capacity (Kelly limit) approximation of loss networks.
8 *
9 * Templated port of matlab/src/api/lossn/lossn_asy.m.
10 *
11 * Scales offered loads nu and capacities C together, nu -> N nu and C -> N C,
12 * and returns the limit of the class loss probabilities as N grows (Kelly
13 * 1986; Ross 1995, Theorem 5.13). In this limit link j blocks independently
14 * with probability a_j, and the a_j solve the complementary slackness
15 * conditions
16 *
17 * sum_k A(j,k) nu_k prod_i (1-a_i)^A(i,k) = C_j if a_j > 0,
18 * <= C_j if a_j = 0,
19 *
20 * the route loss being 1 - prod_j (1-a_j)^A(j,k). With y_j = -log(1-a_j)
21 * these are the optimality conditions of the strictly convex problem
22 *
23 * minimize sum_k nu_k exp(-sum_j A(j,k) y_j) + sum_j C_j y_j, y >= 0,
24 *
25 * solved here by cyclic coordinate descent, each coordinate by Newton's
26 * method on a concave increasing function. The result needs no summation over
27 * states and costs O(J R) per sweep, so it scales to any capacity; it reports
28 * zero loss on links that are underloaded at the fluid scale, so it is
29 * accurate in heavy (overloaded) traffic and for large C. A and C need not be
30 * integral.
31 *
32 * ARITHMETIC. Tolerance-driven Newton steps on exponentials: gated on
33 * num_traits<T>::has_transcendental.
34 *
35 * References: F. P. Kelly, "Blocking probabilities in large circuit-switched
36 * networks", Adv. Appl. Probab. 18, 1986. F. P. Kelly, "Loss Networks", Ann.
37 * Appl. Probab. 1(3), 1991. K. W. Ross, "Multiservice Loss Models for
38 * Broadband Telecommunication Networks", Springer, 1995, Sec. 5.8.
39 *
40 * @see lossn_erlangfp, lossn_links
41 */
42
43#include <cmath>
44#include <cstddef>
45#include <string>
46#include <vector>
47
48#include "line/num/number.h"
49#include "line/util/error.h"
50#include "line/util/matrix.h"
51
52namespace line {
53namespace lossn {
54
55/** Result of lossn_asy. */
56template <class T>
58 std::vector<T> QLen; ///< mean number of route-r calls in progress, nu_r (1 - Loss_r) (R)
59 std::vector<T> Loss; ///< limiting loss probability of route r (R)
60 std::vector<T> a; ///< limiting blocking probability of link j (J)
61 int niter = 0; ///< number of coordinate-descent sweeps
62};
63
64/**
65 * @brief Fluid (Kelly) limit of a loss network by convex duality.
66 *
67 * @param nu offered load of route (class) r (R)
68 * @param A capacity requirement of link j for route r (J x R, nonnegative)
69 * @param C capacity of link j (J, nonnegative)
70 */
71template <class T>
72LossnAsyResult<T> lossn_asy(const std::vector<T>& nu, const Matrix<T>& A, const std::vector<T>& C) {
74 "lossn_asy requires transcendental arithmetic (Newton steps on exponentials)");
75 using std::abs;
76 using std::exp;
77 using std::expm1;
78 const std::size_t R = nu.size();
79 const std::size_t J = C.size();
80 if (A.rows() != J || A.cols() != R)
81 throw InputError("lossn_asy: A must be " + std::to_string(J) + "x" + std::to_string(R) +
82 " (J x R).");
83 const T zero = num_traits<T>::from_int(0), one = num_traits<T>::from_int(1);
84 for (std::size_t k = 0; k < R; ++k)
85 if (nu[k] < zero) throw InputError("lossn_asy: nu, A and C must be nonnegative.");
86 for (std::size_t j = 0; j < J; ++j) {
87 if (C[j] < zero) throw InputError("lossn_asy: nu, A and C must be nonnegative.");
88 for (std::size_t k = 0; k < R; ++k)
89 if (A(j, k) < zero) throw InputError("lossn_asy: nu, A and C must be nonnegative.");
90 }
91
92 const T tol = num_traits<T>::from_double(1e-12);
93 const T steptol = num_traits<T>::from_double(1e-14);
94 const int maxsweeps = 10000;
95
96 // A link of zero capacity blocks every route through it with certainty;
97 // those routes carry nothing and drop out of the optimization
99 out.a.assign(J, zero);
100 std::vector<char> dead(J, 0), lost(R, 0);
101 for (std::size_t j = 0; j < J; ++j) {
102 if (!(C[j] <= zero)) continue;
103 for (std::size_t k = 0; k < R; ++k)
104 if (A(j, k) > zero) dead[j] = 1;
105 if (dead[j]) {
106 out.a[j] = one;
107 for (std::size_t k = 0; k < R; ++k)
108 if (A(j, k) > zero) lost[k] = 1;
109 }
110 }
111 std::vector<T> nul = nu;
112 for (std::size_t k = 0; k < R; ++k)
113 if (lost[k]) nul[k] = zero;
114
115 std::vector<T> y(J, zero);
116 std::vector<T> s(R, zero); // s_k = sum_j A(j,k) y_j over live links
117 std::vector<std::size_t> users;
118 std::vector<T> Aj, rest, base, e;
119 for (int sweep = 1; sweep <= maxsweeps; ++sweep) {
120 out.niter = sweep;
121 T change = zero;
122 for (std::size_t j = 0; j < J; ++j) {
123 if (dead[j]) continue;
124 users.clear();
125 for (std::size_t k = 0; k < R; ++k)
126 if (A(j, k) > zero && nul[k] > zero) users.push_back(k);
127 if (users.empty()) continue;
128 const std::size_t U = users.size();
129 Aj.assign(U, zero);
130 rest.assign(U, zero);
131 base.assign(U, zero);
132 e.assign(U, zero);
133 T sab = zero;
134 for (std::size_t u = 0; u < U; ++u) {
135 Aj[u] = A(j, users[u]);
136 rest[u] = T(s[users[u]] - Aj[u] * y[j]); // contribution of the other links
137 base[u] = T(nul[users[u]] * exp(T(-rest[u])));
138 sab += T(Aj[u] * base[u]);
139 }
140 // g(t) = C_j - sum_k A_jk base_k exp(-A_jk t) is concave increasing
141 T t = zero;
142 if (!(T(C[j] - sab) >= zero)) {
143 t = y[j];
144 T g0 = zero;
145 for (std::size_t u = 0; u < U; ++u) g0 += T(Aj[u] * base[u] * exp(T(-Aj[u] * t)));
146 if (T(C[j] - g0) > zero) t = zero; // restart left of the root
147 for (int it = 1; it <= 100; ++it) {
148 T g = C[j], dg = zero;
149 for (std::size_t u = 0; u < U; ++u) {
150 e[u] = T(base[u] * exp(T(-Aj[u] * t)));
151 g -= T(Aj[u] * e[u]);
152 dg += T(Aj[u] * Aj[u] * e[u]);
153 }
154 const T step = T(-g / dg);
155 t += step;
156 const T scale = t > one ? t : one;
157 if (abs(step) <= T(steptol * scale)) break;
158 }
159 }
160 const T anew = T(-expm1(T(-t)));
161 const T d = abs(T(anew + expm1(T(-y[j]))));
162 if (d > change) change = d;
163 for (std::size_t u = 0; u < U; ++u) s[users[u]] = T(rest[u] + Aj[u] * t);
164 y[j] = t;
165 }
166 if (change <= tol) break;
167 }
168 for (std::size_t j = 0; j < J; ++j)
169 if (!dead[j]) out.a[j] = T(-expm1(T(-y[j])));
170
171 out.Loss.assign(R, zero);
172 out.QLen.assign(R, zero);
173 for (std::size_t k = 0; k < R; ++k) {
174 if (lost[k]) {
175 out.Loss[k] = one;
176 } else {
177 T ay = zero;
178 for (std::size_t j = 0; j < J; ++j)
179 if (!dead[j]) ay += T(A(j, k) * y[j]);
180 out.Loss[k] = T(-expm1(T(-ay)));
181 }
182 out.QLen[k] = T(nu[k] * T(one - out.Loss[k]));
183 }
184 return out;
185}
186
187} // namespace lossn
188} // namespace line
189
190#endif // LINE_API_LOSSN_LOSSN_ASY_H
InputError(const std::string &what)
Definition error.h:105
std::size_t cols() const
Definition matrix.h:90
std::size_t rows() const
Definition matrix.h:89
The exception types the port throws.
Dense matrix and non-owning view.
LossnAsyResult< T > lossn_asy(const std::vector< T > &nu, const Matrix< T > &A, const std::vector< T > &C)
Fluid (Kelly) limit of a loss network by convex duality.
Definition lossn_asy.h:72
Conservation laws of a layered queueing network, enumerated from its structure.
Definition aoi_dist2ph.h:52
Number-type abstraction for the templated API port.
Result of lossn_asy.
Definition lossn_asy.h:57
std::vector< T > a
limiting blocking probability of link j (J)
Definition lossn_asy.h:60
std::vector< T > QLen
mean number of route-r calls in progress, nu_r (1 - Loss_r) (R)
Definition lossn_asy.h:58
std::vector< T > Loss
limiting loss probability of route r (R)
Definition lossn_asy.h:59
int niter
number of coordinate-descent sweeps
Definition lossn_asy.h:61