5#ifndef LINE_API_PFQN_RESPT_PS_MOMENTS_H
6#define LINE_API_PFQN_RESPT_PS_MOMENTS_H
101constexpr double RESPT_PS_AUTO_MAX = 4096.0;
103constexpr double RESPT_PS_EXACT_MAX = 65536.0;
111void respt_ps_exact(
const std::vector<T>& p,
const std::vector<T>& q,
const std::vector<long>& K,
112 std::size_t J, T& W, T& W2) {
115 const std::size_t R = K.size();
117 std::vector<std::size_t> dims(R);
119 for (std::size_t j = 0; j < R; ++j) {
120 dims[j] =
static_cast<std::size_t
>(K[j]) + 1;
123 std::vector<std::size_t> stride(R, 1);
124 for (std::size_t j = 1; j < R; ++j) stride[j] = stride[j - 1] * dims[j - 1];
127 std::vector<long> tot(ns, 0);
128 for (std::size_t lin = 0; lin < ns; ++lin) {
129 std::size_t res = lin;
130 for (std::size_t j = 0; j < R; ++j) {
131 states(lin, j) =
static_cast<long>(res % dims[j]);
133 tot[lin] += states(lin, j);
139 for (std::size_t j = 0; j < R; ++j) r[j] = T(p[j] / q[j]);
140 const double ninf = -std::numeric_limits<double>::infinity();
141 std::vector<double> logpi(ns, 0.0);
142 for (std::size_t lin = 0; lin < ns; ++lin) {
145 for (std::size_t j = 0; j < R; ++j) {
146 const long nj = states(lin, j);
159 for (std::size_t lin = 0; lin < ns; ++lin) mx = std::max(mx, logpi[lin]);
160 std::vector<T> pin(ns, zero);
162 for (std::size_t lin = 0; lin < ns; ++lin) {
166 for (std::size_t lin = 0; lin < ns; ++lin) pin[lin] = T(pin[lin] / psum);
169 for (std::size_t lin = 0; lin < ns; ++lin) {
171 for (std::size_t j = 0; j < R; ++j) {
172 const long nj = states(lin, j);
174 A(lin - stride[j], lin) +=
183 A(lin, lin) += diagv;
188 for (std::size_t i = 0; i < ns; ++i)
189 for (std::size_t k = 0; k < ns; ++k) Mt(i, k) = A(k, i);
190 for (std::size_t i = 0; i < ns; ++i) Mt(i, i) -= q[J];
191 std::vector<T> rhs(ns, zero);
192 for (std::size_t lin = 0; lin < ns; ++lin)
194 const std::vector<T> c =
solve(Mt, rhs);
198 for (std::size_t lin = 0; lin < ns; ++lin) {
210void respt_ps_asymptotic(
const std::vector<T>& p,
const std::vector<T>& q,
211 const std::vector<long>& K, std::size_t J, T& W, T& W2, T& c0, T& c1) {
212 const std::size_t R = K.size();
215 std::vector<T> lambda(R);
218 for (std::size_t j = 0; j < R; ++j) alpha -= T(lambda[j] / q[j]);
219 T Nexp = T(q[0] / p[0]);
220 for (std::size_t j = 1; j < R; ++j) {
221 const T v = T(q[j] / p[j]);
222 if (v > Nexp) Nexp = v;
224 std::vector<T> Gam(R), beta(R);
225 for (std::size_t j = 0; j < R; ++j) {
226 Gam[j] = T(Nexp * p[j] / q[j]);
227 beta[j] = T(num_traits<T>::from_int(K[j]) / Nexp);
232 for (std::size_t j = 0; j < R; ++j) den -= T(lambda[j] / T(q[j] + qJ));
234 for (std::size_t j = 0; j < R; ++j)
235 num -= T(lambda[j] * T(q[j] - qJ) / T(q[j] * T(q[j] + qJ)));
236 const T alpha2 = T(alpha * alpha);
237 const T F10 = T(T(-one / T(alpha2 * qJ)) * num / den);
238 c0 = T(T(-num_traits<T>::from_int(2) / qJ) * F10);
241 for (std::size_t j = 0; j < R; ++j) bg2 += beta[j] * Gam[j] * Gam[j];
242 std::vector<T> f1(R);
243 for (std::size_t j = 0; j < R; ++j)
244 f1[j] = T(T(lambda[j] / T(q[j] + qJ)) *
245 T(F10 - T(num_traits<T>::from_int(2) / T(alpha2 * q[j]))));
246 const T alpha3 = T(alpha2 * alpha);
247 const T alpha4 = T(alpha3 * alpha);
248 std::vector<T> S2j(R);
249 for (std::size_t j = 0; j < R; ++j)
250 S2j[j] = T(T(num_traits<T>::from_int(6) / alpha4) *
251 T(alpha * beta[j] * Gam[j] * Gam[j] +
252 num_traits<T>::from_int(2) * bg2 * beta[j] * Gam[j]));
253 Matrix<T> S2js(R, R, zero);
254 for (std::size_t j = 0; j < R; ++j)
255 for (std::size_t s = 0; s < R; ++s)
256 S2js(j, s) = T(T(num_traits<T>::from_int(3) / alpha3) * beta[j] * Gam[j] * beta[s] *
259 Matrix<T> Amat(R, R, zero);
260 for (std::size_t j = 0; j < R; ++j) Amat(j, j) = one;
261 std::vector<T> rhs(R, zero);
262 for (std::size_t j = 0; j < R; ++j) {
263 for (std::size_t s = 0; s < R; ++s) {
264 const T d = T(q[j] + q[s] + qJ);
265 Amat(j, s) -= T(lambda[j] / d);
266 Amat(j, j) -= T(lambda[s] / d);
267 rhs[j] += T(S2js(j, s) / d);
271 const std::vector<T> F2 =
solve(Amat, rhs);
273 const T f10 = T(T(-num_traits<T>::from_int(3) / T(alpha3 * qJ)) * bg2);
275 for (std::size_t j = 0; j < R; ++j)
276 acc += T(T(num_traits<T>::from_int(2) * Gam[j] * q[j] * F2[j] + S2j[j]) / T(q[j] + qJ));
277 const T F20 = T(T(acc - f10) / den);
278 c1 = T(T(T(-num_traits<T>::from_int(2) / qJ) * F20) + T(c0 / alpha2 * bg2));
280 W = T(T(one / T(alpha * qJ)) *
281 T(one - T(num_traits<T>::from_int(2) / Nexp * bg2 / alpha2)));
282 W2 = T(c0 + T(c1 / Nexp));
300 "pfqn_respt_ps_moments builds its stationary law in the log domain and needs "
301 "transcendental arithmetic");
302 const std::size_t R = S.size();
304 if (N.size() != R || Z.size() != R)
305 throw InputError(
"pfqn_respt_ps_moments: S, N and Z must have the same number of classes");
306 for (std::size_t r = 0; r < R; ++r)
307 if (S[r] <= zero)
throw InputError(
"pfqn_respt_ps_moments: S must be finite and positive");
308 for (std::size_t r = 0; r < R; ++r)
310 throw InputError(
"pfqn_respt_ps_moments: N must contain non-negative integers");
312 std::vector<std::size_t> act;
313 for (std::size_t r = 0; r < R; ++r)
314 if (N[r] > 0) act.push_back(r);
315 for (std::size_t k = 0; k < act.size(); ++k)
316 if (Z[act[k]] <= zero)
318 "pfqn_respt_ps_moments: Z must be finite and positive for every populated class");
320 const T nan = std::numeric_limits<T>::quiet_NaN();
322 res.
W.assign(R, nan);
323 res.
W2.assign(R, nan);
325 res.
c0.assign(R, nan);
326 res.
c1.assign(R, nan);
327 res.
alpha.assign(R, nan);
328 res.
nstates.assign(R, std::numeric_limits<double>::quiet_NaN());
330 if (act.empty())
return res;
332 const std::size_t A = act.size();
333 std::vector<T> qa(A), pa(A);
334 for (std::size_t k = 0; k < A; ++k) {
339 for (std::size_t k = 1; k < A; ++k) {
340 const T v = T(qa[k] / pa[k]);
344 for (std::size_t jj = 0; jj < A; ++jj) {
345 const std::size_t J = act[jj];
346 std::vector<long> K(A);
347 for (std::size_t k = 0; k < A; ++k) K[k] = N[act[k]];
350 for (std::size_t k = 0; k < A; ++k) ns *= static_cast<double>(K[k] + 1);
352 for (std::size_t k = 0; k < A; ++k)
354 res.
alpha[J] = alpha;
357 const bool useExact =
361 if (ns > detail::RESPT_PS_EXACT_MAX)
363 "pfqn_respt_ps_moments: the exact route needs a linear solve above the "
364 "supported dimension; use the asymptotic route");
365 detail::respt_ps_exact(pa, qa, K, jj, res.
W[J], res.
W2[J]);
372 "pfqn_respt_ps_moments: the asymptotic expansion needs normal usage alpha > 0, "
373 "which this model violates");
377 detail::respt_ps_asymptotic(pa, qa, K, jj, res.
W[J], res.
W2[J], res.
c0[J], res.
c1[J]);
386 const std::vector<T>& Z) {
The exception types the port throws.
LU factorization with partial pivoting, templated on the number type.
Dense matrix and non-owning view.
ResptPsMethod
Which route produced the moments of a given class.
@ Exact
Proposition 3, the linear solve.
@ Unavailable
normal usage fails and the exact route was not affordable
@ Asymptotic
Proposition 6, the two-term expansion.
@ None
the class is unpopulated
@ Auto
tail for a single class, pmf otherwise
ResptPsMomentsResult< T > pfqn_respt_ps_moments(const std::vector< T > &S, const std::vector< long > &N, const std::vector< T > &Z, ResptPsRoute route)
Sojourn-time moments at the processor-sharing station of a closed terminal-driven system (Mitra and M...
ResptPsRoute
Requested route.
std::vector< T > solve(const Matrix< T > &A, const std::vector< T > &b)
Convenience: solve Ax = b, leaving A and b untouched.
Number-type abstraction for the templated API port.
Shared scalar machinery for the integration / asymptotic members of the pfqn family (pfqn_le,...
Sojourn-time moments at the PS station, per class.
std::vector< T > c1
(R) first correction, NaN off that route
std::vector< T > W
(R) mean sojourn times; NaN for an unpopulated class
T expansionParam
Nexp = max_r q_r / p_r.
std::vector< T > c0
(R) leading asymptotic term, NaN off that route
std::vector< double > nstates
(R) size of the exact state space
std::vector< ResptPsMethod > method
std::vector< T > W2
(R) second moments
std::vector< T > alpha
(R) unutilized CPU fraction of the open system