5#ifndef LINE_SOLVERS_FLUID_PETRI_SYSTEM_H
6#define LINE_SOLVERS_FLUID_PETRI_SYSTEM_H
36inline double norm_cdf(
double z) {
return 0.5 * std::erfc(-z / std::sqrt(2.0)); }
40 return std::exp(-0.5 * z * z) / std::sqrt(2.0 * 3.14159265358979323846);
48inline std::pair<double, double>
min_closure(
double n,
double c,
double s2,
double vc,
50 if (std::isinf(c))
return std::make_pair(n, 1.0);
51 const double th2 = s2 - 2.0 * cov + vc;
57 return std::make_pair(n, 1.0);
58 return std::make_pair(c, 0.0);
60 const double th = std::sqrt(th2);
61 const double al = (n - c) / th;
64 const double h = n * p + c * (1.0 - p) - th * phi;
65 return std::make_pair(h, p);
71 std::vector<double>
g;
88 const std::size_t A = mu.size();
93 out.
h = std::min(1.0, c);
97 std::vector<std::vector<double>> s(A, std::vector<double>(A, 0.0));
99 for (std::size_t i = 0; i < A; ++i)
100 for (std::size_t j = 0; j < A; ++j) s[i][j] = S(i, j);
104 std::vector<double> covz(A, 0.0);
105 for (std::size_t i = 0; i < A; ++i) covz[i] = s[0][i];
106 std::vector<double> p(A, 1.0);
108 for (std::size_t k = 1; k < A; ++k) {
109 double th2 = vz + s[k][k] - 2.0 * covz[k];
111 if (th2 < 0.0) th2 = 0.0;
112 const double th = std::sqrt(th2);
114 std::vector<double> cw(A, 0.0);
116 const double al = (mz - mu[k]) / th;
120 mw = mz * pk + mu[k] * Phi - th * phi;
121 const double e2 = (mz * mz + vz) * pk + (mu[k] * mu[k] + s[k][k]) * Phi -
122 (mz + mu[k]) * th * phi;
127 if (vw < 0.0) vw = 0.0;
128 for (std::size_t i = 0; i < A; ++i) cw[i] = covz[i] * pk + s[i][k] * Phi;
130 pk = (mu[k] - mz >
petri_fine_tol() * std::max(1.0, std::fabs(mz))) ? 1.0 : 0.0;
131 mw = std::min(mz, mu[k]);
132 vw = pk * vz + (1.0 - pk) * s[k][k];
133 for (std::size_t i = 0; i < A; ++i) cw[i] = covz[i] * pk + s[i][k] * (1.0 - pk);
144 const std::pair<double, double> capped =
min_closure(mz, c, vz, 0.0, 0.0);
145 out.
h = capped.first;
146 out.
g.assign(A, 0.0);
147 double tail = capped.second;
148 for (std::size_t a = A - 1; a >= 1; --a) {
149 out.
g[a] = (1.0 - p[a]) * tail;
159 std::vector<std::vector<std::size_t>>
dslot;
160 std::vector<std::vector<double>>
dval;
162 std::vector<std::vector<std::size_t>>
depslot;
165 std::vector<std::vector<double>>
fmul;
166 std::vector<std::vector<std::vector<std::size_t>>>
fmslot;
167 std::vector<std::vector<std::vector<double>>>
fmval;
170namespace system_detail {
173inline double sig(
const PetriTerms& t,
const std::vector<double>& s2, std::size_t a,
177 return (i < 0 ||
static_cast<std::size_t
>(i) >= s2.size()) ? 0.0
178 : s2[
static_cast<std::size_t
>(i)];
189inline double markfun_of(
const PetriTerms& t,
const std::function<
double(
const Matrix<double>&)>& fun,
190 const std::vector<double>& x,
const std::vector<std::size_t>& read,
191 std::vector<std::size_t>& slots, std::vector<double>& vals) {
192 std::vector<std::size_t> coords;
193 for (std::size_t s = 0; s < t.nm; ++s) {
198 for (std::size_t nd : read)
199 if (t.coord_node[s] + 1 == nd) {
204 Matrix<double> mm(t.I, t.K, 0.0);
205 for (std::size_t s : coords) mm(t.coord_node[s], t.coord_class[s]) = x[s];
206 const double g = fun(mm);
209 for (std::size_t s : coords) {
210 const double h = std::max(1e-6 * std::fabs(x[s]), 1e-6);
211 const std::size_t i = t.coord_node[s], k = t.coord_class[s];
212 Matrix<double> mp = mm, mn = mm;
213 mp(i, k) = mm(i, k) + h;
214 mn(i, k) = std::max(0.0, mm(i, k) - h);
215 const double hh = mp(i, k) - mn(i, k);
216 if (hh <= 0)
continue;
217 const double d = (fun(mp) - fun(mn)) / hh;
227inline void dep_of(
const PetriTerms& t,
const PetriMode& md,
const std::vector<double>& x,
228 PetriTheta& th, std::size_t j) {
229 th.dep[j] = markfun_of(t, md.dep, x, std::vector<std::size_t>(), th.depslot[j], th.depval[j]);
236inline void flowdep_of(
const PetriTerms& t,
const PetriMode& md,
const std::vector<double>& x, PetriTheta& th,
239 for (
const auto& f : md.flow_dep_fun) any = any ||
static_cast<bool>(f);
241 const std::size_t nfl = md.flow_slot.size();
242 th.fmul[j].assign(nfl, 1.0);
243 th.fmslot[j].assign(nfl, std::vector<std::size_t>());
244 th.fmval[j].assign(nfl, std::vector<double>());
245 for (std::size_t a = 0; a < nfl; ++a) {
246 if (a >= md.flow_dep_fun.size() || !md.flow_dep_fun[a])
continue;
247 th.fmul[j][a] = markfun_of(t, md.flow_dep_fun[a], x, md.flow_dep_read[a], th.fmslot[j][a], th.fmval[j][a]);
256inline void fdep_of(
const PetriMode& md,
const std::vector<double>& x, PetriTheta& th, std::size_t j) {
257 const std::size_t nfd = md.fdep_slot.size();
258 std::vector<double> hv(nfd, 0.0), hd(nfd, 0.0);
259 for (std::size_t i = 0; i < nfd; ++i) {
260 const double f = std::max(0.0, x[md.fdep_slot[i]]);
261 hv[i] = md.fdep_fun[i](f);
262 const double step = std::max(1e-6 * std::fabs(f), 1e-6);
263 const double fp = f + step;
264 const double fn = std::max(0.0, f - step);
265 hd[i] = (md.fdep_fun[i](fp) - md.fdep_fun[i](fn)) / (fp - fn);
268 for (std::size_t i = 0; i < nfd; ++i) H *= hv[i];
269 const double g0 = th.dep[j];
270 std::map<std::size_t, double> acc;
271 for (std::size_t q = 0; q < th.depslot[j].size(); ++q) acc[th.depslot[j][q]] += th.depval[j][q] * H;
272 for (std::size_t i = 0; i < nfd; ++i) {
274 for (std::size_t q = 0; q < nfd; ++q)
275 if (q != i) others *= hv[q];
276 acc[md.fdep_slot[i]] += g0 * others * hd[i];
279 th.depslot[j].clear();
280 th.depval[j].clear();
281 for (std::map<std::size_t, double>::const_iterator it = acc.begin(); it != acc.end(); ++it) {
282 th.depslot[j].push_back(it->first);
283 th.depval[j].push_back(it->second);
318 const std::vector<double>& s2_in,
double gate_width = 0.0,
319 double guard_width = -1.0) {
320 if (guard_width < 0.0) guard_width = gate_width;
321 const std::size_t nmod = t.
modes.size();
323 th.
theta.assign(nmod, 0.0);
324 th.
dslot.assign(nmod, std::vector<std::size_t>());
325 th.
dval.assign(nmod, std::vector<double>());
326 th.
dep.assign(nmod, 1.0);
327 th.
depslot.assign(nmod, std::vector<std::size_t>());
328 th.
depval.assign(nmod, std::vector<double>());
329 th.
fmul.assign(nmod, std::vector<double>());
330 th.
fmslot.assign(nmod, std::vector<std::vector<std::size_t>>());
331 th.
fmval.assign(nmod, std::vector<std::vector<double>>());
332 for (std::size_t j = 0; j < nmod; ++j) system_detail::flowdep_of(t, t.
modes[j], x, th, j);
333 const std::vector<double> zeros(std::max<std::size_t>(t.
npair, 1), 0.0);
334 const std::vector<double>& s2 = s2_in.empty() ? zeros : s2_in;
336 for (std::size_t j = 0; j < nmod; ++j) {
338 const std::size_t A = md.
arc_slot.size();
339 std::vector<double> mu(A, 0.0);
340 for (std::size_t a = 0; a < A; ++a) mu[a] = x[md.
arc_slot[a]] / md.
arc_w[a];
344 for (std::size_t a = 0; a < A; ++a)
345 for (std::size_t b = 0; b < A; ++b)
351 const std::size_t nb = md.
inh_slot.size();
352 std::vector<double> gate(nb, 0.0), dgate(nb, 0.0);
354 for (std::size_t b = 0; b < nb; ++b) {
355 const double mb = x[md.
inh_slot[b]];
356 const double thr = md.
inh_thr[b];
357 const double vb = system_detail::sig(t, s2, md.
inh_slot[b], md.
inh_slot[b]);
359 const double sd = std::sqrt(vb);
360 const double zb = (thr - mb) / sd;
363 }
else if (gate_width > 0) {
364 const double w = gate_width * std::max(1.0, thr);
365 const double zb = (thr - mb) / w;
369 gate[b] = (mb < thr -
petri_fine_tol() * std::max(1.0, thr)) ? 1.0 : 0.0;
376 std::vector<std::size_t> gslots(md.
inh_slot);
377 for (std::size_t b = 0; b < nfg; ++b) {
380 const double sgn =
static_cast<double>(md.
guard_sgn[b]);
382 double g = 0.0, dg = 0.0;
384 const double sd = std::sqrt(vb);
385 const double zb = sgn * (fb - thr) / sd;
388 }
else if (guard_width > 0) {
389 const double w = guard_width * std::max(1.0, thr);
390 const double zb = sgn * (fb - thr) / w;
395 g = (sgn > 0 ? fb >= thr - tolg : fb <= thr + tolg) ? 1.0 : 0.0;
403 const std::size_t ng = nb + nfg;
406 std::map<std::size_t, double> acc;
407 for (std::size_t a = 0; a < A; ++a)
409 for (std::size_t b = 0; b < ng; ++b) {
412 others = ginh / gate[b];
415 for (std::size_t q = 0; q < ng; ++q)
416 if (q != b) others *= gate[q];
418 acc[gslots[b]] += mm.
h * others * dgate[b];
420 for (std::map<std::size_t, double>::const_iterator it = acc.begin(); it != acc.end();
422 th.
dslot[j].push_back(it->first);
423 th.
dval[j].push_back(it->second);
425 th.
theta[j] = ginh * mm.
h;
427 if (md.
dep) system_detail::dep_of(t, md, x, th, j);
428 if (!md.
fdep_slot.empty()) system_detail::fdep_of(md, x, th, j);
447 const std::vector<double>& phi,
448 const std::vector<double>& mu,
const PetriTheta& th) {
449 const std::size_t nmod = t.
modes.size();
450 std::vector<std::ptrdiff_t> imm_pos(nmod, -1), latch_pos(nmod, -1);
451 for (std::size_t q = 0; q < t.
imm_idx.size(); ++q)
452 imm_pos[t.
imm_idx[q]] =
static_cast<std::ptrdiff_t
>(q);
453 for (std::size_t q = 0; q < t.
latch_mode.size(); ++q)
454 latch_pos[t.
latch_mode[q]] =
static_cast<std::ptrdiff_t
>(q);
456 std::vector<double> r(t.
nev, 0.0);
457 for (std::size_t e = 0; e < t.
nev; ++e) {
462 const std::ptrdiff_t at = imm_pos[
static_cast<std::size_t
>(t.
ev_mode[e])];
463 r[e] = (phi.empty() || at < 0) ? 0.0 : phi[
static_cast<std::size_t
>(at)];
465 const std::ptrdiff_t at = latch_pos[
static_cast<std::size_t
>(t.
ev_mode[e])];
466 r[e] = (mu.empty() || at < 0) ? 0.0 : mu[
static_cast<std::size_t
>(at)];
470 const std::size_t j =
static_cast<std::size_t
>(t.
ev_mode[e]);
474 const std::size_t j =
static_cast<std::size_t
>(t.
ev_mode[e]);
495 for (std::size_t e = 0; e < t.
nev; ++e) {
497 if (k == 3 || k == 4 || k == 5)
continue;
498 const std::size_t j =
static_cast<std::size_t
>(t.
ev_mode[e]);
503 if (!th.
fmul[j].empty()) {
504 const std::size_t a = t.
ev_flow[e];
506 for (std::size_t q = 0; q < th.
fmslot[j][a].size(); ++q)
509 for (std::size_t q = 0; q < th.
dslot[j].size(); ++q) Jr(e, th.
dslot[j][q]) += base * fm * th.
dval[j][q];
513 for (std::size_t q = 0; q < th.
dslot[j].size(); ++q)
514 Jr(e, th.
dslot[j][q]) += base * th.
dep[j] * th.
dval[j][q];
515 for (std::size_t q = 0; q < th.
depslot[j].size(); ++q)
518 const std::size_t zc = md.
zblk[
static_cast<std::size_t
>(t.
ev_phase[e])];
The exception types the port throws.
Event-based representation of the fluid marking process of a stochastic Petri net.
Dense linear algebra over the templated number type: products, identity, inverse, and powers.
Dense matrix and non-owning view.
MinMulti minmulti_closure(const std::vector< double > &mu, const Matrix< double > &S, double c)
Min-normal closure of E[min(X_1,...,X_A,c)] by Clark's (1961) recursion.
PetriTheta petri_theta(const PetriTerms &t, const std::vector< double > &x, const std::vector< double > &s2_in, double gate_width=0.0, double guard_width=-1.0)
The closed enabling term of every mode, and its derivative.
double norm_cdf(double z)
The standard normal CDF, without a statistics dependency.
std::pair< double, double > min_closure(double n, double c, double s2, double vc, double cov)
E[min(X,Y)] and dE/dE[X] for jointly normal X, Y.
std::vector< double > petri_rates(const PetriTerms &t, const std::vector< double > &x, const std::vector< double > &phi, const std::vector< double > &mu, const PetriTheta &th)
The rate of every event column.
Matrix< double > petri_jacobian(const PetriTerms &t, const PetriTheta &th)
Drift Jacobian A = D * dR/dX.
double petri_fine_tol()
GlobalConstants.FineTol, the reference's own "effectively zero".
double norm_pdf(double z)
The standard normal density.
Conservation laws of a layered queueing network, enumerated from its structure.
Matrix< T > matmul(const Matrix< T > &A, const Matrix< T > &B)
Matrix product A B.
The result of the many-argument closure.
std::vector< double > g
dH/dMU(a): the probability that arc a binds
double v
Var[min] before the cap.
double h
E[min(X_1,...,X_A,c)].
One firing mode of one transition, with its arcs and its firing process.
double c
servers of this mode; infinite for none
std::vector< double > guard_thr
std::vector< int > guard_sgn
std::vector< std::size_t > inh_slot
Inhibitor arcs, as state coordinates and their thresholds.
std::vector< std::size_t > arc_slot
Input arcs, as state coordinates and their multiplicities.
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< std::size_t > guard_slot
Fluid guards: level coordinates, +1 for '>=' and -1 for '<=', and thresholds.
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::vector< std::size_t > fdep_slot
Fluid rate dependences: level coordinates and the multipliers h(level).
bool closable
Whether the enabling degree is worth closing over.
The assembled drift terms of a net.
std::vector< double > rate_base
std::size_t nstate
marking coordinates plus phase coordinates plus fluid levels
std::vector< std::vector< std::ptrdiff_t > > pair_index
std::vector< std::size_t > imm_idx
std::vector< int > ev_phase
std::vector< int > ev_kind
std::vector< std::size_t > ev_flow
Kind 6: the flow's position in its mode's flow_slot; 0 for every other kind.
std::vector< std::size_t > latch_mode
std::vector< int > ev_mode
std::vector< PetriMode > modes
Matrix< double > D
(nstate x nev) incidence
The closed enabling term of every mode and its derivative.
std::vector< std::vector< double > > dval
std::vector< std::vector< double > > depval
std::vector< double > theta
std::vector< std::vector< double > > fmul
Per mode, the marking multiplier of each flow (empty: every flow at 1), its gradient slots and values...
std::vector< std::vector< std::size_t > > dslot
std::vector< double > dep
std::vector< std::vector< std::vector< double > > > fmval
std::vector< std::vector< std::vector< std::size_t > > > fmslot
std::vector< std::vector< std::size_t > > depslot