133 const double TOL = 1e-10;
139 for (std::size_t r = 0; r < L.
classes.size(); ++r)
140 if (std::isfinite(L.
classes[r].population))
142 "solver_ba_snc: method 'snc.upper' supports fully open networks only "
143 "(no closed classes)");
144 std::vector<std::size_t> srcList, qstat;
145 for (std::size_t i = 0; i < M; ++i) {
146 if (L.
stations[i].nodetype == qn::NodeType::Source)
147 srcList.push_back(i);
153 "solver_ba_snc: method 'snc.upper' requires an open network with a Source station");
154 for (std::size_t a = 0; a < qstat.size(); ++a) {
155 const std::size_t i = qstat[a];
158 "solver_ba_snc: method 'snc.upper' does not support delay (infinite-server) "
159 "stations: the service envelope is that of a single busy server");
161 if (std::isfinite(ns) && ns > 1)
163 "solver_ba_snc: method 'snc.upper' does not support multi-server stations");
169 std::vector<std::size_t> pairStation, pairClass, pairFlat;
170 for (std::size_t a = 0; a < qstat.size(); ++a)
171 for (std::size_t r = 0; r < K; ++r) {
172 pairStation.push_back(qstat[a]);
173 pairClass.push_back(r);
174 pairFlat.push_back(qstat[a] * K + r);
176 const std::size_t np = pairFlat.size();
180 std::vector<std::size_t> srcOfCol, clsOfCol;
181 for (std::size_t si = 0; si < srcList.size(); ++si)
182 for (std::size_t r0 = 0; r0 < K; ++r0) {
183 srcOfCol.push_back(srcList[si]);
184 clsOfCol.push_back(r0);
186 const std::size_t ncols = srcOfCol.size();
187 std::vector<std::vector<double>> inject(np, std::vector<double>(ncols, 0.0));
188 std::vector<double> lambda0(np, 0.0);
189 for (std::size_t c = 0; c < ncols; ++c) {
191 if (!std::isfinite(arr) || arr <= 0)
continue;
192 const std::size_t srow = srcOfCol[c] * K + clsOfCol[c];
193 for (std::size_t p = 0; p < np; ++p) {
195 lambda0[p] += inject[p][c];
200 for (std::size_t p = 0; p < np; ++p)
201 for (std::size_t q = 0; q < np; ++q)
206 for (std::size_t i = 0; i < np; ++i)
207 for (std::size_t j = 0; j < np; ++j) ImPt(i, j) = (i == j ? 1.0 : 0.0) - P(j, i);
209 for (std::size_t p = 0; p < np; ++p) rhs0(p, 0) = lambda0[p];
212 for (std::size_t p = 0; p < np; ++p) lamMax = std::max(lamMax, lamAll(p, 0));
213 const double lamTol = 1e-12 * std::max(1.0, lamMax);
214 std::vector<std::size_t> keep;
215 for (std::size_t p = 0; p < np; ++p)
216 if (lamAll(p, 0) > lamTol) keep.push_back(p);
217 if (keep.empty())
throw UnsupportedError(
"solver_ba_snc: the model carries no open traffic");
219 const std::size_t nk = keep.size();
220 std::vector<double> lam(nk, 0.0), mu(nk, 0.0);
221 std::vector<std::size_t> statk(nk), clsk(nk);
222 std::vector<std::vector<double>> injk(nk, std::vector<double>(ncols, 0.0));
224 for (std::size_t a = 0; a < nk; ++a) {
225 const std::size_t p = keep[a];
226 lam[a] = lamAll(p, 0);
227 statk[a] = pairStation[p];
228 clsk[a] = pairClass[p];
230 for (std::size_t b = 0; b < nk; ++b) Pk(a, b) = P(p, keep[b]);
232 if (!std::isfinite(mu[a]) || mu[a] <= 0)
233 throw UnsupportedError(
"solver_ba_snc: station " + std::to_string(statk[a] + 1) +
234 " has no service rate for class " + std::to_string(clsk[a] + 1) +
235 " but carries its traffic");
238 "solver_ba_snc: method 'snc.upper' requires exponential service: station " +
239 std::to_string(statk[a] + 1) +
" class " + std::to_string(clsk[a] + 1) +
240 " is not exponential");
244 for (std::size_t a = 0; a < nk; ++a) {
245 std::size_t nsucc = 0, succ = 0;
246 for (std::size_t b = 0; b < nk; ++b)
247 if (Pk(a, b) > TOL) {
253 "solver_ba_snc: method 'snc.upper' requires deterministic routing downstream of "
254 "the Source: station " +
255 std::to_string(statk[a] + 1) +
" class " + std::to_string(clsk[a] + 1) +
256 " splits its flow over " + std::to_string(nsucc) +
" destinations");
257 if (nsucc == 1 && std::fabs(Pk(a, succ) - 1.0) > 1e-8)
259 "solver_ba_snc: method 'snc.upper' requires deterministic routing downstream of "
260 "the Source: station " +
261 std::to_string(statk[a] + 1) +
" class " + std::to_string(clsk[a] + 1) +
262 " routes onward with probability " + std::to_string(Pk(a, succ)));
266 for (std::size_t c = 0; c < ncols; ++c) {
267 std::size_t ndest = 0;
268 for (std::size_t a = 0; a < nk; ++a)
269 if (injk[a][c] > TOL) ndest++;
270 if (ndest <= 1)
continue;
273 "solver_ba_snc: method 'snc.upper' can split only a Poisson Source: source " +
274 std::to_string(srcOfCol[c] + 1) +
" class " + std::to_string(clsOfCol[c] + 1) +
275 " is not exponential and feeds " + std::to_string(ndest) +
" stations");
279 std::vector<std::size_t> stationsUsed;
280 for (std::size_t a = 0; a < nk; ++a)
281 if (std::find(stationsUsed.begin(), stationsUsed.end(), statk[a]) == stationsUsed.end())
282 stationsUsed.push_back(statk[a]);
283 for (std::size_t u = 0; u < stationsUsed.size(); ++u) {
284 double lo = std::numeric_limits<double>::infinity(), hi = 0.0;
285 for (std::size_t a = 0; a < nk; ++a)
286 if (statk[a] == stationsUsed[u]) {
287 lo = std::min(lo, mu[a]);
288 hi = std::max(hi, mu[a]);
290 if (hi - lo > 1e-8 * std::max(1.0, hi))
292 "solver_ba_snc: method 'snc.upper' requires the classes sharing a station to have "
293 "equal service rates: station " +
294 std::to_string(stationsUsed[u] + 1) +
" carries rates in [" + std::to_string(lo) +
295 ", " + std::to_string(hi) +
"]");
298 const std::size_t ns = stationsUsed.size();
299 std::vector<std::vector<bool>> adj(ns, std::vector<bool>(ns,
false));
300 const auto stIdx = [&](std::size_t station) {
301 return static_cast<std::size_t
>(
302 std::find(stationsUsed.begin(), stationsUsed.end(), station) - stationsUsed.begin());
304 for (std::size_t a = 0; a < nk; ++a)
305 for (std::size_t b = 0; b < nk; ++b)
306 if (Pk(a, b) > TOL) adj[stIdx(statk[a])][stIdx(statk[b])] =
true;
307 const std::vector<std::size_t> order = detail::snc_topo_order(adj);
310 "solver_ba_snc: method 'snc.upper' requires a feed-forward network: the station graph "
311 "has a cycle, so a station's cross traffic is not determined upstream of it");
316 auto outH = std::make_shared<std::vector<snc::Envelope>>(nk);
317 std::vector<snc::Envelope> arvH(nk), srvH(nk);
318 for (std::size_t oi = 0; oi < ns; ++oi) {
319 const std::size_t i = stationsUsed[order[oi]];
320 std::vector<std::size_t> here;
321 for (std::size_t a = 0; a < nk; ++a)
322 if (statk[a] == i) here.push_back(a);
324 for (std::size_t h = 0; h < here.size(); ++h) {
325 const std::size_t a = here[h];
326 std::vector<snc::Envelope> parts;
327 for (std::size_t c = 0; c < ncols; ++c) {
328 if (injk[a][c] <= TOL)
continue;
336 for (std::size_t x = 0; x < m.
D0.rows(); ++x)
337 for (std::size_t y = 0; y < m.
D0.cols(); ++y) {
344 for (std::size_t b = 0; b < nk; ++b)
346 parts.push_back([outH, b](
double theta) {
return (*outH)[b](theta); });
349 " class " + std::to_string(clsk[a] + 1) +
350 " carries traffic with no identifiable source");
351 arvH[a] = detail::snc_sum(parts);
353 for (std::size_t h = 0; h < here.size(); ++h) {
354 const std::size_t a = here[h];
355 std::vector<snc::Envelope> cross;
356 for (std::size_t g = 0; g < here.size(); ++g)
357 if (here[g] != a) cross.push_back(arvH[here[g]]);
358 const double mua = mu[a];
360 const bool alone = cross.empty();
361 srvH[a] = [mua, crossSum, alone](
double theta) {
367 (*outH)[a] = [arva, srva](
double theta) {
373 for (std::size_t a = 0; a < nk; ++a) {
374 const std::pair<std::size_t, std::size_t> key(statk[a], clsk[a]);
375 env.arv[key] = arvH[a];
376 env.srv[key] = srvH[a];
377 env.lam[key] = lam[a];