LINE Solver (C++)
Templated C++ port of the LINE queueing solver
Loading...
Searching...
No Matches
sn_zero_capacity.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_API_SN_SN_ZERO_CAPACITY_H
6#define LINE_API_SN_SN_ZERO_CAPACITY_H
7
8/**
9 * @file
10 * @ingroup api_sn
11 * Port of matlab/src/api/sn/sn_zero_capacity.m: the closed chains trapped at a
12 * zero-capacity station.
13 *
14 * A station with zero servers never completes a job, nor does one that serves
15 * a class at rate zero (an infinite mean service time, which is how a layer
16 * above a zero-capacity LQN element sees it). A closed chain that visits one
17 * therefore ends, in steady state, with its whole population parked there:
18 * zero throughput everywhere, an infinite response time at that station, and
19 * no load on any other. The remaining chains are unaffected, since a trapped
20 * job holds no other resource, so a model with such chains is solved as the
21 * model without them. A rate-zero class at a finite-server station shared with
22 * a live chain would hold servers forever, so that case is refused.
23 *
24 * RATE ZERO IS READ ON AN ENABLED PAIR ONLY. The reference's `sn.rates` holds
25 * NaN where a class has no service; this port holds 0 there with the parallel
26 * `disabled` flag set, so a bare `rates == 0` would read every unvisited pair
27 * as stalled. Only a visited pair can trap a chain, so the outcome is the same.
28 *
29 * ARITHMETIC: field. Comparisons only.
30 */
31
32#include <cmath>
33#include <cstddef>
34#include <string>
35#include <vector>
36
38#include "line/num/number.h"
39
40namespace line {
41namespace api {
42
43/** What `sn_zero_capacity` reports. */
45 /** (nchains) the 1-based zero-capacity station trapping chain c, 0 if none. */
46 std::vector<std::size_t> deadstat;
47 /**
48 * Non-empty when the model has no steady state of this form: an open class
49 * reaches a zero-capacity station, a chain visits two of them, or a rate-zero
50 * finite-server station is shared with a live chain.
51 */
52 std::string reason;
53 /** True when some chain is trapped. */
54 bool any() const {
55 for (std::size_t s : deadstat)
56 if (s > 0) return true;
57 return false;
58 }
59};
60
61template <class T>
63 ZeroCapacity out;
64 const std::size_t M = sn.nstations, K = sn.nclasses;
65 out.deadstat.assign(sn.nchains, 0);
66 // stalled(i, r): station i completes no class-r job
67 std::vector<std::vector<bool>> stalled(M, std::vector<bool>(K, false));
68 bool anystalled = false;
69 for (std::size_t i = 0; i < M; ++i)
70 for (std::size_t r = 0; r < K; ++r) {
71 const bool zr = !sn.disabled[i][r] &&
72 num_traits<T>::to_double(sn.rates(i, r)) == 0.0;
73 stalled[i][r] = zr || sn.stations[i].nservers == 0.0;
74 anystalled = anystalled || stalled[i][r];
75 }
76 if (!anystalled) return out;
77
78 const std::vector<double> st2sf = sn.station_to_stateful();
79 const std::vector<double> njobs = sn.njobs();
80 // visited(c, i, r): chain c visits station i in class r
81 auto visited = [&](std::size_t c, std::size_t i, std::size_t r) {
82 const double sf = st2sf[i];
83 if (std::isnan(sf) || c >= sn.visits.size()) return false;
84 const std::size_t row = static_cast<std::size_t>(sf) - 1;
85 if (row >= sn.visits[c].rows() || r >= sn.visits[c].cols()) return false;
86 return num_traits<T>::to_double(sn.visits[c](row, r)) > 0.0;
87 };
88 for (std::size_t c = 0; c < sn.nchains; ++c) {
89 std::vector<std::size_t> hit;
90 bool open = false;
91 for (std::size_t r = 0; r < K; ++r)
92 if (sn.chains[c][r] && std::isinf(njobs[r])) open = true;
93 for (std::size_t i = 0; i < M; ++i) {
94 bool h = false;
95 for (std::size_t r = 0; r < K && !h; ++r)
96 h = sn.chains[c][r] && stalled[i][r] && visited(c, i, r);
97 if (h) hit.push_back(i);
98 }
99 if (hit.empty()) continue;
100 if (open) {
101 out.reason = "Station " + sn.stations[hit[0]].name +
102 " has zero capacity and is visited by an open class, which accumulates "
103 "there without bound: the model has no steady state.";
104 return out;
105 }
106 if (hit.size() > 1) {
107 std::string names;
108 for (std::size_t k = 0; k < hit.size(); ++k)
109 names += (k ? ", " : "") + sn.stations[hit[k]].name;
110 out.reason = "Chain " + std::to_string(c + 1) + " visits " +
111 std::to_string(hit.size()) + " stations with zero capacity (" + names +
112 "): its jobs are trapped at all of them and how they divide depends on "
113 "the initial state.";
114 return out;
115 }
116 out.deadstat[c] = hit[0] + 1;
117 }
118 for (std::size_t i = 0; i < M; ++i) {
119 bool dead = false;
120 for (std::size_t s : out.deadstat) dead = dead || s == i + 1;
121 const double ns = sn.stations[i].nservers;
122 if (!dead || ns == 0.0 || std::isinf(ns)) continue;
123 for (std::size_t c = 0; c < sn.nchains; ++c) {
124 if (out.deadstat[c] == i + 1) continue;
125 for (std::size_t r = 0; r < K; ++r)
126 if (sn.chains[c][r] && visited(c, i, r)) {
127 out.reason = "Station " + sn.stations[i].name +
128 " serves a class at rate zero and is shared with a chain that "
129 "is not trapped there: the stalled jobs would hold its servers "
130 "forever.";
131 return out;
132 }
133 }
134 }
135 return out;
136}
137
138} // namespace api
139} // namespace line
140
141#endif // LINE_API_SN_SN_ZERO_CAPACITY_H
A network plus its refreshed NetworkStruct.
ZeroCapacity sn_zero_capacity(const qn::NetworkStruct< T > &sn)
Conservation laws of a layered queueing network, enumerated from its structure.
Definition aoi_dist2ph.h:52
A queueing network and its refreshed NetworkStruct.
Number-type abstraction for the templated API port.
What sn_zero_capacity reports.
std::vector< std::size_t > deadstat
(nchains) the 1-based zero-capacity station trapping chain c, 0 if none.
std::string reason
Non-empty when the model has no steady state of this form: an open class reaches a zero-capacity stat...
bool any() const
True when some chain is trapped.