5#ifndef LINE_API_MDD_MDD_DESCRIPTOR_H
6#define LINE_API_MDD_MDD_DESCRIPTOR_H
71inline bool desc_is_preemptive_resume(
const std::string& nm) {
72 return nm ==
"LCFSPR" || nm ==
"FCFSPR" || nm ==
"LCFSPRPRIO" || nm ==
"FCFSPRPRIO";
76inline bool desc_is_shared_server(
const std::string& nm) {
77 return nm ==
"PS" || nm ==
"DPS" || nm ==
"GPS";
80inline std::string desc_upper(
const std::string& s) {
82 for (std::size_t i = 0; i < out.size(); ++i)
83 if (out[i] >=
'a' && out[i] <=
'z') out[i] =
static_cast<char>(out[i] -
'a' +
'A');
88inline int desc_idx(
int n,
int a, std::size_t h) {
90 return 1 + (n - 1) *
static_cast<int>(h) + (a - 1);
94inline int desc_population(
int index, std::size_t h) {
95 if (index == 0)
return 0;
96 return (index - 1) /
static_cast<int>(h) + 1;
100inline int desc_phase(
int index, std::size_t h) {
101 if (index == 0)
return 0;
102 return (index - 1) %
static_cast<int>(h) + 1;
107MddLocalMatrix<T> desc_internal(
const Matrix<T>& D0i, std::size_t h,
int N,
int d) {
108 const T zero = num_traits<T>::from_int(0);
109 typename MddLocalMatrix<T>::Builder bld(
static_cast<std::size_t
>(d));
110 for (
int n = 1; n <= N; ++n)
111 for (std::size_t a = 1; a <= h; ++a)
112 for (std::size_t b = 1; b <= h; ++b) {
113 if (a == b || D0i(a - 1, b - 1) == zero)
continue;
114 bld.add(
static_cast<std::size_t
>(desc_idx(n,
static_cast<int>(a), h)),
115 static_cast<std::size_t
>(desc_idx(n,
static_cast<int>(b), h)),
123MddLocalMatrix<T> desc_departure(
const Matrix<T>& D1i,
const std::vector<T>& piei, std::size_t h,
124 int N,
int d,
const T& mui,
double srv,
const T& pr) {
125 const T zero = num_traits<T>::from_int(0);
126 typename MddLocalMatrix<T>::Builder bld(
static_cast<std::size_t
>(d));
129 for (
int n = 1; n <= N; ++n) {
130 const double cap =
static_cast<double>(n) < srv ?
static_cast<double>(n) : srv;
131 bld.add(
static_cast<std::size_t
>(n),
static_cast<std::size_t
>(n - 1),
132 T(mui * num_traits<T>::from_double(cap) * pr));
135 std::vector<T> t(h, zero);
136 for (std::size_t a = 0; a < h; ++a) {
138 for (std::size_t b = 0; b < h; ++b) s += D1i(a, b);
141 for (
int n = 1; n <= N; ++n)
142 for (std::size_t a = 1; a <= h; ++a) {
143 if (t[a - 1] == zero)
continue;
145 bld.add(
static_cast<std::size_t
>(desc_idx(1,
static_cast<int>(a), h)), 0,
148 for (std::size_t b = 1; b <= h; ++b) {
149 if (piei[b - 1] == zero)
continue;
150 bld.add(
static_cast<std::size_t
>(desc_idx(n,
static_cast<int>(a), h)),
151 static_cast<std::size_t
>(
152 desc_idx(n - 1,
static_cast<int>(b), h)),
153 T(t[a - 1] * pr * piei[b - 1]));
163MddLocalMatrix<T> desc_arrival(
const std::vector<T>& pieb, std::size_t h,
int N,
int d) {
164 const T zero = num_traits<T>::from_int(0), one = num_traits<T>::from_int(1);
165 typename MddLocalMatrix<T>::Builder bld(
static_cast<std::size_t
>(d));
167 for (
int m = 0; m < N; ++m)
168 bld.add(
static_cast<std::size_t
>(m),
static_cast<std::size_t
>(m + 1), one);
170 for (std::size_t b = 1; b <= h; ++b) {
171 if (pieb[b - 1] == zero)
continue;
172 bld.add(0,
static_cast<std::size_t
>(desc_idx(1,
static_cast<int>(b), h)), pieb[b - 1]);
174 for (
int m = 1; m < N; ++m)
175 for (std::size_t c = 1; c <= h; ++c)
176 bld.add(
static_cast<std::size_t
>(desc_idx(m,
static_cast<int>(c), h)),
177 static_cast<std::size_t
>(desc_idx(m + 1,
static_cast<int>(c), h)), one);
184std::vector<std::vector<int>> desc_successors(
const std::vector<int>& s,
185 const std::vector<Matrix<T>>& D0,
186 const std::vector<Matrix<T>>& D1,
187 const std::vector<std::vector<T>>& pie,
188 const std::vector<std::size_t>& h,
189 const Matrix<T>& P) {
190 const T zero = num_traits<T>::from_int(0), one = num_traits<T>::from_int(1);
191 const std::size_t K = s.size();
192 std::vector<std::vector<int>> out;
193 for (std::size_t i = 0; i < K; ++i) {
194 const int ni = desc_population(s[i], h[i]);
195 const int ai = desc_phase(s[i], h[i]);
196 if (ni == 0)
continue;
199 for (std::size_t b = 1; b <= h[i]; ++b) {
200 if (
static_cast<int>(b) == ai || D0[i](ai - 1, b - 1) == zero)
continue;
201 std::vector<int> t = s;
202 t[i] = desc_idx(ni,
static_cast<int>(b), h[i]);
210 for (std::size_t b = 0; b < h[i]; ++b) exits += D1[i](ai - 1, b);
212 if (exits == zero)
continue;
213 for (std::size_t j = 0; j < K; ++j) {
214 if (j == i || !(P(i, j) > zero))
continue;
215 const int nj = desc_population(s[j], h[j]);
216 const int aj = desc_phase(s[j], h[j]);
217 int newi = (h[i] == 1) ? desc_idx(ni - 1, 1, 1) : 0;
218 for (std::size_t bi = 1; bi <= h[i]; ++bi) {
222 }
else if (pie[i][bi - 1] == zero) {
225 newi = desc_idx(ni - 1,
static_cast<int>(bi), h[i]);
230 for (std::size_t bj = 1; bj <= h[j]; ++bj) {
233 if (pie[j][bj - 1] == zero)
continue;
234 newj = desc_idx(1,
static_cast<int>(bj), h[j]);
238 newj = desc_idx(nj + 1, aj, h[j]);
240 std::vector<int> t = s;
245 if (ni == 1 && h[i] > 1)
break;
270 const std::vector<double>& servers,
int N,
273 const std::vector<std::string>& sched =
274 std::vector<std::string>()) {
276 const std::size_t K = mu.size();
277 if (K == 0)
throw InputError(
"mdd_descriptor: the network has no stations");
279 throw InputError(
"mdd_descriptor: the routing matrix is not (K x K)");
280 if (servers.size() != K)
281 throw InputError(
"mdd_descriptor: one server count per station is required");
282 if (N < 0)
throw InputError(
"mdd_descriptor: the population must be non-negative");
284 std::vector<Matrix<T>> D0(K), D1(K);
285 std::vector<std::vector<T>> entry(K);
286 std::vector<std::size_t> h(K, 1);
288 for (std::size_t i = 0; i < K; ++i) {
289 const bool has_law = i < proc.size() && proc[i].present;
293 entry[i] = std::vector<T>(1, one);
299 h[i] = proc[i].phases();
300 entry[i] =
mdd_entry_law(proc[i].pie, D1[i], h[i], i,
"mdd_descriptor");
301 if (h[i] > 1 && servers[i] != 1)
302 throw InputError(
"mdd_descriptor: station " + std::to_string(i + 1) +
303 " has a phase-type service law and " + std::to_string(servers[i]) +
304 " servers; a multi-server or delay station would have to count jobs "
305 "per phase rather than name the phase of one job in service, which "
306 "this encoding does not carry");
307 if (h[i] > 1 && i < sched.size() && !sched[i].empty()) {
308 const std::string nm = detail::desc_upper(sched[i]);
309 if (detail::desc_is_preemptive_resume(nm))
310 throw InputError(
"mdd_descriptor: station " + std::to_string(i + 1) +
311 " combines a phase-type service law with a preemptive-resume "
312 "discipline; the suspended jobs' phases would have to be stacked "
313 "in the local state, which this encoding does not carry, and the "
314 "descriptor would silently model the non-preemptive chain "
316 if (detail::desc_is_shared_server(nm))
317 throw InputError(
"mdd_descriptor: station " + std::to_string(i + 1) +
318 " combines a phase-type service law with a shared-server "
319 "discipline; every job present is in service and holds its own "
320 "phase, which this encoding does not carry. Use mdd_ps, whose "
321 "local state is the per-phase count vector.");
325 std::vector<int> d(K, 0);
326 for (std::size_t i = 0; i < K; ++i) d[i] = 1 + N * static_cast<int>(h[i]);
338 desc.
valuemap.assign(K, std::vector<double>());
339 for (std::size_t i = 0; i < K; ++i) {
340 desc.
valuemap[i].assign(
static_cast<std::size_t
>(d[i]), 0.0);
341 for (
int n = 1; n <= N; ++n)
342 for (std::size_t a = 0; a < h[i]; ++a)
343 desc.
valuemap[i][
static_cast<std::size_t
>(1 + (n - 1) *
static_cast<int>(h[i]) +
344 static_cast<int>(a))] =
345 static_cast<double>(n);
349 desc.
init.assign(K, 0);
350 std::size_t first_phase = 0;
351 for (std::size_t a = 0; a < h[0]; ++a)
352 if (entry[0][a] > zero) {
356 desc.
init[0] = detail::desc_idx(N,
static_cast<int>(first_phase) + 1, h[0]);
358 const std::vector<Matrix<T>> fD0 = D0, fD1 = D1;
359 const std::vector<std::vector<T>> fpie = entry;
360 const std::vector<std::size_t> fh = h;
362 desc.
nextfun = [fD0, fD1, fpie, fh, fP](
const std::vector<int>& state) {
363 return detail::desc_successors(state, fD0, fD1, fpie, fh, fP);
367 for (std::size_t i = 0; i < K; ++i) {
368 if (h[i] == 1)
continue;
370 if (Wi.
nnz == 0)
continue;
376 desc.
events.push_back(ev);
379 for (std::size_t a = 0; a < K; ++a)
380 for (std::size_t b = 0; b < K; ++b) {
381 if (a == b || !(P(a, b) > zero))
continue;
387 ev.
W.push_back(detail::desc_departure(D1[a], entry[a], h[a], N, d[a], mu[a],
388 servers[a], P(a, b)));
389 ev.
W.push_back(detail::desc_arrival(entry[b], h[b], N, d[b]));
390 desc.
events.push_back(ev);
The exception types the port throws.
Dense matrix and non-owning view.
The rate side of the decision-diagram domain: local matrices, events, the Kronecker descriptor,...
std::vector< T > mdd_entry_law(const std::vector< T > &given, const Matrix< T > &D1, std::size_t h, std::size_t i, const std::string &caller)
Entry law of a phase-type station, taken as given or derived from D1.
MddDescriptor< T > mdd_descriptor(const std::vector< T > &mu, const Matrix< T > &P, const std::vector< double > &servers, int N, const std::vector< MddServiceLaw< T > > &proc=std::vector< MddServiceLaw< T > >(), const std::vector< std::string > &sched=std::vector< std::string >())
Build the descriptor.
Number-type abstraction for the templated API port.
Kronecker rate descriptor of a structured model, the input of mdd_mcd.
MddNextState nextfun
Successor function over local indices.
std::vector< int > domain
Local domain per level.
std::vector< std::vector< double > > valuemap
valuemap[i][idx] is the physical occupancy of level i in local state idx.
std::vector< double > servers
Servers per station; infinite for a delay station.
std::vector< std::size_t > nphases
Phases per station, 1 when exponential.
int N
Closed population; the conservation law the level marginals must satisfy.
std::vector< T > mu
Station service rates, 1/E[S]; empty for a descriptor with no queueing parameters.
std::vector< int > init
Initial local index per level.
std::vector< MddEvent< T > > events
The events of the descriptor.
std::size_t K
Number of levels, i.e.
Matrix< T > P
Station-to-station routing matrix.
One event of the Kronecker rate descriptor.
std::size_t b
Station (or mode) the event arrives at, 0-based; equals a for an internal event.
std::size_t a
Station (or transition node) the event departs from, 0-based.
std::vector< MddLocalMatrix< T > > W
Local matrices at the levels named by lev.
std::vector< std::size_t > lev
Levels the event touches, as 0-based level indices, aligned with W.
A local rate matrix W_k^e of the Kronecker descriptor, held row-compressed.
std::size_t nnz
Total number of stored nonzeros.
Phase-type service law of one station, as a Markovian (D0,D1) pair.