5#ifndef LINE_API_PFQN_CFTP_H
6#define LINE_API_PFQN_CFTP_H
90inline int cftp_split_index(
const std::vector<double>& logL,
const Matrix<double>& logfac,
91 std::size_t i, std::size_t j,
int k,
double Lambda) {
92 std::vector<double> lw(
static_cast<std::size_t
>(k) + 1);
93 double m = -std::numeric_limits<double>::infinity();
94 for (
int s = 0; s <= k; ++s) {
95 const double val = s * logL[i] - logfac(i,
static_cast<std::size_t
>(s)) +
96 (k - s) * logL[j] - logfac(j,
static_cast<std::size_t
>(k - s));
97 lw[
static_cast<std::size_t
>(s)] = val;
101 for (
int s = 0; s <= k; ++s) {
102 lw[
static_cast<std::size_t
>(s)] = std::exp(lw[
static_cast<std::size_t
>(s)] - m);
103 tot += lw[
static_cast<std::size_t
>(s)];
106 for (
int s = 0; s <= k; ++s) {
107 c += lw[
static_cast<std::size_t
>(s)] / tot;
108 if (Lambda <= c)
return s;
114inline void cftp_monotone_update(std::vector<int>& x,
double u,
const std::vector<double>& logL,
116 const double lam = 1.0 + u *
static_cast<double>(M - 1);
117 std::size_t j =
static_cast<std::size_t
>(std::floor(lam));
118 if (j > M - 1) j = M - 1;
120 const double Lambda = lam -
static_cast<double>(j);
121 const std::size_t a = j - 1, b = j;
122 const int k = x[a] + x[b];
123 const int l = cftp_split_index(logL, logfac, a, b, k, Lambda);
147 "pfqn_cftp requires transcendental arithmetic: it draws random states and forms "
148 "the station balance functions in the log domain");
150 const std::size_t M = L.size();
151 if (M < 2)
throw InputError(
"pfqn_cftp: at least two stations are required");
152 if (N < 0)
throw InputError(
"pfqn_cftp: negative population");
153 if (nsamples == 0)
throw InputError(
"pfqn_cftp: at least one sample is required");
155 for (std::size_t i = 0; i < M; ++i)
156 if (!(L[i] > zero))
throw InputError(
"pfqn_cftp: all demands L must be strictly positive");
157 std::vector<int> Sv(M, 1);
159 if (S.size() != M)
throw InputError(
"pfqn_cftp: S has the wrong station count");
161 for (std::size_t i = 0; i < M; ++i)
163 throw InputError(
"pfqn_cftp: server counts must be positive or cftp_inf_servers");
169 std::vector<double> logL(M);
170 for (std::size_t i = 0; i < M; ++i) {
173 for (
int m = 1; m <= K; ++m) {
175 ?
static_cast<double>(m)
176 :
static_cast<double>(m < Sv[i] ? m : Sv[i]);
177 acc += std::log(cap);
178 logfac(i,
static_cast<std::size_t
>(m)) = acc;
184 res.
horizon.assign(nsamples, 0);
185 res.
Q.assign(M, zero);
187 std::vector<double> tape;
188 std::vector<int> xU(M), xL(M), x(M);
189 for (std::size_t smp = 0; smp < nsamples; ++smp) {
196 while (
static_cast<long>(tape.size()) < Tback) tape.push_back(
mc_uniform01(
rng));
201 for (
long t = Tback; t >= 1; --t) {
202 const double u = tape[
static_cast<std::size_t
>(t - 1)];
203 detail::cftp_monotone_update(xU, u, logL, logfac, M);
204 detail::cftp_monotone_update(xL, u, logL, logfac, M);
214 const double eps = 1e-2;
215 const long steps =
static_cast<long>(std::ceil(
216 static_cast<double>(M * (M - 1)) / 2.0 *
217 std::log(
static_cast<double>(K > 0 ? K : 1) / eps)));
221 for (
long t = 0; t < steps; ++t) {
225 const int k = x[i] + x[j];
226 const int l = detail::cftp_split_index(logL, logfac, i, j, k,
mc_uniform01(
rng));
231 for (std::size_t i = 0; i < M; ++i) {
232 res.
X(smp, i) = x[i];
236 for (std::size_t i = 0; i < M; ++i)
The exception types the port throws.
Dense matrix and non-owning view.
std::mt19937_64 McRng
The generator type every Monte Carlo entry point in this tree accepts.
std::uint64_t mc_uniform_int(McRng &g, std::uint64_t n)
Uniform integer on [0, n), unbiased by rejection.
double mc_uniform01(McRng &g)
Uniform deviate on [0,1) with 53 significant bits, as a double.
constexpr int cftp_inf_servers
Sentinel for an infinite-server (delay) station, the reference's S = Inf.
CftpMethod
Which sampler to run.
@ Cftp
exact, monotone coupling from the past
@ Approx
the rapidly-mixing approximate sampler M_A
CftpResult< T > pfqn_cftp(const std::vector< T > &L, int N, const std::vector< int > &S, std::size_t nsamples, CftpMethod method, McRng &rng)
Perfect stationary state sampling for closed single-class multiserver product-form networks,...
Number-type abstraction for the templated API port.
Randomness scaffolding shared by the Monte Carlo normalizing-constant estimators (pfqn_mci,...
Return value of pfqn_cftp, mirroring [Q, X, T].
Matrix< int > X
(nsamples x M) sampled states, rows sum to K
std::vector< T > Q
(M) empirical mean queue length
std::vector< long > horizon
(nsamples) coalescence horizon or step count