Dear Spot Team,
I am a postdoc at UCL working on the Cyclist theorem-proving framework, which makes use of Spot. We have recently upgraded Cyclist to use Spot 2.1 from Spot 1.2.4, and one of our benchmark tests has thrown up a possible bug in the Spot library.
Cyclist searches for cyclic proofs, and validates them by building two Buchi automata out of a proof and checking language inclusion (ℒ(A) ⊆ ℒ(B)), which we do by first complementing B and then checking emptiness of the product with A.
With version 1.2.4, we did this using with the following code:
spot::twa a = ...; spot::twa b = ...;
spot::tgba_safra_complement complement(&b); spot::tgba_product product(&a, &b); spot::couvreur99_check ec(&product); spot::emptiness_check_result * res = ec.check();
Since version 2.1 does not provide the tgba_safra_complement class, we now use the following code:
spot::twa_graph_ptr a = ...; spot::twa_graph_ptr b = ...;
spot::twa_graph_ptr det = tgba_determinize(b); spot::twa_graph_ptr complement = to_generalized_buchi(dtwa_complement(det)); spot::const_twa_ptr product = std::make_sharedspot::twa_product(proof, complement); spot::couvreur99_check ec(product); std::shared_ptrspot::emptiness_check_result res = ec.check();
Our benchmark test generates the following automata (given in dot format) for a and b, respectively:
digraph G { rankdir=LR I [label="", style=invis, width=0] I -> 0 0 [label="0"] 0 -> 1 [label="p_0 & !p_1 & !p_2 & !p_3\n{0}"] 1 [label="S1 t_53"] 1 -> 2 [label="!p_0 & p_1 & !p_2 & !p_3\n{0}"] 2 [label="S2 t_53,t_49,t_50,t_51,t_52"] 2 -> 2 [label="!p_0 & p_1 & !p_2 & !p_3\n{0}"] 2 -> 3 [label="p_0 & p_1 & !p_2 & !p_3\n{0}"] 3 [label="S3 t_53,t_52"] 3 -> 4 [label="p_0 & !p_1 & p_2 & !p_3\n{0}"] 3 -> 5 [label="!p_0 & !p_1 & p_2 & !p_3\n{0}"] 4 [label="S5 t_53"] 4 -> 4 [label="p_0 & !p_1 & p_2 & !p_3\n{0}"] 5 [label="S4 t_53"] 5 -> 5 [label="!p_0 & !p_1 & p_2 & !p_3\n{0}"] }
digraph G { rankdir=LR I [label="", style=invis, width=0] I -> 0 0 [label="init"] 0 -> 1 [label="!p_0 & !p_1 & p_2 & !p_3"] 0 -> 2 [label="p_0 & p_1 & !p_2 & !p_3"] 0 -> 3 [label="p_0 & p_1 & !p_2 & !p_3"] 0 -> 4 [label="!p_0 & p_1 & !p_2 & !p_3"] 0 -> 5 [label="!p_0 & p_1 & !p_2 & !p_3"] 0 -> 6 [label="!p_0 & p_1 & !p_2 & !p_3"] 0 -> 7 [label="!p_0 & p_1 & !p_2 & !p_3"] 0 -> 8 [label="!p_0 & p_1 & !p_2 & !p_3"] 0 -> 9 [label="p_0 & !p_1 & p_2 & !p_3"] 0 -> 10 [label="p_0 & !p_1 & !p_2 & !p_3"] 0 -> 0 [label="1"] 1 [label="S4,t53"] 1 -> 1 [label="!p_0 & !p_1 & p_2 & !p_3\n{0}"] 2 [label="S3,t52"] 2 -> 9 [label="p_0 & !p_1 & p_2 & !p_3\n{0}"] 3 [label="S3,t53"] 3 -> 1 [label="!p_0 & !p_1 & p_2 & !p_3\n{0}"] 4 [label="S2,t52"] 5 [label="S2,t51"] 5 -> 2 [label="p_0 & p_1 & !p_2 & !p_3"] 6 [label="S2,t50"] 7 [label="S2,t49"] 7 -> 3 [label="p_0 & p_1 & !p_2 & !p_3"] 8 [label="S2,t53"] 8 -> 6 [label="!p_0 & p_1 & !p_2 & !p_3\n{0}"] 8 -> 8 [label="!p_0 & p_1 & !p_2 & !p_3\n{0}"] 9 [label="S5,t53"] 9 -> 9 [label="p_0 & !p_1 & p_2 & !p_3\n{0}"] 10 [label="S1,t53"] 10 -> 6 [label="!p_0 & p_1 & !p_2 & !p_3\n{0}"] 10 -> 8 [label="!p_0 & p_1 & !p_2 & !p_3"] }
The code I gave above returns a non-null (i.e. non-empty) result, however we expect that this should actually return an empty result.
What I have discovered is that the code above does indeed return an empty result when the use_simulation flag to the tgba_determinize function is set to false (the values of the other two flags do not make a difference). Thus, it seems to me that this is a bug, no?
I am happy to provide any more information which is necessary for diagnosis.
Best wishes,
Reuben Rowe.
Hi Reuben,
Thank you for the report. Using the Python code at the end of this mail (you can copy and paste it on http://spot-sandbox.lrde.epita.fr/ if you do not have built the Python bindings), I have be able to reproduce your issue, and even reduce the automata by a few states. I agree this looks very bogus: flipping the use_simulation flags should not change the result. The two deterministic automata produced are quite different (6 single-state SCCs vs. 1 five-state SCC) so I hope the bug will be easy to find. I'll try to have a look at it when I'm more rested (I know how this option is supposed to work, but I'm not very familiar with the implementation).
In the meantime, I suspect it is safer to disable this use_simulation flag in your code. Also the Python code belows includes a few suggestions you might want to transpose in C++: - use remove_fin() instead of to_generalize_buchi(): removing Fin acceptance is the first step of to_generalized_buchi(), but it is enough to call an emptiness check, - prefer otf_product() to std::make_shared<twa_product>() -- but that's cosmetics, - use product->is_empty() (or product->accepting_word() if you do need to compute a word), this will create couvreur99_check and run it for you, but in the future it may also select more efficient emptiness checks depending on the strength of the automaton to check.
I'm also curious if you have any feedback about how difficult the move from 1.2.4 to 2.1 was.
#!/usr/bin/python3
import spot
a = spot.automaton(""" HOA: v1 States: 5 Start: 4 AP: 3 "p_0" "p_1" "p_2" Acceptance: 1 Inf(0) --BODY-- State: 0 [0&!1&2] 1 [!0&!1&2] 2 State: 1 {0} [0&!1&2] 1 State: 2 {0} [!0&!1&2] 2 State: 3 {0} [0&1&!2] 0 [!0&1&!2] 3 State: 4 [!0&1&!2] 3 --END-- """)
b = spot.automaton(""" HOA: v1 States: 8 Start: 0 AP: 3 "p_0" "p_1" "p_2" Acceptance: 1 Inf(0) --BODY-- State: 0 [t] 0 [!0&!1&2] 1 [!0&1&!2] 4 [!0&1&!2] 5 [!0&1&!2] 6 State: 1 {0} [!0&!1&2] 1 State: 2 [0&!1&2] 7 State: 3 [!0&!1&2] 1 State: 4 [0&1&!2] 2 State: 5 [0&1&!2] 3 State: 6 {0} [!0&1&!2] 6 State: 7 {0} [0&!1&2] 7 --END-- """);
print("use_simulation=True") b1 = spot.tgba_determinize(b, False, True, True, True) b1 = spot.remove_fin(spot.dtwa_complement(b1)) print(spot.otf_product(a, b1).accepting_word())
print("\nuse_simulation=False") b2 = spot.tgba_determinize(b, False, True, False, True) b2 = spot.remove_fin(spot.dtwa_complement(b2)) print(spot.otf_product(a, b2).accepting_word())
Hi Reuben,
I think I fixed it. Until the next release (probably next week) you may want to fix spot/twaalogs/simulation.cc as in
https://gitlab.lrde.epita.fr/spot/spot/commit/d919b78c890cbdf25ad35706b9427c...
Thank you again