1
#include "CTL/PetriNets/OnTheFlyDG.h"
9
#include "PetriEngine/SuccessorGenerator.h"
10
#include "PetriEngine/PQL/Expressions.h"
11
#include "CTL/SearchStrategy/SearchStrategy.h"
14
using namespace PetriEngine::PQL;
15
using namespace DependencyGraph;
19
OnTheFlyDG::OnTheFlyDG(PetriEngine::PetriNet *t_net, bool partial_order) : encoder(t_net->numberOfPlaces(), 0),
20
edge_alloc(new linked_bucket_t<DependencyGraph::Edge,1024*10>(1)),
21
conf_alloc(new linked_bucket_t<char[sizeof(PetriConfig)], 1024*1024>(1)),
22
_redgen(*t_net), _partial_order(partial_order) {
24
n_places = t_net->numberOfPlaces();
25
n_transitions = t_net->numberOfTransitions();
29
OnTheFlyDG::~OnTheFlyDG()
32
//Note: initial marking is in the markings set, therefore it will be deleted by the for loop
33
//TODO: Ensure we don't leak memory here, when code moving is done
34
size_t s = conf_alloc->size();
35
for(size_t i = 0; i < s; ++i)
37
((PetriConfig*)&(*conf_alloc)[i])->~PetriConfig();
43
Condition::Result OnTheFlyDG::initialEval()
45
initialConfiguration();
46
EvaluationContext e(query_marking.marking(), net);
47
return query->evaluate(e);
50
Condition::Result OnTheFlyDG::fastEval(Condition* query, Marking* unfolded)
52
EvaluationContext e(unfolded->marking(), net);
53
return query->evaluate(e);
57
std::vector<DependencyGraph::Edge*> OnTheFlyDG::successors(Configuration *c)
59
PetriEngine::PQL::DistanceContext context(net, query_marking.marking());
60
PetriConfig *v = static_cast<PetriConfig*>(c);
61
trie.unpack(v->marking, encoder.scratchpad().raw());
62
encoder.decode(query_marking.marking(), encoder.scratchpad().raw());
63
// v->printConfiguration();
64
std::vector<Edge*> succs;
65
auto query_type = v->query->getQueryType();
66
if(query_type == EVAL){
68
//assert(false && "Someone told me, this was a bad place to be.");
69
if (fastEval(query, &query_marking) == Condition::RTRUE){
70
succs.push_back(newEdge(*v, 0));///*v->query->distance(context))*/0);
73
else if (query_type == LOPERATOR){
74
if(v->query->getQuantifier() == NEG){
75
// no need to try to evaluate here -- this is already transient in other evaluations.
76
auto cond = static_cast<NotCondition*>(v->query);
77
Configuration* c = createConfiguration(v->marking, v->owner, (*cond)[0]);
78
Edge* e = newEdge(*v, /*v->query->distance(context)*/0);
83
else if(v->query->getQuantifier() == AND){
84
auto cond = static_cast<AndCondition*>(v->query);
85
//Check if left is false
86
std::vector<Condition*> conds;
89
auto res = fastEval(c.get(), &query_marking);
90
if(res == Condition::RFALSE)
94
if(res == Condition::RUNKNOWN)
96
conds.push_back(c.get());
100
Edge *e = newEdge(*v, /*cond->distance(context)*/0);
102
//If we get here, then either both propositions are true (shouldn't be possible)
103
//Or a temporal operator and a true proposition
104
//Or both are temporal
107
assert(c->isTemporal());
108
e->addTarget(createConfiguration(v->marking, v->owner, c));
112
else if(v->query->getQuantifier() == OR){
113
auto cond = static_cast<OrCondition*>(v->query);
114
//Check if left is true
115
std::vector<Condition*> conds;
118
auto res = fastEval(c.get(), &query_marking);
119
if(res == Condition::RTRUE)
121
succs.push_back(newEdge(*v, 0));
124
if(res == Condition::RUNKNOWN)
126
conds.push_back(c.get());
130
//If we get here, either both propositions are false
131
//Or one is false and one is temporal
135
assert(c->isTemporal());
136
Edge *e = newEdge(*v, /*cond->distance(context)*/0);
137
e->addTarget(createConfiguration(v->marking, v->owner, c));
142
assert(false && "An unknown error occoured in the loperator-part of the successor generator");
145
else if (query_type == PATHQEURY){
146
if(v->query->getQuantifier() == A){
147
if (v->query->getPath() == U){
148
auto cond = static_cast<AUCondition*>(v->query);
150
auto r1 = fastEval((*cond)[1], &query_marking);
151
if (r1 != Condition::RUNKNOWN){
152
//right side is not temporal, eval it right now!
153
if (r1 == Condition::RTRUE) { //satisfied, no need to go through successors
154
succs.push_back(newEdge(*v, 0));
156
}//else: It's not valid, no need to add any edge, just add successors
159
//right side is temporal, we need to evaluate it as normal
160
Configuration* c = createConfiguration(v->marking, v->owner, (*cond)[1]);
161
right = newEdge(*v, /*(*cond)[1]->distance(context)*/0);
165
Configuration *left = NULL;
166
auto r0 = fastEval((*cond)[0], &query_marking);
167
if (r0 != Condition::RUNKNOWN) {
168
//left side is not temporal, eval it right now!
169
valid = r0 == Condition::RTRUE;
171
//left side is temporal, include it in the edge
172
left = createConfiguration(v->marking, v->owner, (*cond)[0]);
174
if (valid || left != NULL) {
175
//if left side is guaranteed to be not satisfied, skip successor generation
176
Edge* leftEdge = NULL;
177
nextStates (query_marking, cond,
178
[&](){ leftEdge = newEdge(*v, std::numeric_limits<uint32_t>::max());},
180
auto res = fastEval(cond, &mark);
181
if(res == Condition::RTRUE) return true;
182
if(res == Condition::RFALSE)
185
leftEdge->targets.clear();
189
context.setMarking(mark.marking());
190
leftEdge->weight = 0;//std::min(leftEdge->weight, /*cond->distance(context)*/0);
191
Configuration* c = createConfiguration(createMarking(mark), owner(mark, cond), cond);
192
leftEdge->addTarget(c);
200
leftEdge->addTarget(left);
202
succs.push_back(leftEdge);
206
} //else: Left side is not temporal and it's false, no way to succeed there...
209
succs.push_back(right);
212
else if(v->query->getPath() == F){
213
auto cond = static_cast<AFCondition*>(v->query);
214
Edge *subquery = NULL;
215
auto r = fastEval((*cond)[0], &query_marking);
216
if (r != Condition::RUNKNOWN) {
217
bool valid = r == Condition::RTRUE;
219
succs.push_back(newEdge(*v, 0));
223
subquery = newEdge(*v, /*cond->distance(context)*/0);
224
Configuration* c = createConfiguration(v->marking, v->owner, (*cond)[0]);
225
subquery->addTarget(c);
228
nextStates(query_marking, cond,
229
[&](){e1 = newEdge(*v, std::numeric_limits<uint32_t>::max());},
232
auto res = fastEval(cond, &mark);
233
if(res == Condition::RTRUE) return true;
234
if(res == Condition::RFALSE)
245
context.setMarking(mark.marking());
246
e1->weight = 0;//std::min(e1->weight, /*cond->distance(context)*/0);
247
Configuration* c = createConfiguration(createMarking(mark), owner(mark, cond), cond);
257
if (subquery != nullptr) {
258
succs.push_back(subquery);
261
else if(v->query->getPath() == X){
262
auto cond = static_cast<AXCondition*>(v->query);
263
Edge* e = newEdge(*v, std::numeric_limits<uint32_t>::max());
264
Condition::Result allValid = Condition::RTRUE;
265
nextStates(query_marking, cond,
268
auto res = fastEval((*cond)[0], &mark);
269
if(res != Condition::RUNKNOWN)
271
if (res == Condition::RFALSE) {
272
allValid = Condition::RFALSE;
278
allValid = Condition::RUNKNOWN;
279
context.setMarking(mark.marking());
280
e->weight = 0;//std::min(e->weight, /*cond->distance(context)*/0);
281
Configuration* c = createConfiguration(createMarking(mark), v->owner, (*cond)[0]);
288
if(allValid == Condition::RUNKNOWN)
292
else if(allValid == Condition::RTRUE)
301
else if(v->query->getPath() == G ){
302
assert(false && "Path operator G had not been translated - Parse error detected in succ()");
305
assert(false && "An unknown error occoured in the successor generator");
307
else if(v->query->getQuantifier() == E){
308
if (v->query->getPath() == U){
309
auto cond = static_cast<EUCondition*>(v->query);
311
auto r1 = fastEval((*cond)[1], &query_marking);
312
if (r1 == Condition::RUNKNOWN) {
313
Configuration* c = createConfiguration(v->marking, v->owner, (*cond)[1]);
314
right = newEdge(*v, /*(*cond)[1]->distance(context)*/0);
317
bool valid = r1 == Condition::RTRUE;
319
succs.push_back(newEdge(*v, 0));
321
} // else: right condition is not satisfied, no need to add an edge
325
Configuration *left = NULL;
327
nextStates(query_marking, cond,
329
auto r0 = fastEval((*cond)[0], &query_marking);
330
if (r0 == Condition::RUNKNOWN) {
331
left = createConfiguration(v->marking, v->owner, (*cond)[0]);
333
valid = r0 == Condition::RTRUE;
336
[&](Marking& marking){
337
if(left == NULL && !valid) return false;
338
auto res = fastEval(cond, &marking);
339
if(res == Condition::RFALSE) return true;
340
if(res == Condition::RTRUE)
342
for(auto s : succs){ --s->refcnt; release(s);}
344
succs.push_back(newEdge(*v, 0));
354
succs.back()->addTarget(left);
359
context.setMarking(marking.marking());
360
Edge* e = newEdge(*v, /*cond->distance(context)*/0);
361
Configuration* c1 = createConfiguration(createMarking(marking), owner(marking, cond), cond);
370
if (right != nullptr) {
371
succs.push_back(right);
374
else if(v->query->getPath() == F){
375
auto cond = static_cast<EFCondition*>(v->query);
376
Edge *subquery = NULL;
377
auto r = fastEval((*cond)[0], &query_marking);
378
if (r != Condition::RUNKNOWN) {
379
bool valid = r == Condition::RTRUE;
381
succs.push_back(newEdge(*v, 0));
385
Configuration* c = createConfiguration(v->marking, v->owner, (*cond)[0]);
386
subquery = newEdge(*v, /*cond->distance(context)*/0);
387
subquery->addTarget(c);
390
nextStates(query_marking, cond,
393
auto res = fastEval(cond, &mark);
394
if(res == Condition::RFALSE) return true;
395
if(res == Condition::RTRUE)
397
for(auto s : succs){ --s->refcnt; release(s);}
399
succs.push_back(newEdge(*v, 0));
408
context.setMarking(mark.marking());
409
Edge* e = newEdge(*v, /*cond->distance(context)*/0);
410
Configuration* c = createConfiguration(createMarking(mark), owner(mark, cond), cond);
418
if (subquery != NULL) {
419
succs.push_back(subquery);
422
else if(v->query->getPath() == X){
423
auto cond = static_cast<EXCondition*>(v->query);
424
auto query = (*cond)[0];
425
nextStates(query_marking, cond,
427
[&](Marking& marking) {
428
auto res = fastEval(query, &marking);
429
if(res == Condition::RTRUE)
431
for(auto s : succs){ --s->refcnt; release(s);}
433
succs.push_back(newEdge(*v, 0));
435
} //else: It can't hold there, no need to create an edge
436
else if(res == Condition::RUNKNOWN)
438
context.setMarking(marking.marking());
439
Edge* e = newEdge(*v, /*(*cond)[0]->distance(context)*/0);
440
Configuration* c = createConfiguration(createMarking(marking), v->owner, query);
449
else if(v->query->getPath() == G ){
450
assert(false && "Path operator G had not been translated - Parse error detected in succ()");
453
assert(false && "An unknown error occoured in the successor generator");
458
assert(false && "Should never happen");
460
if(succs.size() == 1 && succs[0]->targets.size() == 1)
462
((PetriConfig*)succs[0]->targets[0])->owner = v->owner;
467
Configuration* OnTheFlyDG::initialConfiguration()
469
if(working_marking.marking() == nullptr)
471
working_marking.setMarking (net->makeInitialMarking());
472
query_marking.setMarking (net->makeInitialMarking());
473
auto o = owner(working_marking, this->query);
474
initial_config = createConfiguration(createMarking(working_marking), o, this->query);
476
return initial_config;
480
void OnTheFlyDG::nextStates(Marking& t_marking, Condition* ptr,
481
std::function<void ()> pre,
482
std::function<bool (Marking&)> foreach,
483
std::function<void ()> post)
486
memcpy(working_marking.marking(), query_marking.marking(), n_places*sizeof(PetriEngine::MarkVal));
487
auto qf = static_cast<QuantifierCondition*>(ptr);
488
if(!_partial_order || ptr->getQuantifier() != E || ptr->getPath() != F || (*qf)[0]->isTemporal())
490
PetriEngine::SuccessorGenerator PNGen(*net);
491
dowork<PetriEngine::SuccessorGenerator>(PNGen, first, pre, foreach);
495
_redgen.setQuery(ptr);
496
dowork<PetriEngine::ReducingSuccessorGenerator>(_redgen, first, pre, foreach);
502
void OnTheFlyDG::cleanUp()
504
while(!recycle.empty())
506
assert(recycle.top()->refcnt == -1);
509
// TODO, implement proper cleanup
513
void OnTheFlyDG::setQuery(const Condition_ptr& query)
516
delete[] working_marking.marking();
517
delete[] query_marking.marking();
518
working_marking.setMarking(nullptr);
519
query_marking.setMarking(nullptr);
520
initialConfiguration();
524
size_t OnTheFlyDG::configurationCount() const
526
return _configurationCount;
529
size_t OnTheFlyDG::markingCount() const
531
return _markingCount;
534
PetriConfig *OnTheFlyDG::createConfiguration(size_t marking, size_t own, Condition* t_query)
536
auto& configs = trie.get_data(marking);
537
for(PetriConfig* c : configs){
538
if(c->query == t_query)
542
_configurationCount++;
543
size_t id = conf_alloc->next(0);
544
char* mem = (*conf_alloc)[id];
545
PetriConfig* newConfig = new (mem) PetriConfig();
546
newConfig->marking = marking;
547
newConfig->query = t_query;
548
newConfig->owner = own;
549
configs.push_back(newConfig);
555
size_t OnTheFlyDG::createMarking(Marking& t_marking){
561
markingStats(t_marking.marking(), sum, allsame, val, active, last);
562
unsigned char type = encoder.getType(sum, active, allsame, val);
563
size_t length = encoder.encode(t_marking.marking(), type);
564
binarywrapper_t w = binarywrapper_t(encoder.scratchpad().raw(), length*8);
565
auto tit = trie.insert(w.raw(), w.size());
573
void OnTheFlyDG::release(Edge* e)
575
assert(e->refcnt == 0);
576
e->is_negated = false;
577
e->processed = false;
585
size_t OnTheFlyDG::owner(Marking& marking, Condition* cond) {
586
// Used for distributed algorithm
591
Edge* OnTheFlyDG::newEdge(Configuration &t_source, uint32_t weight)
596
size_t n = edge_alloc->next(0);
597
e = &(*edge_alloc)[n];
605
assert(e->targets.empty());
606
e->source = &t_source;
608
assert(e->refcnt == 0);
613
void OnTheFlyDG::markingStats(const uint32_t* marking, size_t& sum,
614
bool& allsame, uint32_t& val, uint32_t& active, uint32_t& last)
618
for (uint32_t i = 0; i < n_places; i++)
624
last = std::max(last, i);
625
val = std::max(marking[i], val);
626
if(old != 0 && marking[i] != old) allsame = false;