LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Toggle main menu visibility
Loading...
Searching...
No Matches
fj_char_max.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_CHAR_MAX_H
6
#define LINE_API_FJ_CHAR_MAX_H
7
8
/**
9
* @file
10
* @ingroup api_fj
11
* Gravey's characteristic maximum M_K, an upper bound on the expected maximum
12
* of K i.i.d. random variables.
13
*
14
* Templated port of matlab/src/api/fj/fj_char_max.m, cross-checked against
15
* FJ_char_max.fj_char_max_exp and fj_char_max_erlang in
16
* jar/src/main/java/jline/api/fj/FJ_char_max.java (identical; the JAR splits
17
* the MATLAB dist_type switch into two entry points, which this port mirrors
18
* as two overloads).
19
*
20
* m_K is the greatest lower bound with P(X > m_K) <= 1/K, and
21
* M_K = m_K + K int_{m_K}^inf P(X > x) dx.
22
*
23
* exponential: m_K = ln(K)/mu, M_K = H_K/mu (the bound is tight here)
24
* Erlang-k: m_K solves exp(-mu m) sum_{i<k} (mu m)^i/i! = 1/K,
25
* M_K = (k/mu)[1 + K exp(-mu m_K) (mu m_K)^k / k!]
26
*
27
* static_assert(num_traits<T>::has_transcendental) -- a log in the
28
* exponential case and a bracketed root of a transcendental equation in the
29
* Erlang one.
30
*
31
* The MATLAB 'general' mode, which takes an arbitrary survival function
32
* handle, locates mK by a doubling bracket and a root search, and integrates
33
* the tail by quadrature after a doubling search for the truncation point, is
34
* ported as fj_char_max_general(K, S_X) below (a distinct name, since a callable
35
* argument would be ambiguous with the exponential overload). The reference's
36
* doubling searches have no termination guarantee on a heavy tail; the port
37
* caps the bracket search and refuses, where the reference would loop.
38
*/
39
40
#include "
line/api/fj/fj_harmonic.h
"
41
#include "
line/api/fj/fj_types.h
"
42
#include "
line/api/fj/fj_xmax_erlang.h
"
43
#include "
line/num/number.h
"
44
#include "
line/util/error.h
"
45
46
namespace
line
{
47
namespace
fj
{
48
49
/**
50
* Exponential branch.
51
*
52
* @param K number of samples, K >= 1
53
* @param mu rate, > 0
54
* @return [MK, mK]
55
*/
56
template
<
class
T>
57
FJCharMaxResult<T>
fj_char_max
(
unsigned
K,
const
T& mu) {
58
static_assert
(
num_traits<T>::has_transcendental
,
59
"fj_char_max requires transcendental arithmetic"
);
60
detail::require_positive_K(K,
"fj_char_max"
);
61
if
(mu <=
num_traits<T>::from_int
(0))
throw
InputError
(
"fj_char_max: the rate mu must be positive"
);
62
const
T Kt =
num_traits<T>::from_int
(
static_cast<
long
>
(K));
63
return
{T(
fj_harmonic<T>
(K) / mu), T(detail::num_log(Kt) / mu)};
64
}
65
66
/**
67
* Erlang-k branch.
68
*
69
* @param K number of samples, K >= 1
70
* @param k Erlang stages, k >= 1
71
* @param mu per-stage rate, > 0
72
* @return [MK, mK]
73
*/
74
template
<
class
T>
75
FJCharMaxResult<T>
fj_char_max
(
unsigned
K,
unsigned
k,
const
T& mu) {
76
static_assert
(
num_traits<T>::has_transcendental
,
77
"fj_char_max requires transcendental arithmetic"
);
78
detail::require_positive_K(K,
"fj_char_max"
);
79
if
(k < 1)
throw
InputError
(
"fj_char_max: the Erlang stage count k must be positive"
);
80
if
(mu <=
num_traits<T>::from_int
(0))
throw
InputError
(
"fj_char_max: the rate mu must be positive"
);
81
82
const
T one =
num_traits<T>::from_int
(1);
83
const
T Kt =
num_traits<T>::from_int
(
static_cast<
long
>
(K));
84
const
T kt =
num_traits<T>::from_int
(
static_cast<
long
>
(k));
85
const
T target = one / Kt;
86
87
T hi = kt / mu + detail::num_log(Kt) / mu;
88
if
(hi <=
num_traits<T>::from_int
(0)) hi = kt / mu;
89
while
(detail::erlang_survival(hi, k, mu) > target) hi *=
num_traits<T>::from_int
(2);
90
const
T mK = detail::bisect<T>(
91
[&](
const
T& x) {
return
T(detail::erlang_survival(x, k, mu) - target); },
92
num_traits<T>::from_int
(0), hi,
"fj_char_max"
);
93
94
const
T MK = (kt / mu) * (one + Kt * detail::num_exp(T(-mu * mK)) *
num_pow_int
(T(mu * mK), k) /
95
num_factorial<T>
(k));
96
return
{MK, mK};
97
}
98
99
/**
100
* General branch, the reference's dist_type 'general': the survival function
101
* S_X(x) = P(X > x) of the parallel tasks, as a callable.
102
*
103
* mK solves S_X(mK) = 1/K on [0, u] with u doubled from 1 until S_X(u) <= 1/K
104
* (fzero, here bisection as in the other branches), and
105
* MK = mK + K * int_{mK}^{upper} S_X(x) dx with upper doubled from 1 until
106
* S_X(upper) <= 1e-10 (at most 100 doublings, as in the reference), the
107
* integral by the tree's composite Simpson rule.
108
*
109
* @param K number of samples, K >= 1
110
* @param S_X survival function, S_X(0) = 1, nonincreasing
111
* @return [MK, mK]
112
*/
113
template
<
class
T,
class
F>
114
FJCharMaxResult<T>
fj_char_max_general
(
unsigned
K,
const
F& S_X) {
115
static_assert
(
num_traits<T>::has_transcendental
,
116
"fj_char_max requires transcendental arithmetic"
);
117
detail::require_positive_K(K,
"fj_char_max"
);
118
const
T zero =
num_traits<T>::from_int
(0), one =
num_traits<T>::from_int
(1);
119
const
T two =
num_traits<T>::from_int
(2);
120
const
T Kt =
num_traits<T>::from_int
(
static_cast<
long
>
(K));
121
const
T target = one / Kt;
122
123
// find_mK_general: upper doubled until S_X(upper) <= 1/K
124
T upper = one;
125
unsigned
guard = 0;
126
while
(S_X(upper) > target) {
127
upper *= two;
128
if
(++guard > 1000)
129
throw
NumericError
(
ErrorKind
{
"NonConvergence"
},
"fj_char_max: the survival function does not fall below 1/K"
);
130
}
131
const
T mK = detail::bisect<T>([&](
const
T& x) {
return
T(S_X(x) - target); }, zero, upper,
132
"fj_char_max"
);
133
134
// find_upper_limit(S_X, 1e-10): at most 100 doublings from 1
135
T ulim = one;
136
const
T tail_target =
num_traits<T>::from_double
(1e-10);
137
for
(
unsigned
it = 0; it < 100; ++it) {
138
if
(S_X(ulim) <= tail_target)
break
;
139
ulim *= two;
140
}
141
T tail_integral = zero;
142
if
(ulim > mK) tail_integral = detail::simpson<T>([&](
const
T& x) {
return
T(S_X(x)); }, mK, ulim);
143
const
T MK = T(mK + Kt * tail_integral);
144
return
{MK, mK};
145
}
146
147
}
// namespace fj
148
}
// namespace line
149
150
#endif
// LINE_API_FJ_CHAR_MAX_H
line::InputError::InputError
InputError(const std::string &what)
Definition
error.h:105
line::NumericError::NumericError
NumericError(const std::string &what)
Definition
error.h:112
error.h
The exception types the port throws.
fj_harmonic.h
Harmonic number H_K = sum_{k=1..K} 1/k.
fj_types.h
Shared return types and arithmetic helpers for the templated fork-join port.
fj_xmax_erlang.h
Expected maximum of K i.i.d.
line::fj
Definition
fj_amva.h:34
line::fj::fj_char_max
FJCharMaxResult< T > fj_char_max(unsigned K, const T &mu)
Exponential branch.
Definition
fj_char_max.h:57
line::fj::fj_char_max_general
FJCharMaxResult< T > fj_char_max_general(unsigned K, const F &S_X)
General branch, the reference's dist_type 'general': the survival function S_X(x) = P(X > x) of the p...
Definition
fj_char_max.h:114
line::fj::fj_harmonic
T fj_harmonic(unsigned K)
Harmonic number H_K = sum_{k=1..K} 1/k.
Definition
fj_harmonic.h:37
line
Conservation laws of a layered queueing network, enumerated from its structure.
Definition
aoi_dist2ph.h:52
line::num_factorial
T num_factorial(unsigned n)
Factorial as a value of T.
Definition
number.h:196
line::num_pow_int
T num_pow_int(const T &base, unsigned e)
Integer power, valid in any field (no transcendental requirement).
Definition
number.h:204
number.h
Number-type abstraction for the templated API port.
line::ErrorKind
The <kind> part of a LINE:<category>:<kind> identifier (see _kb/17-error-vocabulary....
Definition
error.h:37
line::fj::FJCharMaxResult
[MK, mK] of fj_char_max: characteristic maximum and its threshold.
Definition
fj_types.h:55
line::num_traits
Definition
number.h:123
include
line
api
fj
fj_char_max.h
Generated by
1.18.0