sccinfo: make it easier to iterate over all SCCs

* src/tgbaalgos/sccinfo.cc, src/tgbaalgos/sccinfo.hh: add
scc_info::begin() and scc_info::end() methods to iterate over the
node_ vector.   Tidy the scc_node structure that that its member
are accessed via methods.
* src/tgbaalgos/safety.cc, src/bin/ltlcross.cc: Simplify using
this interface.
This commit is contained in:
Alexandre Duret-Lutz 2015-01-09 12:01:42 +01:00
parent 579e8fc0a9
commit e01ab2b236
4 changed files with 114 additions and 67 deletions

View file

@ -1,5 +1,5 @@
// -*- coding: utf-8 -*- // -*- coding: utf-8 -*-
// Copyright (C) 2012, 2013, 2014 Laboratoire de Recherche et // Copyright (C) 2012, 2013, 2014, 2015 Laboratoire de Recherche et
// Développement de l'Epita (LRDE). // Développement de l'Epita (LRDE).
// //
// This file is part of Spot, a model checking library. // This file is part of Spot, a model checking library.
@ -1186,9 +1186,8 @@ namespace
{ {
// r == true iff the automaton i is accepting. // r == true iff the automaton i is accepting.
bool r = false; bool r = false;
unsigned c = m->scc_count(); for (auto& scc: *m)
for (unsigned j = 0; j < c; ++j) if (scc.is_accepting())
if (m->is_accepting_scc(j))
{ {
r = true; r = true;
break; break;
@ -1240,15 +1239,13 @@ namespace
// Collect all the states of SSPACE that appear in the accepting SCCs // Collect all the states of SSPACE that appear in the accepting SCCs
// of PROD. (Trivial SCCs are considered accepting.) // of PROD. (Trivial SCCs are considered accepting.)
static void static void
states_in_acc(const spot::scc_info* m, states_in_acc(const spot::scc_info* m, state_set& s)
state_set& s)
{ {
auto aut = m->get_aut(); auto aut = m->get_aut();
auto ps = aut->get_named_prop<const spot::product_states>("product-states"); auto ps = aut->get_named_prop<const spot::product_states>("product-states");
unsigned c = m->scc_count(); for (auto& scc: *m)
for (unsigned n = 0; n < c; ++n) if (scc.is_accepting() || scc.is_trivial())
if (m->is_accepting_scc(n) || m->is_trivial(n)) for (auto i: scc.states())
for (auto i: m->states_of(n))
// Get the projection on sspace. // Get the projection on sspace.
s.insert((*ps)[i].second); s.insert((*ps)[i].second);
} }

View file

@ -1,6 +1,6 @@
// -*- coding: utf-8 -*- // -*- coding: utf-8 -*-
// Copyright (C) 2010, 2011, 2013, 2014 Laboratoire de Recherche et // Copyright (C) 2010, 2011, 2013, 2014, 2015 Laboratoire de Recherche
// Développement de l'Epita (LRDE) // et Développement de l'Epita (LRDE)
// //
// This file is part of Spot, a model checking library. // This file is part of Spot, a model checking library.
// //
@ -25,22 +25,21 @@ namespace spot
{ {
bool bool
is_guarantee_automaton(const const_tgba_digraph_ptr& aut, is_guarantee_automaton(const const_tgba_digraph_ptr& aut,
const scc_info* sm) const scc_info* si)
{ {
// Create an scc_info if the user did not give one to us. // Create an scc_info if the user did not give one to us.
bool need_sm = !sm; bool need_si = !si;
if (need_sm) if (need_si)
sm = new scc_info(aut); si = new scc_info(aut);
bool result = true; bool result = true;
unsigned scc_count = sm->scc_count(); for (auto& scc: *si)
for (unsigned scc = 0; scc < scc_count; ++scc)
{ {
if (!sm->is_accepting_scc(scc)) if (!scc.is_accepting())
continue; continue;
// Accepting SCCs should have only one state. // Accepting SCCs should have only one state.
auto& st = sm->states_of(scc); auto& st = scc.states();
if (st.size() != 1) if (st.size() != 1)
{ {
result = false; result = false;
@ -58,8 +57,8 @@ namespace spot
break; break;
} }
if (need_sm) if (need_si)
delete sm; delete si;
return result; return result;
} }

View file

@ -1,6 +1,6 @@
// -*- coding: utf-8 -*- // -*- coding: utf-8 -*-
// Copyright (C) 2014 Laboratoire de Recherche et Développement de // Copyright (C) 2014, 2015 Laboratoire de Recherche et Développement
// l'Epita. // de l'Epita.
// //
// This file is part of Spot, a model checking library. // This file is part of Spot, a model checking library.
// //
@ -100,7 +100,7 @@ namespace spot
// Fill STATES with any state removed, so that // Fill STATES with any state removed, so that
// remove_component() does not have to traverse the SCC // remove_component() does not have to traverse the SCC
// again. // again.
root_.front().node.states.push_front(curr); root_.front().node.states_.push_front(curr);
// When backtracking the root of an SCC, we must also // When backtracking the root of an SCC, we must also
// remove that SCC from the ARC/ROOT stacks. We must // remove that SCC from the ARC/ROOT stacks. We must
@ -109,23 +109,23 @@ namespace spot
if (root_.front().index == h_[curr]) if (root_.front().index == h_[curr])
{ {
int num = node_.size(); int num = node_.size();
for (auto s: root_.front().node.states) for (auto s: root_.front().node.states_)
{ {
sccof_[s] = num; sccof_[s] = num;
h_[s] = num + 1; h_[s] = num + 1;
} }
bdd cond = root_.front().in_cond; bdd cond = root_.front().in_cond;
auto acc = root_.front().node.acc; auto acc = root_.front().node.acc_marks();
bool triv = root_.front().node.trivial; bool triv = root_.front().node.is_trivial();
node_.emplace_back(acc, triv); node_.emplace_back(acc, triv);
std::swap(node_.back().succ, root_.front().node.succ); std::swap(node_.back().succ_, root_.front().node.succ_);
std::swap(node_.back().states, root_.front().node.states); std::swap(node_.back().states_, root_.front().node.states_);
node_.back().accepting = !triv && aut->acc().accepting(acc); node_.back().accepting_ = !triv && aut->acc().accepting(acc);
root_.pop_front(); root_.pop_front();
// Record the transition between the SCC being popped // Record the transition between the SCC being popped
// and the previous SCC. // and the previous SCC.
if (!root_.empty()) if (!root_.empty())
root_.front().node.succ.emplace_back(cond, num); root_.front().node.succ_.emplace_back(cond, num);
} }
continue; continue;
} }
@ -159,7 +159,7 @@ namespace spot
--spi; --spi;
// Record that there is a transition from this SCC to the // Record that there is a transition from this SCC to the
// dest SCC labelled with cond. // dest SCC labelled with cond.
auto& succ = root_.front().node.succ; auto& succ = root_.front().node.succ_;
scc_succs::iterator i = std::find_if(succ.begin(), succ.end(), scc_succs::iterator i = std::find_if(succ.begin(), succ.end(),
[spi](const scc_trans& x) { [spi](const scc_trans& x) {
return (x.dst == return (x.dst ==
@ -189,13 +189,13 @@ namespace spot
while (threshold > root_.front().index) while (threshold > root_.front().index)
{ {
assert(!root_.empty()); assert(!root_.empty());
acc |= root_.front().node.acc; acc |= root_.front().node.acc_;
acc |= root_.front().in_acc; acc |= root_.front().in_acc;
states.splice(states.end(), root_.front().node.states); states.splice(states.end(), root_.front().node.states_);
succs.insert(succs.end(), succs.insert(succs.end(),
root_.front().node.succ.begin(), root_.front().node.succ_.begin(),
root_.front().node.succ.end()); root_.front().node.succ_.end());
root_.pop_front(); root_.pop_front();
} }
@ -206,24 +206,31 @@ namespace spot
// Accumulate all acceptance conditions, states, SCC // Accumulate all acceptance conditions, states, SCC
// successors, and conditions into the merged SCC. // successors, and conditions into the merged SCC.
root_.front().node.acc |= acc; root_.front().node.acc_ |= acc;
root_.front().node.states.splice(root_.front().node.states.end(), root_.front().node.states_.splice(root_.front().node.states_.end(),
states); states);
root_.front().node.succ.insert(root_.front().node.succ.end(), root_.front().node.succ_.insert(root_.front().node.succ_.end(),
succs.begin(), succs.end()); succs.begin(), succs.end());
// This SCC is no longer trivial. // This SCC is no longer trivial.
root_.front().node.trivial = false; root_.front().node.trivial_ = false;
} }
// An SCC is useful if it is accepting or it has a successor SCC // An SCC is useful if it is accepting or it has a successor SCC
// that is useful. // that is useful.
unsigned scccount = scc_count(); unsigned scccount = scc_count();
for (unsigned i = 0; i < scccount; i++) for (unsigned i = 0; i < scccount; ++i)
{ {
bool useful = node_[i].accepting; if (node_[i].is_accepting())
for (auto j: node_[i].succ) {
useful |= node_[j.dst].useful; node_[i].useful_ = true;
node_[i].useful = useful; continue;
}
for (auto j: node_[i].succ())
if (node_[j.dst].is_useful())
{
node_[i].useful_ = true;
break;
}
} }
} }

View file

@ -1,6 +1,6 @@
// -*- coding: utf-8 -*- // -*- coding: utf-8 -*-
// Copyright (C) 2014 Laboratoire de Recherche et Développement de // Copyright (C) 2014, 2015 Laboratoire de Recherche et Développement
// l'Epita. // de l'Epita.
// //
// This file is part of Spot, a model checking library. // This file is part of Spot, a model checking library.
// //
@ -25,7 +25,6 @@
namespace spot namespace spot
{ {
class SPOT_API scc_info class SPOT_API scc_info
{ {
public: public:
@ -41,24 +40,56 @@ namespace spot
typedef std::vector<scc_trans> scc_succs; typedef std::vector<scc_trans> scc_succs;
struct scc_node class scc_node
{ {
friend class scc_info;
protected:
scc_succs succ_;
acc_cond::mark_t acc_;
std::list<unsigned> states_; // States of the component
bool trivial_:1;
bool accepting_:1;
bool useful_:1;
public:
scc_node(): scc_node():
acc(0U), trivial(true), accepting(false), useful(false) acc_(0U), trivial_(true), accepting_(false), useful_(false)
{ {
} }
scc_node(acc_cond::mark_t acc, bool trivial): scc_node(acc_cond::mark_t acc, bool trivial):
acc(acc), trivial(trivial), accepting(false), useful(false) acc_(acc), trivial_(trivial), accepting_(false), useful_(false)
{ {
} }
scc_succs succ; bool is_trivial() const
acc_cond::mark_t acc; {
std::list<unsigned> states; // States of the component return trivial_;
bool trivial:1; }
bool accepting:1;
bool useful:1; bool is_accepting() const
{
return accepting_;
}
bool is_useful() const
{
return useful_;
}
acc_cond::mark_t acc_marks() const
{
return acc_;
}
const std::list<unsigned>& states() const
{
return states_;
}
const scc_succs& succ() const
{
return succ_;
}
}; };
protected: protected:
@ -98,9 +129,22 @@ namespace spot
return sccof_[st]; return sccof_[st];
} }
auto begin() const
SPOT_RETURN(node_.begin());
auto end() const
SPOT_RETURN(node_.end());
auto cbegin() const
SPOT_RETURN(node_.cbegin());
auto cend() const
SPOT_RETURN(node_.cend());
auto rbegin() const
SPOT_RETURN(node_.rbegin());
auto rend() const
SPOT_RETURN(node_.rend());
const std::list<unsigned>& states_of(unsigned scc) const const std::list<unsigned>& states_of(unsigned scc) const
{ {
return node(scc).states; return node(scc).states();
} }
unsigned one_state_of(unsigned scc) const unsigned one_state_of(unsigned scc) const
@ -117,32 +161,32 @@ namespace spot
const scc_succs& succ(unsigned scc) const const scc_succs& succ(unsigned scc) const
{ {
return node(scc).succ; return node(scc).succ();
} }
bool is_trivial(unsigned scc) const bool is_trivial(unsigned scc) const
{ {
return node(scc).trivial; return node(scc).is_trivial();
} }
acc_cond::mark_t acc(unsigned scc) const acc_cond::mark_t acc(unsigned scc) const
{ {
return node(scc).acc; return node(scc).acc_marks();
} }
bool is_accepting_scc(unsigned scc) const bool is_accepting_scc(unsigned scc) const
{ {
return node(scc).accepting; return node(scc).is_accepting();
} }
bool is_useful_scc(unsigned scc) const bool is_useful_scc(unsigned scc) const
{ {
return node(scc).useful; return node(scc).is_useful();
} }
bool is_useful_state(unsigned st) const bool is_useful_state(unsigned st) const
{ {
return reachable_state(st) && node(scc_of(st)).useful; return reachable_state(st) && node(scc_of(st)).is_useful();
} }
/// \brief Return the set of all used acceptance combinations, for /// \brief Return the set of all used acceptance combinations, for