63 std::vector<double>
N;
68namespace cons_detail {
79 const std::size_t rows = A.
rows(), cols = A.
cols();
82 std::vector<std::size_t> piv;
84 const double tol = 1e-12;
85 for (std::size_t c = 0; c < cols && r < rows; ++c) {
87 double best = std::fabs(R(r, c));
88 for (std::size_t i = r + 1; i < rows; ++i)
89 if (std::fabs(R(i, c)) > best) {
90 best = std::fabs(R(i, c));
94 for (std::size_t i = r; i < rows; ++i) R(i, c) = 0.0;
98 for (std::size_t j = 0; j < cols; ++j) std::swap(R(r, j), R(k, j));
99 const double p = R(r, c);
100 for (std::size_t j = 0; j < cols; ++j) R(r, j) /= p;
101 for (std::size_t i = 0; i < rows; ++i) {
102 if (i == r)
continue;
103 const double f = R(i, c);
104 if (f == 0.0)
continue;
105 for (std::size_t j = 0; j < cols; ++j) R(i, j) -= f * R(r, j);
110 std::vector<std::size_t> free;
111 for (std::size_t c = 0; c < cols; ++c)
112 if (std::find(piv.begin(), piv.end(), c) == piv.end()) free.push_back(c);
114 for (std::size_t i = 0; i < free.size(); ++i) {
116 for (std::size_t rr = 0; rr < piv.size(); ++rr) Z(piv[rr], i) = -R(rr, free[i]);
122inline std::string trim(
double v) {
123 if (v == std::rint(v) && std::fabs(v) < 1e15)
124 return std::to_string(
static_cast<long long>(v));
126 std::snprintf(buf,
sizeof(buf),
"%g", v);
127 return std::string(buf);
131inline std::string row_label(
const PetriTerms& t,
const Matrix<double>& C, std::size_t r) {
133 for (std::size_t s = 0; s < C.
cols(); ++s) {
134 const double w = C(r, s);
135 if (w == 0.0)
continue;
138 nm = t.names_node[t.coord_node[s]] +
"(class " +
139 std::to_string(t.coord_class[s] + 1) +
")";
142 for (std::size_t j = 0; j < t.modes.size(); ++j)
143 for (std::size_t q = 0; q < t.modes[j].zblk.size(); ++q)
144 if (t.modes[j].zblk[q] == s)
145 nm = t.modes[j].label +
" phase " + std::to_string(q + 1);
147 if (!out.empty()) out +=
" + ";
148 out += (w == 1.0) ? nm : (trim(w) +
"*" + nm);
173 for (std::size_t i = 0; i < t.
D.
rows(); ++i)
174 for (std::size_t j = 0; j < t.
D.
cols(); ++j) Dt(j, i) = t.
D(i, j);
177 for (std::size_t i = 0; i < Z.
rows(); ++i)
178 for (std::size_t j = 0; j < Z.
cols(); ++j) C(j, i) = Z(i, j);
181 for (std::size_t i = 0; i < C.
rows(); ++i)
182 for (std::size_t j = 0; j < C.
cols(); ++j)
183 if (std::fabs(C(i, j)) < 1e-12) C(i, j) = 0.0;
186 std::vector<std::size_t> keep;
187 for (std::size_t i = 0; i < C.
rows(); ++i) {
188 double smallest = std::numeric_limits<double>::infinity();
189 for (std::size_t j = 0; j < C.
cols(); ++j)
190 if (C(i, j) != 0.0) smallest = std::min(smallest, std::fabs(C(i, j)));
191 if (!std::isfinite(smallest))
continue;
192 for (std::size_t j = 0; j < C.
cols(); ++j) C(i, j) /= smallest;
196 for (std::size_t r = 0; r < keep.size(); ++r)
197 for (std::size_t j = 0; j < t.
nstate; ++j) cons.
C(r, j) = C(keep[r], j);
198 cons.
N.assign(cons.
C.
rows(), 0.0);
199 for (std::size_t r = 0; r < cons.
C.
rows(); ++r) {
201 for (std::size_t j = 0; j < t.
nstate; ++j) v += cons.
C(r, j) * t.
x0[j];
204 for (std::size_t r = 0; r < cons.
C.
rows(); ++r)
205 for (std::size_t c = 0; c < t.
nev; ++c) {
207 for (std::size_t j = 0; j < t.
nstate; ++j) v += cons.
C(r, j) * t.
D(j, c);
208 cons.
leak = std::max(cons.
leak, std::fabs(v));
210 for (std::size_t r = 0; r < cons.
C.
rows(); ++r)
211 cons.
label.push_back(cons_detail::row_label(t, cons.
C, r));
220 std::vector<double>
b;
222 std::vector<std::vector<bool>>
cover;
234 std::vector<std::vector<double>> rows;
235 std::vector<double> bs;
236 std::vector<std::string> labels;
237 for (std::size_t pi = 0; pi < t.
places.size(); ++pi) {
238 const std::size_t ind = t.
places[pi];
239 const std::size_t ist =
sn.nodes[ind - 1].station;
240 std::vector<std::size_t> slots;
241 for (std::size_t k = 0; k < t.
K; ++k)
242 if (t.
pidx[ind - 1][k] >= 0)
243 slots.push_back(
static_cast<std::size_t
>(t.
pidx[ind - 1][k]));
244 if (slots.empty())
continue;
245 if (ist >= 1 && ist <=
sn.stations.size()) {
246 const double cap =
sn.stations[ist - 1].cap;
247 if (std::isfinite(cap)) {
248 std::vector<double> row(t.
nstate, 0.0);
249 for (std::size_t s : slots) row[s] = 1.0;
252 labels.push_back(
"capacity " + cons_detail::trim(cap) +
" of place " +
255 for (std::size_t k = 0; k < t.
K; ++k) {
256 if (t.
pidx[ind - 1][k] < 0)
continue;
257 if (k >=
sn.stations[ist - 1].classcap.size())
continue;
258 const double ccap =
sn.stations[ist - 1].classcap[k];
259 if (!std::isfinite(ccap))
continue;
260 std::vector<double> row(t.
nstate, 0.0);
261 row[
static_cast<std::size_t
>(t.
pidx[ind - 1][k])] = 1.0;
264 labels.push_back(
"class-" + std::to_string(k + 1) +
" capacity " +
265 cons_detail::trim(ccap) +
" of place " + t.
names_node[ind - 1]);
272 std::vector<bool> keep(rows.size(),
true);
273 for (std::size_t c = 0; c < rows.size(); ++c) {
274 if (!keep[c])
continue;
275 for (std::size_t d = c + 1; d < rows.size(); ++d) {
276 if (!keep[d] || rows[c] != rows[d])
continue;
284 std::vector<std::size_t> idx;
285 for (std::size_t i = 0; i < keep.size(); ++i)
286 if (keep[i]) idx.push_back(i);
289 con.
b.assign(idx.size(), 0.0);
290 con.
cover.assign(idx.size(), std::vector<bool>(t.
nstate,
false));
291 for (std::size_t r = 0; r < idx.size(); ++r) {
292 for (std::size_t s = 0; s < t.
nstate; ++s) {
293 con.
A(r, s) = rows[idx[r]][s];
294 con.
cover[r][s] = rows[idx[r]][s] > 0.0;
296 con.
b[r] = bs[idx[r]];
297 con.
label.push_back(labels[idx[r]]);
309 std::size_t
a = 0,
b = 0;
314 std::vector<std::ptrdiff_t>
bind;
320namespace imm_detail {
330inline bool inhibited(
const PetriMode& md,
const std::vector<double>& x) {
331 for (std::size_t b = 0; b < md.
inh_slot.size(); ++b)
353 const std::size_t n = t.
imm_idx.size();
356 imm.
active.assign(n,
true);
357 imm.
bind.assign(n, -1);
363 for (std::size_t k = 0; k < n; ++k) {
367 " has no enabling arc, so nothing bounds its firing flow and "
368 "the net has no fluid limit. Give it an input place, or make "
370 if (imm_detail::inhibited(md, x)) imm.
active[k] =
false;
376 for (std::size_t k = 0; k < n; ++k) {
382 if (imm.
bind[k] >= 0 &&
384 static_cast<std::size_t
>(imm.
bind[k])) != md.
arc_slot.end())
388 for (std::size_t a = 1; a < md.
arc_slot.size(); ++a) {
390 if (lev < best_lev) {
395 imm.
bind[k] =
static_cast<std::ptrdiff_t
>(best);
399 std::set<std::size_t> pinset;
400 for (std::size_t k = 0; k < n; ++k)
402 pinset.insert(
static_cast<std::size_t
>(imm.
bind[k]));
403 imm.
pins.assign(pinset.begin(), pinset.end());
405 for (std::size_t p : imm.
pins) {
409 imm.
rows.push_back(row);
410 std::vector<std::size_t> grp;
411 for (std::size_t k = 0; k < n; ++k)
412 if (imm.
active[k] && imm.
bind[k] ==
static_cast<std::ptrdiff_t
>(p)) grp.push_back(k);
413 if (grp.size() <= 1)
continue;
414 int topprio = std::numeric_limits<int>::min();
415 for (std::size_t k : grp) topprio = std::max(topprio, t.
modes[t.
imm_idx[k]].prio);
416 std::vector<std::size_t> top, low;
417 for (std::size_t k : grp)
418 (t.
modes[t.
imm_idx[k]].prio == topprio ? top : low).push_back(k);
420 for (std::size_t i = 1; i < top.size(); ++i) {
427 imm.
rows.push_back(rr);
429 for (std::size_t k : low) {
433 imm.
rows.push_back(rr);
436 for (std::size_t k = 0; k < n; ++k)
441 imm.
rows.push_back(rr);
461 const std::size_t I =
sn.nodes.size();
462 bool has_transition =
false;
463 for (std::size_t i = 0; i < I; ++i)
465 if (!has_transition) {
467 v.
reason =
"the model has no Transition node, so it is not a Petri net";
470 for (std::size_t i = 0; i < I; ++i) {
475 v.
reason =
"node " +
sn.nodes[i].name +
" is a " +
477 ". The fluid Petri route solves the marking of a Petri net, and a model "
478 "that also holds queueing stations is two formalisms at once with no "
479 "reference semantics for the hand-off; use SolverCTMC, SolverJMT, "
480 "SolverSSA or SolverLDES";
486 for (std::size_t i = 0; i < I; ++i) {
488 const std::size_t ist =
sn.nodes[i].station;
489 if (ist < 1 || ist >
sn.nstations)
continue;
490 for (std::size_t k = 0; k <
sn.nclasses; ++k) {
492 if (!std::isnan(rate) && rate > 0) {
494 v.
reason =
"place " +
sn.nodes[i].name +
495 " is a QUEUEING place (it declares a service process), whose embedded "
496 "queue this drift does not carry; use SolverLDES";
523 std::vector<double>
t;
525 std::vector<std::vector<std::vector<double>>>
QNt,
UNt,
TNt;
526 std::vector<double>
x;
528 double resnorm = std::numeric_limits<double>::infinity();
543namespace solver_detail {
549 std::vector<std::size_t> active;
552 std::vector<double> N;
562inline Ctx context(
const PetriTerms& terms,
const PetriConservation& cons,
563 const PetriConstraints& con,
const PetriImmediate& imm,
564 const std::vector<std::size_t>& active) {
571 std::vector<double> N = cons.N;
572 if (!active.empty() && C.
rows() > 0) {
573 std::vector<bool> hit(terms.nstate,
false);
574 for (std::size_t c : active)
575 for (std::size_t s = 0; s < terms.nstate; ++s)
576 if (con.cover[c][s]) hit[s] =
true;
577 std::vector<std::size_t> keep;
578 for (std::size_t r = 0; r < C.
rows(); ++r) {
580 for (std::size_t s = 0; s < terms.nstate && !drop; ++s)
581 if (hit[s] && C(r, s) != 0.0) drop =
true;
582 if (!drop) keep.push_back(r);
585 std::vector<double> Nk(keep.size(), 0.0);
586 for (std::size_t i = 0; i < keep.size(); ++i) {
587 for (std::size_t s = 0; s < terms.nstate; ++s) Ck(i, s) = C(keep[i], s);
595 ctx.Dp = Matrix<double>(terms.D.rows(), terms.D.cols(), 0.0);
596 ctx.Dn = Matrix<double>(terms.D.rows(), terms.D.cols(), 0.0);
597 for (std::size_t i = 0; i < terms.D.rows(); ++i)
598 for (std::size_t j = 0; j < terms.D.cols(); ++j) {
599 ctx.Dp(i, j) = std::max(terms.D(i, j), 0.0);
600 ctx.Dn(i, j) = std::min(terms.D(i, j), 0.0);
607 std::vector<double> x, s2, phi, mu, zeta;
610inline Unpacked unpack(
const std::vector<double>& u,
const PetriTerms& t,
611 const PetriImmediate& imm, std::size_t na) {
612 const std::size_t n = t.nstate, npair = t.npair, ni = imm.n, nl = t.latch_mode.size();
614 up.x.assign(u.begin(), u.begin() + n);
615 up.s2.assign(u.begin() + n, u.begin() + n + npair);
616 up.phi.assign(u.begin() + n + npair, u.begin() + n + npair + ni);
617 for (std::size_t i = 0; i < up.phi.size(); ++i) up.phi[i] = std::max(0.0, up.phi[i]);
618 up.mu.assign(u.begin() + n + npair + ni, u.begin() + n + npair + ni + nl);
619 up.zeta.assign(u.begin() + n + npair + ni + nl, u.begin() + n + npair + ni + nl + na);
620 for (std::size_t i = 0; i < up.zeta.size(); ++i) up.zeta[i] = std::max(0.0, up.zeta[i]);
624inline std::ptrdiff_t index_of(
const std::vector<std::size_t>& a, std::size_t v) {
625 for (std::size_t i = 0; i < a.size(); ++i)
626 if (a[i] == v)
return static_cast<std::ptrdiff_t
>(i);
655inline bool clamp_tangent(
const PetriTerms& terms,
const PetriImmediate& imm,
656 const PetriConstraints& con,
const std::vector<std::size_t>& active,
657 const PetriTheta* th, Matrix<double>& T) {
658 const std::vector<std::size_t>& idx = terms.cov_idx;
659 const std::size_t nc = idx.size();
660 T = Matrix<double>(nc, nc, 0.0);
661 for (std::size_t i = 0; i < nc; ++i) T(i, i) = 1.0;
663 std::vector<std::size_t> actk;
664 for (std::size_t k = 0; k < imm.n; ++k)
665 if (imm.active[k] && imm.bind[k] >= 0) actk.push_back(k);
666 const std::vector<std::size_t>& B = imm.pins;
667 if (!actk.empty() && !B.empty()) {
668 Matrix<double> Cf(nc, actk.size(), 0.0);
669 for (std::size_t a = 0; a < actk.size(); ++a) {
670 const std::vector<double>& cv = terms.modes[terms.imm_idx[actk[a]]].cvec;
671 for (std::size_t i = 0; i < nc; ++i) Cf(i, a) = cv[idx[i]];
673 Matrix<double> G0(actk.size(), B.size(), 0.0);
674 for (std::size_t jb = 0; jb < B.size(); ++jb) {
675 std::vector<std::size_t> grp;
677 for (std::size_t q = 0; q < actk.size(); ++q)
678 if (imm.bind[actk[q]] ==
static_cast<std::ptrdiff_t
>(B[jb])) {
680 wsum += terms.modes[terms.imm_idx[actk[q]]].weight;
682 if (grp.empty())
continue;
683 const bool uniform = !(wsum > 0);
684 for (std::size_t q : grp) {
686 uniform ? 1.0 : terms.modes[terms.imm_idx[actk[q]]].weight;
687 G0(q, jb) = w / (uniform ?
static_cast<double>(grp.size()) : wsum);
690 Matrix<double> EB(B.size(), nc, 0.0);
691 for (std::size_t jb = 0; jb < B.size(); ++jb) {
692 const std::ptrdiff_t at = index_of(idx, B[jb]);
693 if (at >= 0) EB(jb,
static_cast<std::size_t
>(at)) = 1.0;
697 for (std::size_t i = 0; i < nc; ++i)
698 for (std::size_t j = 0; j < nc; ++j) T(i, j) -= corr(i, j);
701 std::vector<std::vector<double>> R;
702 for (std::size_t c : active) {
703 std::vector<double> row(nc, 0.0);
704 for (std::size_t i = 0; i < nc; ++i) row[i] = con.A(c, idx[i]);
708 for (std::size_t j : terms.latch_mode) {
709 std::vector<double> row(nc, 0.0);
710 for (std::size_t z : terms.modes[j].zblk) {
711 const std::ptrdiff_t at = index_of(idx, z);
712 if (at >= 0) row[
static_cast<std::size_t
>(at)] = 1.0;
714 for (std::size_t q = 0; q < th->dslot[j].size(); ++q) {
715 const std::ptrdiff_t at = index_of(idx, th->dslot[j][q]);
716 if (at >= 0) row[
static_cast<std::size_t
>(at)] -= th->dval[j][q];
723 for (
const std::vector<double>& row : R)
725 if (std::fabs(v) > 1e-14) any =
true;
727 Matrix<double>
Rm(R.size(), nc, 0.0);
728 for (std::size_t i = 0; i < R.size(); ++i)
729 for (std::size_t j = 0; j < nc; ++j)
Rm(i, j) = R[i][j];
730 Matrix<double> Rt(nc, R.size(), 0.0);
731 for (std::size_t i = 0; i < R.size(); ++i)
732 for (std::size_t j = 0; j < nc; ++j) Rt(j, i) =
Rm(i, j);
733 const Matrix<double> RRt =
matmul(Rm, Rt);
735 Matrix<double> P(nc, nc, 0.0);
736 for (std::size_t i = 0; i < nc; ++i)
737 for (std::size_t j = 0; j < nc; ++j) P(i, j) = (i == j ? 1.0 : 0.0) - corr(i, j);
742 for (std::size_t i = 0; i < nc; ++i)
743 for (std::size_t j = 0; j < nc; ++j)
744 worst = std::max(worst, std::fabs(T(i, j) - (i == j ? 1.0 : 0.0)));
745 return worst > 1e-14;
755inline Matrix<double> sigma_of(
const PetriTerms& terms,
const Matrix<double>& A,
756 const std::vector<double>& r,
const Matrix<double>* clampT) {
757 const std::vector<std::size_t>& idx = terms.cov_idx;
758 const std::size_t nc = idx.size(), ns = terms.stoch_col.size();
759 Matrix<double> Dc(nc, std::max<std::size_t>(ns, 1), 0.0);
760 for (std::size_t i = 0; i < nc; ++i)
761 for (std::size_t j = 0; j < ns; ++j) Dc(i, j) = terms.D(idx[i], terms.stoch_col[j]);
762 Matrix<double> Am(nc, nc, 0.0);
763 for (std::size_t i = 0; i < nc; ++i)
764 for (std::size_t j = 0; j < nc; ++j) Am(i, j) = A(idx[i], idx[j]);
765 if (clampT !=
nullptr) {
773 Matrix<double> Q(nc, nc, 0.0);
774 for (std::size_t i = 0; i < nc; ++i)
775 for (std::size_t j = 0; j < nc; ++j) {
777 for (std::size_t e = 0; e < ns; ++e)
778 v += Dc(i, e) * r[terms.stoch_col[e]] * Dc(j, e);
781 FluidLyapunovInfo info;
783 Matrix<double> Sigma(terms.nstate, terms.nstate, 0.0);
784 for (std::size_t i = 0; i < nc; ++i)
785 for (std::size_t j = 0; j < nc; ++j) Sigma(idx[i], idx[j]) = Sc(i, j);
792 std::vector<double> G, r;
794 Matrix<double> Sigma;
804inline Residual residual(
const std::vector<double>& u,
const Ctx& ctx,
bool quiet) {
805 const PetriTerms& terms = *ctx.terms;
806 const PetriImmediate& imm = *ctx.imm;
807 const std::size_t n = terms.nstate;
808 const Unpacked up = unpack(u, terms, imm, ctx.active.size());
812 res.r =
petri_rates(terms, up.x, up.phi, up.mu, res.th);
815 std::vector<double> gain(n, 1.0);
816 for (std::size_t k = 0; k < ctx.active.size(); ++k)
817 for (std::size_t s = 0; s < n; ++s)
818 if (ctx.con->cover[ctx.active[k]][s]) gain[s] *= up.zeta[k];
819 std::vector<double> drift(n, 0.0);
820 for (std::size_t s = 0; s < n; ++s) {
821 double neg = 0.0, pos = 0.0;
822 for (std::size_t e = 0; e < terms.nev; ++e) {
823 neg += ctx.Dn(s, e) * res.r[e];
824 pos += ctx.Dp(s, e) * res.r[e];
826 drift[s] = neg + gain[s] * pos;
833 clamp_tangent(terms, imm, *ctx.con, ctx.active, &res.th, T);
834 res.Sigma = sigma_of(terms, A, res.r, reduced ? &T :
nullptr);
835 }
catch (
const Error&) {
841 std::vector<double> G;
842 G.reserve(n + ctx.C.rows() + terms.npair + imm.rows.size() + terms.latch_mode.size() +
844 for (
double d : drift) G.push_back(d);
845 for (std::size_t r = 0; r < ctx.C.rows(); ++r) {
847 for (std::size_t s = 0; s < n; ++s) v += ctx.C(r, s) * up.x[s];
848 G.push_back(v - ctx.N[r]);
850 for (std::size_t p = 0; p < terms.npair; ++p)
851 G.push_back(up.s2[p] - res.Sigma(terms.cov_pairs[p].first, terms.cov_pairs[p].second));
852 for (
const PetriImmediate::Row& row : imm.rows) {
855 G.push_back(up.phi[row.a] * row.wb - up.phi[row.b] * row.wa);
856 else G.push_back(up.phi[row.a]);
858 for (std::size_t j : terms.latch_mode) {
860 for (std::size_t z : terms.modes[j].zblk) s += up.x[z];
861 G.push_back(s - res.th.theta[j]);
863 for (std::size_t k = 0; k < ctx.active.size(); ++k) {
865 for (std::size_t s = 0; s < n; ++s) v += ctx.con->A(ctx.active[k], s) * up.x[s];
866 G.push_back(v - ctx.con->b[ctx.active[k]]);
873inline double inf_norm(
const std::vector<double>& v) {
875 for (
double d : v) m = std::max(m, std::fabs(d));
886inline std::vector<double> project(
const std::vector<double>& u,
const std::vector<double>& lb) {
887 if (lb.empty())
return u;
888 if (lb.size() != u.size())
889 throw InputError(
"fluid_petri: the bound vector has " + std::to_string(lb.size()) +
890 " entries for " + std::to_string(u.size()) +
" unknowns");
891 std::vector<double> w = u;
892 for (std::size_t i = 0; i < w.size(); ++i)
893 if (std::isfinite(lb[i])) w[i] = std::max(lb[i], w[i]);
898inline Matrix<double> fdjac(
const Ctx& ctx,
const std::vector<double>& u,
899 const std::vector<double>& G) {
900 const std::size_t n = u.size(), m = G.size();
901 Matrix<double> J(std::max<std::size_t>(m, 1), std::max<std::size_t>(n, 1), 0.0);
902 for (std::size_t k = 0; k < n; ++k) {
903 const double h = 1e-7 * std::max(1.0, std::fabs(u[k]));
904 std::vector<double> up = u;
906 Residual rp = residual(up, ctx,
true);
908 for (std::size_t i = 0; i < m; ++i) J(i, k) = (rp.G[i] - G[i]) / h;
912 Residual rm = residual(up, ctx,
true);
913 if (!rm.ok)
continue;
914 for (std::size_t i = 0; i < m; ++i) J(i, k) = (G[i] - rm.G[i]) / h;
920 std::vector<double> u;
921 std::size_t iterations = 0;
922 bool converged =
false;
923 double resnorm = std::numeric_limits<double>::infinity();
927inline NewtonResult newton(
const Ctx& ctx,
const std::vector<double>& u0,
double tol,
928 std::size_t maxit,
const std::vector<double>& lb) {
930 std::vector<double> u = project(u0, lb);
931 Residual res = residual(u, ctx,
false);
936 std::vector<double> G = res.G;
937 double resnorm = inf_norm(G);
939 for (it = 1; it <= maxit; ++it) {
940 if (resnorm <= tol) {
942 out.iterations = it - 1;
943 out.converged =
true;
944 out.resnorm = resnorm;
947 const Matrix<double> J = fdjac(ctx, u, G);
948 Matrix<double> rhs(G.size(), 1, 0.0);
949 for (std::size_t i = 0; i < G.size(); ++i) rhs(i, 0) = -G[i];
950 const Matrix<double> du =
matmul(
pinv(J), rhs);
952 bool improved =
false;
953 for (
int b = 0; b < 30; ++b) {
954 std::vector<double> un(u.size(), 0.0);
955 for (std::size_t i = 0; i < u.size(); ++i) un[i] = u[i] + lam * du(i, 0);
956 un = project(un, lb);
957 Residual rn = residual(un, ctx,
true);
959 const double v = inf_norm(rn.G);
970 if (!improved)
break;
974 out.converged = resnorm <= tol;
975 out.resnorm = resnorm;
986inline std::vector<double> seed_drift(
const PetriTerms& terms,
const std::vector<double>& xin,
988 std::vector<double> x = xin;
989 for (std::size_t i = 0; i < x.size(); ++i) x[i] = std::max(0.0, x[i]);
990 const PetriTheta th =
991 petri_theta(terms, x, std::vector<double>(std::max<std::size_t>(terms.npair, 1), 0.0));
992 std::vector<double> phi(terms.imm_idx.size(), 0.0);
993 for (std::size_t k = 0; k < phi.size(); ++k) {
994 const std::size_t j = terms.imm_idx[k];
995 phi[k] = lam * terms.modes[j].weight * th.theta[j];
997 std::vector<double> mu(terms.latch_mode.size(), 0.0);
998 for (std::size_t q = 0; q < mu.size(); ++q) {
999 const std::size_t j = terms.latch_mode[q];
1001 for (std::size_t z : terms.modes[j].zblk) s += x[z];
1002 mu[q] = lam * (th.theta[j] - s);
1004 const std::vector<double> r =
petri_rates(terms, x, phi, mu, th);
1005 std::vector<double> d(terms.nstate, 0.0);
1006 for (std::size_t s = 0; s < terms.nstate; ++s) {
1008 for (std::size_t e = 0; e < terms.nev; ++e) v += terms.D(s, e) * r[e];
1025 const std::size_t M =
sn.nstations, K =
sn.nclasses;
1029 throw UnsupportedError(
"solver_fluid_petri: the fluid Petri route cannot solve this "
1030 "model: " + v.
reason +
".");
1033 const std::size_t n = terms.
nstate, npair = terms.
npair;
1034 if (n >
opt.dae_maxstate)
1036 "solver_fluid_petri: the fluid Petri route solves a " + std::to_string(n) +
1037 "-unknown algebraic system with a finite-difference Jacobian, above the limit of " +
1038 std::to_string(
opt.dae_maxstate) +
1039 " set by options.config.dae_maxstate. Raise that limit, or use SolverSSA for a net "
1043 if (cons.
leak > 1e-7)
1044 throw NumericError(
"solver_fluid_petri: the conserved directions and the jump matrix "
1045 "disagree: the largest leak per unit rate is " +
1046 std::to_string(cons.
leak) +
", where it must be zero");
1048 const std::size_t ncon = con.
b.size();
1059 for (
double d : terms.
modes[j].d1) s += d;
1060 rmax = std::max(rmax, s);
1062 for (std::size_t e = 0; e < terms.
nev; ++e)
1064 if (!(rmax > 0)) rmax = 1.0;
1065 const double lam = std::min(1e8, 1e4 * rmax);
1067 for (std::size_t s = 0; s < terms.
nm; ++s) mass += terms.
x0[s];
1068 double Thor = 50.0 * (mass + 1.0) / rmax;
1070 const auto drift_fn = [&terms, lam](
const double&,
const std::vector<double>& y) {
1071 return solver_detail::seed_drift(terms, y, lam);
1073 std::vector<double> tseed;
1074 std::vector<std::vector<double>> xseed;
1075 for (
int attempt = 0; attempt < 6; ++attempt) {
1085 const std::vector<double> d = solver_detail::seed_drift(terms, xseed.back(), lam);
1086 if (solver_detail::inf_norm(d) <= 1e-6 * std::max(1.0, rmax * (mass + 1.0)))
break;
1089 std::vector<double> x = xseed.back();
1093 const std::size_t nimm = imm.
n;
1097 std::vector<double> s2(npair, 0.0);
1098 std::vector<bool> ondiag(npair,
false);
1099 for (std::size_t p = 0; p < npair; ++p) {
1105 std::vector<std::size_t> active;
1106 std::vector<double> phi(nimm, 0.0);
1107 const std::size_t nlatch = terms.
latch_mode.size();
1108 std::vector<double> mu(nlatch, 0.0), zeta;
1109 std::size_t iters = 0;
1110 const std::size_t aset_max = std::max<std::size_t>(6, 2 * (ncon + nimm) + 2);
1111 bool converged =
false;
1112 double resnorm = std::numeric_limits<double>::infinity();
1113 bool have_best =
false;
1114 std::vector<double> bx, bs2, bphi, bmu, bzeta;
1115 std::vector<std::size_t> bactive;
1117 double bres = std::numeric_limits<double>::infinity();
1120 for (std::size_t sweep = 0; sweep < aset_max; ++sweep) {
1121 const solver_detail::Ctx ctx =
1122 solver_detail::context(terms, cons, con, imm, active);
1123 std::vector<double> u0;
1124 u0.insert(u0.end(), x.begin(), x.end());
1125 u0.insert(u0.end(), s2.begin(), s2.end());
1126 u0.insert(u0.end(), phi.begin(), phi.end());
1127 u0.insert(u0.end(), mu.begin(), mu.end());
1128 u0.insert(u0.end(), active.size(), 1.0);
1129 std::vector<double> lb(u0.size(), -std::numeric_limits<double>::infinity());
1130 for (std::size_t k = 0; k < nimm; ++k) lb[n + npair + k] = 0.0;
1131 for (std::size_t k = 0; k < active.size(); ++k)
1132 lb[n + npair + nimm + nlatch + k] = 0.0;
1133 for (std::size_t p = 0; p < npair; ++p)
1134 if (ondiag[p]) lb[n + p] = 0.0;
1136 const solver_detail::NewtonResult nr =
1137 solver_detail::newton(ctx, u0,
opt.tol,
opt.newton_max, lb);
1138 iters += nr.iterations;
1139 resnorm = nr.resnorm;
1140 converged = nr.converged;
1141 const solver_detail::Unpacked up =
1142 solver_detail::unpack(nr.u, terms, imm, active.size());
1149 if (!have_best || resnorm < bres) {
1150 bx = x; bs2 = s2; bphi = phi; bmu = mu; bzeta = zeta;
1151 bactive = active; bimm = imm; bres = resnorm; bconv = converged;
1162 for (
double p : phi) scale = std::max(scale, std::fabs(p));
1163 const double thr = -std::max(
opt.tol, 1e-10) * scale;
1164 for (std::size_t k = 0; k < nimm && !moved; ++k)
1171 if (!moved && nimm > 0) {
1172 const double thr = -std::max(
opt.tol, 1e-10);
1173 for (std::size_t s = 0; s < terms.
nm && !moved; ++s) {
1174 if (x[s] >= thr)
continue;
1175 for (std::size_t k = 0; k < nimm; ++k) {
1180 imm.
bind[k] !=
static_cast<std::ptrdiff_t
>(s)) {
1181 imm.
bind[k] =
static_cast<std::ptrdiff_t
>(s);
1189 if (!moved && ncon > 0) {
1190 std::vector<std::size_t> over;
1191 for (std::size_t c = 0; c < ncon; ++c) {
1193 for (std::size_t s = 0; s < n; ++s) val += con.
A(c, s) * x[s];
1194 if (val > con.
b[c] + std::max(1e-9,
opt.tol) &&
1195 std::find(active.begin(), active.end(), c) == active.end())
1198 if (!over.empty()) {
1199 active.insert(active.end(), over.begin(), over.end());
1202 std::vector<std::size_t> keep;
1203 for (std::size_t i = 0; i < active.size(); ++i)
1204 if (!(i < zeta.size() && zeta[i] > 1 + std::max(1e-9,
opt.tol)))
1205 keep.push_back(active[i]);
1206 if (keep.size() != active.size()) {
1216 if (have_best && !converged && bconv) {
1217 x = bx; s2 = bs2; phi = bphi; mu = bmu; zeta = bzeta;
1218 active = bactive; imm = bimm; converged =
true; resnorm = bres;
1222 "The simultaneous closure solve stopped at residual " + std::to_string(resnorm) +
1223 " after " + std::to_string(iters) +
" Newton steps without reaching " +
1224 std::to_string(
opt.tol) +
". The reported point is the last iterate.");
1228 for (std::size_t s = 0; s < terms.
nm; ++s)
1229 if (x[s] < -std::max(
opt.tol, 1e-10)) {
1231 "The fixed point holds " + std::to_string(x[s]) +
" tokens at " +
1233 ", which is negative: no immediate transition could be rebound to pin that "
1234 "place at zero. Use SolverCTMC, SolverSSA or SolverLDES for this net.");
1238 const solver_detail::Ctx ctx = solver_detail::context(terms, cons, con, imm, active);
1239 std::vector<double> ufin;
1240 ufin.insert(ufin.end(), x.begin(), x.end());
1241 ufin.insert(ufin.end(), s2.begin(), s2.end());
1242 ufin.insert(ufin.end(), phi.begin(), phi.end());
1243 ufin.insert(ufin.end(), mu.begin(), mu.end());
1244 ufin.insert(ufin.end(), zeta.begin(), zeta.end());
1245 const solver_detail::Residual fin = solver_detail::residual(ufin, ctx,
false);
1256 for (std::size_t s = 0; s < terms.
nm; ++s) {
1258 const std::size_t i =
static_cast<std::size_t
>(terms.
coord_station[s]);
1260 out.
QN(i, k) += x[s];
1261 out.
UN(i, k) = out.
QN(i, k);
1263 for (std::map<std::size_t, std::vector<std::size_t>>::const_iterator it =
1266 const std::size_t i = it->first / K, k = it->first % K;
1267 const std::vector<double>& w = terms.
consumer_w.at(it->first);
1269 for (std::size_t q = 0; q < it->second.size(); ++q) val += w[q] * fin.r[it->second[q]];
1270 out.
TN(i, k) += val;
1272 for (std::map<std::size_t, std::vector<std::size_t>>::const_iterator it =
1275 const std::size_t i = it->first / K, k = it->first % K;
1277 for (std::size_t e : it->second) val += fin.r[e];
1278 out.
TN(i, k) += val;
1280 for (std::size_t i = 0; i < M; ++i)
1281 for (std::size_t k = 0; k < K; ++k)
1282 if (out.
TN(i, k) > 1e-14) out.
RN(i, k) = out.
QN(i, k) / out.
TN(i, k);
1290 out.
Sigma = fin.Sigma;
1293 for (std::size_t s = 0; s < terms.
nm; ++s) {
1295 const std::size_t i =
static_cast<std::size_t
>(terms.
coord_station[s]);
1298 for (std::size_t i = 0; i < M; ++i)
1299 for (std::size_t k = 0; k < K; ++k) out.
QStd(i, k) = std::sqrt(out.
QVar(i, k));
1304 for (std::size_t s = 0; s < terms.
nm; ++s) {
1307 std::max(0.0, fin.Sigma(s, s));
1311 for (std::size_t j = 0; j < terms.
modes.size(); ++j) {
1313 for (std::size_t e = 0; e < terms.
nev; ++e)
1314 if (terms.
ev_mode[e] ==
static_cast<int>(j) &&
1323 for (std::size_t c = 0; c < cons.
C.
rows(); ++c) {
1325 for (std::size_t s = 0; s < terms.
nstate; ++s) val += cons.
C(c, s) * x[s];