5#ifndef LINE_API_MDD_MDD_PS_H
6#define LINE_API_MDD_MDD_PS_H
57inline std::vector<std::vector<int>> ps_compositions(std::size_t h,
int N) {
58 std::vector<std::vector<int>> out;
60 for (
int n = 0; n <= N; ++n) out.push_back(std::vector<int>(1, n));
63 const std::vector<std::vector<int>> sub = ps_compositions(h - 1, N);
64 for (
int v1 = 0; v1 <= N; ++v1) {
65 for (std::size_t r = 0; r < sub.
size(); ++r) {
67 for (std::size_t a = 0; a < sub[r].
size(); ++a) s += sub[r][a];
68 if (s > N - v1)
continue;
71 row.insert(row.end(), sub[r].begin(), sub[r].end());
80T ps_share(
int n,
double srv) {
81 const T zero = num_traits<T>::from_int(0), one = num_traits<T>::from_int(1);
82 if (n == 0)
return zero;
83 if (std::isinf(srv))
return one;
84 return T(one / num_traits<T>::from_int(n));
87inline std::size_t ps_lookup(
const std::map<std::vector<int>, std::size_t>& lut,
88 const std::vector<int>& v) {
89 const std::map<std::vector<int>, std::size_t>::const_iterator it = lut.find(v);
91 throw InputError(
"mdd_ps: a per-phase count vector left the composition state space");
96MddLocalMatrix<T> ps_internal(
const Matrix<T>& D0i,
const std::vector<std::vector<int>>& C,
97 const std::map<std::vector<int>, std::size_t>& lut, std::size_t h,
98 std::size_t d,
double srv) {
99 const T zero = num_traits<T>::from_int(0);
100 typename MddLocalMatrix<T>::Builder bld(d);
101 for (std::size_t r = 0; r < d; ++r) {
102 const std::vector<int>& v = C[r];
104 for (std::size_t a = 0; a < h; ++a) n += v[a];
105 if (n == 0)
continue;
106 const T sc = ps_share<T>(n, srv);
107 for (std::size_t a = 0; a < h; ++a) {
108 if (v[a] == 0)
continue;
109 for (std::size_t b = 0; b < h; ++b) {
110 if (a == b || D0i(a, b) == zero)
continue;
111 std::vector<int> w = v;
114 bld.add(r, ps_lookup(lut, w),
115 T(num_traits<T>::from_int(v[a]) * D0i(a, b) * sc));
123MddLocalMatrix<T> ps_departure(
const Matrix<T>& D1i,
const std::vector<std::vector<int>>& C,
124 const std::map<std::vector<int>, std::size_t>& lut, std::size_t h,
125 std::size_t d,
double srv,
const T& pr) {
126 const T zero = num_traits<T>::from_int(0);
127 std::vector<T> t(h, zero);
128 for (std::size_t a = 0; a < h; ++a) {
130 for (std::size_t b = 0; b < h; ++b) s += D1i(a, b);
133 typename MddLocalMatrix<T>::Builder bld(d);
134 for (std::size_t r = 0; r < d; ++r) {
135 const std::vector<int>& v = C[r];
137 for (std::size_t a = 0; a < h; ++a) n += v[a];
138 if (n == 0)
continue;
139 const T sc = ps_share<T>(n, srv);
140 for (std::size_t a = 0; a < h; ++a) {
141 if (v[a] == 0 || t[a] == zero)
continue;
142 std::vector<int> w = v;
144 bld.add(r, ps_lookup(lut, w),
145 T(num_traits<T>::from_int(v[a]) * t[a] * sc * pr));
152MddLocalMatrix<T> ps_arrival(
const std::vector<T>& pieb,
const std::vector<std::vector<int>>& C,
153 const std::map<std::vector<int>, std::size_t>& lut, std::size_t h,
154 int N, std::size_t d) {
155 const T zero = num_traits<T>::from_int(0);
156 typename MddLocalMatrix<T>::Builder bld(d);
157 for (std::size_t r = 0; r < d; ++r) {
158 const std::vector<int>& v = C[r];
160 for (std::size_t a = 0; a < h; ++a) n += v[a];
161 if (n >= N)
continue;
162 for (std::size_t b = 0; b < h; ++b) {
163 if (pieb[b] == zero)
continue;
164 std::vector<int> w = v;
166 bld.add(r, ps_lookup(lut, w), pieb[b]);
173std::vector<std::vector<int>> ps_successors(
174 const std::vector<int>& s,
const std::vector<Matrix<T>>& D0,
const std::vector<Matrix<T>>& D1,
175 const std::vector<std::vector<T>>& pie,
const std::vector<std::size_t>& h,
176 const std::vector<std::vector<std::vector<int>>>& comp,
177 const std::vector<std::map<std::vector<int>, std::size_t>>& lut,
int N,
const Matrix<T>& P) {
178 const T zero = num_traits<T>::from_int(0);
179 const std::size_t K = s.size();
180 std::vector<std::vector<int>> out;
181 for (std::size_t i = 0; i < K; ++i) {
182 const std::vector<int>& v = comp[i][
static_cast<std::size_t
>(s[i])];
184 for (std::size_t a = 0; a < h[i]; ++a) n += v[a];
185 if (n == 0)
continue;
187 for (std::size_t a = 0; a < h[i]; ++a) {
188 if (v[a] == 0)
continue;
189 for (std::size_t b = 0; b < h[i]; ++b) {
190 if (a == b || D0[i](a, b) == zero)
continue;
191 std::vector<int> w = v;
194 std::vector<int> t = s;
195 t[i] =
static_cast<int>(ps_lookup(lut[i], w));
200 for (std::size_t a = 0; a < h[i]; ++a) {
201 if (v[a] == 0)
continue;
203 for (std::size_t b = 0; b < h[i]; ++b) ta += D1[i](a, b);
204 if (ta == zero)
continue;
205 std::vector<int> w = v;
207 for (std::size_t j = 0; j < K; ++j) {
208 if (j == i || !(P(i, j) > zero))
continue;
209 const std::vector<int>& vj = comp[j][
static_cast<std::size_t
>(s[j])];
211 for (std::size_t b = 0; b < h[j]; ++b) nj += vj[b];
212 if (nj >= N)
continue;
213 for (std::size_t b = 0; b < h[j]; ++b) {
214 if (pie[j][b] == zero)
continue;
215 std::vector<int> wj = vj;
217 std::vector<int> t = s;
218 t[i] =
static_cast<int>(ps_lookup(lut[i], w));
219 t[j] =
static_cast<int>(ps_lookup(lut[j], wj));
243 const std::vector<double>& servers,
int N,
247 const std::size_t K = mu.size();
248 if (K == 0)
throw InputError(
"mdd_ps: the network has no stations");
250 throw InputError(
"mdd_ps: the routing matrix is not (K x K)");
251 if (servers.size() != K)
throw InputError(
"mdd_ps: one server count per station is required");
253 std::vector<Matrix<T>> D0(K), D1(K);
254 std::vector<std::vector<T>> entry(K);
255 std::vector<std::size_t> h(K, 1);
257 for (std::size_t i = 0; i < K; ++i) {
258 if (!(servers[i] == 1 || std::isinf(servers[i])))
259 throw InputError(
"mdd_ps: station " + std::to_string(i + 1) +
" has " +
260 std::to_string(servers[i]) +
261 " servers; only processor sharing (1) and infinite server have a "
262 "per-phase-count encoding here");
263 const bool has_law = i < proc.size() && proc[i].present;
267 entry[i] = std::vector<T>(1, one);
273 h[i] = proc[i].phases();
274 entry[i] =
mdd_entry_law(proc[i].pie, D1[i], h[i], i,
"mdd_ps");
278 std::vector<std::vector<std::vector<int>>> comp(K);
279 std::vector<std::map<std::vector<int>, std::size_t>> lut(K);
280 std::vector<int> d(K, 0);
281 for (std::size_t i = 0; i < K; ++i) {
282 comp[i] = detail::ps_compositions(h[i], N);
283 for (std::size_t r = 0; r < comp[i].size(); ++r) lut[i][comp[i][r]] = r;
284 d[i] =
static_cast<int>(comp[i].size());
295 desc.
valuemap.assign(K, std::vector<double>());
296 for (std::size_t i = 0; i < K; ++i) {
297 desc.
valuemap[i].assign(
static_cast<std::size_t
>(d[i]), 0.0);
298 for (std::size_t r = 0; r < static_cast<std::size_t>(d[i]); ++r) {
300 for (std::size_t a = 0; a < h[i]; ++a) s += comp[i][r][a];
301 desc.
valuemap[i][r] =
static_cast<double>(s);
306 desc.
init.assign(K, 0);
307 for (std::size_t i = 0; i < K; ++i) {
308 std::vector<int> v(h[i], 0);
310 std::size_t first = 0;
311 for (std::size_t a = 0; a < h[0]; ++a)
312 if (entry[0][a] > zero) {
318 desc.
init[i] =
static_cast<int>(detail::ps_lookup(lut[i], v));
321 const std::vector<Matrix<T>> fD0 = D0, fD1 = D1;
322 const std::vector<std::vector<T>> fpie = entry;
323 const std::vector<std::size_t> fh = h;
324 const std::vector<std::vector<std::vector<int>>> fcomp = comp;
325 const std::vector<std::map<std::vector<int>, std::size_t>> flut = lut;
328 desc.
nextfun = [fD0, fD1, fpie, fh, fcomp, flut, fN, fP](
const std::vector<int>& state) {
329 return detail::ps_successors(state, fD0, fD1, fpie, fh, fcomp, flut, fN, fP);
333 for (std::size_t i = 0; i < K; ++i) {
334 if (h[i] == 1)
continue;
336 static_cast<std::size_t
>(d[i]),
338 if (Wi.
nnz == 0)
continue;
344 desc.
events.push_back(ev);
346 for (std::size_t a = 0; a < K; ++a)
347 for (std::size_t b = 0; b < K; ++b) {
348 if (a == b || !(P(a, b) > zero))
continue;
354 ev.
W.push_back(detail::ps_departure(D1[a], comp[a], lut[a], h[a],
355 static_cast<std::size_t
>(d[a]), servers[a],
357 ev.
W.push_back(detail::ps_arrival(entry[b], comp[b], lut[b], h[b], N,
358 static_cast<std::size_t
>(d[b])));
359 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_ps(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 > >())
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.