1
/* TAPAAL untimed verification engine verifypn
2
* Copyright (C) 2011-2018 Jonas Finnemann Jensen <jopsen@gmail.com>,
3
* Thomas Søndersø Nielsen <primogens@gmail.com>,
4
* Lars Kærlund Østergaard <larsko@gmail.com>,
5
* Jiri Srba <srba.jiri@gmail.com>,
6
* Peter Gjøl Jensen <root@petergjoel.dk>
9
* Peter Fogh <peter.f1992@gmail.com>
10
* Isabella Kaufmann <bellakaufmann93@gmail.com>
11
* Tobias Skovgaard Jepsen <tobiasj1991@gmail.com>
12
* Lasse Steen Jensen <lassjen88@gmail.com>
13
* Søren Moss Nielsen <soren_moss@mac.com>
14
* Samuel Pastva <daemontus@gmail.com>
15
* Jiri Srba <srba.jiri@gmail.com>
17
* Stubborn sets, query simplification, siphon-trap property
18
* Frederik Meyer Boenneland <sadpantz@gmail.com>
19
* Jakob Dyhr <jakobdyhr@gmail.com>
20
* Peter Gjøl Jensen <root@petergjoel.dk>
21
* Mads Johannsen <mads_johannsen@yahoo.com>
22
* Jiri Srba <srba.jiri@gmail.com>
24
* This program is free software: you can redistribute it and/or modify
25
* it under the terms of the GNU General Public License as published by
26
* the Free Software Foundation, either version 3 of the License, or
27
* (at your option) any later version.
29
* This program is distributed in the hope that it will be useful,
30
* but WITHOUT ANY WARRANTY; without even the implied warranty of
31
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
32
* GNU General Public License for more details.
34
* You should have received a copy of the GNU General Public License
35
* along with this program. If not, see <http://www.gnu.org/licenses/>.
47
#ifdef VERIFYPN_MC_Simplification
52
#include "PetriEngine/PQL/PQLParser.h"
53
#include "PetriEngine/PQL/Contexts.h"
54
#include "PetriEngine/Reachability/ReachabilitySearch.h"
55
#include "PetriEngine/TAR/TARReachability.h"
56
#include "PetriEngine/Reducer.h"
57
#include "PetriParse/QueryXMLParser.h"
58
#include "PetriParse/QueryBinaryParser.h"
59
#include "PetriParse/PNMLParser.h"
60
#include "PetriEngine/PetriNetBuilder.h"
61
#include "PetriEngine/PQL/PQL.h"
62
#include "PetriEngine/options.h"
63
#include "PetriEngine/errorcodes.h"
64
#include "PetriEngine/STSolver.h"
65
#include "PetriEngine/Simplification/Member.h"
66
#include "PetriEngine/Simplification/LinearPrograms.h"
67
#include "PetriEngine/Simplification/Retval.h"
69
#include "CTL/CTLEngine.h"
70
#include "PetriEngine/PQL/Expressions.h"
71
#include "PetriEngine/Colored/ColoredPetriNetBuilder.h"
74
using namespace PetriEngine;
75
using namespace PetriEngine::PQL;
76
using namespace PetriEngine::Reachability;
78
ReturnValue contextAnalysis(ColoredPetriNetBuilder& cpnBuilder, PetriNetBuilder& builder, const PetriNet* net, std::vector<std::shared_ptr<Condition> >& queries)
81
ColoredAnalysisContext context(builder.getPlaceNames(), builder.getTransitionNames(), net, cpnBuilder.getUnfoldedPlaceNames(), cpnBuilder.getUnfoldedTransitionNames(), cpnBuilder.isColored());
82
for(auto& q : queries)
87
if (context.errors().size() > 0) {
88
for (size_t i = 0; i < context.errors().size(); i++) {
89
fprintf(stderr, "Query Context Analysis Error: %s\n", context.errors()[i].toString().c_str());
97
std::vector<std::string> explode(std::string const & s)
99
std::vector<std::string> result;
100
std::istringstream iss(s);
102
for (std::string token; std::getline(iss, token, ','); )
104
result.push_back(std::move(token));
105
if(result.back().empty()) result.pop_back();
111
ReturnValue parseOptions(int argc, char* argv[], options_t& options)
113
for (int i = 1; i < argc; i++) {
114
if (strcmp(argv[i], "-k") == 0 || strcmp(argv[i], "--k-bound") == 0) {
116
fprintf(stderr, "Missing number after \"%s\"\n", argv[i]);
119
if (sscanf(argv[++i], "%d", &options.kbound) != 1 || options.kbound < 0) {
120
fprintf(stderr, "Argument Error: Invalid number of tokens \"%s\"\n", argv[i]);
123
} else if(strcmp(argv[i], "-s") == 0 || strcmp(argv[i], "--search-strategy") == 0){
125
fprintf(stderr, "Missing search strategy after \"%s\"\n\n", argv[i]);
129
if(strcmp(s, "BestFS") == 0)
130
options.strategy = HEUR;
131
else if(strcmp(s, "BFS") == 0)
132
options.strategy = BFS;
133
else if(strcmp(s, "DFS") == 0)
134
options.strategy = DFS;
135
else if(strcmp(s, "RDFS") == 0)
136
options.strategy = RDFS;
137
else if(strcmp(s, "OverApprox") == 0)
138
options.strategy = OverApprox;
140
fprintf(stderr, "Argument Error: Unrecognized search strategy \"%s\"\n", s);
143
} else if (strcmp(argv[i], "-q") == 0 || strcmp(argv[i], "--query-reduction") == 0) {
145
fprintf(stderr, "Missing number after \"%s\"\n\n", argv[i]);
148
if (sscanf(argv[++i], "%d", &options.queryReductionTimeout) != 1 || options.queryReductionTimeout < 0) {
149
fprintf(stderr, "Argument Error: Invalid query reduction timeout argument \"%s\"\n", argv[i]);
152
} else if (strcmp(argv[i], "-l") == 0 || strcmp(argv[i], "--lpsolve-timeout") == 0) {
154
fprintf(stderr, "Missing number after \"%s\"\n\n", argv[i]);
157
if (sscanf(argv[++i], "%d", &options.lpsolveTimeout) != 1 || options.lpsolveTimeout < 0) {
158
fprintf(stderr, "Argument Error: Invalid LPSolve timeout argument \"%s\"\n", argv[i]);
161
} else if (strcmp(argv[i], "-e") == 0 || strcmp(argv[i], "--state-space-exploration") == 0) {
162
options.statespaceexploration = true;
163
} else if (strcmp(argv[i], "-n") == 0 || strcmp(argv[i], "--no-statistics") == 0) {
164
options.printstatistics = false;
165
} else if (strcmp(argv[i], "-t") == 0 || strcmp(argv[i], "--trace") == 0) {
166
options.trace = true;
167
} else if (strcmp(argv[i], "-x") == 0 || strcmp(argv[i], "--xml-queries") == 0) {
169
fprintf(stderr, "Missing number after \"%s\"\n\n", argv[i]);
172
std::vector<std::string> q = explode(argv[++i]);
176
if(sscanf(qn.c_str(), "%d", &n) != 1 || n <= 0)
178
std::cerr << "Error in query numbers : " << qn << std::endl;
182
options.querynumbers.insert(--n);
185
} else if (strcmp(argv[i], "-r") == 0 || strcmp(argv[i], "--reduction") == 0) {
187
fprintf(stderr, "Missing number after \"%s\"\n\n", argv[i]);
190
if (sscanf(argv[++i], "%d", &options.enablereduction) != 1 || options.enablereduction < 0 || options.enablereduction > 3) {
191
fprintf(stderr, "Argument Error: Invalid reduction argument \"%s\"\n", argv[i]);
194
if(options.enablereduction == 3)
196
options.reductions.clear();
197
std::vector<std::string> q = explode(argv[++i]);
201
if(sscanf(qn.c_str(), "%d", &n) != 1 || n < 0 || n > 9)
203
std::cerr << "Error in reduction rule choice : " << qn << std::endl;
208
options.reductions.push_back(n);
212
} else if (strcmp(argv[i], "-d") == 0 || strcmp(argv[i], "--reduction-timeout") == 0) {
214
fprintf(stderr, "Missing number after \"%s\"\n\n", argv[i]);
217
if (sscanf(argv[++i], "%d", &options.reductionTimeout) != 1) {
218
fprintf(stderr, "Argument Error: Invalid reduction timeout argument \"%s\"\n", argv[i]);
221
} else if(strcmp(argv[i], "--seed-offset") == 0) {
222
if (sscanf(argv[++i], "%u", &options.seed_offset) != 1) {
223
fprintf(stderr, "Argument Error: Invalid seed offset argument \"%s\"\n", argv[i]);
226
} else if(strcmp(argv[i], "-p") == 0 || strcmp(argv[i], "--partial-order-reduction") == 0) {
227
options.stubbornreduction = false;
228
} else if(strcmp(argv[i], "-a") == 0 || strcmp(argv[i], "--siphon-trap") == 0) {
230
fprintf(stderr, "Missing number after \"%s\"\n\n", argv[i]);
233
if (sscanf(argv[++i], "%u", &options.siphontrapTimeout) != 1) {
234
fprintf(stderr, "Argument Error: Invalid siphon-trap timeout \"%s\"\n", argv[i]);
237
} else if(strcmp(argv[i], "--siphon-depth") == 0) {
239
fprintf(stderr, "Missing number after \"%s\"\n\n", argv[i]);
242
if (sscanf(argv[++i], "%u", &options.siphonDepth) != 1) {
243
fprintf(stderr, "Argument Error: Invalid siphon-depth count \"%s\"\n", argv[i]);
247
else if (strcmp(argv[i], "-tar") == 0)
252
else if (strcmp(argv[i], "--write-simplified") == 0)
254
options.query_out_file = std::string(argv[++i]);
256
else if(strcmp(argv[i], "--binary-query-io") == 0)
258
if (sscanf(argv[++i], "%u", &options.binary_query_io) != 1 || options.binary_query_io > 3) {
259
fprintf(stderr, "Argument Error: Invalid binary-query-io value \"%s\"\n", argv[i]);
263
else if (strcmp(argv[i], "--write-reduced") == 0)
265
options.model_out_file = std::string(argv[++i]);
267
#ifdef VERIFYPN_MC_Simplification
268
else if (strcmp(argv[i], "-z") == 0)
271
fprintf(stderr, "Missing number after \"%s\"\n\n", argv[i]);
274
if (sscanf(argv[++i], "%u", &options.cores) != 1) {
275
fprintf(stderr, "Argument Error: Invalid cores count \"%s\"\n", argv[i]);
280
else if (strcmp(argv[i], "-ctl") == 0){
282
if(strcmp(argv[i + 1], "local") == 0){
283
options.ctlalgorithm = CTL::Local;
285
else if(strcmp(argv[i + 1], "czero") == 0){
286
options.ctlalgorithm = CTL::CZero;
290
fprintf(stderr, "Argument Error: Invalid ctl-algorithm type \"%s\"\n", argv[i + 1]);
296
} else if (strcmp(argv[i], "-g") == 0 || strcmp(argv[i], "--game-mode") == 0){
297
options.gamemode = true;
298
} else if (strcmp(argv[i], "-c") == 0 || strcmp(argv[i], "--cpn-overapproximation") == 0) {
299
options.cpnOverApprox = true;
300
} else if (strcmp(argv[i], "-h") == 0 || strcmp(argv[i], "--help") == 0) {
301
printf("Usage: verifypn [options] model-file query-file\n"
302
"A tool for answering CTL and reachability queries of place cardinality\n"
303
"deadlock and transition fireability for weighted P/T Petri nets\n"
304
"extended with inhibitor arcs.\n"
307
" -k, --k-bound <number of tokens> Token bound, 0 to ignore (default)\n"
308
" -t, --trace Provide XML-trace to stderr\n"
309
" -s, --search-strategy <strategy> Search strategy:\n"
310
" - BestFS Heuristic search (default)\n"
311
" - BFS Breadth first search\n"
312
" - DFS Depth first search (CTL default)\n"
313
" - RDFS Random depth first search\n"
314
" - OverApprox Linear Over Approx\n"
315
" --seed-offset <number> Extra noise to add to the seed of the random number generation\n"
316
" -e, --state-space-exploration State-space exploration only (query-file is irrelevant)\n"
317
" -x, --xml-query <query index> Parse XML query file and verify query of a given index\n"
318
" -r, --reduction <type> Change structural net reduction:\n"
320
" - 1 aggressive reduction (default)\n"
321
" - 2 reduction preserving k-boundedness\n"
322
" - 3 user defined reduction sequence, eg -r 3 0,1,2,3 to use rules A,B,C,D only, and in that order\n"
323
" -d, --reduction-timeout <timeout> Timeout for structural reductions in seconds (default 60)\n"
324
" -q, --query-reduction <timeout> Query reduction timeout in seconds (default 30)\n"
325
" write -q 0 to disable query reduction\n"
326
" -l, --lpsolve-timeout <timeout> LPSolve timeout in seconds, default 10\n"
327
" -p, --partial-order-reduction Disable partial order reduction (stubborn sets)\n"
328
" -a, --siphon-trap <timeout> Siphon-Trap analysis timeout in seconds (default 0)\n"
329
" --siphon-depth <place count> Search depth of siphon (default 0, which counts all places)\n"
330
" -n, --no-statistics Do not display any statistics (default is to display it)\n"
331
" -h, --help Display this help message\n"
332
" -v, --version Display version information\n"
333
" -ctl <type> Verify CTL properties\n"
334
" - local Liu and Smolka's on-the-fly algorithm\n"
335
" - czero local with certain zero extension (default)\n"
336
" -c, --cpn-overapproximation Over approximate query on Colored Petri Nets (CPN only)\n"
337
//" -g Enable game mode (CTL Only)" // Feature not yet implemented
338
#ifdef VERIFYPN_MC_Simplification
339
" -z <number of cores> Number of cores to use (currently only query simplification)\n"
341
" -tar Enables Trace Abstraction Refinement for reachability properties\n"
342
" --write-simplified <filename> Outputs the queries to the given file after simplification\n"
343
" --write-reduced <filename> Outputs the model to the given file after structural reduction\n"
344
" --binary-query-io <0,1,2,3> Determines the input/output format of the query-file\n"
345
" - 0 MCC XML format for Input and Output\n"
346
" - 1 Input is binary, output is XML\n"
347
" - 2 Output is binary, input is XML\n"
348
" - 3 Input and Output is binary\n"
351
" 0 Successful, query satisfiable\n"
352
" 1 Unsuccesful, query not satisfiable\n"
353
" 2 Unknown, algorithm was unable to answer the question\n"
354
" 3 Error, see stderr for error message\n"
356
"VerifyPN is an untimed CTL verification engine for TAPAAL.\n"
357
"TAPAAL project page: <http://www.tapaal.net>\n");
359
} else if (strcmp(argv[i], "-v") == 0 || strcmp(argv[i], "--version") == 0) {
360
printf("VerifyPN (untimed verification engine for TAPAAL) %s\n", VERIFYPN_VERSION);
361
printf("Copyright (C) 2011-2020\n");
362
printf(" Frederik Meyer Boenneland <sadpantz@gmail.com>\n");
363
printf(" Jakob Dyhr <jakobdyhr@gmail.com>\n");
364
printf(" Peter Fogh <peter.f1992@gmail.com>\n");
365
printf(" Jonas Finnemann Jensen <jopsen@gmail.com>\n");
366
printf(" Lasse Steen Jensen <lassjen88@gmail.com>\n");
367
printf(" Peter Gjøl Jensen <root@petergjoel.dk>\n");
368
printf(" Tobias Skovgaard Jepsen <tobiasj1991@gmail.com>\n");
369
printf(" Mads Johannsen <mads_johannsen@yahoo.com>\n");
370
printf(" Isabella Kaufmann <bellakaufmann93@gmail.com>\n");
371
printf(" Andreas Hairing Klostergaard <kloster92@me.com>\n");
372
printf(" Søren Moss Nielsen <soren_moss@mac.com>\n");
373
printf(" Thomas Søndersø Nielsen <primogens@gmail.com>\n");
374
printf(" Samuel Pastva <daemontus@gmail.com>\n");
375
printf(" Jiri Srba <srba.jiri@gmail.com>\n");
376
printf(" Lars Kærlund Østergaard <larsko@gmail.com>\n");
377
printf("GNU GPLv3 or later <http://gnu.org/licenses/gpl.html>\n");
379
} else if (options.modelfile == NULL) {
380
options.modelfile = argv[i];
381
} else if (options.queryfile == NULL) {
382
options.queryfile = argv[i];
384
fprintf(stderr, "Argument Error: Unrecognized option \"%s\"\n", options.modelfile);
389
if (options.printstatistics) {
390
std::cout << std::endl << "Parameters: ";
391
for (int i = 1; i < argc; i++) {
392
std::cout << argv[i] << " ";
394
std::cout << std::endl;
397
if (options.statespaceexploration) {
398
// for state-space exploration some options are mandatory
399
options.enablereduction = 0;
401
options.queryReductionTimeout = 0;
402
options.lpsolveTimeout = 0;
403
options.siphontrapTimeout = 0;
404
options.stubbornreduction = false;
405
// outputtrace = false;
409
//----------------------- Validate Arguments -----------------------//
411
//Check for model file
412
if (!options.modelfile) {
413
fprintf(stderr, "Argument Error: No model-file provided\n");
417
//Check for query file
418
if (!options.modelfile && !options.statespaceexploration) {
419
fprintf(stderr, "Argument Error: No query-file provided\n");
427
readQueries(options_t& options, std::vector<std::string>& qstrings)
430
std::vector<Condition_ptr > conditions;
431
if (!options.statespaceexploration) {
433
ifstream qfile(options.queryfile, ifstream::in);
435
fprintf(stderr, "Error: Query file \"%s\" couldn't be opened\n", options.queryfile);
436
fprintf(stdout, "CANNOT_COMPUTE\n");
441
if(options.querynumbers.size() == 0)
443
string querystring; // excluding EF and AG
447
buffer << qfile.rdbuf();
448
string querystr = buffer.str(); // including EF and AG
449
//Parse XML the queries and querystr let be the index of xmlquery
451
qstrings.push_back(querystring);
452
//Validate query type
453
if (querystr.substr(0, 2) != "EF" && querystr.substr(0, 2) != "AG") {
454
fprintf(stderr, "Error: Query type \"%s\" not supported, only (EF and AG is supported)\n", querystr.substr(0, 2).c_str());
457
//Check if is invariant
458
bool isInvariant = querystr.substr(0, 2) == "AG";
460
//Wrap in not if isInvariant
461
querystring = querystr.substr(2);
462
std::vector<std::string> tmp;
463
conditions.emplace_back(ParseQuery(querystring, tmp));
464
if(isInvariant) conditions.back() = std::make_shared<AGCondition>(conditions.back());
465
else conditions.back() = std::make_shared<EFCondition>(conditions.back());
469
std::vector<QueryItem> queries;
470
if(options.binary_query_io & 1)
472
QueryBinaryParser parser;
473
if (!parser.parse(qfile, options.querynumbers)) {
474
fprintf(stderr, "Error: Failed parsing binary query file\n");
475
fprintf(stdout, "DO_NOT_COMPETE\n");
479
queries = std::move(parser.queries);
483
QueryXMLParser parser;
484
if (!parser.parse(qfile, options.querynumbers)) {
485
fprintf(stderr, "Error: Failed parsing XML query file\n");
486
fprintf(stdout, "DO_NOT_COMPETE\n");
490
queries = std::move(parser.queries);
494
for(auto& q : queries)
496
if(!options.querynumbers.empty()
497
&& options.querynumbers.count(i) == 0)
504
if (q.parsingResult == QueryItem::UNSUPPORTED_QUERY) {
505
fprintf(stdout, "The selected query in the XML query file is not supported\n");
506
fprintf(stdout, "FORMULA %s CANNOT_COMPUTE\n", q.id.c_str());
509
// fprintf(stdout, "Index of the selected query: %d\n\n", xmlquery);
511
conditions.push_back(q.query);
512
if (conditions.back() == nullptr) {
513
fprintf(stderr, "Error: Failed to parse query \"%s\"\n", q.id.c_str()); //querystr.substr(2).c_str());
514
fprintf(stdout, "FORMULA %s CANNOT_COMPUTE\n", q.id.c_str());
515
conditions.pop_back();
518
qstrings.push_back(q.id);
523
} else { // state-space exploration
524
std::string querystring = "false";
525
std::vector<std::string> empty;
526
conditions.push_back(std::make_shared<EFCondition>(ParseQuery(querystring, empty)));
531
ReturnValue parseModel(AbstractPetriNetBuilder& builder, options_t& options)
534
ifstream mfile(options.modelfile, ifstream::in);
536
fprintf(stderr, "Error: Model file \"%s\" couldn't be opened\n", options.modelfile);
537
fprintf(stdout, "CANNOT_COMPUTE\n");
542
//Parse and build the petri net
544
parser.parse(mfile, &builder);
545
options.isCPN = builder.isColored();
552
void printStats(PetriNetBuilder& builder, options_t& options)
554
if (options.printstatistics) {
555
if (options.enablereduction != 0) {
557
std::cout << "Size of net before structural reductions: " <<
558
builder.numberOfPlaces() << " places, " <<
559
builder.numberOfTransitions() << " transitions" << std::endl;
560
std::cout << "Size of net after structural reductions: " <<
561
builder.numberOfPlaces() - builder.RemovedPlaces() << " places, " <<
562
builder.numberOfTransitions() - builder.RemovedTransitions() << " transitions" << std::endl;
563
std::cout << "Structural reduction finished after " << builder.getReductionTime() <<
564
" seconds" << std::endl;
566
std::cout << "\nNet reduction is enabled.\n";
567
builder.printStats(std::cout);
572
void printUnfoldingStats(ColoredPetriNetBuilder& builder, options_t& options) {
573
if (options.printstatistics) {
574
if (!builder.isColored() && !builder.isUnfolded())
576
std::cout << "\nSize of colored net: " <<
577
builder.getPlaceCount() << " places, " <<
578
builder.getTransitionCount() << " transitions, and " <<
579
builder.getArcCount() << " arcs" << std::endl;
580
std::cout << "Size of unfolded net: " <<
581
builder.getUnfoldedPlaceCount() << " places, " <<
582
builder.getUnfoldedTransitionCount() << " transitions, and " <<
583
builder.getUnfoldedArcCount() << " arcs" << std::endl;
584
std::cout << "Unfolded in " << builder.getUnfoldTime() << " seconds" << std::endl;
588
std::string getXMLQueries(vector<std::shared_ptr<Condition>> queries, vector<std::string> querynames, std::vector<ResultPrinter::Result> results) {
590
for(uint32_t i = 0; i < results.size(); i++) {
591
if (results[i] == ResultPrinter::CTL) {
601
std::stringstream ss;
602
ss << "<?xml version=\"1.0\"?>\n<property-set xmlns=\"http://mcc.lip6.fr/\">\n";
604
for(uint32_t i = 0; i < queries.size(); i++) {
605
if (!(results[i] == ResultPrinter::CTL)) {
608
ss << " <property>\n <id>" << querynames[i] << "</id>\n <description>Simplified</description>\n <formula>\n";
609
queries[i]->toXML(ss, 3);
610
ss << " </formula>\n </property>\n";
613
ss << "</property-set>\n";
618
void writeQueries(vector<std::shared_ptr<Condition>>& queries, vector<std::string>& querynames, std::vector<uint32_t>& order, std::string& filename, bool binary, const std::unordered_map<std::string, uint32_t>& place_names)
624
out.open(filename, std::ios::binary | std::ios::out);
626
for(uint32_t j = 0; j < queries.size(); j++) {
627
if(queries[j]->isTriviallyTrue() || queries[j]->isTriviallyFalse()) continue;
630
out.write(reinterpret_cast<const char *>(&cnt), sizeof(uint32_t));
631
cnt = place_names.size();
632
out.write(reinterpret_cast<const char *>(&cnt), sizeof(uint32_t));
633
for(auto& kv : place_names)
635
out.write(reinterpret_cast<const char *>(&kv.second), sizeof(uint32_t));
636
out.write(kv.first.data(), kv.first.size());
637
out.write("\0", sizeof(char));
642
out.open(filename, std::ios::out);
643
out << "<?xml version=\"1.0\"?>\n<property-set xmlns=\"http://mcc.lip6.fr/\">\n";
646
for(uint32_t j = 0; j < queries.size(); j++) {
648
if(queries[i]->isTriviallyTrue() || queries[i]->isTriviallyFalse()) continue;
651
out.write(querynames[i].data(), querynames[i].size());
652
out.write("\0", sizeof(char));
653
queries[i]->toBinary(out);
657
out << " <property>\n <id>" << querynames[i] << "</id>\n <description>Simplified</description>\n <formula>\n";
658
queries[i]->toXML(out, 3);
659
out << " </formula>\n </property>\n";
665
out << "</property-set>\n";
670
int main(int argc, char* argv[]) {
674
ReturnValue v = parseOptions(argc, argv, options);
675
if(v != ContinueCode) return v;
677
srand (time(NULL) xor options.seed_offset);
678
ColoredPetriNetBuilder cpnBuilder;
679
if(parseModel(cpnBuilder, options) != ContinueCode)
681
std::cerr << "Error parsing the model" << std::endl;
684
if(options.cpnOverApprox && !cpnBuilder.isColored())
686
std::cerr << "CPN OverApproximation is only usable on colored models" << std::endl;
689
if (options.printstatistics) {
690
std::cout << "Finished parsing model" << std::endl;
693
//----------------------- Parse Query -----------------------//
694
std::vector<std::string> querynames;
695
auto queries = readQueries(options, querynames);
697
if(options.printstatistics && options.queryReductionTimeout > 0)
700
std::cout << "RWSTATS LEGEND:";
701
stats.printRules(std::cout);
702
std::cout << std::endl;
705
if(cpnBuilder.isColored())
708
EvaluationContext context(nullptr, nullptr);
709
for (ssize_t qid = queries.size() - 1; qid >= 0; --qid) {
710
queries[qid] = queries[qid]->pushNegation(stats, context, false, false, false);
711
if(options.printstatistics)
713
std::cout << "\nQuery before expansion and reduction: ";
714
queries[qid]->toString(std::cout);
715
std::cout << std::endl;
717
std::cout << "RWSTATS COLORED PRE:";
718
stats.print(std::cout);
719
std::cout << std::endl;
724
if (options.cpnOverApprox) {
725
for (ssize_t qid = queries.size() - 1; qid >= 0; --qid) {
727
EvaluationContext context(nullptr, nullptr);
728
auto q = queries[qid]->pushNegation(stats, context, false, false, false);
729
if (!q->isReachability() || q->isLoopSensitive() || stats.negated_fireability) {
730
std::cerr << "Warning: CPN OverApproximation is only available for Reachability queries without deadlock, negated fireability and UpperBounds, skipping " << querynames[qid] << std::endl;
731
queries.erase(queries.begin() + qid);
732
querynames.erase(querynames.begin() + qid);
737
auto builder = options.cpnOverApprox ? cpnBuilder.stripColors() : cpnBuilder.unfold();
738
printUnfoldingStats(cpnBuilder, options);
740
std::vector<ResultPrinter::Result> results(queries.size(), ResultPrinter::Result::Unknown);
741
ResultPrinter printer(&builder, &options, querynames);
743
//----------------------- Query Simplification -----------------------//
744
bool alldone = options.queryReductionTimeout > 0;
745
PetriNetBuilder b2(builder);
746
std::unique_ptr<PetriNet> qnet(b2.makePetriNet(false));
747
MarkVal* qm0 = qnet->makeInitialMarking();
748
ResultPrinter p2(&b2, &options, querynames);
750
if(queries.size() == 0 || contextAnalysis(cpnBuilder, b2, qnet.get(), queries) != ContinueCode)
752
std::cerr << "Could not analyze the queries" << std::endl;
756
if (options.strategy == PetriEngine::Reachability::OverApprox && options.queryReductionTimeout == 0)
758
// Conflicting flags "-s OverApprox" and "-q 0"
759
std::cerr << "Conflicting flags '-s OverApprox' and '-q 0'" << std::endl;
763
// simplification. We always want to do negation-push and initial marking check.
765
// simplification. We always want to do negation-push and initial marking check.
766
std::vector<LPCache> caches(options.cores);
767
std::atomic<uint32_t> to_handle(queries.size());
768
auto begin = std::chrono::high_resolution_clock::now();
769
auto end = std::chrono::high_resolution_clock::now();
770
std::vector<bool> hadTo(queries.size(), true);
774
auto qt = (options.queryReductionTimeout - std::chrono::duration_cast<std::chrono::seconds>(end - begin).count()) / ( 1 + (to_handle / options.cores));
775
if((to_handle <= options.cores || options.cores == 1) && to_handle > 0)
776
qt = (options.queryReductionTimeout - std::chrono::duration_cast<std::chrono::seconds>(end - begin).count()) / to_handle;
777
std::atomic<uint32_t> cnt(0);
778
#ifdef VERIFYPN_MC_Simplification
780
std::vector<std::thread> threads;
782
std::vector<std::stringstream> tstream(queries.size());
783
uint32_t old = to_handle;
784
for(size_t c = 0; c < std::min<uint32_t>(options.cores, old); ++c)
786
#ifdef VERIFYPN_MC_Simplification
787
threads.push_back(std::thread([&,c](){
789
auto simplify = [&,c](){
791
auto& out = tstream[c];
792
auto& cache = caches[c];
796
if(i >= queries.size()) return;
797
if(!hadTo[i]) continue;
800
EvaluationContext context(qm0, qnet.get());
801
if(options.printstatistics && options.queryReductionTimeout > 0)
803
out << "\nQuery before reduction: ";
804
queries[i]->toString(out);
808
#ifndef VERIFYPN_MC_Simplification
809
qt = (options.queryReductionTimeout - std::chrono::duration_cast<std::chrono::seconds>(end - begin).count()) / (queries.size() - i);
811
// this is used later, we already know that this is a plain reachability (or AG)
812
bool wasAGCPNApprox = dynamic_cast<NotCondition*>(queries[i].get()) != nullptr;
813
int preSize=queries[i]->formulaSize();
814
queries[i] = Condition::initialMarkingRW([&](){ return queries[i]; }, stats, context, false, false, true)
815
->pushNegation(stats, context, false, false, true);
816
wasAGCPNApprox |= dynamic_cast<NotCondition*>(queries[i].get()) != nullptr;
818
if(options.queryReductionTimeout > 0 && options.printstatistics)
820
out << "RWSTATS PRE:";
825
if (options.queryReductionTimeout > 0 && qt > 0)
827
SimplificationContext simplificationContext(qm0, qnet.get(), qt,
828
options.lpsolveTimeout, &cache);
831
queries[i] = (queries[i]->simplify(simplificationContext)).formula->pushNegation(stats, context, false, false, true);
832
wasAGCPNApprox |= dynamic_cast<NotCondition*>(queries[i].get()) != nullptr;
833
if(options.printstatistics)
835
out << "RWSTATS POST:";
839
} catch (std::bad_alloc& ba){
840
std::cerr << "Query reduction failed." << std::endl;
841
std::cerr << "Exception information: " << ba.what() << std::endl;
844
std::exit(ErrorCode);
847
if(options.printstatistics)
849
out << "\nQuery after reduction: ";
850
queries[i]->toString(out);
853
if(simplificationContext.timeout()){
854
if(options.printstatistics)
855
out << "Query reduction reached timeout.\n";
858
if(options.printstatistics)
859
out << "Query reduction finished after " << simplificationContext.getReductionTime() << " seconds.\n";
863
else if(options.printstatistics)
865
out << "Skipping linear-programming (-q 0)" << std::endl;
867
if(options.cpnOverApprox && wasAGCPNApprox)
869
if(queries[i]->isTriviallyTrue())
870
queries[i] = std::make_shared<BooleanCondition>(false);
871
else if(queries[i]->isTriviallyFalse())
872
queries[i] = std::make_shared<BooleanCondition>(true);
873
queries[i]->setInvariant(wasAGCPNApprox);
877
if(options.printstatistics)
879
int postSize=queries[i]->formulaSize();
880
double redPerc = preSize-postSize == 0 ? 0 : ((double)(preSize-postSize)/(double)preSize)*100;
881
out << "Query size reduced from " << preSize << " to " << postSize << " nodes ( " << redPerc << " percent reduction).\n";
885
#ifdef VERIFYPN_MC_Simplification
892
#ifndef VERIFYPN_MC_Simplification
893
std::cout << tstream[0].str() << std::endl;
896
for(size_t i = 0; i < std::min<uint32_t>(options.cores, old); ++i)
899
std::cout << tstream[i].str();
900
std::cout << std::endl;
903
end = std::chrono::high_resolution_clock::now();
905
} while(std::any_of(hadTo.begin(), hadTo.end(), [](auto a) { return a;}) && std::chrono::duration_cast<std::chrono::seconds>(end - begin).count() < options.queryReductionTimeout && to_handle > 0);
908
if(options.query_out_file.size() > 0)
910
std::vector<uint32_t> reorder(queries.size());
911
for(uint32_t i = 0; i < queries.size(); ++i) reorder[i] = i;
912
std::sort(reorder.begin(), reorder.end(), [&queries](auto a, auto b){
914
if(queries[a]->isReachability() != queries[b]->isReachability())
915
return queries[a]->isReachability() > queries[b]->isReachability();
916
if(queries[a]->isLoopSensitive() != queries[b]->isLoopSensitive())
917
return queries[a]->isLoopSensitive() < queries[b]->isLoopSensitive();
918
if(queries[a]->containsNext() != queries[b]->containsNext())
919
return queries[a]->containsNext() < queries[b]->containsNext();
920
return queries[a]->formulaSize() < queries[b]->formulaSize();
922
writeQueries(queries, querynames, reorder, options.query_out_file, options.binary_query_io & 2, builder.getPlaceNames());
928
if (!options.statespaceexploration){
929
for(size_t i = 0; i < queries.size(); ++i)
931
if(queries[i]->isTriviallyTrue()){
932
results[i] = p2.handle(i, queries[i].get(), ResultPrinter::Satisfied).first;
933
if(results[i] == ResultPrinter::Ignore && options.printstatistics)
935
std::cout << "Unable to decide if query is satisfied." << std::endl << std::endl;
937
else if (options.printstatistics) {
938
std::cout << "Query solved by Query Simplification." << std::endl << std::endl;
940
} else if (queries[i]->isTriviallyFalse()) {
941
results[i] = p2.handle(i, queries[i].get(), ResultPrinter::NotSatisfied).first;
942
if(results[i] == ResultPrinter::Ignore && options.printstatistics)
944
std::cout << "Unable to decide if query is satisfied." << std::endl << std::endl;
946
else if (options.printstatistics) {
947
std::cout << "Query solved by Query Simplification." << std::endl << std::endl;
949
} else if (options.strategy == PetriEngine::Reachability::OverApprox){
950
results[i] = p2.handle(i, queries[i].get(), ResultPrinter::Unknown).first;
951
if (options.printstatistics) {
952
std::cout << "Unable to decide if query is satisfied." << std::endl << std::endl;
954
} else if (!queries[i]->isReachability()) {
955
results[i] = ResultPrinter::CTL;
958
queries[i] = queries[i]->prepareForReachability();
963
if(alldone && options.model_out_file.size() == 0) return SuccessCode;
966
options.queryReductionTimeout = 0;
968
//--------------------- Apply Net Reduction ---------------//
970
if (options.enablereduction > 0) {
971
// Compute how many times each place appears in the query
972
builder.startTimer();
973
builder.reduce(queries, results, options.enablereduction, options.trace, nullptr, options.reductionTimeout, options.reductions);
974
printer.setReducer(builder.getReducer());
977
printStats(builder, options);
979
auto net = std::unique_ptr<PetriNet>(builder.makePetriNet());
981
if(options.model_out_file.size() > 0)
984
file.open(options.model_out_file, std::ios::out);
988
if(alldone) return SuccessCode;
990
//----------------------- Verify CTL queries -----------------------//
991
std::vector<size_t> ctl_ids;
992
for(size_t i = 0; i < queries.size(); ++i)
994
if(results[i] == ResultPrinter::CTL)
996
ctl_ids.push_back(i);
1000
if (ctl_ids.size() > 0) {
1002
PetriEngine::Reachability::Strategy reachabilityStrategy=options.strategy;
1005
if(queries.size() == 0 || contextAnalysis(cpnBuilder, builder, net.get(), queries) != ContinueCode)
1007
std::cerr << "An error occurred while assigning indexes" << std::endl;
1010
if(options.strategy == DEFAULT) options.strategy = PetriEngine::Reachability::DFS;
1011
v = CTLMain(net.get(),
1012
options.ctlalgorithm,
1015
options.printstatistics,
1017
options.stubbornreduction,
1023
if (std::find(results.begin(), results.end(), ResultPrinter::Unknown) == results.end()) {
1026
// go back to previous strategy if the program continues
1027
options.strategy=reachabilityStrategy;
1029
options.isctl=false;
1031
//----------------------- Siphon Trap ------------------------//
1033
if(options.siphontrapTimeout > 0){
1034
for (uint32_t i = 0; i < results.size(); i ++) {
1035
bool isDeadlockQuery = std::dynamic_pointer_cast<DeadlockCondition>(queries[i]) != nullptr;
1037
if (results[i] == ResultPrinter::Unknown && isDeadlockQuery) {
1038
STSolver stSolver(printer, *net, queries[i].get(), options.siphonDepth);
1039
stSolver.solve(options.siphontrapTimeout);
1040
results[i] = stSolver.printResult();
1041
if (results[i] == Reachability::ResultPrinter::NotSatisfied && options.printstatistics) {
1042
std::cout << "Query solved by Siphon-Trap Analysis." << std::endl << std::endl;
1047
if (std::find(results.begin(), results.end(), ResultPrinter::Unknown) == results.end()) {
1051
options.siphontrapTimeout = 0;
1053
//----------------------- Reachability -----------------------//
1055
//Analyse context again to reindex query
1056
contextAnalysis(cpnBuilder, builder, net.get(), queries);
1058
// Change default place-holder to default strategy
1059
if(options.strategy == DEFAULT) options.strategy = PetriEngine::Reachability::HEUR;
1061
if(options.tar && net->numberOfPlaces() > 0)
1063
//Create reachability search strategy
1064
TARReachabilitySearch strategy(printer, *net, builder.getReducer(), options.kbound);
1066
// Change default place-holder to default strategy
1067
fprintf(stdout, "Search strategy option was ignored as the TAR engine is called.\n");
1068
options.strategy = PetriEngine::Reachability::DFS;
1070
//Reachability search
1071
strategy.reachable(queries, results,
1072
options.printstatistics,
1077
ReachabilitySearch strategy(*net, printer, options.kbound);
1079
// Change default place-holder to default strategy
1080
if(options.strategy == DEFAULT) options.strategy = PetriEngine::Reachability::HEUR;
1082
//Reachability search
1083
strategy.reachable(queries, results,
1085
options.stubbornreduction,
1086
options.statespaceexploration,
1087
options.printstatistics,