safra: Nodes are grouped by SCC

* src/tests/safra.cc, src/tests/safra.test: Update it.
* src/twaalgos/safra.cc, src/twaalgos/safra.hh: all nodes in a safra
state are grouped by SCC. This is done by putting them in different
braces.  The same SCC can have different ids depending on the safra
state.
This commit is contained in:
Alexandre Lewkowicz 2015-09-24 14:53:53 +02:00 committed by Alexandre Duret-Lutz
parent b59b31f806
commit bb93f6e9af
4 changed files with 177 additions and 148 deletions

View file

@ -38,14 +38,13 @@ int help()
std::cerr << "\t-p\tpretty print states\n"; std::cerr << "\t-p\tpretty print states\n";
std::cerr << "\t-H\toutput hoa format\n"; std::cerr << "\t-H\toutput hoa format\n";
std::cerr << "\t-b\treduce result using bisimulation\n"; std::cerr << "\t-b\treduce result using bisimulation\n";
std::cerr << "\t--emit_scc\ttransitions to accpting scc are accepting\n"; std::cerr << "\t--scc_opt\tUse an SCC-based Safra\n";
return 1; return 1;
} }
int main(int argc, char* argv[]) int main(int argc, char* argv[])
{ {
bool emit_scc = false; bool scc_opt = false;
bool track_scc = false;
bool sim = false; bool sim = false;
bool in_hoa = false; bool in_hoa = false;
bool in_ltl = false; bool in_ltl = false;
@ -81,12 +80,13 @@ int main(int argc, char* argv[])
pretty_print = true; pretty_print = true;
else if (!strncmp(argv[i], "-b", 2)) else if (!strncmp(argv[i], "-b", 2))
sim = true; sim = true;
else if (!strncmp(argv[i], "--emit_scc", 10)) else if (!strncmp(argv[i], "--scc_opt", 9))
emit_scc = true; scc_opt = true;
else if (!strncmp(argv[i], "--track_scc", 11))
track_scc = true;
else else
std::cerr << "Warning: " << argv[i] << " not used\n"; {
std::cerr << "Warning: " << argv[i] << " not used\n";
return 1;
}
} }
if (!input) if (!input)
@ -104,8 +104,7 @@ int main(int argc, char* argv[])
spot::translator trans(dict); spot::translator trans(dict);
trans.set_pref(spot::postprocessor::Deterministic); trans.set_pref(spot::postprocessor::Deterministic);
auto tmp = trans.run(f); auto tmp = trans.run(f);
res = spot::tgba_determinisation(tmp, sim, pretty_print, emit_scc, res = spot::tgba_determinisation(tmp, sim, pretty_print, scc_opt);
track_scc);
f->destroy(); f->destroy();
} }
else if (in_hoa) else if (in_hoa)
@ -114,8 +113,7 @@ int main(int argc, char* argv[])
auto aut = spot::parse_aut(input, pel, dict); auto aut = spot::parse_aut(input, pel, dict);
if (spot::format_parse_aut_errors(std::cerr, input, pel)) if (spot::format_parse_aut_errors(std::cerr, input, pel))
return 2; return 2;
res = tgba_determinisation(aut->aut, sim, pretty_print, emit_scc, res = tgba_determinisation(aut->aut, sim, pretty_print, scc_opt);
track_scc);
} }
res->merge_edges(); res->merge_edges();

View file

@ -117,7 +117,7 @@ EOF
run 0 ../safra --hoa double_b.hoa -H > out.hoa run 0 ../safra --hoa double_b.hoa -H > out.hoa
diff out.hoa out.exp diff out.hoa out.exp
# Test emit_scc optim # Test scc-based optim
cat > out.exp << EOF cat > out.exp << EOF
HOA: v1 HOA: v1
States: 3 States: 3
@ -129,9 +129,9 @@ properties: trans-labels explicit-labels trans-acc deterministic
--BODY-- --BODY--
State: 0 "{₀0₀}" State: 0 "{₀0₀}"
[0] 1 {1} [0] 1 {1}
State: 1 "{₀0 1₀}" State: 1 "{₀0₀}{₁1₁}"
[0] 1 {1} [0] 1 {1}
[!0&1] 2 {1} [!0&1] 2 {0}
State: 2 "{₀1₀}" State: 2 "{₀1₀}"
[0&!1] 2 [0&!1] 2
[1] 2 {1} [1] 2 {1}
@ -154,7 +154,7 @@ State: 1
--END-- --END--
EOF EOF
run 0 ../safra --hoa in.hoa --emit_scc -p -H > out.hoa run 0 ../safra --hoa in.hoa --scc_opt -p -H > out.hoa
diff out.hoa out.exp diff out.hoa out.exp
# Formulas from bench/dtgbasat/formulas # Formulas from bench/dtgbasat/formulas

View file

@ -73,138 +73,166 @@ namespace spot
return lhs.size() > rhs.size(); return lhs.size() > rhs.size();
} }
// Used to remove all acceptance whos value is above and equal max_acc // Used to remove all acceptance whos value is above and equal max_acc
void remove_dead_acc(twa_graph_ptr& aut, unsigned max_acc) void remove_dead_acc(twa_graph_ptr& aut, unsigned max_acc)
{ {
assert(max_acc < 32); assert(max_acc < 32);
unsigned mask = (1 << max_acc) - 1; unsigned mask = (1 << max_acc) - 1;
for (auto& t: aut->edges()) for (auto& t: aut->edges())
{
t.acc &= mask;
}
}
struct compare
{
bool
operator() (const safra_state::safra_node_t& lhs,
const safra_state::safra_node_t& rhs)
{ {
return lhs.second < rhs.second; t.acc &= mask;
} }
};
// Return the sorteds nodes in ascending order
std::vector<safra_state::safra_node_t>
sorted_nodes(const safra_state::nodes_t& nodes)
{
std::vector<safra_state::safra_node_t> res;
for (auto& n: nodes)
res.emplace_back(n.first, n.second);
std::sort(res.begin(), res.end(), compare());
return res;
}
std::string
nodes_to_string(const safra_state::nodes_t& states)
{
auto copy = sorted_nodes(states);
std::ostringstream os;
std::stack<unsigned> s;
bool first = true;
for (auto& n: copy)
{
auto it = n.second.begin();
// Find brace on top of stack in vector
// If brace is not present, then we close it as no other ones of that
// type will be found since we ordered our vector
while (!s.empty())
{
it = std::lower_bound(n.second.begin(), n.second.end(),
s.top());
if (it == n.second.end() || *it != s.top())
{
os << subscript(s.top()) << '}';
s.pop();
}
else
{
if (*it == s.top())
++it;
break;
}
}
// Add new braces
while (it != n.second.end())
{
os << '{' << subscript(*it);
s.push(*it);
++it;
first = true;
}
if (!first)
os << ' ';
os << n.first;
first = false;
}
// Finish unwinding stack to print last braces
while (!s.empty())
{
os << subscript(s.top()) << '}';
s.pop();
}
return os.str();
}
std::vector<std::string>*
print_debug(std::map<safra_state, int>& states)
{
auto res = new std::vector<std::string>(states.size());
for (auto& p: states)
(*res)[p.second] = nodes_to_string(p.first.nodes_);
return res;
}
} }
void struct compare
safra_state::compute_succs(const const_twa_graph_ptr& aut,
const std::vector<bdd_id_t>& bddnums,
std::unordered_map<bdd,
std::pair<unsigned, unsigned>,
bdd_hash>& deltas,
succs_t& res,
const scc_info& scc,
bool track_scc) const
{ {
// Given a bdd returns index of associated safra_state in res bool
std::map<unsigned, unsigned> bdd2num; operator() (const safra_state::safra_node_t& lhs,
for (auto& node: nodes_) const safra_state::safra_node_t& rhs)
{
return lhs.second < rhs.second;
}
};
// Return the sorteds nodes in ascending order
std::vector<safra_state::safra_node_t>
sorted_nodes(const safra_state::nodes_t& nodes)
{
std::vector<safra_state::safra_node_t> res;
for (auto& n: nodes)
res.emplace_back(n.first, n.second);
std::sort(res.begin(), res.end(), compare());
return res;
}
std::string
nodes_to_string(const safra_state::nodes_t& states)
{
auto copy = sorted_nodes(states);
std::ostringstream os;
std::stack<unsigned> s;
bool first = true;
for (auto& n: copy)
{ {
for (auto& t: aut->out(node.first)) auto it = n.second.begin();
// Find brace on top of stack in vector
// If brace is not present, then we close it as no other ones of that
// type will be found since we ordered our vector
while (!s.empty())
{ {
// deltas precalculate all transitions in edge t it = std::lower_bound(n.second.begin(), n.second.end(),
auto p = deltas[t.cond]; s.top());
for (unsigned j = p.first; j < p.second; ++j) if (it == n.second.end() || *it != s.top())
{ {
auto i = bdd2num.insert(std::make_pair(bddnums[j], res.size())); os << subscript(s.top()) << '}';
unsigned idx; s.pop();
if (!i.second) }
// No new state added, so get pre-existing state whichi is an else
// index in the vector of safra states (res) {
idx = i.first->second; if (*it == s.top())
else ++it;
break;
}
}
// Add new braces
while (it != n.second.end())
{
os << '{' << subscript(*it);
s.push(*it);
++it;
first = true;
}
if (!first)
os << ' ';
os << n.first;
first = false;
}
// Finish unwinding stack to print last braces
while (!s.empty())
{
os << subscript(s.top()) << '}';
s.pop();
}
return os.str();
}
std::vector<std::string>*
print_debug(std::map<safra_state, int>& states)
{
auto res = new std::vector<std::string>(states.size());
for (auto& p: states)
(*res)[p.second] = nodes_to_string(p.first.nodes_);
return res;
}
}
unsigned
safra_state::find_scc_brace_id(unsigned scc_id, const scc_info& scc)
{
for (auto& n: nodes_)
{
if (scc_id == scc.scc_of(n.first))
return n.second.front();
}
return -1U;
}
void
safra_state::compute_succs(const const_twa_graph_ptr& aut,
const std::vector<bdd_id_t>& bddnums,
std::unordered_map<bdd,
std::pair<unsigned, unsigned>,
bdd_hash>& deltas,
succs_t& res,
const scc_info& scc,
bool scc_opt) const
{
// Given a bdd returns index of associated safra_state in res
std::map<unsigned, unsigned> bdd2num;
for (auto& node: nodes_)
{
for (auto& t: aut->out(node.first))
{
// deltas precalculate all transitions in edge t
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;
if (!i.second)
// No new state added, so get pre-existing state whichi is an
// index in the vector of safra states (res)
idx = i.first->second;
else
{
// Each new node starts out with same number of nodes as src
idx = res.size();
res.emplace_back(safra_state(nb_braces_.size()),
bddnums[j]);
}
safra_state& ss = res[idx].first;
// Check if we are leaving the SCC, if so we delete all the
// braces as no cycles can be found with that node
if (scc_opt && scc.scc_of(node.first) != scc.scc_of(t.dst))
if (scc.is_accepting_scc(scc.scc_of(t.dst)))
{ {
// Each new node starts out with same number of nodes as src // Find scc_id in the safra state currently being
idx = res.size(); // constructed
res.emplace_back(safra_state(nb_braces_.size()), unsigned scc_id = ss.find_scc_brace_id(scc.scc_of(t.dst),
bddnums[j]); scc);
} // If SCC is present in node use the same id
safra_state& ss = res[idx].first; if (scc_id != -1U)
// Check if we are leaving the SCC, if so we delete all the ss.update_succ({ scc_id }, t.dst,
// braces as no cycles can be found with that node { /* empty */ });
if (track_scc && scc.scc_of(node.first) != scc.scc_of(t.dst)) // Create a new SCC
ss.update_succ({ /* no braces */ }, t.dst, t.acc); else
ss.update_succ({ /* no braces */ }, t.dst,
{ 0 });
}
else
// When entering non accepting SCC don't create any braces
ss.update_succ({ /* no braces */ }, t.dst, { /* empty */ });
else else
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());
@ -325,7 +353,7 @@ namespace spot
std::vector<node_helper::brace_t> copy = braces; std::vector<node_helper::brace_t> copy = braces;
if (acc.count()) if (acc.count())
{ {
assert(acc.has(0) && acc.count() == 1 && "Only one TBA are accepted"); assert(acc.has(0) && acc.count() == 1 && "Only TBA are accepted");
// Accepting edge generate new braces: step A1 // Accepting edge generate new braces: step A1
copy.emplace_back(nb_braces_.size()); copy.emplace_back(nb_braces_.size());
// nb_braces_ gets updated later so put 0 for now // nb_braces_ gets updated later so put 0 for now
@ -412,13 +440,14 @@ namespace spot
twa_graph_ptr twa_graph_ptr
tgba_determinisation(const const_twa_graph_ptr& a, bool bisimulation, tgba_determinisation(const const_twa_graph_ptr& a, bool bisimulation,
bool pretty_print, bool emit_scc, bool track_scc) bool pretty_print, bool scc_opt)
{ {
// Degeneralize // Degeneralize
twa_graph_ptr aut = spot::degeneralize_tba(a); twa_graph_ptr aut = spot::degeneralize_tba(a);
scc_info scc = scc_info(aut); scc_info scc = scc_info(aut);
if (emit_scc) // TODO
emit_accepting_scc(aut, scc); // if (emit_scc)
// emit_accepting_scc(aut, scc);
bdd allap = bddtrue; bdd allap = bddtrue;
@ -474,7 +503,7 @@ namespace spot
power_set seen; power_set seen;
auto init_state = aut->get_init_state_number(); auto init_state = aut->get_init_state_number();
bool start_accepting = scc.is_accepting_scc(scc.scc_of(init_state)) || bool start_accepting = scc.is_accepting_scc(scc.scc_of(init_state)) ||
(!track_scc && !emit_scc); !scc_opt;
safra_state init(init_state, true, start_accepting); safra_state init(init_state, true, start_accepting);
unsigned num = res->new_state(); unsigned num = res->new_state();
res->set_init_state(num); res->set_init_state(num);
@ -489,7 +518,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();
curr.compute_succs(aut, bddnums, deltas, succs, scc, track_scc); curr.compute_succs(aut, bddnums, deltas, succs, scc, scc_opt);
for (auto s: succs) for (auto s: succs)
{ {
auto i = seen.find(s.first); auto i = seen.find(s.first);

View file

@ -61,7 +61,10 @@ namespace spot
bdd_hash>& deltas, bdd_hash>& deltas,
succs_t& res, succs_t& res,
const scc_info& scc, const scc_info& scc,
bool track_scc = false) const; bool scc_opt = false) const;
// scc_id has to be an accepting SCC. This function tries to find a node
// who lives in that SCC and if it does, we return the brace_id of that SCC.
unsigned find_scc_brace_id(unsigned scc_id, const scc_info& scc);
// The outermost brace of each node cannot be green // The outermost brace of each node cannot be green
void ungreenify_last_brace(); void ungreenify_last_brace();
// Used when creating the list of successors // Used when creating the list of successors
@ -87,6 +90,5 @@ namespace spot
tgba_determinisation(const const_twa_graph_ptr& aut, tgba_determinisation(const const_twa_graph_ptr& aut,
bool bisimulation = false, bool bisimulation = false,
bool pretty_print = false, bool pretty_print = false,
bool emit_scc = false, bool scc_opt = false);
bool track_scc = false);
} }