Merge lp:~tapaal-ltl/verifypn/ltl-spring-2021 into lp:verifypn

Proposed by Nikolaj Jensen Ulrik
Status: Merged
Approved by: Jiri Srba
Approved revision: 273
Merged at revision: 246
Proposed branch: lp:~tapaal-ltl/verifypn/ltl-spring-2021
Merge into: lp:verifypn
Diff against target: 6103 lines (+3979/-503)
72 files modified
.bzrignore (+3/-0)
CMakeLists.txt (+1/-1)
Scripts/MCC21/competition-scripts/tapaal-tweaked-rev270.sh (+540/-0)
Scripts/MCC21/competition-scripts/tapaal.sh (+20/-29)
include/LTL/Algorithm/ModelChecker.h (+26/-28)
include/LTL/Algorithm/NestedDepthFirstSearch.h (+16/-11)
include/LTL/Algorithm/TarjanModelChecker.h (+33/-20)
include/LTL/LTL.h (+0/-1)
include/LTL/LTLMain.h (+1/-1)
include/LTL/LTLToBuchi.h (+3/-4)
include/LTL/Replay.h (+66/-0)
include/LTL/Simplification/SpotToPQL.h (+1/-1)
include/LTL/Structures/BitProductStateSet.h (+2/-1)
include/LTL/Structures/BuchiAutomaton.h (+29/-0)
include/LTL/Structures/GuardInfo.h (+43/-2)
include/LTL/Structures/ProductState.h (+0/-1)
include/LTL/Stubborn/AutomatonStubbornSet.h (+99/-0)
include/LTL/Stubborn/EvalAndSetVisitor.h (+74/-0)
include/LTL/Stubborn/ReachabilityStubbornSpooler.h (+52/-0)
include/LTL/Stubborn/ReducingSuccessorGenerator.h (+46/-0)
include/LTL/Stubborn/SafeAutStubbornSet.h (+71/-0)
include/LTL/Stubborn/VisibleLTLStubbornSet.h (+111/-0)
include/LTL/Stubborn/VisibleTransitionVisitor.h (+82/-0)
include/LTL/SuccessorGeneration/AutomatonHeuristic.h (+7/-1)
include/LTL/SuccessorGeneration/BuchiSuccessorGenerator.h (+1/-1)
include/LTL/SuccessorGeneration/ComposedHeuristic.h (+53/-0)
include/LTL/SuccessorGeneration/DistanceHeuristic.h (+3/-0)
include/LTL/SuccessorGeneration/FireCountHeuristic.h (+57/-0)
include/LTL/SuccessorGeneration/Heuristic.h (+9/-0)
include/LTL/SuccessorGeneration/HeuristicParser.h (+28/-0)
include/LTL/SuccessorGeneration/Heuristics.h (+4/-0)
include/LTL/SuccessorGeneration/LogFireCountHeuristic.h (+69/-0)
include/LTL/SuccessorGeneration/ProductSuccessorGenerator.h (+45/-58)
include/LTL/SuccessorGeneration/RandomHeuristic.h (+3/-1)
include/LTL/SuccessorGeneration/ReachStubProductSuccessorGenerator.h (+106/-0)
include/LTL/SuccessorGeneration/ResumingSuccessorGenerator.h (+6/-6)
include/LTL/SuccessorGeneration/Spoolers.h (+3/-0)
include/LTL/SuccessorGeneration/SpoolingSuccessorGenerator.h (+86/-15)
include/LTL/SuccessorGeneration/SuccessorSpooler.h (+2/-2)
include/LTL/SuccessorGeneration/WeightedComposedHeuristic.h (+55/-0)
include/PetriEngine/PQL/Visitor.h (+13/-22)
include/PetriEngine/Reachability/ReachabilitySearch.h (+3/-1)
include/PetriEngine/Reducer.h (+1/-0)
include/PetriEngine/Structures/SuccessorQueue.h (+23/-6)
include/PetriEngine/Stubborn/InterestingTransitionVisitor.h (+10/-2)
include/PetriEngine/Stubborn/ReachabilityStubbornSet.h (+20/-4)
include/PetriEngine/Stubborn/StubbornSet.h (+76/-5)
include/PetriEngine/options.h (+22/-1)
src/LTL/Algorithm/CMakeLists.txt (+1/-1)
src/LTL/Algorithm/LTLToBuchi.cpp (+21/-9)
src/LTL/Algorithm/NestedDepthFirstSearch.cpp (+46/-39)
src/LTL/Algorithm/TarjanModelChecker.cpp (+62/-29)
src/LTL/CMakeLists.txt (+6/-4)
src/LTL/LTLMain.cpp (+167/-57)
src/LTL/Replay.cpp (+326/-0)
src/LTL/Simplification/SpotToPQL.cpp (+5/-5)
src/LTL/Stubborn/AutomatonStubbornSet.cpp (+355/-0)
src/LTL/Stubborn/CMakeLists.txt (+4/-0)
src/LTL/Stubborn/EvalAndSetVisitor.cpp (+126/-0)
src/LTL/Stubborn/SafeAutStubbornSet.cpp (+64/-0)
src/LTL/Stubborn/VisibleLTLStubbornSet.cpp (+176/-0)
src/LTL/Stubborn/VisibleTransitionVisitor.cpp (+132/-0)
src/LTL/SuccessorGeneration/AutomatonHeuristic.cpp (+45/-28)
src/LTL/SuccessorGeneration/CMakeLists.txt (+10/-1)
src/LTL/SuccessorGeneration/HeuristicLexer.l (+65/-0)
src/LTL/SuccessorGeneration/HeuristicParser.y (+72/-0)
src/LTL/SuccessorGeneration/ResumingSuccessorGenerator.cpp (+7/-7)
src/PetriEngine/Reducer.cpp (+43/-0)
src/PetriEngine/Stubborn/InterestingTransitionVisitor.cpp (+56/-1)
src/PetriEngine/Stubborn/ReachabilityStubbornSet.cpp (+3/-2)
src/PetriEngine/Stubborn/StubbornSet.cpp (+12/-63)
src/VerifyPN.cpp (+151/-32)
To merge this branch: bzr merge lp:~tapaal-ltl/verifypn/ltl-spring-2021
Reviewer Review Type Date Requested Status
Jiri Srba Approve
Review via email: mp+404520@code.launchpad.net

Description of the change

Full LTL engine with stubborn sets and all our fancy stuff.
New features:
- Stubborn sets, both classic Valmari method, new reachability method, and mix of the two
- Unified method for specifying heuristics on command-line
- Replay of traces (LTL only)

To post a comment you must log in.
259. By Nikolaj Jensen Ulrik

Merge verifypn@243 into ltl-spring-2021@258

260. By Nikolaj Jensen Ulrik

Merge verifypn@245

261. By Nikolaj Jensen Ulrik

Fix stupid nullptr

262. By Nikolaj Jensen Ulrik

WIP fixing bad traces, still some bugs left

263. By Nikolaj Jensen Ulrik

More sane trace behaviour maybe

264. By Nikolaj Jensen Ulrik

Print GUI-friendly output string from LTL

265. By Nikolaj Jensen Ulrik

Fixing trace formats

266. By Nikolaj Jensen Ulrik

Fix traces also in NDFS

267. By Nikolaj Jensen Ulrik

Fix crash in Tarjan's algorithm when trying to generate a trace when the invariant self loop optimization triggers

268. By Nikolaj Jensen Ulrik

hov

269. By Nikolaj Jensen Ulrik

Fix heuristics not being used

270. By Nikolaj Jensen Ulrik

Refine MCC21 competition script with fresh heuristic and no stubborn sets in random phase

271. By Nikolaj Jensen Ulrik

fix warnings

272. By Nikolaj Jensen Ulrik

Move rev 270 change in tapaal.sh to new file and revert 270

273. By Nikolaj Jensen Ulrik

fully revert tapaal.sh back to trunk version (aka MCC submission), newer LTL script can be found in tapaal-tweaked-rev270.sh

Revision history for this message
Jiri Srba (srba) :
review: Approve

Preview Diff

[H/L] Next/Prev Comment, [J/K] Next/Prev File, [N/P] Next/Prev Hunk
1=== modified file '.bzrignore'
2--- .bzrignore 2020-03-04 14:25:25 +0000
3+++ .bzrignore 2021-08-05 10:56:53 +0000
4@@ -9,5 +9,8 @@
5 src/PetriEngine/PQL/PQLQueryParser.parser.cpp
6 src/PetriEngine/PQL/PQLQueryParser.parser.hpp
7 src/PetriEngine/PQL/PQLQueryTokens.lexer.cpp
8+src/LTL/SuccessorGeneration/HeuristicLexer.lexer.cpp
9+src/LTL/SuccessorGeneration/HeuristicParser.parser.cpp
10+src/LTL/SuccessorGeneration/HeuristicParser.parser.hpp
11 build/
12
13
14=== modified file 'CMakeLists.txt'
15--- CMakeLists.txt 2021-04-16 19:23:03 +0000
16+++ CMakeLists.txt 2021-08-05 10:56:53 +0000
17@@ -51,7 +51,7 @@
18 endif ()
19
20 set(CMAKE_CXX_FLAGS_RELEASE "${CMAKE_CXX_FLAGS_RELEASE} -s -Wall -pedantic-errors -O3 -DNDEBUG")
21-set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -O0 -fno-omit-frame-pointer")
22+set(CMAKE_CXX_FLAGS_DEBUG "${CMAKE_CXX_FLAGS_DEBUG} -O0 -fno-omit-frame-pointer ")
23 set(CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} -O3 -DNDEBUG")
24 if (VERIFYPN_Static AND NOT APPLE)
25 set(CMAKE_EXE_LINKER_FLAGS_RELEASE "${CMAKE_EXE_LINKER_FLAGS_RELEASE} -static")
26
27=== added file 'Scripts/MCC21/competition-scripts/tapaal-tweaked-rev270.sh'
28--- Scripts/MCC21/competition-scripts/tapaal-tweaked-rev270.sh 1970-01-01 00:00:00 +0000
29+++ Scripts/MCC21/competition-scripts/tapaal-tweaked-rev270.sh 2021-08-05 10:56:53 +0000
30@@ -0,0 +1,540 @@
31+#!/bin/bash
32+
33+# This is the initialization script for the participation of TAPAAL
34+# untimed engine verifypn in the Petri net competition 2021.
35+
36+DIR=$(dirname "${BASH_SOURCE[0]}")
37+
38+if [ -z "$MODEL_PATH" ] ; then
39+ MODEL_PATH=.
40+ echo "Setting MODEL_PATH=$MODEL_PATH"
41+else
42+ echo "Got MODEL_PATH=$MODEL_PATH"
43+fi
44+
45+if [ -z "$VERIFYPN" ] ; then
46+ VERIFYPN="$DIR"/verifypn
47+ echo "Setting VERIFYPN=$VERIFYPN"
48+else
49+ echo "Got VERIFYPN=$VERIFYPN"
50+fi
51+
52+if [ -z "$BK_TIME_CONFINEMENT" ] ; then
53+ BK_TIME_CONFINEMENT=3600
54+ echo "Setting BK_TIME_CONFINEMENT=$BK_TIME_CONFINEMENT"
55+else
56+ echo "Got BK_TIME_CONFINEMENT=$BK_TIME_CONFINEMENT"
57+fi
58+TIMEOUT_TOTAL=$(echo "$BK_TIME_CONFINEMENT-10" | bc)
59+
60+if [ -z "$TEMPDIR" ]; then
61+ TEMPDIR="$DIR/tmp"
62+ echo "Setting TEMPDIR=$TEMPDIR"
63+else
64+ echo "Got TEMPDIR=$TEMPDIR"
65+fi
66+
67+TIMEOUT_CMD=timeout
68+TIME_CMD="/usr/bin/time -f \"@@@%e,%M@@@\" "
69+
70+START_TIME=$(date +"%s")
71+SECONDS=0
72+
73+#Allowed memory in kB
74+if [ -z "$BK_MEMORY_CONFINEMENT" ] ; then
75+ BK_MEMORY_CONFINEMENT="16000"
76+ echo "Setting BK_MEMORY_CONFINEMENT=$BK_MEMORY_CONFINEMENT"
77+else
78+ echo "Got BK_MEMORY_CONFINEMENT=$BK_MEMORY_CONFINEMENT"
79+fi
80+MEM=$(echo "$BK_MEMORY_CONFINEMENT-500" | bc)
81+MEM=$(echo "$MEM*1024" | bc)
82+echo "Limiting to $MEM kB"
83+ulimit -v $MEM
84+
85+
86+PAR_CMD=parallel
87+
88+# Verification options
89+OPTIONS=""
90+PARALLEL_SIMPLIFICATION_OPTIONS=""
91+echo "Total timeout: " "$TIMEOUT_TOTAL"
92+
93+# Timeouts (in seconds)
94+TIMEOUT_SIMP=$(echo "$TIMEOUT_TOTAL/5" | bc)
95+TIMEOUT_LP=$(echo "$TIMEOUT_TOTAL/120" | bc)
96+TIMEOUT_RED=$(echo "$TIMEOUT_TOTAL/12" | bc)
97+TIMEOUT_PAR=$(echo "$TIMEOUT_TOTAL/14" | bc) # competition 4.29 min
98+TIMEOUT_SEQ_MIN=$(echo "$TIMEOUT_TOTAL/7" | bc) # competition 8.6 min
99+SIPHONTRAP=$(echo "$TIMEOUT_TOTAL/12" | bc)
100+SHORTRED=$(echo "$TIMEOUT_TOTAL/30" | bc)
101+EXTENDED=$(echo "$TIMEOUT_TOTAL/25" | bc)
102+
103+STRATEGIES_SEQ[0]="-s RDFS -q 40 -l 5 -d $SHORTRED"
104+STRATEGIES_SEQ[1]="-tar -q 0 -d $SHORTRED"
105+
106+
107+STRATEGY_MULTI="-s BFS -q 15 -l 3 -d $SHORTRED"
108+
109+STRATEGIES_PAR[0]="-tar -s RDFS -q 0 -l 0 -d $SHORTRED"
110+STRATEGIES_PAR[1]="-s BestFS -q 0 -l 0 -d $SHORTRED"
111+STRATEGIES_PAR[2]="-s BFS -q 0 -l 0 -d $SHORTRED"
112+STRATEGIES_PAR[3]="-s DFS -q 0 -l 0 -d $SHORTRED"
113+
114+STRATEGIES_RAND[0]="-s RDFS --seed-offset 0 -q $TIMEOUT_TOTAL -l 0 -d $SHORTRED"
115+STRATEGIES_RAND[1]="-s RDFS --seed-offset 1337 -q 0 -l 0 -d 0"
116+STRATEGIES_RAND[2]="-s RDFS --seed-offset 2018 -q 0 -l 0 -d $SHORTRED"
117+STRATEGIES_RAND[3]="-tar -s RDFS --seed-offset 9220 -q 0 -l 0 -d $SHORTRED"
118+
119+run_multi=false;
120+
121+function time_left {
122+ T=$(date +"%s")
123+ SECONDS=$(echo "$T-$START_TIME" | bc)
124+ SECONDS=$(echo "$TIMEOUT_TOTAL-$SECONDS" | bc)
125+ echo "Time left: " $SECONDS
126+ if [[ "$SECONDS" -le 0 ]] ; then
127+ echo "Out of time, terminating!"
128+ rm $QF
129+ rm $MF
130+ exit
131+ fi
132+}
133+
134+time_left
135+
136+function verifyparallel {
137+ # Keep track of time passed (in seconds)
138+ mkdir -p $TEMPDIR
139+ QF=$(mktemp --tmpdir=$TEMPDIR )
140+ MF=$(mktemp --tmpdir=$TEMPDIR )
141+ echo $TEMPDIR
142+ echo $QF
143+ echo $MF
144+ local NUMBER=`cat $CATEGORY | grep "<property>" | wc -l`
145+ QUERIES=( $(seq 1 $NUMBER) )
146+ #MULTIQUERY_INPUT=$(sed -e "s/ /,/g" <<< ${QUERIES[@]})
147+ MULTIQUERY_INPUT=$(echo ${QUERIES[@]} | sed -e "s/ /,/g")
148+ time_left
149+
150+ # Step -1: Colored Overapproximation
151+ echo "---------------------------------------------------"
152+ echo " Step -1: Stripping Colors "
153+ echo "---------------------------------------------------"
154+ echo "Verifying stripped models ($NUMBER in total) "
155+ TMP=$($TIMEOUT_CMD $TIMEOUT_SIMP $VERIFYPN -n -c -q $TIMEOUT_SIMP -l $TIMEOUT_LP -d $TIMEOUT_RED -z 4 -x $MULTIQUERY_INPUT $MODEL_PATH/model.pnml $CATEGORY )
156+ CNT=0
157+ SOLVED=$(echo "$TMP" | grep "FORMULA" | grep -oP "(?<=-)[0-9]+(?=( TRUE)|( FALSE)|( [0-9]))")
158+
159+ for i in $SOLVED ; do
160+ i=$(echo $i | sed -e "s/0*//")
161+ if [ -z "$i" ] ; then i="0"; fi
162+ echo "Solution found by stripping colors (step -1) for query index " $i
163+ unset QUERIES[$i]
164+ CNT=$(echo "$CNT+1" | bc)
165+ done
166+ NUMBER=$(echo "$NUMBER-$CNT" | bc)
167+
168+ echo "$TMP"
169+
170+ #MULTIQUERY_INPUT=$(sed -e "s/ /,/g" <<< ${QUERIES[@]})
171+ MULTIQUERY_INPUT=$(echo ${QUERIES[@]} | sed -e "s/ /,/g")
172+ time_left
173+
174+ if [ -z "$MULTIQUERY_INPUT" ]; then echo "All queries are solved" ; time_left; rm $QF; rm $MF; exit; fi
175+
176+ # Step 0: Simplification
177+ echo "---------------------------------------------------"
178+ echo " Step 0: Parallel Simplification "
179+ echo "---------------------------------------------------"
180+ echo "Doing parallel simplification ($NUMBER in total)"
181+ echo "Total simplification timout is $TIMEOUT_SIMP -- reduction timeout is $TIMEOUT_RED"
182+
183+
184+ if [[ $BK_EXAMINATION == "LTL"* ]]; then
185+ PAR_SIMP_TIMEOUT=$(echo "$TIMEOUT_SIMP + $TIMEOUT_RED" | bc)
186+ else
187+ PAR_SIMP_TIMEOUT=$SECONDS
188+ fi
189+
190+ echo "$TIMEOUT_CMD $PAR_SIMP_TIMEOUT $VERIFYPN -n $PARALLEL_SIMPLIFICATION_OPTIONS -q $TIMEOUT_SIMP -l $TIMEOUT_LP -d $TIMEOUT_RED -z 4 -s OverApprox --binary-query-io 2 --write-simplified $QF --write-reduced $MF -x $MULTIQUERY_INPUT $MODEL_PATH/model.pnml $CATEGORY"
191+
192+ TMP=$($TIMEOUT_CMD $PAR_SIMP_TIMEOUT $VERIFYPN -n $PARALLEL_SIMPLIFICATION_OPTIONS -q $TIMEOUT_SIMP -l $TIMEOUT_LP -d $TIMEOUT_RED -z 4 -s OverApprox --binary-query-io 2 --write-simplified $QF --write-reduced $MF -x $MULTIQUERY_INPUT $MODEL_PATH/model.pnml $CATEGORY)
193+
194+ echo "$TMP"
195+ TMP=$(echo "$TMP" | grep "FORMULA" | wc -l)
196+ for i in $(seq 1 $TMP); do
197+ echo "Solution found by parallel simplification (step 0)"
198+ done
199+
200+ local NUMBER=`echo "$NUMBER-$TMP" | bc`
201+ QUERIES=( $(seq 1 $NUMBER) )
202+
203+ if [[ $BK_EXAMINATION == "LTL"* && ! -s $QF ]]; then
204+ echo "No simplified files created. Constructing non simplified files."
205+ time_left
206+ echo "$VERIFYPN -n -ltl none -q 0 -d 0 -z 4 --binary-query-io 2 --write-simplified $QF --write-reduced $MF -x $MULTIQUERY_INPUT $MODEL_PATH/model.pnml $CATEGORY"
207+ $TIMEOUT_CMD $SECONDS $VERIFYPN -n -ltl none -q 0 -d 0 -z 4 --binary-query-io 2 --write-simplified $QF --write-reduced $MF -x $MULTIQUERY_INPUT $MODEL_PATH/model.pnml $CATEGORY
208+ fi
209+
210+ time_left
211+ echo
212+ if [ ! -s $MF ]; then
213+ echo "Model file after phase 0 is empty (CPN unfolding failed), exiting ..."
214+ rm $QF
215+ rm $MF
216+ exit
217+ fi
218+
219+ # Step 1: Parallel
220+ echo "---------------------------------------------------"
221+ echo " Step 1: Parallel processing "
222+ echo "---------------------------------------------------"
223+ echo "Doing parallel verification of individual queries ($NUMBER in total)"
224+ echo "Each query is verified by ${#STRATEGIES_PAR[@]} parallel strategies for $TIMEOUT_PAR seconds"
225+
226+ i=0
227+ for Q in ${QUERIES[@]}; do
228+
229+ TIMEOUT_PAR=$(( $TIMEOUT_PAR < $SECONDS ? $TIMEOUT_PAR : $SECONDS))
230+ if [[ "$TIMEOUT_PAR" -le 0 ]] ; then echo "Out of time, terminating!"; time_left; rm $QF; rm $MF; exit; fi
231+ echo "------------------- QUERY ${Q} ----------------------"
232+ # Execute verifypn on all parallel strategies
233+ # All processes are killed if one process provides an answer
234+ step1="$($PAR_CMD --line-buffer --halt now,success=1 --timeout $TIMEOUT_PAR --xapply\
235+ eval $TIME_CMD $VERIFYPN -n $OPTIONS {} $MF $QF --binary-query-io 1 -x $Q -n \
236+ ::: "${STRATEGIES_PAR[@]}" 2>&1)"
237+
238+ if [[ $? == 0 ]]; then
239+ #unset QUERIES[$Q-1]
240+ QUERIES=(${QUERIES[@]:0:$i} ${QUERIES[@]:$(($i + 1))})
241+ i=$(echo "$i - 1" | bc)
242+ echo "Solution found by parallel processing (step 1)"
243+ else
244+ echo "No solution found"
245+ fi
246+ FORMULA_RESULT="$(echo "$step1"|grep -m 1 FORMULA)"
247+
248+ if [[ -n "$FORMULA_RESULT" ]]; then
249+ echo "$step1" | sed "/$FORMULA_RESULT/d"
250+ else
251+ echo "$step1"
252+ fi
253+ echo
254+ echo "$FORMULA_RESULT"
255+ time_left
256+ i=$(echo "$i + 1" | bc)
257+ done
258+
259+ # Exit if all queries are answered
260+ if [[ ${#QUERIES[@]} == 0 ]]; then echo "All queries are solved" ; time_left; rm $QF; rm $MF; exit; fi
261+
262+
263+ # Step 2: Sequential
264+ echo "---------------------------------------------------"
265+ echo " Step 2: Sequential processing "
266+ echo "---------------------------------------------------"
267+ echo "Remaining ${#QUERIES[@]} queries are verified sequentially."
268+ echo "Each query is verified for a dynamic timeout (at least $TIMEOUT_SEQ_MIN seconds)"
269+
270+ time_left
271+ # Count the number of remaining queries to try solving sequentially
272+ REMAINING_SEQ=${#QUERIES[@]}
273+ if [[ "$TIMEOUT_SEQ_MIN" -ne "0" ]] ; then
274+ i=0
275+ for Q in ${QUERIES[@]}; do
276+ echo "------------------- QUERY ${Q} ----------------------"
277+ # Calculate remaining time
278+ TIMEOUT_SEQ=$(echo "$SECONDS / $REMAINING_SEQ" | bc)
279+ if [[ "$TIMEOUT_SEQ_MIN" -gt "$TIMEOUT_SEQ" ]]; then TIMEOUT_SEQ=$TIMEOUT_SEQ_MIN; fi
280+ if [[ "$TIMEOUT_SEQ" -gt "$SECONDS" ]]; then TIMEOUT_SEQ=$SECONDS; break; fi
281+ if [[ "$TIMEOUT_SEQ" -le 0 ]] ; then echo "Out of time, terminating!"; time_left; rm $QF; rm $MF; exit; fi
282+
283+ # Execute verifypn on sequential strategy
284+ echo "Running query $Q for $TIMEOUT_SEQ seconds. Remaining: $REMAINING_SEQ queries and $SECONDS seconds"
285+ step1="$($PAR_CMD --line-buffer --halt now,success=1 --timeout $TIMEOUT_SEQ --xapply\
286+ eval $TIME_CMD $VERIFYPN -n $OPTIONS {} $MF $QF --binary-query-io 1 -x $Q -n \
287+ ::: "${STRATEGIES_SEQ[@]}" 2>&1)"
288+ RETVAL=$?
289+
290+ if [[ $RETVAL == 0 ]]; then
291+ #unset QUERIES[$Q-1]
292+ QUERIES=(${QUERIES[@]:0:$i} ${QUERIES[@]:$(($i + 1))})
293+ i=$(echo "$i - 1" | bc)
294+ echo "Solution found by sequential processing (step 2)"
295+ echo "$step1"
296+ else
297+ echo "No solution found"
298+ fi
299+ FORMULA_RESULT="$(echo "$step1"|grep -m 1 FORMULA)"
300+
301+ if [[ -n "$FORMULA_RESULT" ]]; then
302+ echo "$step1" | sed "/$FORMULA_RESULT/d"
303+ else
304+ echo "$step1"
305+ fi
306+ echo
307+ echo "$FORMULA_RESULT"
308+
309+ time_left
310+ REMAINING_SEQ=$((REMAINING_SEQ - 1))
311+ i=$(echo "$i + 1" | bc)
312+ done
313+ fi
314+
315+ # Exit if all queries are answered
316+ if [[ ${#QUERIES[@]} == 0 ]]; then echo "All queries are solved" ; time_left; rm $QF; rm $MF; exit; fi
317+
318+ if $run_multi; then
319+ # Step 3: Multiquery
320+ time_left
321+ echo "---------------------------------------------------"
322+ echo " Step 3: Multiquery processing "
323+ echo "---------------------------------------------------"
324+ echo "Remaining ${#QUERIES[@]} queries are solved using multiquery"
325+ echo "Time remaining: $SECONDS seconds of the initial $TIMEOUT_TOTAL seconds"
326+
327+ # Join remaining query indexes in comma separated string
328+ #MULTIQUERY_INPUT=$(sed -e "s/ /,/g" <<< ${QUERIES[@]})
329+ MULTIQUERY_INPUT=$(echo ${QUERIES[@]} | sed -e "s/ /,/g")
330+
331+ RED=$(echo "$SECONDS/8" | bc)
332+ RUN_TIME=$(echo "$SECONDS*6/8" | bc)
333+ if [[ "$RUN_TIME" -le 0 ]] ; then echo "Out of time, terminating!"; time_left; rm $QF; rm $MF; exit; fi
334+ echo "Running multiquery on -x $MULTIQUERY_INPUT for $RUN_TIME seconds"
335+ TMP=$($TIME_CMD $TIMEOUT_CMD $RUN_TIME $VERIFYPN -n $STRATEGY_MULTI $OPTIONS -d $RED -q $RED -p $MF $QF --binary-query-io 1 -n -x $MULTIQUERY_INPUT )
336+
337+ echo "$TMP"
338+
339+ SOLVED=$(echo "$TMP" | grep -oP "(?<=Query index )[0-9]+(?= was solved)")
340+ SOLVED=$(echo $SOLVED | tr " " "\n" | sort -g -r)
341+
342+ for rem in $SOLVED ; do
343+ QUERIES=(${QUERIES[@]:0:$rem} ${QUERIES[@]:$(($rem + 1))})
344+ echo "Solution found by multiquery processing (step 3) for query index" $rem
345+ done
346+ fi
347+
348+
349+ for trial in $(seq 0 20); do
350+ time_left
351+ if [[ ${#QUERIES[@]} == 0 ]]; then echo "All queries are solved" ; time_left; rm $QF; rm $MF; exit; fi
352+ # Step 4: Parallel random search
353+ echo "---------------------------------------------------"
354+ echo " Step 4: Random Parallel processing "
355+ echo "---------------------------------------------------"
356+ echo "Doing random parallel verification of individual queries (${#QUERIES[@]} in total)"
357+ RUN_TIME=$(echo "$SECONDS/${#QUERIES[@]}" | bc)
358+ echo "Each query is verified by ${#STRATEGIES_RAND[@]} parallel strategies for $RUN_TIME seconds"
359+
360+ i=0
361+ for Q in ${QUERIES[@]}; do
362+ echo "------------------- QUERY ${Q} ----------------------"
363+ # Execute verifypn on all parallel strategies
364+ # All processes are killed if one process provides an answer
365+ RUN_TIME=$(( $RUN_TIME < $SECONDS ? $RUN_TIME : $SECONDS))
366+ if [[ "$RUN_TIME" -le 0 ]] ; then echo "Out of time, terminating!"; time_left; rm $QF; rm $MF; exit; fi
367+ step1="$($PAR_CMD --line-buffer --halt now,success=1 --timeout $RUN_TIME --xapply\
368+ eval $TIME_CMD $VERIFYPN -n $OPTIONS {} $MF $QF --binary-query-io 1 -x $Q -n \
369+ ::: "${STRATEGIES_RAND[@]}" 2>&1)"
370+
371+ if [[ $? == 0 ]]; then
372+ #unset QUERIES[$Q-1]
373+ QUERIES=(${QUERIES[@]:0:$i} ${QUERIES[@]:$(($i + 1))})
374+ i=$(echo "$i - 1" | bc)
375+ echo "Solution found in random processing (step 4)"
376+ else
377+ echo "No solution found"
378+ fi
379+ FORMULA_RESULT="$(echo "$step1"|grep -m 1 FORMULA)"
380+
381+ if [[ -n "$FORMULA_RESULT" ]]; then
382+ echo "$step1" | sed "/$FORMULA_RESULT/d"
383+ else
384+ echo "$step1"
385+ fi
386+ echo
387+ echo "$FORMULA_RESULT"
388+ time_left
389+ i=$(echo "$i + 1" | bc)
390+ done
391+ # Exit if all queries are answered
392+ done
393+
394+ time_left
395+ echo "End of script."
396+ rm $QF
397+ rm $MF
398+}
399+
400+function LTL {
401+ unset STRATEGIES_PAR
402+ STRATEGIES_PAR[0]="-ltl tarjan --ltl-heur dist -q 0 -l 0 -d $SHORTRED"
403+ STRATEGIES_PAR[1]="-ltl tarjan --ltl-heur 'sum aut firecount(5000)' -q 0 -l 0 -d $SHORTRED"
404+ STRATEGIES_PAR[2]="-ltl tarjan -s DFS -p -q 0 -l 0 -d $SHORTRED"
405+ STRATEGIES_PAR[3]="-ltl ndfs -q 0 -l 0 -d $SHORTRED"
406+ unset STRATEGIES_SEQ
407+ STRATEGIES_SEQ[0]="-ltl tarjan -q 40 -l 5 -d $SHORTRED -s BestFS --ltl-por mix --ltl-heur weight-aut"
408+ unset STRATEGIES_RAND
409+ STRATEGIES_RAND[0]="-ltl tarjan -s RDFS --seed-offset 0 -p -q 0 -l 0 -d $SHORTRED"
410+ STRATEGIES_RAND[1]="-ltl tarjan -s RDFS --seed-offset 1337 -p -q 0 -l 0 -d $SHORTRED"
411+ STRATEGIES_RAND[2]="-ltl tarjan -s RDFS --seed-offset 2018 -p -q 0 -l 0 -d $SHORTRED"
412+ STRATEGIES_RAND[3]="-ltl tarjan -s RDFS --seed-offset 9220 -p -q 0 -l 0 -d $SHORTRED"
413+ PARALLEL_SIMPLIFICATION_OPTIONS="-ltl"
414+ verifyparallel
415+}
416+
417+case "$BK_EXAMINATION" in
418+
419+ StateSpace)
420+ echo
421+ echo "*****************************************"
422+ echo "* TAPAAL performing StateSpace search *"
423+ echo "*****************************************"
424+ $TIME_CMD $TIMEOUT_CMD $TIMEOUT_TOTAL $VERIFYPN -n -p -q 0 -e --disable-partitioning -s BFS $MODEL_PATH/model.pnml
425+ time_left
426+ ;;
427+
428+ UpperBounds)
429+ echo
430+ echo "*****************************************"
431+ echo "* TAPAAL CLASSIC verifying UpperBounds *"
432+ echo "*****************************************"
433+ #STRATEGIES_PAR[0]="-s BestFS -q 0 -l 0 -d $SHORTRED"
434+ #STRATEGIES_PAR[1]="-s BestFS -q 0 -l 0 -d 0"
435+ #STRATEGIES_PAR[0]="-s BFS -q 0 -l 0 -d $SHORTRED"
436+ STRATEGIES_PAR[0]="-n -s BFS -q 0 -l 0 -d $SHORTRED"
437+ STRATEGIES_PAR[1]="-n -p -q 0 -l 0 -d $SHORTRED"
438+ STRATEGIES_PAR[2]="-n -p -s DFS -q 0 -l 0 -d $SHORTRED"
439+ STRATEGIES_PAR[3]="-n -tar -q 0 -l 0 -d 0"
440+ CATEGORY="${MODEL_PATH}/${BK_EXAMINATION}.xml"
441+ TIMEOUT_SEQ_MIN=0
442+ TIMEOUT_PAR=$(echo "$TIMEOUT_TOTAL/26" | bc) # competition 2.3 min
443+ run_multi=true
444+ verifyparallel
445+ ;;
446+
447+ ReachabilityDeadlock)
448+ echo
449+ echo "**********************************************"
450+ echo "* TAPAAL checking for ReachabilityDeadlock *"
451+ echo "**********************************************"
452+ STRATEGIES_PAR[0]="-s RDFS --siphon-trap $SIPHONTRAP"
453+ STRATEGIES_PAR[1]="-s BFS -q 0 -l 0 -d $EXTENDED"
454+ STRATEGIES_PAR[2]="-s DFS -q 0 -l 0 -d $EXTENDED"
455+ STRATEGIES_PAR[3]="-tar -q 0 -l 0 -d $EXTENDED"
456+ TIMEOUT_SEQ_MIN=0
457+ CATEGORY="${DIR}/${BK_EXAMINATION}.xml"
458+ TIMEOUT_PAR=$(echo "$TIMEOUT_TOTAL/6" | bc) # competition 10 min
459+ verifyparallel
460+ ;;
461+
462+ QuasiLiveness)
463+ echo
464+ echo "**********************************************"
465+ echo "* TAPAAL checking for QuasiLiveness *"
466+ echo "**********************************************"
467+ TIMEOUT_SEQ_MIN=0
468+ CATEGORY="${DIR}/${BK_EXAMINATION}.xml"
469+ TIMEOUT_PAR=$(echo "$TIMEOUT_TOTAL/6" | bc) # competition 10 min
470+ verifyparallel
471+ ;;
472+
473+ Liveness)
474+ echo
475+ echo "**********************************************"
476+ echo "* TAPAAL checking for Liveness *"
477+ echo "**********************************************"
478+ TIMEOUT_SEQ_MIN=0
479+ CATEGORY="${DIR}/${BK_EXAMINATION}.xml"
480+ TIMEOUT_PAR=$(echo "$TIMEOUT_TOTAL/6" | bc) # competition 10 min
481+ verifyparallel
482+ ;;
483+
484+ StableMarking)
485+ echo
486+ echo "**********************************************"
487+ echo "* TAPAAL checking for StableMarking *"
488+ echo "**********************************************"
489+ TIMEOUT_SEQ_MIN=0
490+ CATEGORY="${DIR}/${BK_EXAMINATION}.xml"
491+ TIMEOUT_PAR=$(echo "$TIMEOUT_TOTAL/6" | bc) # competition 10 min
492+ verifyparallel
493+ ;;
494+
495+ OneSafe)
496+ echo
497+ echo "**********************************************"
498+ echo "* TAPAAL checking for One-Safe *"
499+ echo "**********************************************"
500+ TIMEOUT_SEQ_MIN=0
501+ CATEGORY="${DIR}/${BK_EXAMINATION}.xml"
502+ TIMEOUT_PAR=$(echo "$TIMEOUT_TOTAL/6" | bc) # competition 10 min
503+ verifyparallel
504+ ;;
505+
506+ ReachabilityCardinality)
507+ echo
508+ echo "**********************************************"
509+ echo "* TAPAAL verifying ReachabilityCardinality *"
510+ echo "**********************************************"
511+ CATEGORY="${MODEL_PATH}/${BK_EXAMINATION}.xml"
512+ verifyparallel
513+ ;;
514+
515+ ReachabilityFireability)
516+ echo
517+ echo "**********************************************"
518+ echo "* TAPAAL verifying ReachabilityFireability *"
519+ echo "**********************************************"
520+ CATEGORY="${MODEL_PATH}/${BK_EXAMINATION}.xml"
521+ TIMEOUT_SEQ_MIN=$(echo "$TIMEOUT_TOTAL/6" | bc) # competition 10 min
522+ verifyparallel
523+ ;;
524+
525+ CTLCardinality)
526+ echo
527+ echo "*************************************"
528+ echo "* TAPAAL verifying CTLCardinality *"
529+ echo "*************************************"
530+ CATEGORY="${MODEL_PATH}/${BK_EXAMINATION}.xml"
531+ TIMEOUT_PAR=$(echo "$TIMEOUT_TOTAL/12" | bc) # competition 5 min
532+ verifyparallel
533+ ;;
534+
535+ CTLFireability)
536+ echo
537+ echo "*************************************"
538+ echo "* TAPAAL verifying CTLFireability *"
539+ echo "*************************************"
540+ CATEGORY="${MODEL_PATH}/${BK_EXAMINATION}.xml"
541+ TIMEOUT_PAR=$(echo "$TIMEOUT_TOTAL/12" | bc) # competition 5 min
542+ TIMEOUT_SEQ_MIN=$(echo "$TIMEOUT_TOTAL/6" | bc) # competition 10 min
543+ verifyparallel
544+ ;;
545+
546+ LTLCardinality)
547+ echo
548+ echo "*************************************"
549+ echo "* TAPAAL verifying LTLCardinality *"
550+ echo "*************************************"
551+ CATEGORY="${MODEL_PATH}/${BK_EXAMINATION}.xml"
552+ TIMEOUT_PAR=$(echo "$TIMEOUT_TOTAL/12" | bc) # competition 5 min
553+ LTL
554+ ;;
555+
556+ LTLFireability)
557+ echo
558+ echo "*************************************"
559+ echo "* TAPAAL verifying LTLFireability *"
560+ echo "*************************************"
561+ CATEGORY="${MODEL_PATH}/${BK_EXAMINATION}.xml"
562+ TIMEOUT_PAR=$(echo "$TIMEOUT_TOTAL/12" | bc) # competition 5 min
563+ TIMEOUT_SEQ_MIN=$(echo "$TIMEOUT_TOTAL/6" | bc) # competition 10 min
564+ LTL
565+ ;;
566+ *)
567+ echo "DO_NOT_COMPETE"
568+ exit 0
569+ ;;
570+esac
571
572=== modified file 'Scripts/MCC21/competition-scripts/tapaal.sh'
573--- Scripts/MCC21/competition-scripts/tapaal.sh 2021-04-28 11:15:57 +0000
574+++ Scripts/MCC21/competition-scripts/tapaal.sh 2021-08-05 10:56:53 +0000
575@@ -367,6 +367,23 @@
576 rm $MF
577 }
578
579+function LTL {
580+ unset STRATEGIES_PAR
581+ STRATEGIES_PAR[0]="-ltl tarjan --ltl-heur dist -q 0 -l 0 -d $SHORTRED"
582+ STRATEGIES_PAR[1]="-ltl tarjan --ltl-heur sum-composed-weight --log-fire-count-threshold 5000 -q 0 -l 0 -d $SHORTRED"
583+ STRATEGIES_PAR[2]="-ltl tarjan -s DFS -p -q 0 -l 0 -d $SHORTRED"
584+ STRATEGIES_PAR[3]="-ltl ndfs -q 0 -l 0 -d $SHORTRED"
585+ unset STRATEGIES_SEQ
586+ STRATEGIES_SEQ[0]="-ltl tarjan -q 40 -l 5 -d $SHORTRED -s BestFS --ltl-por mix --ltl-heur weight-aut"
587+ unset STRATEGIES_RAND
588+ STRATEGIES_RAND[0]="-ltl tarjan -s RDFS --seed-offset 0 -q 0 -l 0 -d $SHORTRED"
589+ STRATEGIES_RAND[1]="-ltl tarjan -s RDFS --seed-offset 1337 -q 0 -l 0 -d $SHORTRED"
590+ STRATEGIES_RAND[2]="-ltl tarjan -s RDFS --seed-offset 2018 -q 0 -l 0 -d $SHORTRED"
591+ STRATEGIES_RAND[3]="-ltl tarjan -s RDFS --seed-offset 9220 -q 0 -l 0 -d $SHORTRED"
592+ PARALLEL_SIMPLIFICATION_OPTIONS="-ltl"
593+ verifyparallel
594+}
595+
596 case "$BK_EXAMINATION" in
597
598 StateSpace)
599@@ -503,21 +520,8 @@
600 echo "*************************************"
601 CATEGORY="${MODEL_PATH}/${BK_EXAMINATION}.xml"
602 TIMEOUT_PAR=$(echo "$TIMEOUT_TOTAL/12" | bc) # competition 5 min
603- unset STRATEGIES_PAR
604- STRATEGIES_PAR[0]="-ltl tarjan -s DFS -q 0 -l 0 -d $SHORTRED"
605- STRATEGIES_PAR[1]="-ltl tarjan -q 0 -l 0 -d $SHORTRED"
606- STRATEGIES_PAR[2]="-ltl tarjan -s RDFS -q 0 -l 0 -d $SHORTRED"
607- STRATEGIES_PAR[3]="-ltl ndfs -q 0 -l 0 -d $SHORTRED"
608- unset STRATEGIES_SEQ
609- STRATEGIES_SEQ[0]="-ltl tarjan -q 40 -l 5 -d $SHORTRED"
610- unset STRATEGIES_RAND
611- STRATEGIES_RAND[0]="-ltl tarjan -s RDFS --seed-offset 0 -q 0 -l 0 -d $SHORTRED"
612- STRATEGIES_RAND[1]="-ltl tarjan -s RDFS --seed-offset 1337 -q 0 -l 0 -d $SHORTRED"
613- STRATEGIES_RAND[2]="-ltl tarjan -s RDFS --seed-offset 2018 -q 0 -l 0 -d $SHORTRED"
614- STRATEGIES_RAND[3]="-ltl tarjan -s RDFS --seed-offset 9220 -q 0 -l 0 -d $SHORTRED"
615- PARALLEL_SIMPLIFICATION_OPTIONS="-ltl"
616- verifyparallel
617- ;;
618+ LTL
619+ ;;
620
621 LTLFireability)
622 echo
623@@ -527,20 +531,7 @@
624 CATEGORY="${MODEL_PATH}/${BK_EXAMINATION}.xml"
625 TIMEOUT_PAR=$(echo "$TIMEOUT_TOTAL/12" | bc) # competition 5 min
626 TIMEOUT_SEQ_MIN=$(echo "$TIMEOUT_TOTAL/6" | bc) # competition 10 min
627- unset STRATEGIES_PAR
628- STRATEGIES_PAR[0]="-ltl tarjan -s DFS -q 0 -l 0 -d $SHORTRED"
629- STRATEGIES_PAR[1]="-ltl tarjan -q 0 -l 0 -d $SHORTRED"
630- STRATEGIES_PAR[2]="-ltl tarjan -s RDFS -q 0 -l 0 -d $SHORTRED"
631- STRATEGIES_PAR[3]="-ltl ndfs -q 0 -l 0 -d $SHORTRED"
632- unset STRATEGIES_SEQ
633- STRATEGIES_SEQ[0]="-ltl tarjan -q 40 -l 5 -d $SHORTRED"
634- unset STRATEGIES_RAND
635- STRATEGIES_RAND[0]="-ltl tarjan -s RDFS --seed-offset 0 -q 0 -l 0 -d $SHORTRED"
636- STRATEGIES_RAND[1]="-ltl tarjan -s RDFS --seed-offset 1337 -q 0 -l 0 -d $SHORTRED"
637- STRATEGIES_RAND[2]="-ltl tarjan -s RDFS --seed-offset 2018 -q 0 -l 0 -d $SHORTRED"
638- STRATEGIES_RAND[3]="-ltl tarjan -s RDFS --seed-offset 9220 -q 0 -l 0 -d $SHORTRED"
639- PARALLEL_SIMPLIFICATION_OPTIONS="-ltl"
640- verifyparallel
641+ LTL
642 ;;
643 *)
644 echo "DO_NOT_COMPETE"
645
646=== modified file 'include/LTL/Algorithm/ModelChecker.h'
647--- include/LTL/Algorithm/ModelChecker.h 2021-04-02 09:54:38 +0000
648+++ include/LTL/Algorithm/ModelChecker.h 2021-08-05 10:56:53 +0000
649@@ -20,28 +20,34 @@
650
651 #include "PetriEngine/PQL/PQL.h"
652 #include "LTL/SuccessorGeneration/ProductSuccessorGenerator.h"
653+#include "LTL/SuccessorGeneration/ReachStubProductSuccessorGenerator.h"
654 #include "LTL/SuccessorGeneration/ResumingSuccessorGenerator.h"
655 #include "LTL/SuccessorGeneration/SpoolingSuccessorGenerator.h"
656 #include "LTL/Structures/BitProductStateSet.h"
657+#include "LTL/SuccessorGeneration/ReachStubProductSuccessorGenerator.h"
658 #include "PetriEngine/options.h"
659+
660 #include <iomanip>
661 #include <algorithm>
662
663 namespace LTL {
664- template<typename SuccessorGen>
665+ template<template <typename, typename...> typename ProductSucGen, typename SuccessorGen, typename... Spooler>
666 class ModelChecker {
667 public:
668 ModelChecker(const PetriEngine::PetriNet *net,
669 const PetriEngine::PQL::Condition_ptr &condition,
670 const Structures::BuchiAutomaton &buchi,
671- SuccessorGen &&successorGen,
672- const TraceLevel level = TraceLevel::Transitions,
673- bool shortcircuitweak = true)
674- : net(net), formula(condition), traceLevel(level), shortcircuitweak(shortcircuitweak)
675+ SuccessorGen *successorGen,
676+ std::unique_ptr<Spooler> &&...spooler)
677+ : net(net), formula(condition)
678 {
679- successorGenerator = std::make_unique<ProductSuccessorGenerator<SuccessorGen>>(net, buchi,
680- std::move(successorGen));
681- if (level != TraceLevel::None) {
682+ successorGenerator = std::make_unique<ProductSucGen<SuccessorGen, Spooler...>>(net, buchi, successorGen, std::move(spooler)...);
683+ }
684+
685+ void setOptions(const options_t &options) {
686+ traceLevel = options.trace;
687+ shortcircuitweak = options.ltluseweak;
688+ if (traceLevel != TraceLevel::None) {
689 maxTransName = 0;
690 for (const auto &transname : net->transitionNames()) {
691 maxTransName = std::max(transname.size(), maxTransName);
692@@ -75,14 +81,14 @@
693 << "\tmax tokens: " << stateSet.max_tokens() << std::endl;
694 }
695
696- std::unique_ptr<ProductSuccessorGenerator<SuccessorGen>> successorGenerator;
697+ std::unique_ptr<ProductSucGen<SuccessorGen, Spooler...>> successorGenerator;
698
699 const PetriEngine::PetriNet *net;
700 PetriEngine::PQL::Condition_ptr formula;
701 TraceLevel traceLevel;
702
703 size_t _discovered = 0;
704- const bool shortcircuitweak;
705+ bool shortcircuitweak;
706 bool weakskip = false;
707 bool is_weak = false;
708 size_t maxTransName;
709@@ -99,37 +105,29 @@
710 printTransition(size_t transition, LTL::Structures::ProductState &state, std::ostream &os)
711 {
712 if (transition >= std::numeric_limits<ptrie::uint>::max() - 1) {
713- os << indent << "<deadlock buchi=\"" << state.getBuchiState() << "\"/>";
714+ os << indent << "<deadlock/>";
715 return os;
716 }
717- std::string tname = net->transitionNames()[transition];
718+ os << indent << "<transition id="
719+ // field width stuff obsolete without büchi state printing.
720+ //<< std::setw(maxTransName + 2) << std::left
721+ << std::quoted(net->transitionNames()[transition]);
722 if (traceLevel == TraceLevel::Full) {
723- os << indent << "<transition id=\"" << tname << "\">\n";
724+ os << ">";
725+ os << std::endl;
726 for (size_t i = 0; i < net->numberOfPlaces(); ++i) {
727 for (size_t j = 0; j < state.marking()[i]; ++j) {
728 os << tokenIndent << R"(<token age="0" place=")" << net->placeNames()[i] << "\"/>\n";
729 }
730 }
731-#ifndef NDEBUG
732- os << '\n' << tokenIndent << "<buchi state=\"" << state.getBuchiState() << "\"/>\n";
733-#endif
734 os << indent << "</transition>";
735- } else {
736- os << indent << "<transition id="
737- << std::setw(maxTransName + 1 + 2) << std::left
738- << std::quoted(tname) << " " << "buchisucc=\""
739- << state.getBuchiState() << "\"/>";
740+ }
741+ else {
742+ os << "/>";
743 }
744 return os;
745 }
746-
747 };
748-
749- extern template
750- class ModelChecker<LTL::ResumingSuccessorGenerator>;
751-
752- extern template
753- class ModelChecker<LTL::SpoolingSuccessorGenerator>;
754 }
755
756 #endif //VERIFYPN_MODELCHECKER_H
757
758=== modified file 'include/LTL/Algorithm/NestedDepthFirstSearch.h'
759--- include/LTL/Algorithm/NestedDepthFirstSearch.h 2021-04-02 09:54:38 +0000
760+++ include/LTL/Algorithm/NestedDepthFirstSearch.h 2021-08-05 10:56:53 +0000
761@@ -45,14 +45,13 @@
762 * @tparam W type used for state storage. Use <code>PetriEngine::Structures::TracableStateSet</code> if you want traces,
763 * <code>PetriEngine::Structures::StateSet</code> if you don't care (as it is faster).
764 */
765- template<typename W>
766-class NestedDepthFirstSearch : public ModelChecker<LTL::ResumingSuccessorGenerator> {
767+ template<typename SucGen, typename W>
768+class NestedDepthFirstSearch : public ModelChecker<ProductSuccessorGenerator, SucGen> {
769 public:
770 NestedDepthFirstSearch(const PetriEngine::PetriNet *net, const PetriEngine::PQL::Condition_ptr &query,
771- const Structures::BuchiAutomaton &buchi,
772- TraceLevel level = TraceLevel::Full, const bool shortcircuitweak = true)
773- : ModelChecker(net, query, buchi, LTL::ResumingSuccessorGenerator{*net, query}, level, shortcircuitweak),
774- factory{net, successorGenerator->initial_buchi_state()},
775+ const Structures::BuchiAutomaton &buchi, SucGen *gen)
776+ : ModelChecker<ProductSuccessorGenerator, SucGen>(net, query, buchi, gen),
777+ factory{net, this->successorGenerator->initial_buchi_state()},
778 states(*net, 0, (int) net->numberOfPlaces() + 1) {}
779
780 bool isSatisfied() override;
781@@ -69,7 +68,7 @@
782
783 struct StackEntry {
784 size_t id;
785- successor_info sucinfo;
786+ typename SucGen::sucinfo sucinfo;
787 };
788
789 State *seed;
790@@ -88,10 +87,16 @@
791 };
792
793 extern template
794- class NestedDepthFirstSearch<PetriEngine::Structures::StateSet>;
795-
796- extern template
797- class NestedDepthFirstSearch<PetriEngine::Structures::TracableStateSet>;
798+ class NestedDepthFirstSearch<LTL::ResumingSuccessorGenerator, PetriEngine::Structures::StateSet>;
799+
800+ extern template
801+ class NestedDepthFirstSearch<LTL::ResumingSuccessorGenerator, PetriEngine::Structures::TracableStateSet>;
802+
803+ extern template
804+ class NestedDepthFirstSearch<LTL::SpoolingSuccessorGenerator, PetriEngine::Structures::StateSet>;
805+
806+ extern template
807+ class NestedDepthFirstSearch<LTL::SpoolingSuccessorGenerator, PetriEngine::Structures::TracableStateSet>;
808 }
809
810 #endif //VERIFYPN_NESTEDDEPTHFIRSTSEARCH_H
811
812=== removed file 'include/LTL/Algorithm/RandomNDFS.h'
813=== modified file 'include/LTL/Algorithm/TarjanModelChecker.h'
814--- include/LTL/Algorithm/TarjanModelChecker.h 2021-04-06 13:25:35 +0000
815+++ include/LTL/Algorithm/TarjanModelChecker.h 2021-08-05 10:56:53 +0000
816@@ -25,6 +25,7 @@
817 #include "LTL/SuccessorGeneration/ResumingSuccessorGenerator.h"
818 #include "LTL/SuccessorGeneration/SpoolingSuccessorGenerator.h"
819
820+#include <limits>
821 #include <stack>
822 #include <unordered_set>
823
824@@ -36,21 +37,20 @@
825 * which constitutes a counter-example.
826 * For more details see
827 * <p>
828- * Jaco Geldenhuys & Antti Valmari,
829- * More efficient on-the-fly LTL verification with Tarjan's algorithm,
830+ * Jaco Geldenhuys & Antti Valmari
831+ * More efficient on-the-fly LTL verification with Tarjan's algorithm
832 * https://doi.org/10.1016/j.tcs.2005.07.004
833 * </p>
834 * @tparam SaveTrace whether to save and print counter-examples when possible.
835 */
836- template<typename SuccessorGen, bool SaveTrace = false>
837- class TarjanModelChecker : public ModelChecker<SuccessorGen> {
838+ template<template <typename, typename...> typename ProductSucGen, typename SuccessorGen, bool SaveTrace = false, typename... Spooler>
839+ class TarjanModelChecker : public ModelChecker<ProductSucGen, SuccessorGen, Spooler...> {
840 public:
841 TarjanModelChecker(const PetriEngine::PetriNet *net, const PetriEngine::PQL::Condition_ptr &cond,
842 const Structures::BuchiAutomaton &buchi,
843- SuccessorGen &&successorGen,
844- const TraceLevel level = TraceLevel::Full,
845- const bool shortcircuitweak = true)
846- : ModelChecker<SuccessorGen>(net, cond, buchi, std::move(successorGen), level, shortcircuitweak),
847+ SuccessorGen *successorGen,
848+ std::unique_ptr<Spooler> &&...spooler)
849+ : ModelChecker<ProductSucGen, SuccessorGen, Spooler...>(net, cond, buchi, successorGen, std::move(spooler)...),
850 factory(net, this->successorGenerator->initial_buchi_state()),
851 seen(net, 0)
852 {
853@@ -96,6 +96,7 @@
854 idx_t lowlink;
855 idx_t stateid;
856 idx_t next = std::numeric_limits<idx_t>::max();
857+ bool dstack = true;
858
859 PlainCEntry(idx_t lowlink, idx_t stateid, idx_t next) : lowlink(lowlink), stateid(stateid), next(next) {}
860 };
861@@ -113,6 +114,7 @@
862
863 struct DEntry {
864 idx_t pos; // position in cstack.
865+
866 typename SuccessorGen::sucinfo sucinfo;
867
868 explicit DEntry(idx_t pos) : pos(pos), sucinfo(SuccessorGen::initial_suc_info()) {}
869@@ -143,18 +145,29 @@
870 void printTrace(std::stack<DEntry> &&dstack, std::ostream &os = std::cout);
871
872 };
873-
874- extern template
875- class TarjanModelChecker<LTL::ResumingSuccessorGenerator, true>;
876-
877- extern template
878- class TarjanModelChecker<LTL::ResumingSuccessorGenerator, false>;
879-
880- extern template
881- class TarjanModelChecker<LTL::SpoolingSuccessorGenerator, true>;
882-
883- extern template
884- class TarjanModelChecker<LTL::SpoolingSuccessorGenerator, false>;
885+ extern template
886+ class TarjanModelChecker<ProductSuccessorGenerator, LTL::ResumingSuccessorGenerator, true>;
887+
888+ extern template
889+ class TarjanModelChecker<ProductSuccessorGenerator, LTL::ResumingSuccessorGenerator, false>;
890+
891+ extern template
892+ class TarjanModelChecker<ProductSuccessorGenerator, LTL::SpoolingSuccessorGenerator, true>;
893+
894+ extern template
895+ class TarjanModelChecker<ProductSuccessorGenerator, LTL::SpoolingSuccessorGenerator, false>;
896+
897+ extern template
898+ class TarjanModelChecker<ReachStubProductSuccessorGenerator, LTL::SpoolingSuccessorGenerator, true, VisibleLTLStubbornSet>;
899+
900+ extern template
901+ class TarjanModelChecker<ReachStubProductSuccessorGenerator, LTL::SpoolingSuccessorGenerator, false, VisibleLTLStubbornSet>;
902+
903+ extern template
904+ class TarjanModelChecker<ReachStubProductSuccessorGenerator, LTL::SpoolingSuccessorGenerator, true, EnabledSpooler>;
905+
906+ extern template
907+ class TarjanModelChecker<ReachStubProductSuccessorGenerator, LTL::SpoolingSuccessorGenerator, false, EnabledSpooler>;
908 }
909
910 #endif //VERIFYPN_TARJANMODELCHECKER_H
911
912=== modified file 'include/LTL/LTL.h'
913--- include/LTL/LTL.h 2021-03-23 14:10:04 +0000
914+++ include/LTL/LTL.h 2021-08-05 10:56:53 +0000
915@@ -21,7 +21,6 @@
916 #include "LTL/LTLValidator.h"
917 #include "LTL/Algorithm/TarjanModelChecker.h"
918 #include "LTL/Algorithm/NestedDepthFirstSearch.h"
919-#include "LTL/Algorithm/RandomNDFS.h"
920 #include "LTL/Simplification/SpotToPQL.h"
921 #include "LTL/LTLToBuchi.h"
922
923
924=== modified file 'include/LTL/LTLMain.h'
925--- include/LTL/LTLMain.h 2021-04-07 08:50:10 +0000
926+++ include/LTL/LTLMain.h 2021-08-05 10:56:53 +0000
927@@ -23,7 +23,7 @@
928 #include "PetriEngine/options.h"
929
930 namespace LTL {
931- ReturnValue LTLMain(const PetriEngine::PetriNet *net,
932+ bool LTLMain(const PetriEngine::PetriNet *net,
933 const PetriEngine::PQL::Condition_ptr &query,
934 const std::string &queryName,
935 options_t &options);
936
937=== modified file 'include/LTL/LTLToBuchi.h'
938--- include/LTL/LTLToBuchi.h 2021-04-06 13:10:00 +0000
939+++ include/LTL/LTLToBuchi.h 2021-08-05 10:56:53 +0000
940@@ -26,7 +26,6 @@
941 #include <string>
942
943 #include <spot/tl/formula.hh>
944-
945 namespace LTL {
946 struct AtomicProposition {
947 PetriEngine::PQL::Condition_ptr expression;
948@@ -40,7 +39,7 @@
949 void toSpotFormat(const QueryItem &query, std::ostream &os);
950
951 std::pair<spot::formula, APInfo>
952- to_spot_formula(const PetriEngine::PQL::Condition_ptr &query, APCompression compress_aps);
953+ to_spot_formula(const PetriEngine::PQL::Condition_ptr &query, const options_t &options);
954
955 class BuchiSuccessorGenerator;
956 namespace Structures {
957@@ -49,10 +48,10 @@
958
959 Structures::BuchiAutomaton makeBuchiAutomaton(
960 const PetriEngine::PQL::Condition_ptr &query,
961- APCompression compress_aps = APCompression::Choose);
962+ const options_t &options);
963
964 BuchiSuccessorGenerator
965- makeBuchiSuccessorGenerator(const PetriEngine::PQL::Condition_ptr &query, APCompression compress_aps = APCompression::Choose);
966+ makeBuchiSuccessorGenerator(const PetriEngine::PQL::Condition_ptr &query, const options_t &options);
967
968 class FormulaToSpotSyntax : public PetriEngine::PQL::QueryPrinter {
969 protected:
970
971=== added file 'include/LTL/Replay.h'
972--- include/LTL/Replay.h 1970-01-01 00:00:00 +0000
973+++ include/LTL/Replay.h 2021-08-05 10:56:53 +0000
974@@ -0,0 +1,66 @@
975+/* Copyright (C) 2021 Nikolaj J. Ulrik <nikolaj@njulrik.dk>,
976+ * Simon M. Virenfeldt <simon@simwir.dk>
977+ *
978+ * This program is free software: you can redistribute it and/or modify
979+ * it under the terms of the GNU General Public License as published by
980+ * the Free Software Foundation, either version 3 of the License, or
981+ * (at your option) any later version.
982+ *
983+ * This program is distributed in the hope that it will be useful,
984+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
985+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
986+ * GNU General Public License for more details.
987+ *
988+ * You should have received a copy of the GNU General Public License
989+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
990+ */
991+
992+#ifndef VERIFYPN_REPLAY_H
993+#define VERIFYPN_REPLAY_H
994+
995+#include "LTL/SuccessorGeneration/BuchiSuccessorGenerator.h"
996+
997+#include <iostream>
998+#include <utility>
999+#include <rapidxml.hpp>
1000+
1001+namespace LTL {
1002+ class Replay {
1003+ public:
1004+ Replay(std::istream &is, const PetriEngine::PetriNet *net);
1005+
1006+ struct Token {
1007+ std::string place;
1008+ };
1009+
1010+ struct Transition {
1011+ explicit Transition(std::string id, int buchi) : id(std::move(id)), buchi_state(buchi) {}
1012+
1013+ std::string id;
1014+ int buchi_state;
1015+ std::unordered_map<uint32_t, uint32_t> tokens;
1016+ };
1017+
1018+ void parse(std::istream &xml, const PetriEngine::PetriNet *net);
1019+
1020+ bool replay(const PetriEngine::PetriNet *net, const PetriEngine::PQL::Condition_ptr &cond, const options_t &options);
1021+
1022+ std::vector<Transition> trace;
1023+ private:
1024+
1025+ static constexpr auto DEADLOCK_TRANS = "##deadlock";
1026+ void parseRoot(const rapidxml::xml_node<> *pNode);
1027+
1028+ Transition parseTransition(const rapidxml::xml_node<char> *pNode);
1029+
1030+ void parseToken(const rapidxml::xml_node<char> *pNode, std::unordered_map<uint32_t, uint32_t> &current_marking);
1031+
1032+ size_t loop_idx = std::numeric_limits<size_t>::max();
1033+ std::unordered_map<std::string, int> transitions;
1034+ std::unordered_map<std::string, int> places;
1035+ bool _play_trace(const PetriEngine::PetriNet *net, PetriEngine::SuccessorGenerator &successorGenerator,
1036+ BuchiSuccessorGenerator &buchiGenerator, size_t init_buchi);
1037+ };
1038+}
1039+
1040+#endif //VERIFYPN_REPLAY_H
1041
1042=== added file 'include/LTL/Simplification/SpotToPQL.h'
1043--- include/LTL/Simplification/SpotToPQL.h 1970-01-01 00:00:00 +0000
1044+++ include/LTL/Simplification/SpotToPQL.h 2021-08-05 10:56:53 +0000
1045@@ -36,7 +36,7 @@
1046 * @param formula The formula to simplify.
1047 * @return the simplified formula.
1048 */
1049- PetriEngine::PQL::Condition_ptr simplify(const PetriEngine::PQL::Condition_ptr &formula, APCompression compress = APCompression::Choose);
1050+ PetriEngine::PQL::Condition_ptr simplify(const PetriEngine::PQL::Condition_ptr &formula, const options_t &options);
1051 }
1052
1053 #endif // VERIFYPN_SPOTTOPQL_H
1054
1055=== removed file 'include/LTL/Simplification/SpotToPQL.h'
1056--- include/LTL/Simplification/SpotToPQL.h 2021-04-06 13:10:00 +0000
1057+++ include/LTL/Simplification/SpotToPQL.h 1970-01-01 00:00:00 +0000
1058@@ -36,7 +36,7 @@
1059 * @param formula The formula to simplify.
1060 * @return the simplified formula.
1061 */
1062- PetriEngine::PQL::Condition_ptr simplify(const PetriEngine::PQL::Condition_ptr &formula, APCompression compress = APCompression::Choose);
1063+ PetriEngine::PQL::Condition_ptr simplify(const PetriEngine::PQL::Condition_ptr &formula, const options_t &options);
1064 }
1065
1066 #endif // VERIFYPN_SPOTTOPQL_H
1067
1068=== modified file 'include/LTL/Structures/BitProductStateSet.h'
1069--- include/LTL/Structures/BitProductStateSet.h 2021-04-16 20:55:16 +0000
1070+++ include/LTL/Structures/BitProductStateSet.h 2021-08-05 10:56:53 +0000
1071@@ -22,6 +22,7 @@
1072 #include "LTL/Structures/ProductState.h"
1073 #include <cstdint>
1074 #include <unordered_set>
1075+#include <unordered_map>
1076
1077 namespace LTL::Structures {
1078
1079@@ -63,7 +64,7 @@
1080 template<uint8_t nbits = 16>
1081 class BitProductStateSet : public ProductStateSetInterface {
1082 public:
1083- explicit BitProductStateSet(const PetriEngine::PetriNet *net, int kbound = 0)
1084+ explicit BitProductStateSet(const PetriEngine::PetriNet *net, int kbound = 0, size_t nplaces = -1)
1085 : markings(*net, kbound, net->numberOfPlaces())
1086 {
1087 }
1088
1089=== modified file 'include/LTL/Structures/BuchiAutomaton.h'
1090--- include/LTL/Structures/BuchiAutomaton.h 2021-03-25 10:36:36 +0000
1091+++ include/LTL/Structures/BuchiAutomaton.h 2021-08-05 10:56:53 +0000
1092@@ -54,6 +54,35 @@
1093 break;
1094 }
1095 }
1096+
1097+ /**
1098+ * Evaluate binary decision diagram (BDD) representation of transition guard in given state.
1099+ */
1100+ bool guard_valid(PetriEngine::PQL::EvaluationContext &ctx, bdd bdd) const
1101+ {
1102+ // IDs 0 and 1 are false and true atoms, respectively
1103+ // More details in buddy manual ( http://buddy.sourceforge.net/manual/main.html )
1104+ while (bdd.id() > 1) {
1105+ // find variable to test, and test it
1106+ size_t var = bdd_var(bdd);
1107+ using PetriEngine::PQL::Condition;
1108+ Condition::Result res = ap_info.at(var).expression->evaluate(ctx);
1109+ switch (res) {
1110+ case Condition::RUNKNOWN:
1111+ std::cerr << "Unexpected unknown answer from evaluating query!\n";
1112+ assert(false);
1113+ exit(1);
1114+ break;
1115+ case Condition::RFALSE:
1116+ bdd = bdd_low(bdd);
1117+ break;
1118+ case Condition::RTRUE:
1119+ bdd = bdd_high(bdd);
1120+ break;
1121+ }
1122+ }
1123+ return bdd == bddtrue;
1124+ }
1125 };
1126 }
1127
1128
1129=== modified file 'include/LTL/Structures/GuardInfo.h'
1130--- include/LTL/Structures/GuardInfo.h 2021-02-24 10:25:55 +0000
1131+++ include/LTL/Structures/GuardInfo.h 2021-08-05 10:56:53 +0000
1132@@ -19,16 +19,57 @@
1133 #define VERIFYPN_GUARDINFO_H
1134
1135 #include "PetriEngine/PQL/PQL.h"
1136+#include "LTL/Structures/BuchiAutomaton.h"
1137+#include "LTL/Simplification/SpotToPQL.h"
1138+
1139 #include <vector>
1140+#include <spot/twa/twagraph.hh>
1141+#include <spot/twa/formula2bdd.hh>
1142+
1143+
1144
1145 namespace LTL {
1146 struct GuardInfo {
1147+
1148+ struct Guard {
1149+ PetriEngine::PQL::Condition_ptr condition;
1150+ bdd decision_diagram;
1151+ uint32_t dest;
1152+
1153+ explicit operator bool () {
1154+ return bool(condition);
1155+ }
1156+ };
1157+
1158 GuardInfo(size_t buchiState, bool isAccepting) : buchi_state(buchiState), is_accepting(isAccepting) {}
1159
1160 int buchi_state;
1161- PetriEngine::PQL::Condition_ptr retarding;
1162- std::vector<PetriEngine::PQL::Condition_ptr> progressing;
1163+ Guard retarding;
1164+ std::vector<Guard> progressing;
1165 bool is_accepting;
1166+
1167+
1168+ static std::vector<GuardInfo> from_automaton(const Structures::BuchiAutomaton &aut) {
1169+ std::vector<GuardInfo> state_guards;
1170+ std::vector<AtomicProposition> aps(aut.ap_info.size());
1171+ std::transform(std::begin(aut.ap_info), std::end(aut.ap_info), std::begin(aps),
1172+ [](const std::pair<int, AtomicProposition> &pair) { return pair.second; });
1173+ for (unsigned state = 0; state < aut._buchi->num_states(); ++state) {
1174+ state_guards.emplace_back(state, aut._buchi->state_is_accepting(state));
1175+ for (auto &e : aut._buchi->out(state)) {
1176+ auto formula = spot::bdd_to_formula(e.cond, aut.dict);
1177+ if (e.dst == state) {
1178+ state_guards.back().retarding = Guard{toPQL(formula, aps), e.cond, state};
1179+ } else {
1180+ state_guards.back().progressing.push_back(Guard{toPQL(formula, aps), e.cond, e.dst});
1181+ }
1182+ }
1183+ if (!state_guards.back().retarding) {
1184+ state_guards.back().retarding = Guard{std::make_shared<PetriEngine::PQL::BooleanCondition>(false), bddfalse, state};
1185+ }
1186+ }
1187+ return state_guards;
1188+ }
1189 };
1190 }
1191
1192
1193=== modified file 'include/LTL/Structures/ProductState.h'
1194--- include/LTL/Structures/ProductState.h 2021-03-23 14:10:04 +0000
1195+++ include/LTL/Structures/ProductState.h 2021-08-05 10:56:53 +0000
1196@@ -41,7 +41,6 @@
1197 buchi_state_idx = nplaces;
1198 }
1199
1200- //TODO override equality operators to handle both marking and NBA state
1201 uint32_t getBuchiState() const {
1202 return marking()[buchi_state_idx];
1203 }
1204
1205=== added directory 'include/LTL/Stubborn'
1206=== added file 'include/LTL/Stubborn/AutomatonStubbornSet.h'
1207--- include/LTL/Stubborn/AutomatonStubbornSet.h 1970-01-01 00:00:00 +0000
1208+++ include/LTL/Stubborn/AutomatonStubbornSet.h 2021-08-05 10:56:53 +0000
1209@@ -0,0 +1,99 @@
1210+/* Copyright (C) 2021 Nikolaj J. Ulrik <nikolaj@njulrik.dk>,
1211+ * Simon M. Virenfeldt <simon@simwir.dk>
1212+ *
1213+ * This program is free software: you can redistribute it and/or modify
1214+ * it under the terms of the GNU General Public License as published by
1215+ * the Free Software Foundation, either version 3 of the License, or
1216+ * (at your option) any later version.
1217+ *
1218+ * This program is distributed in the hope that it will be useful,
1219+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1220+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1221+ * GNU General Public License for more details.
1222+ *
1223+ * You should have received a copy of the GNU General Public License
1224+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1225+ */
1226+
1227+#ifndef VERIFYPN_AUTOMATONSTUBBORNSET_H
1228+#define VERIFYPN_AUTOMATONSTUBBORNSET_H
1229+
1230+#include "LTL/Structures/BuchiAutomaton.h"
1231+#include "PetriEngine/Stubborn/ReachabilityStubbornSet.h"
1232+#include "PetriEngine/PQL/PQL.h"
1233+#include "LTL/Structures/GuardInfo.h"
1234+#include "LTL/SuccessorGeneration/SuccessorSpooler.h"
1235+#include "PetriEngine/SuccessorGenerator.h"
1236+
1237+namespace LTL {
1238+ class NondeterministicConjunctionVisitor;
1239+ class AutomatonStubbornSet : public PetriEngine::StubbornSet, public SuccessorSpooler {
1240+ public:
1241+ explicit AutomatonStubbornSet(const PetriEngine::PetriNet &net, const Structures::BuchiAutomaton &aut)
1242+ : PetriEngine::StubbornSet(net), _retarding_stubborn_set(net,false),
1243+ _state_guards(std::move(GuardInfo::from_automaton(aut))),
1244+ _aut(aut),
1245+ _place_checkpoint(new bool[net.numberOfPlaces()]),
1246+ _gen(_net)
1247+ {
1248+ _markbuf.setMarking(net.makeInitialMarking());
1249+ _retarding_stubborn_set.setInterestingVisitor<PetriEngine::AutomatonInterestingTransitionVisitor>();
1250+ }
1251+
1252+ bool prepare(const PetriEngine::Structures::State *marking) override {
1253+ return prepare(dynamic_cast<const LTL::Structures::ProductState*>(marking));
1254+ }
1255+
1256+ bool prepare(const LTL::Structures::ProductState *state) override;
1257+
1258+ uint32_t next() override;
1259+
1260+ void reset() override;
1261+
1262+
1263+ private:
1264+ static bool has_shared_mark(const bool* a, const bool* b, size_t size) {
1265+ for (size_t i = 0; i < size; ++i) {
1266+ if (a[i] && b[i]) return true;
1267+ }
1268+ return false;
1269+ }
1270+
1271+ protected:
1272+ void addToStub(uint32_t t) override;
1273+
1274+ private:
1275+
1276+ PetriEngine::ReachabilityStubbornSet _retarding_stubborn_set;
1277+ const std::vector<GuardInfo> _state_guards;
1278+ const Structures::BuchiAutomaton &_aut;
1279+ std::unique_ptr<bool[]> _place_checkpoint;
1280+ PetriEngine::SuccessorGenerator _gen;
1281+ PetriEngine::Structures::State _markbuf;
1282+ bool _has_enabled_stubborn = false;
1283+ bool _bad = false;
1284+ bool _done = false;
1285+ bool _track_changes = false;
1286+ bool _retarding_satisfied;
1287+
1288+ std::unordered_set<uint32_t> _pending_stubborn;
1289+
1290+
1291+ void _reset_pending();
1292+
1293+ void _apply_pending();
1294+
1295+ void __print_debug();
1296+
1297+ void set_all_stubborn();
1298+
1299+ bool _closure();
1300+
1301+ bool _cond3_valid(uint32_t t);
1302+
1303+ friend class NondeterministicConjunctionVisitor;
1304+ };
1305+
1306+}
1307+
1308+#endif //VERIFYPN_AUTOMATONSTUBBORNSET_H
1309
1310=== added file 'include/LTL/Stubborn/EvalAndSetVisitor.h'
1311--- include/LTL/Stubborn/EvalAndSetVisitor.h 1970-01-01 00:00:00 +0000
1312+++ include/LTL/Stubborn/EvalAndSetVisitor.h 2021-08-05 10:56:53 +0000
1313@@ -0,0 +1,74 @@
1314+/* Copyright (C) 2021 Nikolaj J. Ulrik <nikolaj@njulrik.dk>,
1315+ * Simon M. Virenfeldt <simon@simwir.dk>
1316+ *
1317+ * This program is free software: you can redistribute it and/or modify
1318+ * it under the terms of the GNU General Public License as published by
1319+ * the Free Software Foundation, either version 3 of the License, or
1320+ * (at your option) any later version.
1321+ *
1322+ * This program is distributed in the hope that it will be useful,
1323+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1324+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1325+ * GNU General Public License for more details.
1326+ *
1327+ * You should have received a copy of the GNU General Public License
1328+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1329+ */
1330+
1331+#ifndef VERIFYPN_EVALANDSETVISITOR_H
1332+#define VERIFYPN_EVALANDSETVISITOR_H
1333+
1334+#include "PetriEngine/PQL/MutatingVisitor.h"
1335+#include "PetriEngine/PQL/Contexts.h"
1336+
1337+namespace LTL {
1338+
1339+ class EvalAndSetVisitor : public PetriEngine::PQL::MutatingVisitor {
1340+
1341+ public:
1342+ EvalAndSetVisitor(const PetriEngine::PQL::EvaluationContext &context) : _context(context) {}
1343+
1344+ private:
1345+ const PetriEngine::PQL::EvaluationContext &_context;
1346+ protected:
1347+
1348+ protected:
1349+ void _accept(PetriEngine::PQL::ACondition *condition) override;
1350+
1351+ void _accept(PetriEngine::PQL::ECondition *condition) override;
1352+
1353+ void _accept(PetriEngine::PQL::GCondition *condition) override;
1354+
1355+ void _accept(PetriEngine::PQL::FCondition *condition) override;
1356+
1357+ void _accept(PetriEngine::PQL::XCondition *condition) override;
1358+
1359+ void _accept(PetriEngine::PQL::UntilCondition *condition) override;
1360+
1361+ void _accept(PetriEngine::PQL::NotCondition *element) override;
1362+
1363+ void _accept(PetriEngine::PQL::AndCondition *element) override;
1364+
1365+ void _accept(PetriEngine::PQL::OrCondition *element) override;
1366+
1367+ void _accept(PetriEngine::PQL::LessThanCondition *element) override;
1368+
1369+ void _accept(PetriEngine::PQL::LessThanOrEqualCondition *element) override;
1370+
1371+ void _accept(PetriEngine::PQL::EqualCondition *element) override;
1372+
1373+ void _accept(PetriEngine::PQL::NotEqualCondition *element) override;
1374+
1375+ void _accept(PetriEngine::PQL::DeadlockCondition *element) override;
1376+
1377+ void _accept(PetriEngine::PQL::CompareConjunction *element) override;
1378+
1379+ void _accept(PetriEngine::PQL::UnfoldedUpperBoundsCondition *element) override;
1380+
1381+ void _accept(PetriEngine::PQL::BooleanCondition *element) override;
1382+ };
1383+
1384+}
1385+
1386+
1387+#endif //VERIFYPN_EVALANDSETVISITOR_H
1388
1389=== added file 'include/LTL/Stubborn/ReachabilityStubbornSpooler.h'
1390--- include/LTL/Stubborn/ReachabilityStubbornSpooler.h 1970-01-01 00:00:00 +0000
1391+++ include/LTL/Stubborn/ReachabilityStubbornSpooler.h 2021-08-05 10:56:53 +0000
1392@@ -0,0 +1,52 @@
1393+/* Copyright (C) 2021 Nikolaj J. Ulrik <nikolaj@njulrik.dk>,
1394+ * Simon M. Virenfeldt <simon@simwir.dk>
1395+ *
1396+ * This program is free software: you can redistribute it and/or modify
1397+ * it under the terms of the GNU General Public License as published by
1398+ * the Free Software Foundation, either version 3 of the License, or
1399+ * (at your option) any later version.
1400+ *
1401+ * This program is distributed in the hope that it will be useful,
1402+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1403+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1404+ * GNU General Public License for more details.
1405+ *
1406+ * You should have received a copy of the GNU General Public License
1407+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1408+ */
1409+
1410+#ifndef VERIFYPN_REACHABILITYSTUBBORNSPOOLER_H
1411+#define VERIFYPN_REACHABILITYSTUBBORNSPOOLER_H
1412+
1413+#include "LTL/SuccessorGeneration/SuccessorSpooler.h"
1414+#include "PetriEngine/Stubborn/ReachabilityStubbornSet.h"
1415+#include "PetriEngine/Stubborn/InterestingTransitionVisitor.h"
1416+
1417+namespace LTL {
1418+ class ReachabilityStubbornSpooler : public SuccessorSpooler {
1419+ public:
1420+ ReachabilityStubbornSpooler(const PetriEngine::PetriNet &net, const std::vector<PetriEngine::PQL::Condition_ptr> &conds)
1421+ : _stubborn(net, conds) {
1422+ _stubborn.setInterestingVisitor<PetriEngine::InterestingLTLTransitionVisitor>();
1423+ }
1424+
1425+ bool prepare(const LTL::Structures::ProductState *state) override
1426+ {
1427+ return _stubborn.prepare(state);
1428+ }
1429+
1430+ uint32_t next() override
1431+ {
1432+ return _stubborn.next();
1433+ }
1434+
1435+ void reset() override
1436+ {
1437+ _stubborn.reset();
1438+ }
1439+
1440+ private:
1441+ PetriEngine::ReachabilityStubbornSet _stubborn;
1442+ };
1443+}
1444+#endif //VERIFYPN_REACHABILITYSTUBBORNSPOOLER_H
1445
1446=== added file 'include/LTL/Stubborn/ReducingSuccessorGenerator.h'
1447--- include/LTL/Stubborn/ReducingSuccessorGenerator.h 1970-01-01 00:00:00 +0000
1448+++ include/LTL/Stubborn/ReducingSuccessorGenerator.h 2021-08-05 10:56:53 +0000
1449@@ -0,0 +1,46 @@
1450+/* Copyright (C) 2021 Nikolaj J. Ulrik <nikolaj@njulrik.dk>,
1451+ * Simon M. Virenfeldt <simon@simwir.dk>
1452+ *
1453+ * This program is free software: you can redistribute it and/or modify
1454+ * it under the terms of the GNU General Public License as published by
1455+ * the Free Software Foundation, either version 3 of the License, or
1456+ * (at your option) any later version.
1457+ *
1458+ * This program is distributed in the hope that it will be useful,
1459+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1460+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1461+ * GNU General Public License for more details.
1462+ *
1463+ * You should have received a copy of the GNU General Public License
1464+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1465+ */
1466+
1467+#ifndef VERIFYPN_REDUCINGSUCCESSORGENERATOR_H
1468+#define VERIFYPN_REDUCINGSUCCESSORGENERATOR_H
1469+
1470+#include "PetriEngine/ReducingSuccessorGenerator.h"
1471+#include "LTL/SuccessorGeneration/AutomatonStubbornSet.h"
1472+
1473+#include <utility>
1474+
1475+namespace LTL {
1476+ class ReducingSuccessorGenerator : public PetriEngine::ReducingSuccessorGenerator {
1477+ public:
1478+ ReducingSuccessorGenerator(const PetriEngine::PetriNet &net, const PetriEngine::PQL::Condition_ptr &query)
1479+ : PetriEngine::ReducingSuccessorGenerator(net, std::make_shared<AutomatonStubbornSet>(net, query)) {}
1480+
1481+ ReducingSuccessorGenerator(const PetriEngine::PetriNet &net, const std::shared_ptr<AutomatonStubbornSet> &stubSet)
1482+ : PetriEngine::ReducingSuccessorGenerator(net, stubSet) {}
1483+
1484+
1485+ bool prepare(const PetriEngine::Structures::State *state, const LTL::GuardInfo &info)
1486+ {
1487+ _current = 0;
1488+ _parent = state;
1489+ return (dynamic_pointer_cast<LTL::AutomatonStubbornSet>(_stubSet))->prepare(state, info);
1490+ }
1491+
1492+ };
1493+}
1494+
1495+#endif //VERIFYPN_REDUCINGSUCCESSORGENERATOR_H
1496
1497=== added file 'include/LTL/Stubborn/SafeAutStubbornSet.h'
1498--- include/LTL/Stubborn/SafeAutStubbornSet.h 1970-01-01 00:00:00 +0000
1499+++ include/LTL/Stubborn/SafeAutStubbornSet.h 2021-08-05 10:56:53 +0000
1500@@ -0,0 +1,71 @@
1501+/* Copyright (C) 2021 Nikolaj J. Ulrik <nikolaj@njulrik.dk>,
1502+ * Simon M. Virenfeldt <simon@simwir.dk>
1503+ *
1504+ * This program is free software: you can redistribute it and/or modify
1505+ * it under the terms of the GNU General Public License as published by
1506+ * the Free Software Foundation, either version 3 of the License, or
1507+ * (at your option) any later version.
1508+ *
1509+ * This program is distributed in the hope that it will be useful,
1510+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1511+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1512+ * GNU General Public License for more details.
1513+ *
1514+ * You should have received a copy of the GNU General Public License
1515+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1516+ */
1517+
1518+#ifndef VERIFYPN_SAFEAUTSTUBBORNSET_H
1519+#define VERIFYPN_SAFEAUTSTUBBORNSET_H
1520+
1521+#include "PetriEngine/Stubborn/InterestingTransitionVisitor.h"
1522+#include "LTL/SuccessorGeneration/SuccessorSpooler.h"
1523+#include "PetriEngine/Stubborn/StubbornSet.h"
1524+#include "PetriEngine/PQL/PQL.h"
1525+
1526+namespace LTL {
1527+ class SafeAutStubbornSet : public PetriEngine::StubbornSet, public SuccessorSpooler {
1528+ public:
1529+ SafeAutStubbornSet(const PetriEngine::PetriNet &net,
1530+ const std::vector<PetriEngine::PQL::Condition_ptr> &queries)
1531+ : StubbornSet(net, queries), _unsafe(std::make_unique<bool[]>(net.numberOfTransitions())) {}
1532+
1533+ bool prepare(const PetriEngine::Structures::State *marking) override
1534+ {
1535+ assert(false);
1536+ std::cerr << "Error: SafeAutStubbornSet is implemented only for product states\n";
1537+ exit(1);
1538+ return false;
1539+ }
1540+
1541+ bool prepare(const LTL::Structures::ProductState *state) override;
1542+
1543+ uint32_t next() override {
1544+ return StubbornSet::next();
1545+ }
1546+
1547+ void reset() override {
1548+ StubbornSet::reset();
1549+ memset(_unsafe.get(), false, sizeof(bool) * _net.numberOfTransitions());
1550+ _bad = false;
1551+ }
1552+
1553+ protected:
1554+ void addToStub(uint32_t t) override
1555+ {
1556+ //refinement of bad: can manually check whether firing t would violate some progressing formula.
1557+ if (_unsafe[t] && _enabled[t]) {
1558+ _bad = true;
1559+ return;
1560+ }
1561+ //TODO check safe-ness
1562+ StubbornSet::addToStub(t);
1563+ }
1564+
1565+ private:
1566+ std::unique_ptr<bool[]> _unsafe;
1567+ bool _bad = false;
1568+ };
1569+}
1570+
1571+#endif //VERIFYPN_SAFEAUTSTUBBORNSET_H
1572
1573=== added file 'include/LTL/Stubborn/VisibleLTLStubbornSet.h'
1574--- include/LTL/Stubborn/VisibleLTLStubbornSet.h 1970-01-01 00:00:00 +0000
1575+++ include/LTL/Stubborn/VisibleLTLStubbornSet.h 2021-08-05 10:56:53 +0000
1576@@ -0,0 +1,111 @@
1577+/* Copyright (C) 2021 Nikolaj J. Ulrik <nikolaj@njulrik.dk>,
1578+ * Simon M. Virenfeldt <simon@simwir.dk>
1579+ *
1580+ * This program is free software: you can redistribute it and/or modify
1581+ * it under the terms of the GNU General Public License as published by
1582+ * the Free Software Foundation, either version 3 of the License, or
1583+ * (at your option) any later version.
1584+ *
1585+ * This program is distributed in the hope that it will be useful,
1586+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1587+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1588+ * GNU General Public License for more details.
1589+ *
1590+ * You should have received a copy of the GNU General Public License
1591+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1592+ */
1593+
1594+#ifndef VERIFYPN_VISIBLELTLSTUBBORNSET_H
1595+#define VERIFYPN_VISIBLELTLSTUBBORNSET_H
1596+
1597+
1598+//#include "PetriEngine/ReducingSuccessorGenerator.h"
1599+#include "PetriEngine/Stubborn/StubbornSet.h"
1600+#include "PetriEngine/PQL/PQL.h"
1601+#include "LTL/Stubborn/VisibleTransitionVisitor.h"
1602+#include "LTL/SuccessorGeneration/SuccessorSpooler.h"
1603+
1604+// TODO LTL Stubborn sets should be subclassing just PetriEngine::StubbornSet, then a class
1605+// LTL::StubbornSuccessorSpooler : LTL::SuccessorSpooler has a stubborn set via simple wrapping.
1606+// This way we can avoid the super ugly multiple inheritance caused by both base classes
1607+// each having prepare, next, reset methods.q
1608+namespace LTL {
1609+ class VisibleLTLStubbornSet : public PetriEngine::StubbornSet, public SuccessorSpooler {
1610+ public:
1611+ VisibleLTLStubbornSet(const PetriEngine::PetriNet &net,
1612+ const std::vector<PetriEngine::PQL::Condition_ptr> &queries)
1613+ : StubbornSet(net, queries), _visible(new bool[net.numberOfTransitions()])
1614+ {
1615+ assert(!_netContainsInhibitorArcs);
1616+ memset(_visible.get(), 0, sizeof(bool) * net.numberOfPlaces());
1617+ VisibleTransitionVisitor visible{_visible};
1618+ for (auto &q : queries) {
1619+ q->visit(visible);
1620+ }
1621+ }
1622+
1623+ VisibleLTLStubbornSet(const PetriEngine::PetriNet &net, const PetriEngine::PQL::Condition_ptr &query)
1624+ : StubbornSet(net, query), _visible(new bool[net.numberOfTransitions()])
1625+ {
1626+ assert(!_netContainsInhibitorArcs);
1627+ auto places = std::make_unique<bool[]>(net.numberOfPlaces());
1628+ memset(places.get(), 0, sizeof(bool) * net.numberOfPlaces());
1629+ memset(_visible.get(), 0, sizeof(bool) * net.numberOfTransitions());
1630+ VisibleTransitionVisitor visible{places};
1631+ query->visit(visible);
1632+
1633+ memset(_places_seen.get(), 0, _net.numberOfPlaces());
1634+ for (uint32_t p = 0; p < net.numberOfPlaces(); ++p) {
1635+ if (places[p]) {
1636+ visTrans(p);
1637+ }
1638+ }
1639+ }
1640+
1641+ void visTrans(uint32_t place)
1642+ {
1643+ if (_places_seen[place] > 0) return;
1644+ _places_seen[place] = 1;
1645+ for (uint32_t t = _places[place].pre; t < _places[place].post; ++t) {
1646+ auto tr = _transitions[t];
1647+ _visible[tr.index] = true;
1648+ }
1649+ for (uint32_t t = _places.get()[place].post; t < _places.get()[place + 1].pre; t++) {
1650+ auto tr = _transitions[t];
1651+ if (tr.direction < 0)
1652+ _visible[tr.index] = true;
1653+ }
1654+ }
1655+
1656+ bool prepare(const PetriEngine::Structures::State *marking) override;
1657+
1658+ bool prepare(const LTL::Structures::ProductState *marking) override
1659+ {
1660+ return prepare((const PetriEngine::Structures::State *) marking);
1661+ }
1662+
1663+ uint32_t next() override;
1664+
1665+ void reset();
1666+
1667+ bool generateAll(const LTL::Structures::ProductState *parent);
1668+
1669+ protected:
1670+ void addToStub(uint32_t t) override;
1671+
1672+ private:
1673+ std::unique_ptr<bool[]> _visible;
1674+ light_deque<uint32_t> _skipped;
1675+ uint32_t _key;
1676+ bool _has_enabled_stubborn;
1677+
1678+ void findKeyTransition();
1679+
1680+ void ensureRuleV();
1681+
1682+ void ensureRulesL();
1683+
1684+ };
1685+}
1686+
1687+#endif //VERIFYPN_VISIBLELTLSTUBBORNSET_H
1688
1689=== added file 'include/LTL/Stubborn/VisibleTransitionVisitor.h'
1690--- include/LTL/Stubborn/VisibleTransitionVisitor.h 1970-01-01 00:00:00 +0000
1691+++ include/LTL/Stubborn/VisibleTransitionVisitor.h 2021-08-05 10:56:53 +0000
1692@@ -0,0 +1,82 @@
1693+/* Copyright (C) 2021 Nikolaj J. Ulrik <nikolaj@njulrik.dk>,
1694+ * Simon M. Virenfeldt <simon@simwir.dk>
1695+ *
1696+ * This program is free software: you can redistribute it and/or modify
1697+ * it under the terms of the GNU General Public License as published by
1698+ * the Free Software Foundation, either version 3 of the License, or
1699+ * (at your option) any later version.
1700+ *
1701+ * This program is distributed in the hope that it will be useful,
1702+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1703+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1704+ * GNU General Public License for more details.
1705+ *
1706+ * You should have received a copy of the GNU General Public License
1707+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1708+ */
1709+
1710+#ifndef VERIFYPN_VISIBLETRANSITIONVISITOR_H
1711+#define VERIFYPN_VISIBLETRANSITIONVISITOR_H
1712+
1713+#include "PetriEngine/PQL/Visitor.h"
1714+#include "PetriEngine/PQL/PQL.h"
1715+#include "PetriEngine/PetriNet.h"
1716+#include "PetriEngine/Structures/light_deque.h"
1717+
1718+namespace LTL {
1719+ class VisibleTransitionVisitor : public PetriEngine::PQL::Visitor {
1720+ public:
1721+ explicit VisibleTransitionVisitor(std::unique_ptr<bool[]> &visible)
1722+ : _places(visible) {}
1723+
1724+ protected:
1725+ void _accept(const PetriEngine::PQL::NotCondition *element) override;
1726+
1727+ void _accept(const PetriEngine::PQL::AndCondition *element) override;
1728+
1729+ void _accept(const PetriEngine::PQL::OrCondition *element) override;
1730+
1731+ void _accept(const PetriEngine::PQL::LessThanCondition *element) override;
1732+
1733+ void _accept(const PetriEngine::PQL::LessThanOrEqualCondition *element) override;
1734+
1735+ void _accept(const PetriEngine::PQL::EqualCondition *element) override;
1736+
1737+ void _accept(const PetriEngine::PQL::NotEqualCondition *element) override;
1738+
1739+ void _accept(const PetriEngine::PQL::DeadlockCondition *element) override;
1740+
1741+ void _accept(const PetriEngine::PQL::CompareConjunction *element) override;
1742+
1743+ void _accept(const PetriEngine::PQL::UnfoldedUpperBoundsCondition *element) override;
1744+
1745+ void _accept(const PetriEngine::PQL::UnfoldedIdentifierExpr *element) override;
1746+
1747+ void _accept(const PetriEngine::PQL::LiteralExpr *element) override;
1748+
1749+ void _accept(const PetriEngine::PQL::PlusExpr *element) override;
1750+
1751+ void _accept(const PetriEngine::PQL::MultiplyExpr *element) override;
1752+
1753+ void _accept(const PetriEngine::PQL::MinusExpr *element) override;
1754+
1755+ void _accept(const PetriEngine::PQL::SubtractExpr *element) override;
1756+
1757+ void _accept(const PetriEngine::PQL::ACondition *condition) override;
1758+
1759+ void _accept(const PetriEngine::PQL::ECondition *condition) override;
1760+
1761+ void _accept(const PetriEngine::PQL::GCondition *condition) override;
1762+
1763+ void _accept(const PetriEngine::PQL::FCondition *condition) override;
1764+
1765+ void _accept(const PetriEngine::PQL::XCondition *condition) override;
1766+
1767+ void _accept(const PetriEngine::PQL::UntilCondition *condition) override;
1768+
1769+ private:
1770+ std::unique_ptr<bool[]> &_places;
1771+ };
1772+}
1773+
1774+#endif //VERIFYPN_VISIBLETRANSITIONVISITOR_H
1775
1776=== modified file 'include/LTL/SuccessorGeneration/AutomatonHeuristic.h'
1777--- include/LTL/SuccessorGeneration/AutomatonHeuristic.h 2021-03-24 07:31:53 +0000
1778+++ include/LTL/SuccessorGeneration/AutomatonHeuristic.h 2021-08-05 10:56:53 +0000
1779@@ -31,10 +31,16 @@
1780
1781 bool has_heuristic(const Structures::ProductState &state) override;
1782
1783- private:
1784+ std::ostream &output(std::ostream &os) {
1785+ return os << "AUTOMATON_HEUR";
1786+ }
1787+
1788+ protected:
1789 const PetriEngine::PetriNet *_net;
1790 const LTL::Structures::BuchiAutomaton &_aut;
1791 std::vector<GuardInfo> _state_guards;
1792+
1793+ std::vector<uint32_t> _bfs_dists;
1794 };
1795 }
1796
1797
1798=== modified file 'include/LTL/SuccessorGeneration/BuchiSuccessorGenerator.h'
1799--- include/LTL/SuccessorGeneration/BuchiSuccessorGenerator.h 2021-03-31 11:41:47 +0000
1800+++ include/LTL/SuccessorGeneration/BuchiSuccessorGenerator.h 2021-08-05 10:56:53 +0000
1801@@ -114,7 +114,7 @@
1802
1803 using _succ_iter = std::unique_ptr<spot::twa_succ_iterator, SuccIterDeleter>;
1804 _succ_iter succ = nullptr;
1805- private:
1806+ private:
1807
1808 enum class InvariantSelfLoop {
1809 TRUE, FALSE, UNKNOWN
1810
1811=== added file 'include/LTL/SuccessorGeneration/ComposedHeuristic.h'
1812--- include/LTL/SuccessorGeneration/ComposedHeuristic.h 1970-01-01 00:00:00 +0000
1813+++ include/LTL/SuccessorGeneration/ComposedHeuristic.h 2021-08-05 10:56:53 +0000
1814@@ -0,0 +1,53 @@
1815+/* Copyright (C) 2021 Nikolaj J. Ulrik <nikolaj@njulrik.dk>,
1816+ * Simon M. Virenfeldt <simon@simwir.dk>
1817+ *
1818+ * This program is free software: you can redistribute it and/or modify
1819+ * it under the terms of the GNU General Public License as published by
1820+ * the Free Software Foundation, either version 3 of the License, or
1821+ * (at your option) any later version.
1822+ *
1823+ * This program is distributed in the hope that it will be useful,
1824+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1825+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1826+ * GNU General Public License for more details.
1827+ *
1828+ * You should have received a copy of the GNU General Public License
1829+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1830+ */
1831+
1832+#ifndef VERIFYPN_COMPOSEDHEURISTIC_H
1833+#define VERIFYPN_COMPOSEDHEURISTIC_H
1834+
1835+#include "Heuristic.h"
1836+
1837+#include <utility>
1838+
1839+namespace LTL {
1840+
1841+ class ComposedHeuristic : public Heuristic {
1842+ public:
1843+
1844+ ComposedHeuristic(std::unique_ptr<Heuristic> primary, std::unique_ptr<Heuristic> secondary)
1845+ : _primary(std::move(primary)), _secondary(std::move(secondary)) {}
1846+
1847+ void prepare(const Structures::ProductState &state) override {
1848+ _primary->prepare(state);
1849+ _secondary->prepare(state);
1850+ }
1851+
1852+ uint32_t eval(const Structures::ProductState &state, uint32_t tid) override = 0;
1853+
1854+ bool has_heuristic(const Structures::ProductState &state) override {
1855+ _primary_has_heuristic = _primary->has_heuristic(state);
1856+ _secondary_has_heuristic = _secondary->has_heuristic(state);
1857+ return _primary_has_heuristic || _secondary_has_heuristic;
1858+ }
1859+
1860+ protected:
1861+ std::unique_ptr<Heuristic> _primary, _secondary;
1862+ bool _primary_has_heuristic = false, _secondary_has_heuristic = false;
1863+ };
1864+
1865+}
1866+
1867+#endif //VERIFYPN_COMPOSEDHEURISTIC_H
1868
1869=== modified file 'include/LTL/SuccessorGeneration/DistanceHeuristic.h'
1870--- include/LTL/SuccessorGeneration/DistanceHeuristic.h 2021-03-19 11:48:52 +0000
1871+++ include/LTL/SuccessorGeneration/DistanceHeuristic.h 2021-08-05 10:56:53 +0000
1872@@ -34,6 +34,9 @@
1873 return _cond->distance(context);
1874 }
1875
1876+ std::ostream &output(std::ostream &os) {
1877+ return os << "DIST_HEUR";
1878+ }
1879 private:
1880 const PetriEngine::PetriNet *_net;
1881 const PetriEngine::PQL::Condition_ptr _cond;
1882
1883=== added file 'include/LTL/SuccessorGeneration/FireCountHeuristic.h'
1884--- include/LTL/SuccessorGeneration/FireCountHeuristic.h 1970-01-01 00:00:00 +0000
1885+++ include/LTL/SuccessorGeneration/FireCountHeuristic.h 2021-08-05 10:56:53 +0000
1886@@ -0,0 +1,57 @@
1887+/* Copyright (C) 2021 Nikolaj J. Ulrik <nikolaj@njulrik.dk>,
1888+ * Simon M. Virenfeldt <simon@simwir.dk>
1889+ *
1890+ * This program is free software: you can redistribute it and/or modify
1891+ * it under the terms of the GNU General Public License as published by
1892+ * the Free Software Foundation, either version 3 of the License, or
1893+ * (at your option) any later version.
1894+ *
1895+ * This program is distributed in the hope that it will be useful,
1896+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1897+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1898+ * GNU General Public License for more details.
1899+ *
1900+ * You should have received a copy of the GNU General Public License
1901+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1902+ */
1903+
1904+#ifndef VERIFYPN_FIRECOUNTHEURISTIC_H
1905+#define VERIFYPN_FIRECOUNTHEURISTIC_H
1906+
1907+#include "LTL/SuccessorGeneration/Heuristic.h"
1908+
1909+namespace LTL {
1910+ class FireCountHeuristic : public Heuristic {
1911+ public:
1912+ explicit FireCountHeuristic(const PetriEngine::PetriNet *net) : _fireCount(net->numberOfTransitions()) {}
1913+
1914+ uint32_t eval(const Structures::ProductState &state, uint32_t tid) override {
1915+ assert(tid <= _fireCount.size());
1916+ return _fireCount[tid];
1917+ }
1918+
1919+ bool has_heuristic(const Structures::ProductState &state) override {
1920+ return true;
1921+ }
1922+
1923+ void push(uint32_t tid) override {
1924+ assert(tid <= _fireCount.size());
1925+ ++_fireCount[tid];
1926+ }
1927+
1928+ void pop(uint32_t tid) override {
1929+ assert(tid <= _fireCount.size());
1930+ --_fireCount[tid];
1931+ }
1932+
1933+ std::ostream &output(std::ostream &os) {
1934+ return os << "FIRECOUNT_HEUR";
1935+ }
1936+
1937+
1938+ protected:
1939+ std::vector<uint32_t> _fireCount;
1940+ };
1941+}
1942+
1943+#endif //VERIFYPN_FIRECOUNTHEURISTIC_H
1944
1945=== modified file 'include/LTL/SuccessorGeneration/Heuristic.h'
1946--- include/LTL/SuccessorGeneration/Heuristic.h 2021-03-24 07:31:53 +0000
1947+++ include/LTL/SuccessorGeneration/Heuristic.h 2021-08-05 10:56:53 +0000
1948@@ -20,10 +20,13 @@
1949
1950 #include "PetriEngine/PetriNet.h"
1951 #include "LTL/Structures/ProductState.h"
1952+#include <iostream>
1953
1954 namespace LTL {
1955 class Heuristic {
1956 public:
1957+ virtual void prepare(const LTL::Structures::ProductState &state) {}
1958+
1959 virtual uint32_t eval(const LTL::Structures::ProductState &state, uint32_t tid) = 0;
1960
1961 /**
1962@@ -35,7 +38,13 @@
1963 return true;
1964 }
1965
1966+ virtual void push(uint32_t tid) {};
1967+
1968+ virtual void pop(uint32_t tid) {};
1969+
1970 virtual ~Heuristic() = default;
1971+
1972+ virtual std::ostream &output(std::ostream &os) = 0;
1973 };
1974 }
1975
1976
1977=== added file 'include/LTL/SuccessorGeneration/HeuristicParser.h'
1978--- include/LTL/SuccessorGeneration/HeuristicParser.h 1970-01-01 00:00:00 +0000
1979+++ include/LTL/SuccessorGeneration/HeuristicParser.h 2021-08-05 10:56:53 +0000
1980@@ -0,0 +1,28 @@
1981+/* Copyright (C) 2021 Nikolaj J. Ulrik <nikolaj@njulrik.dk>,
1982+ * Simon M. Virenfeldt <simon@simwir.dk>
1983+ *
1984+ * This program is free software: you can redistribute it and/or modify
1985+ * it under the terms of the GNU General Public License as published by
1986+ * the Free Software Foundation, either version 3 of the License, or
1987+ * (at your option) any later version.
1988+ *
1989+ * This program is distributed in the hope that it will be useful,
1990+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
1991+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
1992+ * GNU General Public License for more details.
1993+ *
1994+ * You should have received a copy of the GNU General Public License
1995+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
1996+ */
1997+
1998+#ifndef VERIFYPN_HEURISTICPARSER_H
1999+#define VERIFYPN_HEURISTICPARSER_H
2000+
2001+namespace LTL {
2002+ std::unique_ptr<LTL::Heuristic> ParseHeuristic(const PetriEngine::PetriNet *net,
2003+ const Structures::BuchiAutomaton &aut,
2004+ const PetriEngine::PQL::Condition_ptr &cond,
2005+ const char* heurString);
2006+}
2007+
2008+#endif //VERIFYPN_HEURISTICPARSER_H
2009
2010=== modified file 'include/LTL/SuccessorGeneration/Heuristics.h'
2011--- include/LTL/SuccessorGeneration/Heuristics.h 2021-03-24 07:31:53 +0000
2012+++ include/LTL/SuccessorGeneration/Heuristics.h 2021-08-05 10:56:53 +0000
2013@@ -21,5 +21,9 @@
2014 #include "DistanceHeuristic.h"
2015 #include "RandomHeuristic.h"
2016 #include "AutomatonHeuristic.h"
2017+#include "ComposedHeuristic.h"
2018+#include "WeightedComposedHeuristic.h"
2019+#include "FireCountHeuristic.h"
2020+#include "LogFireCountHeuristic.h"
2021
2022 #endif //VERIFYPN_HEURISTICS_H
2023
2024=== added file 'include/LTL/SuccessorGeneration/LogFireCountHeuristic.h'
2025--- include/LTL/SuccessorGeneration/LogFireCountHeuristic.h 1970-01-01 00:00:00 +0000
2026+++ include/LTL/SuccessorGeneration/LogFireCountHeuristic.h 2021-08-05 10:56:53 +0000
2027@@ -0,0 +1,69 @@
2028+/* Copyright (C) 2021 Nikolaj J. Ulrik <nikolaj@njulrik.dk>,
2029+ * Simon M. Virenfeldt <simon@simwir.dk>
2030+ *
2031+ * This program is free software: you can redistribute it and/or modify
2032+ * it under the terms of the GNU General Public License as published by
2033+ * the Free Software Foundation, either version 3 of the License, or
2034+ * (at your option) any later version.
2035+ *
2036+ * This program is distributed in the hope that it will be useful,
2037+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2038+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2039+ * GNU General Public License for more details.
2040+ *
2041+ * You should have received a copy of the GNU General Public License
2042+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2043+ */
2044+
2045+#ifndef VERIFYPN_LOGFIRECOUNTHEURISTIC_H
2046+#define VERIFYPN_LOGFIRECOUNTHEURISTIC_H
2047+
2048+#include "Heuristic.h"
2049+#include "FireCountHeuristic.h"
2050+
2051+namespace LTL {
2052+ constexpr int _approx_log(uint32_t i)
2053+ {
2054+ // log2 of an integer is the highest set bit
2055+ // loop is not optimal but portable and likely reasonably performant for small-ish ints
2056+ // (which most of the time is the expected input)
2057+ int r = 0;
2058+ while (i >>= 1) {
2059+ ++r;
2060+ }
2061+ return r;
2062+ }
2063+
2064+ /**
2065+ * Piece-wise zero/logarithmic fire count heuristic, switching to log at threshold defined by parameter
2066+ * (default 200). Intent is to avoid firing the same transition excessively in models with massive token counts.
2067+ */
2068+ class LogFireCountHeuristic : public FireCountHeuristic {
2069+ public:
2070+ explicit LogFireCountHeuristic(const PetriEngine::PetriNet *net, uint32_t threshold = 200)
2071+ : FireCountHeuristic(net), _threshold(threshold) {}
2072+
2073+ uint32_t eval(const Structures::ProductState &state, uint32_t tid) override
2074+ {
2075+ if (_fireCount[tid] < _threshold) return 0;
2076+ return _approx_log(_fireCount[tid] - _threshold);
2077+ }
2078+
2079+ std::ostream &output(std::ostream &os) {
2080+ return os << "LOGFIRECOUNT_HEUR(" << _threshold << ")";
2081+ }
2082+
2083+ private:
2084+ uint32_t _threshold;
2085+
2086+ // compile-time sanity checks, feel free to remove if problematic
2087+ static_assert(_approx_log(1) == 0);
2088+ static_assert(_approx_log(2) == 1);
2089+ static_assert(_approx_log(3) == 1);
2090+ static_assert(_approx_log(9) == 3);
2091+ static_assert(_approx_log(15) == 3);
2092+ static_assert(_approx_log(16) == 4);
2093+ };
2094+}
2095+
2096+#endif //VERIFYPN_LOGFIRECOUNTHEURISTIC_H
2097
2098=== modified file 'include/LTL/SuccessorGeneration/ProductSuccessorGenerator.h'
2099--- include/LTL/SuccessorGeneration/ProductSuccessorGenerator.h 2021-04-02 09:54:38 +0000
2100+++ include/LTL/SuccessorGeneration/ProductSuccessorGenerator.h 2021-08-05 10:56:53 +0000
2101@@ -24,9 +24,11 @@
2102 #include "LTL/Structures/ProductState.h"
2103 #include "BuchiSuccessorGenerator.h"
2104 #include "LTL/LTLToBuchi.h"
2105+#include "LTL/Stubborn/VisibleLTLStubbornSet.h"
2106 #include "LTL/Simplification/SpotToPQL.h"
2107 #include "LTL/Structures/GuardInfo.h"
2108 #include "LTL/SuccessorGeneration/SpoolingSuccessorGenerator.h"
2109+#include "LTL/SuccessorGeneration/ResumingSuccessorGenerator.h"
2110
2111 #include <spot/twa/formula2bdd.hh>
2112 #include <spot/tl/formula.hh>
2113@@ -39,8 +41,8 @@
2114
2115 ProductSuccessorGenerator(const PetriEngine::PetriNet *net,
2116 const Structures::BuchiAutomaton &buchi,
2117- SuccessorGen &&successorGen)
2118- : _successor_generator(std::move(successorGen)), _net(net),
2119+ SuccessorGen *successorGen)
2120+ : _successor_generator(successorGen), _net(net),
2121 buchi(buchi), aut(buchi)
2122 {
2123
2124@@ -48,22 +50,14 @@
2125
2126 [[nodiscard]] size_t initial_buchi_state() const { return buchi.initial_state_number(); };
2127
2128- auto prepare(const LTL::Structures::ProductState *state)
2129- {
2130- buchi.prepare(state->getBuchiState());
2131- buchi_parent = state->getBuchiState();
2132- fresh_marking = true;
2133- return _successor_generator.prepare(state);
2134- }
2135-
2136 bool next(LTL::Structures::ProductState &state)
2137 {
2138 if (fresh_marking) {
2139 fresh_marking = false;
2140- if (!_successor_generator.next(state)) {
2141+ if (!_successor_generator->next(state)) {
2142 // This is a fresh marking, so if there is no more successors for the state the state is deadlocked.
2143 // The semantics for deadlock is to just loop the marking so return true without changing the value of state.
2144- std::copy(_successor_generator.getParent(), _successor_generator.getParent() + state.buchi_state_idx + 1,
2145+ std::copy(_successor_generator->getParent(), _successor_generator->getParent() + state.buchi_state_idx + 1,
2146 state.marking());
2147 }
2148 }
2149@@ -73,7 +67,7 @@
2150 // No valid transition in Büchi automaton for current marking;
2151 // Try next marking(s) and see if we find a successor.
2152 else {
2153- while (_successor_generator.next(state)) {
2154+ while (_successor_generator->next(state)) {
2155 // reset buchi successors
2156 buchi.prepare(buchi_parent);
2157 if (next_buchi_succ(state)) {
2158@@ -118,9 +112,9 @@
2159 * @param state the source state to generate successors from
2160 * @param sucinfo the point in the iteration to start from, as returned by `next`.
2161 */
2162- void prepare(const LTL::Structures::ProductState *state, typename SuccessorGen::sucinfo &sucinfo)
2163+ virtual void prepare(const LTL::Structures::ProductState *state, typename SuccessorGen::sucinfo &sucinfo)
2164 {
2165- _successor_generator.prepare(state, sucinfo);
2166+ _successor_generator->prepare(state, sucinfo);
2167 fresh_marking = sucinfo.fresh();
2168 buchi.prepare(state->getBuchiState());
2169 buchi_parent = state->getBuchiState();
2170@@ -149,26 +143,26 @@
2171 {
2172 if (fresh_marking) {
2173 fresh_marking = false;
2174- if (!_successor_generator.next(state, sucinfo)) {
2175+ if (!_successor_generator->next(state, sucinfo)) {
2176 // This is a fresh marking, so if there are no more successors for the state the state is deadlocked.
2177 // The semantics for deadlock is to just loop the marking so return true without changing the value of state.
2178- std::copy(_successor_generator.getParent(), _successor_generator.getParent() + state.buchi_state_idx + 1,
2179+ std::copy(_successor_generator->getParent(), _successor_generator->getParent() + state.buchi_state_idx + 1,
2180 state.marking());
2181 }
2182 }
2183 if (next_buchi_succ(state)) {
2184- //_successor_generator.getSuccInfo(sucinfo);
2185+ //_successor_generator->getSuccInfo(sucinfo);
2186 sucinfo.buchi_state = state.getBuchiState();
2187 return true;
2188 }
2189 // No valid transition in Büchi automaton for current marking;
2190 // Try next marking(s) and see if we find a successor.
2191 else {
2192- while (_successor_generator.next(state, sucinfo)) {
2193+ while (_successor_generator->next(state, sucinfo)) {
2194 // reset buchi successors
2195 buchi.prepare(buchi_parent);
2196 if (next_buchi_succ(state)) {
2197- //_successor_generator.getSuccInfo(sucinfo);
2198+ //_successor_generator->getSuccInfo(sucinfo);
2199 sucinfo.buchi_state = state.getBuchiState();
2200 return true;
2201 }
2202@@ -183,91 +177,84 @@
2203 return buchi.is_weak();
2204 }
2205
2206- size_t last_transition() const { return _successor_generator.last_transition(); }
2207+ size_t last_transition() const { return _successor_generator->last_transition(); }
2208
2209- size_t fired() const { return _successor_generator.fired(); }
2210+ size_t fired() const { return _successor_generator->fired(); }
2211
2212 //template<typename T = std::enable_if_t<std::is_same_v<SuccessorGen, PetriEngine::ReducingSuccessorGenerator>, void>>
2213- void generateAll(typename SuccessorGen::sucinfo &sucinfo)
2214+ void generateAll(LTL::Structures::ProductState *parent, typename SuccessorGen::sucinfo &sucinfo)
2215 {
2216 if constexpr (std::is_same_v<SuccessorGen, LTL::SpoolingSuccessorGenerator>) {
2217- _successor_generator.generate_all(sucinfo);
2218+ _successor_generator->generate_all(parent, sucinfo);
2219 }
2220 }
2221
2222 size_t numEnabled()
2223 {
2224 if constexpr (std::is_same_v<SuccessorGen, LTL::SpoolingSuccessorGenerator>) {
2225- return _successor_generator.nenabled();
2226+ return _successor_generator->nenabled();
2227 }
2228 return -1;
2229 }
2230
2231- // FIXME const if possible?
2232- bool *enabled() const
2233+ const bool *enabled() const
2234 {
2235 if constexpr (std::is_same_v<SuccessorGen, LTL::SpoolingSuccessorGenerator>) {
2236- return _successor_generator.enabled();
2237+ return _successor_generator->enabled();
2238 }
2239 return nullptr;
2240 };
2241
2242- // FIXME const if possible?
2243- bool *stubborn() const
2244+ const bool *stubborn() const
2245 {
2246 if constexpr (std::is_same_v<SuccessorGen, LTL::SpoolingSuccessorGenerator>) {
2247- return _successor_generator.stubborn();
2248+ return _successor_generator->stubborn();
2249 }
2250 return nullptr;
2251 };
2252
2253 size_t buchiStates() { return buchi.buchiStates(); }
2254
2255+ void push() {
2256+ if constexpr (std::is_same_v<SuccessorGen, LTL::SpoolingSuccessorGenerator>) {
2257+ _successor_generator->push();
2258+ }
2259+ }
2260+
2261+ void pop(const typename SuccessorGen::sucinfo &sucinfo) {
2262+ if constexpr (std::is_same_v<SuccessorGen, LTL::SpoolingSuccessorGenerator>) {
2263+ _successor_generator->pop(sucinfo);
2264+ }
2265+ }
2266+
2267 bool has_invariant_self_loop(const LTL::Structures::ProductState &state) {
2268 return buchi.has_invariant_self_loop(state.getBuchiState());
2269 }
2270- private:
2271- SuccessorGen _successor_generator;
2272+
2273+ virtual ~ProductSuccessorGenerator() = default;
2274+
2275+ protected:
2276+ SuccessorGen *_successor_generator;
2277 const PetriEngine::PetriNet *_net;
2278-
2279 BuchiSuccessorGenerator buchi;
2280+
2281 const LTL::Structures::BuchiAutomaton &aut;
2282 bdd cond;
2283 size_t buchi_parent;
2284 bool fresh_marking = true;
2285-
2286 std::vector<GuardInfo> stateToGuards;
2287-
2288 /**
2289 * Evaluate binary decision diagram (BDD) representation of transition guard in given state.
2290 */
2291 bool guard_valid(const PetriEngine::Structures::State &state, bdd bdd)
2292 {
2293 PetriEngine::PQL::EvaluationContext ctx{state.marking(), _net};
2294- // IDs 0 and 1 are false and true atoms, respectively
2295- // More details in buddy manual ( http://buddy.sourceforge.net/manual/main.html )
2296- while (bdd.id() > 1) {
2297- // find variable to test, and test it
2298- size_t var = bdd_var(bdd);
2299- using PetriEngine::PQL::Condition;
2300- Condition::Result res = buchi.getExpression(var)->evaluate(ctx);
2301- switch (res) {
2302- case Condition::RUNKNOWN:
2303- std::cerr << "Unexpected unknown answer from evaluating query!\n";
2304- assert(false);
2305- exit(1);
2306- break;
2307- case Condition::RFALSE:
2308- bdd = bdd_low(bdd);
2309- break;
2310- case Condition::RTRUE:
2311- bdd = bdd_high(bdd);
2312- break;
2313- }
2314- }
2315- return bdd == bddtrue;
2316+ return buchi.aut.guard_valid(ctx, bdd);
2317 }
2318
2319+
2320+ private:
2321+
2322 bool next_buchi_succ(LTL::Structures::ProductState &state)
2323 {
2324 size_t tmp;
2325
2326=== modified file 'include/LTL/SuccessorGeneration/RandomHeuristic.h'
2327--- include/LTL/SuccessorGeneration/RandomHeuristic.h 2021-04-02 09:54:38 +0000
2328+++ include/LTL/SuccessorGeneration/RandomHeuristic.h 2021-08-05 10:56:53 +0000
2329@@ -31,7 +31,9 @@
2330 return g();
2331 }
2332
2333-
2334+ std::ostream &output(std::ostream &os) {
2335+ return os << "RANDOM_HEUR";
2336+ }
2337 private:
2338 std::random_device rd;
2339 std::mt19937 g;
2340
2341=== added file 'include/LTL/SuccessorGeneration/ReachStubProductSuccessorGenerator.h'
2342--- include/LTL/SuccessorGeneration/ReachStubProductSuccessorGenerator.h 1970-01-01 00:00:00 +0000
2343+++ include/LTL/SuccessorGeneration/ReachStubProductSuccessorGenerator.h 2021-08-05 10:56:53 +0000
2344@@ -0,0 +1,106 @@
2345+/* Copyright (C) 2021 Nikolaj J. Ulrik <nikolaj@njulrik.dk>,
2346+ * Simon M. Virenfeldt <simon@simwir.dk>
2347+ *
2348+ * This program is free software: you can redistribute it and/or modify
2349+ * it under the terms of the GNU General Public License as published by
2350+ * the Free Software Foundation, either version 3 of the License, or
2351+ * (at your option) any later version.
2352+ *
2353+ * This program is distributed in the hope that it will be useful,
2354+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2355+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2356+ * GNU General Public License for more details.
2357+ *
2358+ * You should have received a copy of the GNU General Public License
2359+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2360+ */
2361+
2362+#ifndef VERIFYPN_REACHSTUBPRODUCTSUCCESSORGENERATOR_H
2363+#define VERIFYPN_REACHSTUBPRODUCTSUCCESSORGENERATOR_H
2364+
2365+#include "LTL/SuccessorGeneration/ProductSuccessorGenerator.h"
2366+#include "LTL/SuccessorGeneration/Spoolers.h"
2367+
2368+
2369+namespace LTL {
2370+ template<typename S, typename Spooler>
2371+ class ReachStubProductSuccessorGenerator : public ProductSuccessorGenerator<S> {
2372+ public:
2373+ ReachStubProductSuccessorGenerator(const PetriEngine::PetriNet *net, const Structures::BuchiAutomaton &buchi,
2374+ S *successorGen, std::unique_ptr<Spooler> &&fallbackSpooler)
2375+ : ProductSuccessorGenerator<S>(net, buchi, successorGen), _fallback_spooler(std::move(fallbackSpooler))
2376+ {
2377+ //_fallback_spooler = std::make_unique<Spooler>(fallbackSpooler);
2378+/* if constexpr (std::is_same_v<Spooler, EnabledSpooler>) {
2379+ _fallback_spooler = std::make_unique<Spooler>(net, static_cast<PetriEngine::SuccessorGenerator&>(this->_successor_generator));
2380+ }
2381+ else {
2382+ _fallback_spooler = std::make_unique<Spooler>(net);
2383+ }*/
2384+ // Create the set of büchi states from which we can use reachability stubborn sets.
2385+ calc_safe_reach_states(buchi);
2386+ _reach = std::make_unique<SafeAutStubbornSet>(*net, _progressing_formulae);
2387+
2388+ }
2389+
2390+ void calc_safe_reach_states(const Structures::BuchiAutomaton &buchi) {
2391+ assert(_reach_states.empty());
2392+ std::vector<AtomicProposition> aps(buchi.ap_info.size());
2393+ std::transform(std::begin(buchi.ap_info), std::end(buchi.ap_info), std::begin(aps),
2394+ [](const std::pair<int, AtomicProposition> &pair) { return pair.second; });
2395+ for (unsigned state = 0; state < buchi._buchi->num_states(); ++state) {
2396+ if (buchi._buchi->state_is_accepting(state)) continue;
2397+
2398+ bdd retarding = bddfalse;
2399+ bdd progressing = bddfalse;
2400+ for (auto &e : buchi._buchi->out(state)) {
2401+ if (e.dst == state) {
2402+ retarding = e.cond;
2403+ }
2404+ else {
2405+ progressing |= e.cond;
2406+ }
2407+ }
2408+ if ((retarding | progressing) == bddtrue) {
2409+ _reach_states.insert(std::make_pair(state, BuchiEdge{progressing, toPQL(spot::bdd_to_formula(progressing, buchi.dict), aps)}));
2410+ }
2411+ }
2412+ }
2413+
2414+ void prepare(const LTL::Structures::ProductState *state, typename S::sucinfo &sucinfo) override
2415+ {
2416+ if (auto suc = _reach_states.find(state->getBuchiState()); suc != std::end(_reach_states) && !this->guard_valid(*state, suc->second.bddCond)) {
2417+ (dynamic_cast<PetriEngine::StubbornSet*>(_reach.get()))->setQuery(suc->second.cond.get());
2418+ set_spooler(_reach.get());
2419+ }
2420+ else {
2421+ set_spooler(_fallback_spooler.get());
2422+ }
2423+ ProductSuccessorGenerator<S>::prepare(state, sucinfo);
2424+ }
2425+
2426+ private:
2427+ void set_spooler(SuccessorSpooler *spooler)
2428+ {
2429+ if constexpr (std::is_same_v<S, LTL::SpoolingSuccessorGenerator>)
2430+ this->_successor_generator->setSpooler(spooler);
2431+ else {
2432+ assert(false);
2433+ std::cerr << "Fatal error\n"; exit(1);
2434+ }
2435+ }
2436+
2437+ struct BuchiEdge{
2438+ bdd bddCond;
2439+ PetriEngine::PQL::Condition_ptr cond;
2440+ };
2441+
2442+ std::unique_ptr<Spooler> _fallback_spooler;
2443+ std::unique_ptr<LTL::SuccessorSpooler> _reach;
2444+ std::unordered_map<size_t, BuchiEdge> _reach_states;
2445+ std::vector<PetriEngine::PQL::Condition_ptr> _progressing_formulae;
2446+
2447+ };
2448+}
2449+
2450+#endif //VERIFYPN_REACHSTUBPRODUCTSUCCESSORGENERATOR_H
2451
2452=== modified file 'include/LTL/SuccessorGeneration/ResumingSuccessorGenerator.h'
2453--- include/LTL/SuccessorGeneration/ResumingSuccessorGenerator.h 2021-03-19 11:48:52 +0000
2454+++ include/LTL/SuccessorGeneration/ResumingSuccessorGenerator.h 2021-08-05 10:56:53 +0000
2455@@ -81,14 +81,14 @@
2456 class ResumingSuccessorGenerator : public PetriEngine::SuccessorGenerator {
2457 public:
2458
2459- ResumingSuccessorGenerator(const PetriEngine::PetriNet &net);
2460-
2461- ResumingSuccessorGenerator(const PetriEngine::PetriNet &net, const std::shared_ptr<PetriEngine::StubbornSet> &);
2462-
2463- ResumingSuccessorGenerator(const PetriEngine::PetriNet &net,
2464+ ResumingSuccessorGenerator(const PetriEngine::PetriNet *net);
2465+
2466+ ResumingSuccessorGenerator(const PetriEngine::PetriNet *net, const std::shared_ptr<PetriEngine::StubbornSet> &);
2467+
2468+ ResumingSuccessorGenerator(const PetriEngine::PetriNet *net,
2469 std::vector<std::shared_ptr<PetriEngine::PQL::Condition> > &queries);
2470
2471- ResumingSuccessorGenerator(const PetriEngine::PetriNet &net,
2472+ ResumingSuccessorGenerator(const PetriEngine::PetriNet *net,
2473 const std::shared_ptr<PetriEngine::PQL::Condition> &query);
2474
2475 ~ResumingSuccessorGenerator() override = default;
2476
2477=== modified file 'include/LTL/SuccessorGeneration/Spoolers.h'
2478--- include/LTL/SuccessorGeneration/Spoolers.h 2021-03-31 11:31:54 +0000
2479+++ include/LTL/SuccessorGeneration/Spoolers.h 2021-08-05 10:56:53 +0000
2480@@ -19,5 +19,8 @@
2481 #define VERIFYPN_SPOOLERS_H
2482
2483 #include "EnabledSpooler.h"
2484+#include "LTL/Stubborn/VisibleLTLStubbornSet.h"
2485+#include "LTL/Stubborn/AutomatonStubbornSet.h"
2486+#include "LTL/Stubborn/SafeAutStubbornSet.h"
2487
2488 #endif //VERIFYPN_SPOOLERS_H
2489
2490=== modified file 'include/LTL/SuccessorGeneration/SpoolingSuccessorGenerator.h'
2491--- include/LTL/SuccessorGeneration/SpoolingSuccessorGenerator.h 2021-04-02 09:54:38 +0000
2492+++ include/LTL/SuccessorGeneration/SpoolingSuccessorGenerator.h 2021-08-05 10:56:53 +0000
2493@@ -18,8 +18,10 @@
2494 #ifndef VERIFYPN_SPOOLINGSUCCESSORGENERATOR_H
2495 #define VERIFYPN_SPOOLINGSUCCESSORGENERATOR_H
2496
2497+#include "LTL/Stubborn/AutomatonStubbornSet.h"
2498 #include "LTL/Structures/ProductState.h"
2499 #include "PetriEngine/Structures/SuccessorQueue.h"
2500+#include "LTL/SuccessorGeneration/DistanceHeuristic.h"
2501 #include "LTL/SuccessorGeneration/SuccessorSpooler.h"
2502 #include "LTL/SuccessorGeneration/Heuristics.h"
2503
2504@@ -50,14 +52,14 @@
2505 static constexpr auto NoLastState = std::numeric_limits<size_t>::max();
2506 };
2507
2508- void setSpooler(std::unique_ptr<SuccessorSpooler> &&spooler)
2509+ void setSpooler(SuccessorSpooler *const spooler)
2510 {
2511- _spooler.swap(spooler);
2512+ _spooler = spooler;
2513 }
2514
2515- void setHeuristic(std::unique_ptr<Heuristic> &&heuristic)
2516+ void setHeuristic(Heuristic *const heuristic)
2517 {
2518- _heuristic.swap(heuristic);
2519+ _heuristic = heuristic;
2520 }
2521
2522 [[nodiscard]] static sucinfo initial_suc_info()
2523@@ -96,6 +98,7 @@
2524
2525 } else {
2526 // list of (transition, weight)
2527+ _heuristic->prepare(*state);
2528 std::vector<std::pair<uint32_t, uint32_t>> weighted_tids;
2529 while ((tid = _spooler->next()) != SuccessorSpooler::NoTransition) {
2530 assert(tid <= _net.numberOfTransitions());
2531@@ -106,20 +109,24 @@
2532 // sort by least distance first.
2533 std::sort(std::begin(weighted_tids), std::end(weighted_tids),
2534 [](auto &l, auto &r) { return l.second < r.second; });
2535- sucinfo.successors = SuccessorQueue(weighted_tids.data(), weighted_tids.size(),
2536- [](auto &p) { return p.first; });
2537+ sucinfo.successors = SuccessorQueue(weighted_tids, [](auto &p) { return p.first; });
2538 }
2539 }
2540 }
2541-
2542 bool next(Structures::ProductState &state, sucinfo &sucinfo)
2543 {
2544 assert(sucinfo.successors != nullptr);
2545 if (sucinfo.successors.empty()) {
2546+#ifndef NDEBUG
2547+ //std::cerr << "Not Firing: " << (sucinfo.successors.has_consumed() ? "deadlock" : "done") << std::endl;
2548+#endif
2549 _last = std::numeric_limits<uint32_t>::max();
2550 return false;
2551 }
2552 _last = sucinfo.successors.front();
2553+#ifndef NDEBUG
2554+ //std::cerr << "Firing " << _net.transitionNames()[_last] << std::endl;
2555+#endif
2556 sucinfo.successors.pop();
2557 SuccessorGenerator::_fire(state, _last);
2558 return true;
2559@@ -127,18 +134,82 @@
2560
2561 [[nodiscard]] uint32_t fired() const { return _last; }
2562
2563- void _fire(Structures::ProductState &parent, Structures::ProductState &write, uint32_t tid)
2564+ void generate_all(LTL::Structures::ProductState *parent, sucinfo &sucinfo)
2565 {
2566- PetriEngine::SuccessorGenerator::_fire(write, tid);
2567- write.setBuchiState(parent.getBuchiState());
2568- }
2569-
2570+ assert(_spooler != nullptr);
2571+ assert(sucinfo.successors != nullptr);
2572+ if (!_spooler->generateAll(parent)) return;
2573+ assert(dynamic_cast<VisibleLTLStubbornSet*>(_spooler) != nullptr);
2574+
2575+ uint32_t tid;
2576+ if (!_heuristic) {
2577+ uint32_t nsuc = 0;
2578+ // generate list of transitions that generate a successor.
2579+ auto [first, last] = sucinfo.successors.all_successors();
2580+ for (; first < last; ++first) {
2581+ tid = *first;
2582+ // avoiding duplicates
2583+ if (!static_cast<VisibleLTLStubbornSet*>(_spooler)->stubborn()[tid]) {
2584+ _transbuf[nsuc++] = tid;
2585+ }
2586+ }
2587+ while ((tid = _spooler->next()) != SuccessorSpooler::NoTransition) {
2588+ assert(tid <= _net.numberOfTransitions());
2589+ _transbuf[nsuc++] = tid;
2590+ assert(nsuc <= _net.numberOfTransitions());
2591+ }
2592+ sucinfo.successors.extend_to(_transbuf.get(), nsuc);
2593+
2594+ } else {
2595+ auto evaluate_heuristic = [&] (uint32_t tid) {
2596+ SuccessorGenerator::_fire(_statebuf, tid);
2597+ _statebuf.setBuchiState(sucinfo.buchi_state);
2598+ return _heuristic->eval(_statebuf, tid);
2599+ };
2600+
2601+ // list of (transition, weight)
2602+ std::vector<std::pair<uint32_t, uint32_t>> weighted_tids;
2603+ // grab previous stubborn transitions
2604+ auto [first, last] = sucinfo.successors.all_successors();
2605+ for (; first < last; ++first) {
2606+ tid = *first;
2607+ if (!static_cast<VisibleLTLStubbornSet*>(_spooler)->stubborn()[tid]) {
2608+ weighted_tids.emplace_back(tid, evaluate_heuristic(tid));
2609+ }
2610+ }
2611+
2612+ // add new stubborn transitions
2613+ while ((tid = _spooler->next()) != SuccessorSpooler::NoTransition) {
2614+ assert(tid <= _net.numberOfTransitions());
2615+ weighted_tids.emplace_back(tid, evaluate_heuristic(tid));
2616+ }
2617+ // sort by least distance first.
2618+ std::sort(std::begin(weighted_tids), std::end(weighted_tids),
2619+ [](auto &l, auto &r) { return l.second < r.second; });
2620+ assert(weighted_tids.size() <= _net.numberOfTransitions());
2621+ std::transform(std::begin(weighted_tids), std::end(weighted_tids),
2622+ _transbuf.get(),
2623+ [](auto &p) { return p.first; });
2624+ sucinfo.successors.extend_to(_transbuf.get(), weighted_tids.size());
2625+ }
2626+ }
2627+
2628+ void push() {
2629+ // No transitions have been fired yet. We must be in the initial marking.
2630+ if (!_heuristic || fired() == std::numeric_limits<uint32_t>::max()) return;
2631+ _heuristic->push(fired());
2632+ }
2633+
2634+ void pop(const sucinfo &sc) {
2635+ if (_heuristic && sc.successors.has_consumed())
2636+ _heuristic->pop(sc.successors.last_pop());
2637+ }
2638
2639 private:
2640- std::unique_ptr<SuccessorSpooler> _spooler = nullptr;
2641- std::unique_ptr<Heuristic> _heuristic = nullptr;
2642+ SuccessorSpooler *_spooler = nullptr;
2643+ Heuristic *_heuristic = nullptr;
2644
2645- uint32_t _last;
2646+ uint32_t _last = std::numeric_limits<uint32_t>::max();
2647 std::unique_ptr<uint32_t[]> _transbuf; /* buffer for enabled transitions, size is ntransitions. */
2648 LTL::Structures::ProductState _statebuf;
2649 };
2650
2651=== modified file 'include/LTL/SuccessorGeneration/SuccessorSpooler.h'
2652--- include/LTL/SuccessorGeneration/SuccessorSpooler.h 2021-03-19 11:48:52 +0000
2653+++ include/LTL/SuccessorGeneration/SuccessorSpooler.h 2021-08-05 10:56:53 +0000
2654@@ -34,9 +34,9 @@
2655
2656 }
2657
2658- virtual void generateAll()
2659+ virtual bool generateAll(const LTL::Structures::ProductState *parent)
2660 {
2661-
2662+ return false;
2663 }
2664 static constexpr uint32_t NoTransition = std::numeric_limits<uint32_t>::max();
2665 };
2666
2667=== added file 'include/LTL/SuccessorGeneration/WeightedComposedHeuristic.h'
2668--- include/LTL/SuccessorGeneration/WeightedComposedHeuristic.h 1970-01-01 00:00:00 +0000
2669+++ include/LTL/SuccessorGeneration/WeightedComposedHeuristic.h 2021-08-05 10:56:53 +0000
2670@@ -0,0 +1,55 @@
2671+/* Copyright (C) 2021 Nikolaj J. Ulrik <nikolaj@njulrik.dk>,
2672+ * Simon M. Virenfeldt <simon@simwir.dk>
2673+ *
2674+ * This program is free software: you can redistribute it and/or modify
2675+ * it under the terms of the GNU General Public License as published by
2676+ * the Free Software Foundation, either version 3 of the License, or
2677+ * (at your option) any later version.
2678+ *
2679+ * This program is distributed in the hope that it will be useful,
2680+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
2681+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
2682+ * GNU General Public License for more details.
2683+ *
2684+ * You should have received a copy of the GNU General Public License
2685+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
2686+ */
2687+
2688+#ifndef VERIFYPN_WEIGHTEDCOMPOSEDHEURISTIC_H
2689+#define VERIFYPN_WEIGHTEDCOMPOSEDHEURISTIC_H
2690+
2691+namespace LTL {
2692+
2693+ class WeightedComposedHeuristic: public ComposedHeuristic {
2694+ public:
2695+
2696+ WeightedComposedHeuristic(std::unique_ptr<Heuristic> primary, std::unique_ptr<Heuristic> secondary, uint32_t primary_weight, uint32_t secondary_weight)
2697+ : ComposedHeuristic(std::move(primary), std::move(secondary)), _primary_weight(primary_weight), _secondary_weight(secondary_weight) {}
2698+
2699+ uint32_t eval(const Structures::ProductState &state, uint32_t tid) override {
2700+ if (_primary_has_heuristic && !_secondary_has_heuristic) {
2701+ return _primary->eval(state, tid);
2702+ } else if (!_primary_has_heuristic && _secondary_has_heuristic) {
2703+ return _secondary->eval(state, tid);
2704+ } else if (_primary_has_heuristic && _secondary_has_heuristic) {
2705+ return _primary->eval(state, tid) * _primary_weight + _secondary->eval(state, tid) * _secondary_weight;
2706+ } else {
2707+ return 0;
2708+ }
2709+ }
2710+
2711+
2712+ std::ostream &output(std::ostream &os) {
2713+ os << "WEIGHTED(" << _primary_weight << "*";
2714+ _primary->output(os) << ",";
2715+ os << _secondary_weight << "*";
2716+ _secondary->output(os) << ")";
2717+ return os;
2718+ }
2719+ private:
2720+ uint32_t _primary_weight, _secondary_weight;
2721+ };
2722+
2723+}
2724+
2725+#endif //VERIFYPN_WEIGHTEDCOMPOSEDHEURISTIC_H
2726
2727=== modified file 'include/PetriEngine/PQL/Visitor.h'
2728--- include/PetriEngine/PQL/Visitor.h 2021-03-31 11:31:54 +0000
2729+++ include/PetriEngine/PQL/Visitor.h 2021-08-05 10:56:53 +0000
2730@@ -26,29 +26,20 @@
2731 _accept(element);
2732 }
2733
2734+ //virtual ~Visitor() = default;
2735+
2736 protected:
2737-
2738- virtual void _accept(const NotCondition *element) = 0;
2739-
2740- virtual void _accept(const AndCondition *element) = 0;
2741-
2742- virtual void _accept(const OrCondition *element) = 0;
2743-
2744- virtual void _accept(const LessThanCondition *element) = 0;
2745-
2746- virtual void _accept(const LessThanOrEqualCondition *element) = 0;
2747-
2748-
2749-
2750- virtual void _accept(const EqualCondition *element) = 0;
2751-
2752- virtual void _accept(const NotEqualCondition *element) = 0;
2753-
2754- virtual void _accept(const DeadlockCondition *element) = 0;
2755-
2756- virtual void _accept(const CompareConjunction *element) = 0;
2757-
2758- virtual void _accept(const UnfoldedUpperBoundsCondition *element) = 0;
2759+ virtual void _accept(const NotCondition* element) = 0;
2760+ virtual void _accept(const AndCondition* element) = 0;
2761+ virtual void _accept(const OrCondition* element) = 0;
2762+ virtual void _accept(const LessThanCondition* element) = 0;
2763+ virtual void _accept(const LessThanOrEqualCondition* element) = 0;
2764+ virtual void _accept(const EqualCondition* element) = 0;
2765+ virtual void _accept(const NotEqualCondition* element) = 0;
2766+
2767+ virtual void _accept(const DeadlockCondition* element) = 0;
2768+ virtual void _accept(const CompareConjunction* element) = 0;
2769+ virtual void _accept(const UnfoldedUpperBoundsCondition* element) = 0;
2770
2771 // Quantifiers, most uses of the visitor will not use the quantifiers - so we give a default implementation.
2772 // default behaviour is error
2773
2774=== modified file 'include/PetriEngine/Reachability/ReachabilitySearch.h'
2775--- include/PetriEngine/Reachability/ReachabilitySearch.h 2021-04-07 08:50:10 +0000
2776+++ include/PetriEngine/Reachability/ReachabilitySearch.h 2021-08-05 10:56:53 +0000
2777@@ -104,7 +104,9 @@
2778 }
2779 template <>
2780 inline ReducingSuccessorGenerator _makeSucGen(PetriNet &net, std::vector<PQL::Condition_ptr> &queries) {
2781- return ReducingSuccessorGenerator{net, std::make_shared<ReachabilityStubbornSet>(net, queries)};
2782+ auto stubset = std::make_shared<ReachabilityStubbornSet>(net, queries);
2783+ stubset->setInterestingVisitor<InterestingTransitionVisitor>();
2784+ return ReducingSuccessorGenerator{net, stubset};
2785 }
2786
2787 template<typename Q, typename W, typename G>
2788
2789=== modified file 'include/PetriEngine/Reducer.h'
2790--- include/PetriEngine/Reducer.h 2021-07-05 19:55:24 +0000
2791+++ include/PetriEngine/Reducer.h 2021-08-05 10:56:53 +0000
2792@@ -14,6 +14,7 @@
2793 #include "NetStructures.h"
2794
2795 #include <vector>
2796+#include <optional>
2797
2798 namespace PetriEngine {
2799
2800
2801=== modified file 'include/PetriEngine/Structures/SuccessorQueue.h'
2802--- include/PetriEngine/Structures/SuccessorQueue.h 2021-03-19 13:48:21 +0000
2803+++ include/PetriEngine/Structures/SuccessorQueue.h 2021-08-05 10:56:53 +0000
2804@@ -39,11 +39,11 @@
2805
2806 // construct from array of different type, using fn as transformation function.
2807 template <typename U, typename Fn>
2808- SuccessorQueue(U *src, uint32_t nelem, Fn&& fn)
2809- : _front(0), _size(nelem)
2810+ SuccessorQueue(std::vector<U> &src, Fn&& fn)
2811+ : _front(0), _size(src.size())
2812 {
2813- _data = std::make_unique<T[]>(nelem);
2814- std::transform(src, src + nelem, _data.get(), fn);
2815+ _data = std::make_unique<T[]>(src.size());
2816+ std::transform(std::begin(src), std::end(src), _data.get(), fn);
2817 }
2818
2819 SuccessorQueue() noexcept :_front(0), _size(0), _data(nullptr) {}
2820@@ -60,7 +60,7 @@
2821 ++_front;
2822 }
2823
2824- [[nodiscard]] T size() const
2825+ [[nodiscard]] size_t size() const
2826 {
2827 return _size - _front;
2828 }
2829@@ -74,6 +74,11 @@
2830
2831 [[nodiscard]] bool has_consumed() const { return _front > 0; }
2832
2833+ T last_pop() const {
2834+ assert(has_consumed());
2835+ return _data[_front - 1];
2836+ }
2837+
2838 bool operator==(std::nullptr_t) { return _data == nullptr; }
2839 bool operator!=(std::nullptr_t) { return _data != nullptr; }
2840
2841@@ -84,6 +89,7 @@
2842 */
2843 void extend_to(T *src, uint32_t nelem)
2844 {
2845+ assert(nelem >= _size); // Cannot extend to fewer elements.
2846 auto newdata = std::make_unique<T[]>(nelem);
2847 if (_front != 0) {
2848 //Add previously fired transitions to start of array.
2849@@ -92,7 +98,6 @@
2850 uint32_t sz = _front;
2851 // copy over extended successor list, excluding previously popped elements.
2852 for (uint32_t i = 0; i < nelem; ++i) {
2853- // FIXME potential optimization target if poor performance is found.
2854 auto begin = _data.get(), end = _data.get() + _front;
2855 auto it = std::find(begin, end, src[i]);
2856
2857@@ -107,6 +112,18 @@
2858 _data.swap(newdata);
2859 }
2860
2861+ void append(T *src, uint32_t nelem) {
2862+ auto newdata = std::make_unique<T[]>(nelem + _size);
2863+ memcpy(newdata.get(), _data.get(), sizeof(T) * _size);
2864+ memcpy(newdata.get() + _size, src, sizeof(T) * nelem);
2865+ _size = nelem + _size;
2866+ _data.swap(newdata);
2867+ }
2868+
2869+ std::pair<T*, T*> all_successors() {
2870+ return std::make_pair(_data.get(), &_data[_size]);
2871+ }
2872+
2873 private:
2874 uint32_t _front; /* index of first element */
2875 uint32_t _size; /* size of data array */
2876
2877=== modified file 'include/PetriEngine/Stubborn/InterestingTransitionVisitor.h'
2878--- include/PetriEngine/Stubborn/InterestingTransitionVisitor.h 2021-04-06 08:36:53 +0000
2879+++ include/PetriEngine/Stubborn/InterestingTransitionVisitor.h 2021-08-05 10:56:53 +0000
2880@@ -33,12 +33,13 @@
2881
2882 void negate() { negated = !negated; }
2883
2884- private:
2885+ bool get_negated() const { return negated; }
2886+
2887+ protected:
2888 PetriEngine::StubbornSet &_stubborn;
2889
2890 bool closure;
2891
2892- protected:
2893 void _accept(const PQL::NotCondition *element) override;
2894
2895 void _accept(const PQL::AndCondition *element) override;
2896@@ -222,6 +223,13 @@
2897 void negate_if_satisfied(const Condition *element);
2898 };
2899
2900+ class AutomatonInterestingTransitionVisitor : public InterestingTransitionVisitor {
2901+ public:
2902+ explicit AutomatonInterestingTransitionVisitor(StubbornSet &stubbornSet, bool closure) : InterestingTransitionVisitor(stubbornSet, closure) {}
2903+
2904+ protected:
2905+ void _accept(const PQL::CompareConjunction *element) override;
2906+ };
2907 }
2908
2909
2910
2911=== modified file 'include/PetriEngine/Stubborn/ReachabilityStubbornSet.h'
2912--- include/PetriEngine/Stubborn/ReachabilityStubbornSet.h 2021-03-23 14:10:04 +0000
2913+++ include/PetriEngine/Stubborn/ReachabilityStubbornSet.h 2021-08-05 10:56:53 +0000
2914@@ -20,17 +20,33 @@
2915
2916
2917 #include "PetriEngine/Stubborn/StubbornSet.h"
2918+#include "InterestingTransitionVisitor.h"
2919
2920 namespace PetriEngine {
2921 class ReachabilityStubbornSet : public StubbornSet {
2922 public:
2923- ReachabilityStubbornSet(const PetriNet &net, const std::vector<PQL::Condition_ptr> &queries)
2924- : StubbornSet(net, queries) {}
2925+ ReachabilityStubbornSet(const PetriNet &net, const std::vector<PQL::Condition_ptr> &queries, bool closure = true)
2926+ : StubbornSet(net, queries), _closure(closure) {
2927+ setInterestingVisitor<InterestingTransitionVisitor>();
2928+ }
2929
2930- ReachabilityStubbornSet(const PetriNet &net)
2931- : StubbornSet(net) {}
2932+ ReachabilityStubbornSet(const PetriNet &net, bool closure = true)
2933+ : StubbornSet(net) , _closure(closure) {
2934+ setInterestingVisitor<InterestingTransitionVisitor>();
2935+ }
2936
2937 bool prepare(const Structures::State *state) override;
2938+
2939+ template <typename TVisitor>
2940+ void setInterestingVisitor()
2941+ {
2942+ _interesting = std::make_unique<TVisitor>(*this, _closure);
2943+ }
2944+
2945+ private:
2946+ std::unique_ptr<InterestingTransitionVisitor> _interesting;
2947+
2948+ bool _closure;
2949 };
2950 }
2951
2952
2953=== modified file 'include/PetriEngine/Stubborn/StubbornSet.h'
2954--- include/PetriEngine/Stubborn/StubbornSet.h 2021-03-23 14:10:04 +0000
2955+++ include/PetriEngine/Stubborn/StubbornSet.h 2021-08-05 10:56:53 +0000
2956@@ -92,13 +92,24 @@
2957 _queries = {ptr};
2958 }
2959
2960+ void setQueries(std::vector<PQL::Condition*> conds) {
2961+ _queries = conds;
2962+ }
2963+
2964 [[nodiscard]] size_t nenabled() const { return _nenabled; }
2965
2966 [[nodiscard]] bool *enabled() const { return _enabled.get(); };
2967 [[nodiscard]] bool *stubborn() const { return _stubborn.get(); };
2968
2969- protected:
2970 const PetriEngine::PetriNet &_net;
2971+
2972+ // Bit flags for _places_seen array.
2973+ static constexpr auto PresetSeen = 1;
2974+ static constexpr auto PostsetSeen = 2;
2975+ static constexpr auto InhibPostsetSeen = 4;
2976+ static constexpr auto PresetBad = 8;
2977+ static constexpr auto PostsetBad = 16;
2978+ protected:
2979 const Structures::State *_parent;
2980
2981 struct place_t {
2982@@ -128,18 +139,73 @@
2983
2984 virtual void addToStub(uint32_t t);
2985
2986- void closure();
2987+ template <typename T = std::nullptr_t>
2988+ void closure(T callback = nullptr) {
2989+ while (!_unprocessed.empty()) {
2990+ if constexpr (!std::is_null_pointer_v<T>) {
2991+ if (!callback()) return;
2992+ }
2993+ uint32_t tr = _unprocessed.front();
2994+ _unprocessed.pop_front();
2995+ const TransPtr &ptr = transitions()[tr];
2996+ uint32_t finv = ptr.inputs;
2997+ uint32_t linv = ptr.outputs;
2998+ if (_enabled[tr]) {
2999+ for (; finv < linv; finv++) {
3000+ if (invariants()[finv].direction < 0) {
3001+ auto place = invariants()[finv].place;
3002+ for (uint32_t t = _places.get()[place].post; t < _places.get()[place + 1].pre; t++)
3003+ addToStub(_transitions.get()[t].index);
3004+ }
3005+ }
3006+ if (_netContainsInhibitorArcs) {
3007+ uint32_t next_finv = transitions()[tr + 1].inputs;
3008+ for (; linv < next_finv; linv++) {
3009+ if (invariants()[linv].direction > 0)
3010+ inhibitorPostsetOf(invariants()[linv].place);
3011+ }
3012+ }
3013+ } else {
3014+ bool ok = false;
3015+ bool inhib = false;
3016+ uint32_t cand = std::numeric_limits<uint32_t>::max();
3017+
3018+ // Lets try to see if we havent already added sufficient pre/post
3019+ // for this transition.
3020+ for (; finv < linv; ++finv) {
3021+ const Invariant &inv = invariants()[finv];
3022+ if ((*_parent).marking()[inv.place] < inv.tokens && !inv.inhibitor) {
3023+ inhib = false;
3024+ ok = (_places_seen.get()[inv.place] & 1) != 0;
3025+ cand = inv.place;
3026+ } else if ((*_parent).marking()[inv.place] >= inv.tokens && inv.inhibitor) {
3027+ inhib = true;
3028+ ok = (_places_seen.get()[inv.place] & 2) != 0;
3029+ cand = inv.place;
3030+ }
3031+ if (ok) break;
3032+
3033+ }
3034+
3035+ // OK, we didnt have sufficient, we just pick whatever is left
3036+ // in cand.
3037+ assert(cand != std::numeric_limits<uint32_t>::max());
3038+ if (!ok && cand != std::numeric_limits<uint32_t>::max()) {
3039+ if (!inhib) presetOf(cand);
3040+ else postsetOf(cand);
3041+ }
3042+ }
3043+ }
3044+ }
3045
3046 std::unique_ptr<bool[]> _enabled, _stubborn;
3047 size_t _nenabled;
3048-
3049- protected:
3050 std::unique_ptr<uint8_t[]> _places_seen;
3051 std::unique_ptr<place_t[]> _places;
3052 std::unique_ptr<trans_t[]> _transitions;
3053 light_deque<uint32_t> _unprocessed, _ordering;
3054 std::unique_ptr<uint32_t[]> _dependency;
3055- bool _netContainsInhibitorArcs;
3056+ bool _netContainsInhibitorArcs, _done;
3057 std::vector<std::vector<uint32_t>> _inhibpost;
3058
3059 std::vector<PQL::Condition *> _queries;
3060@@ -151,6 +217,11 @@
3061 void constructDependency();
3062
3063 void checkForInhibitor();
3064+
3065+ void set_all_stubborn() {
3066+ memset(_stubborn.get(), true, _net.numberOfTransitions());
3067+ _done = true;
3068+ }
3069 };
3070 }
3071
3072
3073=== modified file 'include/PetriEngine/options.h'
3074--- include/PetriEngine/options.h 2021-05-24 13:20:23 +0000
3075+++ include/PetriEngine/options.h 2021-08-05 10:56:53 +0000
3076@@ -16,6 +16,7 @@
3077 CTL, LTL
3078 };
3079
3080+//TODO can be moved to LTL/AlgorithmTypes.h?
3081 enum class TraceLevel {
3082 None,
3083 Transitions,
3084@@ -28,6 +29,20 @@
3085 Full
3086 };
3087
3088+enum class LTLPartialOrder {
3089+ None,
3090+ Visible,
3091+ AutomatonReach,
3092+ VisibleReach,
3093+ FullAutomaton
3094+};
3095+
3096+enum class BuchiOptimization {
3097+ Low = 1,
3098+ Medium = 2,
3099+ High = 3
3100+};
3101+
3102 struct options_t {
3103 // bool outputtrace = false;
3104 int kbound = 0;
3105@@ -66,6 +81,13 @@
3106 std::string buchi_out_file;
3107 LTL::BuchiOutType buchi_out_type = LTL::BuchiOutType::Dot;
3108 APCompression ltl_compress_aps = APCompression::None;
3109+ LTLPartialOrder ltl_por = LTLPartialOrder::VisibleReach;
3110+ BuchiOptimization buchiOptimization = BuchiOptimization::Low;
3111+ const char* ltlHeuristic = "aut";
3112+
3113+ bool replay = false;
3114+ std::string replay_file;
3115+
3116
3117 std::string query_out_file;
3118 std::string model_out_file;
3119@@ -82,7 +104,6 @@
3120 uint32_t seed_offset = 0;
3121 int max_intervals = 250; //0 disabled
3122 int max_intervals_reduced = 5;
3123-
3124 size_t seed() { return ++seed_offset; }
3125
3126 void print() {
3127
3128=== modified file 'src/LTL/Algorithm/CMakeLists.txt'
3129--- src/LTL/Algorithm/CMakeLists.txt 2021-04-06 13:25:35 +0000
3130+++ src/LTL/Algorithm/CMakeLists.txt 2021-08-05 10:56:53 +0000
3131@@ -1,7 +1,7 @@
3132 set(CMAKE_INCLUDE_CURRENT_DIR ON)
3133
3134 add_library(LTL_algorithm ${HEADER_FILES}
3135- NestedDepthFirstSearch.cpp LTLToBuchi.cpp TarjanModelChecker.cpp RandomNDFS.cpp ModelChecker.cpp)
3136+ NestedDepthFirstSearch.cpp LTLToBuchi.cpp TarjanModelChecker.cpp)
3137
3138 target_link_libraries(LTL_algorithm PetriEngine)
3139 add_dependencies(LTL_algorithm ptrie-ext spot-ext)
3140
3141=== modified file 'src/LTL/Algorithm/LTLToBuchi.cpp'
3142--- src/LTL/Algorithm/LTLToBuchi.cpp 2021-04-07 07:18:00 +0000
3143+++ src/LTL/Algorithm/LTLToBuchi.cpp 2021-08-05 10:56:53 +0000
3144@@ -17,14 +17,13 @@
3145
3146 #include "LTL/LTLToBuchi.h"
3147 #include "LTL/SuccessorGeneration/BuchiSuccessorGenerator.h"
3148+#include "PetriEngine/options.h"
3149
3150 #include <spot/twaalgos/translate.hh>
3151 #include <spot/tl/parse.hh>
3152 #include <spot/twa/bddprint.hh>
3153 #include <sstream>
3154 #include <spot/twaalgos/dot.hh>
3155-#include <PetriEngine/options.h>
3156-
3157
3158 using namespace PetriEngine::PQL;
3159
3160@@ -130,9 +129,10 @@
3161 (*condition)[0]->visit(*this);
3162 }
3163
3164- std::pair<spot::formula, APInfo> to_spot_formula(const PetriEngine::PQL::Condition_ptr& query, APCompression compress) {
3165+ std::pair<spot::formula, APInfo>
3166+ to_spot_formula (const PetriEngine::PQL::Condition_ptr &query, const options_t &options) {
3167 std::stringstream ss;
3168- FormulaToSpotSyntax spotConverter{ss, compress};
3169+ FormulaToSpotSyntax spotConverter{ss, options.ltl_compress_aps};
3170 query->visit(spotConverter);
3171 std::string spotFormula = ss.str();
3172 if (spotFormula.at(0) == 'E' || spotFormula.at(0) == 'A') {
3173@@ -142,14 +142,26 @@
3174 return std::make_pair(spot_formula, spotConverter.apInfo());
3175 }
3176
3177- Structures::BuchiAutomaton makeBuchiAutomaton(const PetriEngine::PQL::Condition_ptr &query, APCompression compress) {
3178- auto [formula, apinfo] = to_spot_formula(query, compress);
3179+ Structures::BuchiAutomaton makeBuchiAutomaton(const PetriEngine::PQL::Condition_ptr &query, const options_t &options) {
3180+ auto [formula, apinfo] = to_spot_formula(query, options);
3181 formula = spot::formula::Not(formula);
3182 spot::translator translator;
3183 // Ask for Büchi acceptance (rather than generalized Büchi) and medium optimizations
3184 // (default is high which causes many worst case BDD constructions i.e. exponential blow-up)
3185 translator.set_type(spot::postprocessor::BA);
3186- translator.set_level(spot::postprocessor::Low);
3187+ spot::postprocessor::optimization_level level;
3188+ switch(options.buchiOptimization) {
3189+ case BuchiOptimization::Low:
3190+ level = spot::postprocessor::Low;
3191+ break;
3192+ case BuchiOptimization::Medium:
3193+ level = spot::postprocessor::Medium;
3194+ break;
3195+ case BuchiOptimization::High:
3196+ level = spot::postprocessor::High;
3197+ break;
3198+ }
3199+ translator.set_level(level);
3200 spot::twa_graph_ptr automaton = translator.run(formula);
3201 std::unordered_map<int, AtomicProposition> ap_map;
3202 // bind PQL expressions to the atomic proposition IDs used by spot.
3203@@ -162,8 +174,8 @@
3204 return Structures::BuchiAutomaton{automaton, ap_map};
3205 }
3206
3207- BuchiSuccessorGenerator makeBuchiSuccessorGenerator(const Condition_ptr &query, APCompression compress) {
3208- return BuchiSuccessorGenerator{makeBuchiAutomaton(query, compress)};
3209+ BuchiSuccessorGenerator makeBuchiSuccessorGenerator(const Condition_ptr &query, const options_t &options) {
3210+ return BuchiSuccessorGenerator{makeBuchiAutomaton(query, options)};
3211 }
3212
3213 }
3214
3215=== removed file 'src/LTL/Algorithm/ModelChecker.cpp'
3216=== modified file 'src/LTL/Algorithm/NestedDepthFirstSearch.cpp'
3217--- src/LTL/Algorithm/NestedDepthFirstSearch.cpp 2021-04-02 09:54:38 +0000
3218+++ src/LTL/Algorithm/NestedDepthFirstSearch.cpp 2021-08-05 10:56:53 +0000
3219@@ -18,16 +18,16 @@
3220 #include "LTL/Algorithm/NestedDepthFirstSearch.h"
3221
3222 namespace LTL {
3223- template<typename W>
3224- bool NestedDepthFirstSearch<W>::isSatisfied()
3225+ template<typename S, typename W>
3226+ bool NestedDepthFirstSearch<S, W>::isSatisfied()
3227 {
3228- is_weak = successorGenerator->is_weak() && shortcircuitweak;
3229+ this->is_weak = this->successorGenerator->is_weak() && this->shortcircuitweak;
3230 dfs();
3231 return !violation;
3232 }
3233
3234- template<typename W>
3235- void NestedDepthFirstSearch<W>::dfs()
3236+ template<typename S, typename W>
3237+ void NestedDepthFirstSearch<S, W>::dfs()
3238 {
3239 std::stack<size_t> call_stack;
3240 std::stack<StackEntry> todo;
3241@@ -36,26 +36,27 @@
3242 State curState = factory.newState();
3243
3244 {
3245- std::vector<State> initial_states = successorGenerator->makeInitialState();
3246+
3247+ std::vector<State> initial_states = this->successorGenerator->makeInitialState();
3248 for (auto &state : initial_states) {
3249 auto res = states.add(state);
3250 assert(res.first);
3251- todo.push(StackEntry{res.second, LTL::ResumingSuccessorGenerator::initial_suc_info()});
3252- _discovered++;
3253+ todo.push(StackEntry{res.second, S::initial_suc_info()});
3254+ this->_discovered++;
3255 }
3256 }
3257
3258 while (!todo.empty()) {
3259 auto &top = todo.top();
3260 states.decode(curState, top.id);
3261- successorGenerator->prepare(&curState, top.sucinfo);
3262+ this->successorGenerator->prepare(&curState, top.sucinfo);
3263 if (top.sucinfo.has_prev_state()) {
3264 states.decode(working, top.sucinfo.last_state);
3265 }
3266- if (!successorGenerator->next(working, top.sucinfo)) {
3267+ if (!this->successorGenerator->next(working, top.sucinfo)) {
3268 // no successor
3269 todo.pop();
3270- if (successorGenerator->isAccepting(curState)) {
3271+ if (this->successorGenerator->isAccepting(curState)) {
3272 seed = &curState;
3273 ndfs(curState);
3274 if (violation) {
3275@@ -63,9 +64,9 @@
3276 std::stack<std::pair<size_t, size_t>> transitions;
3277 size_t next = top.id;
3278 states.decode(working, next);
3279- while (!successorGenerator->isInitialState(working)) {
3280+ while (!this->successorGenerator->isInitialState(working)) {
3281 auto[parent, transition] = states.getHistory(next);
3282- transitions.push(std::make_pair(next, transition));
3283+ transitions.push(std::make_pair(parent, transition));
3284 next = parent;
3285 states.decode(working, next);
3286 }
3287@@ -81,36 +82,36 @@
3288 if (is_new) {
3289 if constexpr (SaveTrace) {
3290 states.setParent(top.id);
3291- states.setHistory(stateid, successorGenerator->fired());
3292+ states.setHistory(stateid, this->successorGenerator->fired());
3293 }
3294- _discovered++;
3295- todo.push(StackEntry{stateid, LTL::ResumingSuccessorGenerator::initial_suc_info()});
3296+ this->_discovered++;
3297+ todo.push(StackEntry{stateid, S::initial_suc_info()});
3298 }
3299 }
3300 }
3301 }
3302
3303- template<typename W>
3304- void NestedDepthFirstSearch<W>::ndfs(State &state)
3305+ template<typename S, typename W>
3306+ void NestedDepthFirstSearch<S, W>::ndfs(State &state)
3307 {
3308 std::stack<StackEntry> todo;
3309
3310 State working = factory.newState();
3311 State curState = factory.newState();
3312
3313- todo.push(StackEntry{states.add(state).second, LTL::ResumingSuccessorGenerator::initial_suc_info()});
3314+ todo.push(StackEntry{states.add(state).second, S::initial_suc_info()});
3315
3316 while (!todo.empty()) {
3317 auto &top = todo.top();
3318 states.decode(curState, top.id);
3319- successorGenerator->prepare(&curState, top.sucinfo);
3320+ this->successorGenerator->prepare(&curState, top.sucinfo);
3321 if (top.sucinfo.has_prev_state()) {
3322 states.decode(working, top.sucinfo.last_state);
3323 }
3324- if (!successorGenerator->next(working, top.sucinfo)) {
3325+ if (!this->successorGenerator->next(working, top.sucinfo)) {
3326 todo.pop();
3327 } else {
3328- if (is_weak && !successorGenerator->isAccepting(working)) {
3329+ if (this->is_weak && !this->successorGenerator->isAccepting(working)) {
3330 continue;
3331 }
3332 if (working == *seed) {
3333@@ -118,12 +119,12 @@
3334 if constexpr (SaveTrace) {
3335 auto[_, stateid] = states.add(working);
3336 auto seedId = stateid;
3337- states.setHistory2(stateid, successorGenerator->fired());
3338+ states.setHistory2(stateid, this->successorGenerator->fired());
3339 size_t next = stateid;
3340 // follow trace until back at seed state.
3341 do {
3342 auto[state, transition] = states.getHistory2(next);
3343- nested_transitions.push(std::make_pair(next, transition));
3344+ nested_transitions.push(std::make_pair(state, transition));
3345 next = state;
3346 } while (next != seedId);
3347 }
3348@@ -135,18 +136,18 @@
3349 if (is_new) {
3350 if constexpr (SaveTrace) {
3351 states.setParent(top.id);
3352- states.setHistory2(stateid, successorGenerator->fired());
3353+ states.setHistory2(stateid, this->successorGenerator->fired());
3354 }
3355- _discovered++;
3356- todo.push(StackEntry{stateid, LTL::ResumingSuccessorGenerator::initial_suc_info()});
3357+ this->_discovered++;
3358+ todo.push(StackEntry{stateid, S::initial_suc_info()});
3359 }
3360
3361 }
3362 }
3363 }
3364
3365- template<typename W>
3366- void NestedDepthFirstSearch<W>::printStats(std::ostream &os)
3367+ template<typename S, typename W>
3368+ void NestedDepthFirstSearch<S, W>::printStats(std::ostream &os)
3369 {
3370 std::cout << "STATS:\n"
3371 << "\tdiscovered states: " << states.discovered() << std::endl
3372@@ -156,8 +157,8 @@
3373 }
3374
3375
3376- template<typename W>
3377- void NestedDepthFirstSearch<W>::printTrace(std::stack<std::pair<size_t, size_t>> &transitions, std::ostream &os)
3378+ template<typename S, typename W>
3379+ void NestedDepthFirstSearch<S, W>::printTrace(std::stack<std::pair<size_t, size_t>> &transitions, std::ostream &os)
3380 {
3381 State state = factory.newState();
3382 os << "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n"
3383@@ -165,23 +166,29 @@
3384 while (!transitions.empty()) {
3385 auto[stateid, transition] = transitions.top();
3386 states.decode(state, stateid);
3387- printTransition(transition, state, os) << std::endl;
3388+ this->printTransition(transition, state, os) << std::endl;
3389 transitions.pop();
3390 }
3391- printLoop(os);
3392+ this->printLoop(os);
3393 while (!nested_transitions.empty()) {
3394 auto[stateid, transition] = nested_transitions.top();
3395 states.decode(state, stateid);
3396
3397- printTransition(transition, state, os) << std::endl;
3398+ this->printTransition(transition, state, os) << std::endl;
3399 nested_transitions.pop();
3400 }
3401 os << std::endl << "</trace>" << std::endl;
3402 }
3403
3404 template
3405- class NestedDepthFirstSearch<PetriEngine::Structures::StateSet>;
3406-
3407- template
3408- class NestedDepthFirstSearch<PetriEngine::Structures::TracableStateSet>;
3409-}
3410\ No newline at end of file
3411+ class NestedDepthFirstSearch<LTL::ResumingSuccessorGenerator, PetriEngine::Structures::StateSet>;
3412+
3413+ template
3414+ class NestedDepthFirstSearch<LTL::ResumingSuccessorGenerator, PetriEngine::Structures::TracableStateSet>;
3415+
3416+ template
3417+ class NestedDepthFirstSearch<LTL::SpoolingSuccessorGenerator, PetriEngine::Structures::StateSet>;
3418+
3419+ template
3420+ class NestedDepthFirstSearch<LTL::SpoolingSuccessorGenerator, PetriEngine::Structures::TracableStateSet>;
3421+}
3422
3423=== removed file 'src/LTL/Algorithm/RandomNDFS.cpp'
3424=== modified file 'src/LTL/Algorithm/TarjanModelChecker.cpp'
3425--- src/LTL/Algorithm/TarjanModelChecker.cpp 2021-04-02 09:54:38 +0000
3426+++ src/LTL/Algorithm/TarjanModelChecker.cpp 2021-08-05 10:56:53 +0000
3427@@ -19,8 +19,8 @@
3428
3429 namespace LTL {
3430
3431- template<typename S, bool SaveTrace>
3432- bool TarjanModelChecker<S, SaveTrace>::isSatisfied()
3433+ template<template<typename, typename...> typename S, typename G, bool SaveTrace, typename... Spooler>
3434+ bool TarjanModelChecker<S, G, SaveTrace, Spooler...>::isSatisfied()
3435 {
3436 this->is_weak = this->successorGenerator->is_weak() && this->shortcircuitweak;
3437 std::vector<State> initial_states = this->successorGenerator->makeInitialState();
3438@@ -47,14 +47,26 @@
3439 seen.setHistory(stateid, this->successorGenerator->fired());
3440 }
3441 }
3442+
3443 dtop.sucinfo.last_state = stateid;
3444
3445 // lookup successor in 'hash' table
3446 auto suc_pos = chash[hash(stateid)];
3447+ auto marking = seen.getMarkingId(stateid);
3448 while (suc_pos != std::numeric_limits<idx_t>::max() && cstack[suc_pos].stateid != stateid) {
3449+ if constexpr (IsSpooling) {
3450+ if (cstack[suc_pos].dstack && seen.getMarkingId(cstack[suc_pos].stateid) == marking) {
3451+ this->successorGenerator->generateAll(&parent, dtop.sucinfo);
3452+ }
3453+ }
3454 suc_pos = cstack[suc_pos].next;
3455 }
3456 if (suc_pos != std::numeric_limits<idx_t>::max()) {
3457+ if constexpr (IsSpooling) {
3458+ if (cstack[suc_pos].dstack) {
3459+ this->successorGenerator->generateAll(&parent, dtop.sucinfo);
3460+ }
3461+ }
3462 // we found the successor, i.e. there's a loop!
3463 // now update lowlinks and check whether the loop contains an accepting state
3464 update(suc_pos);
3465@@ -84,8 +96,8 @@
3466 * Push a state to the various stacks.
3467 * @param state
3468 */
3469- template<typename S, bool SaveTrace>
3470- void TarjanModelChecker<S, SaveTrace>::push(State &state, size_t stateid) {
3471+ template<template<typename, typename...> typename S, typename G, bool SaveTrace, typename... Spooler>
3472+ void TarjanModelChecker<S, G, SaveTrace, Spooler...>::push(State &state, size_t stateid) {
3473 const auto ctop = static_cast<idx_t>(cstack.size());
3474 const auto h = hash(stateid);
3475 cstack.emplace_back(ctop, stateid, chash[h]);
3476@@ -93,19 +105,23 @@
3477 dstack.push(DEntry{ctop});
3478 if (this->successorGenerator->isAccepting(state)) {
3479 astack.push(ctop);
3480- if (this->successorGenerator->has_invariant_self_loop(state)){
3481+ if (this->successorGenerator->has_invariant_self_loop(state) && !SaveTrace){
3482 //std::cerr << "Invariant self loop found. Violation is true" << std::endl;
3483 violation = true;
3484 invariant_loop = true;
3485 }
3486 }
3487+ if constexpr (IsSpooling) {
3488+ this->successorGenerator->push();
3489+ }
3490 }
3491
3492- template<typename S, bool SaveTrace>
3493- void TarjanModelChecker<S, SaveTrace>::pop()
3494+ template<template<typename, typename...> typename S, typename G, bool SaveTrace, typename... Spooler>
3495+ void TarjanModelChecker<S, G, SaveTrace, Spooler...>::pop()
3496 {
3497 const auto p = dstack.top().pos;
3498 dstack.pop();
3499+ cstack[p].dstack = false;
3500 if (cstack[p].lowlink == p) {
3501 while (cstack.size() > p) {
3502 popCStack();
3503@@ -122,11 +138,14 @@
3504 }
3505 if (!dstack.empty()) {
3506 update(p);
3507+ if constexpr (IsSpooling) {
3508+ this->successorGenerator->pop(dstack.top().sucinfo);
3509+ }
3510 }
3511 }
3512
3513- template<typename S, bool SaveTrace>
3514- void TarjanModelChecker<S, SaveTrace>::popCStack()
3515+ template<template<typename, typename...> typename S, typename G, bool SaveTrace, typename... Spooler>
3516+ void TarjanModelChecker<S, G, SaveTrace, Spooler...>::popCStack()
3517 {
3518 auto h = hash(cstack.back().stateid);
3519 store.insert(cstack.back().stateid);
3520@@ -134,10 +153,11 @@
3521 cstack.pop_back();
3522 }
3523
3524- template<typename S, bool SaveTrace>
3525- void TarjanModelChecker<S, SaveTrace>::update(idx_t to)
3526+ template<template<typename, typename...> typename S, typename G, bool SaveTrace, typename... Spooler>
3527+ void TarjanModelChecker<S, G, SaveTrace, Spooler...>::update(idx_t to)
3528 {
3529 const auto from = dstack.top().pos;
3530+ assert(cstack[to].lowlink != std::numeric_limits<idx_t>::max() && cstack[from].lowlink != std::numeric_limits<idx_t>::max());
3531 if (cstack[to].lowlink <= cstack[from].lowlink) {
3532 // we have now found a loop into earlier seen component cstack[to].lowlink.
3533 // if this earlier component precedes an accepting state,
3534@@ -146,7 +166,7 @@
3535 // either way update the component ID of the state we came from.
3536 cstack[from].lowlink = cstack[to].lowlink;
3537 if constexpr (SaveTrace) {
3538- loopstate = cstack[to].stateid;
3539+ loopstate = cstack[from].stateid;
3540 looptrans = this->successorGenerator->fired();
3541 cstack[from].lowsource = to;
3542
3543@@ -154,8 +174,8 @@
3544 }
3545 }
3546
3547- template<typename S, bool SaveTrace>
3548- bool TarjanModelChecker<S, SaveTrace>::nexttrans(State &state, State &parent, TarjanModelChecker::DEntry &delem)
3549+ template<template<typename, typename...> typename S, typename G, bool SaveTrace, typename... Spooler>
3550+ bool TarjanModelChecker<S, G, SaveTrace, Spooler...>::nexttrans(State &state, State &parent, TarjanModelChecker::DEntry &delem)
3551 {
3552 seen.decode(parent, cstack[delem.pos].stateid);
3553 this->successorGenerator->prepare(&parent, delem.sucinfo);
3554@@ -167,12 +187,13 @@
3555 return res;
3556 }
3557
3558- template<typename S, bool SaveTrace>
3559- void TarjanModelChecker<S, SaveTrace>::printTrace(std::stack<DEntry> &&dstack, std::ostream &os)
3560+ template<template<typename, typename...> typename S, typename G, bool SaveTrace, typename... Spooler>
3561+ void TarjanModelChecker<S, G, SaveTrace, Spooler...>::printTrace(std::stack<DEntry> &&dstack, std::ostream &os)
3562 {
3563 if constexpr (!SaveTrace) {
3564 return;
3565 } else {
3566+ assert(violation);
3567 State state = factory.newState();
3568 os << "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>\n"
3569 "<trace>\n";
3570@@ -185,10 +206,10 @@
3571 p = dstack.top().pos;
3572 auto stateid = cstack[p].stateid;
3573 auto[parent, tid] = seen.getHistory(stateid);
3574- seen.decode(state, stateid);
3575+ seen.decode(state, parent);
3576
3577- this->printTransition(tid, state, os) << '\n';
3578 if (stateid == loopstate) this->printLoop(os);
3579+ this->printTransition(tid, state, os) << '\n';
3580
3581 cstack[p].lowlink = std::numeric_limits<idx_t>::max();
3582 dstack.pop();
3583@@ -198,7 +219,7 @@
3584 p = cstack[p].lowsource;
3585 while (cstack[p].lowlink != std::numeric_limits<idx_t>::max()) {
3586 auto[parent, tid] = seen.getHistory(cstack[p].stateid);
3587- seen.decode(state, cstack[p].stateid);
3588+ seen.decode(state, parent);
3589 this->printTransition(tid, state, os) << '\n';
3590 assert(cstack[p].lowsource != std::numeric_limits<idx_t>::max());
3591 p = cstack[p].lowsource;
3592@@ -211,14 +232,26 @@
3593 }
3594
3595 template
3596- class TarjanModelChecker<LTL::ResumingSuccessorGenerator, true>;
3597-
3598- template
3599- class TarjanModelChecker<LTL::ResumingSuccessorGenerator, false>;
3600-
3601- template
3602- class TarjanModelChecker<LTL::SpoolingSuccessorGenerator, true>;
3603-
3604- template
3605- class TarjanModelChecker<LTL::SpoolingSuccessorGenerator, false>;
3606+ class TarjanModelChecker<ProductSuccessorGenerator, LTL::ResumingSuccessorGenerator, true>;
3607+
3608+ template
3609+ class TarjanModelChecker<ProductSuccessorGenerator, LTL::ResumingSuccessorGenerator, false>;
3610+
3611+ template
3612+ class TarjanModelChecker<ProductSuccessorGenerator, LTL::SpoolingSuccessorGenerator, true>;
3613+
3614+ template
3615+ class TarjanModelChecker<ProductSuccessorGenerator, LTL::SpoolingSuccessorGenerator, false>;
3616+
3617+ template
3618+ class TarjanModelChecker<ReachStubProductSuccessorGenerator, LTL::SpoolingSuccessorGenerator, true, VisibleLTLStubbornSet>;
3619+
3620+ template
3621+ class TarjanModelChecker<ReachStubProductSuccessorGenerator, LTL::SpoolingSuccessorGenerator, false, VisibleLTLStubbornSet>;
3622+
3623+ template
3624+ class TarjanModelChecker<ReachStubProductSuccessorGenerator, LTL::SpoolingSuccessorGenerator, true, EnabledSpooler>;
3625+
3626+ template
3627+ class TarjanModelChecker<ReachStubProductSuccessorGenerator, LTL::SpoolingSuccessorGenerator, false, EnabledSpooler>;
3628 }
3629
3630=== modified file 'src/LTL/CMakeLists.txt'
3631--- src/LTL/CMakeLists.txt 2021-04-06 07:37:30 +0000
3632+++ src/LTL/CMakeLists.txt 2021-08-05 10:56:53 +0000
3633@@ -3,13 +3,15 @@
3634
3635 add_subdirectory(Algorithm)
3636 add_subdirectory(Simplification)
3637+
3638+add_subdirectory(Stubborn)
3639 add_subdirectory(SuccessorGeneration)
3640
3641-add_library(LTL ${HEADER_FILES} LTLMain.cpp)
3642+add_library(LTL ${HEADER_FILES} LTLMain.cpp Replay.cpp)
3643
3644 if (VERIFYPN_Static OR APPLE)
3645- target_link_libraries(LTL PRIVATE LTL_algorithm LTL_simplification LTLSuccessorGeneration spot bddx)
3646+ target_link_libraries(LTL PRIVATE LTL_algorithm LTLStubborn LTL_simplification LTLSuccessorGeneration spot bddx)
3647 else()
3648- target_link_libraries(LTL PRIVATE LTL_algorithm LTL_simplification LTLSuccessorGeneration
3649- -Wl,-Bstatic spot bddx -Wl,-Bdynamic)
3650+ target_link_libraries(LTL PRIVATE LTL_algorithm LTLStubborn LTL_simplification LTLSuccessorGeneration
3651+ -Wl,-Bstatic spot bddx -Wl,-Bdynamic)
3652 endif()
3653
3654=== modified file 'src/LTL/LTLMain.cpp'
3655--- src/LTL/LTLMain.cpp 2021-07-05 19:55:24 +0000
3656+++ src/LTL/LTLMain.cpp 2021-08-05 10:56:53 +0000
3657@@ -22,13 +22,15 @@
3658
3659 #include "LTL/SuccessorGeneration/Spoolers.h"
3660 #include "LTL/SuccessorGeneration/Heuristics.h"
3661+#include "LTL/SuccessorGeneration/HeuristicParser.h"
3662
3663 #include <utility>
3664
3665 using namespace PetriEngine::PQL;
3666 using namespace PetriEngine;
3667
3668-//#define DEBUG_EXPLORED_STATES
3669+// Causes FORMULA <qname> STATS EXPLORED <nexplored> to be printed after verification. Remove if this causes a nuisance.
3670+// #define DEBUG_EXPLORED_STATES
3671
3672 namespace LTL {
3673 struct Result {
3674@@ -75,6 +77,7 @@
3675 const options_t &options)
3676 {
3677 Result result;
3678+ checker->setOptions(options);
3679 result.satisfied = checker->isSatisfied();
3680 result.is_weak = checker->isweak();
3681 #ifdef DEBUG_EXPLORED_STATES
3682@@ -86,7 +89,25 @@
3683 return result;
3684 }
3685
3686- ReturnValue LTLMain(const PetriNet *net,
3687+ std::unique_ptr<Heuristic> make_heuristic(const PetriNet *net,
3688+ const Condition_ptr &negated_formula,
3689+ const Structures::BuchiAutomaton& automaton,
3690+ options_t &options) {
3691+ if (options.strategy == Reachability::Strategy::RDFS) {
3692+ return std::make_unique<RandomHeuristic>(options.seed());
3693+ }
3694+ if (options.strategy != Reachability::Strategy::HEUR && options.strategy != Reachability::Strategy::DEFAULT) {
3695+ return nullptr;
3696+ }
3697+ auto heur = ParseHeuristic(net, automaton, negated_formula, options.ltlHeuristic);
3698+ if (heur == nullptr) {
3699+ std::cerr << "Invalid heuristic specification, terminating.\n";
3700+ exit(1);
3701+ }
3702+ else return heur;
3703+ }
3704+
3705+ bool LTLMain(const PetriNet *net,
3706 const Condition_ptr &query,
3707 const std::string &queryName,
3708 options_t &options)
3709@@ -96,92 +117,174 @@
3710 bool negate_answer = res.second;
3711
3712 // force AP compress off for Büchi prints
3713- auto compress = options.buchi_out_file.empty() ? options.ltl_compress_aps : APCompression::None;
3714+ options.ltl_compress_aps = options.buchi_out_file.empty() ? options.ltl_compress_aps : APCompression::None;
3715
3716- Structures::BuchiAutomaton automaton = makeBuchiAutomaton(negated_formula, compress);
3717+ Structures::BuchiAutomaton automaton = makeBuchiAutomaton(negated_formula, options);
3718 if (!options.buchi_out_file.empty()) {
3719 automaton.output_buchi(options.buchi_out_file, options.buchi_out_type);
3720 }
3721
3722+ bool is_visible_stub = options.stubbornreduction
3723+ && (options.ltl_por == LTLPartialOrder::Visible || options.ltl_por == LTLPartialOrder::VisibleReach)
3724+ && !net->has_inhibitor()
3725+ && !negated_formula->containsNext();
3726+ bool is_autreach_stub = options.stubbornreduction
3727+ && (options.ltl_por == LTLPartialOrder::AutomatonReach ||
3728+ options.ltl_por == LTLPartialOrder::VisibleReach)
3729+ && !net->has_inhibitor();
3730+ bool is_buchi_stub = options.stubbornreduction
3731+ && options.ltl_por == LTLPartialOrder::FullAutomaton
3732+ && !net->has_inhibitor();
3733+
3734+ bool is_stubborn = options.ltl_por != LTLPartialOrder::None && (is_visible_stub || is_autreach_stub || is_buchi_stub);
3735+
3736+ std::unique_ptr<SuccessorSpooler> spooler;
3737+ std::unique_ptr<Heuristic> heuristic = make_heuristic(net, negated_formula, automaton, options);
3738+
3739 Result result;
3740 switch (options.ltlalgorithm) {
3741 case Algorithm::NDFS:
3742- if (options.strategy == PetriEngine::Reachability::RDFS) {
3743+ if (options.strategy != PetriEngine::Reachability::DFS) {
3744 SpoolingSuccessorGenerator gen{net, negated_formula};
3745- gen.setSpooler(std::make_unique<EnabledSpooler>(net, gen));
3746- gen.setHeuristic(std::make_unique<RandomHeuristic>());
3747+ spooler = std::make_unique<EnabledSpooler>(net, gen);
3748+ gen.setSpooler(spooler.get());
3749+ gen.setHeuristic(heuristic.get());
3750
3751- result = _verify(net, negated_formula,
3752- std::make_unique<RandomNDFS>(net, negated_formula, automaton, std::move(gen),
3753- options.trace,
3754- options.ltluseweak, options.seed()),
3755- options);
3756- } else if (options.trace != TraceLevel::None) {
3757- result = _verify(net, negated_formula,
3758- std::make_unique<NestedDepthFirstSearch<PetriEngine::Structures::TracableStateSet>>(
3759- net, negated_formula, automaton, options.trace, options.ltluseweak),
3760- options);
3761+ if (options.trace != TraceLevel::None) {
3762+ result = _verify(
3763+ net, negated_formula,
3764+ std::make_unique<NestedDepthFirstSearch<SpoolingSuccessorGenerator, PetriEngine::Structures::TracableStateSet>>(
3765+ net, negated_formula, automaton, &gen),
3766+ options);
3767+ } else {
3768+ result = _verify(
3769+ net, negated_formula,
3770+ std::make_unique<NestedDepthFirstSearch<SpoolingSuccessorGenerator, PetriEngine::Structures::StateSet>>(
3771+ net, negated_formula, automaton, &gen),
3772+ options);
3773+ }
3774 } else {
3775- result = _verify(net, negated_formula,
3776- std::make_unique<NestedDepthFirstSearch<PetriEngine::Structures::StateSet>>(
3777- net, negated_formula, automaton, options.trace, options.ltluseweak),
3778- options);
3779+ ResumingSuccessorGenerator gen{net};
3780+ if (options.trace != TraceLevel::None) {
3781+ result = _verify(
3782+ net, negated_formula,
3783+ std::make_unique<NestedDepthFirstSearch<ResumingSuccessorGenerator, PetriEngine::Structures::TracableStateSet>>(
3784+ net, negated_formula, automaton, &gen),
3785+ options);
3786+ } else {
3787+ result = _verify(
3788+ net, negated_formula,
3789+ std::make_unique<NestedDepthFirstSearch<ResumingSuccessorGenerator, PetriEngine::Structures::StateSet>>(
3790+ net, negated_formula, automaton, &gen),
3791+ options);
3792+ }
3793 }
3794 break;
3795+
3796 case Algorithm::Tarjan:
3797- if (options.strategy != PetriEngine::Reachability::DFS) {
3798+ if (options.strategy != PetriEngine::Reachability::DFS || is_stubborn) {
3799+ // Use spooling successor generator in case of different search strategy or stubborn set method.
3800 // Running default, BestFS, or RDFS search strategy so use spooling successor generator to enable heuristics.
3801 SpoolingSuccessorGenerator gen{net, negated_formula};
3802- gen.setSpooler(std::make_unique<EnabledSpooler>(net, gen));
3803- if (options.strategy == PetriEngine::Reachability::RDFS) {
3804- gen.setHeuristic(std::make_unique<RandomHeuristic>(options.seed()));
3805- } else if (options.strategy == PetriEngine::Reachability::HEUR
3806- || options.strategy == PetriEngine::Reachability::DEFAULT) {
3807- gen.setHeuristic(std::make_unique<AutomatonHeuristic>(net, automaton));
3808+ if (is_visible_stub) {
3809+ std::cout << "Running stubborn version!" << std::endl;
3810+ spooler = std::make_unique<VisibleLTLStubbornSet>(*net, negated_formula);
3811+ } else if (is_buchi_stub) {
3812+ spooler = std::make_unique<AutomatonStubbornSet>(*net, automaton);
3813+ }
3814+ else {
3815+ spooler = std::make_unique<EnabledSpooler>(net, gen);
3816+ }
3817+
3818+ assert(spooler);
3819+ gen.setSpooler(spooler.get());
3820+ // if search strategy used, set heuristic, otherwise ignore it
3821+ // (default is null which is checked elsewhere)
3822+ if (options.strategy != PetriEngine::Reachability::DFS) {
3823+ assert(heuristic != nullptr);
3824+ gen.setHeuristic(heuristic.get());
3825 }
3826
3827 if (options.trace != TraceLevel::None) {
3828- result = _verify(net, negated_formula,
3829- std::make_unique<TarjanModelChecker<SpoolingSuccessorGenerator, true>>(
3830- net,
3831- negated_formula,
3832- automaton,
3833- std::move(gen),
3834- options.trace,
3835- options.ltluseweak),
3836- options);
3837+ if (is_autreach_stub && is_visible_stub) {
3838+ result = _verify(net, negated_formula,
3839+ std::make_unique<TarjanModelChecker<ReachStubProductSuccessorGenerator, SpoolingSuccessorGenerator, true, VisibleLTLStubbornSet>>(
3840+ net,
3841+ negated_formula,
3842+ automaton,
3843+ &gen,
3844+ std::make_unique<VisibleLTLStubbornSet>(*net, negated_formula)),
3845+ options);
3846+ }
3847+ else if (is_autreach_stub && !is_visible_stub) {
3848+ result = _verify(net, negated_formula,
3849+ std::make_unique<TarjanModelChecker<ReachStubProductSuccessorGenerator, SpoolingSuccessorGenerator, true, EnabledSpooler>>(
3850+ net,
3851+ negated_formula,
3852+ automaton,
3853+ &gen,
3854+ std::make_unique<EnabledSpooler>(net, gen)),
3855+ options);
3856+ }
3857+ else {
3858+ result = _verify(net, negated_formula,
3859+ std::make_unique<TarjanModelChecker<ProductSuccessorGenerator, SpoolingSuccessorGenerator, true>>(
3860+ net,
3861+ negated_formula,
3862+ automaton,
3863+ &gen),
3864+ options);
3865+ }
3866 } else {
3867- result = _verify(net, negated_formula,
3868- std::make_unique<TarjanModelChecker<SpoolingSuccessorGenerator, false>>(
3869- net,
3870- negated_formula,
3871- automaton,
3872- std::move(gen),
3873- options.trace,
3874- options.ltluseweak),
3875- options);
3876+
3877+ if (is_autreach_stub && is_visible_stub) {
3878+ result = _verify(net, negated_formula,
3879+ std::make_unique<TarjanModelChecker<ReachStubProductSuccessorGenerator, SpoolingSuccessorGenerator, false, VisibleLTLStubbornSet>>(
3880+ net,
3881+ negated_formula,
3882+ automaton,
3883+ &gen,
3884+ std::make_unique<VisibleLTLStubbornSet>(*net, negated_formula)),
3885+ options);
3886+ } else if (is_autreach_stub && !is_visible_stub) {
3887+ result = _verify(net, negated_formula,
3888+ std::make_unique<TarjanModelChecker<ReachStubProductSuccessorGenerator, SpoolingSuccessorGenerator, false, EnabledSpooler>>(
3889+ net,
3890+ negated_formula,
3891+ automaton,
3892+ &gen,
3893+ std::make_unique<EnabledSpooler>(net, gen)),
3894+ options);
3895+ }
3896+ else {
3897+ result = _verify(net, negated_formula,
3898+ std::make_unique<TarjanModelChecker<ProductSuccessorGenerator, SpoolingSuccessorGenerator, false>>(
3899+ net,
3900+ negated_formula,
3901+ automaton,
3902+ &gen),
3903+ options);
3904+ }
3905 }
3906 } else {
3907+ ResumingSuccessorGenerator gen{net};
3908+
3909 // no spooling needed, thus use resuming successor generation
3910 if (options.trace != TraceLevel::None) {
3911 result = _verify(net, negated_formula,
3912- std::make_unique<TarjanModelChecker<ResumingSuccessorGenerator, true>>(
3913+ std::make_unique<TarjanModelChecker<ProductSuccessorGenerator, ResumingSuccessorGenerator, true>>(
3914 net,
3915 negated_formula,
3916 automaton,
3917- ResumingSuccessorGenerator{*net},
3918- options.trace,
3919- options.ltluseweak),
3920+ &gen),
3921 options);
3922 } else {
3923 result = _verify(net, negated_formula,
3924- std::make_unique<TarjanModelChecker<ResumingSuccessorGenerator, false>>(
3925+ std::make_unique<TarjanModelChecker<ProductSuccessorGenerator, ResumingSuccessorGenerator, false>>(
3926 net,
3927 negated_formula,
3928 automaton,
3929- ResumingSuccessorGenerator{*net},
3930- options.trace,
3931- options.ltluseweak),
3932+ &gen),
3933 options);
3934 }
3935 }
3936@@ -194,11 +297,18 @@
3937 << (result.satisfied ^ negate_answer ? " TRUE" : " FALSE") << " TECHNIQUES EXPLICIT "
3938 << LTL::to_string(options.ltlalgorithm)
3939 << (result.is_weak ? " WEAK_SKIP" : "")
3940- << std::endl;
3941+ << (is_stubborn ? " STUBBORN" : "")
3942+ << (is_visible_stub ? " CLASSIC_STUB" : "")
3943+ << (is_autreach_stub ? " REACH_STUB" : "")
3944+ << (is_buchi_stub ? " BUCHI_STUB" : "");
3945+ if (heuristic != nullptr) {
3946+ std::cout << " HEURISTIC ";
3947+ heuristic->output(std::cout);
3948+ }
3949+ std::cout << " OPTIM-" << static_cast<int>(options.buchiOptimization) << std::endl;
3950 #ifdef DEBUG_EXPLORED_STATES
3951 std::cout << "FORMULA " << queryName << " STATS EXPLORED " << result.explored_states << std::endl;
3952 #endif
3953-
3954- return SuccessCode;
3955+ return result.satisfied ^ negate_answer;
3956 }
3957 }
3958
3959=== added file 'src/LTL/Replay.cpp'
3960--- src/LTL/Replay.cpp 1970-01-01 00:00:00 +0000
3961+++ src/LTL/Replay.cpp 2021-08-05 10:56:53 +0000
3962@@ -0,0 +1,326 @@
3963+/* Copyright (C) 2021 Nikolaj J. Ulrik <nikolaj@njulrik.dk>,
3964+ * Simon M. Virenfeldt <simon@simwir.dk>
3965+ *
3966+ * This program is free software: you can redistribute it and/or modify
3967+ * it under the terms of the GNU General Public License as published by
3968+ * the Free Software Foundation, either version 3 of the License, or
3969+ * (at your option) any later version.
3970+ *
3971+ * This program is distributed in the hope that it will be useful,
3972+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
3973+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
3974+ * GNU General Public License for more details.
3975+ *
3976+ * You should have received a copy of the GNU General Public License
3977+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
3978+ */
3979+
3980+#include "PetriEngine/PQL/PQL.h"
3981+#include "PetriEngine/PetriNet.h"
3982+#include "LTL/Replay.h"
3983+#include "PetriEngine/Structures/State.h"
3984+#include "PetriEngine/SuccessorGenerator.h"
3985+#include "LTL/LTLToBuchi.h"
3986+
3987+#include <rapidxml.hpp>
3988+#include <vector>
3989+#include <iostream>
3990+#include <cstring>
3991+
3992+using namespace PetriEngine;
3993+using namespace PetriEngine::PQL;
3994+
3995+namespace LTL {
3996+ bool guard_valid(const PQL::EvaluationContext &ctx, const BuchiSuccessorGenerator &buchi, bdd bdd);
3997+
3998+ Replay::Replay(std::istream &is, const PetriEngine::PetriNet *net)
3999+ {
4000+ parse(is, net);
4001+ }
4002+
4003+ void Replay::parse(std::istream &xml, const PetriNet *net)
4004+ {
4005+ places.clear();
4006+ transitions.clear();
4007+ // preallocate reverse lookup for transition and place names. Assume this is always called with the same Petri net.
4008+ for (int i = 0; i < net->placeNames().size(); ++i) {
4009+ places[net->placeNames()[i]] = i;
4010+ }
4011+ for (int i = 0; i < net->transitionNames().size(); ++i) {
4012+ transitions[net->transitionNames()[i]] = i;
4013+ }
4014+ transitions[DEADLOCK_TRANS] = -1;
4015+
4016+ // TODO can also validate transition names up front.
4017+ rapidxml::xml_document<> doc;
4018+ std::vector<char> buffer((std::istreambuf_iterator<char>(xml)), std::istreambuf_iterator<char>());
4019+ buffer.push_back('\0');
4020+ doc.parse<0>(buffer.data());
4021+ rapidxml::xml_node<> *root = doc.first_node();
4022+
4023+ if (root) {
4024+ parseRoot(root);
4025+ } else {
4026+ std::cerr << "Error getting root node." << std::endl;
4027+ assert(false);
4028+ exit(1);
4029+ }
4030+
4031+ }
4032+
4033+ void Replay::parseRoot(const rapidxml::xml_node<> *pNode)
4034+ {
4035+ if (std::strcmp(pNode->name(), "trace") != 0) {
4036+ std::cerr << "Error: Expected trace node. Got: " << pNode->name() << std::endl;
4037+ assert(false);
4038+ exit(1);
4039+ }
4040+ for (auto it = pNode->first_node(); it; it = it->next_sibling()) {
4041+ if (std::strcmp(it->name(), "loop") == 0) loop_idx = trace.size();
4042+ else if (std::strcmp(it->name(), "transition") == 0 || std::strcmp(it->name(), "deadlock") == 0) {
4043+ trace.push_back(parseTransition(it));
4044+ } else {
4045+ std::cerr << "Error: Expected transition or loop node. Got: " << it->name() << std::endl;
4046+ assert(false);
4047+ exit(1);
4048+ }
4049+ }
4050+ if (loop_idx == std::numeric_limits<size_t>::max()) {
4051+ std::cerr << "Error: Missing <loop/> statement in trace\n";
4052+ exit(1);
4053+ }
4054+ }
4055+
4056+ Replay::Transition Replay::parseTransition(const rapidxml::xml_node<char> *pNode)
4057+ {
4058+ std::string id;
4059+ int buchi = -1;
4060+ for (auto it = pNode->first_attribute(); it; it = it->next_attribute()) {
4061+ if (std::strcmp(it->name(), "id") == 0) {
4062+ id = std::string(it->value());
4063+ }
4064+ if (std::strstr(it->name(), "buchi") != nullptr) {
4065+ // buchi field is sometimes spelled slightly differently, but will always contain 'buchi'
4066+ buchi = std::stoi(it->value());
4067+ }
4068+ }
4069+ if (strcmp(pNode->name(), "deadlock") == 0) {
4070+ id = DEADLOCK_TRANS;
4071+ }
4072+ if (id.empty()) {
4073+ std::cerr << "Error: Transition has no id attribute" << std::endl;
4074+ assert(false);
4075+ exit(1);
4076+ }
4077+ if (buchi == -1) {
4078+ std::cerr << "Error: Missing Büchi state" << std::endl;
4079+ assert(false);
4080+ exit(1);
4081+ }
4082+
4083+ Transition transition(id, buchi);
4084+
4085+ for (auto it = pNode->first_node(); it; it = it->next_sibling()) {
4086+ if (std::strcmp(it->name(), "token") != 0) {
4087+ std::cerr << "Warning: Unexpected transition child. Expected: token, but got: " << it->name()
4088+ << std::endl;
4089+ }
4090+ parseToken(it, transition.tokens);
4091+ }
4092+
4093+ return transition;
4094+ }
4095+
4096+ void
4097+ Replay::parseToken(const rapidxml::xml_node<char> *pNode, std::unordered_map<uint32_t, uint32_t> &current_marking)
4098+ {
4099+ for (auto it = pNode->first_attribute(); it; it = it->next_attribute()) {
4100+ if (std::strcmp(it->name(), "place") == 0) {
4101+ std::string val{it->value()};
4102+ if (current_marking.find(places.at(val)) == current_marking.end()) {
4103+ current_marking[places.at(val)] = 1;
4104+ } else {
4105+ ++current_marking[places.at(val)];
4106+ }
4107+ }
4108+ }
4109+ }
4110+
4111+ bool Replay::replay(const PetriEngine::PetriNet *net, const PetriEngine::PQL::Condition_ptr &cond, const options_t &options)
4112+ {
4113+ BuchiSuccessorGenerator buchiGenerator = makeBuchiSuccessorGenerator(cond, options);
4114+ //spot::print_dot(std::cerr, buchiGenerator.aut._buchi);
4115+ PetriEngine::Structures::State state;
4116+ state.setMarking(net->makeInitialMarking());
4117+ PetriEngine::SuccessorGenerator successorGenerator(*net);
4118+
4119+ // generate all initial Buchi states since trace may be dependent on only some initial state.
4120+ size_t prev_buchi = buchiGenerator.initial_state_number();
4121+ buchiGenerator.prepare(prev_buchi);
4122+ bdd bdd;
4123+ std::vector<size_t> init_buchi_states;
4124+ while (buchiGenerator.next(prev_buchi, bdd)) {
4125+ if (guard_valid(EvaluationContext{state.marking(), net}, buchiGenerator, bdd)) {
4126+ init_buchi_states.push_back(prev_buchi);
4127+ }
4128+ }
4129+ std::cout << "Playing back trace. Length: " << trace.size() << std::endl;
4130+ for (int i = 0; i < init_buchi_states.size(); ++i) {
4131+ std::cerr << "Replaying initial state " << i << "/" << init_buchi_states.size() << std::endl;
4132+ if (_play_trace(net, successorGenerator, buchiGenerator, init_buchi_states[i])) {
4133+ std::cerr << "Replay complete. No errors" << std::endl;
4134+ return true;
4135+ }
4136+ }
4137+ std::cerr << "ERROR The trace could not be played back using any valid initial Büchi state. "
4138+ << "See previous output for details."
4139+ << std::endl;
4140+ return false;
4141+ }
4142+
4143+ bool Replay::_play_trace(const PetriEngine::PetriNet *net, PetriEngine::SuccessorGenerator &successorGenerator,
4144+ BuchiSuccessorGenerator &buchiGenerator, size_t init_buchi)
4145+ {
4146+ PetriEngine::Structures::State state;
4147+ PetriEngine::Structures::State loopstate;
4148+ size_t loop_buchi;
4149+ bool loop_accepting = false;
4150+ bool looping = false;
4151+ state.setMarking(net->makeInitialMarking());
4152+ loopstate.setMarking(net->makeInitialMarking());
4153+ size_t prev_buchi = init_buchi;
4154+ bdd bdd;
4155+ bool buchi_failed = false;
4156+ for (int i = 0; i < trace.size(); ++i) {
4157+ const Transition &transition = trace[i];
4158+ // looping part should end up at the state _before_ the <loop/> tag,
4159+ // hence copy state from previous iteration.
4160+ if (i == loop_idx) {
4161+ memcpy(loopstate.marking(), state.marking(), sizeof(uint32_t) * net->numberOfPlaces());
4162+ loop_buchi = prev_buchi;
4163+ looping = true;
4164+ }
4165+ successorGenerator.prepare(&state);
4166+ buchiGenerator.prepare(prev_buchi);
4167+ auto it = transitions.find(transition.id);
4168+ if (it == std::end(transitions)) {
4169+ std::cerr << "Unrecognized transition name " << transition.id << std::endl;
4170+ assert(false);
4171+ exit(1);
4172+ }
4173+ int tid = it->second;
4174+
4175+ if (tid != -1) {
4176+ // fire transition
4177+ if (!successorGenerator.checkPreset(tid)) {
4178+ std::cerr
4179+ << "ERROR the provided trace cannot be replayed on the provided model. Offending transition: "
4180+ << transition.id << " at index: " << i << "\n";
4181+ exit(1);
4182+ }
4183+ successorGenerator.consumePreset(state, tid);
4184+ successorGenerator.producePostset(state, tid);
4185+ }
4186+ else {
4187+ // NOTE Deadlock check, not yet implemented. This assumes no transition in the net is named ##deadlock.
4188+ }
4189+
4190+ if (!transition.tokens.empty()) {
4191+ for (int pid = 0; pid < net->numberOfPlaces(); ++pid) {
4192+ if (transition.tokens.find(pid) == std::end(transition.tokens)) {
4193+ // If trace didn't mention the place, then it must have 0 tokens.
4194+ if (state.marking()[pid] > 0) {
4195+ std::cerr << "ERROR the playback of the trace resulted in mismatch of token counts. \n"
4196+ " Offending place " << net->placeNames()[pid] << ", expected " << 0
4197+ << " tokens but got " << state.marking()[pid] << std::endl;
4198+ std::cerr << " Offending transition: " << transition.id << " at index: " << i << "\n";
4199+ exit(1);
4200+ }
4201+ } else if (transition.tokens.at(pid) != state.marking()[pid]) {
4202+ // If trace did mention the place, then validate token count.
4203+ std::cerr << "ERROR the playback of the trace resulted in mismatch of token counts. \n"
4204+ " Offending place " << net->placeNames()[pid] << ", expected "
4205+ << transition.tokens.at(pid)
4206+ << " tokens but got " << state.marking()[pid] << std::endl;
4207+ std::cerr << "Offending transition: " << transition.id << " at index: " << i << "\n";
4208+ exit(1);
4209+ }
4210+ }
4211+ }
4212+ size_t next_buchi = -1;
4213+ const EvaluationContext &ctx = EvaluationContext{state.marking(), net};
4214+ while (buchiGenerator.next(next_buchi, bdd)) {
4215+ if (next_buchi == transition.buchi_state) break;
4216+ }
4217+ if (!guard_valid(ctx, buchiGenerator, bdd)) {
4218+ // warning since technically we may have multiple initial buchi states,
4219+ // and maybe only some will allow replaying the trace.
4220+ std::cerr << "WARNING guard invalid for noted buchi state " << transition.buchi_state
4221+ << " at index " << i << ". Attempting to proceed anyway.\n";
4222+ buchi_failed = true;
4223+ }
4224+ prev_buchi = next_buchi;
4225+ if (looping && buchiGenerator.is_accepting(prev_buchi)) {
4226+ loop_accepting = true;
4227+ }
4228+
4229+ if (i % 100000 == 0)
4230+ std::cerr << i << "/" << trace.size() << std::endl;
4231+ }
4232+
4233+ bool err = false;
4234+ for (int i = 0; i < net->numberOfPlaces(); ++i) {
4235+ if (state.marking()[i] != loopstate.marking()[i]) {
4236+ if (!err) {
4237+ std::cerr << "ERROR end state not equal to expected loop state.\n";
4238+ err = true;
4239+ }
4240+ std::cerr << " " << net->placeNames()[i] << ": expected" << loopstate.marking()[i] << ", actual "
4241+ << state.marking()[i] << '\n';
4242+ }
4243+ }
4244+ if (prev_buchi != loop_buchi) {
4245+ if (!err) {
4246+ std::cerr << "ERROR end state not equal to expected loop state.\n";
4247+ err = true;
4248+ }
4249+ std::cerr << "Buchi state: expected " << loop_buchi << ", actual " << prev_buchi << std::endl;
4250+ }
4251+ if (err) exit(1);
4252+
4253+ if (!loop_accepting) {
4254+ std::cerr << "WARNING trace did not demonstrate accepting loop using initial Buchi state " << init_buchi
4255+ << ".\n";
4256+ return false;
4257+ }
4258+
4259+ return !buchi_failed;
4260+ }
4261+
4262+ bool guard_valid(const PQL::EvaluationContext &ctx, const BuchiSuccessorGenerator &buchi, bdd bdd)
4263+ {
4264+ using namespace PetriEngine::PQL;
4265+ // IDs 0 and 1 are false and true atoms, respectively
4266+ // More details in buddy manual for details ( http://buddy.sourceforge.net/manual/main.html )
4267+ while (bdd.id() > 1) {
4268+ // find variable to test, and test it
4269+ size_t var = bdd_var(bdd);
4270+ Condition::Result res = buchi.getExpression(var)->evaluate(ctx);
4271+ switch (res) {
4272+ case Condition::RUNKNOWN:
4273+ std::cerr << "Unexpected unknown answer from evaluating query!\n";
4274+ assert(false);
4275+ exit(1);
4276+ break;
4277+ case Condition::RFALSE:
4278+ bdd = bdd_low(bdd);
4279+ break;
4280+ case Condition::RTRUE:
4281+ bdd = bdd_high(bdd);
4282+ break;
4283+ }
4284+ }
4285+ return bdd == bddtrue;
4286+ }
4287+
4288+}
4289
4290=== added file 'src/LTL/Simplification/CMakeLists.txt'
4291=== removed file 'src/LTL/Simplification/CMakeLists.txt'
4292=== modified file 'src/LTL/Simplification/SpotToPQL.cpp'
4293--- src/LTL/Simplification/SpotToPQL.cpp 2021-04-06 13:10:00 +0000
4294+++ src/LTL/Simplification/SpotToPQL.cpp 2021-08-05 10:56:53 +0000
4295@@ -143,15 +143,15 @@
4296 }
4297 }
4298
4299- PetriEngine::PQL::Condition_ptr simplify(const PetriEngine::PQL::Condition_ptr &formula, APCompression compress) {
4300+ PetriEngine::PQL::Condition_ptr simplify(const PetriEngine::PQL::Condition_ptr &formula, const options_t &options) {
4301 using namespace PetriEngine::PQL;
4302 if (auto e = std::dynamic_pointer_cast<ECondition>(formula); e != nullptr) {
4303- return std::make_shared<ECondition>(simplify((*e)[0]));
4304+ return std::make_shared<ECondition>(simplify((*e)[0], options));
4305 } else if (auto a = std::dynamic_pointer_cast<ACondition>(formula); a != nullptr) {
4306- return std::make_shared<ACondition>(simplify((*a)[0]));
4307+ return std::make_shared<ACondition>(simplify((*a)[0], options));
4308 }
4309- auto[f, apinfo] = LTL::to_spot_formula(formula, compress);
4310- spot::tl_simplifier simplifier{1};
4311+ auto[f, apinfo] = LTL::to_spot_formula(formula, options);
4312+ spot::tl_simplifier simplifier{static_cast<int>(options.buchiOptimization)};
4313 f = simplifier.simplify(f);
4314 // spot simplifies using unsupported operators R, W, and M, which we now remove.
4315 f = spot::unabbreviate(f, "RWM");
4316
4317=== added directory 'src/LTL/Stubborn'
4318=== added file 'src/LTL/Stubborn/AutomatonStubbornSet.cpp'
4319--- src/LTL/Stubborn/AutomatonStubbornSet.cpp 1970-01-01 00:00:00 +0000
4320+++ src/LTL/Stubborn/AutomatonStubbornSet.cpp 2021-08-05 10:56:53 +0000
4321@@ -0,0 +1,355 @@
4322+/* Copyright (C) 2021 Nikolaj J. Ulrik <nikolaj@njulrik.dk>,
4323+ * Simon M. Virenfeldt <simon@simwir.dk>
4324+ *
4325+ * This program is free software: you can redistribute it and/or modify
4326+ * it under the terms of the GNU General Public License as published by
4327+ * the Free Software Foundation, either version 3 of the License, or
4328+ * (at your option) any later version.
4329+ *
4330+ * This program is distributed in the hope that it will be useful,
4331+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
4332+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
4333+ * GNU General Public License for more details.
4334+ *
4335+ * You should have received a copy of the GNU General Public License
4336+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
4337+ */
4338+
4339+#include "LTL/Structures/GuardInfo.h"
4340+#include "LTL/Stubborn/AutomatonStubbornSet.h"
4341+#include "LTL/Stubborn/EvalAndSetVisitor.h"
4342+#include "PetriEngine/Stubborn/InterestingTransitionVisitor.h"
4343+
4344+using namespace PetriEngine;
4345+using namespace PetriEngine::PQL;
4346+
4347+namespace LTL {
4348+ class NondeterministicConjunctionVisitor : public InterestingLTLTransitionVisitor {
4349+ public:
4350+ NondeterministicConjunctionVisitor(AutomatonStubbornSet &stubbornSet) :
4351+ InterestingLTLTransitionVisitor(stubbornSet, false),
4352+ _stubborn(stubbornSet), _net(stubbornSet._net) {}
4353+
4354+ protected:
4355+ static constexpr auto PresetBad = StubbornSet::PresetBad;
4356+ static constexpr auto PostsetBad = StubbornSet::PostsetBad;
4357+
4358+ void _accept(const PQL::CompareConjunction *element) override
4359+ {
4360+ if (_stubborn._track_changes || element->isNegated() != negated) {
4361+ InterestingLTLTransitionVisitor::_accept(element);
4362+ return;
4363+ }
4364+ _stubborn._track_changes = true;
4365+ memcpy(_stubborn._place_checkpoint.get(), _stubborn._places_seen.get(), _net.numberOfPlaces());
4366+ _accept_conjunction(element);
4367+ _stubborn._track_changes = false;
4368+ assert(_stubborn._pending_stubborn.empty());
4369+
4370+ }
4371+
4372+ void _accept_conjunction(const PQL::CompareConjunction *element)
4373+ {
4374+ assert(_stubborn._track_changes);
4375+ assert(_stubborn._pending_stubborn.empty());
4376+ assert(element->isNegated() == negated);
4377+ std::vector<std::pair<uint32_t, bool>> cands; // Transition id, Preset
4378+ for (auto &cons : *element) {
4379+ uint32_t tokens = _stubborn.getParent()[cons._place];
4380+
4381+ if (cons._lower == cons._upper) {
4382+ //Compare is equality
4383+ if (cons._lower == tokens) {
4384+ continue;
4385+ } else if (tokens < cons._lower) {
4386+ // Valid candidate, explore preset
4387+ cands.emplace_back(cons._place, true);
4388+ } else {
4389+ // Valid candidate, explore postset
4390+ cands.emplace_back(cons._place, false);
4391+ }
4392+ } else {
4393+ if (tokens < cons._lower && cons._lower != 0) {
4394+ // explore preset
4395+ cands.emplace_back(cons._place, true);
4396+ }
4397+
4398+ if (tokens > cons._upper && cons._upper != std::numeric_limits<uint32_t>::max()) {
4399+ // explore postset
4400+ cands.emplace_back(cons._place, false);
4401+ }
4402+ }
4403+
4404+ // explore cand if available
4405+ // if cand seen previously, return do not add additional (good case)
4406+ // else add cand to candidate list
4407+ auto &[cand, pre] = cands.back();
4408+ if (!cands.empty() && cand == cons._place) {
4409+ assert(_stubborn._pending_stubborn.empty());
4410+ if ((pre && _stubborn._places_seen[cand] & PresetBad) ||
4411+ (!pre && _stubborn._places_seen[cand] & PostsetBad)) {
4412+ cands.pop_back();
4413+ continue;
4414+ }
4415+ // this constraint was a candidate previously, thus we are happy
4416+ if ((pre && _stubborn.seenPre(cand)) || (!pre && _stubborn.seenPost(cand)))
4417+ return;
4418+ }
4419+ }
4420+
4421+ // Run through candidate list and find first candidate not violating s-inv and add it to stub, then return.
4422+ for (size_t i = cands.size(); i > 0; --i) {
4423+ auto &[cand, pre] = cands[i - 1];
4424+ if (pre) {
4425+ //explore pre
4426+ assert(!(_stubborn._places_seen[cand] & PresetBad));
4427+ _stubborn.presetOf(cand, false);
4428+ } else {
4429+ //explore post
4430+ assert(!(_stubborn._places_seen[cand] & PostsetBad));
4431+ _stubborn.postsetOf(cand, false);
4432+ }
4433+
4434+ if (_stubborn._bad) {
4435+ _stubborn._places_seen[cand] |= pre ? PresetBad : PostsetBad;
4436+#ifndef NDEBUG
4437+ //std::cerr << "Bad pre/post and reset" << std::endl;
4438+#endif
4439+ _stubborn._reset_pending();
4440+ continue;
4441+ }
4442+
4443+ if (_stubborn._closure()) {
4444+ // success, return this stubborn set
4445+ _stubborn._apply_pending();
4446+ return;
4447+ } else {
4448+#ifndef NDEBUG
4449+ //std::cerr << "Bad closure and reset" << std::endl;
4450+#endif
4451+ _stubborn._places_seen[cand] |= pre ? PresetBad : PostsetBad;
4452+ _stubborn._reset_pending();
4453+ }
4454+ }
4455+
4456+ // If no candidate not violating s-inv set St=T.
4457+ _stubborn.set_all_stubborn();
4458+ }
4459+
4460+ const PetriEngine::PetriNet &_net;
4461+ AutomatonStubbornSet &_stubborn;
4462+
4463+ };
4464+
4465+ bool AutomatonStubbornSet::prepare(const LTL::Structures::ProductState *state)
4466+ {
4467+ reset();
4468+ _parent = state;
4469+ _gen.prepare(state);
4470+ memset(_places_seen.get(), 0, sizeof(uint8_t) * _net.numberOfPlaces());
4471+ constructEnabled();
4472+ if (_ordering.empty())
4473+ return false;
4474+ if (_ordering.size() == 1) {
4475+ _stubborn[_ordering.front()] = true;
4476+#ifndef NDEBUG
4477+ std::cerr << "Lone successor " << _net.transitionNames()[_ordering.front()] << std::endl;
4478+#endif
4479+ return true;
4480+ }
4481+
4482+
4483+ GuardInfo buchi_state = _state_guards[state->getBuchiState()];
4484+
4485+ PQL::EvaluationContext evaluationContext{_parent->marking(), &_net};
4486+
4487+ // Check if retarding is satisfied for condition 3.
4488+ _retarding_satisfied = _aut.guard_valid(evaluationContext, buchi_state.retarding.decision_diagram);
4489+ /*
4490+ if (!_aut.guard_valid(evaluationContext, buchi_state.retarding.decision_diagram)) {
4491+ set_all_stubborn();
4492+ __print_debug();
4493+ return true;
4494+ }*/
4495+
4496+ // Calculate retarding subborn set to ensure S-INV.
4497+ auto negated_retarding = std::make_unique<NotCondition>(buchi_state.retarding.condition);
4498+ _retarding_stubborn_set.setQuery(negated_retarding.get());
4499+ _retarding_stubborn_set.prepare(state);
4500+
4501+
4502+ _nenabled = _ordering.size();
4503+
4504+ // If a progressing formula satisfies the guard St=T is the only way to ensure NLG.
4505+ for (auto &q : buchi_state.progressing) {
4506+ if (_aut.guard_valid(evaluationContext, q.decision_diagram)) {
4507+ set_all_stubborn();
4508+ __print_debug();
4509+ return true;
4510+ }
4511+ }
4512+
4513+ //Interesting on each progressing formula gives NLG.
4514+ for (auto &q : buchi_state.progressing) {
4515+ EvalAndSetVisitor evalAndSetVisitor{evaluationContext};
4516+ q.condition->visit(evalAndSetVisitor);
4517+
4518+ NondeterministicConjunctionVisitor interesting{*this};
4519+ q.condition->visit(interesting);
4520+ if (_done) return true;
4521+ else {
4522+ assert(!_track_changes);
4523+ assert(_pending_stubborn.empty());
4524+ // Closure to ensure COM.
4525+ _closure();
4526+ if (_bad) {
4527+ set_all_stubborn();
4528+ return true;
4529+ }
4530+ }
4531+ }
4532+
4533+ assert(_unprocessed.empty());
4534+ assert(_pending_stubborn.empty());
4535+ assert(!_bad);
4536+ assert(!_done);
4537+
4538+
4539+
4540+ /*//Check that S-INV is satisfied
4541+ if (has_shared_mark(_stubborn.get(), _retarding_stubborn_set.stubborn(), _net.numberOfTransitions())) {
4542+ memset(_stubborn.get(), true, sizeof(bool) * _net.numberOfTransitions());
4543+ //return true;
4544+ }*/
4545+
4546+ // Ensure we have a key transition in accepting buchi states.
4547+ if (!_has_enabled_stubborn && buchi_state.is_accepting) {
4548+ for (uint32_t i = 0; i < _net.numberOfTransitions(); ++i) {
4549+ if (!_stubborn[i] && _enabled[i]) {
4550+ addToStub(i);
4551+ _closure();
4552+ if (_bad) {
4553+ set_all_stubborn();
4554+ return true;
4555+ }
4556+ break;
4557+ }
4558+ }
4559+ }
4560+
4561+ assert(!has_shared_mark(_stubborn.get(), _retarding_stubborn_set.stubborn(), _net.numberOfTransitions()));
4562+
4563+ __print_debug();
4564+
4565+ return true;
4566+ }
4567+
4568+
4569+ uint32_t AutomatonStubbornSet::next()
4570+ {
4571+ while (!_ordering.empty()) {
4572+ _current = _ordering.front();
4573+ _ordering.pop_front();
4574+ if (_stubborn[_current] && _enabled[_current]) {
4575+ return _current;
4576+ }
4577+ }
4578+ reset();
4579+ return std::numeric_limits<uint32_t>::max();
4580+ }
4581+
4582+ void AutomatonStubbornSet::__print_debug()
4583+ {
4584+#ifndef NDEBUG
4585+ return;
4586+ std::cout << "Enabled: ";
4587+ for (int i = 0; i < _net.numberOfTransitions(); ++i) {
4588+ if (_enabled[i]) {
4589+ std::cout << _net.transitionNames()[i] << ' ';
4590+ }
4591+ }
4592+ std::cout << "\nStubborn: ";
4593+ for (int i = 0; i < _net.numberOfTransitions(); ++i) {
4594+ if (_stubborn[i]) {
4595+ std::cout << _net.transitionNames()[i] << ' ';
4596+ }
4597+ }
4598+ std::cout << std::endl;
4599+#endif
4600+ }
4601+
4602+ void AutomatonStubbornSet::reset()
4603+ {
4604+ StubbornSet::reset();
4605+ memset(_place_checkpoint.get(), 0, _net.numberOfPlaces());
4606+ _retarding_stubborn_set.reset();
4607+ _has_enabled_stubborn = false;
4608+ _bad = false;
4609+ _done = false;
4610+ _track_changes = false;
4611+ _pending_stubborn.clear();
4612+ _unprocessed.clear();
4613+ }
4614+
4615+ void AutomatonStubbornSet::addToStub(uint32_t t)
4616+ {
4617+ if (_retarding_stubborn_set.stubborn()[t] || !_cond3_valid(t)) {
4618+ _bad = true;
4619+ return;
4620+ }
4621+ if (_track_changes) {
4622+ if (_enabled[t])
4623+ _has_enabled_stubborn = true;
4624+ if (_pending_stubborn.insert(t).second) {
4625+ _unprocessed.push_back(t);
4626+ }
4627+ } else {
4628+ StubbornSet::addToStub(t);
4629+ }
4630+ }
4631+
4632+ bool AutomatonStubbornSet::_cond3_valid(uint32_t t)
4633+ {
4634+ EvaluationContext ctx{_markbuf.marking(), &_net};
4635+ if (_retarding_satisfied || !_enabled[t]) return true;
4636+ else {
4637+ assert(_gen.checkPreset(t));
4638+ assert(dynamic_cast<const LTL::Structures::ProductState *>(_parent) != nullptr);
4639+ memcpy(_markbuf.marking(), (*_parent).marking(), _net.numberOfPlaces() * sizeof(MarkVal));
4640+ _gen.consumePreset(_markbuf, t);
4641+ _gen.producePostset(_markbuf, t);
4642+ return _aut.guard_valid(ctx,
4643+ _state_guards[static_cast<const LTL::Structures::ProductState *>(_parent)->getBuchiState()].retarding.decision_diagram);
4644+ }
4645+ }
4646+
4647+ void AutomatonStubbornSet::_reset_pending()
4648+ {
4649+ _bad = false;
4650+ _pending_stubborn.clear();
4651+ _unprocessed.clear();
4652+ memcpy(_places_seen.get(), _place_checkpoint.get(), _net.numberOfPlaces());
4653+ }
4654+
4655+ void AutomatonStubbornSet::_apply_pending()
4656+ {
4657+ assert(!_bad);
4658+ for (auto t : _pending_stubborn) {
4659+ _stubborn[t] = true;
4660+ }
4661+ _pending_stubborn.clear();
4662+ assert(_unprocessed.empty());
4663+ }
4664+
4665+ void AutomatonStubbornSet::set_all_stubborn()
4666+ {
4667+ memset(_stubborn.get(), true, sizeof(bool) * _net.numberOfTransitions());
4668+ _done = true;
4669+ }
4670+
4671+ bool AutomatonStubbornSet::_closure()
4672+ {
4673+ StubbornSet::closure([&]() { return !_bad; });
4674+ return !_bad;
4675+ }
4676+}
4677
4678=== added file 'src/LTL/Stubborn/CMakeLists.txt'
4679--- src/LTL/Stubborn/CMakeLists.txt 1970-01-01 00:00:00 +0000
4680+++ src/LTL/Stubborn/CMakeLists.txt 2021-08-05 10:56:53 +0000
4681@@ -0,0 +1,4 @@
4682+set(CMAKE_INCLUDE_CURRENT_DIR ON)
4683+
4684+add_library(LTLStubborn EvalAndSetVisitor.cpp VisibleTransitionVisitor.cpp VisibleLTLStubbornSet.cpp AutomatonStubbornSet.cpp SafeAutStubbornSet.cpp)
4685+add_dependencies(LTLStubborn PetriEngine)
4686
4687=== added file 'src/LTL/Stubborn/EvalAndSetVisitor.cpp'
4688--- src/LTL/Stubborn/EvalAndSetVisitor.cpp 1970-01-01 00:00:00 +0000
4689+++ src/LTL/Stubborn/EvalAndSetVisitor.cpp 2021-08-05 10:56:53 +0000
4690@@ -0,0 +1,126 @@
4691+/* Copyright (C) 2021 Nikolaj J. Ulrik <nikolaj@njulrik.dk>,
4692+ * Simon M. Virenfeldt <simon@simwir.dk>
4693+ *
4694+ * This program is free software: you can redistribute it and/or modify
4695+ * it under the terms of the GNU General Public License as published by
4696+ * the Free Software Foundation, either version 3 of the License, or
4697+ * (at your option) any later version.
4698+ *
4699+ * This program is distributed in the hope that it will be useful,
4700+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
4701+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
4702+ * GNU General Public License for more details.
4703+ *
4704+ * You should have received a copy of the GNU General Public License
4705+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
4706+ */
4707+
4708+#include "LTL/Stubborn/EvalAndSetVisitor.h"
4709+
4710+namespace LTL {
4711+
4712+ using namespace PetriEngine::PQL;
4713+
4714+ void EvalAndSetVisitor::_accept(PetriEngine::PQL::ACondition *condition) {
4715+ condition->getCond()->visit(*this);
4716+ }
4717+
4718+ void EvalAndSetVisitor::_accept(PetriEngine::PQL::ECondition *condition) {
4719+ condition->getCond()->visit(*this);
4720+ }
4721+
4722+ void EvalAndSetVisitor::_accept(PetriEngine::PQL::XCondition *condition) {
4723+ condition->getCond()->visit(*this);
4724+ }
4725+
4726+ void EvalAndSetVisitor::_accept(PetriEngine::PQL::GCondition *condition) {
4727+ condition->getCond()->visit(*this);
4728+ auto res = condition->getCond()->getSatisfied();
4729+ if (res != Condition::RFALSE) res = Condition::RUNKNOWN;
4730+ condition->setSatisfied(res);
4731+ }
4732+
4733+ void EvalAndSetVisitor::_accept(PetriEngine::PQL::FCondition *condition) {
4734+ condition->getCond()->visit(*this);
4735+ auto res = condition->getCond()->getSatisfied();
4736+ if (res != Condition::RTRUE) res = Condition::RUNKNOWN;
4737+ condition->setSatisfied(res);
4738+ }
4739+
4740+ void EvalAndSetVisitor::_accept(PetriEngine::PQL::UntilCondition *condition) {
4741+ condition->getCond1()->visit(*this);
4742+ condition->getCond2()->visit(*this);
4743+ auto r2 = condition->getCond2()->getSatisfied();
4744+ if (r2 != Condition::RFALSE) {
4745+ condition->setSatisfied(r2);
4746+ return;
4747+ }
4748+ auto r1 = condition->getCond1()->getSatisfied();
4749+ if (r1 == Condition::RFALSE) {
4750+ condition->setSatisfied(Condition::RFALSE);
4751+ return;
4752+ }
4753+ condition->setSatisfied(Condition::RUNKNOWN);
4754+ }
4755+
4756+ void EvalAndSetVisitor::_accept(NotCondition *element) {
4757+ element->getCond()->visit(*this);
4758+ auto res = element->getCond()->getSatisfied();
4759+ if (res != Condition::RUNKNOWN) res = res == Condition::RFALSE ? Condition::RTRUE : Condition::RFALSE;
4760+ element->setSatisfied(res);
4761+ }
4762+
4763+ void EvalAndSetVisitor::_accept(AndCondition *element) {
4764+ auto res = Condition::RTRUE;
4765+ for (auto &c : *element) {
4766+ c->visit(*this);
4767+ auto r = c->getSatisfied();
4768+ if (r == Condition::RFALSE) res = Condition::RFALSE;
4769+ else if (r == Condition::RUNKNOWN && res != Condition::RFALSE) res = Condition::RUNKNOWN;
4770+ }
4771+ element->setSatisfied(res);
4772+ }
4773+
4774+ void EvalAndSetVisitor::_accept(OrCondition *element) {
4775+ auto res = Condition::RFALSE;
4776+ for (auto &c : *element) {
4777+ c->visit(*this);
4778+ auto r = c->getSatisfied();
4779+ if (r == Condition::RTRUE) res = Condition::RTRUE;
4780+ else if (r == Condition::RUNKNOWN && res != Condition::RTRUE) res = Condition::RUNKNOWN;
4781+ }
4782+ element->setSatisfied(res);
4783+ }
4784+
4785+ void EvalAndSetVisitor::_accept(LessThanCondition *element) {
4786+ element->evalAndSet(_context);
4787+ }
4788+
4789+ void EvalAndSetVisitor::_accept(LessThanOrEqualCondition *element) {
4790+ element->evalAndSet(_context);
4791+ }
4792+
4793+ void EvalAndSetVisitor::_accept(EqualCondition *element) {
4794+ element->evalAndSet(_context);
4795+ }
4796+
4797+ void EvalAndSetVisitor::_accept(NotEqualCondition *element) {
4798+ element->evalAndSet(_context);
4799+ }
4800+
4801+ void EvalAndSetVisitor::_accept(DeadlockCondition *element) {
4802+ element->evalAndSet(_context);
4803+ }
4804+
4805+ void EvalAndSetVisitor::_accept(CompareConjunction *element) {
4806+ element->evalAndSet(_context);
4807+ }
4808+
4809+ void EvalAndSetVisitor::_accept(UnfoldedUpperBoundsCondition *element) {
4810+ element->evalAndSet(_context);
4811+ }
4812+
4813+ void EvalAndSetVisitor::_accept(BooleanCondition *element) {
4814+ element->evalAndSet(_context);
4815+ }
4816+}
4817
4818=== added file 'src/LTL/Stubborn/SafeAutStubbornSet.cpp'
4819--- src/LTL/Stubborn/SafeAutStubbornSet.cpp 1970-01-01 00:00:00 +0000
4820+++ src/LTL/Stubborn/SafeAutStubbornSet.cpp 2021-08-05 10:56:53 +0000
4821@@ -0,0 +1,64 @@
4822+/* Copyright (C) 2021 Nikolaj J. Ulrik <nikolaj@njulrik.dk>,
4823+ * Simon M. Virenfeldt <simon@simwir.dk>
4824+ *
4825+ * This program is free software: you can redistribute it and/or modify
4826+ * it under the terms of the GNU General Public License as published by
4827+ * the Free Software Foundation, either version 3 of the License, or
4828+ * (at your option) any later version.
4829+ *
4830+ * This program is distributed in the hope that it will be useful,
4831+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
4832+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
4833+ * GNU General Public License for more details.
4834+ *
4835+ * You should have received a copy of the GNU General Public License
4836+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
4837+ */
4838+
4839+#include "LTL/Stubborn/SafeAutStubbornSet.h"
4840+
4841+namespace LTL {
4842+ using namespace PetriEngine;
4843+
4844+ bool SafeAutStubbornSet::prepare(const LTL::Structures::ProductState *state)
4845+ {
4846+ reset();
4847+ _parent = state;
4848+ memset(_places_seen.get(), 0, _net.numberOfPlaces());
4849+
4850+ constructEnabled();
4851+ if (_ordering.empty()) {
4852+ return false;
4853+ }
4854+ if (_ordering.size() == 1) {
4855+ _stubborn[_ordering.front()] = true;
4856+ return true;
4857+ }
4858+
4859+ InterestingTransitionVisitor interesting{*this, false};
4860+
4861+ for (auto &q : _queries) {
4862+ q->evalAndSet(PQL::EvaluationContext((*_parent).marking(), &_net));
4863+ q->visit(interesting);
4864+ }
4865+ assert(!_bad);
4866+
4867+ _unsafe.swap(_stubborn);
4868+ //memset(_stubborn.get(), false, sizeof(bool) * _net.numberOfTransitions());
4869+ _unprocessed.clear();
4870+ memset(_places_seen.get(), 0, _net.numberOfPlaces());
4871+
4872+ assert(_unprocessed.empty());
4873+
4874+ for (auto &q : _queries) {
4875+ q->visit(interesting);
4876+ closure();
4877+ if (_bad) {
4878+ // abort
4879+ set_all_stubborn();
4880+ return true;
4881+ }
4882+ }
4883+ return true;
4884+ }
4885+}
4886\ No newline at end of file
4887
4888=== added file 'src/LTL/Stubborn/VisibleLTLStubbornSet.cpp'
4889--- src/LTL/Stubborn/VisibleLTLStubbornSet.cpp 1970-01-01 00:00:00 +0000
4890+++ src/LTL/Stubborn/VisibleLTLStubbornSet.cpp 2021-08-05 10:56:53 +0000
4891@@ -0,0 +1,176 @@
4892+/* Copyright (C) 2021 Nikolaj J. Ulrik <nikolaj@njulrik.dk>,
4893+ * Simon M. Virenfeldt <simon@simwir.dk>
4894+ *
4895+ * This program is free software: you can redistribute it and/or modify
4896+ * it under the terms of the GNU General Public License as published by
4897+ * the Free Software Foundation, either version 3 of the License, or
4898+ * (at your option) any later version.
4899+ *
4900+ * This program is distributed in the hope that it will be useful,
4901+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
4902+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
4903+ * GNU General Public License for more details.
4904+ *
4905+ * You should have received a copy of the GNU General Public License
4906+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
4907+ */
4908+
4909+#include "PetriEngine/Stubborn/InterestingTransitionVisitor.h"
4910+#include "LTL/Stubborn/VisibleLTLStubbornSet.h"
4911+#include "LTL/Stubborn/EvalAndSetVisitor.h"
4912+
4913+using namespace PetriEngine;
4914+using namespace PetriEngine::PQL;
4915+
4916+namespace LTL {
4917+ bool VisibleLTLStubbornSet::prepare(const PetriEngine::Structures::State *marking) {
4918+ reset();
4919+ _parent = marking;
4920+ PQL::EvaluationContext evaluationContext{_parent->marking(), &_net};
4921+ memset(_places_seen.get(), 0, _net.numberOfPlaces());
4922+ constructEnabled();
4923+ if (_ordering.empty()) return false;
4924+ if (_ordering.size() == 1) {
4925+ _stubborn[_ordering.front()] = true;
4926+ return true;
4927+ }
4928+ //TODO needed? We do not run Interesting visitor so we do not immediately need it, but is is needed by closure?
4929+ for (auto &q : _queries) {
4930+ EvalAndSetVisitor evalAndSetVisitor{evaluationContext};
4931+ q->visit(evalAndSetVisitor);
4932+ }
4933+ findKeyTransition();
4934+
4935+ ensureRuleV();
4936+
4937+ ensureRulesL();
4938+
4939+ _nenabled = _ordering.size();
4940+
4941+
4942+ if (!_has_enabled_stubborn) {
4943+ memset(_stubborn.get(), 1, _net.numberOfTransitions());
4944+ }
4945+#ifdef STUBBORN_STATISTICS
4946+ float num_stubborn = 0;
4947+ float num_enabled = 0;
4948+ float num_enabled_stubborn = 0;
4949+ for (int i = 0; i < _net.numberOfTransitions(); ++i) {
4950+ if (_stubborn[i]) ++num_stubborn;
4951+ if (_fallback_spooler[i]) ++num_enabled;
4952+ if (_stubborn[i] && _fallback_spooler[i]) ++num_enabled_stubborn;
4953+ }
4954+ std::cerr << "Enabled: " << num_enabled << "/" << _net.numberOfTransitions() << " (" << num_enabled/_net.numberOfTransitions()*100.0 << "%),\t\t "
4955+ << "Stubborn: " << num_stubborn << "/" << _net.numberOfTransitions() << " (" << num_stubborn/_net.numberOfTransitions()*100.0 << "%),\t\t "
4956+ << "Enabled stubborn: " << num_enabled_stubborn << "/" << num_enabled << " (" << num_enabled_stubborn/num_enabled*100.0 << "%)" << std::endl;
4957+#endif
4958+ return true;
4959+ }
4960+
4961+ uint32_t VisibleLTLStubbornSet::next() {
4962+ while (!_ordering.empty()) {
4963+ _current = _ordering.front();
4964+ _ordering.pop_front();
4965+ if (_stubborn[_current] && _enabled[_current]) {
4966+ return _current;
4967+ }
4968+ else {
4969+ _skipped.push_back(_current);
4970+ }
4971+ }
4972+ reset();
4973+ return std::numeric_limits<uint32_t>::max();
4974+ }
4975+
4976+ void VisibleLTLStubbornSet::findKeyTransition() {
4977+ // try to find invisible key transition first
4978+ assert(!_ordering.empty());
4979+ auto tkey = _ordering.front();
4980+ if (_visible[tkey]) {
4981+ for (uint32_t tid = 0; tid < _net.numberOfTransitions(); ++tid) {
4982+ if (_enabled[tid] && !_visible[tid]) {
4983+ tkey = tid;
4984+ break;
4985+ }
4986+ }
4987+ }
4988+ addToStub(tkey);
4989+
4990+ // include relevant transitions
4991+ auto ptr = transitions()[tkey];
4992+ uint32_t finv = ptr.inputs;
4993+ uint32_t linv = ptr.outputs;
4994+
4995+ for (; finv < linv; ++finv) {
4996+ auto inv = invariants()[finv];
4997+ postsetOf(inv.place, true);
4998+ }
4999+ }
5000+
The diff has been truncated for viewing.

Subscribers

People subscribed via source and target branches