5#ifndef LINE_API_LOSSN_LOSSN_KR_H
6#define LINE_API_LOSSN_LOSSN_KR_H
83void kr_check_rho_b(
const char* fn,
const std::vector<T>& rho,
const std::vector<T>& b) {
84 if (b.size() != rho.size())
85 throw InputError(std::string(fn) +
": b has " + std::to_string(b.size()) +
86 " entries but rho has " + std::to_string(rho.size()) +
".");
88 for (
const T& r : rho)
89 if (r < zero)
throw InputError(std::string(fn) +
": rho must be nonnegative.");
94std::vector<long> kr_integral_b(
const char* fn,
const std::vector<T>& b) {
95 std::vector<long> out(b.size(), 0);
96 for (std::size_t k = 0; k < b.size(); ++k) {
98 if (!(x >= 0.0) || std::fabs(x - std::round(x)) > 1e-9)
99 throw InputError(std::string(fn) +
": b must contain nonnegative integers.");
100 out[k] =
static_cast<long>(std::llround(x));
107T kr_cap1(
const T& x) {
108 const T one = num_traits<T>::from_int(1);
109 return x > one ? one : x;
117T kr_normal_hazard(
const T& x) {
121 if (x >= num_traits<T>::from_int(-35)) {
122 const T two = num_traits<T>::from_int(2);
123 const T pi = num_traits<T>::from_double(3.14159265358979323846);
124 return T(exp(T(-(x * x) / two)) / sqrt(T(two * pi)) /
125 T(num_traits<T>::from_double(0.5) * erfc(T(-x / sqrt(two)))));
127 const T one = num_traits<T>::from_int(1);
128 const T x2 = T(x * x), x4 = T(x2 * x2), x6 = T(x4 * x2);
129 return T(-x / T(one - one / x2 + num_traits<T>::from_int(3) / x4 -
130 num_traits<T>::from_int(15) / x6));
149 const char* fn =
"lossn_kr";
150 detail::kr_check_rho_b(fn, rho, b);
151 const std::vector<long> bi = detail::kr_integral_b(fn, b);
153 if (!(cd >= 0.0) || std::fabs(cd - std::round(cd)) > 1e-9)
154 throw InputError(std::string(fn) +
": C must be a nonnegative integer.");
155 const long Ci =
static_cast<long>(std::llround(cd));
156 const std::size_t K = rho.size();
160 std::vector<T>& q = out.
q;
161 q.assign(
static_cast<std::size_t
>(Ci + 1), zero);
163 for (
long n = 1; n <= Ci; ++n) {
165 for (std::size_t k = 0; k < K; ++k)
166 if (bi[k] > 0 && n - bi[k] >= 0)
168 q[
static_cast<std::size_t
>(n - bi[k])]);
172 const T scale = q[
static_cast<std::size_t
>(n)];
173 for (
long m = 0; m <= n; ++m)
174 q[
static_cast<std::size_t
>(m)] = T(q[
static_cast<std::size_t
>(m)] / scale);
178 for (
const T& v : q) tot += v;
179 for (T& v : q) v = T(v / tot);
181 out.
B.assign(K, zero);
182 out.
QLen.assign(K, zero);
183 for (std::size_t k = 0; k < K; ++k) {
186 for (
long n = std::max<long>(0, Ci - bi[k] + 1); n <= Ci; ++n)
187 acc += q[
static_cast<std::size_t
>(n)];
188 out.
B[k] = detail::kr_cap1(acc);
190 out.
QLen[k] = T(rho[k] * T(one - out.
B[k]));
220 const std::vector<T>& b,
221 const std::vector<T>& Btarget) {
222 const char* fn =
"lossn_kr_dim";
223 detail::kr_check_rho_b(fn, rho, b);
224 const std::vector<long> bi = detail::kr_integral_b(fn, b);
225 const std::size_t K = rho.size();
226 if (Btarget.size() != K)
227 throw InputError(std::string(fn) +
": Btarget has " + std::to_string(Btarget.size()) +
228 " entries but rho has " + std::to_string(K) +
".");
230 for (
const T& t : Btarget)
231 if (!(t > zero) || t > one)
throw InputError(std::string(fn) +
": Btarget must lie in (0,1].");
233 const long Cmax = 100000000L;
234 std::vector<T> q(1024, zero);
240 std::vector<T> B(K, zero);
242 for (std::size_t k = 0; k < K; ++k) {
245 for (
long n = std::max<long>(0, C - bi[k] + 1); n <= C; ++n)
246 acc += q[
static_cast<std::size_t
>(n)];
247 B[k] = detail::kr_cap1(T(acc / total));
249 if (B[k] > Btarget[k]) ok =
false;
258 std::string(fn) +
": No capacity up to " + std::to_string(Cmax) +
259 " meets the blocking targets.");
261 if (
static_cast<std::size_t
>(C + 1) > q.size()) q.resize(2 * q.size(), zero);
263 for (std::size_t k = 0; k < K; ++k)
264 if (bi[k] > 0 && C - bi[k] >= 0)
266 q[
static_cast<std::size_t
>(C - bi[k])]);
268 total += q[
static_cast<std::size_t
>(C)];
271 const T scale = q[
static_cast<std::size_t
>(C)];
272 for (
long m = 0; m <= C; ++m)
273 q[
static_cast<std::size_t
>(m)] = T(q[
static_cast<std::size_t
>(m)] / scale);
274 total = T(total / scale);
282 const std::vector<T>& b,
const T& Btarget) {
283 return lossn_kr_dim(rho, b, std::vector<T>(rho.size(), Btarget));
311 const std::vector<T>& b,
const T& C) {
313 "lossn_kr_asy evaluates the normal density and distribution function");
315 const char* fn =
"lossn_kr_asy";
316 const std::size_t K = rho.size();
318 throw InputError(std::string(fn) +
": b has " + std::to_string(b.size()) +
319 " entries but rho has " + std::to_string(K) +
".");
321 for (std::size_t k = 0; k < K; ++k)
322 if (rho[k] < zero || b[k] < zero)
323 throw InputError(std::string(fn) +
": rho and b must be nonnegative.");
324 if (!(C > zero))
throw InputError(std::string(fn) +
": C must be a positive scalar.");
326 T sbr = zero, sb2r = zero;
327 for (std::size_t k = 0; k < K; ++k) {
328 sbr += T(b[k] * rho[k]);
329 sb2r += T(b[k] * b[k] * rho[k]);
331 const T sqC = sqrt(C);
333 out.
alpha = T(T(C - sbr) / sqC);
334 out.
sigma = sqrt(T(sb2r / C));
335 out.
B.assign(K, zero);
336 if (out.
sigma == zero) {
342 for (std::size_t k = 0; k < K; ++k) out.
B[k] = detail::kr_cap1(T(b[k] * out.
delta / sqC));
NumericError(const std::string &what)
The exception types the port throws.
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.
KrDimResult< T > lossn_kr_dim(const std::vector< T > &rho, const std::vector< T > &b, const std::vector< T > &Btarget)
Smallest link capacity meeting per-class blocking targets.
KrAsyResult< T > lossn_kr_asy(const std::vector< T > &rho, const std::vector< T > &b, const T &C)
Normal approximation of multirate single-link blocking for large C.
Conservation laws of a layered queueing network, enumerated from its structure.
Number-type abstraction for the templated API port.
The <kind> part of a LINE:<category>:<kind> identifier (see _kb/17-error-vocabulary....
T sigma
normalized occupancy standard deviation
std::vector< T > B
approximate blocking probability of class k
T delta
blocking per unit of call size, times sqrt(C)
T alpha
normalized spare capacity (C - sum b rho)/sqrt(C)
std::vector< T > B
blocking probabilities at that capacity
long C
smallest capacity with B_k(C) <= Btarget_k for all k
std::vector< T > q
occupancy distribution, q[n] = P(n units busy), n = 0..C
std::vector< T > B
blocking probability of class k
std::vector< T > QLen
mean number of class-k calls in progress, rho_k (1 - B_k)