180 double tol = std::numeric_limits<double>::epsilon(),
181 const std::string& method =
"auto",
182 double maxloss = std::numeric_limits<double>::infinity()) {
184 "pfqn_explicit_ld requires transcendental arithmetic: the alternating sums are "
185 "carried as signed log-sum-exps so that no intermediate can overflow. Use "
186 "pfqn_gld for the same constant in exact arithmetic");
191 const double dinf = std::numeric_limits<double>::infinity();
192 const std::size_t R = N.size();
199 if (method !=
"auto" && method !=
"distinct" && method !=
"repeated")
201 "pfqn_explicit_ld: unrecognized method, use 'auto', 'distinct' (Eq. 15) or 'repeated' "
204 for (
int v : N) Nsum += v;
205 if (Nsum < 0)
return res;
211 if (L.
rows() == 0 || L.
cols() == 0)
return res;
212 if (
static_cast<std::size_t
>(L.
cols()) != R)
213 throw InputError(
"pfqn_explicit_ld: the demand matrix must have one column per class of N");
214 const std::size_t M =
static_cast<std::size_t
>(L.
rows());
215 for (std::size_t i = 0; i < M; ++i)
216 for (std::size_t r = 0; r < R; ++r)
218 throw InputError(
"pfqn_explicit_ld: the demand matrix must be nonnegative");
219 const std::size_t Nt =
static_cast<std::size_t
>(Nsum);
222 std::vector<std::vector<T> > alpha(M, std::vector<T>(Nt, one));
223 if (mu.
rows() != 0 && mu.
cols() != 0) {
224 if (
static_cast<std::size_t
>(mu.
rows()) != M)
226 "pfqn_explicit_ld: the load-dependent rate matrix must have one row per station "
228 if (
static_cast<std::size_t
>(mu.
cols()) < Nt)
230 "pfqn_explicit_ld: the load-dependent rate matrix must have at least sum(N) "
232 for (std::size_t i = 0; i < M; ++i)
233 for (std::size_t k = 0; k < Nt; ++k) {
234 alpha[i][k] = mu(i, k);
235 if (!(alpha[i][k] > zero))
237 "pfqn_explicit_ld: the load-dependent rates must be strictly positive");
244 std::vector<std::size_t> s(M, Nt);
245 std::vector<T> alphaS(M, one);
246 for (std::size_t i = 0; i < M; ++i) {
247 const T tail = alpha[i][Nt - 1];
249 for (std::size_t n = Nt; n > 1; --n) {
250 const T d = T(alpha[i][n - 2] - tail);
256 alphaS[i] = alpha[i][s[i] - 1];
260 std::vector<std::vector<T> > lcum(M), lbr(M);
261 std::vector<std::vector<double> > sbr(M);
262 std::vector<int> vcap(M, 0);
263 for (std::size_t i = 0; i < M; ++i) {
264 lcum[i].resize(s[i]);
267 for (std::size_t v = 0; v < s[i]; ++v) {
268 lcum[i][v] = (v == 0) ? zero : T(lcum[i][v - 1] + log(alpha[i][v - 1]));
269 const T br = T(one - (((v == 0) ? zero : alpha[i][v - 1]) / alphaS[i]));
273 }
else if (br > zero) {
277 lbr[i][v] = log(T(zero - br));
282 vcap[i] =
static_cast<int>(std::min<std::size_t>(s[i] - 1, Nt));
290 const auto induced = [&](
const std::vector<int>& t) {
291 std::vector<T> th(M, zero);
292 for (std::size_t i = 0; i < M; ++i)
293 for (std::size_t r = 0; r < R; ++r)
297 const auto scaled = [&](
const std::vector<int>& t) {
298 std::vector<T> th = induced(t);
299 for (std::size_t i = 0; i < M; ++i) th[i] = T(th[i] / alphaS[i]);
302 const auto redundant_at = [&](std::vector<T> th) {
303 std::sort(th.begin(), th.end(), [](
const T& a,
const T& b) { return a < b; });
304 const T scale = th.back();
307 if (!(scale > zero))
return false;
309 for (std::size_t i = 1; i < th.size(); ++i)
310 if (!(T(th[i] - th[i - 1]) > gap))
return true;
313 bool isRedundant =
false;
317 std::vector<T> th(M);
318 for (std::size_t i = 0; i < M; ++i) th[i] = T(L(i, 0) / alphaS[i]);
319 isRedundant = redundant_at(th);
321 std::vector<int> t(R, 0);
324 for (
int val : t) ts += val;
325 if (ts > 0 && redundant_at(scaled(t))) {
330 while (r > 0 && t[r - 1] == N[r - 1]) t[--r] = 0;
335 std::string expr = method;
336 if (expr ==
"auto") {
337 expr = isRedundant ?
"repeated" :
"distinct";
338 }
else if (expr ==
"distinct" && isRedundant) {
340 "pfqn_explicit_ld: Eq. (15) requires pairwise distinct scaled demands, but two of them "
341 "agree to within tol. Use 'auto' or 'repeated'");
345 detail::SignedLse<T> total;
348 std::vector<T> th(M);
349 for (std::size_t i = 0; i < M; ++i) th[i] = L(i, 0);
350 total = detail::explicit_hlld(th, M, Nsum, alphaS, vcap, lcum, lbr, sbr, expr, tol);
353 std::vector<T> lterm;
354 std::vector<double> sterm;
355 double innerLoss = 0.0;
356 std::vector<int> t(R, 0);
359 for (
int val : t) ts += val;
361 std::vector<T> th = induced(t);
363 for (
const T& val : th)
364 if (val > thmax) thmax = val;
366 const detail::SignedLse<T> h = detail::explicit_hlld(
367 th, M, Nsum, alphaS, vcap, lcum, lbr, sbr, expr, tol);
368 innerLoss = std::max(innerLoss, h.lossDigits);
371 for (std::size_t r = 0; r < R; ++r) {
377 sterm.push_back(
static_cast<double>(h.sgn) *
378 (((Nsum - ts) % 2 == 0) ? 1.0 : -1.0));
383 while (r > 0 && t[r - 1] == N[r - 1]) t[--r] = 0;
387 total = detail::explicit_signed_logsumexp(lterm, sterm);
388 total.lossDigits = std::max(total.lossDigits, innerLoss);
395 if (std::isfinite(maxloss) && (total.sgn < 0 || total.lossDigits > maxloss)) {
401 if (total.sgn == 0) {