5#ifndef LINE_SOLVERS_FLUID_PETRI_TERMS_H
6#define LINE_SOLVERS_FLUID_PETRI_TERMS_H
90 std::vector<double>
d1;
91 std::vector<double>
pie;
99 std::vector<std::size_t>
zblk;
122 std::vector<std::vector<std::ptrdiff_t>>
pidx;
139 std::vector<std::pair<std::size_t, std::size_t>>
cov_pairs;
148 std::vector<double>
x0;
152namespace terms_detail {
156inline std::vector<std::vector<double>> pad(
const Matrix<T>& A, std::size_t I, std::size_t K,
158 std::vector<std::vector<double>> out(I, std::vector<double>(K, fill));
159 for (std::size_t i = 0; i < I && i < A.
rows(); ++i)
160 for (std::size_t k = 0; k < K && k < A.
cols(); ++k)
166inline void add_pair(std::map<std::pair<std::size_t, std::size_t>, std::size_t>& key,
167 std::vector<std::pair<std::size_t, std::size_t>>& pairs, std::size_t a,
169 const std::pair<std::size_t, std::size_t> k(std::min(a, b), std::max(a, b));
170 if (key.find(k) != key.end())
return;
171 key[k] = pairs.size();
183 std::size_t ind, std::size_t m, PetriTerms& t, std::size_t nm) {
190 : (
"Mode" + std::to_string(m + 1));
191 rec.label = t.names_node[ind - 1] +
"." + mname;
193 const std::vector<std::vector<double>> en = pad(tp.
enabling[m], t.I, t.K, 0.0);
194 const std::vector<std::vector<double>> fir = pad(tp.
firing[m], t.I, t.K, 0.0);
195 const std::vector<std::vector<double>> inh =
196 pad(tp.
inhibiting[m], t.I, t.K, std::numeric_limits<double>::infinity());
198 std::vector<double> cvec(nm, 0.0);
199 for (std::size_t p = 0; p < t.I; ++p)
200 for (std::size_t k = 0; k < t.K; ++k) {
201 const double w = en[p][k];
202 if (!(w > 0))
continue;
204 throw InputError(
"fluid_petri_terms: mode " + rec.label +
205 " has a non-finite enabling arc weight at " + t.names_node[p] +
206 ". An arc that no marking can satisfy disables the mode; declare "
207 "a finite multiplicity");
208 if (t.pidx[p][k] < 0)
209 throw InputError(
"fluid_petri_terms: mode " + rec.label +
210 " takes an enabling arc from " + t.names_node[p] +
211 ", which is not a Place");
212 const std::size_t s =
static_cast<std::size_t
>(t.pidx[p][k]);
213 rec.arc_slot.push_back(s);
214 rec.arc_w.push_back(w);
217 for (std::size_t p = 0; p < t.I; ++p)
218 for (std::size_t k = 0; k < t.K; ++k) {
222 if (fir[p][k] <= 0 || t.pidx[p][k] < 0)
continue;
223 cvec[
static_cast<std::size_t
>(t.pidx[p][k])] += fir[p][k];
225 for (std::size_t p = 0; p < t.I; ++p)
226 for (std::size_t k = 0; k < t.K; ++k) {
227 const double th = inh[p][k];
230 if (std::isinf(th) || th <= 0 || t.pidx[p][k] < 0)
continue;
231 rec.inh_slot.push_back(
static_cast<std::size_t
>(t.pidx[p][k]));
232 rec.inh_thr.push_back(th);
237 rec.c = std::isnan(c) ? 1.0 : c;
242 const std::function<T(
const std::vector<T>&)> g = tp.
firingdep[m];
243 const std::size_t I = t.I, K = t.K;
244 rec.dep = [g, I, K](
const Matrix<double>& mm) ->
double {
245 std::vector<T> flat(I * K, num_traits<T>::from_int(0));
246 for (std::size_t i = 0; i < I; ++i)
247 for (std::size_t k = 0; k < K; ++k)
248 flat[i * K + k] = num_traits<T>::from_double(mm(i, k));
249 return num_traits<T>::to_double(g(flat));
257 rec.d1.assign(1, 0.0);
258 rec.pie.assign(1, 1.0);
259 rec.closable =
false;
264 throw InputError(
"fluid_petri_terms: mode " + rec.label +
265 " has no Markovian firing process. The renewal families are converted "
266 "to phase type before the solver runs, so this is a distribution the "
267 "fluid Petri route cannot time; use SolverCTMC or SolverLDES");
268 const lang::Distrib<T>& fp = tp.
firingproc[m];
269 Matrix<double> D0, D1;
270 if (fp.D0.rows() > 0 && fp.D1.rows() > 0) {
271 D0 = Matrix<double>(fp.D0.rows(), fp.D0.cols(), 0.0);
272 for (std::size_t i = 0; i < D0.rows(); ++i)
273 for (std::size_t j = 0; j < D0.cols(); ++j)
274 D0(i, j) = num_traits<T>::to_double(fp.D0(i, j));
275 D1 = Matrix<double>(fp.D1.rows(), fp.D1.cols(), 0.0);
276 for (std::size_t i = 0; i < D1.rows(); ++i)
277 for (std::size_t j = 0; j < D1.cols(); ++j)
278 D1(i, j) = num_traits<T>::to_double(fp.D1(i, j));
281 const double mean = num_traits<T>::to_double(fp.mean);
283 throw InputError(
"fluid_petri_terms: mode " + rec.label +
284 " has a non-positive mean firing time");
285 D0 = Matrix<double>(1, 1, -1.0 / mean);
286 D1 = Matrix<double>(1, 1, 1.0 / mean);
291 rec.d1.assign(rec.nph, 0.0);
292 for (std::size_t h = 0; h < rec.nph; ++h) {
294 for (std::size_t hp = 0; hp < D1.cols(); ++hp) acc += D1(h, hp);
297 rec.pie.assign(std::max<std::size_t>(rec.nph, 1), 0.0);
300 for (std::size_t h = 0; h < rec.nph && h < fp.params.size(); ++h) {
301 rec.pie[h] = num_traits<T>::to_double(fp.params[h]);
305 std::fill(rec.pie.begin(), rec.pie.end(), 0.0);
308 for (std::size_t h = 0; h < rec.nph; ++h) rec.pie[h] /= tot;
313 rec.closable = !(rec.arc_slot.size() <= 1 && std::isinf(rec.c));
327 t.
I =
sn.nodes.size();
331 for (std::size_t i = 0; i < t.
I; ++i) t.
names_node[i] =
sn.nodes[i].name;
332 for (std::size_t i = 1; i <= t.
I; ++i) {
341 t.
m0full.assign(t.
I, std::vector<double>(t.
K, 0.0));
342 for (std::size_t pi = 0; pi < t.
places.size(); ++pi) {
343 const std::size_t ind = t.
places[pi];
344 const typename std::map<std::size_t, std::vector<T>>::const_iterator im =
345 sn.initmarking.find(ind);
346 if (im ==
sn.initmarking.end())
continue;
347 for (std::size_t k = 0; k < t.
K && k < im->second.size(); ++k) {
351 " holds an infinite initial marking of class " +
352 std::to_string(k + 1));
356 bool any_declared =
false;
357 for (std::size_t pi = 0; pi < t.
places.size() && !any_declared; ++pi)
358 any_declared = (
sn.initmarking.find(t.
places[pi]) !=
sn.initmarking.end());
360 for (std::size_t r = 0; r <
sn.classes.size(); ++r) {
362 if (!(njobs > 0.0) || !std::isfinite(njobs))
continue;
363 if (
sn.classes[r].refstat < 1 ||
sn.classes[r].refstat >
sn.station_to_node.size())
365 const std::size_t ref_node =
sn.station_to_node[
sn.classes[r].refstat - 1];
366 if (ref_node >= 1 && ref_node <= t.
I &&
368 t.
m0full[ref_node - 1][r] += njobs;
373 std::vector<std::vector<bool>> touched(t.
I, std::vector<bool>(t.
K,
false));
374 for (std::size_t ti = 0; ti < t.
transitions.size(); ++ti) {
376 const typename std::map<std::size_t, qn::TransitionParam<T>>::const_iterator it =
377 sn.transparam.find(ind);
378 if (it ==
sn.transparam.end())
continue;
380 for (std::size_t m = 0; m < tp.
nmodes; ++m) {
381 const std::vector<std::vector<double>> en =
382 terms_detail::pad(tp.
enabling[m], t.
I, t.
K, 0.0);
383 const std::vector<std::vector<double>> fir =
384 terms_detail::pad(tp.
firing[m], t.
I, t.
K, 0.0);
385 const std::vector<std::vector<double>> inh = terms_detail::pad(
386 tp.
inhibiting[m], t.
I, t.
K, std::numeric_limits<double>::infinity());
387 for (std::size_t i = 0; i < t.
I; ++i)
388 for (std::size_t k = 0; k < t.
K; ++k)
389 if (en[i][k] > 0 || fir[i][k] != 0 ||
390 (!std::isinf(inh[i][k]) && inh[i][k] > 0))
391 touched[i][k] =
true;
397 std::size_t snd, r, qnd, l;
399 std::vector<SrcArr> src_arr;
400 for (std::size_t ind = 1; ind <= t.
I; ++ind) {
402 const std::size_t ist =
sn.nodes[ind - 1].station;
403 if (ist < 1)
continue;
404 for (std::size_t r = 0; r < t.
K; ++r) {
406 if (std::isnan(lambda) || lambda <= 0)
continue;
407 for (std::size_t pi = 0; pi < t.
places.size(); ++pi) {
408 const std::size_t jnd = t.
places[pi];
409 for (std::size_t s = 0; s < t.
K; ++s) {
411 sn.rtnodes((ind - 1) * t.
K + r, (jnd - 1) * t.
K + s));
413 touched[jnd - 1][s] =
true;
419 src_arr.push_back(a);
426 t.
pidx.assign(t.
I, std::vector<std::ptrdiff_t>(t.
K, -1));
428 for (std::size_t pi = 0; pi < t.
places.size(); ++pi) {
429 const std::size_t ind = t.
places[pi];
430 for (std::size_t k = 0; k < t.
K; ++k) {
431 if (!(touched[ind - 1][k] || t.
m0full[ind - 1][k] > 0))
continue;
432 t.
pidx[ind - 1][k] =
static_cast<std::ptrdiff_t
>(nm);
435 t.
coord_station.push_back(
static_cast<std::ptrdiff_t
>(
sn.nodes[ind - 1].station) - 1);
442 std::size_t nstate = nm;
443 for (std::size_t ti = 0; ti < t.
transitions.size(); ++ti) {
445 const typename std::map<std::size_t, qn::TransitionParam<T>>::const_iterator it =
446 sn.transparam.find(ind);
447 if (it ==
sn.transparam.end())
continue;
448 for (std::size_t m = 0; m < it->second.nmodes; ++m) {
449 PetriMode rec = terms_detail::build_mode(
sn, it->second, ind, m, t, nm);
452 for (std::size_t h = 0; h < rec.
nph; ++h) rec.
zblk[h] = nstate + h;
455 t.
modes.push_back(rec);
461 for (std::size_t j = 0; j < t.
modes.size(); ++j)
462 if (t.
modes[j].cvec.size() < nstate) t.
modes[j].cvec.resize(nstate, 0.0);
463 for (std::size_t j = 0; j < t.
modes.size(); ++j) {
469 std::vector<std::vector<double>> cols;
470 std::vector<double> rate_base;
471 std::vector<int> ev_kind, ev_mode, ev_phase, ev_to;
472 std::vector<std::ptrdiff_t> ev_station, ev_class;
473 const auto emit = [&](
const std::vector<double>& col,
double base,
int kind,
int mode,
474 int phase,
int to, std::ptrdiff_t station, std::ptrdiff_t cls) {
476 rate_base.push_back(base);
477 ev_kind.push_back(kind);
478 ev_mode.push_back(mode);
479 ev_phase.push_back(phase);
481 ev_station.push_back(station);
482 ev_class.push_back(cls);
485 for (std::size_t q = 0; q < t.
timed_idx.size(); ++q) {
489 emit(md.
cvec, md.
d1[0], 1,
static_cast<int>(j), 0, 0, -1, -1);
491 for (std::size_t h = 0; h < md.
nph; ++h)
492 for (std::size_t hp = 0; hp < md.
nph; ++hp) {
493 const double w = md.
D1(h, hp);
494 if (w <= 0)
continue;
495 std::vector<double> col = md.
cvec;
496 col[md.
zblk[hp]] += 1.0;
497 col[md.
zblk[h]] -= 1.0;
498 emit(col, w, 1,
static_cast<int>(j),
static_cast<int>(h),
499 static_cast<int>(hp), -1, -1);
501 for (std::size_t h = 0; h < md.
nph; ++h)
502 for (std::size_t hp = 0; hp < md.
nph; ++hp) {
503 if (hp == h)
continue;
504 const double w = md.
D0(h, hp);
505 if (w <= 0)
continue;
506 std::vector<double> col(nstate, 0.0);
507 col[md.
zblk[hp]] = 1.0;
508 col[md.
zblk[h]] = -1.0;
509 emit(col, w, 2,
static_cast<int>(j),
static_cast<int>(h),
510 static_cast<int>(hp), -1, -1);
518 for (std::size_t q = 0; q < t.
timed_idx.size(); ++q) {
521 if (md.
nph <= 1)
continue;
522 std::vector<double> col(nstate, 0.0);
523 for (std::size_t h = 0; h < md.
nph; ++h) col[md.
zblk[h]] = md.
pie[h];
524 emit(col, 1.0, 5,
static_cast<int>(j), 0, 0, -1, -1);
526 for (std::size_t q = 0; q < t.
imm_idx.size(); ++q) {
527 const std::size_t j = t.
imm_idx[q];
528 emit(t.
modes[j].cvec, 1.0, 4,
static_cast<int>(j), 0, 0, -1, -1);
530 for (std::size_t a = 0; a < src_arr.size(); ++a) {
531 const SrcArr& rec = src_arr[a];
532 const std::size_t ist =
sn.nodes[rec.snd - 1].station;
533 if (ist >= 1 && ist <=
sn.stations.size()) {
537 "fluid_petri_terms: source " + t.
names_node[rec.snd - 1] +
538 " has a non-exponential arrival for class " + std::to_string(rec.r + 1) +
539 ". The fluid Petri route models an arrival as a constant-propensity event, "
540 "which a renewal stream with memory is not; use SolverCTMC, SolverJMT or "
543 std::vector<double> col(nstate, 0.0);
544 col[
static_cast<std::size_t
>(t.
pidx[rec.qnd - 1][rec.l])] = 1.0;
548 sn.rtnodes((rec.snd - 1) * t.
K + rec.r, (rec.qnd - 1) * t.
K + rec.l));
549 emit(col, base, 3, -1, 0, 0,
static_cast<std::ptrdiff_t
>(ist) - 1,
550 static_cast<std::ptrdiff_t
>(rec.r));
555 for (std::size_t e = 0; e < t.
nev; ++e)
556 for (std::size_t s = 0; s < nstate; ++s)
557 if (cols[e][s] != 0.0) t.
D(s, e) = cols[e][s];
567 std::map<std::pair<std::size_t, std::size_t>, std::size_t> pairkey;
568 for (std::size_t j = 0; j < t.
modes.size(); ++j) {
571 for (std::size_t a = 0; a < md.
arc_slot.size(); ++a)
572 for (std::size_t b = a; b < md.
arc_slot.size(); ++b)
575 for (std::size_t b = 0; b < md.
inh_slot.size(); ++b)
579 const std::size_t pn = std::max<std::size_t>(nm, 1);
580 t.
pair_index.assign(pn, std::vector<std::ptrdiff_t>(pn, -1));
581 for (std::size_t i = 0; i < t.
npair; ++i) {
583 static_cast<std::ptrdiff_t
>(i);
585 static_cast<std::ptrdiff_t
>(i);
588 for (std::size_t e = 0; e < t.
nev; ++e) {
593 for (std::size_t q = 0; q < t.
timed_idx.size(); ++q)
596 for (std::size_t s = 0; s < nstate; ++s) t.
cov_idx[s] = s;
604 for (std::size_t e = 0; e < t.
nev; ++e) {
607 const std::size_t kk =
609 static_cast<std::size_t
>(t.
ev_class[e]);
615 for (std::size_t a = 0; a < md.
arc_slot.size(); ++a) {
616 const std::size_t s = md.
arc_slot[a];
618 const std::size_t kk =
626 t.
x0.assign(nstate, 0.0);
628 for (std::size_t j = 0; j < t.
modes.size(); ++j) {
630 if (md.
nph <= 1)
continue;
631 double e = std::numeric_limits<double>::infinity();
632 for (std::size_t a = 0; a < md.
arc_slot.size(); ++a)
635 for (std::size_t h = 0; h < md.
nph; ++h) t.
x0[md.
zblk[h]] = std::min(e, md.
c) * md.
pie[h];
UnsupportedError(const std::string &what)
A network plus its refreshed NetworkStruct.
The exception types the port throws.
Dense matrix and non-owning view.
PetriTerms petri_build_terms(const qn::NetworkStruct< T > &sn)
Assemble the drift terms of a net.
double petri_fine_tol()
GlobalConstants.FineTol, the reference's own "effectively zero".
TimingStrategy
SPN transition timing, with the values of MATLAB TimingStrategy.
@ IMMEDIATE
fires with zero delay, resolved by weight and priority
@ TIMED
fires after its firing distribution elapses
ProcessType
Distribution kinds, with the values of MATLAB ProcessType.
A queueing network and its refreshed NetworkStruct.
One firing mode of one transition, with its arcs and its firing process.
double c
servers of this mode; infinite for none
std::size_t mode
mode index within it, 0-based
std::vector< double > d1
row sums of D1, the completion rate out of each phase
std::vector< std::size_t > inh_slot
Inhibitor arcs, as state coordinates and their thresholds.
lang::TimingStrategy timing
std::vector< std::size_t > arc_slot
Input arcs, as state coordinates and their multiplicities.
std::vector< double > pie
entry distribution over the phases
std::size_t nph
phases of the firing process; 0 for an immediate mode
std::vector< std::size_t > zblk
The phase coordinates of a multi-phase mode; empty otherwise.
std::vector< double > arc_w
std::vector< double > inh_thr
std::function< double(const Matrix< double > &)> dep
Marking-dependent firing multiplier; empty for the unit one.
std::size_t node
1-based node index of the transition
std::vector< double > cvec
The mode's incidence column, over the whole state.
bool closable
Whether the enabling degree is worth closing over.
The assembled drift terms of a net.
std::vector< std::vector< double > > m0full
std::vector< std::size_t > stoch_col
std::vector< double > rate_base
std::vector< std::size_t > coord_class
std::vector< std::size_t > places
std::size_t nstate
marking coordinates plus phase coordinates
std::vector< std::size_t > imm_col
std::vector< std::vector< std::ptrdiff_t > > pidx
pidx(p,k) is the state coordinate of (0-based node p, class k), or npos.
std::vector< std::size_t > transitions
1-based node indices
std::vector< std::size_t > latch_col
std::vector< std::vector< std::ptrdiff_t > > pair_index
std::vector< std::size_t > imm_idx
std::vector< std::size_t > timed_idx
std::size_t nm
marking coordinates alone
std::vector< std::string > names_node
0-based, one per node
std::vector< int > ev_phase
std::vector< std::size_t > coord_node
std::vector< int > ev_kind
std::vector< std::pair< std::size_t, std::size_t > > cov_pairs
The Sigma entries the closure reads, and the map back to them.
std::map< std::size_t, std::vector< std::size_t > > producers
std::map< std::size_t, std::vector< std::size_t > > consumers
(station*K + class) -> the events that take tokens out of / into it.
std::vector< std::ptrdiff_t > ev_class
std::vector< std::size_t > cov_idx
std::map< std::size_t, std::vector< double > > consumer_w
std::vector< std::size_t > latch_mode
std::vector< int > ev_mode
std::vector< std::ptrdiff_t > ev_station
std::vector< PetriMode > modes
std::vector< std::ptrdiff_t > coord_station
Matrix< double > D
(nstate x nev) incidence
The parameters of a Cache node, MATLAB's sn.nodeparam{ind} for a Cache.
std::vector< double > firingprio
firing priority per mode
std::vector< lang::TimingStrategy > timing
immediate or timed
std::vector< std::string > modenames
std::vector< lang::Distrib< T > > firingproc
firing distribution per mode
std::vector< double > nmodeservers
servers per mode, may be infinite
std::vector< T > fireweight
weight among simultaneously enabled modes
std::vector< Matrix< T > > firing
firing[m](p,r): class-r tokens mode m moves to/from place p when it fires.
std::vector< Matrix< T > > enabling
enabling[m](p,r): class-r tokens of place p (0-based node) mode m needs.
std::vector< std::function< T(const std::vector< T > &)> > firingdep
Marking-dependent firing-rate multiplier g_m(marking); an empty entry is the unit multiplier.
std::vector< Matrix< T > > inhibiting
inhibiting[m](p,r): class-r tokens of p that BLOCK mode m (Inf = never).