safra: Use sub-transitions during determinization

* src/tests/safra.cc, src/tests/safra.test:  Update results.
* src/twaalgos/safra.cc, src/twaalgos/safra.hh: The use of transitions
resulted in non deterministic automata.  By using sub-transitions the
problem is solved.
This commit is contained in:
Alexandre Lewkowicz 2015-05-25 18:24:39 +02:00 committed by Alexandre Duret-Lutz
parent 8b1f9d3712
commit d0d42f86f9
4 changed files with 104 additions and 31 deletions

View file

@ -55,6 +55,7 @@ int main(int argc, char* argv[])
return 2; return 2;
res = tgba_determinisation(aut->aut); res = tgba_determinisation(aut->aut);
} }
res->merge_transitions();
spot::dotty_reachable(std::cout, res); spot::dotty_reachable(std::cout, res);
} }

View file

@ -22,7 +22,7 @@
. ./defs . ./defs
set -e set -e
cat >input.hoa << EOF cat >double_a.hoa << EOF
HOA: v1 HOA: v1
name: "F\"x > 1\"" name: "F\"x > 1\""
States: 2 States: 2
@ -50,20 +50,21 @@ digraph G {
0 -> 1 [label="a"] 0 -> 1 [label="a"]
1 [label="1"] 1 [label="1"]
1 -> 2 [label="a\n{0}"] 1 -> 2 [label="a\n{0}"]
1 -> 3 [label="b\n{3}"] 1 -> 3 [label="!a & b\n{3}"]
2 [label="2"] 2 [label="2"]
2 -> 1 [label="a"] 2 -> 1 [label="a"]
2 -> 3 [label="b"] 2 -> 3 [label="!a & b"]
3 [label="3"] 3 [label="3"]
3 -> 3 [label="b"] 3 -> 0 [label="a & !b"]
3 -> 0 [label="a"] 3 -> 2 [label="a & b"]
3 -> 3 [label="!a & b"]
} }
EOF EOF
run 0 ../safra --hoa input.hoa > out.dot run 0 ../safra --hoa double_a.hoa > out.dot
diff out.dot out.exp diff out.dot out.exp
cat >input.hoa << EOF cat >double_b.hoa << EOF
HOA: v1 HOA: v1
name: "F\"x > 1\"" name: "F\"x > 1\""
States: 2 States: 2
@ -90,13 +91,25 @@ digraph G {
0 [label="0"] 0 [label="0"]
0 -> 1 [label="a"] 0 -> 1 [label="a"]
1 [label="1"] 1 [label="1"]
1 -> 1 [label="a"] 1 -> 1 [label="a & !b"]
1 -> 2 [label="b\n{0}"] 1 -> 2 [label="a & b"]
1 -> 3 [label="!a & b\n{0}"]
2 [label="2"] 2 [label="2"]
2 -> 2 [label="b\n{0}"] 2 -> 3 [label="!a & b\n{0}"]
2 -> 0 [label="a"] 2 -> 4 [label="a & !b"]
2 -> 5 [label="a & b"]
3 [label="3"]
3 -> 0 [label="a & !b"]
3 -> 2 [label="a & b"]
3 -> 3 [label="!a & b\n{0}"]
4 [label="4"]
4 -> 1 [label="a\n{0}"]
4 -> 3 [label="!a & b\n{0}"]
5 [label="5"]
5 -> 1 [label="a\n{0}"]
5 -> 3 [label="!a & b\n{0}"]
} }
EOF EOF
run 0 ../safra --hoa input.hoa > out.dot run 0 ../safra --hoa double_b.hoa > out.dot
diff out.dot out.exp diff out.dot out.exp

View file

@ -18,6 +18,8 @@
// along with this program. If not, see <http://www.gnu.org/licenses/>. // along with this program. If not, see <http://www.gnu.org/licenses/>.
#include <deque> #include <deque>
#include <utility>
#include <unordered_map>
#include "safra.hh" #include "safra.hh"
#include "twaalgos/degen.hh" #include "twaalgos/degen.hh"
@ -25,16 +27,23 @@
namespace spot namespace spot
{ {
auto auto
safra_state::compute_succs(const const_twa_graph_ptr& aut) const -> succs_t safra_state::compute_succs(const const_twa_graph_ptr& aut,
const std::vector<unsigned>& bddnums,
std::unordered_map<bdd,
std::pair<unsigned, unsigned>,
bdd_hash>& deltas) const -> succs_t
{ {
succs_t res; succs_t res;
// Given a bdd returns index of associated safra_state in res // Given a bdd returns index of associated safra_state in res
std::map<bdd, unsigned, bdd_less_than> bdd2num; std::map<unsigned, unsigned> bdd2num;
for (auto& node: nodes_) for (auto& node: nodes_)
{ {
for (auto& t: aut->out(node.first)) for (auto& t: aut->out(node.first))
{ {
auto i = bdd2num.insert(std::make_pair(t.cond, res.size())); auto p = deltas[t.cond];
for (unsigned j = p.first; j < p.second; ++j)
{
auto i = bdd2num.insert(std::make_pair(bddnums[j], res.size()));
unsigned idx; unsigned idx;
if (!i.second) if (!i.second)
idx = i.first->second; idx = i.first->second;
@ -42,13 +51,15 @@ namespace spot
{ {
// Each new node starts out with same number of nodes as src // Each new node starts out with same number of nodes as src
idx = res.size(); idx = res.size();
res.emplace_back(safra_state(nb_braces_.size()), t.cond); res.emplace_back(safra_state(nb_braces_.size()),
bddnums[j]);
} }
safra_state& ss = res[idx].first; safra_state& ss = res[idx].first;
ss.update_succ(node.second, t.dst, t.acc); ss.update_succ(node.second, t.dst, t.acc);
assert(ss.nb_braces_.size() == ss.is_green_.size()); assert(ss.nb_braces_.size() == ss.is_green_.size());
} }
} }
}
for (auto& s: res) for (auto& s: res)
{ {
safra_state& tmp = s.first; safra_state& tmp = s.first;
@ -231,11 +242,54 @@ namespace spot
twa_graph_ptr twa_graph_ptr
tgba_determinisation(const const_twa_graph_ptr& a) tgba_determinisation(const const_twa_graph_ptr& a)
{ {
// Degeneralize
const_twa_graph_ptr aut; const_twa_graph_ptr aut;
if (a->acc().is_generalized_buchi()) if (a->acc().is_generalized_buchi())
aut = spot::degeneralize_tba(a); aut = spot::degeneralize_tba(a);
else else
aut = a; aut = a;
bdd allap = bddtrue;
{
typedef std::set<bdd, bdd_less_than> sup_map;
sup_map sup;
// Record occurrences of all guards
for (auto& t: aut->transitions())
sup.emplace(t.cond);
for (auto& i: sup)
allap &= bdd_support(i);
}
// Preprocessing
// Used to convert atomic bdd to id
std::unordered_map<bdd, unsigned, bdd_hash> bdd2num;
std::vector<bdd> num2bdd;
// Nedded for compute succs
// Used to convert large bdd to indexes
std::unordered_map<bdd, std::pair<unsigned, unsigned>, bdd_hash> deltas;
std::vector<unsigned> bddnums;
for (auto& t: aut->transitions())
{
auto it = deltas.find(t.cond);
if (it == deltas.end())
{
bdd all = t.cond;
unsigned prev = bddnums.size();
while (all != bddfalse)
{
bdd one = bdd_satoneset(all, allap, bddfalse);
all -= one;
auto p = bdd2num.emplace(one, num2bdd.size());
if (p.second)
num2bdd.push_back(one);
bddnums.emplace_back(p.first->second);
}
deltas[t.cond] = std::make_pair(prev, bddnums.size());
}
}
unsigned nc = bdd2num.size();
auto res = make_twa_graph(aut->get_dict()); auto res = make_twa_graph(aut->get_dict());
res->copy_ap_of(aut); res->copy_ap_of(aut);
res->prop_copy(aut, res->prop_copy(aut,
@ -261,7 +315,7 @@ namespace spot
safra_state curr = todo.front(); safra_state curr = todo.front();
unsigned src_num = seen.find(curr)->second; unsigned src_num = seen.find(curr)->second;
todo.pop_front(); todo.pop_front();
succs_t succs = curr.compute_succs(aut); succs_t succs = curr.compute_succs(aut, bddnums, deltas);
for (auto s: succs) for (auto s: succs)
{ {
auto i = seen.find(s.first); auto i = seen.find(s.first);
@ -278,9 +332,10 @@ namespace spot
seen.insert(std::make_pair(s.first, dst_num)); seen.insert(std::make_pair(s.first, dst_num));
} }
if (s.first.color_ != -1U) if (s.first.color_ != -1U)
res->new_transition(src_num, dst_num, s.second, {s.first.color_}); res->new_transition(src_num, dst_num, num2bdd[s.second],
{s.first.color_});
else else
res->new_transition(src_num, dst_num, s.second); res->new_transition(src_num, dst_num, num2bdd[s.second]);
} }
} }
return res; return res;

View file

@ -40,14 +40,18 @@ namespace spot
class safra_state class safra_state
{ {
public: public:
typedef std::vector<std::pair<safra_state, bdd>> succs_t; typedef std::vector<std::pair<safra_state, unsigned>> succs_t;
bool operator<(const safra_state& other) const; bool operator<(const safra_state& other) const;
// Print each sub-state with their associated braces of a safra state // Print each sub-state with their associated braces of a safra state
void print_debug(unsigned state_id); void print_debug(unsigned state_id);
safra_state(unsigned state_number, bool init_state = false); safra_state(unsigned state_number, bool init_state = false);
// Given a certain transition_label, compute all the successors of that // Given a certain transition_label, compute all the successors of that
// label, and return that new node. // label, and return that new node.
succs_t compute_succs(const const_twa_graph_ptr& aut) const; succs_t compute_succs(const const_twa_graph_ptr& aut,
const std::vector<unsigned>& bddnums,
std::unordered_map<bdd,
std::pair<unsigned, unsigned>,
bdd_hash>& deltas) const;
// Used when creating the list of successors // Used when creating the list of successors
// A new intermediate node is created with src's braces and with dst as id // A new intermediate node is created with src's braces and with dst as id
// A merge is done if dst already existed in *this // A merge is done if dst already existed in *this