Commit 2fb436a1 authored by Alexandre Duret-Lutz's avatar Alexandre Duret-Lutz

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.
parent b6745482
......@@ -46,12 +46,10 @@
#include "ltlvisit/mutation.hh"
#include "ltlvisit/relabel.hh"
#include "tgbaalgos/lbtt.hh"
#include "tgba/tgbaproduct.hh"
#include "tgbaalgos/product.hh"
#include "tgbaalgos/gtec/gtec.hh"
#include "tgbaalgos/randomgraph.hh"
#include "tgbaalgos/sccinfo.hh"
#include "tgbaalgos/scc.hh"
#include "tgbaalgos/dotty.hh"
#include "tgbaalgos/isweakscc.hh"
#include "tgbaalgos/reducerun.hh"
#include "tgbaalgos/word.hh"
......@@ -880,7 +878,7 @@ namespace
string_to_tmp(string_ltl_wring, serial, filename_ltl_wring);
}
spot::const_tgba_ptr
spot::const_tgba_digraph_ptr
translate(unsigned int translator_num, char l, statistics_formula* fstats,
bool& problem)
{
......@@ -900,7 +898,7 @@ namespace
const char* status_str = 0;
spot::const_tgba_ptr res = 0;
spot::const_tgba_digraph_ptr res = 0;
if (timed_out)
{
// This is not considered to be a global error.
......@@ -1059,15 +1057,14 @@ namespace
st->edges = s.transitions;
st->transitions = s.sub_transitions;
st->acc = res->acc().num_sets();
spot::scc_map m(res);
m.build_map();
spot::scc_info m(res);
unsigned c = m.scc_count();
st->scc = c;
st->nondetstates = spot::count_nondet_states(res);
st->nondeterministic = st->nondetstates != 0;
for (unsigned n = 0; n < c; ++n)
{
if (!m.accepting(n))
if (!m.is_accepting_scc(n))
++st->nonacc_scc;
else if (is_terminal_scc(m, n))
++st->terminal_scc;
......@@ -1090,8 +1087,8 @@ namespace
};
static bool
check_empty_prod(const spot::const_tgba_ptr& aut_i,
const spot::const_tgba_ptr& aut_j,
check_empty_prod(const spot::const_tgba_digraph_ptr& aut_i,
const spot::const_tgba_digraph_ptr& aut_j,
size_t i, size_t j, bool icomp, bool jcomp)
{
auto prod = spot::product(aut_i, aut_j);
......@@ -1145,7 +1142,7 @@ namespace
}
static bool
cross_check(const std::vector<spot::scc_map*>& maps, char l, unsigned p)
cross_check(const std::vector<spot::scc_info*>& maps, char l, unsigned p)
{
size_t m = maps.size();
if (verbose)
......@@ -1167,13 +1164,17 @@ namespace
unsigned verified = 0;
unsigned violated = 0;
for (size_t i = 0; i < m; ++i)
if (spot::scc_map* m = maps[i])
if (spot::scc_info* m = maps[i])
{
// r == true iff the automaton i is accepting.
bool r = false;
unsigned c = m->scc_count();
for (unsigned j = 0; (j < c) && !r; ++j)
r |= m->accepting(j);
for (unsigned j = 0; j < c; ++j)
if (m->is_accepting_scc(j))
{
r = true;
break;
}
res[i] = r;
if (r)
++verified;
......@@ -1216,41 +1217,34 @@ namespace
return false;
}
typedef std::set<spot::state*, spot::state_ptr_less_than> state_set;
typedef std::set<unsigned> state_set;
// Collect all the states of SSPACE that appear in the accepting SCCs
// of PROD.
// of PROD. (Trivial SCCs are considered accepting.)
static void
states_in_acc(const spot::scc_map* m,
const spot::const_tgba_ptr& sspace,
states_in_acc(const spot::scc_info* m,
state_set& s)
{
auto aut = m->get_aut();
auto ps = static_cast<const spot::product_states*>
(aut->get_named_prop("product-states"));
unsigned c = m->scc_count();
for (unsigned n = 0; n < c; ++n)
if (m->accepting(n))
if (m->is_accepting_scc(n) || m->is_trivial(n))
for (auto i: m->states_of(n))
{
spot::state* x = aut->project_state(i, sspace);
assert(x);
if (!s.insert(x).second)
x->destroy();
}
// Get the projection on sspace.
s.insert((*ps)[i].second);
}
static bool
consistency_check(const spot::scc_map* pos, const spot::scc_map* neg,
const spot::const_tgba_ptr& sspace)
consistency_check(const spot::scc_info* pos, const spot::scc_info* neg)
{
// the states of SSPACE should appear in the accepting SCC of at
// least one of POS or NEG. Maybe both.
state_set s;
states_in_acc(pos, sspace, s);
states_in_acc(neg, sspace, s);
bool res = s.size() == states;
for (auto i: s)
i->destroy();
return res;
states_in_acc(pos, s);
states_in_acc(neg, s);
return s.size() == states;
}
typedef
......@@ -1423,12 +1417,12 @@ namespace
// These store the result of the translation of the positive and
// negative formulas.
size_t m = translators.size();
std::vector<spot::const_tgba_ptr> pos(m);
std::vector<spot::const_tgba_ptr> neg(m);
std::vector<spot::const_tgba_digraph_ptr> pos(m);
std::vector<spot::const_tgba_digraph_ptr> neg(m);
// These store the complement of the above results, when we can
// compute it easily.
std::vector<spot::const_tgba_ptr> comp_pos(m);
std::vector<spot::const_tgba_ptr> comp_neg(m);
std::vector<spot::const_tgba_digraph_ptr> comp_pos(m);
std::vector<spot::const_tgba_digraph_ptr> comp_neg(m);
unsigned n = vstats.size();
......@@ -1577,19 +1571,18 @@ namespace
auto statespace = spot::random_graph(states, density, ap, dict);
// Products of the state space with the positive automata.
std::vector<spot::const_tgba_ptr> pos_prod(m);
std::vector<spot::const_tgba_digraph_ptr> pos_prod(m);
// Products of the state space with the negative automata.
std::vector<spot::const_tgba_ptr> neg_prod(m);
std::vector<spot::const_tgba_digraph_ptr> neg_prod(m);
// Associated SCC maps.
std::vector<spot::scc_map*> pos_map(m);
std::vector<spot::scc_map*> neg_map(m);
std::vector<spot::scc_info*> pos_map(m);
std::vector<spot::scc_info*> neg_map(m);
for (size_t i = 0; i < m; ++i)
if (pos[i])
{
auto p = spot::product(pos[i], statespace);
pos_prod[i] = p;
spot::scc_map* sm = new spot::scc_map(p);
sm->build_map();
auto sm = new spot::scc_info(p);
pos_map[i] = sm;
// Statistics
......@@ -1608,8 +1601,7 @@ namespace
{
auto p = spot::product(neg[i], statespace);
neg_prod[i] = p;
spot::scc_map* sm = new spot::scc_map(p);
sm->build_map();
auto sm = new spot::scc_info(p);
neg_map[i] = sm;
// Statistics
......@@ -1636,8 +1628,7 @@ namespace
std::cerr << "info: consistency_check (P" << i
<< ",N" << i << "), state-space #"
<< p << '/' << products << '\n';
if (!(consistency_check(pos_map[i], neg_map[i],
statespace)))
if (!(consistency_check(pos_map[i], neg_map[i])))
{
++problems;
......
......@@ -22,6 +22,7 @@
#include "tgba.hh"
#include "tgbaalgos/gtec/gtec.hh"
#include <utility>
namespace spot
{
......@@ -38,6 +39,10 @@ namespace spot
if (last_support_conditions_input_)
last_support_conditions_input_->destroy();
delete iter_cache_;
// Destroy all named properties.
for (auto& np: named_prop_)
np.second.second(np.second.first);
}
bdd
......@@ -78,4 +83,30 @@ namespace spot
return !couvreur99(shared_from_this())->check();
}
void
tgba::set_named_prop(std::string s,
void* val, std::function<void(void*)> destructor)
{
auto p = named_prop_.emplace(std::piecewise_construct,
std::forward_as_tuple(s),
std::forward_as_tuple(val, destructor));
if (!p.second)
{
p.first->second.second(p.first->second.first);
p.first->second = std::make_pair(val, destructor);
}
}
void*
tgba::get_named_prop(std::string s) const
{
auto i = named_prop_.find(s);
if (i == named_prop_.end())
return nullptr;
return i->second.first;
}
}
......@@ -27,6 +27,8 @@
#include "acc.hh"
#include <cassert>
#include <memory>
#include <unordered_map>
#include <functional>
#include "misc/casts.hh"
#include "misc/hash.hh"
......@@ -657,8 +659,20 @@ namespace spot
bprop is;
};
#ifndef SWIG
// Dynamic properties, are given with a name and a destructor function.
std::unordered_map<std::string,
std::pair<void*,
std::function<void(void*)>>> named_prop_;
#endif
public:
#ifndef SWIG
void set_named_prop(std::string s,
void* val, std::function<void(void*)> destructor);
void* get_named_prop(std::string s) const;
#endif
bool has_single_acc_set() const
{
return is.single_acc_set;
......
......@@ -570,7 +570,7 @@ namespace spot
{
public:
static safra_tree_automaton*
create_safra_automaton(const const_tgba_ptr& a);
create_safra_automaton(const const_tgba_digraph_ptr& a);
private:
typedef std::set<int> atomic_list_t;
typedef std::set<bdd, bdd_less_than> conjunction_list_t;
......@@ -585,7 +585,8 @@ namespace spot
/// \brief The body of Safra's construction.
safra_tree_automaton*
safra_determinisation::create_safra_automaton(const const_tgba_ptr& a)
safra_determinisation::create_safra_automaton
(const const_tgba_digraph_ptr& a)
{
// initialization.
auto sba_aut = degeneralize(a);
......@@ -1074,7 +1075,7 @@ namespace spot
// tgba_safra_complement
//////////////////////////
tgba_safra_complement::tgba_safra_complement(const const_tgba_ptr& a)
tgba_safra_complement::tgba_safra_complement(const const_tgba_digraph_ptr& a)
: tgba(a->get_dict()), automaton_(a),
safra_(safra_determinisation::create_safra_automaton(a))
{
......
......@@ -50,7 +50,7 @@ namespace spot
class SPOT_API tgba_safra_complement : public tgba
{
public:
tgba_safra_complement(const const_tgba_ptr& a);
tgba_safra_complement(const const_tgba_digraph_ptr& a);
virtual ~tgba_safra_complement();
// tgba interface.
......@@ -67,7 +67,7 @@ namespace spot
protected:
virtual bdd compute_support_conditions(const state* state) const;
private:
const_tgba_ptr automaton_;
const_tgba_digraph_ptr automaton_;
void* safra_;
#if TRANSFORM_TO_TBA
acc_cond::mark_t the_acceptance_cond_;
......@@ -82,7 +82,7 @@ namespace spot
typedef std::shared_ptr<const tgba_safra_complement>
const_tgba_safra_complement_ptr;
inline tgba_safra_complement_ptr
make_safra_complement(const const_tgba_ptr& a)
make_safra_complement(const const_tgba_digraph_ptr& a)
{
return std::make_shared<tgba_safra_complement>(a);
}
......
......@@ -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_;
public:
outgoing_acc(const const_tgba_ptr& a, const scc_map* sm): a_(a), sm_(sm)
{
}
cache_t::const_iterator fill_cache(const state* s)
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_;
void fill_cache(unsigned 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);
}
// Intersection of all outgoing acceptance sets
acc_cond::mark_t common_acc(const state* s)
public:
outgoing_acc(const const_tgba_digraph_ptr& a, const scc_info* sm):
a_(a), cache_(a->num_states()), sm_(sm)
{
cache_t::const_iterator i = cache_.find(s);
if (i == cache_.end())
i = fill_cache(s);
return i->second.first;
unsigned n = a->num_states();
for (unsigned s = 0; s < n; ++s)
fill_cache(s);
}
// Union of all outgoing acceptance sets
acc_cond::mark_t union_acc(const state* s)
// Intersection of all outgoing acceptance sets
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.second;
assert(s < cache_.size());
return std::get<0>(cache_[s]);
}
};
// 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)
// Union of all outgoing acceptance sets
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)
{