5#ifndef LINE_API_PFQN_PFQN_BUSYP_H
6#define LINE_API_PFQN_PFQN_BUSYP_H
60inline double busyp_lse(
const std::vector<double>& v) {
61 double m = -std::numeric_limits<double>::infinity();
62 for (std::size_t i = 0; i < v.size(); ++i)
63 if (v[i] > m) m = v[i];
64 if (!std::isfinite(m))
return m;
66 for (std::size_t i = 0; i < v.size(); ++i) s += std::exp(v[i] - m);
67 return m + std::log(s);
77std::vector<std::vector<double>> busyp_rates(
const Matrix<T>& mu,
78 const std::vector<std::size_t>& idx,
80 std::vector<std::vector<double>> out(idx.size(), std::vector<double>(K, 0.0));
81 const std::size_t cols = mu.
cols();
82 for (std::size_t i = 0; i < idx.size(); ++i)
83 for (std::size_t k = 0; k < K; ++k)
89inline std::vector<std::vector<double>> busyp_rates(
90 const std::function<
double(std::size_t, std::size_t)>& mu,
91 const std::vector<std::size_t>& idx, std::size_t K) {
92 std::vector<std::vector<double>> out(idx.size(), std::vector<double>(K, 0.0));
93 for (std::size_t i = 0; i < idx.size(); ++i)
94 for (std::size_t k = 1; k <= K; ++k) out[i][k - 1] = mu(idx[i], k);
104inline std::vector<double> busyp_lgvec(
const std::vector<double>& alpha,
105 const std::vector<std::vector<double>>& mu,
107 const double neg_inf = -std::numeric_limits<double>::infinity();
108 std::vector<double> lg(K + 1, neg_inf);
110 for (std::size_t i = 0; i < alpha.size(); ++i) {
111 std::vector<double> li(K + 1, 0.0);
113 for (std::size_t m = 1; m <= K; ++m) {
114 acc += std::log(alpha[i]) - std::log(mu[i][m - 1]);
117 std::vector<double> lgnew(K + 1, neg_inf);
118 std::vector<double> terms;
119 for (std::size_t m = 0; m <= K; ++m) {
120 terms.assign(m + 1, neg_inf);
121 for (std::size_t k = 0; k <= m; ++k) terms[k] = lg[m - k] + li[k];
122 lgnew[m] = busyp_lse(terms);
134template <
class RateSource>
135std::size_t busyp_trunc(
const std::vector<double>& alpha,
const RateSource& mu,
136 const std::vector<std::size_t>& subnet, std::size_t nmax,
138 std::size_t K = std::max<std::size_t>(nmax + 8, 16);
139 std::vector<std::vector<double>> rows = busyp_rates(mu, subnet, K);
140 double rho_max = 0.0;
141 for (std::size_t i = 0; i < alpha.size(); ++i)
142 rho_max = std::max(rho_max, alpha[i] / rows[i][K - 1]);
144 throw InputError(
"pfqn_busyp: the subnetwork is not stable, its busy period is infinite");
146 const std::vector<double> lg = busyp_lgvec(alpha, busyp_rates(mu, subnet, K), K);
149 double r = std::exp(lg[K] - lg[K - 1]);
150 if (!(r < 1)) r = rho_max;
151 const double ltail = lg[K] + std::log(r) - std::log1p(-r);
152 std::vector<double> partial(lg.begin() +
static_cast<std::ptrdiff_t
>(nmax), lg.end());
153 if (ltail - busyp_lse(partial) < std::log(tol))
return K;
157 "pfqn_busyp: the open busy period sum did not converge, the subnetwork "
158 "is nearly saturated");
166 std::vector<double>
b;
167 std::vector<double>
lG;
168 std::vector<double>
lH;
185template <
class T,
class RateSource>
188 const std::vector<std::size_t>& subnet,
189 const std::vector<std::size_t>& n,
190 const std::vector<double>& gamma = {},
192 const std::size_t J = alpha.size();
193 const bool is_closed = std::isfinite(N);
195 std::vector<std::size_t> target = subnet;
196 std::sort(target.begin(), target.end());
197 target.erase(std::unique(target.begin(), target.end()), target.end());
198 if (target.empty())
throw InputError(
"pfqn_busyp: the subnetwork must be non-empty");
199 if (is_closed && target.size() >= J)
202 "pfqn_busyp: in a closed network the subnetwork must be a proper subset of "
204 if (target.back() >= J)
205 throw InputError(
"pfqn_busyp: the subnetwork indexes are out of range");
207 std::vector<bool> in_subnet(J,
false);
208 for (std::size_t i = 0; i < target.size(); ++i) in_subnet[target[i]] =
true;
209 std::vector<std::size_t> compl_nodes;
210 for (std::size_t j = 0; j < J; ++j)
211 if (!in_subnet[j]) compl_nodes.push_back(j);
213 std::size_t nmax = 0;
214 for (std::size_t t = 0; t < n.size(); ++t) {
216 throw InputError(
"pfqn_busyp: the busy period order must be a positive integer");
217 if (is_closed &&
static_cast<double>(n[t]) > N)
218 throw InputError(
"pfqn_busyp: the busy period order must be an integer in 1..N");
219 nmax = std::max(nmax, n[t]);
221 if (!is_closed && gamma.empty())
222 throw InputError(
"pfqn_busyp: an open network requires the external arrival rates gamma");
228 for (std::size_t i = 0; i < compl_nodes.size(); ++i)
229 for (std::size_t j = 0; j < target.size(); ++j)
230 inflow += alpha[compl_nodes[i]] *
231 num_traits<T>::to_double(P(compl_nodes[i], target[j]));
233 for (std::size_t j = 0; j < target.size(); ++j) inflow += gamma[target[j]];
236 "pfqn_busyp: no job ever enters the subnetwork, its busy period is undefined");
238 std::vector<double> alpha_sub, alpha_compl;
239 for (std::size_t i = 0; i < target.size(); ++i) alpha_sub.push_back(alpha[target[i]]);
240 for (std::size_t i = 0; i < compl_nodes.size(); ++i)
241 alpha_compl.push_back(alpha[compl_nodes[i]]);
243 BusyPeriodResult out;
244 out.b.assign(n.size(), 0.0);
246 const std::size_t pop =
static_cast<std::size_t
>(std::llround(N));
247 out.lG = detail::busyp_lgvec(alpha_sub, detail::busyp_rates(mu, target, pop), pop);
248 out.lH = detail::busyp_lgvec(alpha_compl,
249 detail::busyp_rates(mu, compl_nodes, pop), pop);
250 for (std::size_t t = 0; t < n.size(); ++t) {
252 std::vector<double> terms;
253 for (std::size_t m = n[t]; m <= pop; ++m)
254 terms.push_back(out.lG[m] + out.lH[pop - m]);
255 out.b[t] = std::exp(detail::busyp_lse(terms) - out.lG[n[t] - 1] -
256 out.lH[pop - n[t]] - std::log(inflow));
259 const std::size_t K = detail::busyp_trunc(alpha_sub, mu, target, nmax, tol);
260 out.lG = detail::busyp_lgvec(alpha_sub, detail::busyp_rates(mu, target, K), K);
261 for (std::size_t t = 0; t < n.size(); ++t) {
263 std::vector<double> terms;
264 for (std::size_t m = n[t]; m <= K; ++m) terms.push_back(out.lG[m]);
265 out.b[t] = std::exp(detail::busyp_lse(terms) - out.lG[n[t] - 1] -
NumericError(const std::string &what)
The exception types the port throws.
Dense matrix and non-owning view.
BusyPeriodResult pfqn_busyp(const std::vector< double > &alpha, const RateSource &mu, const Matrix< T > &P, double N, const std::vector< std::size_t > &subnet, const std::vector< std::size_t > &n, const std::vector< double > &gamma={}, double tol=PFQN_BUSYP_DEFAULT_TOL)
Mean busy period of order n for the subnetwork.
constexpr double PFQN_BUSYP_DEFAULT_TOL
Default relative tolerance of the open-network tail truncation.
Number-type abstraction for the templated API port.
What pfqn_busyp returns: the durations and the two constant sequences.
std::vector< double > lH
log constants of the complement, empty when open
std::vector< double > lG
log normalizing constants of the subnetwork
std::vector< double > b
mean duration per requested order