Replace most uses of scc_map by scc_info.
This involves reimplementing some algorithms using tgba_digraph, and implementing an explicit product that takes two tgba_digraphs and produces a tgba_digraph. * src/tgbaalgos/product.cc, src/tgbaalgos/product.hh: New files. * src/tgbaalgos/Makefile.am: Adjust. * src/bin/ltlcross.cc, src/tgba/tgba.cc, src/tgba/tgba.hh, src/tgba/tgbasafracomplement.cc, src/tgba/tgbasafracomplement.hh, src/tgbaalgos/cycles.cc, src/tgbaalgos/cycles.hh, src/tgbaalgos/degen.cc, src/tgbaalgos/degen.hh, src/tgbaalgos/isweakscc.cc, src/tgbaalgos/isweakscc.hh, src/tgbaalgos/minimize.cc, src/tgbaalgos/minimize.hh, src/tgbaalgos/powerset.cc, src/tgbaalgos/powerset.hh, src/tgbaalgos/safety.cc, src/tgbaalgos/safety.hh, src/tgbaalgos/sccinfo.cc, src/tgbaalgos/sccinfo.hh, src/tgbatest/complementation.cc, src/tgbatest/emptchk.cc, src/tgbatest/ltl2ta.test, src/tgbatest/ltl2tgba.cc, src/tgbatest/randtgba.cc: Update to use scc_info and/or tgba_digraph.
This commit is contained in:
parent
b6745482af
commit
2fb436a174
27 changed files with 497 additions and 394 deletions
|
|
@ -53,6 +53,7 @@ tgbaalgos_HEADERS = \
|
|||
neverclaim.hh \
|
||||
postproc.hh \
|
||||
powerset.hh \
|
||||
product.hh \
|
||||
projrun.hh \
|
||||
randomgraph.hh \
|
||||
reachiter.hh \
|
||||
|
|
@ -100,6 +101,7 @@ libtgbaalgos_la_SOURCES = \
|
|||
neverclaim.cc \
|
||||
postproc.cc \
|
||||
powerset.cc \
|
||||
product.cc \
|
||||
projrun.cc \
|
||||
randomgraph.cc \
|
||||
reachiter.cc \
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@
|
|||
|
||||
namespace spot
|
||||
{
|
||||
enumerate_cycles::enumerate_cycles(const scc_map& map)
|
||||
enumerate_cycles::enumerate_cycles(const scc_info& map)
|
||||
: aut_(map.get_aut()), sm_(map)
|
||||
{
|
||||
}
|
||||
|
|
@ -84,12 +84,13 @@ namespace spot
|
|||
dfs_.push_back(e);
|
||||
}
|
||||
|
||||
// FIXME: Recode this algorithm using unsigned states.
|
||||
void
|
||||
enumerate_cycles::run(unsigned scc)
|
||||
{
|
||||
bool keep_going = true;
|
||||
|
||||
push_state(tag_state(sm_.one_state_of(scc)->clone()));
|
||||
push_state(tag_state(aut_->state_from_number(sm_.one_state_of(scc))));
|
||||
|
||||
while (keep_going && !dfs_.empty())
|
||||
{
|
||||
|
|
@ -109,7 +110,7 @@ namespace spot
|
|||
// Ignore those that are not on the SCC, or destination
|
||||
// that have been "virtually" deleted from A(v).
|
||||
state* s = cur.succ->current_state();
|
||||
if ((sm_.scc_of_state(s) != scc)
|
||||
if ((sm_.scc_of(aut_->state_number(s)) != scc)
|
||||
|| (cur.ts->second.del.find(s) != cur.ts->second.del.end()))
|
||||
{
|
||||
s->destroy();
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@
|
|||
#ifndef SPOT_TGBAALGOS_CYCLES_HH
|
||||
# define SPOT_TGBAALGOS_CYCLES_HH
|
||||
|
||||
#include "scc.hh"
|
||||
#include "sccinfo.hh"
|
||||
#include "misc/hash.hh"
|
||||
#include <deque>
|
||||
|
||||
|
|
@ -62,13 +62,11 @@ namespace spot
|
|||
/// dfs_ stack. Only the last portion of this stack may form a
|
||||
/// cycle.
|
||||
///
|
||||
/// The class constructor takes an scc_map that should already have
|
||||
/// been built for its automaton. Calling <code>run(n)</code> will
|
||||
/// enumerate all elementary cycles in SCC <code>n</code>. Each
|
||||
/// time an SCC is found, the method cycle_found(s) is called with
|
||||
/// the initial state s of the cycle: the cycle is constituted from
|
||||
/// all the states that are on the \c dfs_ stack after \c s
|
||||
/// (including \c s).
|
||||
/// Calling <code>run(n)</code> will enumerate all elementary cycles
|
||||
/// in SCC <code>n</code>. Each time an SCC is found, the method
|
||||
/// cycle_found(s) is called with the initial state s of the cycle:
|
||||
/// the cycle is constituted from all the states that are on the \c
|
||||
/// dfs_ stack after \c s (including \c s).
|
||||
///
|
||||
/// You should inherit from this class and redefine the
|
||||
/// cycle_found() method to perform any work you would like to do on
|
||||
|
|
@ -112,9 +110,9 @@ namespace spot
|
|||
typedef hash_type::iterator tagged_state;
|
||||
|
||||
// The automaton we are working on.
|
||||
const_tgba_ptr aut_;
|
||||
const_tgba_digraph_ptr aut_;
|
||||
// The SCC map built for aut_.
|
||||
const scc_map& sm_;
|
||||
const scc_info& sm_;
|
||||
|
||||
// The DFS stack. Each entry contains a tagged state, an iterator
|
||||
// on the transitions leaving that state, and a Boolean f
|
||||
|
|
@ -131,7 +129,7 @@ namespace spot
|
|||
dfs_stack dfs_;
|
||||
|
||||
public:
|
||||
enumerate_cycles(const scc_map& map);
|
||||
enumerate_cycles(const scc_info& map);
|
||||
virtual ~enumerate_cycles() {}
|
||||
|
||||
/// \brief Run in SCC scc, and call \a cycle_found() for any new
|
||||
|
|
|
|||
|
|
@ -26,7 +26,7 @@
|
|||
#include <vector>
|
||||
#include <algorithm>
|
||||
#include <iterator>
|
||||
#include "tgbaalgos/scc.hh"
|
||||
#include "tgbaalgos/sccinfo.hh"
|
||||
#include "tgba/bddprint.hh"
|
||||
|
||||
//#define DEGEN_DEBUG
|
||||
|
|
@ -38,32 +38,19 @@ namespace spot
|
|||
// A state in the degenalized automaton corresponds to a state in
|
||||
// the TGBA associated to a level. The level is just an index in
|
||||
// the list of acceptance sets.
|
||||
typedef std::pair<const state*, unsigned> degen_state;
|
||||
typedef std::pair<unsigned, unsigned> degen_state;
|
||||
|
||||
struct degen_state_hash
|
||||
{
|
||||
size_t
|
||||
operator()(const degen_state& s) const
|
||||
{
|
||||
return s.first->hash() & wang32_hash(s.second);
|
||||
}
|
||||
};
|
||||
|
||||
struct degen_state_equal
|
||||
{
|
||||
bool
|
||||
operator()(const degen_state& left,
|
||||
const degen_state& right) const
|
||||
{
|
||||
if (left.second != right.second)
|
||||
return false;
|
||||
return left.first->compare(right.first) == 0;
|
||||
return wang32_hash(s.first ^ wang32_hash(s.second));
|
||||
}
|
||||
};
|
||||
|
||||
// Associate the degeneralized state to its number.
|
||||
typedef std::unordered_map<degen_state, int,
|
||||
degen_state_hash, degen_state_equal> ds2num_map;
|
||||
typedef std::unordered_map<degen_state, int, degen_state_hash> ds2num_map;
|
||||
|
||||
// Queue of state to be processed.
|
||||
typedef std::deque<degen_state> queue_t;
|
||||
|
|
@ -72,96 +59,64 @@ namespace spot
|
|||
// SCC -- we do not care about the other) of some state.
|
||||
class outgoing_acc
|
||||
{
|
||||
const_tgba_ptr a_;
|
||||
typedef std::pair<acc_cond::mark_t, acc_cond::mark_t> cache_entry;
|
||||
typedef std::unordered_map<const state*, cache_entry,
|
||||
state_ptr_hash, state_ptr_equal> cache_t;
|
||||
cache_t cache_;
|
||||
const scc_map* sm_;
|
||||
const_tgba_digraph_ptr a_;
|
||||
typedef std::tuple<acc_cond::mark_t,
|
||||
acc_cond::mark_t,
|
||||
bool> cache_entry;
|
||||
std::vector<cache_entry> cache_;
|
||||
const scc_info* sm_;
|
||||
|
||||
public:
|
||||
outgoing_acc(const const_tgba_ptr& a, const scc_map* sm): a_(a), sm_(sm)
|
||||
void fill_cache(unsigned s)
|
||||
{
|
||||
}
|
||||
|
||||
cache_t::const_iterator fill_cache(const state* s)
|
||||
{
|
||||
unsigned s1 = sm_ ? sm_->scc_of_state(s) : 0;
|
||||
unsigned s1 = sm_ ? sm_->scc_of(s) : 0;
|
||||
acc_cond::mark_t common = a_->acc().all_sets();
|
||||
acc_cond::mark_t union_ = 0U;
|
||||
for (auto it: a_->succ(s))
|
||||
bool has_acc_self_loop = false;
|
||||
for (auto& t: a_->out(s))
|
||||
{
|
||||
// Ignore transitions that leave the SCC of s.
|
||||
const state* d = it->current_state();
|
||||
unsigned s2 = sm_ ? sm_->scc_of_state(d) : 0;
|
||||
d->destroy();
|
||||
unsigned d = t.dst;
|
||||
unsigned s2 = sm_ ? sm_->scc_of(d) : 0;
|
||||
if (s2 != s1)
|
||||
continue;
|
||||
|
||||
acc_cond::mark_t set = it->current_acceptance_conditions();
|
||||
common &= set;
|
||||
union_ |= set;
|
||||
common &= t.acc;
|
||||
union_ |= t.acc;
|
||||
|
||||
// an accepting self-loop?
|
||||
has_acc_self_loop |= (t.dst == s) && a_->acc().accepting(t.acc);
|
||||
}
|
||||
cache_entry e(common, union_);
|
||||
return cache_.emplace(s, e).first;
|
||||
cache_[s] = std::make_tuple(common, union_, has_acc_self_loop);
|
||||
}
|
||||
|
||||
public:
|
||||
outgoing_acc(const const_tgba_digraph_ptr& a, const scc_info* sm):
|
||||
a_(a), cache_(a->num_states()), sm_(sm)
|
||||
{
|
||||
unsigned n = a->num_states();
|
||||
for (unsigned s = 0; s < n; ++s)
|
||||
fill_cache(s);
|
||||
}
|
||||
|
||||
// Intersection of all outgoing acceptance sets
|
||||
acc_cond::mark_t common_acc(const state* s)
|
||||
acc_cond::mark_t common_acc(unsigned s)
|
||||
{
|
||||
cache_t::const_iterator i = cache_.find(s);
|
||||
if (i == cache_.end())
|
||||
i = fill_cache(s);
|
||||
return i->second.first;
|
||||
assert(s < cache_.size());
|
||||
return std::get<0>(cache_[s]);
|
||||
}
|
||||
|
||||
// Union of all outgoing acceptance sets
|
||||
acc_cond::mark_t union_acc(const state* s)
|
||||
{
|
||||
cache_t::const_iterator i = cache_.find(s);
|
||||
if (i == cache_.end())
|
||||
i = fill_cache(s);
|
||||
return i->second.second;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
// Check whether a state has an accepting self-loop, with a catch.
|
||||
class has_acc_loop
|
||||
{
|
||||
const_tgba_ptr a_;
|
||||
typedef std::unordered_map<const state*, bool,
|
||||
state_ptr_hash, state_ptr_equal> cache_t;
|
||||
cache_t cache_;
|
||||
state_unicity_table& uniq_;
|
||||
|
||||
public:
|
||||
has_acc_loop(const const_tgba_ptr& a, state_unicity_table& uniq):
|
||||
a_(a),
|
||||
uniq_(uniq)
|
||||
acc_cond::mark_t union_acc(unsigned s)
|
||||
{
|
||||
assert(s < cache_.size());
|
||||
return std::get<1>(cache_[s]);
|
||||
}
|
||||
|
||||
bool check(const state* s)
|
||||
// Has an accepting self-loop
|
||||
bool has_acc_selfloop(unsigned s)
|
||||
{
|
||||
auto p = cache_.emplace(s, false);
|
||||
if (p.second)
|
||||
{
|
||||
for (auto it: a_->succ(s))
|
||||
{
|
||||
// Look only for transitions that are accepting.
|
||||
if (!a_->acc().accepting(it->current_acceptance_conditions()))
|
||||
continue;
|
||||
// Look only for self-loops.
|
||||
const state* dest = uniq_(it->current_state());
|
||||
if (dest == s)
|
||||
{
|
||||
p.first->second = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return p.first->second;
|
||||
assert(s < cache_.size());
|
||||
return std::get<2>(cache_[s]);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -196,7 +151,6 @@ namespace spot
|
|||
void
|
||||
print(int scc)
|
||||
{
|
||||
std::vector<bdd>::iterator i;
|
||||
std::cout << "Order_" << scc << ":\t";
|
||||
for (auto i: order_)
|
||||
std::cout << i << ", ";
|
||||
|
|
@ -234,7 +188,7 @@ namespace spot
|
|||
|
||||
template<bool want_sba>
|
||||
tgba_digraph_ptr
|
||||
degeneralize_aux(const const_tgba_ptr& a, bool use_z_lvl,
|
||||
degeneralize_aux(const const_tgba_digraph_ptr& a, bool use_z_lvl,
|
||||
bool use_cust_acc_orders, int use_lvl_cache,
|
||||
bool skip_levels)
|
||||
{
|
||||
|
|
@ -274,14 +228,6 @@ namespace spot
|
|||
// Initialize scc_orders
|
||||
scc_orders orders(a->acc(), skip_levels);
|
||||
|
||||
// Make sure we always use the same pointer for identical states
|
||||
// from the input automaton.
|
||||
state_unicity_table uniq;
|
||||
|
||||
// Accepting loop checker, for some heuristics.
|
||||
has_acc_loop acc_loop(a, uniq);
|
||||
|
||||
// These maps make it possible to convert degen_state to number
|
||||
// and vice-versa.
|
||||
ds2num_map ds2num;
|
||||
|
||||
|
|
@ -293,27 +239,27 @@ namespace spot
|
|||
typedef std::map<int, unsigned> tr_cache_t;
|
||||
tr_cache_t tr_cache;
|
||||
|
||||
// State level cache
|
||||
typedef std::map<const state*, unsigned> lvl_cache_t;
|
||||
lvl_cache_t lvl_cache;
|
||||
// Read this early, because it might create a state if the
|
||||
// automaton is empty.
|
||||
degen_state s(a->get_init_state_number(), 0);
|
||||
|
||||
// State->level cache
|
||||
std::vector<std::pair<unsigned, bool>> lvl_cache(a->num_states());
|
||||
|
||||
// Compute SCCs in order to use any optimization.
|
||||
scc_map m(a);
|
||||
scc_info* m = nullptr;
|
||||
if (use_scc)
|
||||
m.build_map();
|
||||
m = new scc_info(a);
|
||||
|
||||
// Cache for common outgoing acceptances.
|
||||
outgoing_acc outgoing(a, use_scc ? &m : 0);
|
||||
outgoing_acc outgoing(a, m);
|
||||
|
||||
queue_t todo;
|
||||
|
||||
const state* s0 = uniq(a->get_init_state());
|
||||
degen_state s(s0, 0);
|
||||
|
||||
// As a heuristic for building SBA, if the initial state has at
|
||||
// least one accepting self-loop, start the degeneralization on
|
||||
// the accepting level.
|
||||
if (want_sba && acc_loop.check(s0))
|
||||
if (want_sba && outgoing.has_acc_selfloop(s.first))
|
||||
s.second = order.size();
|
||||
// Otherwise, check for acceptance conditions common to all
|
||||
// outgoing transitions, and assume we have already seen these and
|
||||
|
|
@ -322,7 +268,7 @@ namespace spot
|
|||
{
|
||||
auto set = outgoing.common_acc(s.first);
|
||||
if (use_cust_acc_orders)
|
||||
s.second = orders.next_level(m.initial(), s.second, set);
|
||||
s.second = orders.next_level(m->initial(), s.second, set);
|
||||
else
|
||||
while (s.second < order.size()
|
||||
&& set.has(order[s.second]))
|
||||
|
|
@ -345,7 +291,7 @@ namespace spot
|
|||
// If such state exists level from chache is used.
|
||||
// If not, a new level (starting with 0) is computed.
|
||||
if (use_lvl_cache)
|
||||
lvl_cache[s.first] = s.second;
|
||||
lvl_cache[s.first] = std::make_pair(s.second, true);
|
||||
|
||||
while (!todo.empty())
|
||||
{
|
||||
|
|
@ -363,19 +309,19 @@ namespace spot
|
|||
// Check SCC for state s
|
||||
int s_scc = -1;
|
||||
if (use_scc)
|
||||
s_scc = m.scc_of_state(s.first);
|
||||
s_scc = m->scc_of(s.first);
|
||||
|
||||
for (auto i: a->succ(s.first))
|
||||
for (auto& i: a->out(s.first))
|
||||
{
|
||||
degen_state d(uniq(i->current_state()), 0);
|
||||
degen_state d(i.dst, 0);
|
||||
|
||||
// Check whether the target SCC is accepting
|
||||
bool is_scc_acc;
|
||||
int scc;
|
||||
if (use_scc)
|
||||
{
|
||||
scc = m.scc_of_state(d.first);
|
||||
is_scc_acc = m.accepting(scc);
|
||||
scc = m->scc_of(d.first);
|
||||
is_scc_acc = m->is_accepting_scc(scc);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -386,7 +332,7 @@ namespace spot
|
|||
}
|
||||
|
||||
// The old level is slevel. What should be the new one?
|
||||
auto acc = i->current_acceptance_conditions();
|
||||
auto acc = i.acc;
|
||||
auto otheracc = outgoing.common_acc(d.first);
|
||||
|
||||
if (want_sba && is_acc)
|
||||
|
|
@ -459,9 +405,9 @@ namespace spot
|
|||
// If lvl_cache is used and switching SCCs, use level
|
||||
// from cache
|
||||
if (use_lvl_cache && s_scc != scc
|
||||
&& lvl_cache.find(d.first) != lvl_cache.end())
|
||||
&& lvl_cache[d.first].second)
|
||||
{
|
||||
d.second = lvl_cache[d.first];
|
||||
d.second = lvl_cache[d.first].first;
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -490,7 +436,8 @@ namespace spot
|
|||
// state that has at least one accepting
|
||||
// self-loop, start the degeneralization on
|
||||
// the accepting level.
|
||||
if (s_scc != scc && acc_loop.check(d.first))
|
||||
if (s_scc != scc
|
||||
&& outgoing.has_acc_selfloop(d.first))
|
||||
{
|
||||
d.second = order.size();
|
||||
}
|
||||
|
|
@ -558,50 +505,47 @@ namespace spot
|
|||
|
||||
if (use_lvl_cache)
|
||||
{
|
||||
auto res = lvl_cache.emplace(d.first, d.second);
|
||||
|
||||
if (!res.second)
|
||||
auto lvl = d.second;
|
||||
if (lvl_cache[d.first].second)
|
||||
{
|
||||
if (use_lvl_cache == 3)
|
||||
res.first->second =
|
||||
std::max(res.first->second, d.second);
|
||||
lvl = std::max(lvl_cache[d.first].first, lvl);
|
||||
else if (use_lvl_cache == 2)
|
||||
res.first->second =
|
||||
std::min(res.first->second, d.second);
|
||||
lvl = std::min(lvl_cache[d.first].first, lvl);
|
||||
}
|
||||
lvl_cache[d.first] = std::make_pair(lvl, true);
|
||||
}
|
||||
}
|
||||
|
||||
unsigned& t = tr_cache[dest * 2 + is_acc];
|
||||
|
||||
if (t == 0) // Create transition.
|
||||
t = res->new_acc_transition(src, dest,
|
||||
i->current_condition(), is_acc);
|
||||
t = res->new_acc_transition(src, dest, i.cond, is_acc);
|
||||
else // Update existing transition.
|
||||
res->trans_data(t).cond |= i->current_condition();
|
||||
res->trans_data(t).cond |= i.cond;
|
||||
}
|
||||
tr_cache.clear();
|
||||
}
|
||||
|
||||
#ifdef DEGEN_DEBUG
|
||||
std::vector<bdd>::iterator i;
|
||||
std::cout << "Orig. order: \t";
|
||||
for (i = order.begin(); i != order.end(); i++)
|
||||
for (auto i: order)
|
||||
{
|
||||
bdd_print_acc(std::cout, dict, *i);
|
||||
std::cout << ", ";
|
||||
std::cout << i << ", ";
|
||||
}
|
||||
std::cout << std::endl;
|
||||
orders.print(dict);
|
||||
std::cout << '\n';
|
||||
orders.print();
|
||||
#endif
|
||||
|
||||
delete m;
|
||||
|
||||
res->merge_transitions();
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
tgba_digraph_ptr
|
||||
degeneralize(const const_tgba_ptr& a,
|
||||
degeneralize(const const_tgba_digraph_ptr& a,
|
||||
bool use_z_lvl, bool use_cust_acc_orders,
|
||||
int use_lvl_cache, bool skip_levels)
|
||||
{
|
||||
|
|
@ -616,7 +560,7 @@ namespace spot
|
|||
}
|
||||
|
||||
tgba_digraph_ptr
|
||||
degeneralize_tba(const const_tgba_ptr& a,
|
||||
degeneralize_tba(const const_tgba_digraph_ptr& a,
|
||||
bool use_z_lvl, bool use_cust_acc_orders,
|
||||
int use_lvl_cache, bool skip_levels)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -49,13 +49,13 @@ namespace spot
|
|||
/// with transition-based acceptance.
|
||||
/// \@{
|
||||
SPOT_API tgba_digraph_ptr
|
||||
degeneralize(const const_tgba_ptr& a, bool use_z_lvl = true,
|
||||
degeneralize(const const_tgba_digraph_ptr& a, bool use_z_lvl = true,
|
||||
bool use_cust_acc_orders = false,
|
||||
int use_lvl_cache = 1,
|
||||
bool skip_levels = true);
|
||||
|
||||
SPOT_API tgba_digraph_ptr
|
||||
degeneralize_tba(const const_tgba_ptr& a, bool use_z_lvl = true,
|
||||
degeneralize_tba(const const_tgba_digraph_ptr& a, bool use_z_lvl = true,
|
||||
bool use_cust_acc_orders = false,
|
||||
int use_lvl_cache = 1,
|
||||
bool skip_levels = true);
|
||||
|
|
|
|||
|
|
@ -31,7 +31,7 @@ namespace spot
|
|||
public:
|
||||
bool result;
|
||||
|
||||
weak_checker(const scc_map& map)
|
||||
weak_checker(const scc_info& map)
|
||||
: enumerate_cycles(map), result(true)
|
||||
{
|
||||
}
|
||||
|
|
@ -65,10 +65,10 @@ namespace spot
|
|||
}
|
||||
|
||||
bool
|
||||
is_inherently_weak_scc(scc_map& map, unsigned scc)
|
||||
is_inherently_weak_scc(scc_info& map, unsigned scc)
|
||||
{
|
||||
// If no cycle is accepting, the SCC is weak.
|
||||
if (!map.accepting(scc))
|
||||
if (!map.is_accepting_scc(scc))
|
||||
return true;
|
||||
// If the SCC is accepting, but one cycle is not, the SCC is not
|
||||
// weak.
|
||||
|
|
@ -78,57 +78,43 @@ namespace spot
|
|||
}
|
||||
|
||||
bool
|
||||
is_weak_scc(scc_map& map, unsigned scc)
|
||||
is_weak_scc(scc_info& map, unsigned scc)
|
||||
{
|
||||
// If no cycle is accepting, the SCC is weak.
|
||||
if (!map.accepting(scc))
|
||||
if (!map.is_accepting_scc(scc))
|
||||
return true;
|
||||
// If all transitions use the same acceptance set, the SCC is weak.
|
||||
return map.useful_acc_of(scc).size() == 1;
|
||||
return map.used_acc_of(scc).size() == 1;
|
||||
}
|
||||
|
||||
bool
|
||||
is_complete_scc(scc_map& map, unsigned scc)
|
||||
is_complete_scc(scc_info& map, unsigned scc)
|
||||
{
|
||||
auto a = map.get_aut();
|
||||
for (auto s: map.states_of(scc))
|
||||
{
|
||||
tgba_succ_iterator* it = a->succ_iter(s);
|
||||
|
||||
// If a state has no successors, the SCC is not complete.
|
||||
if (!it->first())
|
||||
{
|
||||
a->release_iter(it);
|
||||
return false;
|
||||
}
|
||||
|
||||
// Sum guards on all outgoing transitions.
|
||||
bool has_succ = false;
|
||||
bdd sumall = bddfalse;
|
||||
do
|
||||
for (auto& t: a->out(s))
|
||||
{
|
||||
const state *next = it->current_state();
|
||||
// check it's the same scc
|
||||
if (map.scc_of_state(next) == scc)
|
||||
sumall |= it->current_condition();
|
||||
next->destroy();
|
||||
has_succ = true;
|
||||
if (map.scc_of(t.dst) == scc)
|
||||
sumall |= t.cond;
|
||||
if (sumall == bddtrue)
|
||||
break;
|
||||
}
|
||||
while (it->next());
|
||||
a->release_iter(it);
|
||||
|
||||
if (sumall != bddtrue)
|
||||
if (!has_succ || sumall != bddtrue)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool
|
||||
is_terminal_scc(scc_map& map, unsigned scc)
|
||||
is_terminal_scc(scc_info& map, unsigned scc)
|
||||
{
|
||||
// If all transitions use all acceptance conditions, the SCC is weak.
|
||||
return (map.accepting(scc)
|
||||
&& map.useful_acc_of(scc).size() == 1
|
||||
return (map.is_accepting_scc(scc)
|
||||
&& map.used_acc_of(scc).size() == 1
|
||||
&& is_complete_scc(map, scc));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@
|
|||
#ifndef SPOT_TGBAALGOS_ISWEAKSCC_HH
|
||||
# define SPOT_TGBAALGOS_ISWEAKSCC_HH
|
||||
|
||||
#include "scc.hh"
|
||||
#include "sccinfo.hh"
|
||||
|
||||
namespace spot
|
||||
{
|
||||
|
|
@ -36,15 +36,14 @@ namespace spot
|
|||
/// Note the terminal SCCs are also inherently weak with that
|
||||
/// definition.
|
||||
///
|
||||
/// The scc_map \a map should have been built already. The absence
|
||||
/// of accepting cycle is easy to check (the scc_map can tell
|
||||
/// whether the SCC is non-accepting already). Similarly, an SCC in
|
||||
/// which all transitions belong to all acceptance sets is
|
||||
/// necessarily weak.
|
||||
/// For other accepting SCCs, this function enumerates all cycles in
|
||||
/// the given SCC (it stops if it find a non-accepting cycle).
|
||||
/// The absence of accepting cycle is easy to check (the scc_info
|
||||
/// object can tell whether the SCC is non-accepting already).
|
||||
/// Similarly, an SCC in which all transitions belong to all
|
||||
/// acceptance sets is necessarily weak. For other accepting SCCs,
|
||||
/// this function enumerates all cycles in the given SCC (it stops
|
||||
/// if it find a non-accepting cycle).
|
||||
SPOT_API bool
|
||||
is_inherently_weak_scc(scc_map& map, unsigned scc);
|
||||
is_inherently_weak_scc(scc_info& map, unsigned scc);
|
||||
|
||||
/// \brief Whether the SCC number \a scc in \a map is weak.
|
||||
///
|
||||
|
|
@ -52,27 +51,21 @@ namespace spot
|
|||
/// are fully accepting (i.e., the belong to all acceptance sets).
|
||||
///
|
||||
/// Note that terminal SCCs are also weak with that definition.
|
||||
///
|
||||
/// The scc_map \a map should have been built already.
|
||||
SPOT_API bool
|
||||
is_weak_scc(scc_map& map, unsigned scc);
|
||||
is_weak_scc(scc_info& map, unsigned scc);
|
||||
|
||||
/// \brief Whether the SCC number \a scc in \a map is complete.
|
||||
///
|
||||
/// An SCC is complete iff for all states and all label there exists
|
||||
/// a transition that stays into this SCC.
|
||||
///
|
||||
/// The scc_map \a map should have been built already.
|
||||
SPOT_API bool
|
||||
is_complete_scc(scc_map& map, unsigned scc);
|
||||
is_complete_scc(scc_info& map, unsigned scc);
|
||||
|
||||
/// \brief Whether the SCC number \a scc in \a map is terminal.
|
||||
///
|
||||
/// An SCC is terminal if it is weak, complete, and accepting.
|
||||
///
|
||||
/// The scc_map \a map should have been built already.
|
||||
SPOT_API bool
|
||||
is_terminal_scc(scc_map& map, unsigned scc);
|
||||
is_terminal_scc(scc_info& map, unsigned scc);
|
||||
|
||||
/// @}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@
|
|||
#include "tgbaalgos/gtec/gtec.hh"
|
||||
#include "tgbaalgos/safety.hh"
|
||||
#include "tgbaalgos/sccfilter.hh"
|
||||
#include "tgbaalgos/scc.hh"
|
||||
#include "tgbaalgos/sccinfo.hh"
|
||||
#include "tgbaalgos/ltl2tgba_fm.hh"
|
||||
#include "tgbaalgos/bfssteps.hh"
|
||||
#include "tgbaalgos/isdet.hh"
|
||||
|
|
@ -183,7 +183,7 @@ namespace spot
|
|||
struct wdba_search_acc_loop : public bfs_steps
|
||||
{
|
||||
wdba_search_acc_loop(const const_tgba_ptr& det_a,
|
||||
unsigned scc_n, scc_map& sm,
|
||||
unsigned scc_n, scc_info& sm,
|
||||
power_map& pm, const state* dest)
|
||||
: bfs_steps(det_a), scc_n(scc_n), sm(sm), pm(pm), dest(dest)
|
||||
{
|
||||
|
|
@ -194,7 +194,8 @@ namespace spot
|
|||
filter(const state* s)
|
||||
{
|
||||
s = seen(s);
|
||||
if (sm.scc_of_state(s) != scc_n)
|
||||
if (sm.scc_of(std::static_pointer_cast<const tgba_digraph>(a_)
|
||||
->state_number(s)) != scc_n)
|
||||
return 0;
|
||||
return s;
|
||||
}
|
||||
|
|
@ -206,7 +207,7 @@ namespace spot
|
|||
}
|
||||
|
||||
unsigned scc_n;
|
||||
scc_map& sm;
|
||||
scc_info& sm;
|
||||
power_map& pm;
|
||||
const state* dest;
|
||||
state_unicity_table seen;
|
||||
|
|
@ -215,12 +216,12 @@ namespace spot
|
|||
|
||||
bool
|
||||
wdba_scc_is_accepting(const const_tgba_digraph_ptr& det_a, unsigned scc_n,
|
||||
const const_tgba_ptr& orig_a, scc_map& sm,
|
||||
const const_tgba_ptr& orig_a, scc_info& sm,
|
||||
power_map& pm)
|
||||
{
|
||||
|
||||
// Get some state from the SCC #n.
|
||||
const state* start = sm.one_state_of(scc_n)->clone();
|
||||
const state* start = det_a->state_from_number(sm.one_state_of(scc_n));
|
||||
|
||||
// Find a loop around START in SCC #n.
|
||||
wdba_search_acc_loop wsal(det_a, scc_n, sm, pm, start);
|
||||
|
|
@ -478,7 +479,7 @@ namespace spot
|
|||
}
|
||||
|
||||
|
||||
tgba_digraph_ptr minimize_monitor(const const_tgba_ptr& a)
|
||||
tgba_digraph_ptr minimize_monitor(const const_tgba_digraph_ptr& a)
|
||||
{
|
||||
hash_set* final = new hash_set;
|
||||
hash_set* non_final = new hash_set;
|
||||
|
|
@ -498,7 +499,7 @@ namespace spot
|
|||
return res;
|
||||
}
|
||||
|
||||
tgba_digraph_ptr minimize_wdba(const const_tgba_ptr& a)
|
||||
tgba_digraph_ptr minimize_wdba(const const_tgba_digraph_ptr& a)
|
||||
{
|
||||
hash_set* final = new hash_set;
|
||||
hash_set* non_final = new hash_set;
|
||||
|
|
@ -520,8 +521,7 @@ namespace spot
|
|||
// We also keep track of whether an SCC is useless
|
||||
// (i.e., it is not the start of any accepting word).
|
||||
|
||||
scc_map sm(det_a);
|
||||
sm.build_map();
|
||||
scc_info sm(det_a);
|
||||
unsigned scc_count = sm.scc_count();
|
||||
// SCC that have been marked as useless.
|
||||
std::vector<bool> useless(scc_count);
|
||||
|
|
@ -537,8 +537,8 @@ namespace spot
|
|||
for (unsigned m = 0; m < scc_count; ++m)
|
||||
{
|
||||
bool is_useless = true;
|
||||
bool transient = sm.trivial(m);
|
||||
const scc_map::succ_type& succ = sm.succ(m);
|
||||
bool transient = sm.is_trivial(m);
|
||||
auto& succ = sm.succ(m);
|
||||
|
||||
if (transient && succ.empty())
|
||||
{
|
||||
|
|
@ -552,11 +552,10 @@ namespace spot
|
|||
// Also SCCs are useless if all their successor are
|
||||
// useless.
|
||||
unsigned l = k;
|
||||
for (scc_map::succ_type::const_iterator j = succ.begin();
|
||||
j != succ.end(); ++j)
|
||||
for (auto& j: succ)
|
||||
{
|
||||
is_useless &= useless[j->first];
|
||||
unsigned dj = d[j->first];
|
||||
is_useless &= useless[j.dst];
|
||||
unsigned dj = d[j.dst];
|
||||
if (dj < l)
|
||||
l = dj;
|
||||
}
|
||||
|
|
@ -586,10 +585,8 @@ namespace spot
|
|||
if (!is_useless)
|
||||
{
|
||||
hash_set* dest_set = (d[m] & 1) ? non_final : final;
|
||||
const std::list<const state*>& l = sm.states_of(m);
|
||||
std::list<const state*>::const_iterator il;
|
||||
for (il = l.begin(); il != l.end(); ++il)
|
||||
dest_set->insert((*il)->clone());
|
||||
for (auto s: sm.states_of(m))
|
||||
dest_set->insert(det_a->state_from_number(s));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -57,7 +57,7 @@ namespace spot
|
|||
/// \param a the automaton to convert into a minimal deterministic monitor
|
||||
/// \pre Dead SCCs should have been removed from \a a before
|
||||
/// calling this function.
|
||||
SPOT_API tgba_digraph_ptr minimize_monitor(const const_tgba_ptr& a);
|
||||
SPOT_API tgba_digraph_ptr minimize_monitor(const const_tgba_digraph_ptr& a);
|
||||
|
||||
/// \brief Minimize a Büchi automaton in the WDBA class.
|
||||
///
|
||||
|
|
@ -93,7 +93,7 @@ namespace spot
|
|||
month = oct
|
||||
}
|
||||
\endverbatim */
|
||||
SPOT_API tgba_digraph_ptr minimize_wdba(const const_tgba_ptr& a);
|
||||
SPOT_API tgba_digraph_ptr minimize_wdba(const const_tgba_digraph_ptr& a);
|
||||
|
||||
/// \brief Minimize an automaton if it represents an obligation property.
|
||||
///
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@
|
|||
#include "misc/hash.hh"
|
||||
#include "tgbaalgos/powerset.hh"
|
||||
#include "bdd.h"
|
||||
#include "tgbaalgos/scc.hh"
|
||||
#include "tgbaalgos/sccinfo.hh"
|
||||
#include "tgbaalgos/cycles.hh"
|
||||
#include "tgbaalgos/gtec/gtec.hh"
|
||||
#include "tgba/tgbaproduct.hh"
|
||||
|
|
@ -42,8 +42,9 @@
|
|||
|
||||
namespace spot
|
||||
{
|
||||
// FIXME: Redo this algorithm using the tgba_digraph interface.
|
||||
tgba_digraph_ptr
|
||||
tgba_powerset(const const_tgba_ptr& aut, power_map& pm, bool merge)
|
||||
tgba_powerset(const const_tgba_digraph_ptr& aut, power_map& pm, bool merge)
|
||||
{
|
||||
typedef power_map::power_state power_state;
|
||||
typedef std::map<power_map::power_state, int> power_set;
|
||||
|
|
@ -112,7 +113,7 @@ namespace spot
|
|||
}
|
||||
|
||||
tgba_digraph_ptr
|
||||
tgba_powerset(const const_tgba_ptr& aut)
|
||||
tgba_powerset(const const_tgba_digraph_ptr& aut)
|
||||
{
|
||||
power_map pm;
|
||||
return tgba_powerset(aut, pm);
|
||||
|
|
@ -130,7 +131,7 @@ namespace spot
|
|||
typedef std::set<trans*> trans_set;
|
||||
typedef std::vector<trans_set> set_set;
|
||||
protected:
|
||||
const_tgba_ptr ref_;
|
||||
const_tgba_digraph_ptr ref_;
|
||||
power_map& refmap_;
|
||||
trans_set reject_; // set of rejecting transitions
|
||||
set_set accept_; // set of cycles that are accepting
|
||||
|
|
@ -139,7 +140,7 @@ namespace spot
|
|||
unsigned cycles_left_; // count of cycles left to explore
|
||||
|
||||
public:
|
||||
fix_scc_acceptance(const scc_map& sm, const_tgba_ptr ref,
|
||||
fix_scc_acceptance(const scc_info& sm, const_tgba_digraph_ptr ref,
|
||||
power_map& refmap, unsigned threshold)
|
||||
: enumerate_cycles(sm), ref_(ref), refmap_(refmap),
|
||||
threshold_(threshold)
|
||||
|
|
@ -173,8 +174,7 @@ namespace spot
|
|||
|
||||
bool is_cycle_accepting(cycle_iter begin, trans_set& ts) const
|
||||
{
|
||||
auto a = std::static_pointer_cast<tgba_digraph>
|
||||
(std::const_pointer_cast<tgba>(aut_));
|
||||
auto a = std::const_pointer_cast<tgba_digraph>(aut_);
|
||||
|
||||
// Build an automaton representing this loop.
|
||||
auto loop_a = make_tgba_digraph(aut_->get_dict());
|
||||
|
|
@ -268,20 +268,19 @@ namespace spot
|
|||
|
||||
static bool
|
||||
fix_dba_acceptance(tgba_digraph_ptr det,
|
||||
const_tgba_ptr ref, power_map& refmap,
|
||||
const_tgba_digraph_ptr ref, power_map& refmap,
|
||||
unsigned threshold)
|
||||
{
|
||||
det->copy_acceptance_conditions_of(ref);
|
||||
|
||||
scc_map sm(det);
|
||||
sm.build_map();
|
||||
scc_info sm(det);
|
||||
|
||||
unsigned scc_count = sm.scc_count();
|
||||
|
||||
fix_scc_acceptance fsa(sm, ref, refmap, threshold);
|
||||
|
||||
for (unsigned m = 0; m < scc_count; ++m)
|
||||
if (!sm.trivial(m))
|
||||
if (!sm.is_trivial(m))
|
||||
if (fsa.fix_scc(m))
|
||||
return true;
|
||||
return false;
|
||||
|
|
@ -289,7 +288,7 @@ namespace spot
|
|||
}
|
||||
|
||||
tgba_digraph_ptr
|
||||
tba_determinize(const const_tgba_ptr& aut,
|
||||
tba_determinize(const const_tgba_digraph_ptr& aut,
|
||||
unsigned threshold_states, unsigned threshold_cycles)
|
||||
{
|
||||
power_map pm;
|
||||
|
|
|
|||
|
|
@ -65,9 +65,10 @@ namespace spot
|
|||
/// transitions.
|
||||
//@{
|
||||
SPOT_API tgba_digraph_ptr
|
||||
tgba_powerset(const const_tgba_ptr& aut, power_map& pm, bool merge = true);
|
||||
tgba_powerset(const const_tgba_digraph_ptr& aut,
|
||||
power_map& pm, bool merge = true);
|
||||
SPOT_API tgba_digraph_ptr
|
||||
tgba_powerset(const const_tgba_ptr& aut);
|
||||
tgba_powerset(const const_tgba_digraph_ptr& aut);
|
||||
//@}
|
||||
|
||||
|
||||
|
|
@ -107,7 +108,7 @@ namespace spot
|
|||
/// whenever an SCC of the constructed automaton has more than \a
|
||||
/// threshold_cycles cycles.
|
||||
SPOT_API tgba_digraph_ptr
|
||||
tba_determinize(const const_tgba_ptr& aut,
|
||||
tba_determinize(const const_tgba_digraph_ptr& aut,
|
||||
unsigned threshold_states = 0,
|
||||
unsigned threshold_cycles = 0);
|
||||
|
||||
|
|
|
|||
108
src/tgbaalgos/product.cc
Normal file
108
src/tgbaalgos/product.cc
Normal file
|
|
@ -0,0 +1,108 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2014 Laboratoire de Recherche et
|
||||
// Développement de l'Epita (LRDE).
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
//
|
||||
// Spot is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Spot is distributed in the hope that it will be useful, but WITHOUT
|
||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#include "product.hh"
|
||||
#include "tgba/tgbagraph.hh"
|
||||
#include <deque>
|
||||
#include <unordered_map>
|
||||
#include "misc/hash.hh"
|
||||
|
||||
namespace spot
|
||||
{
|
||||
namespace
|
||||
{
|
||||
typedef std::pair<unsigned, unsigned> product_state;
|
||||
|
||||
struct product_state_hash
|
||||
{
|
||||
size_t
|
||||
operator()(product_state s) const
|
||||
{
|
||||
return wang32_hash(s.first ^ wang32_hash(s.second));
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
tgba_digraph_ptr product(const const_tgba_digraph_ptr& left,
|
||||
const const_tgba_digraph_ptr& right,
|
||||
unsigned left_state,
|
||||
unsigned right_state)
|
||||
{
|
||||
std::unordered_map<product_state, unsigned, product_state_hash> s2n;
|
||||
std::deque<std::pair<product_state, unsigned>> todo;
|
||||
|
||||
assert(left->get_dict() == right->get_dict());
|
||||
auto res = make_tgba_digraph(left->get_dict());
|
||||
res->copy_ap_of(left);
|
||||
res->copy_ap_of(right);
|
||||
res->set_acceptance_conditions(left->acc().num_sets()
|
||||
+ right->acc().num_sets());
|
||||
|
||||
auto v = new product_states;
|
||||
res->set_named_prop("product-states", v, [](void* vv) {
|
||||
delete static_cast<product_states*>(vv);
|
||||
});
|
||||
|
||||
auto new_state =
|
||||
[&](unsigned left_state, unsigned right_state) -> unsigned
|
||||
{
|
||||
product_state x(left_state, right_state);
|
||||
auto p = s2n.emplace(x, 0);
|
||||
if (p.second) // This is a new state
|
||||
{
|
||||
p.first->second = res->new_state();
|
||||
todo.emplace_back(x, p.first->second);
|
||||
assert(p.first->second == v->size());
|
||||
v->push_back(x);
|
||||
}
|
||||
return p.first->second;
|
||||
};
|
||||
|
||||
new_state(left_state, right_state);
|
||||
while (!todo.empty())
|
||||
{
|
||||
auto top = todo.front();
|
||||
todo.pop_front();
|
||||
for (auto& l: left->out(top.first.first))
|
||||
for (auto& r: right->out(top.first.second))
|
||||
{
|
||||
auto cond = l.cond & r.cond;
|
||||
if (cond == bddfalse)
|
||||
continue;
|
||||
auto dst = new_state(l.dst, r.dst);
|
||||
res->new_transition(top.second, dst, cond,
|
||||
res->acc().join(left->acc(), l.acc,
|
||||
right->acc(), r.acc));
|
||||
// If right is deterministic, we can abort immediately!
|
||||
}
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
tgba_digraph_ptr product(const const_tgba_digraph_ptr& left,
|
||||
const const_tgba_digraph_ptr& right)
|
||||
{
|
||||
return product(left, right,
|
||||
left->get_init_state_number(),
|
||||
right->get_init_state_number());
|
||||
}
|
||||
|
||||
}
|
||||
45
src/tgbaalgos/product.hh
Normal file
45
src/tgbaalgos/product.hh
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2014 Laboratoire de Recherche et
|
||||
// Développement de l'Epita (LRDE).
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
//
|
||||
// Spot is free software; you can redistribute it and/or modify it
|
||||
// under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation; either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Spot is distributed in the hope that it will be useful, but WITHOUT
|
||||
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
|
||||
// License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
#ifndef SPOT_TGBAALGOS_PRODUCT_HH
|
||||
# define SPOT_TGBAALGOS_PRODUCT_HH
|
||||
|
||||
#include "misc/common.hh"
|
||||
#include "tgba/fwd.hh"
|
||||
#include <vector>
|
||||
#include <utility>
|
||||
|
||||
namespace spot
|
||||
{
|
||||
// The tgba constructed by product() below contain property named
|
||||
// "product-states" with type product_states.
|
||||
typedef std::vector<std::pair<unsigned, unsigned>> product_states;
|
||||
|
||||
SPOT_API
|
||||
tgba_digraph_ptr product(const const_tgba_digraph_ptr& left,
|
||||
const const_tgba_digraph_ptr& right);
|
||||
|
||||
SPOT_API
|
||||
tgba_digraph_ptr product(const const_tgba_digraph_ptr& left,
|
||||
const const_tgba_digraph_ptr& right,
|
||||
unsigned left_state,
|
||||
unsigned right_state);
|
||||
}
|
||||
|
||||
#endif // SPOT_TGBAALGOS_PRODUCT_HH
|
||||
|
|
@ -24,26 +24,23 @@
|
|||
namespace spot
|
||||
{
|
||||
bool
|
||||
is_guarantee_automaton(const const_tgba_ptr& aut, const scc_map* sm)
|
||||
is_guarantee_automaton(const const_tgba_digraph_ptr& aut,
|
||||
const scc_info* sm)
|
||||
{
|
||||
// Create an scc_map of 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;
|
||||
if (need_sm)
|
||||
{
|
||||
scc_map* x = new scc_map(aut);
|
||||
x->build_map();
|
||||
sm = x;
|
||||
}
|
||||
sm = new scc_info(aut);
|
||||
|
||||
bool result = true;
|
||||
|
||||
unsigned scc_count = sm->scc_count();
|
||||
for (unsigned scc = 0; (scc < scc_count) && result; ++scc)
|
||||
for (unsigned scc = 0; scc < scc_count; ++scc)
|
||||
{
|
||||
if (!sm->accepting(scc))
|
||||
if (!sm->is_accepting_scc(scc))
|
||||
continue;
|
||||
// Accepting SCCs should have only one state.
|
||||
const std::list<const state*>& st = sm->states_of(scc);
|
||||
auto& st = sm->states_of(scc);
|
||||
if (st.size() != 1)
|
||||
{
|
||||
result = false;
|
||||
|
|
@ -51,50 +48,28 @@ namespace spot
|
|||
}
|
||||
// The state should have only one transition that is a
|
||||
// self-loop labelled by true.
|
||||
const state* s = *st.begin();
|
||||
tgba_succ_iterator* it = aut->succ_iter(s);
|
||||
it->first();
|
||||
assert(!it->done());
|
||||
state* dest = it->current_state();
|
||||
bdd cond = it->current_condition();
|
||||
result = (!it->next()) && (cond == bddtrue) && (!dest->compare(s));
|
||||
dest->destroy();
|
||||
aut->release_iter(it);
|
||||
auto src = st.front();
|
||||
auto out = aut->out(src);
|
||||
auto it = out.begin();
|
||||
assert(it != out.end());
|
||||
result =
|
||||
(it->cond == bddtrue) && (it->dst == src) && (++it == out.end());
|
||||
if (!result)
|
||||
break;
|
||||
}
|
||||
|
||||
// Free the scc_map if we created it.
|
||||
if (need_sm)
|
||||
delete sm;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool is_safety_mwdba(const const_tgba_ptr& aut)
|
||||
bool is_safety_mwdba(const const_tgba_digraph_ptr& aut)
|
||||
{
|
||||
state_unicity_table seen; // States already seen.
|
||||
std::deque<const state*> todo; // A queue of states yet to explore.
|
||||
|
||||
todo.push_back(seen(aut->get_init_state()));
|
||||
|
||||
bool all_accepting = true;
|
||||
while (all_accepting && !todo.empty())
|
||||
{
|
||||
const state* s = todo.front();
|
||||
todo.pop_front();
|
||||
|
||||
for (auto it: aut->succ(s))
|
||||
{
|
||||
auto acc = it->current_acceptance_conditions();
|
||||
if (!aut->acc().accepting(acc))
|
||||
{
|
||||
all_accepting = false;
|
||||
break;
|
||||
}
|
||||
if (const state* d = seen.is_new(it->current_state()))
|
||||
todo.push_back(d);
|
||||
}
|
||||
}
|
||||
return all_accepting;
|
||||
for (auto& t: aut->transitions())
|
||||
if (!aut->is_dead_transition(t))
|
||||
if (!aut->acc().accepting(t.acc))
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2010, 2011, 2013 Laboratoire de Recherche et
|
||||
// Copyright (C) 2010, 2011, 2013, 2014 Laboratoire de Recherche et
|
||||
// Développement de l'Epita (LRDE)
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
|
|
@ -20,7 +20,7 @@
|
|||
#ifndef SPOT_TGBAALGOS_SAFETY_HH
|
||||
# define SPOT_TGBAALGOS_SAFETY_HH
|
||||
|
||||
#include "scc.hh"
|
||||
#include "sccinfo.hh"
|
||||
|
||||
namespace spot
|
||||
{
|
||||
|
|
@ -43,11 +43,11 @@ namespace spot
|
|||
///
|
||||
/// \param aut the automaton to check
|
||||
///
|
||||
/// \param sm an scc_map of the automaton if available (it will be
|
||||
/// built otherwise. If you supply an scc_map you should call
|
||||
/// build_map() before passing it to this function.
|
||||
/// \param sm an scc_info object for the automaton if available (it
|
||||
/// will be built otherwise).
|
||||
SPOT_API bool
|
||||
is_guarantee_automaton(const const_tgba_ptr& aut, const scc_map* sm = 0);
|
||||
is_guarantee_automaton(const const_tgba_digraph_ptr& aut,
|
||||
const scc_info* sm = 0);
|
||||
|
||||
/// \brief Whether a minimized WDBA represents a safety property.
|
||||
///
|
||||
|
|
@ -57,7 +57,7 @@ namespace spot
|
|||
///
|
||||
/// \param aut the automaton to check
|
||||
SPOT_API bool
|
||||
is_safety_mwdba(const const_tgba_ptr& aut);
|
||||
is_safety_mwdba(const const_tgba_digraph_ptr& aut);
|
||||
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -121,7 +121,7 @@ namespace spot
|
|||
node_.emplace_back(acc, triv);
|
||||
std::swap(node_.back().succ, root_.front().node.succ);
|
||||
std::swap(node_.back().states, root_.front().node.states);
|
||||
node_.back().accepting = aut->acc().accepting(acc);
|
||||
node_.back().accepting = !triv && aut->acc().accepting(acc);
|
||||
root_.pop_front();
|
||||
// Record the transition between the SCC being popped
|
||||
// and the previous SCC.
|
||||
|
|
@ -229,6 +229,16 @@ namespace spot
|
|||
}
|
||||
|
||||
|
||||
std::set<acc_cond::mark_t> scc_info::used_acc_of(unsigned scc) const
|
||||
{
|
||||
std::set<acc_cond::mark_t> res;
|
||||
for (auto src: states_of(scc))
|
||||
for (auto& t: aut_->out(src))
|
||||
if (scc_of(t.dst) == scc)
|
||||
res.insert(t.acc);
|
||||
return res;
|
||||
}
|
||||
|
||||
std::vector<std::set<acc_cond::mark_t>> scc_info::used_acc() const
|
||||
{
|
||||
unsigned n = aut_->num_states();
|
||||
|
|
|
|||
|
|
@ -45,12 +45,12 @@ namespace spot
|
|||
struct scc_node
|
||||
{
|
||||
scc_node():
|
||||
acc(0U), trivial(true)
|
||||
acc(0U), trivial(true), accepting(false), useful(false)
|
||||
{
|
||||
}
|
||||
|
||||
scc_node(acc_cond::mark_t acc, bool trivial):
|
||||
acc(acc), trivial(trivial)
|
||||
acc(acc), trivial(trivial), accepting(false), useful(false)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -104,6 +104,18 @@ namespace spot
|
|||
return node(scc).states;
|
||||
}
|
||||
|
||||
unsigned one_state_of(unsigned scc) const
|
||||
{
|
||||
return states_of(scc).front();
|
||||
}
|
||||
|
||||
/// \brief Get number of the SCC containing the initial state.
|
||||
unsigned initial() const
|
||||
{
|
||||
assert(scc_count() - 1 == scc_of(aut_->get_init_state_number()));
|
||||
return scc_count() - 1;
|
||||
}
|
||||
|
||||
const scc_succs& succ(unsigned scc) const
|
||||
{
|
||||
return node(scc).succ;
|
||||
|
|
@ -138,6 +150,9 @@ namespace spot
|
|||
/// each accepting SCC.
|
||||
std::vector<std::set<acc_cond::mark_t>> used_acc() const;
|
||||
|
||||
std::set<acc_cond::mark_t> used_acc_of(unsigned scc) const;
|
||||
|
||||
|
||||
std::vector<bool> weak_sccs() const;
|
||||
|
||||
bdd scc_ap_support(unsigned scc) const;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue