106 using Vec = std::vector<double>;
107 using Mat = std::vector<Vec>;
108 const double Inf = std::numeric_limits<double>::infinity();
110 auto structure = [](
const std::string& m) {
115 const std::size_t nn = L.
nodes.size();
116 std::vector<std::size_t> places, transitions;
117 for (std::size_t i = 1; i <= nn; ++i) {
119 if (t == qn::NodeType::Place) places.push_back(i);
120 else if (t == qn::NodeType::Transition) transitions.push_back(i);
121 else if (t != qn::NodeType::Source && t != qn::NodeType::Sink)
122 structure(
"node " + L.
nodes[i - 1].name +
" is neither a Place, a Transition, a "
123 "Source nor a Sink; the ispn method solves pure stochastic Petri nets");
127 "solver_mam_ispn: the ispn method needs a model holding Place nodes");
128 const std::size_t NP = places.size();
129 const std::size_t PL = NP * R;
130 auto lvl = [R](std::size_t pp, std::size_t k) {
return pp * R + k; };
131 auto place_of = [&places](std::size_t node) ->
long {
132 for (std::size_t pp = 0; pp < places.size(); ++pp)
133 if (places[pp] == node)
return static_cast<long>(pp);
138 Mat pre(PL), post(PL), inh(PL);
140 std::vector<bool> imm;
141 std::size_t ncol = 0;
142 auto add_col = [&]() {
143 for (std::size_t l = 0; l < PL; ++l) {
144 pre[l].push_back(0.0);
145 post[l].push_back(0.0);
146 inh[l].push_back(Inf);
150 for (
const std::size_t ind : transitions) {
154 const std::string& tname = L.
nodes[ind - 1].name;
155 for (std::size_t m = 0; m < tp.
nmodes; ++m) {
157 structure(
"mode " + std::to_string(m + 1) +
" of transition " + tname +
158 " has a marking-dependent firing rate, which the ispn method does "
168 expo = proc.
order() == 1;
169 if (expo) r = D(proc.
D1(0, 0));
173 "solver_mam_ispn: mode " + std::to_string(m + 1) +
" of transition " +
174 tname +
" has a non-exponential firing time; the ispn method needs "
175 "exponential or immediate transitions");
180 const std::size_t c = add_col();
181 for (std::size_t pp = 0; pp < NP; ++pp) {
182 const std::size_t q = places[pp] - 1;
183 for (std::size_t k = 0; k < R; ++k) {
184 const std::size_t l = lvl(pp, k);
185 if (en && q < en->rows() && k < en->cols()) pre[l][c] = std::max(0.0, D((*en)(q, k)));
186 if (fi && q < fi->rows() && k < fi->cols()) post[l][c] = std::max(0.0, D((*fi)(q, k)));
187 if (ih && q < ih->rows() && k < ih->cols()) {
188 const double v = D((*ih)(q, k));
190 inh[l][c] = (v <= 0 || std::isnan(v)) ? Inf : v;
195 if (ns >= 1e9) ns = Inf;
197 imm.push_back(isimm);
201 const std::size_t nmodes = ncol;
205 std::vector<std::size_t> srcst(R, 0);
206 for (std::size_t ind = 1; ind <= nn; ++ind) {
207 if (L.
nodes[ind - 1].nodetype != qn::NodeType::Source)
continue;
208 const std::size_t ist = L.
nodes[ind - 1].station;
209 if (ist == 0)
continue;
210 for (std::size_t k = 0; k < R; ++k) {
211 if (L.
disabled[ist - 1][k])
continue;
212 const double lk = D(L.
rates(ist - 1, k));
213 if (std::isnan(lk) || lk <= 0)
continue;
216 "solver_mam_ispn: class " + L.
classes[k].name +
" arrives at the Source with "
217 "a non-exponential process; the ispn method needs Poisson arrivals");
220 for (std::size_t jnd = 1; jnd <= nn; ++jnd)
221 for (std::size_t r = 0; r < R; ++r) {
222 const std::size_t row = (ind - 1) * R + k, col = (jnd - 1) * R + r;
224 const double pr = D(L.
rtnodes(row, col));
225 if (!(pr > 0))
continue;
226 const long pp = place_of(jnd);
228 structure(
"the Source routes class " + L.
classes[k].name +
" to " +
229 L.
nodes[jnd - 1].name +
", which is not a Place");
230 const std::size_t c = add_col();
231 post[lvl(
static_cast<std::size_t
>(pp), r)][c] = 1.0;
232 rate.push_back(lk * pr);
233 imm.push_back(
false);
242 for (std::size_t pp = 0; pp < NP; ++pp) {
245 for (std::size_t k = 0; k < R && k < it->second.size(); ++k) {
246 const double v = D(it->second[k]);
248 anyset = anyset || v != 0;
252 std::fill(m0.begin(), m0.end(), 0.0);
253 for (std::size_t k = 0; k < R; ++k) {
254 const double nj = L.
classes[k].population;
255 if (!std::isfinite(nj) || nj <= 0)
continue;
256 const std::size_t rs = L.
classes[k].refstat;
257 if (rs == 0 || rs > L.
nstations)
continue;
258 for (std::size_t pp = 0; pp < NP; ++pp)
259 if (L.
nodes[places[pp] - 1].station == rs) {
267 std::vector<std::string> lname(PL);
268 for (std::size_t pp = 0; pp < NP; ++pp)
269 for (std::size_t k = 0; k < R; ++k)
270 lname[lvl(pp, k)] = R == 1 ? L.
nodes[places[pp] - 1].name
271 : L.
nodes[places[pp] - 1].name +
"." + L.
classes[k].name;
272 auto join = [&lname](
const std::vector<std::size_t>& ix) {
274 for (std::size_t i = 0; i < ix.size(); ++i) s += (i ?
", " :
"") + lname[ix[i]];
277 auto used_level = [&](std::size_t l) {
278 for (std::size_t c = 0; c < ncol; ++c)
279 if (pre[l][c] != 0 || post[l][c] != 0)
return true;
283 if (!
opt.ispn_place.empty()) {
284 const std::string& want =
opt.ispn_place;
285 for (std::size_t l = 0; l < PL && p0 == PL; ++l)
286 if (lname[l] == want) p0 = l;
287 if (p0 == PL && R > 1) {
288 for (std::size_t pp = 0; pp < NP; ++pp) {
289 if (L.
nodes[places[pp] - 1].name != want)
continue;
290 std::vector<std::size_t> used;
291 for (std::size_t k = 0; k < R; ++k)
292 if (used_level(lvl(pp, k))) used.push_back(lvl(pp, k));
293 if (used.size() == 1) p0 = used[0];
298 std::vector<std::size_t> all(PL);
299 for (std::size_t l = 0; l < PL; ++l) all[l] = l;
301 "solver_mam_ispn: options.config.ispn_place = '" + want +
"' names no place "
302 "level; the levels are " + join(all));
305 Mat C(PL, Vec(ncol, 0.0));
306 for (std::size_t l = 0; l < PL; ++l)
307 for (std::size_t c = 0; c < ncol; ++c) C[l][c] = post[l][c] - pre[l][c];
309 std::vector<std::size_t> cand;
310 for (std::size_t l = 0; l < PL; ++l)
311 if (!covered[l] && (used_level(l) || m0[l] > 0)) cand.push_back(l);
313 structure(
"every place is covered by a non-negative S-invariant, so the net is "
314 "bounded and has a finite state space; solve it with SolverCTMC");
316 structure(
"the iSPN needs exactly one unbounded place, but " +
317 std::to_string(cand.size()) +
" places are not covered by an S-invariant (" +
318 join(cand) +
"); name the unbounded one with options.config.ispn_place");
324 if (
opt.ispn_maxlevel > 0) iopt.
maxlevel =
static_cast<int>(
opt.ispn_maxlevel);
338 for (std::size_t pp = 0; pp < NP; ++pp) {
339 const std::size_t ist = L.
nodes[places[pp] - 1].station;
340 if (ist == 0)
continue;
341 for (std::size_t k = 0; k < R; ++k) {
342 const std::size_t l = lvl(pp, k);
344 for (std::size_t c = 0; c < nmodes && c < res.
tput.size(); ++c) tk += res.
tput[c] * pre[l][c];
349 Vec X(R, 0.0), Cv(R, 0.0);
350 for (std::size_t k = 0; k < R; ++k) {
352 Tp(srcst[k] - 1, k) = lambda[k];
355 const std::size_t rs = L.
classes[k].refstat;
356 if (rs >= 1 && rs <= M) X[k] = Tp(rs - 1, k);
359 for (std::size_t i = 0; i < M; ++i) qs += Q(i, k);
360 if (X[k] > 0) Cv[k] = qs / X[k];
362 for (std::size_t i = 0; i < M; ++i)
363 for (std::size_t k = 0; k < R; ++k) {
364 s.
Q(i, k) = F(Q(i, k));
365 s.
U(i, k) = F(Q(i, k));
366 s.
Tp(i, k) = F(Tp(i, k));
367 if (Tp(i, k) > 0) s.
R(i, k) = F(Q(i, k) / Tp(i, k));
371 for (std::size_t k = 0; k < R; ++k) {