1
/* PeTe - Petri Engine exTremE
2
* Copyright (C) 2011 Jonas Finnemann Jensen <jopsen@gmail.com>,
3
* Thomas Søndersø Nielsen <primogens@gmail.com>,
4
* Lars Kærlund Østergaard <larsko@gmail.com>,
5
* Peter Gjøl Jensen <root@petergjoel.dk>
7
* This program is free software: you can redistribute it and/or modify
8
* it under the terms of the GNU General Public License as published by
9
* the Free Software Foundation, either version 3 of the License, or
10
* (at your option) any later version.
12
* This program is distributed in the hope that it will be useful,
13
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
* GNU General Public License for more details.
17
* You should have received a copy of the GNU General Public License
18
* along with this program. If not, see <http://www.gnu.org/licenses/>.
24
#include "PetriEngine/PetriNetBuilder.h"
25
#include "PetriEngine/PetriNet.h"
26
#include "PetriEngine/PQL/PQLParser.h"
27
#include "PetriEngine/PQL/PQL.h"
28
#include "PetriEngine/PQL/Contexts.h"
29
#include "PetriEngine/Reducer.h"
30
#include "PetriEngine/PQL/Expressions.h"
34
namespace PetriEngine {
36
PetriNetBuilder::PetriNetBuilder() : AbstractPetriNetBuilder(),
39
PetriNetBuilder::PetriNetBuilder(const PetriNetBuilder& other)
40
: _placenames(other._placenames), _transitionnames(other._transitionnames),
41
_transitions(other._transitions), _places(other._places),
42
initialMarking(other.initialMarking), reducer(this)
47
void PetriNetBuilder::addPlace(const string &name, int tokens, double, double) {
48
if(_placenames.count(name) == 0)
50
uint32_t next = _placenames.size();
51
_places.emplace_back();
52
_placenames[name] = next;
55
uint32_t id = _placenames[name];
57
while(initialMarking.size() <= id) initialMarking.emplace_back();
58
initialMarking[id] = tokens;
62
void PetriNetBuilder::addTransition(const string &name,
64
if(_transitionnames.count(name) == 0)
66
uint32_t next = _transitionnames.size();
67
_transitions.emplace_back();
68
_transitionnames[name] = next;
72
void PetriNetBuilder::addInputArc(const string &place, const string &transition, bool inhibitor, int weight) {
73
if(_transitionnames.count(transition) == 0)
75
addTransition(transition,0.0,0.0);
77
if(_placenames.count(place) == 0)
79
addPlace(place,0,0,0);
81
uint32_t p = _placenames[place];
82
uint32_t t = _transitionnames[transition];
88
arc.inhib = inhibitor;
89
assert(t < _transitions.size());
90
assert(p < _places.size());
91
_transitions[t].pre.push_back(arc);
92
_transitions[t].inhib |= inhibitor;
93
_places[p].consumers.push_back(t);
94
_places[p].inhib |= inhibitor;
97
void PetriNetBuilder::addOutputArc(const string &transition, const string &place, int weight) {
98
if(_transitionnames.count(transition) == 0)
100
addTransition(transition,0,0);
102
if(_placenames.count(place) == 0)
104
addPlace(place,0,0,0);
106
uint32_t p = _placenames[place];
107
uint32_t t = _transitionnames[transition];
109
assert(t < _transitions.size());
110
assert(p < _places.size());
116
_transitions[t].post.push_back(arc);
117
_places[p].producers.push_back(t);
120
uint32_t PetriNetBuilder::nextPlaceId(std::vector<uint32_t>& counts, std::vector<uint32_t>& pcounts, std::vector<uint32_t>& ids, bool reorder)
122
uint32_t cand = std::numeric_limits<uint32_t>::max();
123
uint32_t cnt = std::numeric_limits<uint32_t>::max();
124
for(uint32_t i = 0; i < _places.size(); ++i)
126
uint32_t nnum = (pcounts[i] == 0 ? 0 : (counts[0] == 0 ? 0 : std::max(counts[i], pcounts[i])));
127
if( ids[i] == std::numeric_limits<uint32_t>::max() &&
131
if(!reorder) return i;
139
PetriNet* PetriNetBuilder::makePetriNet(bool reorder) {
142
* The basic idea is to construct three arrays, the first array,
143
* _invariants points to "arcs" - they are triplets (weight, place, inhibitor)
144
* _transitions are pairs, (input, output) are indexes in the _invariants array
145
* _placeToPtrs is an indirection going from a place-index to the FIRST transition
146
* with a non-inhibitor arc consuming from the given place.
148
* For all the indexes and indirections, notice that we only track the
149
* beginning. We can naturally use the "next" value as the end. eg. the
150
* inputs of a transition are between "input" and "output". The outputs
151
* are between "output" and the "input" of the next transition.
153
* This allows us to quickly skip a lot of checks when generating successors
154
* Beware that currently "orphans" and "inhibitor orphans" are special-cases
155
* and currently handled as "consuming" from place id=0.
157
* If anybody wants to spend time on it, this is the first step towards
158
* a decision-tree like construction, possibly improving successor generation.
161
uint32_t nplaces = _places.size() - reducer.RemovedPlaces();
162
uint32_t ntrans = _transitions.size() - reducer.RemovedTransitions();
164
std::vector<uint32_t> place_cons_count = std::vector<uint32_t>(_places.size());
165
std::vector<uint32_t> place_prod_count = std::vector<uint32_t>(_places.size());
166
std::vector<uint32_t> place_idmap = std::vector<uint32_t>(_places.size());
167
std::vector<uint32_t> trans_idmap = std::vector<uint32_t>(_transitions.size());
169
uint32_t invariants = 0;
171
for(uint32_t i = 0; i < _places.size(); ++i)
173
place_idmap[i] = std::numeric_limits<uint32_t>::max();
176
place_cons_count[i] = _places[i].consumers.size();
177
place_prod_count[i] = _places[i].producers.size();
178
invariants += _places[i].consumers.size() + _places[i].producers.size();
182
for(uint32_t i = 0; i < _transitions.size(); ++i)
184
trans_idmap[i] = std::numeric_limits<uint32_t>::max();
187
PetriNet* net = new PetriNet(ntrans, invariants, nplaces);
189
uint32_t next = nextPlaceId(place_cons_count, place_prod_count, place_idmap, reorder);
191
uint32_t freeinv = 0;
192
uint32_t freetrans = 0;
194
// first handle orphans
196
if(place_idmap.size() > next) place_idmap[next] = free;
197
net->_placeToPtrs[free] = freetrans;
198
for(size_t t = 0; t < _transitions.size(); ++t)
200
Transition& trans = _transitions[t];
201
if (std::all_of(trans.pre.begin(), trans.pre.end(), [](Arc& a){return a.inhib;}))
203
// ALL have to be inhibitor, if any. Otherwise not orphan
205
if(trans.skip) continue;
206
net->_transitions[freetrans].inputs = freeinv;
209
for(auto pre : trans.pre)
211
Invariant& iv = net->_invariants[freeinv];
212
iv.place = pre.place;
213
iv.tokens = pre.weight;
214
iv.inhibitor = pre.inhib;
216
assert(place_cons_count[pre.place] > 0);
217
--place_cons_count[pre.place];
221
net->_transitions[freetrans].outputs = freeinv;
223
for(auto post : trans.post)
225
assert(freeinv < net->_ninvariants);
226
net->_invariants[freeinv].place = post.place;
227
net->_invariants[freeinv].tokens = post.weight;
231
trans_idmap[t] = freetrans;
239
while(next != std::numeric_limits<uint32_t>::max())
241
if(first) // already set for first iteration to handle orphans
247
place_idmap[next] = free;
248
net->_placeToPtrs[free] = freetrans;
251
for(auto t : _places[next].consumers)
253
Transition& trans = _transitions[t];
254
if(trans.skip) continue;
256
net->_transitions[freetrans].inputs = freeinv;
258
// check first, we are going to change state later, but we can
259
// break here, so no statechange inside loop!
261
bool all_inhib = true;
263
for(const Arc& pre : trans.pre)
265
all_inhib &= pre.inhib;
267
// if transition belongs to previous place
268
if( (!pre.inhib && place_idmap[pre.place] < free) ||
269
freeinv + cnt >= net->_ninvariants)
275
// or arc from place is an inhibitor
276
if(pre.place == next && pre.inhib)
284
// skip for now, either T-a->P is inhibitor, or was allready added for other P'
285
// or all a's are inhibitors.
286
if(!ok || all_inhib) continue;
288
trans_idmap[t] = freeinv;
290
// everything is good, change state!.
291
for(auto pre : trans.pre)
293
Invariant& iv = net->_invariants[freeinv];
294
iv.place = pre.place;
295
iv.tokens = pre.weight;
296
iv.inhibitor = pre.inhib;
298
assert(place_cons_count[pre.place] > 0);
299
--place_cons_count[pre.place];
302
net->_transitions[freetrans].outputs = freeinv;
303
for(auto post : trans.post)
305
assert(freeinv < net->_ninvariants);
306
auto& post_inv = net->_invariants[freeinv];
307
post_inv.place = post.place;
308
post_inv.tokens = post.weight;
309
--place_prod_count[post.place];
313
trans_idmap[t] = freetrans;
316
assert(freeinv <= invariants);
319
next = nextPlaceId(place_cons_count, place_prod_count, place_idmap, reorder);
322
// Reindex for great justice!
323
for(uint32_t i = 0; i < freeinv; i++)
325
net->_invariants[i].place = place_idmap[net->_invariants[i].place];
326
assert(net->_invariants[i].place < nplaces);
327
assert(net->_invariants[i].tokens > 0);
330
// std::cout << "init" << std::endl;
331
for(uint32_t i = 0; i < _places.size(); ++i)
333
if(place_idmap[i] != std::numeric_limits<uint32_t>::max())
335
net->_initialMarking[place_idmap[i]] = initialMarking[i];
336
// std::cout << place_idmap[i] << " : " << initialMarking[i] << std::endl;
340
// reindex place-names
342
net->_placenames.resize(_placenames.size());
343
int rindex = _placenames.size() - 1;
344
for(auto& i : _placenames)
346
i.second = place_idmap[i.second];
347
if(i.second != std::numeric_limits<uint32_t>::max())
349
net->_placenames[i.second] = i.first;
350
assert(_placenames[net->_placenames[i.second]] == i.second);
354
net->_placenames[rindex] = i.first;
359
net->_transitionnames.resize(_transitionnames.size());
360
int trindex = _transitionnames.size() - 1;
361
for(auto& i : _transitionnames)
363
i.second = trans_idmap[i.second];
364
if(i.second != std::numeric_limits<uint32_t>::max())
366
net->_transitionnames[i.second] = i.first;
370
net->_transitionnames[trindex] = i.first;
376
for(size_t t = 0; t < net->numberOfTransitions(); ++t)
379
auto tiv = std::make_pair(&net->_invariants[net->_transitions[t].inputs], &net->_invariants[net->_transitions[t].outputs]);
380
for(; tiv.first != tiv.second; ++tiv.first)
382
tiv.first->direction = tiv.first->inhibitor ? 0 : -1;
384
auto tov = std::make_pair(&net->_invariants[net->_transitions[t].outputs], &net->_invariants[net->_transitions[t + 1].inputs]);
385
for(; tov.first != tov.second; ++tov.first)
387
if(tov.first->place == tiv.first->place)
390
if(tiv.first->inhibitor) tiv.first->direction = tov.first->direction = 1;
391
else if(tiv.first->tokens < tov.first->tokens) tiv.first->direction = tov.first->direction = 1;
392
else if(tiv.first->tokens == tov.first->tokens) tiv.first->direction = tov.first->direction = 0;
393
else if(tiv.first->tokens > tov.first->tokens) tiv.first->direction = tov.first->direction = -1;
397
if(!found) assert(tiv.first->direction < 0 || tiv.first->inhibitor);
401
auto tiv = std::make_pair(&net->_invariants[net->_transitions[t].outputs], &net->_invariants[net->_transitions[t + 1].inputs]);
402
for(; tiv.first != tiv.second; ++tiv.first)
404
tiv.first->direction = 1;
406
auto tov = std::make_pair(&net->_invariants[net->_transitions[t].inputs], &net->_invariants[net->_transitions[t].outputs]);
407
for(; tov.first != tov.second; ++tov.first)
410
if(tov.first->place == tiv.first->place)
412
if (tov.first->inhibitor) tiv.first->direction = tov.first->direction = 1;
413
else if(tiv.first->tokens > tov.first->tokens) tiv.first->direction = tov.first->direction = 1;
414
else if(tiv.first->tokens == tov.first->tokens) tiv.first->direction = tov.first->direction = 0;
415
else if(tiv.first->tokens < tov.first->tokens) tiv.first->direction = tov.first->direction = -1;
419
if(!found) assert(tiv.first->direction > 0);
427
void PetriNetBuilder::sort()
429
for(Place& p : _places)
431
std::sort(p.consumers.begin(), p.consumers.end());
432
std::sort(p.producers.begin(), p.producers.end());
435
for(Transition& t : _transitions)
437
std::sort(t.pre.begin(), t.pre.end());
438
std::sort(t.post.begin(), t.post.end());
442
void PetriNetBuilder::reduce( std::vector<std::shared_ptr<PQL::Condition> >& queries,
443
std::vector<Reachability::ResultPrinter::Result>& results,
444
int reductiontype, bool reconstructTrace, const PetriNet* net, int timeout, std::vector<uint32_t>& reductions)
446
QueryPlaceAnalysisContext placecontext(getPlaceNames(), getTransitionNames(), net);
447
bool all_reach = true;
448
bool remove_loops = true;
449
bool contains_next = false;
450
for(uint32_t i = 0; i < queries.size(); ++i)
452
if(results[i] == Reachability::ResultPrinter::Unknown ||
453
results[i] == Reachability::ResultPrinter::CTL )
455
queries[i]->analyze(placecontext);
456
all_reach &= (results[i] != Reachability::ResultPrinter::CTL);
457
remove_loops &= !queries[i]->isLoopSensitive();
458
// There is a deadlock somewhere, if it is not alone, we cannot reduce.
459
// this has similar problems as nested next.
460
contains_next |= queries[i]->containsNext() || queries[i]->nestedDeadlock();
463
reducer.Reduce(placecontext, reductiontype, reconstructTrace, timeout, remove_loops, all_reach, contains_next, reductions);