LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Loading...
Searching...
No Matches
fj_xmax_hyperexp.h
Go to the documentation of this file.
1/*
2 * Copyright (c) 2012-2026, QORE Lab, Imperial College London
3 * All rights reserved.
4 */
5#ifndef LINE_API_FJ_XMAX_HYPEREXP_H
6#define LINE_API_FJ_XMAX_HYPEREXP_H
7
8/**
9 * @file
10 * @ingroup api_fj
11 * Expected maximum of K i.i.d. two-phase hyperexponential service times.
12 *
13 * Templated port of matlab/src/api/fj/fj_xmax_hyperexp.m.
14 *
15 * X_K^max = sum_{n=1..K} (-1)^{n+1} sum_{m=0..n}
16 * C(n,m) p1^m p2^{n-m} / (m mu1 + (n-m) mu2)
17 *
18 * from the inclusion-exclusion expansion of 1 - F(x)^K. Every term is
19 * rational, so the sum is exact in the field -- which is the point, because
20 * it alternates: the terms grow like 2^K while the result stays O(log K/mu),
21 * so in double the answer is destroyed by cancellation somewhere around
22 * K = 25 and is pure noise by K = 40.
23 *
24 * REFERENCE DEFECT: FJ_xmax.fj_xmax_hyperexp in
25 * jar/src/main/java/jline/api/fj/FJ_xmax.java drops the p1^m p2^{n-m} factor
26 * from the inner sum, so the JAR computes a different quantity and ignores p1
27 * entirely except in its validation. MATLAB is ground truth and is what this
28 * port follows.
29 */
30
32#include "line/num/number.h"
33#include "line/util/error.h"
34
35namespace line {
36namespace fj {
37
38/**
39 * @brief Expected maximum of K i.i.d. two-phase hyperexponential service
40 * times.
41 *
42 * @param K number of branches, K >= 1
43 * @param p1 probability of the first phase, 0 < p1 < 1
44 * @param mu1 rate of the first phase, > 0
45 * @param mu2 rate of the second phase, > 0
46 * @return expected maximum of K hyperexponential samples
47 */
48template <class T>
49T fj_xmax_hyperexp(unsigned K, const T& p1, const T& mu1, const T& mu2) {
50 detail::require_positive_K(K, "fj_xmax_hyperexp");
51 const T zero = num_traits<T>::from_int(0), one = num_traits<T>::from_int(1);
52 if (p1 <= zero || p1 >= one) throw InputError("fj_xmax_hyperexp: p1 must lie in (0,1)");
53 if (mu1 <= zero || mu2 <= zero) throw InputError("fj_xmax_hyperexp: the rates mu1 and mu2 must be positive");
54 const T p2 = one - p1;
55
56 T Xmax = zero;
57 for (unsigned n = 1; n <= K; ++n) {
58 T inner = zero;
59 for (unsigned m = 0; m <= n; ++m) {
60 const T den = num_traits<T>::from_int(static_cast<long>(m)) * mu1 +
61 num_traits<T>::from_int(static_cast<long>(n - m)) * mu2;
62 if (den > zero)
63 inner += detail::fj_binom<T>(n, m) * num_pow_int(p1, m) * num_pow_int(p2, n - m) / den;
64 }
65 if ((n + 1) % 2 == 0) Xmax += inner;
66 else Xmax -= inner;
67 }
68 return Xmax;
69}
70
71} // namespace fj
72} // namespace line
73
74#endif // LINE_API_FJ_XMAX_HYPEREXP_H
InputError(const std::string &what)
Definition error.h:39
The exception types the port throws.
Shared return types and arithmetic helpers for the templated fork-join port.
T fj_xmax_hyperexp(unsigned K, const T &p1, const T &mu1, const T &mu2)
Expected maximum of K i.i.d.
T num_pow_int(const T &base, unsigned e)
Integer power, valid in any field (no transcendental requirement).
Definition number.h:192
Number-type abstraction for the templated API port.