5#ifndef LINE_API_PFQN_PFQN_BK_H
6#define LINE_API_PFQN_PFQN_BK_H
60 std::vector<std::size_t>
A;
61 std::vector<std::size_t>
B;
77std::vector<std::size_t> bk_multiplicity(
const std::vector<std::vector<T>>& L) {
78 const std::size_t M = L.size();
79 std::vector<std::size_t> mult(M, 1);
80 if (M == 0)
return mult;
81 const std::size_t R = L[0].size();
82 const double tol = 1e-8;
83 for (std::size_t i = 0; i < M; ++i) {
84 if (mult[i] > 1)
continue;
85 for (std::size_t j = i + 1; j < M; ++j) {
86 double scale = 1.0, diff = 0.0;
87 for (std::size_t r = 0; r < R; ++r) {
90 scale = std::max(scale, std::max(std::abs(a), std::abs(b)));
91 diff = std::max(diff, std::abs(a - b));
93 if (diff <= tol * scale) {
104T bk_psi(
const std::vector<T>& z,
const std::vector<std::vector<T>>& Lg,
const std::vector<T>& N,
105 const std::vector<T>& Z) {
109 for (std::size_t r = 0; r < z.size(); ++r) f += Z[r] * z[r] - N[r] * log(z[r]);
110 for (std::size_t i = 0; i < Lg.size(); ++i) {
112 for (std::size_t r = 0; r < z.size(); ++r) u += Lg[i][r] * z[r];
119std::vector<T> bk_grad(
const std::vector<T>& z,
const std::vector<std::vector<T>>& Lg,
120 const std::vector<T>& N,
const std::vector<T>& Z) {
121 const T one = num_traits<T>::from_int(1);
122 const std::size_t R = z.size();
124 for (std::size_t r = 0; r < R; ++r) g[r] = Z[r] - N[r] / z[r];
125 for (std::size_t i = 0; i < Lg.size(); ++i) {
126 T u = num_traits<T>::from_int(0);
127 for (std::size_t r = 0; r < R; ++r) u += Lg[i][r] * z[r];
128 const T d = one / (one - u);
129 for (std::size_t r = 0; r < R; ++r) g[r] += d * Lg[i][r];
135Matrix<T> bk_hessian(
const std::vector<T>& z,
const std::vector<std::vector<T>>& Lg,
136 const std::vector<T>& N) {
137 const T one = num_traits<T>::from_int(1), zero = num_traits<T>::from_int(0);
138 const std::size_t R = z.size();
139 Matrix<T> H(R, R, zero);
140 for (std::size_t r = 0; r < R; ++r) H(r, r) = N[r] / (z[r] * z[r]);
141 for (std::size_t i = 0; i < Lg.size(); ++i) {
143 for (std::size_t r = 0; r < R; ++r) u += Lg[i][r] * z[r];
144 const T d = one / (one - u);
146 for (std::size_t r = 0; r < R; ++r)
147 for (std::size_t s = 0; s < R; ++s) H(r, s) += d2 * Lg[i][r] * Lg[i][s];
164 "pfqn_bk requires transcendental arithmetic (saddle point expansion of log G)");
171 const std::size_t M = L.
rows(), R = L.
cols();
174 res.
X.assign(R, zero);
177 for (std::size_t r = 0; r < N.size(); ++r) Ntot += N[r];
178 if (L.
empty() || N.empty() || !(Ntot > zero)) {
179 for (std::size_t r = 0; r < R; ++r) res.
A.push_back(r);
182 if (N.size() != R)
throw InputError(
"pfqn_bk: L and N disagree on the class count");
183 std::vector<T> Zv = Z;
184 if (Zv.empty()) Zv.assign(R, zero);
185 if (Zv.size() != R)
throw InputError(
"pfqn_bk: L and Z disagree on the class count");
188 std::size_t nkeep = 0;
189 for (std::size_t r = 0; r < R; ++r)
190 if (N[r] > zero) ++nkeep;
191 if (nkeep > 0 && nkeep < R) {
193 std::vector<T> Nk, Zk;
194 std::vector<std::size_t> map;
196 for (std::size_t r = 0; r < R; ++r) {
197 if (!(N[r] > zero))
continue;
198 for (std::size_t i = 0; i < M; ++i) Lk(i, c) = L(i, r);
207 for (std::size_t k = 0; k < map.size(); ++k) {
208 res.
X[map[k]] = red.
X[k];
209 for (std::size_t i = 0; i < M; ++i) res.
U(i, map[k]) = red.
U(i, k);
211 for (std::size_t k = 0; k < red.
A.size(); ++k) res.
A.push_back(map[red.
A[k]]);
212 for (std::size_t k = 0; k < red.
B.size(); ++k) res.
B.push_back(map[red.
B[k]]);
217 std::vector<std::vector<T>> Lq;
218 for (std::size_t i = 0; i < M; ++i) {
220 for (std::size_t r = 0; r < R; ++r) s += L(i, r);
221 if (!(s > zero))
continue;
222 std::vector<T> row(R);
223 for (std::size_t r = 0; r < R; ++r) row[r] = L(i, r);
230 const double inf = std::numeric_limits<double>::infinity();
231 std::vector<double> mu(R, inf);
232 std::vector<long> poleRow(R, -1);
233 std::vector<bool> isPole(Lq.size(),
false);
234 std::vector<std::size_t> mult = detail::bk_multiplicity(Lq);
235 bool hasGroup =
false;
236 for (std::size_t i = 0; i < mult.size(); ++i)
237 if (mult[i] > 1) hasGroup =
true;
239 for (std::size_t i = 0; i < Lq.size(); ++i) {
240 if (mult[i] > 1)
continue;
241 std::size_t nz = 0, cnt = 0;
242 for (std::size_t r = 0; r < R; ++r)
243 if (Lq[i][r] > zero) {
247 if (cnt != 1)
continue;
248 if (Zv[nz] > fineTol)
continue;
252 poleRow[nz] =
static_cast<long>(i);
255 for (std::size_t r = 0; r < R; ++r)
256 if (poleRow[r] >= 0) isPole[
static_cast<std::size_t
>(poleRow[r])] =
true;
258 std::vector<std::vector<T>> Lg;
259 for (std::size_t i = 0; i < Lq.size(); ++i)
260 if (!isPole[i]) Lg.push_back(Lq[i]);
263 std::vector<T> muT(R);
264 for (std::size_t r = 0; r < R; ++r)
267 std::vector<bool> onBound(R,
false);
269 for (std::size_t r = 0; r < R; ++r) {
271 for (std::size_t i = 0; i < Lg.size(); ++i) den += Lg[i][r];
272 if (!(den > zero)) den = fineTol;
274 if (!std::isinf(mu[r])) {
276 if (z[r] > cap) z[r] = cap;
279 for (
int it = 0; it < 200 && !Lg.empty(); ++it) {
281 for (std::size_t i = 0; i < Lg.size(); ++i) {
283 for (std::size_t r = 0; r < R; ++r) u += Lg[i][r] * z[r];
284 if (u > umax) umax = u;
287 for (std::size_t r = 0; r < R; ++r) z[r] = z[r] * num_traits<T>::from_double(0.7);
289 for (std::size_t outer = 0; outer <= R; ++outer) {
290 for (std::size_t r = 0; r < R; ++r)
291 if (onBound[r]) z[r] = muT[r];
292 std::vector<std::size_t> freeIdx;
293 for (std::size_t r = 0; r < R; ++r)
294 if (!onBound[r]) freeIdx.push_back(r);
295 if (freeIdx.empty())
break;
296 const std::size_t nf = freeIdx.size();
297 for (
int it = 0; it < 500; ++it) {
298 std::vector<T> g = detail::bk_grad(z, Lg, N, Zv);
300 for (std::size_t i = 0; i < nf; ++i) {
306 Matrix<T> H = detail::bk_hessian(z, Lg, N);
308 std::vector<T> rhs(nf);
309 for (std::size_t i = 0; i < nf; ++i) {
310 for (std::size_t j = 0; j < nf; ++j) Hf(i, j) = H(freeIdx[i], freeIdx[j]);
311 rhs[i] = zero - g[freeIdx[i]];
316 }
catch (
const std::exception&) {
321 std::vector<T> zt(R);
322 while (alpha >= 1e-14) {
324 for (std::size_t i = 0; i < nf; ++i)
327 for (std::size_t i = 0; i < nf && ok; ++i) {
328 const std::size_t r = freeIdx[i];
329 if (!(zt[r] > zero)) ok =
false;
330 if (ok && !std::isinf(mu[r]) && zt[r] > muT[r]) ok =
false;
332 for (std::size_t i = 0; i < Lg.size() && ok; ++i) {
334 for (std::size_t r = 0; r < R; ++r) u += Lg[i][r] * zt[r];
335 if (!(u < one)) ok =
false;
344 std::vector<T> g = detail::bk_grad(z, Lg, N, Zv);
346 for (std::size_t i = 0; i < nf; ++i) {
347 const std::size_t r = freeIdx[i];
348 if (std::isinf(mu[r]))
continue;
357 for (std::size_t r = 0; r < R; ++r)
358 if (onBound[r]) z[r] = muT[r];
361 for (std::size_t r = 0; r < R; ++r) {
362 for (std::size_t i = 0; i < M; ++i) {
363 T u = L(i, r) * z[r];
364 if (onBound[r] && u > one) u = one;
373 const T psi0 = detail::bk_psi(z, Lg, N, Zv);
378 Matrix<T> H = detail::bk_hessian(z, Lg, N);
379 const std::size_t nf = res.
A.size();
381 for (std::size_t i = 0; i < nf; ++i)
382 for (std::size_t j = 0; j < nf; ++j) Haa(i, j) = H(res.
A[i], res.
A[j]);
386 for (std::size_t i = 0; i < nf; ++i) logdet += log(
num_abs(LU(i, i)));
389 for (std::size_t i = 0; i < nf; ++i) {
390 const std::size_t r = res.
A[i];
392 if (!std::isinf(mu[r])) lG -= log(one - z[r] / muT[r]);
406 if (x < 25.0)
return std::exp(x * x) * std::erfc(x);
407 const double y = 1.0 / (2.0 * x * x);
408 double term = 1.0,
sum = 1.0;
409 for (
int k = 1; k <= 12; ++k) {
410 term *= -(2 * k - 1) * y;
413 return sum / (x * std::sqrt(M_PI));
419T bk_h1(
const T& z,
const std::vector<T>& D,
const T& N,
const T& Z) {
422 T f = Z * z - N * log(z);
423 for (std::size_t i = 0; i < D.size(); ++i) f -= log(one - D[i] * z);
428T bk_h1d1(
const T& z,
const std::vector<T>& D,
const T& N,
const T& Z) {
429 const T one = num_traits<T>::from_int(1);
431 for (std::size_t i = 0; i < D.size(); ++i) g += D[i] / (one - D[i] * z);
436T bk_h1d2(
const T& z,
const std::vector<T>& D,
const T& N) {
437 const T one = num_traits<T>::from_int(1);
439 for (std::size_t i = 0; i < D.size(); ++i) {
440 const T d = one - D[i] * z;
441 h += D[i] * D[i] / (d * d);
447T bk_h1d3(
const T& z,
const std::vector<T>& D,
const T& N) {
448 const T one = num_traits<T>::from_int(1), two = num_traits<T>::from_int(2);
449 T h = num_traits<T>::from_int(-2) * N / (z * z * z);
450 for (std::size_t i = 0; i < D.size(); ++i) {
451 const T d = one - D[i] * z;
452 h += two * D[i] * D[i] * D[i] / (d * d * d);
458T bk_saddle1(
const std::vector<T>& D,
const T& N,
const T& Z) {
459 if (D.empty())
return N / Z;
461 for (std::size_t i = 0; i < D.size(); ++i)
462 dmax = std::max(dmax, num_traits<T>::to_double(D[i]));
463 const double hi = 1.0 / dmax;
464 T z = num_traits<T>::from_double(0.5 * hi);
465 for (
int it = 0; it < 200; ++it) {
466 const T g = bk_h1d1(z, D, N, Z);
467 if (std::abs(num_traits<T>::to_double(g)) <=
468 1e-14 * std::max(1.0, num_traits<T>::to_double(N)))
470 const T dz = (num_traits<T>::from_int(0) - g) / bk_h1d2(z, D, N);
473 const double zt = num_traits<T>::to_double(z) + alpha * num_traits<T>::to_double(dz);
474 if (zt > 0 && zt < hi)
break;
476 if (alpha < 1e-14)
break;
478 if (alpha < 1e-14)
break;
479 z += num_traits<T>::from_double(alpha) * dz;
496 "pfqn_bkue requires transcendental arithmetic (uniform expansion of log G)");
503 if (!(N > zero))
return res;
505 for (std::size_t i = 0; i < L.size(); ++i)
506 if (L[i] > zero) Lv.push_back(L[i]);
512 std::size_t ipole = 0;
513 for (std::size_t i = 1; i < Lv.size(); ++i)
514 if (Lv[i] > Lv[ipole]) ipole = i;
516 const double tolL = 1e-8 * std::max(1.0, dmax);
517 std::size_t ties = 0;
518 std::vector<double> sorted;
519 for (std::size_t i = 0; i < Lv.size(); ++i) {
521 if (std::abs(v - dmax) <= tolL) ++ties;
524 std::sort(sorted.begin(), sorted.end());
525 bool hasGroup =
false;
526 for (std::size_t i = 1; i < sorted.size(); ++i)
527 if (sorted[i] - sorted[i - 1] <= tolL) hasGroup =
true;
528 const bool hasPole = (ties == 1) && hasGroup;
532 for (std::size_t i = 0; i < Lv.size(); ++i)
533 if (i != ipole) D.push_back(Lv[i]);
534 zp = one / Lv[ipole];
538 const T z0 = detail::bk_saddle1(D, N, Z);
539 const T h2 = detail::bk_h1d2(z0, D, N);
540 const T h3 = detail::bk_h1d3(z0, D, N);
545 res.
lG = detail::bk_h1(z0, D, N, Z) - log(z0) -
556 res.
lG = detail::bk_h1(z0, D, N, Z) +
559 res.
lG = detail::bk_h1(zp, D, N, Z) +
579 const std::string& method =
"mva",
double tol = 1e-10,
580 int maxiter = 1000) {
582 "pfqn_bklc requires transcendental arithmetic");
584 const std::size_t M = L.
rows(), R = L.
cols();
586 res.
X.assign(R, zero);
591 for (std::size_t r = 0; r < N.size(); ++r) Ntot += N[r];
592 if (L.
empty() || !(Ntot > zero))
return res;
593 std::vector<T> Zv = Z;
594 if (Zv.empty()) Zv.assign(R, zero);
595 const bool ue = (method ==
"ue");
596 if (tol <= 0) tol = 1e-10;
597 if (maxiter <= 0) maxiter = 1000;
600 std::vector<T> X(R, zero);
603 for (std::size_t r = 0; r < R; ++r) {
605 X[r] = (std::isfinite(v) && v >= 0) ? seed.
X[r] : zero;
607 }
catch (
const std::exception&) {
609 for (std::size_t r = 0; r < R; ++r) {
610 T cap = zero,
sum = zero;
611 for (std::size_t i = 0; i < M; ++i) {
612 if (L(i, r) > cap) cap = L(i, r);
615 if (!(X[r] > zero) && N[r] > zero) X[r] = N[r] / (Zv[r] +
sum);
616 if (cap > zero && X[r] > one / cap) X[r] = one / cap;
620 for (res.
it = 1; res.
it <= maxiter; ++res.
it) {
621 std::vector<T> Xold = X;
622 for (std::size_t l = 0; l < R; ++l) {
623 if (!(N[l] > zero)) {
625 for (std::size_t i = 0; i < M; ++i) Q(i, l) = zero;
630 for (std::size_t i = 0; i < M; ++i) {
632 for (std::size_t k = 0; k < R; ++k)
633 if (k != l) busy += L(i, k) * X[k];
640 std::vector<T> Qi(M, zero);
644 for (
int nn = 1; nn <= Nl; ++nn) {
648 for (std::size_t i = 0; i < M; ++i) Qi[i] = D[i] * Xl * (one + Qi[i]);
652 for (std::size_t i = 0; i < M; ++i) Q(i, l) = Qi[i];
655 for (std::size_t i = 0; i < M; ++i) Dm(i, 0) = D[i];
660 for (std::size_t i = 0; i < M; ++i) Q(i, l) =
mva.QN(i, 0);
663 double diff = 0.0, xmax = 1.0;
664 for (std::size_t r = 0; r < R; ++r) {
669 if (diff <= tol * xmax)
break;
671 if (res.
it > maxiter) res.
it = maxiter;
674 for (std::size_t r = 0; r < R; ++r)
675 for (std::size_t i = 0; i < M; ++i) res.
U(i, r) = L(i, r) * X[r];
The exception types the port throws.
LU factorization with partial pivoting, templated on the number type.
Dense matrix and non-owning view.
MvaResult< T > pfqn_mva(const Matrix< T > &L, const std::vector< int > &N, const Matrix< T > &Z, const std::vector< int > &mi)
Exact Mean Value Analysis for closed product-form networks (Reiser and Lavenberg 1980).
BkResult< T > pfqn_bk(const Matrix< T > &L, const std::vector< T > &N, const std::vector< T > &Z)
Birman-Kogan saddle point normalizing constant with bottleneck detection.
BkLcResult< T > pfqn_bklc(const Matrix< T > &L, const std::vector< T > &N, const std::vector< T > &Z, const std::string &method="mva", double tol=1e-10, int maxiter=1000)
Birman-Kogan load concealment algorithm (Algorithm 2).
BkResult< T > pfqn_bkue(const std::vector< T > &L, const T &N, const T &Z)
Birman-Kogan uniform (van der Waerden) expansion for a single chain.
double bk_erfcx(double x)
Scaled complementary error function exp(x^2)*erfc(x) for x >= 0.
std::vector< std::size_t > lu_factor(Matrix< T > &A)
In-place LU of A (n x n).
std::vector< T > solve(const Matrix< T > &A, const std::vector< T > &b)
Convenience: solve Ax = b, leaving A and b untouched.
Number-type abstraction for the templated API port.
Exact Mean Value Analysis for closed product-form networks (Reiser and Lavenberg 1980).
Return value of pfqn_bklc.
Return value of pfqn_bk, mirroring [G, lG, X, U, A, B].
std::vector< std::size_t > A
chains whose dedicated station is not saturated
Matrix< T > U
(M x R) utilizations
std::vector< T > X
the saddle point coordinates
std::vector< std::size_t > B
chains whose dedicated station is a bottleneck