dtbasat,dtgbasat: rewrite using the tgba_digraph interface

This gets rid of many state*/int conversions.  We now use scc_info
instead of scc_map.  Finally the loops are now all 0-based.

* src/tgbaalgos/sccinfo.cc, src/tgbaalgos/sccinfo.hh (weak_sccs): New
method.
* src/tgbaalgos/dtbasat.cc, src/tgbaalgos/dtbasat.hh,
src/tgbaalgos/dtgbasat.cc, src/tgbaalgos/dtgbasat.hh: Use the
tgba_digraph interface.
* src/tgbatest/ltl2tgba.cc: Adjust calls.
This commit is contained in:
Alexandre Duret-Lutz 2014-08-20 16:11:50 +02:00
parent 99d28c3cc2
commit edb220af6a
7 changed files with 381 additions and 425 deletions

View file

@ -24,7 +24,7 @@
#include "reachiter.hh" #include "reachiter.hh"
#include <map> #include <map>
#include <utility> #include <utility>
#include "scc.hh" #include "sccinfo.hh"
#include "tgba/bddprint.hh" #include "tgba/bddprint.hh"
#include "stats.hh" #include "stats.hh"
#include "misc/satsolver.hh" #include "misc/satsolver.hh"
@ -57,11 +57,11 @@ namespace spot
struct transition struct transition
{ {
int src; unsigned src;
bdd cond; bdd cond;
int dst; unsigned dst;
transition(int src, bdd cond, int dst) transition(unsigned src, bdd cond, unsigned dst)
: src(src), cond(cond), dst(dst) : src(src), cond(cond), dst(dst)
{ {
} }
@ -89,10 +89,10 @@ namespace spot
struct src_cond struct src_cond
{ {
int src; unsigned src;
bdd cond; bdd cond;
src_cond(int src, bdd cond) src_cond(unsigned src, bdd cond)
: src(src), cond(cond) : src(src), cond(cond)
{ {
} }
@ -115,10 +115,10 @@ namespace spot
struct state_pair struct state_pair
{ {
int a; unsigned a;
int b; unsigned b;
state_pair(int a, int b) state_pair(unsigned a, unsigned b)
: a(a), b(b) : a(a), b(b)
{ {
} }
@ -210,65 +210,34 @@ namespace spot
std::map<state_pair, int> prodid; std::map<state_pair, int> prodid;
std::map<path, int> pathid_ref; std::map<path, int> pathid_ref;
std::map<path, int> pathid_cand; std::map<path, int> pathid_cand;
int nvars; int nvars = 0;
typedef std::unordered_map<const state*, int, unsigned cand_size;
state_ptr_hash, state_ptr_equal> state_map;
typedef std::unordered_map<int, const state*> int_map;
state_map state_to_int;
int_map int_to_state;
int cand_size;
~dict()
{
state_map::const_iterator s = state_to_int.begin();
while (s != state_to_int.end())
// Always advance the iterator before deleting the key.
s++->first->destroy();
}
}; };
unsigned declare_vars(const const_tgba_digraph_ptr& aut,
class filler_dfs: public tgba_reachable_iterator_depth_first dict& d,
bdd ap,
bool state_based,
scc_info& sm)
{ {
protected: unsigned ref_size = aut->num_states();
dict& d;
int size_; if (d.cand_size == -1U)
bdd ap_; for (unsigned i = 0; i < ref_size; ++i)
bool state_based_; if (sm.reachable_state(i))
scc_map& sm_; ++d.cand_size; // Note that we start from -1U the
public: // cand_size is one less than the
filler_dfs(const const_tgba_ptr& aut, dict& d, bdd ap, bool state_based, // number of reachable states.
scc_map& sm)
: tgba_reachable_iterator_depth_first(aut), d(d), ap_(ap), for (unsigned i = 0; i < ref_size; ++i)
state_based_(state_based), sm_(sm)
{ {
d.nvars = 0; if (!sm.reachable_state(i))
} continue;
int size() unsigned i_scc = sm.scc_of(i);
{ bool is_trivial = sm.is_trivial(i_scc);
return size_;
}
void end() for (unsigned j = 0; j < d.cand_size; ++j)
{
size_ = seen.size();
if (d.cand_size == -1)
d.cand_size = size_ - 1;
// Reverse the "seen" map. States are labeled from 1 to size_.
for (dict::state_map::const_iterator i2 = seen.begin();
i2 != seen.end(); ++i2)
d.int_to_state[i2->second] = i2->first;
for (int i = 1; i <= size_; ++i)
{
unsigned i_scc = sm_.scc_of_state(d.int_to_state[i]);
bool is_trivial = sm_.trivial(i_scc);
for (int j = 1; j <= d.cand_size; ++j)
{ {
d.prodid[state_pair(j, i)] = ++d.nvars; d.prodid[state_pair(j, i)] = ++d.nvars;
@ -276,11 +245,13 @@ namespace spot
if (is_trivial) if (is_trivial)
continue; continue;
for (int k = 1; k <= size_; ++k) for (unsigned k = 0; k < ref_size; ++k)
{ {
if (sm_.scc_of_state(d.int_to_state[k]) != i_scc) if (!sm.reachable_state(k))
continue; continue;
for (int l = 1; l <= d.cand_size; ++l) if (sm.scc_of(k) != i_scc)
continue;
for (unsigned l = 0; l < d.cand_size; ++l)
{ {
if (i == k && j == l) if (i == k && j == l)
continue; continue;
@ -292,47 +263,48 @@ namespace spot
} }
} }
std::swap(d.state_to_int, seen); for (unsigned i = 0; i < d.cand_size; ++i)
for (int i = 1; i <= d.cand_size; ++i)
{ {
int transacc = -1; int transacc = -1;
if (state_based_) if (state_based)
// All outgoing transitions use the same acceptance variable. // All outgoing transitions use the same acceptance variable.
transacc = ++d.nvars; transacc = ++d.nvars;
for (int j = 1; j <= d.cand_size; ++j) for (unsigned j = 0; j < d.cand_size; ++j)
{ {
bdd all = bddtrue; bdd all = bddtrue;
while (all != bddfalse) while (all != bddfalse)
{ {
bdd one = bdd_satoneset(all, ap_, bddfalse); bdd one = bdd_satoneset(all, ap, bddfalse);
all -= one; all -= one;
transition t(i, one, j); transition t(i, one, j);
d.transid[t] = ++d.nvars; d.transid[t] = ++d.nvars;
d.revtransid.emplace(d.nvars, t); d.revtransid.emplace(d.nvars, t);
int ta = d.transacc[t] = int ta = d.transacc[t] =
state_based_ ? transacc : ++d.nvars; state_based ? transacc : ++d.nvars;
d.revtransacc.emplace(ta, t); d.revtransacc.emplace(ta, t);
} }
} }
} }
return ref_size;
} }
};
typedef std::pair<int, int> sat_stats; typedef std::pair<int, int> sat_stats;
static static
sat_stats dtba_to_sat(std::ostream& out, const_tgba_ptr ref, sat_stats dtba_to_sat(std::ostream& out,
const const_tgba_digraph_ptr& ref,
dict& d, bool state_based) dict& d, bool state_based)
{ {
clause_counter nclauses; clause_counter nclauses;
int ref_size = 0;
scc_map sm(ref); // Compute the AP used in the hard way.
sm.build_map(); bdd ap = bddtrue;
bdd ap = sm.aprec_set_of(sm.initial()); for (auto& t: ref->transitions())
if (!ref->is_dead_transition(t))
ap &= bdd_support(t.cond);
// Count the number of atomic propositions // Count the number of atomic propositions
int nap = 0; int nap = 0;
@ -346,12 +318,10 @@ namespace spot
nap = 1 << nap; nap = 1 << nap;
} }
// Number all the SAT variable we may need. scc_info sm(ref);
{
filler_dfs f(ref, d, ap, state_based, sm); // Number all the SAT variables we may need.
f.run(); unsigned ref_size = declare_vars(ref, d, ap, state_based, sm);
ref_size = f.size();
}
// empty automaton is impossible // empty automaton is impossible
if (d.cand_size == 0) if (d.cand_size == 0)
@ -370,14 +340,14 @@ namespace spot
#endif #endif
dout << "symmetry-breaking clauses\n"; dout << "symmetry-breaking clauses\n";
int j = 0; unsigned j = 0;
bdd all = bddtrue; bdd all = bddtrue;
while (all != bddfalse) while (all != bddfalse)
{ {
bdd s = bdd_satoneset(all, ap, bddfalse); bdd s = bdd_satoneset(all, ap, bddfalse);
all -= s; all -= s;
for (int i = 1; i < d.cand_size; ++i) for (unsigned i = 0; i < d.cand_size - 1; ++i)
for (int k = (i - 1) * nap + j + 3; k <= d.cand_size; ++k) for (unsigned k = i * nap + j + 2; k < d.cand_size; ++k)
{ {
transition t(i, s, k); transition t(i, s, k);
int ti = d.transid[t]; int ti = d.transid[t];
@ -391,7 +361,7 @@ namespace spot
dout << "(none)\n"; dout << "(none)\n";
dout << "(1) the candidate automaton is complete\n"; dout << "(1) the candidate automaton is complete\n";
for (int q1 = 1; q1 <= d.cand_size; ++q1) for (unsigned q1 = 0; q1 < d.cand_size; ++q1)
{ {
bdd all = bddtrue; bdd all = bddtrue;
while (all != bddfalse) while (all != bddfalse)
@ -401,7 +371,7 @@ namespace spot
#if DEBUG #if DEBUG
dout; dout;
for (int q2 = 1; q2 <= d.cand_size; q2++) for (unsigned q2 = 0; q2 < d.cand_size; q2++)
{ {
transition t(q1, s, q2); transition t(q1, s, q2);
out << t << "δ"; out << t << "δ";
@ -411,7 +381,7 @@ namespace spot
out << '\n'; out << '\n';
#endif #endif
for (int q2 = 1; q2 <= d.cand_size; q2++) for (unsigned q2 = 0; q2 < d.cand_size; q2++)
{ {
transition t(q1, s, q2); transition t(q1, s, q2);
int ti = d.transid[t]; int ti = d.transid[t];
@ -425,31 +395,28 @@ namespace spot
} }
dout << "(2) the initial state is reachable\n"; dout << "(2) the initial state is reachable\n";
dout << state_pair(1, 1) << '\n'; dout << state_pair(0, 0) << '\n';
out << d.prodid[state_pair(1, 1)] << " 0\n"; out << d.prodid[state_pair(0, 0)] << " 0\n";
++nclauses; ++nclauses;
for (std::map<state_pair, int>::const_iterator pit = d.prodid.begin(); for (std::map<state_pair, int>::const_iterator pit = d.prodid.begin();
pit != d.prodid.end(); ++pit) pit != d.prodid.end(); ++pit)
{ {
int q1 = pit->first.a; unsigned q1 = pit->first.a;
int q1p = pit->first.b; unsigned q1p = pit->first.b;
dout << "(3) augmenting paths based on Cand[" << q1 dout << "(3) augmenting paths based on Cand[" << q1
<< "] and Ref[" << q1p << "]\n"; << "] and Ref[" << q1p << "]\n";
for (auto it: ref->succ(d.int_to_state[q1p])) for (auto& tr: ref->out(q1p))
{ {
const state* dps = it->current_state(); unsigned dp = tr.dst;
int dp = d.state_to_int[dps]; bdd all = tr.cond;
dps->destroy();
bdd all = it->current_condition();
while (all != bddfalse) while (all != bddfalse)
{ {
bdd s = bdd_satoneset(all, ap, bddfalse); bdd s = bdd_satoneset(all, ap, bddfalse);
all -= s; all -= s;
for (int q2 = 1; q2 <= d.cand_size; q2++) for (unsigned q2 = 0; q2 < d.cand_size; q2++)
{ {
transition t(q1, s, q2); transition t(q1, s, q2);
int ti = d.transid[t]; int ti = d.transid[t];
@ -474,19 +441,23 @@ namespace spot
// construction of contraints (4,5) : all loops in the product // construction of contraints (4,5) : all loops in the product
// where no accepting run is detected in the ref. automaton, // where no accepting run is detected in the ref. automaton,
// must also be marked as not accepting in the cand. automaton // must also be marked as not accepting in the cand. automaton
for (int q1p = 1; q1p <= ref_size; ++q1p) for (unsigned q1p = 0; q1p < ref_size; ++q1p)
{ {
unsigned q1p_scc = sm.scc_of_state(d.int_to_state[q1p]); if (!sm.reachable_state(q1p))
if (sm.trivial(q1p_scc))
continue; continue;
for (int q2p = 1; q2p <= ref_size; ++q2p) unsigned q1p_scc = sm.scc_of(q1p);
if (sm.is_trivial(q1p_scc))
continue;
for (unsigned q2p = 0; q2p < ref_size; ++q2p)
{ {
if (!sm.reachable_state(q2p))
continue;
// We are only interested in transition that can form a // We are only interested in transition that can form a
// cycle, so they must belong to the same SCC. // cycle, so they must belong to the same SCC.
if (sm.scc_of_state(d.int_to_state[q2p]) != q1p_scc) if (sm.scc_of(q2p) != q1p_scc)
continue; continue;
for (int q1 = 1; q1 <= d.cand_size; ++q1) for (unsigned q1 = 0; q1 < d.cand_size; ++q1)
for (int q2 = 1; q2 <= d.cand_size; ++q2) for (unsigned q2 = 0; q2 < d.cand_size; ++q2)
{ {
path p1(q1, q1p, q2, q2p); path p1(q1, q1p, q2, q2p);
@ -499,25 +470,20 @@ namespace spot
else else
pid1 = d.pathid_ref[p1]; pid1 = d.pathid_ref[p1];
for (auto it: ref->succ(d.int_to_state[q2p])) for (auto& tr: ref->out(q2p))
{ {
const state* dps = it->current_state(); unsigned dp = tr.dst;
// Skip destinations not in the SCC. // Skip destinations not in the SCC.
if (sm.scc_of_state(dps) != q1p_scc) if (sm.scc_of(dp) != q1p_scc)
{
dps->destroy();
continue; continue;
}
int dp = d.state_to_int[dps];
dps->destroy();
if (it->current_acceptance_conditions() == all_acc) if (tr.acc == all_acc)
continue; continue;
for (int q3 = 1; q3 <= d.cand_size; ++q3) for (unsigned q3 = 0; q3 < d.cand_size; ++q3)
{ {
if (dp == q1p && q3 == q1) // (4) looping if (dp == q1p && q3 == q1) // (4) looping
{ {
bdd all = it->current_condition(); bdd all = tr.cond;
while (all != bddfalse) while (all != bddfalse)
{ {
bdd s = bdd_satoneset(all, ap, bddfalse); bdd s = bdd_satoneset(all, ap, bddfalse);
@ -544,7 +510,7 @@ namespace spot
if (pid1 == pid2) if (pid1 == pid2)
continue; continue;
bdd all = it->current_condition(); bdd all = tr.cond;
while (all != bddfalse) while (all != bddfalse)
{ {
bdd s = bdd_satoneset(all, ap, bddfalse); bdd s = bdd_satoneset(all, ap, bddfalse);
@ -568,19 +534,23 @@ namespace spot
// construction of contraints (6,7): all loops in the product // construction of contraints (6,7): all loops in the product
// where accepting run is detected in the ref. automaton, must // where accepting run is detected in the ref. automaton, must
// also be marked as accepting in the candidate. // also be marked as accepting in the candidate.
for (int q1p = 1; q1p <= ref_size; ++q1p) for (unsigned q1p = 0; q1p < ref_size; ++q1p)
{ {
unsigned q1p_scc = sm.scc_of_state(d.int_to_state[q1p]); if (!sm.reachable_state(q1p))
if (sm.trivial(q1p_scc))
continue; continue;
for (int q2p = 1; q2p <= ref_size; ++q2p) unsigned q1p_scc = sm.scc_of(q1p);
if (sm.is_trivial(q1p_scc))
continue;
for (unsigned q2p = 0; q2p < ref_size; ++q2p)
{ {
if (!sm.reachable_state(q2p))
continue;
// We are only interested in transition that can form a // We are only interested in transition that can form a
// cycle, so they must belong to the same SCC. // cycle, so they must belong to the same SCC.
if (sm.scc_of_state(d.int_to_state[q2p]) != q1p_scc) if (sm.scc_of(q2p) != q1p_scc)
continue; continue;
for (int q1 = 1; q1 <= d.cand_size; ++q1) for (unsigned q1 = 0; q1 < d.cand_size; ++q1)
for (int q2 = 1; q2 <= d.cand_size; ++q2) for (unsigned q2 = 0; q2 < d.cand_size; ++q2)
{ {
path p1(q1, q1p, q2, q2p); path p1(q1, q1p, q2, q2p);
dout << "(6&7) matching paths from candidate based on " dout << "(6&7) matching paths from candidate based on "
@ -592,27 +562,21 @@ namespace spot
else else
pid1 = d.pathid_cand[p1]; pid1 = d.pathid_cand[p1];
for (auto it: ref->succ(d.int_to_state[q2p])) for (auto& tr: ref->out(q2p))
{ {
const state* dps = it->current_state(); unsigned dp = tr.dst;
// Skip destinations not in the SCC. // Skip destinations not in the SCC.
if (sm.scc_of_state(dps) != q1p_scc) if (sm.scc_of(dp) != q1p_scc)
{
dps->destroy();
continue; continue;
} for (unsigned q3 = 0; q3 < d.cand_size; q3++)
int dp = d.state_to_int[dps];
dps->destroy();
for (int q3 = 1; q3 <= d.cand_size; q3++)
{ {
if (dp == q1p && q3 == q1) // (6) looping if (dp == q1p && q3 == q1) // (6) looping
{ {
// We only care about the looping case if // We only care about the looping case if
// it is accepting in the reference. // it is accepting in the reference.
if (it->current_acceptance_conditions() if (tr.acc != all_acc)
!= all_acc)
continue; continue;
bdd all = it->current_condition(); bdd all = tr.cond;
while (all != bddfalse) while (all != bddfalse)
{ {
bdd s = bdd_satoneset(all, ap, bddfalse); bdd s = bdd_satoneset(all, ap, bddfalse);
@ -637,7 +601,7 @@ namespace spot
if (pid1 == pid2) if (pid1 == pid2)
continue; continue;
bdd all = it->current_condition(); bdd all = tr.cond;
while (all != bddfalse) while (all != bddfalse)
{ {
bdd s = bdd_satoneset(all, ap, bddfalse); bdd s = bdd_satoneset(all, ap, bddfalse);
@ -667,7 +631,7 @@ namespace spot
static tgba_digraph_ptr static tgba_digraph_ptr
sat_build(const satsolver::solution& solution, dict& satdict, sat_build(const satsolver::solution& solution, dict& satdict,
const_tgba_ptr aut, bool state_based) const_tgba_digraph_ptr aut, bool state_based)
{ {
auto autdict = aut->get_dict(); auto autdict = aut->get_dict();
auto a = make_tgba_digraph(autdict); auto a = make_tgba_digraph(autdict);
@ -710,7 +674,7 @@ namespace spot
&& acc_states.find(t->second.src) != acc_states.end(); && acc_states.find(t->second.src) != acc_states.end();
last_aut_trans = last_aut_trans =
a->new_acc_transition(t->second.src - 1, t->second.dst - 1, a->new_acc_transition(t->second.src, t->second.dst,
t->second.cond, accept); t->second.cond, accept);
last_sat_trans = &t->second; last_sat_trans = &t->second;
@ -770,8 +734,8 @@ namespace spot
} }
tgba_digraph_ptr tgba_digraph_ptr
dtba_sat_synthetize(const const_tgba_ptr& a, int target_state_number, dtba_sat_synthetize(const const_tgba_digraph_ptr& a,
bool state_based) int target_state_number, bool state_based)
{ {
if (target_state_number == 0) if (target_state_number == 0)
return nullptr; return nullptr;
@ -828,7 +792,7 @@ namespace spot
} }
tgba_digraph_ptr tgba_digraph_ptr
dtba_sat_minimize(const const_tgba_ptr& a, bool state_based) dtba_sat_minimize(const const_tgba_digraph_ptr& a, bool state_based)
{ {
int n_states = stats_reachable(a).states; int n_states = stats_reachable(a).states;
@ -847,7 +811,8 @@ namespace spot
} }
tgba_digraph_ptr tgba_digraph_ptr
dtba_sat_minimize_dichotomy(const const_tgba_ptr& a, bool state_based) dtba_sat_minimize_dichotomy(const const_tgba_digraph_ptr& a,
bool state_based)
{ {
int max_states = stats_reachable(a).states - 1; int max_states = stats_reachable(a).states - 1;
int min_states = 1; int min_states = 1;

View file

@ -41,7 +41,7 @@ namespace spot
/// If no equivalent deterministic TBA with \a target_state_number /// If no equivalent deterministic TBA with \a target_state_number
/// states is found, a null pointer /// states is found, a null pointer
SPOT_API tgba_digraph_ptr SPOT_API tgba_digraph_ptr
dtba_sat_synthetize(const const_tgba_ptr& a, dtba_sat_synthetize(const const_tgba_digraph_ptr& a,
int target_state_number, int target_state_number,
bool state_based = false); bool state_based = false);
@ -52,7 +52,8 @@ namespace spot
/// ///
/// If no smaller TBA exist, this returns a null pointer. /// If no smaller TBA exist, this returns a null pointer.
SPOT_API tgba_digraph_ptr SPOT_API tgba_digraph_ptr
dtba_sat_minimize(const const_tgba_ptr& a, bool state_based = false); dtba_sat_minimize(const const_tgba_digraph_ptr& a,
bool state_based = false);
/// \brief Attempt to minimize a deterministic TBA with a SAT solver. /// \brief Attempt to minimize a deterministic TBA with a SAT solver.
/// ///
@ -61,7 +62,7 @@ namespace spot
// //
/// If no smaller TBA exist, this returns a null pointer. /// If no smaller TBA exist, this returns a null pointer.
SPOT_API tgba_digraph_ptr SPOT_API tgba_digraph_ptr
dtba_sat_minimize_dichotomy(const const_tgba_ptr& a, dtba_sat_minimize_dichotomy(const const_tgba_digraph_ptr& a,
bool state_based = false); bool state_based = false);
} }

View file

@ -24,7 +24,7 @@
#include "reachiter.hh" #include "reachiter.hh"
#include <map> #include <map>
#include <utility> #include <utility>
#include "scc.hh" #include "sccinfo.hh"
#include "tgba/bddprint.hh" #include "tgba/bddprint.hh"
#include "ltlast/constant.hh" #include "ltlast/constant.hh"
#include "stats.hh" #include "stats.hh"
@ -60,9 +60,9 @@ namespace spot
struct transition struct transition
{ {
int src; unsigned src;
bdd cond; bdd cond;
int dst; unsigned dst;
transition(int src, bdd cond, int dst) transition(int src, bdd cond, int dst)
: src(src), cond(cond), dst(dst) : src(src), cond(cond), dst(dst)
@ -92,7 +92,7 @@ namespace spot
struct src_cond struct src_cond
{ {
int src; unsigned src;
bdd cond; bdd cond;
src_cond(int src, bdd cond) src_cond(int src, bdd cond)
@ -118,10 +118,10 @@ namespace spot
struct transition_acc struct transition_acc
{ {
int src; unsigned src;
bdd cond; bdd cond;
bdd acc; bdd acc;
int dst; unsigned dst;
transition_acc(int src, bdd cond, bdd acc, int dst) transition_acc(int src, bdd cond, bdd acc, int dst)
: src(src), cond(cond), acc(acc), dst(dst) : src(src), cond(cond), acc(acc), dst(dst)
@ -156,22 +156,22 @@ namespace spot
struct path struct path
{ {
int src_cand; unsigned src_cand;
int src_ref; unsigned src_ref;
int dst_cand; unsigned dst_cand;
int dst_ref; unsigned dst_ref;
bdd acc_cand; bdd acc_cand;
bdd acc_ref; bdd acc_ref;
path(int src_cand, int src_ref) path(unsigned src_cand, unsigned src_ref)
: src_cand(src_cand), src_ref(src_ref), : src_cand(src_cand), src_ref(src_ref),
dst_cand(src_cand), dst_ref(src_ref), dst_cand(src_cand), dst_ref(src_ref),
acc_cand(bddfalse), acc_ref(bddfalse) acc_cand(bddfalse), acc_ref(bddfalse)
{ {
} }
path(int src_cand, int src_ref, path(unsigned src_cand, unsigned src_ref,
int dst_cand, int dst_ref, unsigned dst_cand, unsigned dst_ref,
bdd acc_cand, bdd acc_ref) bdd acc_cand, bdd acc_ref)
: src_cand(src_cand), src_ref(src_ref), : src_cand(src_cand), src_ref(src_ref),
dst_cand(dst_cand), dst_ref(dst_ref), dst_cand(dst_cand), dst_ref(dst_ref),
@ -259,13 +259,13 @@ namespace spot
rev_acc_map revtransaccid; rev_acc_map revtransaccid;
std::map<path, int> pathid; std::map<path, int> pathid;
int nvars; int nvars = 0;
typedef std::unordered_map<const state*, int, //typedef std::unordered_map<const state*, int,
state_ptr_hash, state_ptr_equal> state_map; //state_ptr_hash, state_ptr_equal> state_map;
typedef std::unordered_map<int, const state*> int_map; //typedef std::unordered_map<int, const state*> int_map;
state_map state_to_int; //state_map state_to_int;
int_map int_to_state; // int_map int_to_state;
int cand_size; unsigned cand_size;
unsigned int cand_nacc; unsigned int cand_nacc;
std::vector<bdd> cand_acc; // size cand_nacc std::vector<bdd> cand_acc; // size cand_nacc
@ -275,34 +275,18 @@ namespace spot
bdd cand_all_acc; bdd cand_all_acc;
bdd ref_all_acc; bdd ref_all_acc;
std::vector<bool> is_weak_scc;
~dict() ~dict()
{ {
state_map::const_iterator s = state_to_int.begin();
while (s != state_to_int.end())
// Always advance the iterator before deleting the key.
s++->first->destroy();
aut->get_dict()->unregister_all_my_variables(this); aut->get_dict()->unregister_all_my_variables(this);
} }
}; };
class filler_dfs: public tgba_reachable_iterator_depth_first unsigned declare_vars(const const_tgba_digraph_ptr& aut,
dict& d, bdd ap, bool state_based, scc_info& sm)
{ {
protected:
dict& d;
int size_;
bdd ap_;
bool state_based_;
scc_map& sm_;
public:
filler_dfs(const const_tgba_ptr& aut, dict& d, bdd ap, bool state_based,
scc_map& sm)
: tgba_reachable_iterator_depth_first(aut), d(d), ap_(ap),
state_based_(state_based), sm_(sm)
{
d.nvars = 0;
bdd_dict_ptr bd = aut->get_dict(); bdd_dict_ptr bd = aut->get_dict();
ltl::default_environment& env = ltl::default_environment::instance(); ltl::default_environment& env = ltl::default_environment::instance();
@ -347,36 +331,32 @@ namespace spot
refall = bdd_high(refall); refall = bdd_high(refall);
} }
}
int size() unsigned ref_size = aut->num_states();
if (d.cand_size == -1U)
for (unsigned i = 0; i < ref_size; ++i)
if (sm.reachable_state(i))
++d.cand_size; // Note that we start from -1U the
// cand_size is one less than the
// number of reachable states.
for (unsigned i = 0; i < ref_size; ++i)
{ {
return size_; if (!sm.reachable_state(i))
}
void end()
{
size_ = seen.size();
if (d.cand_size == -1)
d.cand_size = size_ - 1;
for (dict::state_map::const_iterator i2 = seen.begin();
i2 != seen.end(); ++i2)
d.int_to_state[i2->second] = i2->first;
for (int i = 1; i <= size_; ++i)
{
unsigned i_scc = sm_.scc_of_state(d.int_to_state[i]);
bool is_weak = is_weak_scc(sm_, i_scc);
for (int j = 1; j <= d.cand_size; ++j)
{
for (int k = 1; k <= size_; ++k)
{
if (sm_.scc_of_state(d.int_to_state[k]) != i_scc)
continue; continue;
for (int l = 1; l <= d.cand_size; ++l) unsigned i_scc = sm.scc_of(i);
bool is_weak = d.is_weak_scc[i_scc];
for (unsigned j = 0; j < d.cand_size; ++j)
{
for (unsigned k = 0; k < ref_size; ++k)
{
if (!sm.reachable_state(k))
continue;
if (sm.scc_of(k) != i_scc)
continue;
for (unsigned l = 0; l < d.cand_size; ++l)
{ {
size_t sfp = is_weak ? 1 : d.all_ref_acc.size(); size_t sfp = is_weak ? 1 : d.all_ref_acc.size();
for (size_t fp = 0; fp < sfp; ++fp) for (size_t fp = 0; fp < sfp; ++fp)
@ -396,17 +376,15 @@ namespace spot
} }
} }
std::swap(d.state_to_int, seen); if (!state_based)
if (!state_based_)
{ {
for (int i = 1; i <= d.cand_size; ++i) for (unsigned i = 0; i < d.cand_size; ++i)
for (int j = 1; j <= d.cand_size; ++j) for (unsigned j = 0; j < d.cand_size; ++j)
{ {
bdd all = bddtrue; bdd all = bddtrue;
while (all != bddfalse) while (all != bddfalse)
{ {
bdd one = bdd_satoneset(all, ap_, bddfalse); bdd one = bdd_satoneset(all, ap, bddfalse);
all -= one; all -= one;
transition t(i, one, j); transition t(i, one, j);
@ -427,16 +405,16 @@ namespace spot
} }
else // state based else // state based
{ {
for (int i = 1; i <= d.cand_size; ++i) for (unsigned i = 0; i < d.cand_size; ++i)
for (unsigned n = 0; n < d.cand_nacc; ++n) for (unsigned n = 0; n < d.cand_nacc; ++n)
{ {
++d.nvars; ++d.nvars;
for (int j = 1; j <= d.cand_size; ++j) for (unsigned j = 1; j < d.cand_size; ++j)
{ {
bdd all = bddtrue; bdd all = bddtrue;
while (all != bddfalse) while (all != bddfalse)
{ {
bdd one = bdd_satoneset(all, ap_, bddfalse); bdd one = bdd_satoneset(all, ap, bddfalse);
all -= one; all -= one;
transition_acc ta(i, one, d.cand_acc[n], j); transition_acc ta(i, one, d.cand_acc[n], j);
@ -445,13 +423,13 @@ namespace spot
} }
} }
} }
for (int i = 1; i <= d.cand_size; ++i) for (unsigned i = 0; i < d.cand_size; ++i)
for (int j = 1; j <= d.cand_size; ++j) for (unsigned j = 0; j < d.cand_size; ++j)
{ {
bdd all = bddtrue; bdd all = bddtrue;
while (all != bddfalse) while (all != bddfalse)
{ {
bdd one = bdd_satoneset(all, ap_, bddfalse); bdd one = bdd_satoneset(all, ap, bddfalse);
all -= one; all -= one;
transition t(i, one, j); transition t(i, one, j);
@ -460,21 +438,22 @@ namespace spot
} }
} }
} }
return ref_size;
} }
};
typedef std::pair<int, int> sat_stats; typedef std::pair<int, int> sat_stats;
static static
sat_stats dtgba_to_sat(std::ostream& out, const_tgba_ptr ref, sat_stats dtgba_to_sat(std::ostream& out, const_tgba_digraph_ptr ref,
dict& d, bool state_based) dict& d, bool state_based)
{ {
clause_counter nclauses; clause_counter nclauses;
int ref_size = 0;
scc_map sm(ref); // Compute the AP used in the hard way.
sm.build_map(); bdd ap = bddtrue;
bdd ap = sm.aprec_set_of(sm.initial()); for (auto& t: ref->transitions())
if (!ref->is_dead_transition(t))
ap &= bdd_support(t.cond);
// Count the number of atomic propositions // Count the number of atomic propositions
int nap = 0; int nap = 0;
@ -488,12 +467,11 @@ namespace spot
nap = 1 << nap; nap = 1 << nap;
} }
// Number all the SAT variable we may need. scc_info sm(ref);
{ d.is_weak_scc = sm.weak_sccs();
filler_dfs f(ref, d, ap, state_based, sm);
f.run(); // Number all the SAT variables we may need.
ref_size = f.size(); unsigned ref_size = declare_vars(ref, d, ap, state_based, sm);
}
// empty automaton is impossible // empty automaton is impossible
if (d.cand_size == 0) if (d.cand_size == 0)
@ -518,8 +496,8 @@ namespace spot
{ {
bdd s = bdd_satoneset(all, ap, bddfalse); bdd s = bdd_satoneset(all, ap, bddfalse);
all -= s; all -= s;
for (int i = 1; i < d.cand_size; ++i) for (unsigned i = 0; i < d.cand_size - 1; ++i)
for (int k = (i - 1) * nap + j + 3; k <= d.cand_size; ++k) for (unsigned k = i * nap + j + 2; k < d.cand_size; ++k)
{ {
transition t(i, s, k); transition t(i, s, k);
int ti = d.transid[t]; int ti = d.transid[t];
@ -533,7 +511,7 @@ namespace spot
dout << "(none)\n"; dout << "(none)\n";
dout << "(8) the candidate automaton is complete\n"; dout << "(8) the candidate automaton is complete\n";
for (int q1 = 1; q1 <= d.cand_size; ++q1) for (unsigned q1 = 0; q1 < d.cand_size; ++q1)
{ {
bdd all = bddtrue; bdd all = bddtrue;
while (all != bddfalse) while (all != bddfalse)
@ -543,7 +521,7 @@ namespace spot
#if DEBUG #if DEBUG
dout; dout;
for (int q2 = 1; q2 <= d.cand_size; q2++) for (unsigned q2 = 0; q2 < d.cand_size; ++q2)
{ {
transition t(q1, s, q2); transition t(q1, s, q2);
out << t << "δ"; out << t << "δ";
@ -553,7 +531,7 @@ namespace spot
out << '\n'; out << '\n';
#endif #endif
for (int q2 = 1; q2 <= d.cand_size; q2++) for (unsigned q2 = 0; q2 < d.cand_size; ++q2)
{ {
transition t(q1, s, q2); transition t(q1, s, q2);
int ti = d.transid[t]; int ti = d.transid[t];
@ -566,31 +544,30 @@ namespace spot
} }
dout << "(9) the initial state is reachable\n"; dout << "(9) the initial state is reachable\n";
dout << path(1, 1) << '\n'; dout << path(0, 0) << '\n';
out << d.pathid[path(1, 1)] << " 0\n"; out << d.pathid[path(0, 0)] << " 0\n";
++nclauses; ++nclauses;
for (int q1 = 1; q1 <= d.cand_size; ++q1) for (unsigned q1 = 0; q1 < d.cand_size; ++q1)
for (int q1p = 1; q1p <= ref_size; ++q1p) for (unsigned q1p = 0; q1p < ref_size; ++q1p)
{ {
if (!sm.reachable_state(q1p))
continue;
dout << "(10) augmenting paths based on Cand[" << q1 dout << "(10) augmenting paths based on Cand[" << q1
<< "] and Ref[" << q1p << "]\n"; << "] and Ref[" << q1p << "]\n";
path p1(q1, q1p); path p1(q1, q1p);
int p1id = d.pathid[p1]; int p1id = d.pathid[p1];
for (auto it: ref->succ(d.int_to_state[q1p])) for (auto& tr: ref->out(q1p))
{ {
const state* dps = it->current_state(); unsigned dp = tr.dst;
int dp = d.state_to_int[dps]; bdd all = tr.cond;
dps->destroy();
bdd all = it->current_condition();
while (all != bddfalse) while (all != bddfalse)
{ {
bdd s = bdd_satoneset(all, ap, bddfalse); bdd s = bdd_satoneset(all, ap, bddfalse);
all -= s; all -= s;
for (int q2 = 1; q2 <= d.cand_size; q2++) for (unsigned q2 = 0; q2 < d.cand_size; ++q2)
{ {
transition t(q1, s, q2); transition t(q1, s, q2);
int ti = d.transid[t]; int ti = d.transid[t];
@ -612,20 +589,24 @@ namespace spot
bdd all_acc = ref->all_acceptance_conditions(); bdd all_acc = ref->all_acceptance_conditions();
// construction of constraints (11,12,13) // construction of constraints (11,12,13)
for (int q1p = 1; q1p <= ref_size; ++q1p) for (unsigned q1p = 0; q1p < ref_size; ++q1p)
{ {
unsigned q1p_scc = sm.scc_of_state(d.int_to_state[q1p]); if (!sm.reachable_state(q1p))
for (int q2p = 1; q2p <= ref_size; ++q2p) continue;
unsigned q1p_scc = sm.scc_of(q1p);
for (unsigned q2p = 0; q2p < ref_size; ++q2p)
{ {
if (!sm.reachable_state(q2p))
continue;
// We are only interested in transition that can form a // We are only interested in transition that can form a
// cycle, so they must belong to the same SCC. // cycle, so they must belong to the same SCC.
if (sm.scc_of_state(d.int_to_state[q2p]) != q1p_scc) if (sm.scc_of(q2p) != q1p_scc)
continue; continue;
bool is_weak = is_weak_scc(sm, q1p_scc); bool is_weak = d.is_weak_scc[q1p_scc];
bool is_acc = sm.accepting(q1p_scc); bool is_acc = sm.is_accepting_scc(q1p_scc);
for (int q1 = 1; q1 <= d.cand_size; ++q1) for (unsigned q1 = 0; q1 < d.cand_size; ++q1)
for (int q2 = 1; q2 <= d.cand_size; ++q2) for (unsigned q2 = 0; q2 < d.cand_size; ++q2)
{ {
size_t sf = d.all_cand_acc.size(); size_t sf = d.all_cand_acc.size();
size_t sfp = is_weak ? 1 : d.all_ref_acc.size(); size_t sfp = is_weak ? 1 : d.all_ref_acc.size();
@ -639,24 +620,17 @@ namespace spot
int pid = d.pathid[p]; int pid = d.pathid[p];
for (auto it: ref->succ(d.int_to_state[q2p])) for (auto& tr: ref->out(q2p))
{ {
const state* dps = it->current_state(); unsigned dp = tr.dst;
// Skip destinations not in the SCC. // Skip destinations not in the SCC.
if (sm.scc_of_state(dps) != q1p_scc) if (sm.scc_of(dp) != q1p_scc)
{
dps->destroy();
continue; continue;
}
int dp = d.state_to_int[dps];
dps->destroy();
for (int q3 = 1; q3 <= d.cand_size; ++q3) for (unsigned q3 = 0; q3 < d.cand_size; ++q3)
{ {
bdd all = it->current_condition(); bdd all = tr.cond;
bdd curacc = bdd curacc = tr.acc;
it->current_acceptance_conditions();
while (all != bddfalse) while (all != bddfalse)
{ {
bdd l = bdd_satoneset(all, ap, bddfalse); bdd l = bdd_satoneset(all, ap, bddfalse);
@ -839,7 +813,7 @@ namespace spot
static tgba_digraph_ptr static tgba_digraph_ptr
sat_build(const satsolver::solution& solution, dict& satdict, sat_build(const satsolver::solution& solution, dict& satdict,
const_tgba_ptr aut, bool state_based) const_tgba_digraph_ptr aut, bool state_based)
{ {
auto autdict = aut->get_dict(); auto autdict = aut->get_dict();
auto a = make_tgba_digraph(autdict); auto a = make_tgba_digraph(autdict);
@ -888,8 +862,8 @@ namespace spot
acc = i->second; acc = i->second;
} }
last_aut_trans = a->new_transition(t->second.src - 1, last_aut_trans = a->new_transition(t->second.src,
t->second.dst - 1, t->second.dst,
t->second.cond, t->second.cond,
acc); acc);
last_sat_trans = &t->second; last_sat_trans = &t->second;
@ -936,7 +910,8 @@ namespace spot
} }
tgba_digraph_ptr tgba_digraph_ptr
dtgba_sat_synthetize (const const_tgba_ptr& a, unsigned target_acc_number, dtgba_sat_synthetize(const const_tgba_digraph_ptr& a,
unsigned target_acc_number,
int target_state_number, bool state_based) int target_state_number, bool state_based)
{ {
if (target_state_number == 0) if (target_state_number == 0)
@ -997,7 +972,7 @@ namespace spot
} }
tgba_digraph_ptr tgba_digraph_ptr
dtgba_sat_minimize(const const_tgba_ptr& a, dtgba_sat_minimize(const const_tgba_digraph_ptr& a,
unsigned target_acc_number, unsigned target_acc_number,
bool state_based) bool state_based)
{ {
@ -1019,7 +994,7 @@ namespace spot
} }
tgba_digraph_ptr tgba_digraph_ptr
dtgba_sat_minimize_dichotomy(const const_tgba_ptr& a, dtgba_sat_minimize_dichotomy(const const_tgba_digraph_ptr& a,
unsigned target_acc_number, unsigned target_acc_number,
bool state_based) bool state_based)
{ {

View file

@ -45,7 +45,7 @@ namespace spot
/// equivalent to \a a. If no such TGBA is found, a null pointer is /// equivalent to \a a. If no such TGBA is found, a null pointer is
/// returned. /// returned.
SPOT_API tgba_digraph_ptr SPOT_API tgba_digraph_ptr
dtgba_sat_synthetize(const const_tgba_ptr& a, dtgba_sat_synthetize(const const_tgba_digraph_ptr& a,
unsigned target_acc_number, unsigned target_acc_number,
int target_state_number, int target_state_number,
bool state_based = false); bool state_based = false);
@ -57,7 +57,7 @@ namespace spot
/// ///
/// If no smaller TGBA exist, this returns a null pointer. /// If no smaller TGBA exist, this returns a null pointer.
SPOT_API tgba_digraph_ptr SPOT_API tgba_digraph_ptr
dtgba_sat_minimize(const const_tgba_ptr& a, dtgba_sat_minimize(const const_tgba_digraph_ptr& a,
unsigned target_acc_number, unsigned target_acc_number,
bool state_based = false); bool state_based = false);
@ -68,7 +68,7 @@ namespace spot
// //
/// If no smaller TBA exist, this returns a null pointer. /// If no smaller TBA exist, this returns a null pointer.
SPOT_API tgba_digraph_ptr SPOT_API tgba_digraph_ptr
dtgba_sat_minimize_dichotomy(const const_tgba_ptr& a, dtgba_sat_minimize_dichotomy(const const_tgba_digraph_ptr& a,
unsigned target_acc_number, unsigned target_acc_number,
bool state_based = false); bool state_based = false);
} }

View file

@ -250,6 +250,17 @@ namespace spot
return result; return result;
} }
std::vector<bool> scc_info::weak_sccs() const
{
unsigned n = scc_count();
std::vector<bool> result(scc_count());
auto acc = used_acc();
bdd all = bdd_support(aut_->neg_acceptance_conditions());
for (unsigned s = 0; s < n; ++s)
result[s] = !is_accepting_scc(s) || acc[s] == all;
return result;
}
bdd scc_info::scc_ap_support(unsigned scc) const bdd scc_info::scc_ap_support(unsigned scc) const
{ {
bdd support = bddtrue; bdd support = bddtrue;

View file

@ -147,6 +147,8 @@ namespace spot
/// at position #i. /// at position #i.
std::vector<bdd> used_acc() const; std::vector<bdd> used_acc() const;
std::vector<bool> weak_sccs() const;
bdd scc_ap_support(unsigned scc) const; bdd scc_ap_support(unsigned scc) const;
}; };

View file

@ -1369,7 +1369,8 @@ checked_main(int argc, char** argv)
if (opt_dtbasat >= 0) if (opt_dtbasat >= 0)
{ {
tm.start("dtbasat"); tm.start("dtbasat");
auto satminimized = dtba_sat_synthetize(a, opt_dtbasat); auto satminimized =
dtba_sat_synthetize(ensure_digraph(a), opt_dtbasat);
tm.stop("dtbasat"); tm.stop("dtbasat");
if (satminimized) if (satminimized)
a = satminimized; a = satminimized;
@ -1377,7 +1378,8 @@ checked_main(int argc, char** argv)
else if (opt_dtgbasat >= 0) else if (opt_dtgbasat >= 0)
{ {
tm.start("dtgbasat"); tm.start("dtgbasat");
auto satminimized = dtgba_sat_minimize(a, opt_dtgbasat); auto satminimized = dtgba_sat_minimize(ensure_digraph(a),
opt_dtgbasat);
tm.stop("dtgbasat"); tm.stop("dtgbasat");
if (satminimized) if (satminimized)
a = satminimized; a = satminimized;