LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Loading...
Searching...
No Matches
lossn_revsens.h
Go to the documentation of this file.
1#ifndef LINE_API_LOSSN_LOSSN_REVSENS_H
2#define LINE_API_LOSSN_LOSSN_REVSENS_H
3
4/**
5 * @file lossn_revsens.h
6 * @ingroup api_lossn
7 * @brief Revenue sensitivities and implied costs of a loss network.
8 *
9 * Templated port of matlab/src/api/lossn/lossn_revsens.m.
10 *
11 * A route-r call in progress earns revenue at rate w_r, so the long-run
12 * revenue rate is W = sum_r w_r E[n_r]. Under the product form the
13 * sensitivity of W to the offered load of route k is (Kelly 1988; Ross 1995,
14 * Sec. 6.3)
15 *
16 * dW/dnu_k = (1 - B_k) (w_k - c_k), c_k = W(C) - W(C - A e_k),
17 *
18 * where the implied cost c_k is the revenue lost by permanently removing the
19 * units one route-k call holds. A positive dW_k means that admitting more
20 * route-k traffic raises the revenue; a negative value means route k displaces
21 * more valuable traffic than it earns. The link implied cost
22 * clink(j,r) = W(C) - W(C - A(j,r) e_j) is the revenue lost by removing the
23 * units of one route-r call from link j alone.
24 *
25 * Methods:
26 * - 'exact' (also 'manjunath', 'ms'): implied costs by one exact solution per
27 * route and per distinct (link, requirement) pair, needs integral A and C.
28 * - 'rec': as exact with MDD-rec, also for fractional A and C.
29 * - 'erlangfp': Kelly's approximation from the Erlang fixed point. The link
30 * implied costs solve the linear system
31 * c(j,k) = sum_l eta(j,l) [B_jl(C_j - A_jk) - B_jl(C_j)]
32 * (w_l - sum_{i ~= j} c(i,l))
33 * with eta(j,l) the load of route l reduced by the other links and
34 * B_jl(.) the Kaufman-Roberts blocking of link j, and
35 * c_k = sum_j c(j,k), dW_k = prod_j (1 - E(j,k)) (w_k - c_k). It reproduces
36 * Ross Table 5.1.
37 * - 'mci': the score-function estimator dW/dnu_l = Cov(n_l, r(n))/nu_l of
38 * lossn_mci, with confidence intervals in ci.
39 *
40 * ARITHMETIC. The fixed point and the Monte Carlo estimator are
41 * transcendental, so the header is gated on num_traits<T>::has_transcendental.
42 *
43 * References: F. P. Kelly, "Routing in circuit-switched networks: optimization,
44 * shadow prices and decentralization", Adv. Appl. Probab. 20, 1988. K. W. Ross,
45 * "Multiservice Loss Models for Broadband Telecommunication Networks",
46 * Springer, 1995, Sec. 6.3 and Table 5.1.
47 *
48 * @see lossn_links, lossn_mci, lossn_erlangfp, lossn_kr
49 */
50
51#include <algorithm>
52#include <cmath>
53#include <cstddef>
54#include <cstdint>
55#include <limits>
56#include <string>
57#include <vector>
58
65#include "line/num/number.h"
66#include "line/util/error.h"
67#include "line/util/linalg.h"
68#include "line/util/lu.h"
69#include "line/util/matrix.h"
70
71namespace line {
72namespace lossn {
73
74/** Result of lossn_revsens. */
75template <class T>
77 std::vector<T> dW; ///< (R) dW/dnu_k
78 std::vector<T> c; ///< (R) route implied cost, NaN for a route that never fits
79 T W = num_traits<T>::from_int(0); ///< revenue rate sum_r w_r E[n_r]
80 Matrix<T> clink; ///< (J x R) link implied cost, NaN where A(j,r) > C(j)
81 // Filled only by 'mci' (hasCI):
82 bool hasCI = false;
83 std::vector<T> revsens; ///< (R) the point estimates of dW
84 Matrix<T> revsensCI; ///< (R x 2) interval on dW
85 T level = num_traits<T>::from_int(0); ///< confidence level of the interval
86};
87
88namespace detail {
89
90/** The erlangfp branch: Kelly's implied-cost linear system at the Erlang fixed point. */
91template <class T>
92LossnRevsensResult<T> revsens_erlangfp(const std::vector<T>& nu, const Matrix<T>& A,
93 const std::vector<T>& C, const std::vector<T>& w) {
94 const std::size_t R = nu.size(), J = C.size();
95 const T zero = num_traits<T>::from_int(0), one = num_traits<T>::from_int(1);
96 // Same integral rescaling of each link as lossn_erlangfp, so that the
97 // Kaufman-Roberts recursion counts whole units
98 std::vector<std::vector<long long>> Aint(J, std::vector<long long>(R, 0));
99 std::vector<long long> Cint(J, 0);
100 for (std::size_t j = 0; j < J; ++j) {
101 long long m = 1;
102 for (std::size_t r = 0; r <= R; ++r) {
103 const double x = (r < R) ? num_traits<T>::to_double(A(j, r)) : num_traits<T>::to_double(C[j]);
104 const long long d = erlangfp_rat_den(x);
105 if (d > 0) m = m / erlangfp_gcd(m, d) * d;
106 }
107 for (std::size_t r = 0; r < R; ++r)
108 Aint[j][r] = std::llround(static_cast<double>(m) * num_traits<T>::to_double(A(j, r)));
109 Cint[j] = static_cast<long long>(
110 std::floor(static_cast<double>(m) * num_traits<T>::to_double(C[j]) + 1e-9));
111 }
112 const ErlangFpResult<T> fp = lossn_erlangfp(nu, A, C);
113 const Matrix<T>& E = fp.E;
114 LossnRevsensResult<T> out;
115 for (std::size_t r = 0; r < R; ++r) out.W += T(w[r] * fp.QLen[r]);
116
117 // Reduced load of route l on link j: thinned by every other link it uses
118 Matrix<T> eta(J, R, zero);
119 for (std::size_t j = 0; j < J; ++j)
120 for (std::size_t l = 0; l < R; ++l)
121 if (Aint[j][l] > 0) {
122 T t = nu[l];
123 for (std::size_t i = 0; i < J; ++i)
124 if (i != j && Aint[i][l] > 0) t = T(t * T(one - E(i, l)));
125 eta(j, l) = t;
126 }
127
128 // Unknowns c(j,k) for every pair with Aint(j,k) > 0, in column-major order
129 std::vector<std::size_t> pj, pk;
130 std::vector<std::vector<long>> idx(J, std::vector<long>(R, -1));
131 for (std::size_t k = 0; k < R; ++k)
132 for (std::size_t j = 0; j < J; ++j)
133 if (Aint[j][k] > 0) {
134 idx[j][k] = static_cast<long>(pj.size());
135 pj.push_back(j);
136 pk.push_back(k);
137 }
138 const std::size_t P = pj.size();
139 Matrix<T> M = eye<T>(P);
140 std::vector<T> rhs(P, zero);
141 for (std::size_t j = 0; j < J; ++j) {
142 std::vector<T> etaj(R), bj(R);
143 std::vector<long long> levels;
144 for (std::size_t l = 0; l < R; ++l) {
145 etaj[l] = eta(j, l);
146 bj[l] = num_traits<T>::from_int(static_cast<long>(Aint[j][l]));
147 if (Aint[j][l] > 0) levels.push_back(Aint[j][l]);
148 }
149 if (levels.empty()) continue;
150 std::sort(levels.begin(), levels.end());
151 levels.erase(std::unique(levels.begin(), levels.end()), levels.end());
152 const std::vector<T> Q0 = lossn_kr(etaj, bj, static_cast<long>(Cint[j])).B;
153 for (const long long a : levels) {
154 const std::vector<T> Qa =
155 Cint[j] - a < 0 ? std::vector<T>(R, one)
156 : lossn_kr(etaj, bj, static_cast<long>(Cint[j] - a)).B;
157 for (std::size_t k = 0; k < R; ++k) {
158 if (Aint[j][k] != a) continue;
159 const std::size_t p = static_cast<std::size_t>(idx[j][k]);
160 for (std::size_t l = 0; l < R; ++l) {
161 if (Aint[j][l] <= 0) continue;
162 const T coef = T(T(Qa[l] - Q0[l]) * eta(j, l)); // extra blocking of route l
163 rhs[p] += T(coef * w[l]);
164 for (std::size_t i = 0; i < J; ++i)
165 if (i != j && Aint[i][l] > 0) {
166 const std::size_t q = static_cast<std::size_t>(idx[i][l]);
167 M(p, q) = T(M(p, q) + coef);
168 }
169 }
170 }
171 }
172 }
173 const std::vector<T> cvec = P > 0 ? solve(M, rhs) : std::vector<T>();
174 out.clink = Matrix<T>(J, R, zero);
175 for (std::size_t p = 0; p < P; ++p) out.clink(pj[p], pk[p]) = cvec[p];
176 out.c.assign(R, zero);
177 out.dW.assign(R, zero);
178 for (std::size_t k = 0; k < R; ++k) {
179 T pass = one;
180 for (std::size_t j = 0; j < J; ++j) {
181 out.c[k] += out.clink(j, k);
182 pass = T(pass * T(one - E(j, k)));
183 }
184 out.dW[k] = T(pass * T(w[k] - out.c[k]));
185 }
186 return out;
187}
188
189} // namespace detail
190
191/**
192 * @brief Revenue sensitivities and implied costs of a loss network.
193 *
194 * @param nu offered load of route r (R)
195 * @param A capacity requirement of link j for route r (J x R)
196 * @param C capacity of link j (J)
197 * @param w revenue rate of a route-r call in progress (R)
198 * @param method 'exact' (default), 'rec', 'erlangfp' or 'mci'
199 * @param mciopt options for 'mci' (samples, alpha, gamma); its revenue field is
200 * overwritten with w
201 * @param seed seed of the 'mci' generator
202 */
203template <class T>
204LossnRevsensResult<T> lossn_revsens(const std::vector<T>& nu, const Matrix<T>& A,
205 const std::vector<T>& C, const std::vector<T>& w,
206 const std::string& method = "exact",
207 const LossnMciOptions<T>& mciopt = LossnMciOptions<T>(),
208 std::uint64_t seed = 0) {
210 "lossn_revsens requires transcendental arithmetic");
211 const std::size_t R = nu.size(), J = C.size();
212 if (A.rows() != J || A.cols() != R)
213 throw InputError("lossn_revsens: A must be " + std::to_string(J) + "x" + std::to_string(R) +
214 " (J x R).");
215 if (w.size() != R)
216 throw InputError("lossn_revsens: w must have " + std::to_string(R) + " entries.");
217 const T zero = num_traits<T>::from_int(0), one = num_traits<T>::from_int(1);
218 const T nan = num_traits<T>::from_double(std::numeric_limits<double>::quiet_NaN());
219 const std::string m = detail::lossn_lower(method.empty() ? std::string("exact") : method);
220
221 if (m == "exact" || m == "manjunath" || m == "ms" || m == "rec") {
222 const bool rec = (m == "rec");
223 auto qlen = [&](const std::vector<T>& Cv) {
224 return rec ? lossn_rec(nu, A, Cv).QLen : lossn_manjunath(nu, A, Cv).QLen;
225 };
226 auto revenue = [&](const std::vector<T>& Q) {
227 T s = zero;
228 for (std::size_t r = 0; r < R; ++r) s += T(w[r] * Q[r]);
229 return s;
230 };
231 std::vector<T> QLen, Loss;
232 if (rec) {
233 const LossnRecResult<T> r0 = lossn_rec(nu, A, C);
234 QLen = r0.QLen;
235 Loss = r0.Loss;
236 } else {
237 const LossnManjunathResult<T> r0 = lossn_manjunath(nu, A, C);
238 QLen = r0.QLen;
239 Loss = r0.Loss;
240 }
242 out.W = revenue(QLen);
243 out.c.assign(R, nan);
244 out.dW.assign(R, zero);
245 for (std::size_t k = 0; k < R; ++k) {
246 std::vector<T> Ck(J);
247 bool fits = true, allzero = true;
248 for (std::size_t j = 0; j < J; ++j) {
249 Ck[j] = T(C[j] - A(j, k));
250 if (Ck[j] < zero) fits = false;
251 if (A(j, k) != zero) allzero = false;
252 }
253 if (!fits) continue; // never admitted: B_k = 1 and dW_k = 0
254 out.c[k] = allzero ? zero : T(out.W - revenue(qlen(Ck)));
255 out.dW[k] = T(T(one - Loss[k]) * T(w[k] - out.c[k]));
256 }
257 out.clink = Matrix<T>(J, R, zero);
258 for (std::size_t j = 0; j < J; ++j)
259 for (const T& a : detail::lossn_row_levels(A, j)) {
260 T val = nan;
261 if (!(T(C[j] - a) < zero)) {
262 std::vector<T> Cs = C;
263 Cs[j] = T(C[j] - a);
264 val = T(out.W - revenue(qlen(Cs)));
265 }
266 for (std::size_t r = 0; r < R; ++r)
267 if (A(j, r) == a) out.clink(j, r) = val;
268 }
269 return out;
270 }
271 if (m == "erlangfp") return detail::revsens_erlangfp(nu, A, C, w);
272 if (m == "mci") {
273 LossnMciOptions<T> o = mciopt;
274 o.revenue = w;
275 const LossnMciResult<T> cim = lossn_mci(nu, A, C, o, seed);
277 out.W = cim.revenue;
278 out.dW = cim.revsens;
279 out.c = cim.impliedCost;
280 out.clink = cim.linkImpliedCost;
281 out.hasCI = true;
282 out.revsens = cim.revsens;
283 out.revsensCI = cim.revsensCI;
284 out.level = cim.level;
285 return out;
286 }
287 throw InputError("lossn_revsens: Unknown method '" + method +
288 "'; use exact, rec, erlangfp or mci.");
289}
290
291} // namespace lossn
292} // namespace line
293
294#endif // LINE_API_LOSSN_LOSSN_REVSENS_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 linear algebra over the templated number type: products, identity, inverse, and powers.
Erlang fixed-point (reduced-load) approximation for a loss network.
The multirate single-link loss system (stochastic knapsack): exact blocking by the Kaufman-Roberts re...
Exact analysis of a loss network by the Manjunath-Sikdar transform.
Monte Carlo importance-sampling summation for product-form loss networks.
Exact analysis of a loss network by MDD-rec.
LU factorization with partial pivoting, templated on the number type.
Dense matrix and non-owning view.
LossnManjunathResult< T > lossn_manjunath(const std::vector< T > &nu, const Matrix< T > &A, const std::vector< T > &C, const LossnManjunathOptions &options=LossnManjunathOptions())
Exact normalizing constant, carried load and blocking of a loss network.
KrResult< T > lossn_kr(const std::vector< T > &rho, const std::vector< T > &b, const T &C)
Exact occupancy distribution and per-class blocking of a stochastic knapsack.
Definition lossn_kr.h:147
ErlangFpResult< T > lossn_erlangfp(const std::vector< T > &nu, const Matrix< T > &A, const std::vector< T > &C, const da::FpiOptions &options=lossn_erlangfp_options())
Erlang fixed-point (reduced-load) approximation for a loss network.
LossnMciResult< T > lossn_mci(const std::vector< T > &nu, const Matrix< T > &A, const std::vector< T > &C, const LossnMciOptions< T > &opt, Rng &gen)
Estimate the normalizing constant and the blocking probabilities.
Definition lossn_mci.h:184
LossnRecResult< T > lossn_rec(const std::vector< T > &nu, const Matrix< T > &A, const std::vector< T > &C)
Exact loss-network analysis by MDD-rec.
Definition lossn_rec.h:141
LossnRevsensResult< T > lossn_revsens(const std::vector< T > &nu, const Matrix< T > &A, const std::vector< T > &C, const std::vector< T > &w, const std::string &method="exact", const LossnMciOptions< T > &mciopt=LossnMciOptions< T >(), std::uint64_t seed=0)
Revenue sensitivities and implied costs of a loss network.
Conservation laws of a layered queueing network, enumerated from its structure.
Definition aoi_dist2ph.h:52
std::vector< T > solve(const Matrix< T > &A, const std::vector< T > &b)
Convenience: solve Ax = b, leaving A and b untouched.
Definition lu.h:158
Matrix< T > eye(std::size_t n)
Identity of order n.
Definition linalg.h:28
Number-type abstraction for the templated API port.
Result of lossn_manjunath.
std::vector< T > QLen
mean carried load E[n_r] per route
std::vector< T > Loss
blocking probability per route
Options of lossn_mci, MATLAB's options struct.
Definition lossn_mci.h:77
std::vector< T > revenue
revenue rate w per route, empty = no revenue sensitivity
Definition lossn_mci.h:81
Result of lossn_mci.
Definition lossn_mci.h:86
Matrix< T > revsensCI
(R x 2) delta-method interval of revsens
Definition lossn_mci.h:103
T revenue
revenue rate W = sum_k w_k E[X_k]
Definition lossn_mci.h:101
std::vector< T > revsens
dW/dnu_l (R)
Definition lossn_mci.h:102
Matrix< T > linkImpliedCost
(J x R) link implied cost W(C) - W(C - A(j,r) e_j)
Definition lossn_mci.h:105
std::vector< T > impliedCost
route implied cost W(C) - W(C - A e_k), NaN if undefined (R)
Definition lossn_mci.h:104
Carried load, blocking, log normalising constant and walk count.
Definition lossn_rec.h:68
std::vector< T > Loss
Blocking probability per class.
Definition lossn_rec.h:72
std::vector< T > QLen
Mean number of class-r calls in progress, the carried load.
Definition lossn_rec.h:70
Result of lossn_revsens.
std::vector< T > dW
(R) dW/dnu_k
T level
confidence level of the interval
Matrix< T > revsensCI
(R x 2) interval on dW
std::vector< T > c
(R) route implied cost, NaN for a route that never fits
T W
revenue rate sum_r w_r E[n_r]
std::vector< T > revsens
(R) the point estimates of dW
Matrix< T > clink
(J x R) link implied cost, NaN where A(j,r) > C(j)