249 const std::vector<T>& cap,
const std::vector<bool>& isDelay,
250 const Matrix<T>& rt,
const std::vector<std::size_t>& stationToStateful,
253 "npfqn_sqd requires transcendental arithmetic (the effective-rate "
254 "calibration uses exp, log and real powers)");
257 const std::size_t M = ST.size();
258 if (M == 0)
throw InputError(
"npfqn_sqd: empty station list");
259 if (V.size() != M || cap.size() != M || isDelay.size() != M ||
260 stationToStateful.size() != M)
261 throw InputError(
"npfqn_sqd: the per-station inputs disagree on the station count");
262 if (nclasses == 0)
throw InputError(
"npfqn_sqd: the class count must be positive");
263 if (N < 0)
throw InputError(
"npfqn_sqd: the population must be nonnegative");
264 if (!
opt.initialV1.empty() &&
opt.initialV1.size() != M)
265 throw InputError(
"npfqn_sqd: initialV1 must have one entry per station");
274 detail::sqd_effective_routing(rt, stationToStateful, nclasses, isDelay);
276 std::vector<T> V1(M, zero), v1init(M, zero), L_buf(M, zero), L_svr(M, zero);
277 for (std::size_t i = 0; i < M; ++i) {
278 v1init[i] =
opt.initialV1.empty() ? INITIAL_V1 :
opt.initialV1[i];
282 std::vector<T> W_buf(M, zero), W_svr(M, zero);
284 for (
int pop = 1; pop <= N; ++pop) {
286 for (std::size_t i = 0; i < M; ++i) {
293 if (
opt.ownServerNeighbor) {
294 if (!detail::sqd_is_inf(cap[i]))
295 pBlockDown = detail::sqd_mm1k_blocking(cap[i], T(X * V[i] * ST[i]));
297 for (std::size_t j = 0; j < M; ++j)
298 if (!isDelay[j] && pEff(i, j) > zero && !detail::sqd_is_inf(cap[j]))
299 pBlockDown += pEff(i, j) *
300 detail::sqd_mm1k_blocking(cap[j], T(X * V[j] * ST[j]));
304 if (
opt.ownServerNeighbor) {
307 for (std::size_t j = 0; j < M; ++j) n += pEff(i, j) * L_svr[j];
315 detail::sqd_beta_gamma(cap[i], pBlockDown,
opt.calibrationMode,
316 CALIBRATION_EPSILON, beta, gamma);
317 T base = T(T(n - one) / beta);
318 if (base < zero) base = zero;
319 const T expArg = pow(base, gamma);
320 mu_n = T(n * V1[i] * exp(T(-expArg)));
322 if (mu_n < tiny) mu_n = tiny;
324 W_buf[i] = T(one / mu_n * T(one + n));
325 W_svr[i] = T(ST[i] * T(one + L_svr[i]));
327 if (
opt.serverBlockingTime) {
329 for (std::size_t j = 0; j < M; ++j) {
330 if (isDelay[j] || !(pEff(i, j) > zero) || detail::sqd_is_inf(cap[j]))
continue;
331 const T pBj = detail::sqd_mm1k_blocking(cap[j], T(X * V[j] * ST[j]));
332 const T denom = T(ST[i] + ST[j]);
334 if (denom > eps15) theta = T(ST[j] / denom);
335 bt += pEff(i, j) * pBj * ST[j] * theta;
343 for (std::size_t i = 0; i < M; ++i) sumVW += V[i] * T(W_buf[i] + W_svr[i]);
347 for (std::size_t i = 0; i < M; ++i) {
348 L_buf[i] = X * V[i] * W_buf[i];
349 L_svr[i] = X * V[i] * W_svr[i];
354 for (std::size_t i = 0; i < M; ++i) {
355 if (isDelay[i])
continue;
357 if (
opt.ownServerNeighbor) {
358 if (!detail::sqd_is_inf(cap[i]))
359 pBlock = detail::sqd_mm1k_blocking(cap[i], T(X * V[i] * ST[i]));
361 for (std::size_t j = 0; j < M; ++j)
362 if (!isDelay[j] && pEff(i, j) > zero && !detail::sqd_is_inf(cap[j]))
363 pBlock += pEff(i, j) *
364 detail::sqd_mm1k_blocking(cap[j], T(X * V[j] * ST[j]));
366 V1[i] =
opt.freshV1 ? T(v1init[i] * T(one - pBlock))
367 : T(V1[i] * T(one - pBlock));
368 if (V1[i] < tiny) V1[i] = tiny;
374 res.
X.assign(M, zero);
375 res.
Q.assign(M, zero);
376 res.
U.assign(M, zero);
377 res.
R.assign(M, zero);
378 for (std::size_t i = 0; i < M; ++i) {
379 const T T_i = X * V[i];
380 const T Q_i = T(L_buf[i] + L_svr[i]);
381 const T W_tot = T(W_buf[i] + W_svr[i]);
384 res.
R[i] = (T_i > eps15) ? T(Q_i / T_i) : W_tot;
385 const T u = T(T_i * ST[i]);
386 res.
U[i] = (u < one) ? u : one;
SqdResult< T > npfqn_sqd(const std::vector< T > &ST, const std::vector< T > &V, const std::vector< T > &cap, const std::vector< bool > &isDelay, const Matrix< T > &rt, const std::vector< std::size_t > &stationToStateful, std::size_t nclasses, int N, const SqdOptions< T > &opt)
Smith Queue Decomposition (SQD): approximate MVA for closed networks under Blocking-After-Service (ma...