LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Loading...
Searching...
No Matches
solver_nc_busyp.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_SOLVERS_NC_SOLVER_NC_BUSYP_H
6#define LINE_SOLVERS_NC_SOLVER_NC_BUSYP_H
7
8/**
9 * @file
10 * @ingroup line_solvers
11 * Port of `@SolverNC/getAvgBusyPeriod.m` and of the Python-native
12 * `SolverNC.getAvgBusyPeriod`: the mean busy period of order n for a set of
13 * stations, from `pfqn_busyp` (Daduna, J. ACM 35(3), 1988).
14 *
15 * WHAT THIS LAYER ADDS OVER THE API. `pfqn_busyp` takes the paper's inputs --
16 * relative arrival rates, a rate law, a routing matrix -- and this reads them
17 * off a NetworkStruct:
18 *
19 * alpha = Vchain(:,1), the chain visit ratios. The CLOSED formula is
20 * homogeneous of degree zero in alpha, so unnormalized visits serve; the
21 * OPEN one is not, and alpha there is scaled to absolute rates by lambda.
22 * mu(j,k) = scaling(j,k) / STchain(j), a RATE and not the dimensionless
23 * lldscaling `pfqn_ncld` takes. The precedence is the one `solver_ncld`
24 * uses: an infinite server first (scaling = k), then a declared
25 * lldscaling row, then the multiserver staircase min(k,c).
26 * P = the station-to-station chain routing, the class blocks of
27 * `sn_rt_stations` weighted by the class visits. That weighting is exact,
28 * being a flow balance and not an approximation.
29 *
30 * AN OPEN MODEL DROPS THE SOURCE. The Jackson network of the paper has no
31 * Source station: its outflow is the external stream gamma, so the Source is
32 * removed from the node set and `gamma_j = lambda * P(source, j)`.
33 *
34 * SINGLE CHAIN ONLY. The paper is written for identical customers; Section 5
35 * only sketches the multichain extension, which no codebase implements, so a
36 * multichain model is refused rather than answered from a chain aggregate that
37 * the theorem does not cover.
38 */
39
40#include <algorithm>
41#include <cmath>
42#include <cstddef>
43#include <functional>
44#include <limits>
45#include <vector>
46
51#include "line/util/error.h"
52
53namespace line {
54namespace nc {
55
56/**
57 * Mean busy period of order n for a set of stations.
58 *
59 * @param sn the network structure, single-chain
60 * @param subnet zero-based STATION indexes forming the subnetwork
61 * @param orders busy period orders, 1 for the ordinary busy period
62 * @return one duration per requested order
63 */
64template <class T>
65std::vector<double> solver_nc_busyp(const qn::NetworkStruct<T>& sn,
66 const std::vector<std::size_t>& subnet,
67 const std::vector<std::size_t>& orders) {
68 if (sn.nchains > 1)
69 throw UnsupportedError(
70 "solver_nc_busyp: the busy period of a subnetwork is defined for "
71 "single-chain models only; Section 5 of Daduna (1988) sketches the "
72 "multichain extension, which is not implemented");
73
74 const std::size_t M = sn.nstations, K = sn.nclasses;
77
78 // station-to-station routing of the chain, the class blocks weighted by the
79 // class visits: exact, being a flow balance
80 Matrix<double> Pst(M, M, 0.0);
81 for (std::size_t i = 0; i < M; ++i) {
82 double vtot = 0.0;
83 for (std::size_t r = 0; r < K; ++r) vtot += num_traits<T>::to_double(rt.Vst(i, r));
84 for (std::size_t j = 0; j < M; ++j) {
85 double flow = 0.0;
86 for (std::size_t r = 0; r < K; ++r)
87 for (std::size_t s = 0; s < K; ++s)
88 flow += num_traits<T>::to_double(rt.Vst(i, r)) *
89 num_traits<T>::to_double(rt.rtst(i * K + r, j * K + s));
90 Pst(i, j) = (vtot > 0) ? flow / vtot : 0.0;
91 }
92 }
93
94 std::vector<double> st_time(M, 0.0), visits(M, 0.0);
95 for (std::size_t i = 0; i < M; ++i) {
96 st_time[i] = num_traits<T>::to_double(d.STchain(i, 0));
97 visits[i] = num_traits<T>::to_double(d.Vchain(i, 0));
98 }
99
100 // mu(j,k): the rate of station j holding k jobs, in the solver_ncld order
101 const auto rate_of = [&](std::size_t j, std::size_t k) -> double {
102 double scaling;
103 const double servers = num_traits<T>::to_double(sn.stations[j].nservers);
104 const std::vector<T>& lld = sn.stations[j].lldscaling;
105 if (!std::isfinite(servers)) {
106 scaling = static_cast<double>(k);
107 } else if (!lld.empty()) {
108 const std::size_t idx = std::min(k, lld.size()) - 1;
109 scaling = num_traits<T>::to_double(lld[idx]);
110 } else {
111 scaling = std::min(static_cast<double>(k), servers);
112 }
113 return scaling / st_time[j];
114 };
115
116 const double N = d.Nchain.empty() ? std::numeric_limits<double>::infinity()
117 : d.Nchain[0];
118 if (!std::isfinite(N)) {
119 // the Source is not a node of the Jackson network: its outflow is gamma
120 std::size_t source = M;
121 for (std::size_t i = 0; i < M; ++i)
122 if (sn.stations[i].nodetype == lang::NodeType::Source) {
123 source = i;
124 break;
125 }
126 if (source == M)
127 throw InputError("solver_nc_busyp: an open model must own a Source station");
128 for (std::size_t t = 0; t < subnet.size(); ++t)
129 if (subnet[t] == source)
130 throw InputError(
131 "solver_nc_busyp: the Source cannot belong to the subnetwork");
132
133 double lambda = 0.0;
134 for (std::size_t r = 0; r < K; ++r) {
135 const double rate = num_traits<T>::to_double(sn.rates(source, r));
136 if (std::isfinite(rate)) lambda += rate;
137 }
138 std::vector<std::size_t> keep;
139 std::vector<std::size_t> remap(M, 0);
140 for (std::size_t i = 0; i < M; ++i)
141 if (i != source) {
142 remap[i] = keep.size();
143 keep.push_back(i);
144 }
145 std::vector<double> alpha(keep.size(), 0.0), gamma(keep.size(), 0.0);
146 Matrix<double> P(keep.size(), keep.size(), 0.0);
147 for (std::size_t i = 0; i < keep.size(); ++i) {
148 alpha[i] = lambda * visits[keep[i]] / visits[source];
149 gamma[i] = lambda * Pst(source, keep[i]);
150 for (std::size_t j = 0; j < keep.size(); ++j) P(i, j) = Pst(keep[i], keep[j]);
151 }
152 std::vector<std::size_t> mapped;
153 for (std::size_t t = 0; t < subnet.size(); ++t) mapped.push_back(remap[subnet[t]]);
154 const std::function<double(std::size_t, std::size_t)> mu =
155 [&](std::size_t j, std::size_t k) { return rate_of(keep[j], k); };
156 return pfqn::pfqn_busyp(alpha, mu, P, N, mapped, orders, gamma).b;
157 }
158
159 const std::function<double(std::size_t, std::size_t)> mu = rate_of;
160 return pfqn::pfqn_busyp(visits, mu, Pst, N, subnet, orders).b;
161}
162
163} // namespace nc
164} // namespace line
165
166#endif // LINE_SOLVERS_NC_SOLVER_NC_BUSYP_H
InputError(const std::string &what)
Definition error.h:39
UnsupportedError(const std::string &what)
Definition error.h:51
A network plus its refreshed NetworkStruct.
The exception types the port throws.
SnRtStations< T > sn_rt_stations(const qn::NetworkStruct< T > &sn)
ChainDemands< T > sn_get_demands_chain(const qn::NetworkStruct< T > &L)
Port of sn_get_demands_chain.
Definition sn_chain.h:63
std::vector< double > solver_nc_busyp(const qn::NetworkStruct< T > &sn, const std::vector< std::size_t > &subnet, const std::vector< std::size_t > &orders)
Mean busy period of order n for a set of stations.
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.
Definition pfqn_busyp.h:186
A queueing network and its refreshed NetworkStruct.
Mean busy period of order n for a subnetwork of a product-form network.
Chain aggregation and de-aggregation.
Port of matlab/src/api/sn/sn_rt_stations.m.
What sn_rt_stations returns: the complemented routing and the station visits.
Matrix< T > rtst
(nstations*nclasses) square, STATION-major rows
Matrix< T > Vst
(nstations x nclasses), cellsum(sn.visits) by station
The chain-level view of a layer, as sn_get_demands_chain returns it.
Definition sn_chain.h:46
std::vector< double > Nchain
(C) population, infinite for an open chain
Definition sn_chain.h:51
Matrix< T > STchain
(M x C) mean service time
Definition sn_chain.h:48
Matrix< T > Vchain
(M x C) visits
Definition sn_chain.h:49
std::vector< double > b
mean duration per requested order
Definition pfqn_busyp.h:166