Commit 9ab4b840 authored by Alexandre Duret-Lutz's avatar Alexandre Duret-Lutz
Browse files

simulation: do not depend on bdd numbers for ordering classes

Fixes #262 again.  Reported by Maximilien Colange.

* spot/twaalgos/simulation.cc: Use state numbers to order classes, not
their signatures.  The problem was that even if two simulation of the
same automaton assign the same signature, the BDD identifier used for
that signature might be different, and therefore the ordering obtained
by using BDDs as keys in a map can be different.  A side-effect of
this change is that the order of states in automata produced by
simulation-based reduction may change; many tests had to be updated.
* tests/core/ltl2tgba.test: Add a new test case based on Maximilien's
report.
* tests/core/complement.test, tests/core/det.test,
tests/core/parseaut.test, tests/core/prodor.test, tests/core/scc.test,
tests/python/atva16-fig2a.ipynb, tests/python/automata.ipynb,
tests/python/decompose.ipynb, tests/python/decompose_scc.py,
tests/python/highlighting.ipynb, tests/python/piperead.ipynb,
tests/python/sccinfo.py, tests/python/simstate.py,
tests/python/testingaut.ipynb, tests/python/word.ipynb: Update
test case for new order of states.
parent 101c2533
...@@ -275,7 +275,7 @@ namespace spot ...@@ -275,7 +275,7 @@ namespace spot
// We run through the map bdd/list<state>, and we update // We run through the map bdd/list<state>, and we update
// the previous_class_ with the new data. // the previous_class_ with the new data.
for (auto& p: bdd_lstate_) for (auto& p: sorted_classes_)
{ {
// If the signature of a state is bddfalse (no // If the signature of a state is bddfalse (no
// edges) the class of this state is bddfalse // edges) the class of this state is bddfalse
...@@ -283,11 +283,11 @@ namespace spot ...@@ -283,11 +283,11 @@ namespace spot
// simplifications in the signature by removing a // simplifications in the signature by removing a
// edge which has as a destination a state with // edge which has as a destination a state with
// no outgoing edge. // no outgoing edge.
if (p.first == bddfalse) if (p->first == bddfalse)
for (auto s: p.second) for (unsigned s: p->second)
previous_class_[s] = bddfalse; previous_class_[s] = bddfalse;
else else
for (auto s: p.second) for (unsigned s: p->second)
previous_class_[s] = *it_bdd; previous_class_[s] = *it_bdd;
++it_bdd; ++it_bdd;
} }
...@@ -302,10 +302,10 @@ namespace spot ...@@ -302,10 +302,10 @@ namespace spot
{ {
update_previous_class(); update_previous_class();
nb_partition_before = bdd_lstate_.size(); nb_partition_before = bdd_lstate_.size();
bdd_lstate_.clear();
nb_po_before = po_size_; nb_po_before = po_size_;
po_size_ = 0; po_size_ = 0;
update_sig(); update_sig();
// print_partition();
go_to_next_it(); go_to_next_it();
} }
...@@ -347,8 +347,18 @@ namespace spot ...@@ -347,8 +347,18 @@ namespace spot
void update_sig() void update_sig()
{ {
bdd_lstate_.clear();
sorted_classes_.clear();
for (unsigned s = 0; s < size_a_; ++s) for (unsigned s = 0; s < size_a_; ++s)
bdd_lstate_[compute_sig(s)].emplace_back(s); {
bdd sig = compute_sig(s);
auto p = bdd_lstate_.emplace(std::piecewise_construct,
std::make_tuple(sig),
std::make_tuple());
p.first->second.emplace_back(s);
if (p.second)
sorted_classes_.emplace_back(p.first);
}
} }
...@@ -390,7 +400,7 @@ namespace spot ...@@ -390,7 +400,7 @@ namespace spot
std::list<bdd>::iterator it_bdd = used_var_.begin(); std::list<bdd>::iterator it_bdd = used_var_.begin();
for (auto& p: bdd_lstate_) for (auto& p: sorted_classes_)
{ {
// If the signature of a state is bddfalse (no edges) the // If the signature of a state is bddfalse (no edges) the
// class of this state is bddfalse instead of an anonymous // class of this state is bddfalse instead of an anonymous
...@@ -398,9 +408,9 @@ namespace spot ...@@ -398,9 +408,9 @@ namespace spot
// removing an edge which has as a destination a state // removing an edge which has as a destination a state
// with no outgoing edge. // with no outgoing edge.
bdd acc = bddfalse; bdd acc = bddfalse;
if (p.first != bddfalse) if (p->first != bddfalse)
acc = *it_bdd; acc = *it_bdd;
now_to_next.emplace_back(p.first, acc); now_to_next.emplace_back(p->first, acc);
++it_bdd; ++it_bdd;
} }
...@@ -450,16 +460,16 @@ namespace spot ...@@ -450,16 +460,16 @@ namespace spot
if (implications) if (implications)
implications->resize(bdd_lstate_.size()); implications->resize(bdd_lstate_.size());
// Create one state per partition. // Create one state per class.
for (auto& p: bdd_lstate_) for (auto& p: sorted_classes_)
{ {
bdd cl = previous_class_[p.second.front()]; bdd cl = previous_class_[p->second.front()];
// A state may be referred to either by // A state may be referred to either by
// its class, or by all the implied classes. // its class, or by all the implied classes.
auto s = gb->new_state(cl.id()); auto s = gb->new_state(cl.id());
gb->alias_state(s, relation_[cl].id()); gb->alias_state(s, relation_[cl].id());
// update state_mapping // update state_mapping
for (auto& st : p.second) for (auto& st : p->second)
(*state_mapping)[st] = s; (*state_mapping)[st] = s;
if (implications) if (implications)
(*implications)[s] = relation_[cl]; (*implications)[s] = relation_[cl];
...@@ -479,14 +489,14 @@ namespace spot ...@@ -479,14 +489,14 @@ namespace spot
auto all_inf = all_inf_; auto all_inf = all_inf_;
// For each class, we will create // For each class, we will create
// all the edges between the states. // all the edges between the states.
for (auto& p: bdd_lstate_) for (auto& p: sorted_classes_)
{ {
// All states in p.second have the same class, so just // All states in p.second have the same class, so just
// pick the class of the first one first one. // pick the class of the first one first one.
bdd src = previous_class_[p.second.front()]; bdd src = previous_class_[p->second.front()];
// Get the signature to derive successors. // Get the signature to derive successors.
bdd sig = compute_sig(p.second.front()); bdd sig = compute_sig(p->second.front());
if (Cosimulation) if (Cosimulation)
sig = bdd_compose(sig, bddfalse, bdd_var(bdd_initial)); sig = bdd_compose(sig, bddfalse, bdd_var(bdd_initial));
...@@ -621,12 +631,12 @@ namespace spot ...@@ -621,12 +631,12 @@ namespace spot
// where is the new class name. // where is the new class name.
void print_partition() void print_partition()
{ {
for (auto& p: bdd_lstate_) for (auto& p: sorted_classes_)
{ {
std::cerr << "partition: " std::cerr << "partition: "
<< bdd_format_isop(a_->get_dict(), p.first) << bdd_format_isop(a_->get_dict(), p->first)
<< std::endl; << std::endl;
for (auto s: p.second) for (auto s: p->second)
std::cerr << " - " std::cerr << " - "
<< a_->format_state(a_->state_from_number(s)) << a_->format_state(a_->state_from_number(s))
<< '\n'; << '\n';
...@@ -654,9 +664,13 @@ namespace spot ...@@ -654,9 +664,13 @@ namespace spot
// Represent the class of each state at the previous iteration. // Represent the class of each state at the previous iteration.
vector_state_bdd previous_class_; vector_state_bdd previous_class_;
// The list of state for each class at the current_iteration. // The list of states for each class at the current_iteration.
// Computed in `update_sig'. // Computed in `update_sig'.
map_bdd_lstate bdd_lstate_; map_bdd_lstate bdd_lstate_;
// The above map, sorted by states number instead of BDD
// identifier to avoid non-determinism while iterating over all
// states.
std::vector<map_bdd_lstate::const_iterator> sorted_classes_;
// The queue of free bdd. They will be used as the identifier // The queue of free bdd. They will be used as the identifier
// for the class. // for the class.
......
...@@ -118,11 +118,11 @@ properties: trans-labels explicit-labels trans-acc complete ...@@ -118,11 +118,11 @@ properties: trans-labels explicit-labels trans-acc complete
properties: deterministic stutter-invariant properties: deterministic stutter-invariant
--BODY-- --BODY--
State: 0 State: 0
[0] 1 {1}
[!0] 0 [!0] 0
State: 1
[0] 1 {1} [0] 1 {1}
State: 1
[!0] 0 {0} [!0] 0 {0}
[0] 1 {1}
--END-- --END--
EOF EOF
diff out expected diff out expected
#!/bin/sh #!/bin/sh
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (C) 2013, 2014, 2015, 2016 Laboratoire de Recherche et # Copyright (C) 2013-2017 Laboratoire de Recherche et Développement de
# Développement de l'Epita (LRDE). # l'Epita (LRDE).
# #
# This file is part of Spot, a model checking library. # This file is part of Spot, a model checking library.
# #
...@@ -148,20 +148,20 @@ cat >expected-nd.hoa <<EOF ...@@ -148,20 +148,20 @@ cat >expected-nd.hoa <<EOF
HOA: v1.1 HOA: v1.1
name: "XGa R (!a & Fa)" name: "XGa R (!a & Fa)"
States: 2 States: 2
Start: 1 Start: 0
AP: 1 "a" AP: 1 "a"
acc-name: Buchi acc-name: Buchi
Acceptance: 1 Inf(0) Acceptance: 1 Inf(0)
properties: trans-labels explicit-labels state-acc !complete properties: trans-labels explicit-labels state-acc !complete
properties: !deterministic properties: !deterministic
spot.highlight.states: 1 1 spot.highlight.states: 0 1
spot.highlight.edges: 2 2 3 2 spot.highlight.edges: 1 2 2 2
--BODY-- --BODY--
State: 0 {0} State: 0
[0] 0
State: 1
[!0] 0 [!0] 0
[!0] 1 [!0] 1
State: 1 {0}
[0] 1
--END-- --END--
EOF EOF
diff expected-nd.hoa out-nd.hoa diff expected-nd.hoa out-nd.hoa
...@@ -173,19 +173,19 @@ cat >expected-rand.hoa <<EOF ...@@ -173,19 +173,19 @@ cat >expected-rand.hoa <<EOF
HOA: v1.1 HOA: v1.1
name: "XGa R (!a & Fa)" name: "XGa R (!a & Fa)"
States: 2 States: 2
Start: 0 Start: 1
AP: 1 "a" AP: 1 "a"
acc-name: Buchi acc-name: Buchi
Acceptance: 1 Inf(0) Acceptance: 1 Inf(0)
properties: trans-labels explicit-labels state-acc !complete properties: trans-labels explicit-labels state-acc !complete
properties: !deterministic properties: !deterministic
spot.highlight.states: 0 1 spot.highlight.states: 1 1
--BODY-- --BODY--
State: 0 State: 0 {0}
[!0] 0 [0] 0
State: 1
[!0] 1 [!0] 1
State: 1 {0} [!0] 0
[0] 1
--END-- --END--
EOF EOF
diff expected-rand.hoa rand.hoa diff expected-rand.hoa rand.hoa
...@@ -197,20 +197,20 @@ cat >expected-rand2.hoa <<EOF ...@@ -197,20 +197,20 @@ cat >expected-rand2.hoa <<EOF
HOA: v1.1 HOA: v1.1
name: "XGa R (!a & Fa)" name: "XGa R (!a & Fa)"
States: 2 States: 2
Start: 0 Start: 1
AP: 1 "a" AP: 1 "a"
acc-name: Buchi acc-name: Buchi
Acceptance: 1 Inf(0) Acceptance: 1 Inf(0)
properties: trans-labels explicit-labels state-acc !complete properties: trans-labels explicit-labels state-acc !complete
properties: !deterministic properties: !deterministic
spot.highlight.states: 0 5 spot.highlight.states: 1 5
spot.highlight.edges: 1 5 2 5 spot.highlight.edges: 2 5 3 5
--BODY-- --BODY--
State: 0 State: 0 {0}
[!0] 0 [0] 0
State: 1
[!0] 1 [!0] 1
State: 1 {0} [!0] 0
[0] 1
--END-- --END--
EOF EOF
diff expected-rand2.hoa rand2.hoa diff expected-rand2.hoa rand2.hoa
......
...@@ -255,3 +255,11 @@ ltl2tgba --low --any 'Xp1 xor (Fp1 M (!p1 M (Fp0 W p1)))' \ ...@@ -255,3 +255,11 @@ ltl2tgba --low --any 'Xp1 xor (Fp1 M (!p1 M (Fp0 W p1)))' \
ltl2tgba --low --any 'Xp1 xor (Fp1 M (!p1 M (Fp0 W p1)))' "$s" >res2 ltl2tgba --low --any 'Xp1 xor (Fp1 M (!p1 M (Fp0 W p1)))' "$s" >res2
ltl2tgba --low --any 'Fp0 -> XXG(1 U Gp1)' "$s" >>res2 ltl2tgba --low --any 'Fp0 -> XXG(1 U Gp1)' "$s" >>res2
diff res1 res2 diff res1 res2
# Another case where different but isomorphic automata
# were output (issue #262 again).
f1='F(Gp0 <-> Gp1)'
f2='Gp1 | FGp0'
(ltl2tgba -xsimul=1 --low "$f1"; ltl2tgba -xsimul=1 --low "$f2") > res1
ltl2tgba -xsimul=1 --low "$f1" "$f2" > res2
diff res1 res2
...@@ -1712,7 +1712,7 @@ cat >expected <<EOF ...@@ -1712,7 +1712,7 @@ cat >expected <<EOF
HOA: v1 HOA: v1
name: "(c U d) & G(Fa & Fb)" name: "(c U d) & G(Fa & Fb)"
States: 3 States: 3
Start: 1 Start: 0
AP: 4 "c" "d" "a" "b" AP: 4 "c" "d" "a" "b"
acc-name: generalized-Buchi 2 acc-name: generalized-Buchi 2
Acceptance: 2 Inf(0)&Inf(1) Acceptance: 2 Inf(0)&Inf(1)
...@@ -1720,39 +1720,39 @@ properties: implicit-labels trans-acc complete deterministic ...@@ -1720,39 +1720,39 @@ properties: implicit-labels trans-acc complete deterministic
properties: stutter-invariant properties: stutter-invariant
--BODY-- --BODY--
State: 0 State: 0
2
0 0
1
1
2
0 0
1
1
2
0 0
1
1
2
0 0
0 {0} 1
0 {0} 1
0 {0}
0 {0}
0 {1}
0 {1}
0 {1}
0 {1}
0 {0 1}
0 {0 1}
0 {0 1}
0 {0 1}
State: 1 State: 1
2
1 1
0
0
2
1 1
0
0
2
1 1
0
0
2
1 1
0 1 {0}
0 1 {0}
1 {0}
1 {0}
1 {1}
1 {1}
1 {1}
1 {1}
1 {0 1}
1 {0 1}
1 {0 1}
1 {0 1}
State: 2 State: 2
2 2
2 2
......
#!/bin/sh #!/bin/sh
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (C) 2015 Laboratoire de Recherche et Développement # Copyright (C) 2015, 2017 Laboratoire de Recherche et Développement
# de l'Epita (LRDE). # de l'Epita (LRDE).
# #
# This file is part of Spot, a model checking library. # This file is part of Spot, a model checking library.
...@@ -39,10 +39,10 @@ properties: trans-labels explicit-labels trans-acc complete ...@@ -39,10 +39,10 @@ properties: trans-labels explicit-labels trans-acc complete
properties: stutter-invariant properties: stutter-invariant
--BODY-- --BODY--
State: 0 State: 0
[0&1] 1 {1}
[!0&1] 1
[0] 0 {1} [0] 0 {1}
[!0] 0 [!0] 0
[0&1] 1 {1}
[!0&1] 1
State: 1 State: 1
[0&1] 1 {0 1} [0&1] 1 {0 1}
[!0&1] 1 {0} [!0&1] 1 {0}
...@@ -70,10 +70,10 @@ Acceptance: 2 Inf(0)&Inf(1) ...@@ -70,10 +70,10 @@ Acceptance: 2 Inf(0)&Inf(1)
properties: trans-labels explicit-labels trans-acc stutter-invariant properties: trans-labels explicit-labels trans-acc stutter-invariant
--BODY-- --BODY--
State: 0 State: 0
[0&1] 1 {1}
[!0&1] 1
[0] 0 {1} [0] 0 {1}
[!0] 0 [!0] 0
[0&1] 1 {1}
[!0&1] 1
State: 1 State: 1
[0&1] 1 {0 1} [0&1] 1 {0 1}
[!0&1] 1 {0} [!0&1] 1 {0}
......
...@@ -93,8 +93,8 @@ Acceptance: 1 Inf(0) ...@@ -93,8 +93,8 @@ Acceptance: 1 Inf(0)
properties: trans-labels explicit-labels state-acc properties: trans-labels explicit-labels state-acc
--BODY-- --BODY--
State: 0 State: 0
[0&2] 1
[0] 0 [0] 0
[0&2] 1
State: 1 {0} State: 1 {0}
[2] 1 [2] 1
--END-- --END--
......
...@@ -97,85 +97,85 @@ ...@@ -97,85 +97,85 @@
"<text text-anchor=\"start\" x=\"121.75\" y=\"-319.8\" font-family=\"Lato\" font-size=\"14.00\" fill=\"#f17cb0\">\u2776</text>\n", "<text text-anchor=\"start\" x=\"121.75\" y=\"-319.8\" font-family=\"Lato\" font-size=\"14.00\" fill=\"#f17cb0\">\u2776</text>\n",
"<text text-anchor=\"start\" x=\"137.75\" y=\"-319.8\" font-family=\"Lato\" font-size=\"14.00\">)</text>\n", "<text text-anchor=\"start\" x=\"137.75\" y=\"-319.8\" font-family=\"Lato\" font-size=\"14.00\">)</text>\n",
"<!-- I -->\n", "<!-- I -->\n",
"<!-- 1 -->\n", "<!-- 0 -->\n",
"<g id=\"node2\" class=\"node\"><title>1</title>\n", "<g id=\"node2\" class=\"node\"><title>0</title>\n",
"<ellipse fill=\"#ffffaa\" stroke=\"black\" cx=\"56\" cy=\"-156\" rx=\"18\" ry=\"18\"/>\n", "<ellipse fill=\"#ffffaa\" stroke=\"black\" cx=\"56\" cy=\"-75\" rx=\"18\" ry=\"18\"/>\n",
"<text text-anchor=\"middle\" x=\"56\" y=\"-152.3\" font-family=\"Lato\" font-size=\"14.00\">1</text>\n", "<text text-anchor=\"middle\" x=\"56\" y=\"-71.3\" font-family=\"Lato\" font-size=\"14.00\">0</text>\n",
"</g>\n", "</g>\n",
"<!-- I&#45;&gt;1 -->\n", "<!-- I&#45;&gt;0 -->\n",
"<g id=\"edge1\" class=\"edge\"><title>I&#45;&gt;1</title>\n", "<g id=\"edge1\" class=\"edge\"><title>I&#45;&gt;0</title>\n",
"<path fill=\"none\" stroke=\"black\" d=\"M1.15491,-156C2.79388,-156 17.1543,-156 30.6317,-156\"/>\n", "<path fill=\"none\" stroke=\"black\" d=\"M1.15491,-75C2.79388,-75 17.1543,-75 30.6317,-75\"/>\n",
"<polygon fill=\"black\" stroke=\"black\" points=\"37.9419,-156 30.9419,-159.15 34.4419,-156 30.9419,-156 30.9419,-156 30.9419,-156 34.4419,-156 30.9418,-152.85 37.9419,-156 37.9419,-156\"/>\n", "<polygon fill=\"black\" stroke=\"black\" points=\"37.9419,-75 30.9419,-78.1501 34.4419,-75 30.9419,-75.0001 30.9419,-75.0001 30.9419,-75.0001 34.4419,-75 30.9418,-71.8501 37.9419,-75 37.9419,-75\"/>\n",
"</g>\n", "</g>\n",
"<!-- 1&#45;&gt;1 -->\n", "<!-- 0&#45;&gt;0 -->\n",
"<g id=\"edge4\" class=\"edge\"><title>1&#45;&gt;1</title>\n", "<g id=\"edge2\" class=\"edge\"><title>0&#45;&gt;0</title>\n",
"<path fill=\"none\" stroke=\"black\" d=\"M49.6208,-173.037C48.3189,-182.858 50.4453,-192 56,-192 60.166,-192 62.4036,-186.858 62.7128,-180.143\"/>\n", "<path fill=\"none\" stroke=\"black\" d=\"M49.6208,-92.0373C48.3189,-101.858 50.4453,-111 56,-111 60.166,-111 62.4036,-105.858 62.7128,-99.1433\"/>\n",
"<polygon fill=\"black\" stroke=\"black\" points=\"62.3792,-173.037 65.8541,-179.882 62.5434,-176.533 62.7076,-180.03 62.7076,-180.03 62.7076,-180.03 62.5434,-176.533 59.561,-180.177 62.3792,-173.037 62.3792,-173.037\"/>\n", "<polygon fill=\"black\" stroke=\"black\" points=\"62.3792,-92.0373 65.8541,-98.8818 62.5434,-95.5335 62.7076,-99.0296 62.7076,-99.0296 62.7076,-99.0296 62.5434,-95.5335 59.561,-99.1774 62.3792,-92.0373 62.3792,-92.0373\"/>\n",
"<text text-anchor=\"middle\" x=\"56\" y=\"-195.8\" font-family=\"Lato\" font-size=\"14.00\">1</text>\n", "<text text-anchor=\"start\" x=\"51.5\" y=\"-114.8\" font-family=\"Lato\" font-size=\"14.00\">1</text>\n",
"</g>\n", "</g>\n",
"<!-- 0 -->\n", "<!-- 1 -->\n",
"<g id=\"node3\" class=\"node\"><title>0</title>\n", "<g id=\"node3\" class=\"node\"><title>1</title>\n",
"<ellipse fill=\"#ffffaa\" stroke=\"black\" cx=\"169\" cy=\"-247\" rx=\"18\" ry=\"18\"/>\n", "<ellipse fill=\"#ffffaa\" stroke=\"black\" cx=\"169\" cy=\"-118\" rx=\"18\" ry=\"18\"/>\n",
"<text text-anchor=\"middle\" x=\"169\" y=\"-243.3\" font-family=\"Lato\" font-size=\"14.00\">0</text>\n", "<text text-anchor=\"start\" x=\"164.5\" y=\"-114.3\" font-family=\"Lato\" font-size=\"14.00\">1</text>\n",
"</g>\n", "</g>\n",
"<!-- 1&#45;&gt;0 -->\n", "<!-- 0&#45;&gt;1 -->\n",
"<g id=\"edge3\" class=\"edge\"><title>1&#45;&gt;0</title>\n", "<g id=\"edge3\" class=\"edge\"><title>0&#45;&gt;1</title>\n",
"<path fill=\"none\" stroke=\"black\" d=\"M70.6108,-167.158C90.2734,-183.278 126.706,-213.147 149.007,-231.429\"/>\n", "<path fill=\"none\" stroke=\"black\" d=\"M73.3784,-81.3448C92.4242,-88.7229 123.976,-100.946 145.359,-109.229\"/>\n",
"<polygon fill=\"black\" stroke=\"black\" points=\"154.625,-236.035 147.214,-234.033 151.918,-233.816 149.211,-231.597 149.211,-231.597 149.211,-231.597 151.918,-233.816 151.208,-229.161 154.625,-236.035 154.625,-236.035\"/>\n", "<polygon fill=\"black\" stroke=\"black\" points=\"151.915,-111.769 144.25,-112.178 148.652,-110.505 145.388,-109.24 145.388,-109.24 145.388,-109.24 148.652,-110.505 146.526,-106.303 151.915,-111.769 151.915,-111.769\"/>\n",
"<text text-anchor=\"start\" x=\"92\" y=\"-220.8\" font-family=\"Lato\" font-size=\"14.00\">!a &amp; !b</text>\n", "<text text-anchor=\"start\" x=\"99\" y=\"-107.8\" font-family=\"Lato\" font-size=\"14.00\">a | b</text>\n",
"</g>\n", "</g>\n",
"<!-- 2 -->\n", "<!-- 2 -->\n",
"<g id=\"node4\" class=\"node\"><title>2</title>\n", "<g id=\"node4\" class=\"node\"><title>2</title>\n",
"<ellipse fill=\"#ffffaa\" stroke=\"black\" cx=\"169\" cy=\"-18\" rx=\"18\" ry=\"18\"/>\n", "<ellipse fill=\"#ffffaa\" stroke=\"black\" cx=\"169\" cy=\"-18\" rx=\"18\" ry=\"18\"/>\n",
"<text text-anchor=\"middle\" x=\"169\" y=\"-14.3\" font-family=\"Lato\" font-size=\"14.00\">2</text>\n", "<text text-anchor=\"middle\" x=\"169\" y=\"-14.3\" font-family=\"Lato\" font-size=\"14.00\">2</text>\n",
"</g>\n", "</g>\n",
"<!-- 1&#45;&gt;2 -->\n", "<!-- 0&#45;&gt;2 -->\n",
"<g id=\"edge5\" class=\"edge\"><title>1&#45;&gt;2</title>\n", "<g id=\"edge4\" class=\"edge\"><title>0&#45;&gt;2</title>\n",
"<path fill=\"none\" stroke=\"black\" d=\"M68.0489,-142.264C87.9125,-117.568 129.699,-65.6173 152.446,-37.3378\"/>\n", "<path fill=\"none\" stroke=\"black\" d=\"M72.4341,-67.0744C91.6528,-57.2053 124.596,-40.2883 146.337,-29.1242\"/>\n",
"<polygon fill=\"black\" stroke=\"black\" points=\"156.996,-31.6809 155.063,-39.1098 154.802,-34.4082 152.608,-37.1354 152.608,-37.1354 152.608,-37.1354 154.802,-34.4082 150.154,-35.1611 156.996,-31.6809 156.996,-31.6809\"/>\n", "<polygon fill=\"black\" stroke=\"black\" points=\"152.697,-25.8583 147.909,-31.8581 149.584,-27.4571 146.47,-29.056 146.47,-29.056 146.47,-29.056 149.584,-27.4571 145.031,-26.2538 152.697,-25.8583 152.697,-25.8583\"/>\n",
"<text text-anchor=\"start\" x=\"99\" y=\"-114.8\" font-family=\"Lato\" font-size=\"14.00\">a | b</text>\n", "<text text-anchor=\"start\" x=\"92\" y=\"-59.8\" font-family=\"Lato\" font-size=\"14.00\">!a &amp; !b</text>\n",
"</g>\n",