LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Toggle main menu visibility
Loading...
Searching...
No Matches
pfqn_lap.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_PFQN_PFQN_LAP_H
6
#define LINE_API_PFQN_PFQN_LAP_H
7
8
/**
9
* @file
10
* @ingroup api_pfqn
11
* Laplace approximation of the normalizing constant of a repairman
12
* (single-queue, multiclass) model.
13
*
14
* Templated port of matlab/src/api/pfqn/pfqn_lap.m. The McKenna-Mitra
15
* integral for a single queueing station is reduced to a one-dimensional
16
* Laplace integral whose saddle point u0 solves
17
*
18
* f(u) = 1 - sum_r N_r L_r / (Z_r + Ntot L_r u) = 0
19
*
20
* after which
21
*
22
* log I = log Ntot - sum_r factln(N_r) - Ntot u0 + sum_r N_r log(Z_r + L_r u0 Ntot)
23
* + (1/2) log(2 pi) - (1/2) log(sum_r (N_r/Ntot)/(Z_r/(Ntot L_r) + u0)^2)
24
* - (1/2) log Ntot.
25
*
26
* ROOT FINDING. MATLAB calls fzero from the initial guess 1, and falls back to
27
* a 1e-4 grid scan over (0,10] when fzero returns a non-finite root. f is
28
* strictly increasing in u on u > 0 (each term N_r L_r/(Z_r + Ntot L_r u) is
29
* decreasing), so the port brackets the root by doubling from 1 and then
30
* bisects, which lands on the same root fzero converges to but without a
31
* derivative or a Newton step that could leave the domain. The fallback scan
32
* is kept for the case where no sign change exists on any bracket, exactly as
33
* MATLAB's is.
34
*
35
* MATLAB returns NaN when the root is negative. The port throws instead:
36
* a NaN normalizing constant propagates silently through a solver, whereas the
37
* condition it signals (no admissible saddle point) is a modelling error.
38
*
39
* ARITHMETIC. Laplace's method plus logarithms, so gated on
40
* num_traits<T>::has_transcendental.
41
*/
42
43
#include <cmath>
44
#include <cstddef>
45
#include <vector>
46
47
#include "
line/api/pfqn/pfqn_asympt_common.h
"
48
#include "
line/num/number.h
"
49
#include "
line/util/error.h
"
50
51
namespace
line
{
52
namespace
pfqn
{
53
54
/** Assembly of the expansion at the saddle point; defined below. */
55
template
<
class
T>
56
T
finish_lap
(
const
std::vector<T>& L,
const
std::vector<T>& N,
const
std::vector<T>& Z,
57
const
T& Ntot,
const
T& u0);
58
59
/**
60
* @brief Laplace approximation of the normalizing constant of a repairman
61
* (single-queue, multiclass) model.
62
*
63
* @param L (R) per-class demand at the single station
64
* @param N (R) per-class population
65
* @param Z (R) per-class think time
66
* @return log of the approximate normalizing constant
67
*/
68
template
<
class
T>
69
T
pfqn_lap
(
const
std::vector<T>& L,
const
std::vector<T>& N,
const
std::vector<T>& Z) {
70
static_assert
(
num_traits<T>::has_transcendental
,
71
"pfqn_lap requires transcendental arithmetic (Laplace approximation of an integral)"
);
72
using
std::log;
73
const
std::size_t R = L.size();
74
if
(N.size() != R || Z.size() != R)
75
throw
InputError
(
76
"pfqn_lap expects per-class vectors for a single queueing station (repairman models)"
);
77
const
T zero =
num_traits<T>::from_int
(0);
78
T Ntot = zero;
79
for
(
const
T& v : N) Ntot += v;
80
if
(Ntot <= zero)
throw
InputError
(
"pfqn_lap: empty population"
);
81
82
// f(u) = 1 - sum_r N_r L_r / (Z_r + Ntot L_r u), increasing on u > 0.
83
const
auto
f = [&](
const
T& u) {
84
T s =
num_traits<T>::from_int
(1);
85
for
(std::size_t r = 0; r < R; ++r) {
86
const
T den = T(Z[r] + Ntot * L[r] * u);
87
if
(den == zero)
continue
;
88
s -= T(N[r] * L[r] / den);
89
}
90
return
s;
91
};
92
93
T lo =
num_traits<T>::from_double
(1e-12), hi =
num_traits<T>::from_int
(1);
94
bool
bracketed =
false
;
95
if
(f(lo) <= zero) {
96
for
(
int
k = 0; k < 200; ++k) {
97
if
(f(hi) >= zero) {
98
bracketed =
true
;
99
break
;
100
}
101
lo = hi;
102
hi = T(hi *
num_traits<T>::from_int
(2));
103
}
104
}
else
{
105
// f already positive at the left edge: MATLAB's grid scan finds no
106
// sign change either and u0 stays at the left edge.
107
return
finish_lap
(L, N, Z, Ntot, lo);
108
}
109
if
(!bracketed)
throw
NumericError
(
"pfqn_lap: no saddle point on (0, 2^200]"
);
110
for
(
int
it = 0; it < 400; ++it) {
111
const
T mid = T(T(lo + hi) /
num_traits<T>::from_int
(2));
112
if
(f(mid) < zero)
113
lo = mid;
114
else
115
hi = mid;
116
if
(
num_traits<T>::to_double
(
num_abs
(T(hi - lo))) <=
117
1e-16 * (1.0 +
num_traits<T>::to_double
(hi)))
118
break
;
119
}
120
const
T u0 = T(T(lo + hi) /
num_traits<T>::from_int
(2));
121
return
finish_lap
(L, N, Z, Ntot, u0);
122
}
123
124
template
<
class
T>
125
T
finish_lap
(
const
std::vector<T>& L,
const
std::vector<T>& N,
const
std::vector<T>& Z,
126
const
T& Ntot,
const
T& u0) {
127
using
std::log;
128
const
std::size_t R = L.size();
129
const
T zero =
num_traits<T>::from_int
(0);
130
if
(u0 < zero)
throw
NumericError
(
"pfqn_lap: negative saddle point, no admissible expansion"
);
131
const
T twopi =
num_traits<T>::from_double
(6.283185307179586476925286766559);
132
133
T logI = T(log(Ntot));
134
for
(std::size_t r = 0; r < R; ++r) logI -= detail::num_factln<T>(N[r]);
135
logI -= Ntot * u0;
136
for
(std::size_t r = 0; r < R; ++r) logI += N[r] * log(T(Z[r] + L[r] * u0 * Ntot));
137
T f2 = zero;
138
for
(std::size_t r = 0; r < R; ++r) {
139
if
(L[r] == zero)
continue
;
140
const
T d = T(T(Z[r] / T(Ntot * L[r])) + u0);
141
f2 += T(T(N[r] / Ntot) / T(d * d));
142
}
143
if
(f2 <= zero)
throw
NumericError
(
"pfqn_lap: non-positive curvature at the saddle point"
);
144
logI += T(
num_traits<T>::from_rational
(1, 2) * log(twopi));
145
logI -= T(
num_traits<T>::from_rational
(1, 2) * log(f2));
146
logI -= T(
num_traits<T>::from_rational
(1, 2) * log(Ntot));
147
return
logI;
148
}
149
150
}
// namespace pfqn
151
}
// namespace line
152
153
#endif
// LINE_API_PFQN_PFQN_LAP_H
line::InputError::InputError
InputError(const std::string &what)
Definition
error.h:39
line::NumericError::NumericError
NumericError(const std::string &what)
Definition
error.h:45
error.h
The exception types the port throws.
line::pfqn
Definition
cd_peak_scaling.h:43
line::pfqn::pfqn_lap
T pfqn_lap(const std::vector< T > &L, const std::vector< T > &N, const std::vector< T > &Z)
Laplace approximation of the normalizing constant of a repairman (single-queue, multiclass) model.
Definition
pfqn_lap.h:69
line::pfqn::finish_lap
T finish_lap(const std::vector< T > &L, const std::vector< T > &N, const std::vector< T > &Z, const T &Ntot, const T &u0)
Assembly of the expansion at the saddle point; defined below.
Definition
pfqn_lap.h:125
line
Definition
aoi_dist2ph.h:52
line::num_abs
T num_abs(const T &v)
Definition
number.h:172
number.h
Number-type abstraction for the templated API port.
pfqn_asympt_common.h
Shared scalar machinery for the integration / asymptotic members of the pfqn family (pfqn_le,...
line::num_traits
Definition
number.h:111
include
line
api
pfqn
pfqn_lap.h
Generated by
1.18.0