5
* Created on 15 February 2014, 10:50
8
#include "PetriEngine/Reducer.h"
9
#include "PetriEngine/PetriNet.h"
10
#include "PetriEngine/PetriNetBuilder.h"
11
#include "PetriParse/PNMLParser.h"
16
namespace PetriEngine {
18
Reducer::Reducer(PetriNetBuilder* p)
26
void Reducer::Print(QueryPlaceAnalysisContext& context) {
27
std::cout << "\nNET INFO:\n"
28
<< "Number of places: " << parent->numberOfPlaces() << std::endl
29
<< "Number of transitions: " << parent->numberOfTransitions()
30
<< std::endl << std::endl;
31
for (uint32_t t = 0; t < parent->numberOfTransitions(); t++) {
32
std::cout << "Transition " << t << " :\n";
33
if(parent->_transitions[t].skip)
35
std::cout << "\tSKIPPED" << std::endl;
37
for(auto& arc : parent->_transitions[t].pre)
40
std::cout << "\tInput place " << arc.place
41
<< " (" << getPlaceName(arc.place) << ")"
42
<< " with arc-weight " << arc.weight << std::endl;
44
for(auto& arc : parent->_transitions[t].post)
47
std::cout << "\tOutput place " << arc.place
48
<< " (" << getPlaceName(arc.place) << ")"
49
<< " with arc-weight " << arc.weight << std::endl;
51
std::cout << std::endl;
53
for (uint32_t i = 0; i < parent->numberOfPlaces(); i++) {
54
std::cout << "Marking at place "<< i <<
55
" is: " << parent->initMarking()[i] << std::endl;
57
for (uint32_t i = 0; i < parent->numberOfPlaces(); i++) {
58
std::cout << "Query count for place " << i
59
<< " is: " << context.getQueryPlaceCount()[i] << std::endl;
63
std::string Reducer::getTransitionName(uint32_t transition)
65
for(auto t : parent->_transitionnames)
67
if(t.second == transition) return t.first;
73
std::string Reducer::newTransName()
76
auto tmp = prefix + std::to_string(_tnameid);
77
while(parent->_transitionnames.count(tmp) >= 1)
80
tmp = prefix + std::to_string(_tnameid);
86
std::string Reducer::getPlaceName(uint32_t place)
88
for(auto t : parent->_placenames)
90
if(t.second == place) return t.first;
96
Transition& Reducer::getTransition(uint32_t transition)
98
return parent->_transitions[transition];
101
ArcIter Reducer::getOutArc(Transition& trans, uint32_t place)
105
auto ait = std::lower_bound(trans.post.begin(), trans.post.end(), a);
106
if(ait != trans.post.end() && ait->place == place)
112
return trans.post.end();
116
ArcIter Reducer::getInArc(uint32_t place, Transition& trans)
120
auto ait = std::lower_bound(trans.pre.begin(), trans.pre.end(), a);
121
if(ait != trans.pre.end() && ait->place == place)
127
return trans.pre.end();
131
void Reducer::eraseTransition(std::vector<uint32_t>& set, uint32_t el)
133
auto lb = std::lower_bound(set.begin(), set.end(), el);
134
assert(lb != set.end());
139
void Reducer::skipTransition(uint32_t t)
141
++_removedTransitions;
142
Transition& trans = getTransition(t);
144
for(auto p : trans.post)
146
eraseTransition(parent->_places[p.place].producers, t);
148
for(auto p : trans.pre)
150
eraseTransition(parent->_places[p.place].consumers, t);
155
assert(consistent());
156
_skipped_trans.push_back(t);
159
void Reducer::skipPlace(uint32_t place)
162
Place& pl = parent->_places[place];
165
for(auto& t : pl.consumers)
167
Transition& trans = getTransition(t);
168
auto ait = getInArc(place, trans);
169
if(ait != trans.pre.end() && ait->place == place)
170
trans.pre.erase(ait);
173
for(auto& t : pl.producers)
175
Transition& trans = getTransition(t);
176
auto ait = getOutArc(trans, place);
177
if(ait != trans.post.end() && ait->place == place)
178
trans.post.erase(ait);
180
pl.consumers.clear();
181
pl.producers.clear();
182
assert(consistent());
186
bool Reducer::consistent()
190
for(size_t i = 0; i < parent->numberOfTransitions(); ++i)
192
Transition& t = parent->_transitions[i];
194
assert(std::is_sorted(t.pre.begin(), t.pre.end()));
195
assert(std::is_sorted(t.post.end(), t.post.end()));
196
assert(!t.skip || (t.pre.size() == 0 && t.post.size() == 0));
199
assert(a.weight > 0);
200
Place& p = parent->_places[a.place];
202
assert(std::find(p.consumers.begin(), p.consumers.end(), i) != p.consumers.end());
206
assert(a.weight > 0);
207
Place& p = parent->_places[a.place];
209
assert(std::find(p.producers.begin(), p.producers.end(), i) != p.producers.end());
213
assert(strans == _removedTransitions);
216
for(size_t i = 0; i < parent->numberOfPlaces(); ++i)
218
Place& p = parent->_places[i];
219
if(p.skip) ++splaces;
220
assert(std::is_sorted(p.consumers.begin(), p.consumers.end()));
221
assert(std::is_sorted(p.producers.begin(), p.producers.end()));
222
assert(!p.skip || (p.consumers.size() == 0 && p.producers.size() == 0));
224
for(uint c : p.consumers)
226
Transition& t = parent->_transitions[c];
228
auto a = getInArc(i, t);
229
assert(a != t.pre.end());
230
assert(a->place == i);
233
for(uint prod : p.producers)
235
Transition& t = parent->_transitions[prod];
237
auto a = getOutArc(t, i);
238
assert(a != t.post.end());
239
assert(a->place == i);
242
assert(splaces == _removedPlaces);
247
bool Reducer::ReducebyRuleA(uint32_t* placeInQuery) {
248
// Rule A - find transition t that has exactly one place in pre and post and remove one of the places (and t)
249
bool continueReductions = false;
250
const size_t numberoftransitions = parent->numberOfTransitions();
251
for (uint32_t t = 0; t < numberoftransitions; t++) {
252
if(hasTimedout()) return false;
253
Transition& trans = getTransition(t);
255
// we have already removed
256
if(trans.skip) continue;
258
// A2. we have more/less than one arc in pre or post
259
// checked first to avoid out-of-bounds when looking up indexes.
260
if(trans.pre.size() != 1) continue;
262
uint32_t pPre = trans.pre[0].place;
264
// A2. Check that pPre goes only to t
265
if(parent->_places[pPre].consumers.size() != 1) continue;
267
// A3. We have weight of more than one on input
268
// and is empty on output (should not happen).
269
auto w = trans.pre[0].weight;
271
for(auto t : parent->_places[pPre].producers)
273
if((getOutArc(parent->_transitions[t], trans.pre[0].place)->weight % w) != 0)
282
// A4. Do inhibitor check, neither T, pPre or pPost can be involved with any inhibitor
283
if(parent->_places[pPre].inhib|| trans.inhib) continue;
285
// A5. dont mess with query!
286
if(placeInQuery[pPre] > 0) continue;
287
// check A1, A4 and A5 for post
288
for(auto& pPost : trans.post)
290
if(parent->_places[pPost.place].inhib || pPre == pPost.place || placeInQuery[pPost.place] > 0)
298
continueReductions = true;
301
// here we need to remember when a token is created in pPre (some
302
// transition with an output in P is fired), t is fired instantly!.
303
if(reconstructTrace) {
304
Place& pre = parent->_places[pPre];
305
std::string tname = getTransitionName(t);
306
for(size_t pp : pre.producers)
308
std::string prefire = getTransitionName(pp);
309
_postfire[prefire].push_back(tname);
311
_extraconsume[tname].emplace_back(getPlaceName(pPre), w);
312
for(size_t i = 0; i < parent->initMarking()[pPre]; ++i)
314
_initfire.push_back(tname);
318
for(auto& pPost : trans.post)
320
// UA2. move the token for the initial marking, makes things simpler.
321
parent->initialMarking[pPost.place] += ((parent->initialMarking[pPre]/w) * pPost.weight);
323
parent->initialMarking[pPre] = 0;
325
// Remove transition t and the place that has no tokens in m0
326
// UA1. remove transition
327
auto toMove = trans.post;
331
for(auto& _t : parent->_places[pPre].producers)
334
// move output-arcs to post.
335
Transition& src = getTransition(_t);
336
auto source = *getOutArc(src, pPre);
337
for(auto& pPost : toMove)
340
a.place = pPost.place;
341
a.weight = (source.weight/w) * pPost.weight;
342
assert(a.weight > 0);
344
auto dest = std::lower_bound(src.post.begin(), src.post.end(), a);
345
if(dest == src.post.end() || dest->place != pPost.place)
347
dest = src.post.insert(dest, a);
348
auto& prod = parent->_places[pPost.place].producers;
349
auto lb = std::lower_bound(prod.begin(), prod.end(), _t);
354
dest->weight += ((source.weight/w) * pPost.weight);
356
assert(dest->weight > 0);
361
} // end of Rule A main for-loop
362
return continueReductions;
365
bool Reducer::ReducebyRuleB(uint32_t* placeInQuery, bool remove_deadlocks, bool remove_consumers) {
367
// Rule B - find place p that has exactly one transition in pre and exactly one in post and remove the place
368
bool continueReductions = false;
369
const size_t numberofplaces = parent->numberOfPlaces();
370
for (uint32_t p = 0; p < numberofplaces; p++) {
371
if(hasTimedout()) return false;
372
Place& place = parent->_places[p];
374
if(place.skip) continue; // already removed
375
// B5. dont mess up query
376
if(placeInQuery[p] > 0)
379
// B2. Only one consumer/producer
380
if( place.consumers.size() != 1 ||
381
place.producers.size() < 1)
382
continue; // no orphan removal
384
auto tIn = place.consumers[0];
386
// B1. producer is not consumer
388
for(auto& tOut : place.producers)
393
continue; // cannot remove this kind either
398
auto prod = place.producers;
399
Transition& in = getTransition(tIn);
400
for(auto tOut : prod)
402
Transition& out = getTransition(tOut);
404
if(out.post.size() != 1 && in.pre.size() != 1)
405
continue; // at least one has to be singular for this to work
407
if((!remove_deadlocks || !remove_consumers) && in.pre.size() != 1)
408
// the buffer can mean deadlocks and other interesting things
409
// also we can "hide" tokens, so we need to make sure not
410
// to remove consumers.
413
if(parent->initMarking()[p] > 0 && in.pre.size() != 1)
416
auto inArc = getInArc(p, in);
417
auto outArc = getOutArc(out, p);
419
// B3. Output is a multiple of input and nonzero.
420
if(outArc->weight < inArc->weight)
422
if((outArc->weight % inArc->weight) != 0)
425
size_t multiplier = outArc->weight / inArc->weight;
427
// B4. Do inhibitor check, neither In, out or place can be involved with any inhibitor
428
if(place.inhib || in.inhib || out.inhib)
431
// B6. also, none of the places in the post-set of consuming transition can be participating in inhibitors.
432
// B7. nor can they appear in the query.
434
bool post_ok = false;
435
for(const Arc& a : in.post)
437
post_ok |= parent->_places[a.place].inhib;
438
post_ok |= placeInQuery[a.place];
446
for(const Arc& a : in.pre)
448
pre_ok |= parent->_places[a.place].inhib;
449
pre_ok |= placeInQuery[a.place];
457
if(in.pre.size() > 1)
458
for(const Arc& arc : out.pre)
459
ok &= placeInQuery[arc.place] == 0;
463
// B2.a Check that there is no other place than p that gives to tPost,
464
// tPre can give to other places
465
auto& arcs = in.pre.size() < out.post.size() ? in.pre : out.post;
466
for (auto& arc : arcs) {
467
if (arc.weight > 0 && arc.place != p) {
476
// UB2. we need to remember initial marking
477
uint initm = parent->initMarking()[p];
478
initm /= inArc->weight; // integer-devision is floor by default
482
// remember reduction for recreation of trace
483
std::string toutname = getTransitionName(tOut);
484
std::string tinname = getTransitionName(tIn);
485
std::string pname = getPlaceName(p);
486
Arc& a = *getInArc(p, in);
487
_extraconsume[tinname].emplace_back(pname, a.weight);
488
for(size_t i = 0; i < multiplier; ++i)
490
_postfire[toutname].push_back(tinname);
493
for(size_t i = 0; initm > 0 && i < initm / inArc->weight; ++i )
495
_initfire.push_back(tinname);
499
continueReductions = true;
501
// UB1. Remove place p
502
parent->initialMarking[p] = 0;
503
// We need to remember that when tOut fires, tIn fires just after.
504
// this should fix the trace
506
// UB3. move arcs from t' to t
507
for (auto& arc : in.post) { // remove tPost
508
auto _arc = getOutArc(out, arc.place);
509
// UB2. Update initial marking
510
parent->initialMarking[arc.place] += initm*arc.weight;
511
if(_arc != out.post.end())
513
_arc->weight += arc.weight*multiplier;
517
out.post.push_back(arc);
518
out.post.back().weight *= multiplier;
519
parent->_places[arc.place].producers.push_back(tOut);
521
std::sort(out.post.begin(), out.post.end());
522
std::sort(parent->_places[arc.place].producers.begin(),
523
parent->_places[arc.place].producers.end());
526
for (auto& arc : in.pre) { // remove tPost
529
auto _arc = getInArc(arc.place, out);
530
// UB2. Update initial marking
531
parent->initialMarking[arc.place] += initm*arc.weight;
532
if(_arc != out.pre.end())
534
_arc->weight += arc.weight*multiplier;
538
out.pre.push_back(arc);
539
out.pre.back().weight *= multiplier;
540
parent->_places[arc.place].consumers.push_back(tOut);
542
std::sort(out.pre.begin(), out.pre.end());
543
std::sort(parent->_places[arc.place].consumers.begin(),
544
parent->_places[arc.place].consumers.end());
548
for(auto it = out.post.begin(); it != out.post.end(); ++it)
556
for(auto it = place.producers.begin(); it != place.producers.end(); ++it)
560
place.producers.erase(it);
565
// UB1. remove transition
566
if(place.producers.size() == 0)
571
} // end of Rule B main for-loop
572
assert(consistent());
573
return continueReductions;
576
bool Reducer::ReducebyRuleC(uint32_t* placeInQuery) {
577
// Rule C - Places with same input and output-transitions which a modulo each other
578
bool continueReductions = false;
580
_pflags.resize(parent->_places.size(), 0);
581
std::fill(_pflags.begin(), _pflags.end(), 0);
583
for(uint32_t touter = 0; touter < parent->numberOfTransitions(); ++touter)
584
for(size_t outer = 0; outer < parent->_transitions[touter].post.size(); ++outer)
586
auto pouter = parent->_transitions[touter].post[outer].place;
587
if(_pflags[pouter] > 0) continue;
589
if(hasTimedout()) return false;
590
if(parent->_places[pouter].skip) continue;
593
if(parent->_places[pouter].inhib) continue;
595
for (size_t inner = outer + 1; inner < parent->_transitions[touter].post.size(); ++inner)
597
auto pinner = parent->_transitions[touter].post[inner].place;
598
if(parent->_places[pinner].skip) continue;
601
if(parent->_places[pinner].inhib) continue;
603
for(size_t swp = 0; swp < 2; ++swp)
605
if(hasTimedout()) return false;
606
if( parent->_places[pinner].skip ||
607
parent->_places[pouter].skip) break;
612
if(swp == 1) std::swap(p1, p2);
614
Place& place1 = parent->_places[p1];
616
// C1. Not same place
619
// C5. Dont mess with query
620
if(placeInQuery[p2] > 0)
623
Place& place2 = parent->_places[p2];
625
// C2, C3. Consumer and producer-sets must match
626
if(place1.consumers.size() < place2.consumers.size() ||
627
place1.producers.size() > place2.producers.size())
630
long double mult = 1;
632
// C8. Consumers must match with weights
635
for(size_t i = 0; i < place2.consumers.size(); ++i)
637
while(j < place1.consumers.size() && place1.consumers[j] < place2.consumers[i] ) ++j;
638
if(place1.consumers.size() <= j || place1.consumers[j] != place2.consumers[i])
644
Transition& trans = getTransition(place1.consumers[j]);
645
auto a1 = getInArc(p1, trans);
646
auto a2 = getInArc(p2, trans);
647
assert(a1 != trans.pre.end());
648
assert(a2 != trans.pre.end());
649
mult = std::max(mult, ((long double)a2->weight) / ((long double)a1->weight));
654
// C6. We do not care about excess markings in p2.
655
if(mult != std::numeric_limits<long double>::max() &&
656
(((long double)parent->initialMarking[p1]) * mult) > ((long double)parent->initialMarking[p2]))
662
// C7. Producers must match with weights
664
for(size_t i = 0; i < place1.producers.size(); ++i)
666
while(j < place2.producers.size() && place2.producers[j] < place1.producers[i]) ++j;
667
if(j == place2.producers.size() || place1.producers[i] != place2.producers[j])
673
Transition& trans = getTransition(place1.producers[i]);
674
auto a1 = getOutArc(trans, p1);
675
auto a2 = getOutArc(trans, p2);
676
assert(a1 != trans.post.end());
677
assert(a2 != trans.post.end());
679
if(((long double)a1->weight)*mult > ((long double)a2->weight))
687
else if(ok == 1) continue;
689
parent->initialMarking[p2] = 0;
693
for(auto t : place2.consumers)
695
std::string tname = getTransitionName(t);
696
const ArcIter arc = getInArc(p2, getTransition(t));
697
_extraconsume[tname].emplace_back(getPlaceName(p2), arc->weight);
701
continueReductions = true;
710
assert(consistent());
711
return continueReductions;
714
bool Reducer::ReducebyRuleD(uint32_t* placeInQuery) {
715
// Rule D - two transitions with the same pre and post and same inhibitor arcs
716
// This does not alter the trace.
717
bool continueReductions = false;
718
_tflags.resize(parent->_transitions.size(), 0);
719
std::fill(_tflags.begin(), _tflags.end(), 0);
720
bool has_empty_trans = false;
721
for(size_t t = 0; t < parent->_transitions.size(); ++t)
723
auto& trans = parent->_transitions[t];
724
if(!trans.skip && trans.pre.size() == 0 && trans.post.size() == 0)
731
has_empty_trans = true;
735
for(auto& op : parent->_places)
736
for(size_t outer = 0; outer < op.consumers.size(); ++outer)
738
auto touter = op.consumers[outer];
739
if(hasTimedout()) return false;
740
if(_tflags[touter] != 0) continue;
742
Transition& tout = getTransition(touter);
743
if (tout.skip) continue;
746
if (tout.inhib) continue;
748
for(size_t inner = outer + 1; inner < op.consumers.size(); ++inner) {
749
auto tinner = op.consumers[inner];
750
Transition& tin = getTransition(tinner);
751
if (tin.skip || tout.skip) continue;
754
if (tin.inhib) continue;
756
for (size_t swp = 0; swp < 2; ++swp) {
757
if(hasTimedout()) return false;
759
if (tin.skip || tout.skip) break;
763
if (swp == 1) std::swap(t1, t2);
765
// D1. not same transition
768
Transition& trans1 = getTransition(t1);
769
Transition& trans2 = getTransition(t2);
771
// From D3, and D4 we have that pre and post-sets are the same
772
if (trans1.post.size() != trans2.post.size()) break;
773
if (trans1.pre.size() != trans2.pre.size()) break;
776
uint mult = std::numeric_limits<uint>::max();
777
// D4. postsets must match
778
for (int i = trans1.post.size() - 1; i >= 0; --i) {
779
Arc& arc = trans1.post[i];
780
Arc& arc2 = trans2.post[i];
781
if (arc2.place != arc.place) {
786
if (mult == std::numeric_limits<uint>::max()) {
787
if (arc2.weight < arc.weight || (arc2.weight % arc.weight) != 0) {
791
mult = arc2.weight / arc.weight;
793
} else if (arc2.weight != arc.weight * mult) {
800
else if (ok == 1) continue;
802
// D3. Presets must match
803
for (int i = trans1.pre.size() - 1; i >= 0; --i) {
804
Arc& arc = trans1.pre[i];
805
Arc& arc2 = trans2.pre[i];
806
if (arc2.place != arc.place) {
811
if (mult == std::numeric_limits<uint>::max()) {
812
if (arc2.weight < arc.weight || (arc2.weight % arc.weight) != 0) {
816
mult = arc2.weight / arc.weight;
818
} else if (arc2.weight != arc.weight * mult) {
825
else if (ok == 1) continue;
827
// UD1. Remove transition t2
828
continueReductions = true;
832
break; // break the swap loop
835
} // end of main for loop for rule D
836
assert(consistent());
837
return continueReductions;
840
bool Reducer::ReducebyRuleE(uint32_t* placeInQuery) {
841
bool continueReductions = false;
842
const size_t numberofplaces = parent->numberOfPlaces();
843
for(uint32_t p = 0; p < numberofplaces; ++p)
845
if(hasTimedout()) return false;
846
Place& place = parent->_places[p];
847
if(place.skip) continue;
848
if(place.inhib) continue;
849
if(place.producers.size() > place.consumers.size()) continue;
851
std::set<uint32_t> notenabled;
853
for(uint cons : place.consumers)
855
Transition& t = getTransition(cons);
856
auto in = getInArc(p, t);
857
if(in->weight <= parent->initialMarking[p])
859
auto out = getOutArc(t, p);
860
if(out == t.post.end() || out->place != p || out->weight >= in->weight)
868
notenabled.insert(cons);
872
if(!ok || notenabled.size() == 0) continue;
874
for(uint prod : place.producers)
876
if(notenabled.count(prod) == 0)
881
// check that producing arcs originate from transition also
882
// consuming. If so, we know it will never fire.
883
Transition& t = getTransition(prod);
884
ArcIter it = getInArc(p, t);
885
if(it == t.pre.end())
895
continueReductions = true;
897
if(placeInQuery[p] == 0)
898
parent->initialMarking[p] = 0;
900
bool skipplace = (notenabled.size() == place.consumers.size()) && (placeInQuery[p] == 0);
901
for(uint cons : notenabled)
902
skipTransition(cons);
908
assert(consistent());
909
return continueReductions;
912
bool Reducer::ReducebyRuleI(uint32_t* placeInQuery, bool remove_loops, bool remove_consumers) {
913
bool reduced = false;
916
std::vector<uint32_t> wtrans;
917
std::vector<bool> tseen(parent->numberOfTransitions(), false);
919
for(uint32_t p = 0; p < parent->numberOfPlaces(); ++p)
921
if(hasTimedout()) return false;
922
if(placeInQuery[p] > 0)
924
const Place& place = parent->_places[p];
925
for(auto t : place.consumers)
933
for(auto t : place.producers)
944
std::vector<bool> pseen(parent->numberOfPlaces(), false);
945
while(!wtrans.empty())
947
if(hasTimedout()) return false;
948
auto t = wtrans.back();
950
const Transition& trans = parent->_transitions[t];
951
for(const Arc& arc : trans.pre)
953
const Place& place = parent->_places[arc.place];
956
for(auto pt : place.consumers)
960
Transition& trans = parent->_transitions[pt];
961
auto it = trans.post.begin();
962
for(; it != trans.post.end(); ++it)
963
if(it->place >= arc.place) break;
964
if(it != trans.post.end() && it->place == arc.place)
966
auto it2 = trans.pre.begin();
967
for(; it2 != trans.pre.end(); ++it2)
968
if(it2->place >= arc.place || it2->inhib) break;
969
if(it->weight <= it2->weight) continue;
972
wtrans.push_back(pt);
978
for(auto pt : place.producers)
982
Transition& trans = parent->_transitions[pt];
983
auto it = trans.pre.begin();
984
for(; it != trans.pre.end(); ++it)
985
if(it->place >= arc.place) break;
987
if(it != trans.pre.end() && it->place == arc.place && !it->inhib)
989
auto it2 = trans.post.begin();
990
for(; it2 != trans.post.end(); ++it2)
991
if(it2->place >= arc.place) break;
992
if(it->weight >= it2->weight) continue;
996
wtrans.push_back(pt);
1000
for(auto pt : place.consumers)
1002
if(!tseen[pt] && (!remove_consumers || placeInQuery[arc.place] > 0))
1005
wtrans.push_back(pt);
1009
pseen[arc.place] = true;
1013
for(size_t t = 0; t < parent->numberOfTransitions(); ++t)
1015
if(!tseen[t] && !parent->_transitions[t].skip)
1022
for(size_t p = 0; p < parent->numberOfPlaces(); ++p)
1024
if(!pseen[p] && !parent->_places[p].skip && placeInQuery[p] == 0)
1026
assert(placeInQuery[p] == 0);
1037
const size_t numberofplaces = parent->numberOfPlaces();
1038
for(uint32_t p = 0; p < numberofplaces; ++p)
1040
if(hasTimedout()) return false;
1041
Place& place = parent->_places[p];
1042
if(place.skip) continue;
1043
if(place.inhib) continue;
1044
if(placeInQuery[p] > 0) continue;
1045
if(place.consumers.size() > 0) continue;
1050
std::vector<uint32_t> torem;
1051
if(remove_consumers)
1053
for(auto& t : place.producers)
1055
auto& trans = parent->_transitions[t];
1056
if(trans.post.size() != 1) // place will be removed later
1059
for(auto& a : trans.pre)
1061
if(placeInQuery[a.place] > 0)
1066
if(ok) torem.push_back(t);
1072
assert(consistent());
1079
bool Reducer::ReducebyRuleF(uint32_t* placeInQuery) {
1080
bool continueReductions = false;
1081
const size_t numberofplaces = parent->numberOfPlaces();
1082
for(uint32_t p = 0; p < numberofplaces; ++p)
1084
if(hasTimedout()) return false;
1085
Place& place = parent->_places[p];
1086
if(place.skip) continue;
1087
if(place.inhib) continue;
1088
if(place.producers.size() < place.consumers.size()) continue;
1089
if(placeInQuery[p] != 0) continue;
1092
for(uint cons : place.consumers)
1094
Transition& t = getTransition(cons);
1095
auto w = getInArc(p, t)->weight;
1096
if(w > parent->initialMarking[p])
1103
auto it = getOutArc(t, p);
1104
if(it == t.post.end() ||
1118
if((numberofplaces - _removedPlaces) > 1)
1120
if(reconstructTrace)
1122
for(auto t : place.consumers)
1124
std::string tname = getTransitionName(t);
1125
const ArcIter arc = getInArc(p, getTransition(t));
1126
_extraconsume[tname].emplace_back(getPlaceName(p), arc->weight);
1130
continueReductions = true;
1134
assert(consistent());
1135
return continueReductions;
1139
bool Reducer::ReducebyRuleG(uint32_t* placeInQuery, bool remove_loops, bool remove_consumers) {
1140
if(!remove_loops) return false;
1141
bool continueReductions = false;
1142
for(uint32_t t = 0; t < parent->numberOfTransitions(); ++t)
1144
if(hasTimedout()) return false;
1145
Transition& trans = parent->_transitions[t];
1146
if(trans.skip) continue;
1147
if(trans.inhib) continue;
1148
if(trans.pre.size() < trans.post.size()) continue;
1149
if(!remove_loops && trans.pre.size() == 0) continue;
1151
auto postit = trans.post.begin();
1152
auto preit = trans.pre.begin();
1157
if(preit == trans.pre.end() && postit == trans.post.end())
1159
if(preit == trans.pre.end())
1164
if(preit->inhib || parent->_places[preit->place].inhib)
1169
if(postit != trans.post.end() && preit->place == postit->place)
1171
if(!remove_consumers && preit->weight != postit->weight)
1176
if((placeInQuery[preit->place] > 0 && preit->weight != postit->weight) ||
1177
(placeInQuery[preit->place] == 0 && preit->weight < postit->weight))
1185
else if(postit == trans.post.end() || preit->place < postit->place)
1187
if(placeInQuery[preit->place] > 0 || !remove_consumers)
1196
// could not match a post with a pre
1203
for(preit = trans.pre.begin();preit != trans.pre.end(); ++preit)
1205
if(preit->inhib || parent->_places[preit->place].inhib)
1217
assert(consistent());
1218
return continueReductions;
1221
bool Reducer::ReducebyRuleH(uint32_t* placeInQuery)
1223
if(reconstructTrace)
1224
return false; // we don't know where in the loop the tokens are needed
1225
auto transok = [this](uint32_t t) -> uint32_t {
1226
auto& trans = parent->_transitions[t];
1231
trans.pre.size() != 1 ||
1232
trans.post.size() != 1)
1237
auto p1 = trans.pre[0].place;
1238
auto p2 = trans.post[0].place;
1240
// we actually do not need weights to be 1 here.
1241
// there is a special case when the places are always "inputting"
1242
// and "outputting" with a GCD that is equal to the weight of the
1243
// specific transition.
1244
// Ie, the place always have a number of tokens (disregarding
1245
// initial tokens) that is dividable with the transition weight
1247
if(trans.pre[0].weight != 1 ||
1248
trans.post[0].weight != 1 ||
1250
parent->_places[p1].inhib ||
1251
parent->_places[p2].inhib)
1258
auto removeLoop = [this,placeInQuery](std::vector<uint32_t>& loop) -> bool {
1260
for(; i < loop.size(); ++i)
1261
if(loop[i] == loop.back())
1264
assert(_tflags[loop.back()]== 1);
1265
if(i == loop.size() - 1)
1268
auto p1 = parent->_transitions[loop[i]].pre[0].place;
1269
bool removed = false;
1271
for(size_t j = i + 1; j < loop.size() - 1; ++j)
1275
auto p2 = parent->_transitions[loop[j]].pre[0].place;
1276
if(placeInQuery[p2] > 0 || placeInQuery[p1] > 0)
1287
skipTransition(loop[j-1]);
1288
auto& place1 = parent->_places[p1];
1289
auto& place2 = parent->_places[p2];
1293
for(auto p2it : place2.consumers)
1295
auto& t = parent->_transitions[p2it];
1296
auto arc = getInArc(p2, t);
1297
assert(arc != t.pre.end());
1298
assert(arc->place == p2);
1301
auto dest = std::lower_bound(t.pre.begin(), t.pre.end(), a);
1302
if(dest == t.pre.end() || dest->place != p1)
1304
t.pre.insert(dest, a);
1305
auto lb = std::lower_bound(place1.consumers.begin(), place1.consumers.end(), p2it);
1306
place1.consumers.insert(lb, p2it);
1310
dest->weight += a.weight;
1317
auto p2it = place2.producers.begin();
1319
for(;p2it != place2.producers.end(); ++p2it)
1321
auto& t = parent->_transitions[*p2it];
1322
Arc a = *getOutArc(t, p2);
1324
auto dest = std::lower_bound(t.post.begin(), t.post.end(), a);
1325
if(dest == t.post.end() || dest->place != p1)
1327
t.post.insert(dest, a);
1328
auto lb = std::lower_bound(place1.producers.begin(), place1.producers.end(), *p2it);
1329
place1.producers.insert(lb, *p2it);
1333
dest->weight += a.weight;
1338
parent->initialMarking[p1] += parent->initialMarking[p2];
1340
assert(placeInQuery[p2] == 0);
1345
bool continueReductions = false;
1346
for(uint32_t t = 0; t < parent->numberOfTransitions(); ++t)
1349
return continueReductions;
1350
_tflags.resize(parent->_transitions.size(), 0);
1351
std::fill(_tflags.begin(), _tflags.end(), 0);
1352
std::vector<uint32_t> stack;
1354
if(_tflags[t] != 0) continue;
1355
auto& trans = parent->_transitions[t];
1356
if(trans.skip) continue;
1357
_tflags[t] = transok(t);
1358
if(_tflags[t] != 1) continue;
1362
while(stack.size() > 0 && outer)
1365
return continueReductions;
1366
auto it = stack.back();
1367
auto post = parent->_transitions[it].post[0].place;
1369
for(auto& nt : parent->_places[post].consumers)
1372
return continueReductions;
1373
auto& nexttrans = parent->_transitions[nt];
1374
if(nt == it || nexttrans.skip)
1375
continue; // handled elsewhere
1376
if(_tflags[nt] == 1 && stack.size() > 1)
1378
stack.push_back(nt);
1379
bool found = removeLoop(stack);
1380
continueReductions |= found;
1392
else if(_tflags[nt] == 0)
1394
_tflags[nt] = transok(nt);
1395
if(_tflags[nt] == 2)
1401
assert(_tflags[nt] == 1);
1402
stack.push_back(nt);
1419
return continueReductions;
1422
bool Reducer::ReducebyRuleJ(uint32_t* placeInQuery)
1424
bool continueReductions = false;
1425
for(uint32_t t = 0; t < parent->numberOfTransitions(); ++t)
1428
return continueReductions;
1430
if(parent->_transitions[t].skip ||
1431
parent->_transitions[t].inhib ||
1432
parent->_transitions[t].pre.size() != 1)
1434
auto p = parent->_transitions[t].pre[0].place;
1435
if(placeInQuery[p] > 0)
1437
continue; // can be relaxed
1439
if(parent->initialMarking[p] > 0)
1441
continue; // can be relaxed
1443
const Place& place = parent->_places[p];
1444
if(place.skip) continue;
1445
if(place.inhib) continue;
1446
if(place.consumers.size() < 1) continue;
1447
if(place.producers.size() < 1) continue;
1449
// check that prod and cons are not overlapping
1450
const auto presize = place.producers.size(); // can be relaxed >= 2
1451
const auto postsize = place.consumers.size(); // can be relaxed >= 2
1453
for(size_t i = 0; i < postsize; ++i)
1454
{ // this can be done smarter than a quadratic loop!
1455
for(size_t j = 0; j < presize; ++j)
1457
ok &= place.consumers[i] != place.producers[j];
1461
// check that post of consumer is not messing with query or inhib
1462
// if either all pre or all post are query-free, we are ok.
1463
bool inquery = false;
1464
for(auto t : place.consumers)
1466
Transition& trans = parent->_transitions[t];
1467
if(trans.pre.size() == 1) // can be relaxed
1469
// check that weights match
1471
ok &= trans.pre[0].weight == 1;
1472
ok &= !trans.pre[0].inhib;
1479
for(auto& pp : trans.post)
1481
ok &= !parent->_places[pp.place].inhib;
1482
inquery |= placeInQuery[pp.place] > 0;
1483
ok &= pp.weight == 1; // can be relaxed
1489
// check that pre of producing do not mess with query or inhib
1490
for(auto& t : place.producers)
1492
Transition& trans = parent->_transitions[t];
1493
for(const auto& arc : trans.post)
1495
ok &= !inquery || placeInQuery[arc.place] == 0;
1496
ok &= !parent->_places[arc.place].inhib;
1501
continueReductions = true;
1502
// otherwise we can skip the place by merging up the two transitions
1503
// constructing 4 new transitions, one for each combination.
1504
// In the binary case, we want to achieve the following four transitions
1505
// post[n] = pre[n] + post[n]
1506
// pre[0] = pre[0] + post[1]
1507
// pre[1] = pre[1] + post[0]
1509
// start by copying out the post of each of the posts
1512
std::vector<std::vector<Arc>> posts;
1513
std::vector<Transition> pres;
1515
for(auto t : pp.consumers)
1516
posts.push_back(parent->_transitions[t].post);
1518
for(auto t : pp.producers)
1519
pres.push_back(parent->_transitions[t]);
1521
// remove old transitions, we will create new ones
1522
for(auto t : pp.consumers)
1525
for(auto t : pp.producers)
1528
// compute all permutations
1529
for(auto& trans : pres)
1531
for(auto& postset : posts)
1533
auto id = parent->_transitions.size();
1534
if(!_skipped_trans.empty())
1535
id = _skipped_trans.back();
1539
parent->_transitions.emplace_back();
1541
parent->_transitions[id] = trans;
1542
auto& target = parent->_transitions[id];
1543
for(auto& arc : postset)
1544
target.addPostArc(arc);
1547
if(_skipped_trans.empty())
1548
parent->_transitionnames[newTransName()] = id;
1550
for(auto& arc : target.pre)
1551
parent->_places[arc.place].addConsumer(id);
1552
for(auto& arc : target.post)
1553
parent->_places[arc.place].addProducer(id);
1554
if(!_skipped_trans.empty())
1556
--_removedTransitions; // recycling
1557
_skipped_trans.pop_back();
1559
parent->_transitions[id].skip = false;
1560
parent->_transitions[id].inhib = false;
1566
return continueReductions;
1569
void Reducer::Reduce(QueryPlaceAnalysisContext& context, int enablereduction, bool reconstructTrace, int timeout, bool remove_loops, bool remove_consumers, bool next_safe, std::vector<uint32_t>& reduction) {
1570
this->_timeout = timeout;
1571
_timer = std::chrono::high_resolution_clock::now();
1572
assert(consistent());
1573
this->reconstructTrace = reconstructTrace;
1574
if(reconstructTrace && enablereduction >= 1 && enablereduction <= 2)
1575
std::cout << "Rule H disabled when a trace is requested." << std::endl;
1576
if (enablereduction == 2) { // for k-boundedness checking only rules A, D and H are applicable
1577
bool changed = true;
1578
while (changed && !hasTimedout()) {
1582
while(ReducebyRuleA(context.getQueryPlaceCount())) changed = true;
1583
while(ReducebyRuleD(context.getQueryPlaceCount())) changed = true;
1584
while(ReducebyRuleH(context.getQueryPlaceCount())) changed = true;
1588
else if (enablereduction == 1) { // in the aggressive reduction all four rules are used as long as they remove something
1589
bool changed = false;
1592
if(remove_loops && !next_safe)
1593
while(ReducebyRuleI(context.getQueryPlaceCount(), remove_loops, remove_consumers)) changed = true;
1595
do { // start by rules that do not move tokens
1597
while(ReducebyRuleE(context.getQueryPlaceCount())) changed = true;
1598
while(ReducebyRuleC(context.getQueryPlaceCount())) changed = true;
1601
while(ReducebyRuleF(context.getQueryPlaceCount())) changed = true;
1602
while(ReducebyRuleG(context.getQueryPlaceCount(), remove_loops, remove_consumers)) changed = true;
1604
while(ReducebyRuleI(context.getQueryPlaceCount(), remove_loops, remove_consumers)) changed = true;
1605
while(ReducebyRuleD(context.getQueryPlaceCount())) changed = true;
1607
} while(changed && !hasTimedout());
1609
{ // then apply tokens moving rules
1610
//while(ReducebyRuleJ(context.getQueryPlaceCount())) changed = true;
1611
while(ReducebyRuleB(context.getQueryPlaceCount(), remove_loops, remove_consumers)) changed = true;
1612
while(ReducebyRuleA(context.getQueryPlaceCount())) changed = true;
1614
} while(changed && !hasTimedout());
1615
if(!next_safe && !changed)
1617
// Only try RuleH last. It can reduce applicability of other rules.
1618
while(ReducebyRuleH(context.getQueryPlaceCount())) changed = true;
1620
} while(!hasTimedout() && changed);
1625
const char* rnames = "ABCDEFGHIJ";
1626
for(int i = reduction.size() - 1; i > 0; --i)
1630
if(reduction[i] != 2 && reduction[i] != 4)
1632
std::cerr << "Skipping Rule" << rnames[reduction[i]] << " due to NEXT operator in proposition" << std::endl;
1633
reduction.erase(reduction.begin() + i);
1637
if(!remove_loops && reduction[i] == 5)
1639
std::cerr << "Skipping Rule" << rnames[reduction[i]] << " as proposition is loop sensitive" << std::endl;
1640
reduction.erase(reduction.begin() + i);
1643
bool changed = true;
1644
while(changed && !hasTimedout())
1647
for(auto r : reduction)
1650
auto c = std::chrono::high_resolution_clock::now();
1651
auto op = _removedPlaces;
1652
auto ot = _removedTransitions;
1657
while(ReducebyRuleA(context.getQueryPlaceCount())) changed = true;
1660
while(ReducebyRuleB(context.getQueryPlaceCount(), remove_loops, remove_consumers)) changed = true;
1663
while(ReducebyRuleC(context.getQueryPlaceCount())) changed = true;
1666
while(ReducebyRuleD(context.getQueryPlaceCount())) changed = true;
1669
while(ReducebyRuleE(context.getQueryPlaceCount())) changed = true;
1672
while(ReducebyRuleF(context.getQueryPlaceCount())) changed = true;
1675
while(ReducebyRuleG(context.getQueryPlaceCount(), remove_loops, remove_consumers)) changed = true;
1678
while(ReducebyRuleH(context.getQueryPlaceCount())) changed = true;
1681
while(ReducebyRuleI(context.getQueryPlaceCount(), remove_loops, remove_consumers)) changed = true;
1684
while(ReducebyRuleJ(context.getQueryPlaceCount())) changed = true;
1688
auto end = std::chrono::high_resolution_clock::now();
1689
auto diff = std::chrono::duration_cast<std::chrono::seconds>(end - c);
1690
std::cout << "SPEND " << diff.count() << " ON " << rnames[r] << std::endl;
1691
std::cout << "REM " << ((int)_removedPlaces-(int)op) << " " << ((int)_removedTransitions-(int)ot) << std::endl;
1700
void Reducer::postFire(std::ostream& out, const std::string& transition)
1702
if(_postfire.count(transition) > 0)
1704
std::queue<std::string> tofire;
1706
for(auto& el : _postfire[transition]) tofire.push(el);
1708
for(auto& el : _postfire[transition])
1711
out << "\t<transition id=\"" << el << "\">\n";
1712
extraConsume(out, el);
1713
out << "\t</transition>\n";
1719
void Reducer::initFire(std::ostream& out)
1721
for(std::string& init : _initfire)
1723
out << "\t<transition id=\"" << init << "\">\n";
1724
extraConsume(out, init);
1725
out << "\t</transition>\n";
1726
postFire(out, init);
1730
void Reducer::extraConsume(std::ostream& out, const std::string& transition)
1732
if(_extraconsume.count(transition) > 0)
1734
for(auto& ec : _extraconsume[transition])
1741
} //PetriNet namespace