5#ifndef LINE_SOLVERS_CTMC_SOLVER_CTMC_POOLS_H
6#define LINE_SOLVERS_CTMC_SOLVER_CTMC_POOLS_H
49 const std::string& cname,
const std::string& what) {
55 "SolverCTMC serves a heterogeneous server pool with a renewal phase-type law; class '" +
57 " law in " + what +
".");
58 const std::size_t n = m.
D0.rows();
60 double asum = 0, d1n = 0, err = 0;
61 for (std::size_t j = 0; j < n; ++j) asum += num_traits<T>::to_double(a[j]);
64 for (std::size_t i = 0; i < n; ++i) {
68 for (std::size_t j = 0; j < n; ++j) {
77 "SolverCTMC serves a heterogeneous server pool with a renewal phase-type law; class '" +
78 cname +
"' at station '" + name +
"' has a correlated or matrix-exponential law in " +
85 const std::size_t R =
sn.nclasses;
89 for (std::size_t j = 0; j < st.
lldscaling.size(); ++j)
92 const auto rit =
sn.retrialparam.find(ist);
93 if (rit !=
sn.retrialparam.end())
94 for (std::size_t r = 0; r < rit->second.retrial_proc.size(); ++r)
95 if (!rit->second.retrial_proc[r].disabled) retrial =
true;
96 bool balk =
false, reneg =
false, immf =
false, reply =
false, par =
false, rr =
false,
98 for (std::size_t r = 0; r < st.
balking.size(); ++r)
100 for (std::size_t r = 0; r < st.
impatience.size(); ++r)
102 if (
sn.immfeed.size() >= ist)
103 for (std::size_t r = 0; r <
sn.immfeed[ist - 1].size(); ++r)
104 if (
sn.immfeed[ist - 1][r]) immf =
true;
105 if (
sn.replyblock.size() >= ind)
106 for (std::size_t r = 0; r <
sn.replyblock[ind - 1].size(); ++r)
107 if (
sn.replyblock[ind - 1][r]) reply =
true;
110 const std::vector<lang::RoutingStrategy>& rt =
sn.nodes[ind - 1].routing;
111 for (std::size_t r = 0; r < rt.size(); ++r)
114 for (std::size_t s = 0; s <
sn.issignal.size() && s < R && !sig; ++s) {
115 if (!
sn.issignal[s])
continue;
116 const std::size_t col = (ind - 1) * R + s;
117 for (std::size_t i = 0; i <
sn.rtnodes.rows() && col <
sn.rtnodes.cols(); ++i)
120 if (lld) why =
"load-dependent service";
121 else if (st.
cdscaling) why =
"class-dependent service";
122 else if (st.
jdscaling) why =
"joint-dependent service";
123 else if (
static_cast<bool>(
sn.gdscaling)) why =
"global dependence";
124 else if (
sn.breakdownparam.count(ist)) why =
"server breakdowns";
125 else if (retrial) why =
"retrial";
126 else if (balk) why =
"balking";
127 else if (reneg) why =
"reneging";
128 else if (
sn.isbasblocking.size() >= ind &&
sn.isbasblocking[ind - 1]) why =
"BAS blocking";
129 else if (immf) why =
"immediate feedback";
130 else if (reply) why =
"synchronous calls";
131 else if (par) why =
"server parallelism";
132 else if (rr) why =
"round-robin routing";
133 else if (sig) why =
"signals";
135 throw UnsupportedError(
"SolverCTMC does not combine heterogeneous server pools with " + why +
136 " (station '" +
sn.nodes[ind - 1].name +
"').");
142 const std::size_t R =
sn.nclasses;
144 const std::string& name =
sn.nodes[ind - 1].name;
155 "SolverCTMC supports heterogeneous server pools under FCFS, HOL, FCFSPRIO, LCFS, "
156 "LCFSPRIO and SIRO; station '" + name +
"' uses " +
160 if (
sn.nvars_of(ind) > 0)
162 "Station '" + name +
"' combines heterogeneous server pools with another feature that "
163 "owns a local variable (polling, BAS blocking, breakdown, a modulated process); "
164 "SolverCTMC cannot represent both.");
168 pool.
count.assign(NT, 0.0);
169 pool.
compat.assign(NT, std::vector<bool>(R,
false));
170 for (std::size_t t = 0; t < NT; ++t) {
172 const std::vector<bool>& c = st.
server_types[t].compatible;
173 for (std::size_t r = 0; r < R; ++r) pool.
compat[t][r] = c.empty() || (r < c.size() && c[r]);
175 pool.
pools.assign(R, std::vector<std::size_t>());
176 pool.
off.assign(R, std::vector<std::size_t>());
177 pool.
len.assign(R, std::vector<std::size_t>());
178 pool.
alpha.assign(R, std::vector<std::vector<T>>());
179 pool.
exit.assign(R, std::vector<std::vector<T>>());
181 pool.
fsfrate.assign(NT, std::vector<double>(R, 0.0));
183 for (std::size_t r = 0; r < R; ++r) {
186 bool hasPoolLaw =
false;
187 for (std::size_t t = 0; t < NT; ++t) {
188 const std::vector<lang::Distrib<T>>& sv = st.
server_types[t].service;
189 hasPoolLaw = hasPoolLaw || (pool.
compat[t][r] && r < sv.size() && !sv[r].disabled);
191 if (!baseOk && !hasPoolLaw)
continue;
192 for (std::size_t t = 0; t < NT; ++t)
193 if (pool.
compat[t][r]) pool.
pools[r].push_back(t + 1);
194 const std::string& cname =
sn.classes[r].name;
195 if (pool.
pools[r].empty())
197 "' declares no server pool compatible with class '" + cname +
198 "', so a job of that class would wait forever.");
202 require_ph(base, basemap, name, cname,
"its default service");
204 std::vector<mam::Map<T>> blocks;
205 std::size_t shift = 0;
206 for (std::size_t k = 0; k < pool.
pools[r].size(); ++k) {
207 const std::size_t t = pool.
pools[r][k];
208 const std::vector<lang::Distrib<T>>& sv = st.
server_types[t - 1].service;
210 if (r < sv.size() && !sv[r].disabled) {
216 "Server pool '" + st.
server_types[t - 1].name +
"' of station '" + name +
217 "' accepts class '" + cname +
"' but declares no law for it, and the "
218 "station's own service for the class is disabled.");
221 const std::size_t n = law.
D0.rows();
224 for (std::size_t j = 0; j < n; ++j) as += a[j];
225 for (std::size_t j = 0; j < n; ++j) a[j] = T(a[j] / as);
226 std::vector<T> ex(n, zero);
227 for (std::size_t i = 0; i < n; ++i)
228 for (std::size_t j = 0; j < n; ++j) ex[i] -= law.
D0(i, j);
229 pool.
off[r].push_back(shift);
230 pool.
len[r].push_back(n);
231 pool.
alpha[r].push_back(a);
232 pool.
exit[r].push_back(ex);
233 pool.
D0[r].push_back(law.
D0);
235 blocks.push_back(law);
238 Matrix<T> D0x(shift, shift, zero), D1x(shift, shift, zero);
239 std::vector<T> exx(shift, zero);
240 for (std::size_t k = 0; k < blocks.size(); ++k) {
241 const std::size_t o = pool.
off[r][k];
242 for (std::size_t i = 0; i < pool.
len[r][k]; ++i) {
243 exx[o + i] = pool.
exit[r][k][i];
244 for (std::size_t j = 0; j < pool.
len[r][k]; ++j) D0x(o + i, o + j) = blocks[k].D0(i, j);
249 for (std::size_t i = 0; i < shift; ++i)
250 for (std::size_t j = 0; j < pool.
len[r][0]; ++j) D1x(i, j) = T(exx[i] * pool.
alpha[r][0][j]);
257 sn.service[ist - 1][r] = d;
258 if (
sn.disabled.size() >= ist &&
sn.disabled[ist - 1].size() > r)
sn.disabled[ist - 1][r] =
false;
260 std::vector<std::size_t> ncls(NT, 0);
261 for (std::size_t t = 0; t < NT; ++t)
262 for (std::size_t r = 0; r < R; ++r) ncls[t] += pool.
compat[t][r] ? 1 : 0;
264 for (std::size_t t = 0; t < NT; ++t) pool.
alfsorder[t] = t + 1;
266 [&](std::size_t a, std::size_t b) { return ncls[a - 1] < ncls[b - 1]; });
268 for (std::size_t r = 0; r < R; ++r) multi = multi || pool.
pools[r].size() >= 2;
272 std::vector<std::size_t> p(NT);
273 for (std::size_t t = 0; t < NT; ++t) p[t] = t + 1;
275 pool.
perms.push_back(p);
276 }
while (std::next_permutation(p.begin(), p.end()));
277 sn.nvars[ind - 1][2 * R] = 1;
280 for (std::size_t t = 0; t < NT; ++t) total += pool.
count[t];
282 sn.ctmcpool[ind] = pool;
290 for (std::size_t ind = 1; ind <=
sn.nodes.size(); ++ind) {
291 const std::size_t ist =
sn.nodes[ind - 1].station;
292 if (ist == 0 ||
sn.stations[ist - 1].server_types.empty() ||
sn.ctmcpool.count(ind))
continue;
312 "SolverCTMC does not combine heterogeneous server pools with fork-join: the tag "
313 "augmentation adds sibling classes the pool compatibility does not name.");
316 for (std::size_t ind = 1; ind <= in.
nodes.size(); ++ind) {
317 const std::size_t ist = in.
nodes[ind - 1].station;
318 if (ist == 0 || in.
stations[ist - 1].server_types.empty() || in.
ctmcpool.count(ind))
continue;
323 if (sp == in.
statespace.end() || sp->second.rows() == 0)
continue;
324 if (sp->second.rows() > 1)
325 throw UnsupportedError(
"SolverCTMC cannot place a distribution over initial states on "
326 "the heterogeneous server pools of station '" +
327 in.
nodes[ind - 1].name +
"'.");
328 std::vector<T> row(sp->second.cols());
329 for (std::size_t c = 0; c < sp->second.cols(); ++c) row[c] = sp->second(0, c);
332 for (std::size_t r = 0; r < R; ++r) m(0, r) = mg.second[r];
341 const std::size_t ind =
sn.node_of_station(ist);
342 return ind != 0 && !
sn.ctmcpool.empty() &&
sn.ctmcpool.count(ind) != 0;
UnsupportedError(const std::string &what)
A network plus its refreshed NetworkStruct.
std::vector< Station< T > > stations
stations[k-1] is the k-th station
std::vector< NodeDef > nodes
every node, in creation order
std::map< std::size_t, CtmcPool< T > > ctmcpool
Heterogeneous server pools rewritten for the CTMC, by 1-based NODE index.
std::map< std::size_t, Matrix< T > > statespace
bool isfjaugmented
sn.isfjaugmented: this struct came out of fj_tag, so its Fork nodes are STATEFUL and its Join nodes c...
What refreshProcessRepresentations and refreshLST compute FROM a distribution: the (D0,...
The exception types the port throws.
Markovian arrival process descriptors: stationary vectors, rate, moments, autocorrelation and the ind...
Dense matrix and non-owning view.
void pool_station(qn::NetworkStruct< T > &sn, std::size_t ind, std::size_t ist)
Rewrite one station; the struct is the solver's private copy.
void refuse_features(const qn::NetworkStruct< T > &sn, std::size_t ind, std::size_t ist)
The features a pooled station cannot be combined with, sub_refuse_features of the reference.
void require_ph(const lang::Distrib< T > &d, const mam::Map< T > &m, const std::string &name, const std::string &cname, const std::string &what)
A renewal phase-type law is what a pool server can carry; anything else is refused by name.
bool ctmc_pools(const qn::NetworkStruct< T > &in, qn::NetworkStruct< T > &out)
Rewrite a copy of in into out when it has pooled stations; false, leaving out untouched,...
bool ctmc_pool_station(const qn::NetworkStruct< T > &sn, std::size_t ist)
Utilization at a pooled station: the mean number of busy servers of the class over the bank.
bool ctmc_has_pools(const qn::NetworkStruct< T > &sn)
True when the struct has a heterogeneous-server station that ctmc_pools rewrites.
mam::Map< T > dist_to_map(const Distrib< T > &d)
SchedStrategy
Scheduling disciplines, with the values of MATLAB SchedStrategy.
ProcessType
Distribution kinds, with the values of MATLAB ProcessType.
const char * sched_to_text(SchedStrategy s)
const char * process_to_text(ProcessType p)
The MATLAB ProcessType name, as sn.procid prints it.
void dist_refresh_moments(Distrib< T > &d)
Fill in the first two moments of a distribution given by its matrices.
T map_mean(const Map< T > &m)
Mean inter-arrival time, 1/lambda.
std::vector< T > map_pie(const Map< T > &m)
Phase distribution seen by an arriving job, pie = pi D1 / (pi D1 e).
std::pair< T, std::vector< T > > to_marginal_aggr(const NetworkStruct< T > &sn, std::size_t ind, const std::vector< T > &state_i)
Port of State.toMarginalAggr: the job counts of one node's state row, without the per-phase detail to...
Conservation laws of a layered queueing network, enumerated from its structure.
A queueing network and its refreshed NetworkStruct.
Port of the event half of MATLAB's +State package: the successor states an event produces at one node...
Matrix< T > D0
The (D0,D1) pair when the type carries one directly.
static constexpr double FineTol
static constexpr double CoarseTol
A MAP as the pair of matrices (D0, D1).
CTMC bookkeeping of a heterogeneous-server station, written by ctmc::ctmc_pools on the solver's priva...
std::vector< std::vector< std::size_t > > len
offset and length of block k of class r
std::vector< std::vector< std::vector< T > > > alpha
entry vector of block k of class r
std::vector< std::vector< std::vector< T > > > exit
exit rates of block k of class r
std::vector< std::vector< bool > > compat
compat[t-1][r-1]
std::vector< double > count
servers per pool
std::vector< std::size_t > alfsorder
pools by ascending class count (ALFS)
std::vector< std::vector< Matrix< T > > > D0
D0 of block k of class r.
std::vector< std::vector< std::size_t > > pools
pools[r-1]: compatible pools of r, ascending
lang::HeteroSchedPolicy policy
bool rotate
ALIS/FAIRNESS order is part of the state.
std::vector< std::vector< std::size_t > > off
std::vector< std::vector< double > > fsfrate
fsfrate[t-1][r-1] = 1/mean (FSF)
std::vector< std::vector< std::size_t > > perms
every pool order, identity first
One station of the network.
std::vector< BalkingParam > balking
std::vector< std::size_t > server_parallelism
Queue.setServerParallelism(class, n): the servers a job seizes for the whole of its service,...
double nservers
may be infinite (a Delay, or an inf-scheduled task)
CdScaling< T > jdscaling
sn.jdscaling for this station: MATLAB's Station.ljdScaling, the JOINT dependence map eta_i(n),...
std::vector< T > lldscaling
sn.lldscaling for this station: the multiplier at population 1, 2, ... Empty when the station is not ...
std::vector< lang::ImpatienceType > impatience
lang::HeteroSchedPolicy hetero_policy
CdScaling< T > cdscaling
sn.cdscaling for this station: the class-dependence map, empty when unset.
std::vector< ServerType > server_types