sanity: Replace tabulars by spaces in *.cc *.hh *.hxx
* bin/autfilt.cc, bin/common_aoutput.cc, bin/common_aoutput.hh, bin/common_finput.cc, bin/common_finput.hh, bin/common_hoaread.cc, bin/common_output.cc, bin/common_output.hh, bin/common_post.cc, bin/common_post.hh, bin/common_r.hh, bin/common_range.cc, bin/common_range.hh, bin/common_setup.cc, bin/common_trans.cc, bin/common_trans.hh, bin/dstar2tgba.cc, bin/genltl.cc, bin/ltl2tgba.cc, bin/ltl2tgta.cc, bin/ltlcross.cc, bin/ltldo.cc, bin/ltlfilt.cc, bin/ltlgrind.cc, bin/randaut.cc, bin/randltl.cc, bin/spot-x.cc, spot/graph/graph.hh, spot/graph/ngraph.hh, spot/kripke/kripkegraph.hh, spot/ltsmin/ltsmin.cc, spot/ltsmin/ltsmin.hh, spot/misc/bareword.cc, spot/misc/bitvect.cc, spot/misc/bitvect.hh, spot/misc/common.hh, spot/misc/escape.cc, spot/misc/fixpool.hh, spot/misc/formater.cc, spot/misc/hash.hh, spot/misc/intvcmp2.cc, spot/misc/intvcmp2.hh, spot/misc/intvcomp.cc, spot/misc/intvcomp.hh, spot/misc/location.hh, spot/misc/minato.cc, spot/misc/minato.hh, spot/misc/mspool.hh, spot/misc/optionmap.cc, spot/misc/optionmap.hh, spot/misc/random.cc, spot/misc/random.hh, spot/misc/satsolver.cc, spot/misc/satsolver.hh, spot/misc/timer.cc, spot/misc/timer.hh, spot/misc/tmpfile.cc, spot/misc/trival.hh, spot/parseaut/fmterror.cc, spot/parseaut/parsedecl.hh, spot/parseaut/public.hh, spot/parsetl/fmterror.cc, spot/parsetl/parsedecl.hh, spot/priv/accmap.hh, spot/priv/bddalloc.cc, spot/priv/freelist.cc, spot/priv/trim.cc, spot/priv/weight.cc, spot/priv/weight.hh, spot/ta/taexplicit.cc, spot/ta/taexplicit.hh, spot/ta/taproduct.cc, spot/ta/taproduct.hh, spot/ta/tgtaexplicit.cc, spot/ta/tgtaexplicit.hh, spot/ta/tgtaproduct.cc, spot/ta/tgtaproduct.hh, spot/taalgos/dot.cc, spot/taalgos/dot.hh, spot/taalgos/emptinessta.cc, spot/taalgos/emptinessta.hh, spot/taalgos/minimize.cc, spot/taalgos/tgba2ta.cc, spot/taalgos/tgba2ta.hh, spot/tl/apcollect.cc, spot/tl/contain.cc, spot/tl/contain.hh, spot/tl/dot.cc, spot/tl/exclusive.cc, spot/tl/exclusive.hh, spot/tl/formula.cc, spot/tl/formula.hh, spot/tl/length.cc, spot/tl/mark.cc, spot/tl/mutation.cc, spot/tl/mutation.hh, spot/tl/parse.hh, spot/tl/print.cc, spot/tl/print.hh, spot/tl/randomltl.cc, spot/tl/randomltl.hh, spot/tl/relabel.cc, spot/tl/relabel.hh, spot/tl/remove_x.cc, spot/tl/simplify.cc, spot/tl/simplify.hh, spot/tl/snf.cc, spot/tl/snf.hh, spot/tl/unabbrev.cc, spot/tl/unabbrev.hh, spot/twa/acc.cc, spot/twa/acc.hh, spot/twa/bdddict.cc, spot/twa/bdddict.hh, spot/twa/bddprint.cc, spot/twa/formula2bdd.cc, spot/twa/formula2bdd.hh, spot/twa/taatgba.cc, spot/twa/taatgba.hh, spot/twa/twa.cc, spot/twa/twa.hh, spot/twa/twagraph.cc, spot/twa/twagraph.hh, spot/twa/twaproduct.cc, spot/twa/twaproduct.hh, spot/twaalgos/are_isomorphic.cc, spot/twaalgos/are_isomorphic.hh, spot/twaalgos/bfssteps.cc, spot/twaalgos/bfssteps.hh, spot/twaalgos/cleanacc.cc, spot/twaalgos/complete.cc, spot/twaalgos/compsusp.cc, spot/twaalgos/compsusp.hh, spot/twaalgos/copy.cc, spot/twaalgos/cycles.cc, spot/twaalgos/cycles.hh, spot/twaalgos/degen.cc, spot/twaalgos/degen.hh, spot/twaalgos/determinize.cc, spot/twaalgos/determinize.hh, spot/twaalgos/dot.cc, spot/twaalgos/dot.hh, spot/twaalgos/dtbasat.cc, spot/twaalgos/dtbasat.hh, spot/twaalgos/dtwasat.cc, spot/twaalgos/dtwasat.hh, spot/twaalgos/emptiness.cc, spot/twaalgos/emptiness.hh, spot/twaalgos/emptiness_stats.hh, spot/twaalgos/gtec/ce.cc, spot/twaalgos/gtec/ce.hh, spot/twaalgos/gtec/gtec.cc, spot/twaalgos/gtec/gtec.hh, spot/twaalgos/gtec/sccstack.cc, spot/twaalgos/gtec/status.cc, spot/twaalgos/gv04.cc, spot/twaalgos/hoa.cc, spot/twaalgos/hoa.hh, spot/twaalgos/isdet.cc, spot/twaalgos/isunamb.cc, spot/twaalgos/isweakscc.cc, spot/twaalgos/lbtt.cc, spot/twaalgos/lbtt.hh, spot/twaalgos/ltl2taa.cc, spot/twaalgos/ltl2taa.hh, spot/twaalgos/ltl2tgba_fm.cc, spot/twaalgos/ltl2tgba_fm.hh, spot/twaalgos/magic.cc, spot/twaalgos/magic.hh, spot/twaalgos/mask.cc, spot/twaalgos/mask.hh, spot/twaalgos/minimize.cc, spot/twaalgos/minimize.hh, spot/twaalgos/ndfs_result.hxx, spot/twaalgos/neverclaim.cc, spot/twaalgos/neverclaim.hh, spot/twaalgos/postproc.cc, spot/twaalgos/postproc.hh, spot/twaalgos/powerset.cc, spot/twaalgos/powerset.hh, spot/twaalgos/product.cc, spot/twaalgos/product.hh, spot/twaalgos/projrun.cc, spot/twaalgos/projrun.hh, spot/twaalgos/randomgraph.cc, spot/twaalgos/randomgraph.hh, spot/twaalgos/randomize.cc, spot/twaalgos/randomize.hh, spot/twaalgos/reachiter.cc, spot/twaalgos/reachiter.hh, spot/twaalgos/relabel.cc, spot/twaalgos/relabel.hh, spot/twaalgos/remfin.cc, spot/twaalgos/remprop.cc, spot/twaalgos/sbacc.cc, spot/twaalgos/sccfilter.cc, spot/twaalgos/sccfilter.hh, spot/twaalgos/sccinfo.cc, spot/twaalgos/sccinfo.hh, spot/twaalgos/se05.cc, spot/twaalgos/se05.hh, spot/twaalgos/sepsets.cc, spot/twaalgos/simulation.cc, spot/twaalgos/simulation.hh, spot/twaalgos/stats.cc, spot/twaalgos/stats.hh, spot/twaalgos/strength.cc, spot/twaalgos/strength.hh, spot/twaalgos/stripacc.cc, spot/twaalgos/stutter.cc, spot/twaalgos/stutter.hh, spot/twaalgos/tau03.cc, spot/twaalgos/tau03opt.cc, spot/twaalgos/tau03opt.hh, spot/twaalgos/totgba.cc, spot/twaalgos/translate.cc, spot/twaalgos/word.cc, tests/core/acc.cc, tests/core/bitvect.cc, tests/core/checkpsl.cc, tests/core/checkta.cc, tests/core/consterm.cc, tests/core/emptchk.cc, tests/core/equalsf.cc, tests/core/graph.cc, tests/core/ikwiad.cc, tests/core/intvcmp2.cc, tests/core/intvcomp.cc, tests/core/kind.cc, tests/core/kripkecat.cc, tests/core/ltlrel.cc, tests/core/ngraph.cc, tests/core/randtgba.cc, tests/core/readltl.cc, tests/core/reduc.cc, tests/core/safra.cc, tests/core/syntimpl.cc, tests/ltsmin/modelcheck.cc: Replace tabulars by 8 spaces. * tests/sanity/style.test: Add checks for no tabulars in *.cc *.hh *.hxx
This commit is contained in:
parent
1eee12b8b4
commit
f7e7b4f14e
239 changed files with 25359 additions and 25355 deletions
|
|
@ -116,13 +116,13 @@ namespace spot
|
|||
trival prop_det = ref_->prop_deterministic();
|
||||
if (prop_det)
|
||||
{
|
||||
ref_deterministic_ = true;
|
||||
ref_deterministic_ = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Count the number of state even if we know that the
|
||||
// automaton is non-deterministic, as this can be used to
|
||||
// decide if two automata are non-isomorphic.
|
||||
// Count the number of state even if we know that the
|
||||
// automaton is non-deterministic, as this can be used to
|
||||
// decide if two automata are non-isomorphic.
|
||||
nondet_states_ = spot::count_nondet_states(ref_);
|
||||
ref_deterministic_ = (nondet_states_ == 0);
|
||||
}
|
||||
|
|
@ -136,12 +136,12 @@ namespace spot
|
|||
if (ref_deterministic_)
|
||||
{
|
||||
if (autdet || (!autdet && spot::is_deterministic(aut)))
|
||||
return are_isomorphic_det(ref_, aut);
|
||||
return are_isomorphic_det(ref_, aut);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (autdet || nondet_states_ != spot::count_nondet_states(aut))
|
||||
return false;
|
||||
return false;
|
||||
}
|
||||
|
||||
auto tmp = make_twa_graph(aut, twa::prop_set::all());
|
||||
|
|
@ -159,7 +159,7 @@ namespace spot
|
|||
|
||||
bool
|
||||
isomorphism_checker::are_isomorphic(const const_twa_graph_ptr ref,
|
||||
const const_twa_graph_ptr aut)
|
||||
const const_twa_graph_ptr aut)
|
||||
{
|
||||
if (trivially_different(ref, aut))
|
||||
return false;
|
||||
|
|
|
|||
|
|
@ -49,7 +49,7 @@ namespace spot
|
|||
/// \ingroup twa_misc
|
||||
/// \brief Check whether two automata are isomorphic.
|
||||
static bool are_isomorphic(const const_twa_graph_ptr ref,
|
||||
const const_twa_graph_ptr aut);
|
||||
const const_twa_graph_ptr aut);
|
||||
|
||||
private:
|
||||
bool is_isomorphic_(const const_twa_graph_ptr aut);
|
||||
|
|
|
|||
|
|
@ -73,34 +73,34 @@ namespace spot
|
|||
|
||||
while (!todo.empty())
|
||||
{
|
||||
const state* src = todo.front();
|
||||
todo.pop_front();
|
||||
for (auto i: a_->succ(src))
|
||||
{
|
||||
const state* dest = filter(i->dst());
|
||||
const state* src = todo.front();
|
||||
todo.pop_front();
|
||||
for (auto i: a_->succ(src))
|
||||
{
|
||||
const state* dest = filter(i->dst());
|
||||
|
||||
if (!dest)
|
||||
continue;
|
||||
if (!dest)
|
||||
continue;
|
||||
|
||||
bdd cond = i->cond();
|
||||
acc_cond::mark_t acc = i->acc();
|
||||
twa_run::step s = { src, cond, acc };
|
||||
bdd cond = i->cond();
|
||||
acc_cond::mark_t acc = i->acc();
|
||||
twa_run::step s = { src, cond, acc };
|
||||
|
||||
if (match(s, dest))
|
||||
{
|
||||
// Found it!
|
||||
if (match(s, dest))
|
||||
{
|
||||
// Found it!
|
||||
finalize(father, s, start, l);
|
||||
return dest;
|
||||
}
|
||||
return dest;
|
||||
}
|
||||
|
||||
// Common case: record backlinks and continue BFS
|
||||
// for unvisited states.
|
||||
if (father.find(dest) == father.end())
|
||||
{
|
||||
todo.push_back(dest);
|
||||
father[dest] = s;
|
||||
}
|
||||
}
|
||||
// Common case: record backlinks and continue BFS
|
||||
// for unvisited states.
|
||||
if (father.find(dest) == father.end())
|
||||
{
|
||||
todo.push_back(dest);
|
||||
father[dest] = s;
|
||||
}
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -91,12 +91,12 @@ namespace spot
|
|||
/// unless you do not want \a l to be updated (in which case an empty
|
||||
/// finalize() will do).
|
||||
virtual void finalize(const std::map<const state*, twa_run::step,
|
||||
state_ptr_less_than>& father,
|
||||
const twa_run::step& s,
|
||||
const state* start,
|
||||
twa_run::steps& l);
|
||||
state_ptr_less_than>& father,
|
||||
const twa_run::step& s,
|
||||
const state* start,
|
||||
twa_run::steps& l);
|
||||
|
||||
protected:
|
||||
const_twa_ptr a_; ///< The spot::tgba we are searching into.
|
||||
const_twa_ptr a_; ///< The spot::tgba we are searching into.
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ namespace spot
|
|||
twa_graph_ptr cleanup_acceptance(const_twa_graph_ptr aut)
|
||||
{
|
||||
return cleanup_acceptance_here(make_twa_graph(aut,
|
||||
twa::prop_set::all()));
|
||||
twa::prop_set::all()));
|
||||
}
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -37,50 +37,50 @@ namespace spot
|
|||
auto um = aut->acc().unsat_mark();
|
||||
if (!um.first)
|
||||
{
|
||||
// We cannot safely complete an automaton if its
|
||||
// acceptance is always satisfiable.
|
||||
auto acc = aut->set_buchi();
|
||||
for (auto& t: aut->edge_vector())
|
||||
t.acc = acc;
|
||||
// We cannot safely complete an automaton if its
|
||||
// acceptance is always satisfiable.
|
||||
auto acc = aut->set_buchi();
|
||||
for (auto& t: aut->edge_vector())
|
||||
t.acc = acc;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Loop over the states and search a state that has only self
|
||||
// loop labeled by the same non-accepting mark. This will be
|
||||
// our sink state. Note that we do not even have to ensure
|
||||
// that the state is complete as we will complete the whole
|
||||
// automaton in a second pass.
|
||||
for (unsigned i = 0; i < n; ++i)
|
||||
{
|
||||
bool sinkable = true;
|
||||
bool first = true;
|
||||
acc_cond::mark_t commonacc = 0U;
|
||||
for (auto& t: aut->out(i))
|
||||
{
|
||||
if (t.dst != i) // Not a self-loop
|
||||
{
|
||||
sinkable = false;
|
||||
break;
|
||||
}
|
||||
if (first)
|
||||
{
|
||||
commonacc = t.acc;
|
||||
first = false;
|
||||
}
|
||||
else if (t.acc != commonacc)
|
||||
{
|
||||
sinkable = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (sinkable && !aut->acc().accepting(commonacc))
|
||||
{
|
||||
// We have found a sink!
|
||||
um.second = commonacc;
|
||||
sink = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
// Loop over the states and search a state that has only self
|
||||
// loop labeled by the same non-accepting mark. This will be
|
||||
// our sink state. Note that we do not even have to ensure
|
||||
// that the state is complete as we will complete the whole
|
||||
// automaton in a second pass.
|
||||
for (unsigned i = 0; i < n; ++i)
|
||||
{
|
||||
bool sinkable = true;
|
||||
bool first = true;
|
||||
acc_cond::mark_t commonacc = 0U;
|
||||
for (auto& t: aut->out(i))
|
||||
{
|
||||
if (t.dst != i) // Not a self-loop
|
||||
{
|
||||
sinkable = false;
|
||||
break;
|
||||
}
|
||||
if (first)
|
||||
{
|
||||
commonacc = t.acc;
|
||||
first = false;
|
||||
}
|
||||
else if (t.acc != commonacc)
|
||||
{
|
||||
sinkable = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (sinkable && !aut->acc().accepting(commonacc))
|
||||
{
|
||||
// We have found a sink!
|
||||
um.second = commonacc;
|
||||
sink = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsigned t = aut->num_edges();
|
||||
|
|
@ -92,42 +92,42 @@ namespace spot
|
|||
// Now complete all states (excluding any newly added the sink).
|
||||
for (unsigned i = 0; i < n; ++i)
|
||||
{
|
||||
bdd missingcond = bddtrue;
|
||||
acc_cond::mark_t acc = 0U;
|
||||
for (auto& t: aut->out(i))
|
||||
{
|
||||
missingcond -= t.cond;
|
||||
// FIXME: This is ugly.
|
||||
//
|
||||
// In case the automaton uses state-based acceptance, we
|
||||
// need to put the new edge in the same set as all
|
||||
// the other.
|
||||
//
|
||||
// In case the automaton uses edge-based acceptance,
|
||||
// it does not matter what acceptance set we put the new
|
||||
// edge into.
|
||||
//
|
||||
// So in both cases, we put the edge in the same
|
||||
// acceptance sets as the last outgoing edge of the
|
||||
// state.
|
||||
acc = t.acc;
|
||||
}
|
||||
// If the state has incomplete successors, we need to add a
|
||||
// edge to some sink state.
|
||||
if (missingcond != bddfalse)
|
||||
{
|
||||
// If we haven't found any sink, simply add one.
|
||||
if (sink == -1U)
|
||||
{
|
||||
sink = aut->new_state();
|
||||
aut->new_edge(sink, sink, bddtrue, um.second);
|
||||
}
|
||||
// In case the automaton use state-based acceptance, propagate
|
||||
// the acceptance of the first edge to the one we add.
|
||||
if (aut->prop_state_acc() != true)
|
||||
acc = 0U;
|
||||
aut->new_edge(i, sink, missingcond, acc);
|
||||
}
|
||||
bdd missingcond = bddtrue;
|
||||
acc_cond::mark_t acc = 0U;
|
||||
for (auto& t: aut->out(i))
|
||||
{
|
||||
missingcond -= t.cond;
|
||||
// FIXME: This is ugly.
|
||||
//
|
||||
// In case the automaton uses state-based acceptance, we
|
||||
// need to put the new edge in the same set as all
|
||||
// the other.
|
||||
//
|
||||
// In case the automaton uses edge-based acceptance,
|
||||
// it does not matter what acceptance set we put the new
|
||||
// edge into.
|
||||
//
|
||||
// So in both cases, we put the edge in the same
|
||||
// acceptance sets as the last outgoing edge of the
|
||||
// state.
|
||||
acc = t.acc;
|
||||
}
|
||||
// If the state has incomplete successors, we need to add a
|
||||
// edge to some sink state.
|
||||
if (missingcond != bddfalse)
|
||||
{
|
||||
// If we haven't found any sink, simply add one.
|
||||
if (sink == -1U)
|
||||
{
|
||||
sink = aut->new_state();
|
||||
aut->new_edge(sink, sink, bddtrue, um.second);
|
||||
}
|
||||
// In case the automaton use state-based acceptance, propagate
|
||||
// the acceptance of the first edge to the one we add.
|
||||
if (aut->prop_state_acc() != true)
|
||||
acc = 0U;
|
||||
aut->new_edge(i, sink, missingcond, acc);
|
||||
}
|
||||
}
|
||||
|
||||
// Get rid of any named property if the automaton changed.
|
||||
|
|
@ -142,11 +142,11 @@ namespace spot
|
|||
twa_graph_ptr complete(const const_twa_ptr& aut)
|
||||
{
|
||||
auto res = make_twa_graph(aut, {
|
||||
true, // state based
|
||||
true, // inherently_weak
|
||||
true, // deterministic
|
||||
true, // stutter inv.
|
||||
});
|
||||
true, // state based
|
||||
true, // inherently_weak
|
||||
true, // deterministic
|
||||
true, // stutter inv.
|
||||
});
|
||||
complete_here(res);
|
||||
return res;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -46,104 +46,104 @@ namespace spot
|
|||
public:
|
||||
typedef std::map<formula, formula> fmap_t;
|
||||
ltl_suspender_visitor(fmap_t& g2s, fmap_t& a2o, bool oblig)
|
||||
: g2s_(g2s), a2o_(a2o), oblig_(oblig)
|
||||
: g2s_(g2s), a2o_(a2o), oblig_(oblig)
|
||||
{
|
||||
}
|
||||
|
||||
formula
|
||||
visit(formula f)
|
||||
{
|
||||
switch (op o = f.kind())
|
||||
{
|
||||
case op::Or:
|
||||
case op::And:
|
||||
{
|
||||
vec res;
|
||||
vec oblig;
|
||||
vec susp;
|
||||
unsigned mos = f.size();
|
||||
for (unsigned i = 0; i < mos; ++i)
|
||||
{
|
||||
formula c = f[i];
|
||||
if (c.is_boolean())
|
||||
res.push_back(c);
|
||||
else if (oblig_ && c.is_syntactic_obligation())
|
||||
oblig.push_back(c);
|
||||
else if (c.is_eventual() && c.is_universal())
|
||||
susp.push_back(c);
|
||||
else
|
||||
res.push_back(recurse(c));
|
||||
}
|
||||
if (!oblig.empty())
|
||||
{
|
||||
res.push_back(recurse(formula::multop(o, oblig)));
|
||||
}
|
||||
if (!susp.empty())
|
||||
{
|
||||
formula x = formula::multop(o, susp);
|
||||
// Rewrite 'x' as 'G"x"'
|
||||
formula g = recurse(x);
|
||||
if (o == op::And)
|
||||
{
|
||||
res.push_back(g);
|
||||
}
|
||||
else
|
||||
{
|
||||
// res || susp -> (res && G![susp]) || G[susp])
|
||||
auto r = formula::multop(o, res);
|
||||
auto gn = formula::G(formula::Not(g[0]));
|
||||
return formula::Or({formula::And({r, gn}), g});
|
||||
}
|
||||
}
|
||||
return formula::multop(o, res);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return f.map([this](formula f)
|
||||
{
|
||||
return this->recurse(f);
|
||||
});
|
||||
}
|
||||
switch (op o = f.kind())
|
||||
{
|
||||
case op::Or:
|
||||
case op::And:
|
||||
{
|
||||
vec res;
|
||||
vec oblig;
|
||||
vec susp;
|
||||
unsigned mos = f.size();
|
||||
for (unsigned i = 0; i < mos; ++i)
|
||||
{
|
||||
formula c = f[i];
|
||||
if (c.is_boolean())
|
||||
res.push_back(c);
|
||||
else if (oblig_ && c.is_syntactic_obligation())
|
||||
oblig.push_back(c);
|
||||
else if (c.is_eventual() && c.is_universal())
|
||||
susp.push_back(c);
|
||||
else
|
||||
res.push_back(recurse(c));
|
||||
}
|
||||
if (!oblig.empty())
|
||||
{
|
||||
res.push_back(recurse(formula::multop(o, oblig)));
|
||||
}
|
||||
if (!susp.empty())
|
||||
{
|
||||
formula x = formula::multop(o, susp);
|
||||
// Rewrite 'x' as 'G"x"'
|
||||
formula g = recurse(x);
|
||||
if (o == op::And)
|
||||
{
|
||||
res.push_back(g);
|
||||
}
|
||||
else
|
||||
{
|
||||
// res || susp -> (res && G![susp]) || G[susp])
|
||||
auto r = formula::multop(o, res);
|
||||
auto gn = formula::G(formula::Not(g[0]));
|
||||
return formula::Or({formula::And({r, gn}), g});
|
||||
}
|
||||
}
|
||||
return formula::multop(o, res);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
return f.map([this](formula f)
|
||||
{
|
||||
return this->recurse(f);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
formula
|
||||
recurse(formula f)
|
||||
{
|
||||
formula res;
|
||||
if (f.is_boolean())
|
||||
return f;
|
||||
if (oblig_ && f.is_syntactic_obligation())
|
||||
{
|
||||
fmap_t::const_iterator i = assoc_.find(f);
|
||||
if (i != assoc_.end())
|
||||
return i->second;
|
||||
formula res;
|
||||
if (f.is_boolean())
|
||||
return f;
|
||||
if (oblig_ && f.is_syntactic_obligation())
|
||||
{
|
||||
fmap_t::const_iterator i = assoc_.find(f);
|
||||
if (i != assoc_.end())
|
||||
return i->second;
|
||||
|
||||
std::ostringstream s;
|
||||
print_psl(s << "〈", f) << "〉";
|
||||
res = formula::ap(s.str());
|
||||
a2o_[res] = f;
|
||||
assoc_[f] = res;
|
||||
return res;
|
||||
}
|
||||
if (f.is_eventual() && f.is_universal())
|
||||
{
|
||||
fmap_t::const_iterator i = assoc_.find(f);
|
||||
if (i != assoc_.end())
|
||||
return formula::G(i->second);
|
||||
std::ostringstream s;
|
||||
print_psl(s << "〈", f) << "〉";
|
||||
res = formula::ap(s.str());
|
||||
a2o_[res] = f;
|
||||
assoc_[f] = res;
|
||||
return res;
|
||||
}
|
||||
if (f.is_eventual() && f.is_universal())
|
||||
{
|
||||
fmap_t::const_iterator i = assoc_.find(f);
|
||||
if (i != assoc_.end())
|
||||
return formula::G(i->second);
|
||||
|
||||
std::ostringstream s;
|
||||
print_psl(s << '[', f) << "]$";
|
||||
res = formula::ap(s.str());
|
||||
g2s_[res] = f;
|
||||
assoc_[f] = res;
|
||||
return formula::G(res);
|
||||
}
|
||||
return visit(f);
|
||||
std::ostringstream s;
|
||||
print_psl(s << '[', f) << "]$";
|
||||
res = formula::ap(s.str());
|
||||
g2s_[res] = f;
|
||||
assoc_[f] = res;
|
||||
return formula::G(res);
|
||||
}
|
||||
return visit(f);
|
||||
}
|
||||
|
||||
private:
|
||||
fmap_t& g2s_;
|
||||
fmap_t assoc_; // This one is only needed by the visitor.
|
||||
fmap_t assoc_; // This one is only needed by the visitor.
|
||||
fmap_t& a2o_;
|
||||
bool oblig_;
|
||||
};
|
||||
|
|
@ -160,22 +160,22 @@ namespace spot
|
|||
{
|
||||
bdd_dict_ptr dict = left->get_dict();
|
||||
auto right =
|
||||
iterated_simulations(scc_filter(ltl_to_tgba_fm(f, dict, true, true),
|
||||
false));
|
||||
iterated_simulations(scc_filter(ltl_to_tgba_fm(f, dict, true, true),
|
||||
false));
|
||||
|
||||
twa_graph_ptr res = make_twa_graph(dict);
|
||||
{
|
||||
// Copy all atomic propositions, except the one corresponding
|
||||
// to the variable v used for synchronization.
|
||||
int vn = bdd_var(v);
|
||||
assert(dict->bdd_map[vn].type = bdd_dict::var);
|
||||
formula vf = dict->bdd_map[vn].f;
|
||||
for (auto a: left->ap())
|
||||
if (a != vf)
|
||||
res->register_ap(a);
|
||||
for (auto a: right->ap())
|
||||
if (a != vf)
|
||||
res->register_ap(a);
|
||||
// Copy all atomic propositions, except the one corresponding
|
||||
// to the variable v used for synchronization.
|
||||
int vn = bdd_var(v);
|
||||
assert(dict->bdd_map[vn].type = bdd_dict::var);
|
||||
formula vf = dict->bdd_map[vn].f;
|
||||
for (auto a: left->ap())
|
||||
if (a != vf)
|
||||
res->register_ap(a);
|
||||
for (auto a: right->ap())
|
||||
if (a != vf)
|
||||
res->register_ap(a);
|
||||
}
|
||||
|
||||
unsigned lsets = left->num_sets();
|
||||
|
|
@ -195,78 +195,78 @@ namespace spot
|
|||
res->set_init_state(i);
|
||||
|
||||
while (!todo.empty())
|
||||
{
|
||||
p = todo.front();
|
||||
todo.pop_front();
|
||||
const state* ls = p.first;
|
||||
const state* rs = p.second;
|
||||
int src = seen[p];
|
||||
{
|
||||
p = todo.front();
|
||||
todo.pop_front();
|
||||
const state* ls = p.first;
|
||||
const state* rs = p.second;
|
||||
int src = seen[p];
|
||||
|
||||
for (auto li: left->succ(ls))
|
||||
{
|
||||
state_pair d(li->dst(), ris);
|
||||
bdd lc = li->cond();
|
||||
for (auto li: left->succ(ls))
|
||||
{
|
||||
state_pair d(li->dst(), ris);
|
||||
bdd lc = li->cond();
|
||||
|
||||
twa_succ_iterator* ri = nullptr;
|
||||
// Should we reset the right automaton?
|
||||
if ((lc & v) == lc)
|
||||
{
|
||||
// No.
|
||||
ri = right->succ_iter(rs);
|
||||
ri->first();
|
||||
}
|
||||
// Yes. Reset the right automaton.
|
||||
else
|
||||
{
|
||||
p.second = ris;
|
||||
}
|
||||
twa_succ_iterator* ri = nullptr;
|
||||
// Should we reset the right automaton?
|
||||
if ((lc & v) == lc)
|
||||
{
|
||||
// No.
|
||||
ri = right->succ_iter(rs);
|
||||
ri->first();
|
||||
}
|
||||
// Yes. Reset the right automaton.
|
||||
else
|
||||
{
|
||||
p.second = ris;
|
||||
}
|
||||
|
||||
// This loops over all the right edges
|
||||
// if RI is defined. Otherwise this just makes
|
||||
// one iteration as if the right automaton was
|
||||
// looping in state 0 with "true".
|
||||
while (!ri || !ri->done())
|
||||
{
|
||||
bdd cond = lc;
|
||||
acc_cond::mark_t racc = radd;
|
||||
if (ri)
|
||||
{
|
||||
cond = lc & ri->cond();
|
||||
// Skip incompatible edges.
|
||||
if (cond == bddfalse)
|
||||
{
|
||||
ri->next();
|
||||
continue;
|
||||
}
|
||||
d.second = ri->dst();
|
||||
racc = ri->acc();
|
||||
}
|
||||
// This loops over all the right edges
|
||||
// if RI is defined. Otherwise this just makes
|
||||
// one iteration as if the right automaton was
|
||||
// looping in state 0 with "true".
|
||||
while (!ri || !ri->done())
|
||||
{
|
||||
bdd cond = lc;
|
||||
acc_cond::mark_t racc = radd;
|
||||
if (ri)
|
||||
{
|
||||
cond = lc & ri->cond();
|
||||
// Skip incompatible edges.
|
||||
if (cond == bddfalse)
|
||||
{
|
||||
ri->next();
|
||||
continue;
|
||||
}
|
||||
d.second = ri->dst();
|
||||
racc = ri->acc();
|
||||
}
|
||||
|
||||
int dest;
|
||||
pair_map::const_iterator i = seen.find(d);
|
||||
if (i != seen.end()) // Is this an existing state?
|
||||
{
|
||||
dest = i->second;
|
||||
}
|
||||
else
|
||||
{
|
||||
dest = res->new_state();
|
||||
seen[d] = dest;
|
||||
todo.push_back(d);
|
||||
}
|
||||
int dest;
|
||||
pair_map::const_iterator i = seen.find(d);
|
||||
if (i != seen.end()) // Is this an existing state?
|
||||
{
|
||||
dest = i->second;
|
||||
}
|
||||
else
|
||||
{
|
||||
dest = res->new_state();
|
||||
seen[d] = dest;
|
||||
todo.push_back(d);
|
||||
}
|
||||
|
||||
acc_cond::mark_t a = li->acc() | (racc << lsets);
|
||||
res->new_edge(src, dest, bdd_exist(cond, v), a);
|
||||
acc_cond::mark_t a = li->acc() | (racc << lsets);
|
||||
res->new_edge(src, dest, bdd_exist(cond, v), a);
|
||||
|
||||
if (ri)
|
||||
ri->next();
|
||||
else
|
||||
break;
|
||||
}
|
||||
if (ri)
|
||||
right->release_iter(ri);
|
||||
}
|
||||
}
|
||||
if (ri)
|
||||
ri->next();
|
||||
else
|
||||
break;
|
||||
}
|
||||
if (ri)
|
||||
right->release_iter(ri);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
|
@ -274,9 +274,9 @@ namespace spot
|
|||
|
||||
twa_graph_ptr
|
||||
compsusp(formula f, const bdd_dict_ptr& dict,
|
||||
bool no_wdba, bool no_simulation,
|
||||
bool early_susp, bool no_susp_product, bool wdba_smaller,
|
||||
bool oblig)
|
||||
bool no_wdba, bool no_simulation,
|
||||
bool early_susp, bool no_susp_product, bool wdba_smaller,
|
||||
bool oblig)
|
||||
{
|
||||
ltl_suspender_visitor::fmap_t g2s;
|
||||
ltl_suspender_visitor::fmap_t a2o;
|
||||
|
|
@ -286,18 +286,18 @@ namespace spot
|
|||
// Translate the patched formula, and remove useless SCCs.
|
||||
twa_graph_ptr res =
|
||||
scc_filter(ltl_to_tgba_fm(g, dict, true, true, false, false,
|
||||
nullptr, nullptr),
|
||||
false);
|
||||
nullptr, nullptr),
|
||||
false);
|
||||
|
||||
if (!no_wdba)
|
||||
{
|
||||
twa_graph_ptr min = minimize_obligation(res, g,
|
||||
nullptr, wdba_smaller);
|
||||
if (min != res)
|
||||
{
|
||||
res = min;
|
||||
no_simulation = true;
|
||||
}
|
||||
twa_graph_ptr min = minimize_obligation(res, g,
|
||||
nullptr, wdba_smaller);
|
||||
if (min != res)
|
||||
{
|
||||
res = min;
|
||||
no_simulation = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!no_simulation)
|
||||
|
|
@ -307,21 +307,21 @@ namespace spot
|
|||
spot::formula_bdd_map susp;
|
||||
for (auto& it: g2s)
|
||||
{
|
||||
auto j = dict->var_map.find(it.first);
|
||||
// If no BDD variable of this suspended formula exist in the
|
||||
// BDD dict, it means the suspended subformulae was never
|
||||
// actually used in the automaton. Just skip it. FIXME: It
|
||||
// would be better if we had a way to check that the variable
|
||||
// is used in this automaton, and not in some automaton
|
||||
// (sharing the same dictionary.)
|
||||
if (j != dict->var_map.end())
|
||||
susp[it.second] = bdd_ithvar(j->second);
|
||||
auto j = dict->var_map.find(it.first);
|
||||
// If no BDD variable of this suspended formula exist in the
|
||||
// BDD dict, it means the suspended subformulae was never
|
||||
// actually used in the automaton. Just skip it. FIXME: It
|
||||
// would be better if we had a way to check that the variable
|
||||
// is used in this automaton, and not in some automaton
|
||||
// (sharing the same dictionary.)
|
||||
if (j != dict->var_map.end())
|
||||
susp[it.second] = bdd_ithvar(j->second);
|
||||
}
|
||||
|
||||
// Remove suspendable formulae from non-accepting SCCs.
|
||||
bdd suspvars = bddtrue;
|
||||
for (formula_bdd_map::const_iterator i = susp.begin();
|
||||
i != susp.end(); ++i)
|
||||
i != susp.end(); ++i)
|
||||
suspvars &= i->second;
|
||||
|
||||
bdd allaccap = bddtrue; // set of atomic prop used in accepting SCCs.
|
||||
|
|
@ -332,8 +332,8 @@ namespace spot
|
|||
// in accepting SCC.
|
||||
unsigned sn = si.scc_count();
|
||||
for (unsigned n = 0; n < sn; n++)
|
||||
if (si.is_accepting_scc(n))
|
||||
allaccap &= si.scc_ap_support(n);
|
||||
if (si.is_accepting_scc(n))
|
||||
allaccap &= si.scc_ap_support(n);
|
||||
|
||||
bdd ignored = bdd_exist(suspvars, allaccap);
|
||||
suspvars = bdd_existcomp(suspvars, allaccap);
|
||||
|
|
@ -343,9 +343,9 @@ namespace spot
|
|||
// Do we need to synchronize any suspended formula?
|
||||
if (!susp.empty() && !no_susp_product)
|
||||
for (formula_bdd_map::const_iterator i = susp.begin();
|
||||
i != susp.end(); ++i)
|
||||
if ((allaccap & i->second) == allaccap)
|
||||
res = susp_prod(res, i->first, i->second);
|
||||
i != susp.end(); ++i)
|
||||
if ((allaccap & i->second) == allaccap)
|
||||
res = susp_prod(res, i->first, i->second);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -50,7 +50,7 @@ namespace spot
|
|||
/// spot::translator class instead.
|
||||
SPOT_API twa_graph_ptr
|
||||
compsusp(formula f, const bdd_dict_ptr& dict,
|
||||
bool no_wdba = false, bool no_simulation = false,
|
||||
bool early_susp = false, bool no_susp_product = false,
|
||||
bool wdba_smaller = false, bool oblig = false);
|
||||
bool no_wdba = false, bool no_simulation = false,
|
||||
bool early_susp = false, bool no_susp_product = false,
|
||||
bool wdba_smaller = false, bool oblig = false);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,8 +38,8 @@ namespace spot
|
|||
std::set<unsigned>* incomplete = nullptr;
|
||||
if (preserve_names)
|
||||
{
|
||||
names = new std::vector<std::string>;
|
||||
out->set_named_prop("state-names", names);
|
||||
names = new std::vector<std::string>;
|
||||
out->set_named_prop("state-names", names);
|
||||
}
|
||||
|
||||
// States already seen.
|
||||
|
|
@ -49,59 +49,59 @@ namespace spot
|
|||
|
||||
auto new_state = [&](const state* s) -> unsigned
|
||||
{
|
||||
auto p = seen.emplace(s, 0);
|
||||
if (p.second)
|
||||
{
|
||||
p.first->second = out->new_state();
|
||||
todo.push_back(p.first);
|
||||
if (names)
|
||||
names->push_back(aut->format_state(s));
|
||||
}
|
||||
else
|
||||
{
|
||||
s->destroy();
|
||||
}
|
||||
return p.first->second;
|
||||
auto p = seen.emplace(s, 0);
|
||||
if (p.second)
|
||||
{
|
||||
p.first->second = out->new_state();
|
||||
todo.push_back(p.first);
|
||||
if (names)
|
||||
names->push_back(aut->format_state(s));
|
||||
}
|
||||
else
|
||||
{
|
||||
s->destroy();
|
||||
}
|
||||
return p.first->second;
|
||||
};
|
||||
|
||||
out->set_init_state(new_state(aut->get_init_state()));
|
||||
while (!todo.empty())
|
||||
{
|
||||
const state* src1;
|
||||
unsigned src2;
|
||||
std::tie(src1, src2) = *todo.front();
|
||||
todo.pop_front();
|
||||
for (auto* t: aut->succ(src1))
|
||||
{
|
||||
if (SPOT_UNLIKELY(max_states < out->num_states()))
|
||||
{
|
||||
// If we have reached the max number of state, never try
|
||||
// to create a new one.
|
||||
auto i = seen.find(t->dst());
|
||||
if (i == seen.end())
|
||||
{
|
||||
if (!incomplete)
|
||||
incomplete = new std::set<unsigned>;
|
||||
incomplete->insert(src2);
|
||||
continue;
|
||||
}
|
||||
out->new_edge(src2, i->second, t->cond(), t->acc());
|
||||
}
|
||||
else
|
||||
{
|
||||
out->new_edge(src2, new_state(t->dst()), t->cond(), t->acc());
|
||||
}
|
||||
}
|
||||
const state* src1;
|
||||
unsigned src2;
|
||||
std::tie(src1, src2) = *todo.front();
|
||||
todo.pop_front();
|
||||
for (auto* t: aut->succ(src1))
|
||||
{
|
||||
if (SPOT_UNLIKELY(max_states < out->num_states()))
|
||||
{
|
||||
// If we have reached the max number of state, never try
|
||||
// to create a new one.
|
||||
auto i = seen.find(t->dst());
|
||||
if (i == seen.end())
|
||||
{
|
||||
if (!incomplete)
|
||||
incomplete = new std::set<unsigned>;
|
||||
incomplete->insert(src2);
|
||||
continue;
|
||||
}
|
||||
out->new_edge(src2, i->second, t->cond(), t->acc());
|
||||
}
|
||||
else
|
||||
{
|
||||
out->new_edge(src2, new_state(t->dst()), t->cond(), t->acc());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
auto s = seen.begin();
|
||||
while (s != seen.end())
|
||||
{
|
||||
// Advance the iterator before deleting the "key" pointer.
|
||||
const state* ptr = s->first;
|
||||
++s;
|
||||
ptr->destroy();
|
||||
// Advance the iterator before deleting the "key" pointer.
|
||||
const state* ptr = s->first;
|
||||
++s;
|
||||
ptr->destroy();
|
||||
}
|
||||
|
||||
if (incomplete)
|
||||
|
|
|
|||
|
|
@ -46,21 +46,21 @@ namespace spot
|
|||
|
||||
while (!q.empty())
|
||||
{
|
||||
y = q.back();
|
||||
q.pop_back();
|
||||
y = q.back();
|
||||
q.pop_back();
|
||||
|
||||
info_[y].mark = false;
|
||||
for (auto x: info_[y].b)
|
||||
{
|
||||
assert(info_[x].seen);
|
||||
// insert y in A(x)
|
||||
info_[x].del[y] = false;
|
||||
// unmark x recursively if marked
|
||||
if (info_[x].mark)
|
||||
q.push_back(x);
|
||||
}
|
||||
// empty B(y)
|
||||
info_[y].b.clear();
|
||||
info_[y].mark = false;
|
||||
for (auto x: info_[y].b)
|
||||
{
|
||||
assert(info_[x].seen);
|
||||
// insert y in A(x)
|
||||
info_[x].del[y] = false;
|
||||
// unmark x recursively if marked
|
||||
if (info_[x].mark)
|
||||
q.push_back(x);
|
||||
}
|
||||
// empty B(y)
|
||||
info_[y].b.clear();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -84,57 +84,57 @@ namespace spot
|
|||
|
||||
while (keep_going && !dfs_.empty())
|
||||
{
|
||||
dfs_entry& cur = dfs_.back();
|
||||
if (cur.succ == 0)
|
||||
cur.succ = aut_->get_graph().state_storage(cur.s).succ;
|
||||
else
|
||||
cur.succ = aut_->edge_storage(cur.succ).next_succ;
|
||||
if (cur.succ)
|
||||
{
|
||||
// Explore one successor.
|
||||
dfs_entry& cur = dfs_.back();
|
||||
if (cur.succ == 0)
|
||||
cur.succ = aut_->get_graph().state_storage(cur.s).succ;
|
||||
else
|
||||
cur.succ = aut_->edge_storage(cur.succ).next_succ;
|
||||
if (cur.succ)
|
||||
{
|
||||
// Explore one successor.
|
||||
|
||||
// Ignore those that are not on the SCC, or destination
|
||||
// that have been "virtually" deleted from A(v).
|
||||
unsigned s = aut_->edge_storage(cur.succ).dst;
|
||||
// Ignore those that are not on the SCC, or destination
|
||||
// that have been "virtually" deleted from A(v).
|
||||
unsigned s = aut_->edge_storage(cur.succ).dst;
|
||||
|
||||
if ((sm_.scc_of(s) != scc) || (info_[cur.s].del[s]))
|
||||
continue;
|
||||
if ((sm_.scc_of(s) != scc) || (info_[cur.s].del[s]))
|
||||
continue;
|
||||
|
||||
info_[s].seen = true;
|
||||
if (!info_[s].mark)
|
||||
{
|
||||
push_state(s);
|
||||
}
|
||||
else if (!info_[s].reach)
|
||||
{
|
||||
keep_going = cycle_found(s);
|
||||
cur.f = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
nocycle(cur.s, s);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// No more successors.
|
||||
bool f = cur.f;
|
||||
unsigned v = cur.s;
|
||||
info_[s].seen = true;
|
||||
if (!info_[s].mark)
|
||||
{
|
||||
push_state(s);
|
||||
}
|
||||
else if (!info_[s].reach)
|
||||
{
|
||||
keep_going = cycle_found(s);
|
||||
cur.f = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
nocycle(cur.s, s);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// No more successors.
|
||||
bool f = cur.f;
|
||||
unsigned v = cur.s;
|
||||
|
||||
dfs_.pop_back();
|
||||
if (f)
|
||||
unmark(v);
|
||||
info_[v].reach = true;
|
||||
dfs_.pop_back();
|
||||
if (f)
|
||||
unmark(v);
|
||||
info_[v].reach = true;
|
||||
|
||||
// Update the predecessor in the stack if there is one.
|
||||
if (!dfs_.empty())
|
||||
{
|
||||
if (f)
|
||||
dfs_.back().f = true;
|
||||
else
|
||||
nocycle(dfs_.back().s, v);
|
||||
}
|
||||
}
|
||||
// Update the predecessor in the stack if there is one.
|
||||
if (!dfs_.empty())
|
||||
{
|
||||
if (f)
|
||||
dfs_.back().f = true;
|
||||
else
|
||||
nocycle(dfs_.back().s, v);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Purge the dfs_ stack, in case we aborted because cycle_found()
|
||||
|
|
@ -150,8 +150,8 @@ namespace spot
|
|||
++i;
|
||||
do
|
||||
{
|
||||
std::cout << i->s << ' ';
|
||||
++i;
|
||||
std::cout << i->s << ' ';
|
||||
++i;
|
||||
}
|
||||
while (i != dfs_.end());
|
||||
std::cout << '\n';
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ namespace spot
|
|||
title = {Enumerating the Cycles of a Digraph: A New
|
||||
Preprocessing Strategy},
|
||||
journal = {Information Sciences},
|
||||
year = {1982},
|
||||
year = {1982},
|
||||
volume = {27},
|
||||
number = {3},
|
||||
pages = {163--182},
|
||||
|
|
@ -80,7 +80,7 @@ namespace spot
|
|||
struct state_info
|
||||
{
|
||||
state_info(unsigned num)
|
||||
: seen(false), reach(false), mark(false), del(num)
|
||||
: seen(false), reach(false), mark(false), del(num)
|
||||
{
|
||||
}
|
||||
bool seen;
|
||||
|
|
|
|||
|
|
@ -61,66 +61,66 @@ namespace spot
|
|||
{
|
||||
const_twa_graph_ptr a_;
|
||||
typedef std::tuple<acc_cond::mark_t,
|
||||
acc_cond::mark_t,
|
||||
bool> cache_entry;
|
||||
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(s) : 0;
|
||||
acc_cond::mark_t common = a_->acc().all_sets();
|
||||
unsigned s1 = sm_ ? sm_->scc_of(s) : 0;
|
||||
acc_cond::mark_t common = a_->acc().all_sets();
|
||||
acc_cond::mark_t union_ = 0U;
|
||||
bool has_acc_self_loop = false;
|
||||
bool seen = false;
|
||||
for (auto& t: a_->out(s))
|
||||
bool has_acc_self_loop = false;
|
||||
bool seen = false;
|
||||
for (auto& t: a_->out(s))
|
||||
{
|
||||
// Ignore edges that leave the SCC of s.
|
||||
unsigned d = t.dst;
|
||||
unsigned s2 = sm_ ? sm_->scc_of(d) : 0;
|
||||
if (s2 != s1)
|
||||
continue;
|
||||
// Ignore edges that leave the SCC of s.
|
||||
unsigned d = t.dst;
|
||||
unsigned s2 = sm_ ? sm_->scc_of(d) : 0;
|
||||
if (s2 != s1)
|
||||
continue;
|
||||
|
||||
common &= t.acc;
|
||||
union_ |= t.acc;
|
||||
|
||||
// an accepting self-loop?
|
||||
has_acc_self_loop |= (t.dst == s) && a_->acc().accepting(t.acc);
|
||||
seen = true;
|
||||
// an accepting self-loop?
|
||||
has_acc_self_loop |= (t.dst == s) && a_->acc().accepting(t.acc);
|
||||
seen = true;
|
||||
}
|
||||
if (!seen)
|
||||
common = 0U;
|
||||
if (!seen)
|
||||
common = 0U;
|
||||
cache_[s] = std::make_tuple(common, union_, has_acc_self_loop);
|
||||
}
|
||||
|
||||
public:
|
||||
outgoing_acc(const const_twa_graph_ptr& a, const scc_info* sm):
|
||||
a_(a), cache_(a->num_states()), sm_(sm)
|
||||
a_(a), cache_(a->num_states()), sm_(sm)
|
||||
{
|
||||
unsigned n = a->num_states();
|
||||
for (unsigned s = 0; s < n; ++s)
|
||||
fill_cache(s);
|
||||
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(unsigned s)
|
||||
{
|
||||
assert(s < cache_.size());
|
||||
return std::get<0>(cache_[s]);
|
||||
assert(s < cache_.size());
|
||||
return std::get<0>(cache_[s]);
|
||||
}
|
||||
|
||||
// Union of all outgoing acceptance sets
|
||||
acc_cond::mark_t union_acc(unsigned s)
|
||||
{
|
||||
assert(s < cache_.size());
|
||||
return std::get<1>(cache_[s]);
|
||||
assert(s < cache_.size());
|
||||
return std::get<1>(cache_[s]);
|
||||
}
|
||||
|
||||
// Has an accepting self-loop
|
||||
bool has_acc_selfloop(unsigned s)
|
||||
{
|
||||
assert(s < cache_.size());
|
||||
return std::get<2>(cache_[s]);
|
||||
assert(s < cache_.size());
|
||||
return std::get<2>(cache_[s]);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -134,20 +134,20 @@ namespace spot
|
|||
unsigned
|
||||
next_level(int slevel, acc_cond::mark_t set, bool skip_levels)
|
||||
{
|
||||
// Update the order with any new set we discover
|
||||
if (auto newsets = set - found_)
|
||||
{
|
||||
newsets.fill(std::back_inserter(order_));
|
||||
found_ |= newsets;
|
||||
}
|
||||
// Update the order with any new set we discover
|
||||
if (auto newsets = set - found_)
|
||||
{
|
||||
newsets.fill(std::back_inserter(order_));
|
||||
found_ |= newsets;
|
||||
}
|
||||
|
||||
unsigned next = slevel;
|
||||
while (next < order_.size() && set.has(order_[next]))
|
||||
{
|
||||
++next;
|
||||
if (!skip_levels)
|
||||
break;
|
||||
}
|
||||
{
|
||||
++next;
|
||||
if (!skip_levels)
|
||||
break;
|
||||
}
|
||||
return next;
|
||||
}
|
||||
|
||||
|
|
@ -156,7 +156,7 @@ namespace spot
|
|||
{
|
||||
std::cout << "Order_" << scc << ":\t";
|
||||
for (auto i: order_)
|
||||
std::cout << i << ", ";
|
||||
std::cout << i << ", ";
|
||||
std::cout << '\n';
|
||||
}
|
||||
};
|
||||
|
|
@ -169,7 +169,7 @@ namespace spot
|
|||
|
||||
public:
|
||||
scc_orders(bool skip_levels):
|
||||
skip_levels_(skip_levels)
|
||||
skip_levels_(skip_levels)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -191,12 +191,12 @@ namespace spot
|
|||
template<bool want_sba>
|
||||
twa_graph_ptr
|
||||
degeneralize_aux(const const_twa_graph_ptr& a, bool use_z_lvl,
|
||||
bool use_cust_acc_orders, int use_lvl_cache,
|
||||
bool skip_levels, bool ignaccsl)
|
||||
bool use_cust_acc_orders, int use_lvl_cache,
|
||||
bool skip_levels, bool ignaccsl)
|
||||
{
|
||||
if (!a->acc().is_generalized_buchi())
|
||||
throw std::runtime_error
|
||||
("degeneralize() can only works with generalized Büchi acceptance");
|
||||
throw std::runtime_error
|
||||
("degeneralize() can only works with generalized Büchi acceptance");
|
||||
|
||||
bool use_scc = use_lvl_cache || use_cust_acc_orders || use_z_lvl;
|
||||
|
||||
|
|
@ -207,7 +207,7 @@ namespace spot
|
|||
res->copy_ap_of(a);
|
||||
res->set_buchi();
|
||||
if (want_sba)
|
||||
res->prop_state_acc(true);
|
||||
res->prop_state_acc(true);
|
||||
// Preserve determinism, weakness, and stutter-invariance
|
||||
res->prop_copy(a, { false, true, true, true });
|
||||
|
||||
|
|
@ -218,17 +218,17 @@ namespace spot
|
|||
// denote accepting states.
|
||||
std::vector<unsigned> order;
|
||||
{
|
||||
// FIXME: revisit this comment once everything compiles again.
|
||||
//
|
||||
// The order is arbitrary, but it turns out that using push_back
|
||||
// instead of push_front often gives better results because
|
||||
// acceptance sets at the beginning if the cycle are more often
|
||||
// used in the automaton. (This surprising fact is probably
|
||||
// related to the order in which we declare the BDD variables
|
||||
// during the translation.)
|
||||
unsigned n = a->num_sets();
|
||||
for (unsigned i = n; i > 0; --i)
|
||||
order.push_back(i - 1);
|
||||
// FIXME: revisit this comment once everything compiles again.
|
||||
//
|
||||
// The order is arbitrary, but it turns out that using push_back
|
||||
// instead of push_front often gives better results because
|
||||
// acceptance sets at the beginning if the cycle are more often
|
||||
// used in the automaton. (This surprising fact is probably
|
||||
// related to the order in which we declare the BDD variables
|
||||
// during the translation.)
|
||||
unsigned n = a->num_sets();
|
||||
for (unsigned i = n; i > 0; --i)
|
||||
order.push_back(i - 1);
|
||||
}
|
||||
|
||||
// Initialize scc_orders
|
||||
|
|
@ -255,7 +255,7 @@ namespace spot
|
|||
// Compute SCCs in order to use any optimization.
|
||||
scc_info* m = nullptr;
|
||||
if (use_scc)
|
||||
m = new scc_info(a);
|
||||
m = new scc_info(a);
|
||||
|
||||
// Cache for common outgoing acceptances.
|
||||
outgoing_acc outgoing(a, m);
|
||||
|
|
@ -266,27 +266,27 @@ namespace spot
|
|||
// least one accepting self-loop, start the degeneralization on
|
||||
// the accepting level.
|
||||
if (want_sba && !ignaccsl && outgoing.has_acc_selfloop(s.first))
|
||||
s.second = order.size();
|
||||
s.second = order.size();
|
||||
// Otherwise, check for acceptance conditions common to all
|
||||
// outgoing edges, and assume we have already seen these and
|
||||
// start on the associated level.
|
||||
if (s.second == 0)
|
||||
{
|
||||
auto set = outgoing.common_acc(s.first);
|
||||
if (use_cust_acc_orders)
|
||||
s.second = orders.next_level(m->initial(), s.second, set);
|
||||
else
|
||||
while (s.second < order.size()
|
||||
&& set.has(order[s.second]))
|
||||
{
|
||||
++s.second;
|
||||
if (!skip_levels)
|
||||
break;
|
||||
}
|
||||
// There is not accepting level for TBA, let reuse level 0.
|
||||
if (!want_sba && s.second == order.size())
|
||||
s.second = 0;
|
||||
}
|
||||
{
|
||||
auto set = outgoing.common_acc(s.first);
|
||||
if (use_cust_acc_orders)
|
||||
s.second = orders.next_level(m->initial(), s.second, set);
|
||||
else
|
||||
while (s.second < order.size()
|
||||
&& set.has(order[s.second]))
|
||||
{
|
||||
++s.second;
|
||||
if (!skip_levels)
|
||||
break;
|
||||
}
|
||||
// There is not accepting level for TBA, let reuse level 0.
|
||||
if (!want_sba && s.second == order.size())
|
||||
s.second = 0;
|
||||
}
|
||||
|
||||
ds2num[s] = res->new_state();
|
||||
todo.push_back(s);
|
||||
|
|
@ -297,249 +297,249 @@ 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] = std::make_pair(s.second, true);
|
||||
lvl_cache[s.first] = std::make_pair(s.second, true);
|
||||
|
||||
while (!todo.empty())
|
||||
{
|
||||
s = todo.front();
|
||||
todo.pop_front();
|
||||
int src = ds2num[s];
|
||||
unsigned slevel = s.second;
|
||||
{
|
||||
s = todo.front();
|
||||
todo.pop_front();
|
||||
int src = ds2num[s];
|
||||
unsigned slevel = s.second;
|
||||
|
||||
// If we have a state on the last level, it should be accepting.
|
||||
bool is_acc = slevel == order.size();
|
||||
// On the accepting level, start again from level 0.
|
||||
if (want_sba && is_acc)
|
||||
slevel = 0;
|
||||
// If we have a state on the last level, it should be accepting.
|
||||
bool is_acc = slevel == order.size();
|
||||
// On the accepting level, start again from level 0.
|
||||
if (want_sba && is_acc)
|
||||
slevel = 0;
|
||||
|
||||
// Check SCC for state s
|
||||
int s_scc = -1;
|
||||
if (use_scc)
|
||||
s_scc = m->scc_of(s.first);
|
||||
// Check SCC for state s
|
||||
int s_scc = -1;
|
||||
if (use_scc)
|
||||
s_scc = m->scc_of(s.first);
|
||||
|
||||
for (auto& i: a->out(s.first))
|
||||
{
|
||||
degen_state d(i.dst, 0);
|
||||
for (auto& i: a->out(s.first))
|
||||
{
|
||||
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(d.first);
|
||||
is_scc_acc = m->is_accepting_scc(scc);
|
||||
}
|
||||
else
|
||||
{
|
||||
// If we have no SCC information, treat all SCCs as
|
||||
// accepting.
|
||||
scc = -1;
|
||||
is_scc_acc = true;
|
||||
}
|
||||
// Check whether the target SCC is accepting
|
||||
bool is_scc_acc;
|
||||
int scc;
|
||||
if (use_scc)
|
||||
{
|
||||
scc = m->scc_of(d.first);
|
||||
is_scc_acc = m->is_accepting_scc(scc);
|
||||
}
|
||||
else
|
||||
{
|
||||
// If we have no SCC information, treat all SCCs as
|
||||
// accepting.
|
||||
scc = -1;
|
||||
is_scc_acc = true;
|
||||
}
|
||||
|
||||
// The old level is slevel. What should be the new one?
|
||||
auto acc = i.acc;
|
||||
auto otheracc = outgoing.common_acc(d.first);
|
||||
// The old level is slevel. What should be the new one?
|
||||
auto acc = i.acc;
|
||||
auto otheracc = outgoing.common_acc(d.first);
|
||||
|
||||
if (want_sba && is_acc)
|
||||
{
|
||||
// Ignore the last expected acceptance set (the value of
|
||||
// prev below) if it is common to all other outgoing
|
||||
// edges (of the current state) AND if it is not
|
||||
// used by any outgoing edge of the destination
|
||||
// state.
|
||||
//
|
||||
// 1) It's correct to do that, because this acceptance
|
||||
// set is common to other outgoing edges.
|
||||
// Therefore if we make a cycle to this state we
|
||||
// will eventually see that acceptance set thanks
|
||||
// to the "pulling" of the common acceptance sets
|
||||
// of the destination state (d.first).
|
||||
//
|
||||
// 2) It's also desirable because it makes the
|
||||
// degeneralization idempotent (up to a renaming
|
||||
// of states). Consider the following automaton
|
||||
// where 1 is initial and => marks accepting
|
||||
// edges: 1=>1, 1=>2, 2->2, 2->1. This is
|
||||
// already an SBA, with 1 as accepting state.
|
||||
// However if you try degeralize it without
|
||||
// ignoring *prev, you'll get two copies of state
|
||||
// 2, depending on whether we reach it using 1=>2
|
||||
// or from 2->2. If this example was not clear,
|
||||
// play with the "degenid.test" test case.
|
||||
//
|
||||
// 3) Ignoring all common acceptance sets would also
|
||||
// be correct, but it would make the
|
||||
// degeneralization produce larger automata in some
|
||||
// cases. The current condition to ignore only one
|
||||
// acceptance set if is this not used by the next
|
||||
// state is a heuristic that is compatible with
|
||||
// point 2) above while not causing more states to
|
||||
// be generated in our benchmark of 188 formulae
|
||||
// from the literature.
|
||||
if (!order.empty())
|
||||
{
|
||||
unsigned prev = order.size() - 1;
|
||||
auto common = outgoing.common_acc(s.first);
|
||||
if (common.has(order[prev]))
|
||||
{
|
||||
auto u = outgoing.union_acc(d.first);
|
||||
if (!u.has(order[prev]))
|
||||
acc -= a->acc().mark(order[prev]);
|
||||
}
|
||||
}
|
||||
}
|
||||
// A edge in the SLEVEL acceptance set should
|
||||
// be directed to the next acceptance set. If the
|
||||
// current edge is also in the next acceptance
|
||||
// set, then go to the one after, etc.
|
||||
//
|
||||
// See Denis Oddoux's PhD thesis for a nice
|
||||
// explanation (in French).
|
||||
// @PhDThesis{ oddoux.03.phd,
|
||||
// author = {Denis Oddoux},
|
||||
// title = {Utilisation des automates alternants pour un
|
||||
// model-checking efficace des logiques
|
||||
// temporelles lin{\'e}aires.},
|
||||
// school = {Universit{\'e}e Paris 7},
|
||||
// year = {2003},
|
||||
// address= {Paris, France},
|
||||
// month = {December}
|
||||
// }
|
||||
if (is_scc_acc)
|
||||
{
|
||||
// If lvl_cache is used and switching SCCs, use level
|
||||
// from cache
|
||||
if (use_lvl_cache && s_scc != scc
|
||||
&& lvl_cache[d.first].second)
|
||||
{
|
||||
d.second = lvl_cache[d.first].first;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Complete (or replace) the acceptance sets of
|
||||
// this link with the acceptance sets common to
|
||||
// all edges leaving the destination state.
|
||||
if (s_scc == scc)
|
||||
acc |= otheracc;
|
||||
else
|
||||
acc = otheracc;
|
||||
if (want_sba && is_acc)
|
||||
{
|
||||
// Ignore the last expected acceptance set (the value of
|
||||
// prev below) if it is common to all other outgoing
|
||||
// edges (of the current state) AND if it is not
|
||||
// used by any outgoing edge of the destination
|
||||
// state.
|
||||
//
|
||||
// 1) It's correct to do that, because this acceptance
|
||||
// set is common to other outgoing edges.
|
||||
// Therefore if we make a cycle to this state we
|
||||
// will eventually see that acceptance set thanks
|
||||
// to the "pulling" of the common acceptance sets
|
||||
// of the destination state (d.first).
|
||||
//
|
||||
// 2) It's also desirable because it makes the
|
||||
// degeneralization idempotent (up to a renaming
|
||||
// of states). Consider the following automaton
|
||||
// where 1 is initial and => marks accepting
|
||||
// edges: 1=>1, 1=>2, 2->2, 2->1. This is
|
||||
// already an SBA, with 1 as accepting state.
|
||||
// However if you try degeralize it without
|
||||
// ignoring *prev, you'll get two copies of state
|
||||
// 2, depending on whether we reach it using 1=>2
|
||||
// or from 2->2. If this example was not clear,
|
||||
// play with the "degenid.test" test case.
|
||||
//
|
||||
// 3) Ignoring all common acceptance sets would also
|
||||
// be correct, but it would make the
|
||||
// degeneralization produce larger automata in some
|
||||
// cases. The current condition to ignore only one
|
||||
// acceptance set if is this not used by the next
|
||||
// state is a heuristic that is compatible with
|
||||
// point 2) above while not causing more states to
|
||||
// be generated in our benchmark of 188 formulae
|
||||
// from the literature.
|
||||
if (!order.empty())
|
||||
{
|
||||
unsigned prev = order.size() - 1;
|
||||
auto common = outgoing.common_acc(s.first);
|
||||
if (common.has(order[prev]))
|
||||
{
|
||||
auto u = outgoing.union_acc(d.first);
|
||||
if (!u.has(order[prev]))
|
||||
acc -= a->acc().mark(order[prev]);
|
||||
}
|
||||
}
|
||||
}
|
||||
// A edge in the SLEVEL acceptance set should
|
||||
// be directed to the next acceptance set. If the
|
||||
// current edge is also in the next acceptance
|
||||
// set, then go to the one after, etc.
|
||||
//
|
||||
// See Denis Oddoux's PhD thesis for a nice
|
||||
// explanation (in French).
|
||||
// @PhDThesis{ oddoux.03.phd,
|
||||
// author = {Denis Oddoux},
|
||||
// title = {Utilisation des automates alternants pour un
|
||||
// model-checking efficace des logiques
|
||||
// temporelles lin{\'e}aires.},
|
||||
// school = {Universit{\'e}e Paris 7},
|
||||
// year = {2003},
|
||||
// address= {Paris, France},
|
||||
// month = {December}
|
||||
// }
|
||||
if (is_scc_acc)
|
||||
{
|
||||
// If lvl_cache is used and switching SCCs, use level
|
||||
// from cache
|
||||
if (use_lvl_cache && s_scc != scc
|
||||
&& lvl_cache[d.first].second)
|
||||
{
|
||||
d.second = lvl_cache[d.first].first;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Complete (or replace) the acceptance sets of
|
||||
// this link with the acceptance sets common to
|
||||
// all edges leaving the destination state.
|
||||
if (s_scc == scc)
|
||||
acc |= otheracc;
|
||||
else
|
||||
acc = otheracc;
|
||||
|
||||
// If use_z_lvl is on, start with level zero 0 when
|
||||
// switching SCCs
|
||||
unsigned next = (!use_z_lvl || s_scc == scc) ? slevel : 0;
|
||||
// If use_z_lvl is on, start with level zero 0 when
|
||||
// switching SCCs
|
||||
unsigned next = (!use_z_lvl || s_scc == scc) ? slevel : 0;
|
||||
|
||||
// If using custom acc orders, get next level
|
||||
// for this scc
|
||||
if (use_cust_acc_orders)
|
||||
{
|
||||
d.second = orders.next_level(scc, next, acc);
|
||||
}
|
||||
// Else compute level according the global acc order
|
||||
else
|
||||
{
|
||||
// As a heuristic, if we enter the SCC on a
|
||||
// state that has at least one accepting
|
||||
// self-loop, start the degeneralization on
|
||||
// the accepting level.
|
||||
if (s_scc != scc
|
||||
&& !ignaccsl
|
||||
&& outgoing.has_acc_selfloop(d.first))
|
||||
{
|
||||
d.second = order.size();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Consider both the current acceptance
|
||||
// sets, and the acceptance sets common to
|
||||
// the outgoing edges of the
|
||||
// destination state. But don't do
|
||||
// that if the state is accepting and we
|
||||
// are not skipping levels.
|
||||
if (skip_levels || !is_acc)
|
||||
while (next < order.size()
|
||||
&& acc.has(order[next]))
|
||||
{
|
||||
++next;
|
||||
if (!skip_levels)
|
||||
break;
|
||||
}
|
||||
d.second = next;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// If using custom acc orders, get next level
|
||||
// for this scc
|
||||
if (use_cust_acc_orders)
|
||||
{
|
||||
d.second = orders.next_level(scc, next, acc);
|
||||
}
|
||||
// Else compute level according the global acc order
|
||||
else
|
||||
{
|
||||
// As a heuristic, if we enter the SCC on a
|
||||
// state that has at least one accepting
|
||||
// self-loop, start the degeneralization on
|
||||
// the accepting level.
|
||||
if (s_scc != scc
|
||||
&& !ignaccsl
|
||||
&& outgoing.has_acc_selfloop(d.first))
|
||||
{
|
||||
d.second = order.size();
|
||||
}
|
||||
else
|
||||
{
|
||||
// Consider both the current acceptance
|
||||
// sets, and the acceptance sets common to
|
||||
// the outgoing edges of the
|
||||
// destination state. But don't do
|
||||
// that if the state is accepting and we
|
||||
// are not skipping levels.
|
||||
if (skip_levels || !is_acc)
|
||||
while (next < order.size()
|
||||
&& acc.has(order[next]))
|
||||
{
|
||||
++next;
|
||||
if (!skip_levels)
|
||||
break;
|
||||
}
|
||||
d.second = next;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// In case we are building a TBA is_acc has to be
|
||||
// set differently for each edge, and
|
||||
// we do not need to stay use final level.
|
||||
if (!want_sba)
|
||||
{
|
||||
is_acc = d.second == order.size();
|
||||
if (is_acc) // The edge is accepting
|
||||
{
|
||||
d.second = 0; // Make it go to the first level.
|
||||
// Skip levels as much as possible.
|
||||
if (!a->acc().accepting(acc) && !skip_levels)
|
||||
{
|
||||
if (use_cust_acc_orders)
|
||||
{
|
||||
d.second = orders.next_level(scc, d.second, acc);
|
||||
}
|
||||
else
|
||||
{
|
||||
while (d.second < order.size() &&
|
||||
acc.has(order[d.second]))
|
||||
++d.second;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
// In case we are building a TBA is_acc has to be
|
||||
// set differently for each edge, and
|
||||
// we do not need to stay use final level.
|
||||
if (!want_sba)
|
||||
{
|
||||
is_acc = d.second == order.size();
|
||||
if (is_acc) // The edge is accepting
|
||||
{
|
||||
d.second = 0; // Make it go to the first level.
|
||||
// Skip levels as much as possible.
|
||||
if (!a->acc().accepting(acc) && !skip_levels)
|
||||
{
|
||||
if (use_cust_acc_orders)
|
||||
{
|
||||
d.second = orders.next_level(scc, d.second, acc);
|
||||
}
|
||||
else
|
||||
{
|
||||
while (d.second < order.size() &&
|
||||
acc.has(order[d.second]))
|
||||
++d.second;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Have we already seen this destination?
|
||||
int dest;
|
||||
ds2num_map::const_iterator di = ds2num.find(d);
|
||||
if (di != ds2num.end())
|
||||
{
|
||||
dest = di->second;
|
||||
}
|
||||
else
|
||||
{
|
||||
dest = res->new_state();
|
||||
ds2num[d] = dest;
|
||||
todo.push_back(d);
|
||||
// Insert new state to cache
|
||||
// Have we already seen this destination?
|
||||
int dest;
|
||||
ds2num_map::const_iterator di = ds2num.find(d);
|
||||
if (di != ds2num.end())
|
||||
{
|
||||
dest = di->second;
|
||||
}
|
||||
else
|
||||
{
|
||||
dest = res->new_state();
|
||||
ds2num[d] = dest;
|
||||
todo.push_back(d);
|
||||
// Insert new state to cache
|
||||
|
||||
if (use_lvl_cache)
|
||||
{
|
||||
auto lvl = d.second;
|
||||
if (lvl_cache[d.first].second)
|
||||
{
|
||||
if (use_lvl_cache == 3)
|
||||
lvl = std::max(lvl_cache[d.first].first, lvl);
|
||||
else if (use_lvl_cache == 2)
|
||||
lvl = std::min(lvl_cache[d.first].first, lvl);
|
||||
}
|
||||
lvl_cache[d.first] = std::make_pair(lvl, true);
|
||||
}
|
||||
}
|
||||
if (use_lvl_cache)
|
||||
{
|
||||
auto lvl = d.second;
|
||||
if (lvl_cache[d.first].second)
|
||||
{
|
||||
if (use_lvl_cache == 3)
|
||||
lvl = std::max(lvl_cache[d.first].first, lvl);
|
||||
else if (use_lvl_cache == 2)
|
||||
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];
|
||||
unsigned& t = tr_cache[dest * 2 + is_acc];
|
||||
|
||||
if (t == 0) // Create edge.
|
||||
t = res->new_acc_edge(src, dest, i.cond, is_acc);
|
||||
else // Update existing edge.
|
||||
res->edge_data(t).cond |= i.cond;
|
||||
}
|
||||
tr_cache.clear();
|
||||
}
|
||||
if (t == 0) // Create edge.
|
||||
t = res->new_acc_edge(src, dest, i.cond, is_acc);
|
||||
else // Update existing edge.
|
||||
res->edge_data(t).cond |= i.cond;
|
||||
}
|
||||
tr_cache.clear();
|
||||
}
|
||||
|
||||
#ifdef DEGEN_DEBUG
|
||||
std::cout << "Orig. order: \t";
|
||||
for (auto i: order)
|
||||
{
|
||||
std::cout << i << ", ";
|
||||
}
|
||||
{
|
||||
std::cout << i << ", ";
|
||||
}
|
||||
std::cout << '\n';
|
||||
orders.print();
|
||||
#endif
|
||||
|
|
@ -553,7 +553,7 @@ namespace spot
|
|||
|
||||
twa_graph_ptr
|
||||
degeneralize(const const_twa_graph_ptr& a,
|
||||
bool use_z_lvl, bool use_cust_acc_orders,
|
||||
bool use_z_lvl, bool use_cust_acc_orders,
|
||||
int use_lvl_cache, bool skip_levels, bool ignaccsl)
|
||||
{
|
||||
// If this already a degeneralized digraph, there is nothing we
|
||||
|
|
@ -562,13 +562,13 @@ namespace spot
|
|||
return std::const_pointer_cast<twa_graph>(a);
|
||||
|
||||
return degeneralize_aux<true>(a, use_z_lvl, use_cust_acc_orders,
|
||||
use_lvl_cache, skip_levels, ignaccsl);
|
||||
use_lvl_cache, skip_levels, ignaccsl);
|
||||
}
|
||||
|
||||
twa_graph_ptr
|
||||
degeneralize_tba(const const_twa_graph_ptr& a,
|
||||
bool use_z_lvl, bool use_cust_acc_orders,
|
||||
int use_lvl_cache, bool skip_levels, bool ignaccsl)
|
||||
bool use_z_lvl, bool use_cust_acc_orders,
|
||||
int use_lvl_cache, bool skip_levels, bool ignaccsl)
|
||||
{
|
||||
// If this already a degeneralized digraph, there is nothing we
|
||||
// can improve.
|
||||
|
|
@ -576,6 +576,6 @@ namespace spot
|
|||
return std::const_pointer_cast<twa_graph>(a);
|
||||
|
||||
return degeneralize_aux<false>(a, use_z_lvl, use_cust_acc_orders,
|
||||
use_lvl_cache, skip_levels, ignaccsl);
|
||||
use_lvl_cache, skip_levels, ignaccsl);
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -51,16 +51,16 @@ namespace spot
|
|||
/// \@{
|
||||
SPOT_API twa_graph_ptr
|
||||
degeneralize(const const_twa_graph_ptr& a, bool use_z_lvl = true,
|
||||
bool use_cust_acc_orders = false,
|
||||
int use_lvl_cache = 1,
|
||||
bool skip_levels = true,
|
||||
bool ignaccsl = false);
|
||||
bool use_cust_acc_orders = false,
|
||||
int use_lvl_cache = 1,
|
||||
bool skip_levels = true,
|
||||
bool ignaccsl = false);
|
||||
|
||||
SPOT_API twa_graph_ptr
|
||||
degeneralize_tba(const const_twa_graph_ptr& a, bool use_z_lvl = true,
|
||||
bool use_cust_acc_orders = false,
|
||||
int use_lvl_cache = 1,
|
||||
bool skip_levels = true,
|
||||
bool ignaccsl = false);
|
||||
bool use_cust_acc_orders = false,
|
||||
int use_lvl_cache = 1,
|
||||
bool skip_levels = true,
|
||||
bool ignaccsl = false);
|
||||
/// \@}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -112,16 +112,16 @@ namespace spot
|
|||
using power_set = std::map<safra_state, int>;
|
||||
const char* const sub[10] =
|
||||
{
|
||||
"\u2080",
|
||||
"\u2081",
|
||||
"\u2082",
|
||||
"\u2083",
|
||||
"\u2084",
|
||||
"\u2085",
|
||||
"\u2086",
|
||||
"\u2087",
|
||||
"\u2088",
|
||||
"\u2089",
|
||||
"\u2080",
|
||||
"\u2081",
|
||||
"\u2082",
|
||||
"\u2083",
|
||||
"\u2084",
|
||||
"\u2085",
|
||||
"\u2086",
|
||||
"\u2087",
|
||||
"\u2088",
|
||||
"\u2089",
|
||||
};
|
||||
|
||||
std::string subscript(unsigned start)
|
||||
|
|
@ -157,18 +157,18 @@ namespace spot
|
|||
assert(max_acc < 32);
|
||||
unsigned mask = (1 << max_acc) - 1;
|
||||
for (auto& t: aut->edges())
|
||||
{
|
||||
t.acc &= mask;
|
||||
}
|
||||
{
|
||||
t.acc &= mask;
|
||||
}
|
||||
}
|
||||
|
||||
struct compare
|
||||
{
|
||||
bool
|
||||
operator() (const safra_state::safra_node_t& lhs,
|
||||
const safra_state::safra_node_t& rhs)
|
||||
const safra_state::safra_node_t& rhs)
|
||||
{
|
||||
return lhs.second < rhs.second;
|
||||
return lhs.second < rhs.second;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -178,7 +178,7 @@ namespace spot
|
|||
{
|
||||
std::vector<safra_state::safra_node_t> res;
|
||||
for (auto& n: nodes)
|
||||
res.emplace_back(n.first, n.second);
|
||||
res.emplace_back(n.first, n.second);
|
||||
std::sort(res.begin(), res.end(), compare());
|
||||
return res;
|
||||
}
|
||||
|
|
@ -191,46 +191,46 @@ namespace spot
|
|||
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;
|
||||
}
|
||||
{
|
||||
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();
|
||||
}
|
||||
{
|
||||
os << subscript(s.top()) << '}';
|
||||
s.pop();
|
||||
}
|
||||
return os.str();
|
||||
}
|
||||
|
||||
|
|
@ -239,7 +239,7 @@ namespace spot
|
|||
{
|
||||
auto res = new std::vector<std::string>(states.size());
|
||||
for (auto& p: states)
|
||||
(*res)[p.second] = nodes_to_string(p.first.nodes_);
|
||||
(*res)[p.second] = nodes_to_string(p.first.nodes_);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
|
@ -250,33 +250,33 @@ namespace spot
|
|||
|
||||
safra_state
|
||||
safra_state::compute_succ(const const_twa_graph_ptr& aut,
|
||||
const bdd& ap,
|
||||
const scc_info& scc,
|
||||
const std::map<int, bdd>& implications,
|
||||
const std::vector<bool>& is_connected,
|
||||
bool use_scc,
|
||||
bool use_simulation) const
|
||||
const bdd& ap,
|
||||
const scc_info& scc,
|
||||
const std::map<int, bdd>& implications,
|
||||
const std::vector<bool>& is_connected,
|
||||
bool use_scc,
|
||||
bool use_simulation) const
|
||||
{
|
||||
safra_state ss = safra_state(nb_braces_.size());
|
||||
for (auto& node: nodes_)
|
||||
{
|
||||
for (auto& t: aut->out(node.first))
|
||||
{
|
||||
if (!bdd_implies(ap, t.cond))
|
||||
continue;
|
||||
// Check if we are leaving the SCC, if so we delete all the
|
||||
// braces as no cycles can be found with that node
|
||||
if (use_scc && scc.scc_of(node.first) != scc.scc_of(t.dst))
|
||||
if (scc.is_accepting_scc(scc.scc_of(t.dst)))
|
||||
for (auto& t: aut->out(node.first))
|
||||
{
|
||||
if (!bdd_implies(ap, t.cond))
|
||||
continue;
|
||||
// Check if we are leaving the SCC, if so we delete all the
|
||||
// braces as no cycles can be found with that node
|
||||
if (use_scc && scc.scc_of(node.first) != scc.scc_of(t.dst))
|
||||
if (scc.is_accepting_scc(scc.scc_of(t.dst)))
|
||||
// Entering accepting SCC so add brace
|
||||
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
|
||||
ss.update_succ(node.second, t.dst, t.acc);
|
||||
assert(ss.nb_braces_.size() == ss.is_green_.size());
|
||||
}
|
||||
else
|
||||
// When entering non accepting SCC don't create any braces
|
||||
ss.update_succ({ /* no braces */ }, t.dst, { /* empty */ });
|
||||
else
|
||||
ss.update_succ(node.second, t.dst, t.acc);
|
||||
assert(ss.nb_braces_.size() == ss.is_green_.size());
|
||||
}
|
||||
}
|
||||
if (use_simulation)
|
||||
ss.merge_redundant_states(implications, scc, is_connected);
|
||||
|
|
@ -287,52 +287,52 @@ namespace spot
|
|||
|
||||
void
|
||||
safra_state::compute_succs(const const_twa_graph_ptr& aut,
|
||||
succs_t& res,
|
||||
const scc_info& scc,
|
||||
const std::map<int, bdd>& implications,
|
||||
const std::vector<bool>& is_connected,
|
||||
std::unordered_map<bdd, unsigned, bdd_hash>&
|
||||
bdd2num,
|
||||
std::vector<bdd>& all_bdds,
|
||||
bool use_scc,
|
||||
bool use_simulation,
|
||||
bool use_stutter) const
|
||||
succs_t& res,
|
||||
const scc_info& scc,
|
||||
const std::map<int, bdd>& implications,
|
||||
const std::vector<bool>& is_connected,
|
||||
std::unordered_map<bdd, unsigned, bdd_hash>&
|
||||
bdd2num,
|
||||
std::vector<bdd>& all_bdds,
|
||||
bool use_scc,
|
||||
bool use_simulation,
|
||||
bool use_stutter) const
|
||||
{
|
||||
for (auto& ap: all_bdds)
|
||||
{
|
||||
safra_state ss = *this;
|
||||
safra_state ss = *this;
|
||||
|
||||
if (use_stutter && aut->prop_stutter_invariant())
|
||||
{
|
||||
std::vector<color_t> colors;
|
||||
unsigned int counter = 0;
|
||||
std::map<safra_state, unsigned int> safra2id;
|
||||
bool stop = false;
|
||||
while (!stop)
|
||||
{
|
||||
auto pair = safra2id.insert({ss, counter++});
|
||||
// insert should never fail
|
||||
assert(pair.second);
|
||||
ss = ss.compute_succ(aut, ap, scc, implications, is_connected,
|
||||
use_scc, use_simulation);
|
||||
colors.push_back(ss.color_);
|
||||
stop = safra2id.find(ss) != safra2id.end();
|
||||
}
|
||||
// Add color of final transition that loops back
|
||||
colors.push_back(ss.color_);
|
||||
unsigned int loop_start = safra2id[ss];
|
||||
for (auto& min: safra2id)
|
||||
{
|
||||
if (min.second >= loop_start && ss < min.first)
|
||||
ss = min.first;
|
||||
}
|
||||
ss.color_ = *std::min_element(colors.begin(), colors.end());
|
||||
}
|
||||
else
|
||||
ss = compute_succ(aut, ap, scc, implications, is_connected,
|
||||
use_scc, use_simulation);
|
||||
unsigned bdd_idx = bdd2num[ap];
|
||||
res.emplace_back(ss, bdd_idx);
|
||||
if (use_stutter && aut->prop_stutter_invariant())
|
||||
{
|
||||
std::vector<color_t> colors;
|
||||
unsigned int counter = 0;
|
||||
std::map<safra_state, unsigned int> safra2id;
|
||||
bool stop = false;
|
||||
while (!stop)
|
||||
{
|
||||
auto pair = safra2id.insert({ss, counter++});
|
||||
// insert should never fail
|
||||
assert(pair.second);
|
||||
ss = ss.compute_succ(aut, ap, scc, implications, is_connected,
|
||||
use_scc, use_simulation);
|
||||
colors.push_back(ss.color_);
|
||||
stop = safra2id.find(ss) != safra2id.end();
|
||||
}
|
||||
// Add color of final transition that loops back
|
||||
colors.push_back(ss.color_);
|
||||
unsigned int loop_start = safra2id[ss];
|
||||
for (auto& min: safra2id)
|
||||
{
|
||||
if (min.second >= loop_start && ss < min.first)
|
||||
ss = min.first;
|
||||
}
|
||||
ss.color_ = *std::min_element(colors.begin(), colors.end());
|
||||
}
|
||||
else
|
||||
ss = compute_succ(aut, ap, scc, implications, is_connected,
|
||||
use_scc, use_simulation);
|
||||
unsigned bdd_idx = bdd2num[ap];
|
||||
res.emplace_back(ss, bdd_idx);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -350,12 +350,12 @@ namespace spot
|
|||
continue;
|
||||
// index to see if there is a path from scc2 -> scc1
|
||||
unsigned idx = scc.scc_count() * scc.scc_of(n2.first) +
|
||||
scc.scc_of(n1.first);
|
||||
scc.scc_of(n1.first);
|
||||
if (bdd_implies(implications.at(n1.first),
|
||||
implications.at(n2.first)) && !is_connected[idx])
|
||||
{
|
||||
to_remove.push_back(n1.first);
|
||||
}
|
||||
{
|
||||
to_remove.push_back(n1.first);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (auto& n: to_remove)
|
||||
|
|
@ -442,8 +442,8 @@ namespace spot
|
|||
|
||||
void
|
||||
node_helper::truncate_braces(std::vector<brace_t>& braces,
|
||||
const std::vector<unsigned>& rem_succ_of,
|
||||
std::vector<size_t>& nb_braces)
|
||||
const std::vector<unsigned>& rem_succ_of,
|
||||
std::vector<size_t>& nb_braces)
|
||||
{
|
||||
for (unsigned idx = 0; idx < braces.size(); ++idx)
|
||||
{
|
||||
|
|
@ -582,8 +582,8 @@ namespace spot
|
|||
|
||||
twa_graph_ptr
|
||||
tgba_determinize(const const_twa_graph_ptr& a,
|
||||
bool pretty_print, bool use_scc,
|
||||
bool use_simulation, bool use_stutter)
|
||||
bool pretty_print, bool use_scc,
|
||||
bool use_simulation, bool use_stutter)
|
||||
{
|
||||
if (a->prop_deterministic())
|
||||
return std::const_pointer_cast<twa_graph>(a);
|
||||
|
|
@ -642,10 +642,10 @@ namespace spot
|
|||
res->copy_ap_of(aut);
|
||||
res->prop_copy(aut,
|
||||
{ false, // state based
|
||||
false, // inherently_weak
|
||||
false, // deterministic
|
||||
true // stutter inv
|
||||
});
|
||||
false, // inherently_weak
|
||||
false, // deterministic
|
||||
true // stutter inv
|
||||
});
|
||||
|
||||
// Given a safra_state get its associated state in output automata.
|
||||
// Required to create new edges from 2 safra-state
|
||||
|
|
@ -676,13 +676,13 @@ namespace spot
|
|||
if (s.first.nodes_.empty())
|
||||
continue;
|
||||
auto i = seen.find(s.first);
|
||||
unsigned dst_num;
|
||||
if (i != seen.end())
|
||||
{
|
||||
dst_num = i->second;
|
||||
}
|
||||
else
|
||||
{
|
||||
unsigned dst_num;
|
||||
if (i != seen.end())
|
||||
{
|
||||
dst_num = i->second;
|
||||
}
|
||||
else
|
||||
{
|
||||
dst_num = res->new_state();
|
||||
todo.push_back(s.first);
|
||||
seen.insert(std::make_pair(s.first, dst_num));
|
||||
|
|
@ -690,7 +690,7 @@ namespace spot
|
|||
if (s.first.color_ != -1U)
|
||||
{
|
||||
res->new_edge(src_num, dst_num, num2bdd[s.second],
|
||||
{s.first.color_});
|
||||
{s.first.color_});
|
||||
// We only care about green acc which are odd
|
||||
if (s.first.color_ % 2 == 1)
|
||||
sets = std::max(s.first.color_ + 1, sets);
|
||||
|
|
|
|||
|
|
@ -74,8 +74,8 @@ namespace spot
|
|||
/// possible.)
|
||||
SPOT_API twa_graph_ptr
|
||||
tgba_determinize(const const_twa_graph_ptr& aut,
|
||||
bool pretty_print = false,
|
||||
bool use_scc = true,
|
||||
bool use_simulation = true,
|
||||
bool use_stutter = true);
|
||||
bool pretty_print = false,
|
||||
bool use_scc = true,
|
||||
bool use_simulation = true,
|
||||
bool use_stutter = true);
|
||||
}
|
||||
|
|
|
|||
1094
spot/twaalgos/dot.cc
1094
spot/twaalgos/dot.cc
File diff suppressed because it is too large
Load diff
|
|
@ -42,6 +42,6 @@ namespace spot
|
|||
/// 'a' shows the acceptance, 'k' uses state-based labels if possible.
|
||||
SPOT_API std::ostream&
|
||||
print_dot(std::ostream& os,
|
||||
const const_twa_ptr& g,
|
||||
const char* options = nullptr);
|
||||
const const_twa_ptr& g,
|
||||
const char* options = nullptr);
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -41,8 +41,8 @@ namespace spot
|
|||
/// states is found, a null pointer
|
||||
SPOT_API twa_graph_ptr
|
||||
dtba_sat_synthetize(const const_twa_graph_ptr& a,
|
||||
int target_state_number,
|
||||
bool state_based = false);
|
||||
int target_state_number,
|
||||
bool state_based = false);
|
||||
|
||||
/// \brief Attempt to minimize a deterministic TBA with a SAT solver.
|
||||
///
|
||||
|
|
@ -52,8 +52,8 @@ namespace spot
|
|||
/// If no smaller TBA exist, this returns a null pointer.
|
||||
SPOT_API twa_graph_ptr
|
||||
dtba_sat_minimize(const const_twa_graph_ptr& a,
|
||||
bool state_based = false,
|
||||
int max_states = -1);
|
||||
bool state_based = false,
|
||||
int max_states = -1);
|
||||
|
||||
/// \brief Attempt to minimize a deterministic TBA with a SAT solver.
|
||||
///
|
||||
|
|
@ -63,6 +63,6 @@ namespace spot
|
|||
/// If no smaller TBA exist, this returns a null pointer.
|
||||
SPOT_API twa_graph_ptr
|
||||
dtba_sat_minimize_dichotomy(const const_twa_graph_ptr& a,
|
||||
bool state_based = false,
|
||||
int max_states = -1);
|
||||
bool state_based = false,
|
||||
int max_states = -1);
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -49,11 +49,11 @@ namespace spot
|
|||
/// returned.
|
||||
SPOT_API twa_graph_ptr
|
||||
dtwa_sat_synthetize(const const_twa_graph_ptr& a,
|
||||
unsigned target_acc_number,
|
||||
const acc_cond::acc_code& target_acc,
|
||||
int target_state_number,
|
||||
bool state_based = false,
|
||||
bool colored = false);
|
||||
unsigned target_acc_number,
|
||||
const acc_cond::acc_code& target_acc,
|
||||
int target_state_number,
|
||||
bool state_based = false,
|
||||
bool colored = false);
|
||||
|
||||
/// \brief Attempt to minimize a deterministic TωA with a SAT solver.
|
||||
///
|
||||
|
|
@ -63,11 +63,11 @@ namespace spot
|
|||
/// If no smaller TGBA exists, this returns a null pointer.
|
||||
SPOT_API twa_graph_ptr
|
||||
dtwa_sat_minimize(const const_twa_graph_ptr& a,
|
||||
unsigned target_acc_number,
|
||||
const acc_cond::acc_code& target_acc,
|
||||
bool state_based = false,
|
||||
int max_states = -1,
|
||||
bool colored = false);
|
||||
unsigned target_acc_number,
|
||||
const acc_cond::acc_code& target_acc,
|
||||
bool state_based = false,
|
||||
int max_states = -1,
|
||||
bool colored = false);
|
||||
|
||||
/// \brief Attempt to minimize a deterministic TωA with a SAT solver.
|
||||
///
|
||||
|
|
@ -77,11 +77,11 @@ namespace spot
|
|||
/// If no smaller TBA exist, this returns a null pointer.
|
||||
SPOT_API twa_graph_ptr
|
||||
dtwa_sat_minimize_dichotomy(const const_twa_graph_ptr& a,
|
||||
unsigned target_acc_number,
|
||||
const acc_cond::acc_code& target_acc,
|
||||
bool state_based = false,
|
||||
int max_states = -1,
|
||||
bool colored = false);
|
||||
unsigned target_acc_number,
|
||||
const acc_cond::acc_code& target_acc,
|
||||
bool state_based = false,
|
||||
int max_states = -1,
|
||||
bool colored = false);
|
||||
|
||||
/// \brief High-level interface to SAT-based minimization
|
||||
///
|
||||
|
|
|
|||
|
|
@ -120,24 +120,24 @@ namespace spot
|
|||
{
|
||||
const char* name;
|
||||
emptiness_check_ptr(*construct)(const const_twa_ptr&,
|
||||
spot::option_map);
|
||||
spot::option_map);
|
||||
unsigned int min_acc;
|
||||
unsigned int max_acc;
|
||||
};
|
||||
|
||||
ec_algo ec_algos[] =
|
||||
{
|
||||
{ "Cou99", couvreur99, 0, -1U },
|
||||
{ "CVWY90", magic_search, 0, 1 },
|
||||
{ "GV04", explicit_gv04_check, 0, 1 },
|
||||
{ "SE05", se05, 0, 1 },
|
||||
{ "Tau03", explicit_tau03_search, 1, -1U },
|
||||
{ "Tau03_opt", explicit_tau03_opt_search, 0, -1U },
|
||||
{ "Cou99", couvreur99, 0, -1U },
|
||||
{ "CVWY90", magic_search, 0, 1 },
|
||||
{ "GV04", explicit_gv04_check, 0, 1 },
|
||||
{ "SE05", se05, 0, 1 },
|
||||
{ "Tau03", explicit_tau03_search, 1, -1U },
|
||||
{ "Tau03_opt", explicit_tau03_opt_search, 0, -1U },
|
||||
};
|
||||
}
|
||||
|
||||
emptiness_check_instantiator::emptiness_check_instantiator(option_map o,
|
||||
void* i)
|
||||
void* i)
|
||||
: o_(o), info_(i)
|
||||
{
|
||||
}
|
||||
|
|
@ -171,21 +171,21 @@ namespace spot
|
|||
option_map o;
|
||||
if (opt_str)
|
||||
{
|
||||
const char* opt_start = opt_str + 1;
|
||||
const char* opt_end = strchr(opt_start, ')');
|
||||
if (!opt_end)
|
||||
{
|
||||
*err = opt_start;
|
||||
return nullptr;
|
||||
}
|
||||
std::string opt(opt_start, opt_end);
|
||||
const char* opt_start = opt_str + 1;
|
||||
const char* opt_end = strchr(opt_start, ')');
|
||||
if (!opt_end)
|
||||
{
|
||||
*err = opt_start;
|
||||
return nullptr;
|
||||
}
|
||||
std::string opt(opt_start, opt_end);
|
||||
|
||||
const char* res = o.parse_options(opt.c_str());
|
||||
if (res)
|
||||
{
|
||||
*err = opt.c_str() - res + opt_start;
|
||||
return nullptr;
|
||||
}
|
||||
const char* res = o.parse_options(opt.c_str());
|
||||
if (res)
|
||||
{
|
||||
*err = opt.c_str() - res + opt_start;
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
if (!opt_str)
|
||||
|
|
@ -200,19 +200,19 @@ namespace spot
|
|||
ec_algo* info = ec_algos;
|
||||
for (unsigned i = 0; i < sizeof(ec_algos)/sizeof(*ec_algos); ++i, ++info)
|
||||
if (n == info->name)
|
||||
{
|
||||
*err = nullptr;
|
||||
{
|
||||
*err = nullptr;
|
||||
|
||||
struct emptiness_check_instantiator_aux:
|
||||
struct emptiness_check_instantiator_aux:
|
||||
public emptiness_check_instantiator
|
||||
{
|
||||
emptiness_check_instantiator_aux(option_map o, void* i):
|
||||
emptiness_check_instantiator(o, i)
|
||||
{
|
||||
}
|
||||
};
|
||||
return std::make_shared<emptiness_check_instantiator_aux>(o, info);
|
||||
}
|
||||
{
|
||||
emptiness_check_instantiator_aux(option_map o, void* i):
|
||||
emptiness_check_instantiator(o, i)
|
||||
{
|
||||
}
|
||||
};
|
||||
return std::make_shared<emptiness_check_instantiator_aux>(o, info);
|
||||
}
|
||||
*err = name;
|
||||
return nullptr;
|
||||
}
|
||||
|
|
@ -233,13 +233,13 @@ namespace spot
|
|||
aut = run.aut;
|
||||
for (step s : run.prefix)
|
||||
{
|
||||
s.s = s.s->clone();
|
||||
prefix.push_back(s);
|
||||
s.s = s.s->clone();
|
||||
prefix.push_back(s);
|
||||
}
|
||||
for (step s : run.cycle)
|
||||
{
|
||||
s.s = s.s->clone();
|
||||
cycle.push_back(s);
|
||||
s.s = s.s->clone();
|
||||
cycle.push_back(s);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -248,8 +248,8 @@ namespace spot
|
|||
{
|
||||
if (&run != this)
|
||||
{
|
||||
this->~twa_run();
|
||||
new(this) twa_run(run);
|
||||
this->~twa_run();
|
||||
new(this) twa_run(run);
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
|
@ -265,7 +265,7 @@ namespace spot
|
|||
os << " " << a->format_state(st.s) << "\n | ";
|
||||
bdd_print_formula(os, d, st.label);
|
||||
if (st.acc)
|
||||
os << '\t' << st.acc;
|
||||
os << '\t' << st.acc;
|
||||
os << '\n';
|
||||
};
|
||||
|
||||
|
|
@ -308,7 +308,7 @@ namespace spot
|
|||
const state*
|
||||
search(const state* start, twa_run::steps& l)
|
||||
{
|
||||
return this->bfs_steps::search(filter(start), l);
|
||||
return this->bfs_steps::search(filter(start), l);
|
||||
}
|
||||
|
||||
const state*
|
||||
|
|
@ -358,7 +358,7 @@ namespace spot
|
|||
do
|
||||
{
|
||||
assert(seg != cycle.begin());
|
||||
--seg;
|
||||
--seg;
|
||||
seen_acc |= seg->acc;
|
||||
}
|
||||
while (!a->acc().accepting(seen_acc));
|
||||
|
|
@ -372,10 +372,10 @@ namespace spot
|
|||
assert(seg != cycle.end());
|
||||
seen_acc |= seg->acc;
|
||||
|
||||
twa_run::step st = { seg->s->clone(), seg->label, seg->acc };
|
||||
res->cycle.push_back(st);
|
||||
twa_run::step st = { seg->s->clone(), seg->label, seg->acc };
|
||||
res->cycle.push_back(st);
|
||||
|
||||
++seg;
|
||||
++seg;
|
||||
}
|
||||
while (!a->acc().accepting(seen_acc));
|
||||
segment_next = seg == cycle.end() ? cycle.front().s : seg->s;
|
||||
|
|
@ -388,7 +388,7 @@ namespace spot
|
|||
const state* s = shpath.search(segment_next->clone(), res->cycle);
|
||||
ss.clear();
|
||||
assert(s->compare(segment_start) == 0);
|
||||
(void)s;
|
||||
(void)s;
|
||||
}
|
||||
|
||||
// Compute the prefix: it's the shortest path from the initial
|
||||
|
|
@ -396,7 +396,7 @@ namespace spot
|
|||
|
||||
// Register all states from the cycle as target of the BFS.
|
||||
for (twa_run::steps::const_iterator i = res->cycle.begin();
|
||||
i != res->cycle.end(); ++i)
|
||||
i != res->cycle.end(); ++i)
|
||||
ss.insert(i->s);
|
||||
|
||||
const state* prefix_start = a->get_init_state();
|
||||
|
|
@ -409,27 +409,27 @@ namespace spot
|
|||
state_set::const_iterator ps = ss.find(prefix_start);
|
||||
if (ps != ss.end())
|
||||
{
|
||||
// The initial state is on the cycle.
|
||||
prefix_start->destroy();
|
||||
cycle_entry_point = *ps;
|
||||
// The initial state is on the cycle.
|
||||
prefix_start->destroy();
|
||||
cycle_entry_point = *ps;
|
||||
}
|
||||
else
|
||||
{
|
||||
// This initial state is outside the cycle. Compute the prefix.
|
||||
// This initial state is outside the cycle. Compute the prefix.
|
||||
cycle_entry_point = shpath.search(prefix_start, res->prefix);
|
||||
}
|
||||
|
||||
// Locate cycle_entry_point on the cycle.
|
||||
twa_run::steps::iterator cycle_ep_it;
|
||||
for (cycle_ep_it = res->cycle.begin();
|
||||
cycle_ep_it != res->cycle.end()
|
||||
&& cycle_entry_point->compare(cycle_ep_it->s); ++cycle_ep_it)
|
||||
cycle_ep_it != res->cycle.end()
|
||||
&& cycle_entry_point->compare(cycle_ep_it->s); ++cycle_ep_it)
|
||||
continue;
|
||||
assert(cycle_ep_it != res->cycle.end());
|
||||
|
||||
// Now shift the cycle so it starts on cycle_entry_point.
|
||||
res->cycle.splice(res->cycle.end(), res->cycle,
|
||||
res->cycle.begin(), cycle_ep_it);
|
||||
res->cycle.begin(), cycle_ep_it);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
|
@ -446,195 +446,195 @@ namespace spot
|
|||
|
||||
if (prefix.empty())
|
||||
{
|
||||
l = &cycle;
|
||||
in = "cycle";
|
||||
if (!debug)
|
||||
os << "No prefix.\nCycle:\n";
|
||||
l = &cycle;
|
||||
in = "cycle";
|
||||
if (!debug)
|
||||
os << "No prefix.\nCycle:\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
l = &prefix;
|
||||
in = "prefix";
|
||||
if (!debug)
|
||||
os << "Prefix:\n";
|
||||
l = &prefix;
|
||||
in = "prefix";
|
||||
if (!debug)
|
||||
os << "Prefix:\n";
|
||||
}
|
||||
|
||||
twa_run::steps::const_iterator i = l->begin();
|
||||
|
||||
if (s->compare(i->s))
|
||||
{
|
||||
if (debug)
|
||||
os << "ERROR: First state of run (in " << in << "): "
|
||||
<< aut->format_state(i->s)
|
||||
<< "\ndoes not match initial state of automata: "
|
||||
<< aut->format_state(s) << '\n';
|
||||
s->destroy();
|
||||
return false;
|
||||
if (debug)
|
||||
os << "ERROR: First state of run (in " << in << "): "
|
||||
<< aut->format_state(i->s)
|
||||
<< "\ndoes not match initial state of automata: "
|
||||
<< aut->format_state(s) << '\n';
|
||||
s->destroy();
|
||||
return false;
|
||||
}
|
||||
|
||||
for (; i != l->end(); ++serial)
|
||||
{
|
||||
if (debug)
|
||||
{
|
||||
// Keep track of the serial associated to each state so we
|
||||
// can note duplicate states and make the replay easier to read.
|
||||
auto o = seen.find(s);
|
||||
std::ostringstream msg;
|
||||
if (o != seen.end())
|
||||
{
|
||||
for (auto d: o->second)
|
||||
msg << " == " << d;
|
||||
o->second.insert(serial);
|
||||
s->destroy();
|
||||
s = o->first;
|
||||
}
|
||||
else
|
||||
{
|
||||
seen[s].insert(serial);
|
||||
}
|
||||
os << "state " << serial << " in " << in << msg.str() << ": ";
|
||||
}
|
||||
else
|
||||
{
|
||||
os << " ";
|
||||
}
|
||||
os << aut->format_state(s) << '\n';
|
||||
if (debug)
|
||||
{
|
||||
// Keep track of the serial associated to each state so we
|
||||
// can note duplicate states and make the replay easier to read.
|
||||
auto o = seen.find(s);
|
||||
std::ostringstream msg;
|
||||
if (o != seen.end())
|
||||
{
|
||||
for (auto d: o->second)
|
||||
msg << " == " << d;
|
||||
o->second.insert(serial);
|
||||
s->destroy();
|
||||
s = o->first;
|
||||
}
|
||||
else
|
||||
{
|
||||
seen[s].insert(serial);
|
||||
}
|
||||
os << "state " << serial << " in " << in << msg.str() << ": ";
|
||||
}
|
||||
else
|
||||
{
|
||||
os << " ";
|
||||
}
|
||||
os << aut->format_state(s) << '\n';
|
||||
|
||||
// expected outgoing transition
|
||||
bdd label = i->label;
|
||||
acc_cond::mark_t acc = i->acc;
|
||||
// expected outgoing transition
|
||||
bdd label = i->label;
|
||||
acc_cond::mark_t acc = i->acc;
|
||||
|
||||
// compute the next expected state
|
||||
const state* next;
|
||||
++i;
|
||||
if (i != l->end())
|
||||
{
|
||||
next = i->s;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (l == &prefix)
|
||||
{
|
||||
l = &cycle;
|
||||
in = "cycle";
|
||||
i = l->begin();
|
||||
if (!debug)
|
||||
os << "Cycle:\n";
|
||||
}
|
||||
next = l->begin()->s;
|
||||
}
|
||||
// compute the next expected state
|
||||
const state* next;
|
||||
++i;
|
||||
if (i != l->end())
|
||||
{
|
||||
next = i->s;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (l == &prefix)
|
||||
{
|
||||
l = &cycle;
|
||||
in = "cycle";
|
||||
i = l->begin();
|
||||
if (!debug)
|
||||
os << "Cycle:\n";
|
||||
}
|
||||
next = l->begin()->s;
|
||||
}
|
||||
|
||||
// browse the actual outgoing transitions
|
||||
twa_succ_iterator* j = aut->succ_iter(s);
|
||||
// When not debugging, S is not used as key in SEEN, so we can
|
||||
// destroy it right now.
|
||||
if (!debug)
|
||||
s->destroy();
|
||||
if (j->first())
|
||||
do
|
||||
{
|
||||
if (j->cond() != label
|
||||
|| j->acc() != acc)
|
||||
continue;
|
||||
// browse the actual outgoing transitions
|
||||
twa_succ_iterator* j = aut->succ_iter(s);
|
||||
// When not debugging, S is not used as key in SEEN, so we can
|
||||
// destroy it right now.
|
||||
if (!debug)
|
||||
s->destroy();
|
||||
if (j->first())
|
||||
do
|
||||
{
|
||||
if (j->cond() != label
|
||||
|| j->acc() != acc)
|
||||
continue;
|
||||
|
||||
const state* s2 = j->dst();
|
||||
if (s2->compare(next))
|
||||
{
|
||||
s2->destroy();
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
s = s2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
while (j->next());
|
||||
if (j->done())
|
||||
{
|
||||
if (debug)
|
||||
{
|
||||
os << "ERROR: no transition with label="
|
||||
<< bdd_format_formula(aut->get_dict(), label)
|
||||
<< " and acc=" << aut->acc().format(acc)
|
||||
<< " leaving state " << serial
|
||||
<< " for state " << aut->format_state(next) << '\n'
|
||||
<< "The following transitions leave state " << serial
|
||||
<< ":\n";
|
||||
if (j->first())
|
||||
do
|
||||
{
|
||||
const state* s2 = j->dst();
|
||||
os << " * label="
|
||||
<< bdd_format_formula(aut->get_dict(),
|
||||
j->cond())
|
||||
<< " and acc="
|
||||
<< (aut->acc().format
|
||||
(j->acc()))
|
||||
<< " going to " << aut->format_state(s2) << '\n';
|
||||
s2->destroy();
|
||||
}
|
||||
while (j->next());
|
||||
}
|
||||
aut->release_iter(j);
|
||||
s->destroy();
|
||||
return false;
|
||||
}
|
||||
if (debug)
|
||||
{
|
||||
os << "transition with label="
|
||||
<< bdd_format_formula(aut->get_dict(), label)
|
||||
<< " and acc=" << aut->acc().format(acc)
|
||||
<< std::endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
os << " | ";
|
||||
bdd_print_formula(os, aut->get_dict(), label);
|
||||
os << '\t';
|
||||
aut->acc().format(acc);
|
||||
os << std::endl;
|
||||
}
|
||||
aut->release_iter(j);
|
||||
const state* s2 = j->dst();
|
||||
if (s2->compare(next))
|
||||
{
|
||||
s2->destroy();
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
s = s2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
while (j->next());
|
||||
if (j->done())
|
||||
{
|
||||
if (debug)
|
||||
{
|
||||
os << "ERROR: no transition with label="
|
||||
<< bdd_format_formula(aut->get_dict(), label)
|
||||
<< " and acc=" << aut->acc().format(acc)
|
||||
<< " leaving state " << serial
|
||||
<< " for state " << aut->format_state(next) << '\n'
|
||||
<< "The following transitions leave state " << serial
|
||||
<< ":\n";
|
||||
if (j->first())
|
||||
do
|
||||
{
|
||||
const state* s2 = j->dst();
|
||||
os << " * label="
|
||||
<< bdd_format_formula(aut->get_dict(),
|
||||
j->cond())
|
||||
<< " and acc="
|
||||
<< (aut->acc().format
|
||||
(j->acc()))
|
||||
<< " going to " << aut->format_state(s2) << '\n';
|
||||
s2->destroy();
|
||||
}
|
||||
while (j->next());
|
||||
}
|
||||
aut->release_iter(j);
|
||||
s->destroy();
|
||||
return false;
|
||||
}
|
||||
if (debug)
|
||||
{
|
||||
os << "transition with label="
|
||||
<< bdd_format_formula(aut->get_dict(), label)
|
||||
<< " and acc=" << aut->acc().format(acc)
|
||||
<< std::endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
os << " | ";
|
||||
bdd_print_formula(os, aut->get_dict(), label);
|
||||
os << '\t';
|
||||
aut->acc().format(acc);
|
||||
os << std::endl;
|
||||
}
|
||||
aut->release_iter(j);
|
||||
|
||||
// Sum acceptance conditions.
|
||||
//
|
||||
// (Beware l and i designate the next step to consider.
|
||||
// Therefore if i is at the beginning of the cycle, `acc'
|
||||
// contains the acceptance conditions of the last transition
|
||||
// in the prefix; we should not account it.)
|
||||
if (l == &cycle && i != l->begin())
|
||||
{
|
||||
all_acc |= acc;
|
||||
if (!all_acc_seen && aut->acc().accepting(all_acc))
|
||||
{
|
||||
all_acc_seen = true;
|
||||
if (debug)
|
||||
os << "all acceptance conditions ("
|
||||
<< aut->acc().format(all_acc)
|
||||
<< ") have been seen\n";
|
||||
}
|
||||
}
|
||||
// Sum acceptance conditions.
|
||||
//
|
||||
// (Beware l and i designate the next step to consider.
|
||||
// Therefore if i is at the beginning of the cycle, `acc'
|
||||
// contains the acceptance conditions of the last transition
|
||||
// in the prefix; we should not account it.)
|
||||
if (l == &cycle && i != l->begin())
|
||||
{
|
||||
all_acc |= acc;
|
||||
if (!all_acc_seen && aut->acc().accepting(all_acc))
|
||||
{
|
||||
all_acc_seen = true;
|
||||
if (debug)
|
||||
os << "all acceptance conditions ("
|
||||
<< aut->acc().format(all_acc)
|
||||
<< ") have been seen\n";
|
||||
}
|
||||
}
|
||||
}
|
||||
s->destroy();
|
||||
if (!aut->acc().accepting(all_acc))
|
||||
{
|
||||
if (debug)
|
||||
os << "ERROR: The cycle's acceptance conditions ("
|
||||
<< aut->acc().format(all_acc)
|
||||
<< ") do not\nmatch those of the automaton ("
|
||||
<< aut->acc().format(aut->acc().all_sets())
|
||||
<< ")\n";
|
||||
return false;
|
||||
if (debug)
|
||||
os << "ERROR: The cycle's acceptance conditions ("
|
||||
<< aut->acc().format(all_acc)
|
||||
<< ") do not\nmatch those of the automaton ("
|
||||
<< aut->acc().format(aut->acc().all_sets())
|
||||
<< ")\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
auto o = seen.begin();
|
||||
while (o != seen.end())
|
||||
{
|
||||
// Advance the iterator before deleting the "key" pointer.
|
||||
const state* ptr = o->first;
|
||||
++o;
|
||||
ptr->destroy();
|
||||
// Advance the iterator before deleting the "key" pointer.
|
||||
const state* ptr = o->first;
|
||||
++o;
|
||||
ptr->destroy();
|
||||
}
|
||||
|
||||
return true;
|
||||
|
|
@ -655,33 +655,33 @@ namespace spot
|
|||
auto e = l->end();
|
||||
for (auto i = l->begin(); i != e;)
|
||||
{
|
||||
bdd label = i->label;
|
||||
acc_cond::mark_t acc = i->acc;
|
||||
unsigned dst;
|
||||
++i;
|
||||
if (i != e)
|
||||
{
|
||||
dst = a->state_number(i->s);
|
||||
}
|
||||
else if (l == &prefix)
|
||||
{
|
||||
l = &cycle;
|
||||
i = l->begin();
|
||||
e = l->end();
|
||||
dst = a->state_number(i->s);
|
||||
}
|
||||
else
|
||||
{
|
||||
dst = a->state_number(l->begin()->s);
|
||||
}
|
||||
bdd label = i->label;
|
||||
acc_cond::mark_t acc = i->acc;
|
||||
unsigned dst;
|
||||
++i;
|
||||
if (i != e)
|
||||
{
|
||||
dst = a->state_number(i->s);
|
||||
}
|
||||
else if (l == &prefix)
|
||||
{
|
||||
l = &cycle;
|
||||
i = l->begin();
|
||||
e = l->end();
|
||||
dst = a->state_number(i->s);
|
||||
}
|
||||
else
|
||||
{
|
||||
dst = a->state_number(l->begin()->s);
|
||||
}
|
||||
|
||||
for (auto& t: a->out(src))
|
||||
if (t.dst == dst && t.cond == label && t.acc == acc)
|
||||
{
|
||||
(*h)[a->get_graph().index_of_edge(t)] = color;
|
||||
break;
|
||||
}
|
||||
src = dst;
|
||||
for (auto& t: a->out(src))
|
||||
if (t.dst == dst && t.cond == label && t.acc == acc)
|
||||
{
|
||||
(*h)[a->get_graph().index_of_edge(t)] = color;
|
||||
break;
|
||||
}
|
||||
src = dst;
|
||||
}
|
||||
a->set_named_prop("highlight-edges", h);
|
||||
}
|
||||
|
|
@ -717,7 +717,7 @@ namespace spot
|
|||
{
|
||||
// expected outgoing transition
|
||||
bdd label = i->label;
|
||||
acc_cond::mark_t acc = i->acc;
|
||||
acc_cond::mark_t acc = i->acc;
|
||||
|
||||
// compute the next expected state
|
||||
const state* next;
|
||||
|
|
@ -737,9 +737,9 @@ namespace spot
|
|||
}
|
||||
|
||||
// browse the actual outgoing transitions and
|
||||
// look for next;
|
||||
const state* the_next = nullptr;
|
||||
for (auto j: aut->succ(s))
|
||||
// look for next;
|
||||
const state* the_next = nullptr;
|
||||
for (auto j: aut->succ(s))
|
||||
{
|
||||
if (j->cond() != label
|
||||
|| j->acc() != acc)
|
||||
|
|
@ -748,27 +748,27 @@ namespace spot
|
|||
const state* s2 = j->dst();
|
||||
if (s2->compare(next) == 0)
|
||||
{
|
||||
the_next = s2;
|
||||
break;
|
||||
}
|
||||
s2->destroy();
|
||||
the_next = s2;
|
||||
break;
|
||||
}
|
||||
s2->destroy();
|
||||
}
|
||||
assert(res);
|
||||
s->destroy();
|
||||
s = the_next;
|
||||
s = the_next;
|
||||
|
||||
|
||||
auto p = seen.emplace(next, 0);
|
||||
if (p.second)
|
||||
p.first->second = res->new_state();
|
||||
dst = p.first->second;
|
||||
auto p = seen.emplace(next, 0);
|
||||
if (p.second)
|
||||
p.first->second = res->new_state();
|
||||
dst = p.first->second;
|
||||
|
||||
res->new_edge(src, dst, label, acc);
|
||||
src = dst;
|
||||
res->new_edge(src, dst, label, acc);
|
||||
src = dst;
|
||||
|
||||
// Sum acceptance conditions.
|
||||
if (l == &cycle && i != l->begin())
|
||||
seen_acc |= acc;
|
||||
seen_acc |= acc;
|
||||
}
|
||||
|
||||
s->destroy();
|
||||
|
|
|
|||
|
|
@ -78,7 +78,7 @@ namespace spot
|
|||
{
|
||||
public:
|
||||
emptiness_check_result(const const_twa_ptr& a,
|
||||
option_map o = option_map())
|
||||
option_map o = option_map())
|
||||
: a_(a), o_(o)
|
||||
{
|
||||
}
|
||||
|
|
@ -126,8 +126,8 @@ namespace spot
|
|||
/// Notify option updates.
|
||||
virtual void options_updated(const option_map& old);
|
||||
|
||||
const_twa_ptr a_; ///< The automaton.
|
||||
option_map o_; ///< The options.
|
||||
const_twa_ptr a_; ///< The automaton.
|
||||
option_map o_; ///< The options.
|
||||
};
|
||||
|
||||
typedef std::shared_ptr<emptiness_check_result> emptiness_check_result_ptr;
|
||||
|
|
@ -192,8 +192,8 @@ namespace spot
|
|||
virtual void options_updated(const option_map& old);
|
||||
|
||||
protected:
|
||||
const_twa_ptr a_; ///< The automaton.
|
||||
option_map o_; ///< The options
|
||||
const_twa_ptr a_; ///< The automaton.
|
||||
option_map o_; ///< The options
|
||||
};
|
||||
|
||||
typedef std::shared_ptr<emptiness_check> emptiness_check_ptr;
|
||||
|
|
@ -270,7 +270,7 @@ namespace spot
|
|||
acc_cond::mark_t acc;
|
||||
|
||||
step(const state* s, bdd label, acc_cond::mark_t acc)
|
||||
: s(s), label(label), acc(acc)
|
||||
: s(s), label(label), acc(acc)
|
||||
{
|
||||
}
|
||||
step()
|
||||
|
|
|
|||
|
|
@ -75,14 +75,14 @@ namespace spot
|
|||
seteq(const unsigned_statistics& o)
|
||||
{
|
||||
if (!set)
|
||||
{
|
||||
for (auto& i: o.stats)
|
||||
stats[i.first] = (o.*i.second)();
|
||||
set = true;
|
||||
return true;
|
||||
}
|
||||
{
|
||||
for (auto& i: o.stats)
|
||||
stats[i.first] = (o.*i.second)();
|
||||
set = true;
|
||||
return true;
|
||||
}
|
||||
if (*this == o)
|
||||
return true;
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -94,13 +94,13 @@ namespace spot
|
|||
operator==(const unsigned_statistics_copy& o) const
|
||||
{
|
||||
for (auto& i: stats)
|
||||
{
|
||||
auto i2 = o.stats.find(i.first);
|
||||
if (i2 == o.stats.end())
|
||||
return false;
|
||||
if (i.second != i2->second)
|
||||
return false;
|
||||
}
|
||||
{
|
||||
auto i2 = o.stats.find(i.first);
|
||||
if (i2 == o.stats.end())
|
||||
return false;
|
||||
if (i.second != i2->second)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -125,13 +125,13 @@ namespace spot
|
|||
: states_(0), transitions_(0), depth_(0), max_depth_(0)
|
||||
{
|
||||
stats["states"] =
|
||||
static_cast<unsigned_statistics::unsigned_fun>(&ec_statistics::states);
|
||||
static_cast<unsigned_statistics::unsigned_fun>(&ec_statistics::states);
|
||||
stats["transitions"] =
|
||||
static_cast<unsigned_statistics::unsigned_fun>
|
||||
(&ec_statistics::transitions);
|
||||
static_cast<unsigned_statistics::unsigned_fun>
|
||||
(&ec_statistics::transitions);
|
||||
stats["max. depth"] =
|
||||
static_cast<unsigned_statistics::unsigned_fun>
|
||||
(&ec_statistics::max_depth);
|
||||
static_cast<unsigned_statistics::unsigned_fun>
|
||||
(&ec_statistics::max_depth);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -157,7 +157,7 @@ namespace spot
|
|||
{
|
||||
depth_ += n;
|
||||
if (depth_ > max_depth_)
|
||||
max_depth_ = depth_;
|
||||
max_depth_ = depth_;
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -192,10 +192,10 @@ namespace spot
|
|||
}
|
||||
|
||||
private :
|
||||
unsigned states_; /// number of disctint visited states
|
||||
unsigned transitions_; /// number of visited transitions
|
||||
unsigned depth_; /// maximal depth of the stack(s)
|
||||
unsigned max_depth_; /// maximal depth of the stack(s)
|
||||
unsigned states_; /// number of disctint visited states
|
||||
unsigned transitions_; /// number of visited transitions
|
||||
unsigned depth_; /// maximal depth of the stack(s)
|
||||
unsigned max_depth_; /// maximal depth of the stack(s)
|
||||
};
|
||||
|
||||
/// \brief Accepting Run Search statistics.
|
||||
|
|
@ -210,11 +210,11 @@ namespace spot
|
|||
: prefix_states_(0), cycle_states_(0)
|
||||
{
|
||||
stats["(non unique) states for prefix"] =
|
||||
static_cast<unsigned_statistics::unsigned_fun>
|
||||
(&ars_statistics::ars_prefix_states);
|
||||
static_cast<unsigned_statistics::unsigned_fun>
|
||||
(&ars_statistics::ars_prefix_states);
|
||||
stats["(non unique) states for cycle"] =
|
||||
static_cast<unsigned_statistics::unsigned_fun>
|
||||
(&ars_statistics::ars_cycle_states);
|
||||
static_cast<unsigned_statistics::unsigned_fun>
|
||||
(&ars_statistics::ars_cycle_states);
|
||||
}
|
||||
|
||||
void
|
||||
|
|
@ -242,8 +242,8 @@ namespace spot
|
|||
}
|
||||
|
||||
private:
|
||||
unsigned prefix_states_; /// states visited to construct the prefix
|
||||
unsigned cycle_states_; /// states visited to construct the cycle
|
||||
unsigned prefix_states_; /// states visited to construct the prefix
|
||||
unsigned cycle_states_; /// states visited to construct the cycle
|
||||
};
|
||||
|
||||
/// \brief Accepting Cycle Search Space statistics
|
||||
|
|
@ -257,8 +257,8 @@ namespace spot
|
|||
acss_statistics()
|
||||
{
|
||||
stats["search space states"] =
|
||||
static_cast<unsigned_statistics::unsigned_fun>
|
||||
(&acss_statistics::acss_states);
|
||||
static_cast<unsigned_statistics::unsigned_fun>
|
||||
(&acss_statistics::acss_states);
|
||||
}
|
||||
|
||||
virtual
|
||||
|
|
|
|||
|
|
@ -32,8 +32,8 @@ namespace spot
|
|||
{
|
||||
public:
|
||||
shortest_path(const state_set* t,
|
||||
const std::shared_ptr<const couvreur99_check_status>& ecs,
|
||||
couvreur99_check_result* r)
|
||||
const std::shared_ptr<const couvreur99_check_status>& ecs,
|
||||
couvreur99_check_result* r)
|
||||
: bfs_steps(ecs->aut), target(t), ecs(ecs), r(r)
|
||||
{
|
||||
}
|
||||
|
|
@ -41,22 +41,22 @@ namespace spot
|
|||
const state*
|
||||
search(const state* start, twa_run::steps& l)
|
||||
{
|
||||
return this->bfs_steps::search(filter(start), l);
|
||||
return this->bfs_steps::search(filter(start), l);
|
||||
}
|
||||
|
||||
const state*
|
||||
filter(const state* s)
|
||||
{
|
||||
r->inc_ars_prefix_states();
|
||||
auto i = ecs->h.find(s);
|
||||
s->destroy();
|
||||
// Ignore unknown states ...
|
||||
if (i == ecs->h.end())
|
||||
return nullptr;
|
||||
// ... as well as dead states.
|
||||
if (i->second == -1)
|
||||
return nullptr;
|
||||
return i->first;
|
||||
r->inc_ars_prefix_states();
|
||||
auto i = ecs->h.find(s);
|
||||
s->destroy();
|
||||
// Ignore unknown states ...
|
||||
if (i == ecs->h.end())
|
||||
return nullptr;
|
||||
// ... as well as dead states.
|
||||
if (i->second == -1)
|
||||
return nullptr;
|
||||
return i->first;
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
@ -87,7 +87,7 @@ namespace spot
|
|||
unsigned count = 0;
|
||||
for (auto i: ecs_->h)
|
||||
if (i.second >= scc_root)
|
||||
++count;
|
||||
++count;
|
||||
return count;
|
||||
}
|
||||
|
||||
|
|
@ -107,7 +107,7 @@ namespace spot
|
|||
// Register all states from the cycle as target of the BFS.
|
||||
state_set ss;
|
||||
for (twa_run::steps::const_iterator i = run_->cycle.begin();
|
||||
i != run_->cycle.end(); ++i)
|
||||
i != run_->cycle.end(); ++i)
|
||||
ss.insert(i->s);
|
||||
shortest_path shpath(&ss, ecs_, this);
|
||||
|
||||
|
|
@ -121,27 +121,27 @@ namespace spot
|
|||
state_set::const_iterator ps = ss.find(prefix_start);
|
||||
if (ps != ss.end())
|
||||
{
|
||||
// The initial state is on the cycle.
|
||||
prefix_start->destroy();
|
||||
cycle_entry_point = *ps;
|
||||
// The initial state is on the cycle.
|
||||
prefix_start->destroy();
|
||||
cycle_entry_point = *ps;
|
||||
}
|
||||
else
|
||||
{
|
||||
// This initial state is outside the cycle. Compute the prefix.
|
||||
// This initial state is outside the cycle. Compute the prefix.
|
||||
cycle_entry_point = shpath.search(prefix_start, run_->prefix);
|
||||
}
|
||||
|
||||
// Locate cycle_entry_point on the cycle.
|
||||
twa_run::steps::iterator cycle_ep_it;
|
||||
for (cycle_ep_it = run_->cycle.begin();
|
||||
cycle_ep_it != run_->cycle.end()
|
||||
&& cycle_entry_point->compare(cycle_ep_it->s); ++cycle_ep_it)
|
||||
cycle_ep_it != run_->cycle.end()
|
||||
&& cycle_entry_point->compare(cycle_ep_it->s); ++cycle_ep_it)
|
||||
continue;
|
||||
assert(cycle_ep_it != run_->cycle.end());
|
||||
|
||||
// Now shift the cycle so it starts on cycle_entry_point.
|
||||
run_->cycle.splice(run_->cycle.end(), run_->cycle,
|
||||
run_->cycle.begin(), cycle_ep_it);
|
||||
run_->cycle.begin(), cycle_ep_it);
|
||||
|
||||
return run_;
|
||||
}
|
||||
|
|
@ -157,68 +157,68 @@ namespace spot
|
|||
//
|
||||
// This idea is taken from Product<T>::findWitness in LBTT 1.1.2,
|
||||
// which in turn is probably inspired from
|
||||
// @Article{ latvala.00.fi,
|
||||
// author = {Timo Latvala and Keijo Heljanko},
|
||||
// title = {Coping With Strong Fairness},
|
||||
// journal = {Fundamenta Informaticae},
|
||||
// year = {2000},
|
||||
// volume = {43},
|
||||
// number = {1--4},
|
||||
// pages = {1--19},
|
||||
// publisher = {IOS Press}
|
||||
// @Article{ latvala.00.fi,
|
||||
// author = {Timo Latvala and Keijo Heljanko},
|
||||
// title = {Coping With Strong Fairness},
|
||||
// journal = {Fundamenta Informaticae},
|
||||
// year = {2000},
|
||||
// volume = {43},
|
||||
// number = {1--4},
|
||||
// pages = {1--19},
|
||||
// publisher = {IOS Press}
|
||||
// }
|
||||
const state* substart = ecs_->cycle_seed;
|
||||
do
|
||||
{
|
||||
struct scc_bfs final: bfs_steps
|
||||
{
|
||||
const couvreur99_check_status* ecs;
|
||||
couvreur99_check_result* r;
|
||||
acc_cond::mark_t& acc_to_traverse;
|
||||
int scc_root;
|
||||
struct scc_bfs final: bfs_steps
|
||||
{
|
||||
const couvreur99_check_status* ecs;
|
||||
couvreur99_check_result* r;
|
||||
acc_cond::mark_t& acc_to_traverse;
|
||||
int scc_root;
|
||||
|
||||
scc_bfs(const couvreur99_check_status* ecs,
|
||||
couvreur99_check_result* r, acc_cond::mark_t& acc_to_traverse)
|
||||
: bfs_steps(ecs->aut), ecs(ecs), r(r),
|
||||
acc_to_traverse(acc_to_traverse),
|
||||
scc_root(ecs->root.top().index)
|
||||
{
|
||||
}
|
||||
scc_bfs(const couvreur99_check_status* ecs,
|
||||
couvreur99_check_result* r, acc_cond::mark_t& acc_to_traverse)
|
||||
: bfs_steps(ecs->aut), ecs(ecs), r(r),
|
||||
acc_to_traverse(acc_to_traverse),
|
||||
scc_root(ecs->root.top().index)
|
||||
{
|
||||
}
|
||||
|
||||
virtual const state*
|
||||
filter(const state* s) override
|
||||
{
|
||||
auto i = ecs->h.find(s);
|
||||
s->destroy();
|
||||
// Ignore unknown states.
|
||||
if (i == ecs->h.end())
|
||||
return nullptr;
|
||||
// Stay in the final SCC.
|
||||
if (i->second < scc_root)
|
||||
return nullptr;
|
||||
r->inc_ars_cycle_states();
|
||||
return i->first;
|
||||
}
|
||||
virtual const state*
|
||||
filter(const state* s) override
|
||||
{
|
||||
auto i = ecs->h.find(s);
|
||||
s->destroy();
|
||||
// Ignore unknown states.
|
||||
if (i == ecs->h.end())
|
||||
return nullptr;
|
||||
// Stay in the final SCC.
|
||||
if (i->second < scc_root)
|
||||
return nullptr;
|
||||
r->inc_ars_cycle_states();
|
||||
return i->first;
|
||||
}
|
||||
|
||||
virtual bool
|
||||
match(twa_run::step& st, const state* s) override
|
||||
{
|
||||
acc_cond::mark_t less_acc =
|
||||
acc_to_traverse - st.acc;
|
||||
if (less_acc != acc_to_traverse
|
||||
|| (acc_to_traverse == 0U
|
||||
&& s == ecs->cycle_seed))
|
||||
{
|
||||
acc_to_traverse = less_acc;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
virtual bool
|
||||
match(twa_run::step& st, const state* s) override
|
||||
{
|
||||
acc_cond::mark_t less_acc =
|
||||
acc_to_traverse - st.acc;
|
||||
if (less_acc != acc_to_traverse
|
||||
|| (acc_to_traverse == 0U
|
||||
&& s == ecs->cycle_seed))
|
||||
{
|
||||
acc_to_traverse = less_acc;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
} b(ecs_.get(), this, acc_to_traverse);
|
||||
} b(ecs_.get(), this, acc_to_traverse);
|
||||
|
||||
substart = b.search(substart, run_->cycle);
|
||||
assert(substart);
|
||||
substart = b.search(substart, run_->cycle);
|
||||
assert(substart);
|
||||
}
|
||||
while (acc_to_traverse != 0U || substart != ecs_->cycle_seed);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,8 +35,8 @@ namespace spot
|
|||
{
|
||||
public:
|
||||
couvreur99_check_result(const
|
||||
std::shared_ptr<const couvreur99_check_status>& ecs,
|
||||
option_map o = option_map());
|
||||
std::shared_ptr<const couvreur99_check_status>& ecs,
|
||||
option_map o = option_map());
|
||||
|
||||
virtual twa_run_ptr accepting_run() override;
|
||||
|
||||
|
|
|
|||
|
|
@ -47,11 +47,11 @@ namespace spot
|
|||
poprem_ = o.get("poprem", 1);
|
||||
ecs_ = std::make_shared<couvreur99_check_status>(a);
|
||||
stats["removed components"] =
|
||||
static_cast<spot::unsigned_statistics::unsigned_fun>
|
||||
(&couvreur99_check::get_removed_components);
|
||||
static_cast<spot::unsigned_statistics::unsigned_fun>
|
||||
(&couvreur99_check::get_removed_components);
|
||||
stats["vmsize"] =
|
||||
static_cast<spot::unsigned_statistics::unsigned_fun>
|
||||
(&couvreur99_check::get_vmsize);
|
||||
static_cast<spot::unsigned_statistics::unsigned_fun>
|
||||
(&couvreur99_check::get_vmsize);
|
||||
}
|
||||
|
||||
couvreur99_check::~couvreur99_check()
|
||||
|
|
@ -80,12 +80,12 @@ namespace spot
|
|||
// If rem has been updated, removing states is very easy.
|
||||
if (poprem_)
|
||||
{
|
||||
assert(!ecs_->root.rem().empty());
|
||||
dec_depth(ecs_->root.rem().size());
|
||||
for (auto i: ecs_->root.rem())
|
||||
ecs_->h[i] = -1;
|
||||
// ecs_->root.rem().clear();
|
||||
return;
|
||||
assert(!ecs_->root.rem().empty());
|
||||
dec_depth(ecs_->root.rem().size());
|
||||
for (auto i: ecs_->root.rem())
|
||||
ecs_->h[i] = -1;
|
||||
// ecs_->root.rem().clear();
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove from H all states which are reachable from state FROM.
|
||||
|
|
@ -102,29 +102,29 @@ namespace spot
|
|||
|
||||
for (;;)
|
||||
{
|
||||
// Remove each destination of this iterator.
|
||||
if (i->first())
|
||||
do
|
||||
{
|
||||
inc_transitions();
|
||||
// Remove each destination of this iterator.
|
||||
if (i->first())
|
||||
do
|
||||
{
|
||||
inc_transitions();
|
||||
|
||||
const state* s = i->dst();
|
||||
auto j = ecs_->h.find(s);
|
||||
assert(j != ecs_->h.end());
|
||||
s->destroy();
|
||||
const state* s = i->dst();
|
||||
auto j = ecs_->h.find(s);
|
||||
assert(j != ecs_->h.end());
|
||||
s->destroy();
|
||||
|
||||
if (j->second != -1)
|
||||
{
|
||||
j->second = -1;
|
||||
to_remove.push(ecs_->aut->succ_iter(j->first));
|
||||
}
|
||||
}
|
||||
while (i->next());
|
||||
ecs_->aut->release_iter(i);
|
||||
if (to_remove.empty())
|
||||
break;
|
||||
i = to_remove.top();
|
||||
to_remove.pop();
|
||||
if (j->second != -1)
|
||||
{
|
||||
j->second = -1;
|
||||
to_remove.push(ecs_->aut->succ_iter(j->first));
|
||||
}
|
||||
}
|
||||
while (i->next());
|
||||
ecs_->aut->release_iter(i);
|
||||
if (to_remove.empty())
|
||||
break;
|
||||
i = to_remove.top();
|
||||
to_remove.pop();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -134,10 +134,10 @@ namespace spot
|
|||
{
|
||||
auto acc = ecs_->aut->acc();
|
||||
if (acc.get_acceptance().is_f())
|
||||
return nullptr;
|
||||
return nullptr;
|
||||
if (acc.uses_fin_acceptance())
|
||||
throw std::runtime_error
|
||||
("Fin acceptance is not supported by couvreur99()");
|
||||
throw std::runtime_error
|
||||
("Fin acceptance is not supported by couvreur99()");
|
||||
}
|
||||
|
||||
// We use five main data in this algorithm:
|
||||
|
|
@ -170,126 +170,126 @@ namespace spot
|
|||
|
||||
while (!todo.empty())
|
||||
{
|
||||
assert(ecs_->root.size() == arc.size());
|
||||
assert(ecs_->root.size() == arc.size());
|
||||
|
||||
// We are looking at the next successor in SUCC.
|
||||
twa_succ_iterator* succ = todo.top().second;
|
||||
// We are looking at the next successor in SUCC.
|
||||
twa_succ_iterator* succ = todo.top().second;
|
||||
|
||||
// If there is no more successor, backtrack.
|
||||
if (succ->done())
|
||||
{
|
||||
// We have explored all successors of state CURR.
|
||||
const state* curr = todo.top().first;
|
||||
// If there is no more successor, backtrack.
|
||||
if (succ->done())
|
||||
{
|
||||
// We have explored all successors of state CURR.
|
||||
const state* curr = todo.top().first;
|
||||
|
||||
// Backtrack TODO.
|
||||
todo.pop();
|
||||
dec_depth();
|
||||
// Backtrack TODO.
|
||||
todo.pop();
|
||||
dec_depth();
|
||||
|
||||
// If poprem is used, fill rem with any component removed,
|
||||
// so that remove_component() does not have to traverse
|
||||
// the SCC again.
|
||||
auto i = ecs_->h.find(curr);
|
||||
assert(i != ecs_->h.end());
|
||||
if (poprem_)
|
||||
{
|
||||
ecs_->root.rem().push_front(i->first);
|
||||
inc_depth();
|
||||
}
|
||||
// When backtracking the root of an SCC, we must also
|
||||
// remove that SCC from the ARC/ROOT stacks. We must
|
||||
// discard from H all reachable states from this SCC.
|
||||
assert(!ecs_->root.empty());
|
||||
if (ecs_->root.top().index == i->second)
|
||||
{
|
||||
assert(!arc.empty());
|
||||
arc.pop();
|
||||
remove_component(curr);
|
||||
ecs_->root.pop();
|
||||
}
|
||||
ecs_->aut->release_iter(succ);
|
||||
// Do not destroy CURR: it is a key in H.
|
||||
continue;
|
||||
}
|
||||
// If poprem is used, fill rem with any component removed,
|
||||
// so that remove_component() does not have to traverse
|
||||
// the SCC again.
|
||||
auto i = ecs_->h.find(curr);
|
||||
assert(i != ecs_->h.end());
|
||||
if (poprem_)
|
||||
{
|
||||
ecs_->root.rem().push_front(i->first);
|
||||
inc_depth();
|
||||
}
|
||||
// When backtracking the root of an SCC, we must also
|
||||
// remove that SCC from the ARC/ROOT stacks. We must
|
||||
// discard from H all reachable states from this SCC.
|
||||
assert(!ecs_->root.empty());
|
||||
if (ecs_->root.top().index == i->second)
|
||||
{
|
||||
assert(!arc.empty());
|
||||
arc.pop();
|
||||
remove_component(curr);
|
||||
ecs_->root.pop();
|
||||
}
|
||||
ecs_->aut->release_iter(succ);
|
||||
// Do not destroy CURR: it is a key in H.
|
||||
continue;
|
||||
}
|
||||
|
||||
// We have a successor to look at.
|
||||
inc_transitions();
|
||||
// Fetch the values (destination state, acceptance conditions
|
||||
// of the arc) we are interested in...
|
||||
const state* dest = succ->dst();
|
||||
acc_cond::mark_t acc = succ->acc();
|
||||
// ... and point the iterator to the next successor, for
|
||||
// the next iteration.
|
||||
succ->next();
|
||||
// We do not need SUCC from now on.
|
||||
// We have a successor to look at.
|
||||
inc_transitions();
|
||||
// Fetch the values (destination state, acceptance conditions
|
||||
// of the arc) we are interested in...
|
||||
const state* dest = succ->dst();
|
||||
acc_cond::mark_t acc = succ->acc();
|
||||
// ... and point the iterator to the next successor, for
|
||||
// the next iteration.
|
||||
succ->next();
|
||||
// We do not need SUCC from now on.
|
||||
|
||||
// Are we going to a new state?
|
||||
auto p = ecs_->h.emplace(dest, num + 1);
|
||||
if (p.second)
|
||||
{
|
||||
// Yes. Bump number, stack the stack, and register its
|
||||
// successors for later processing.
|
||||
ecs_->root.push(++num);
|
||||
arc.push(acc);
|
||||
twa_succ_iterator* iter = ecs_->aut->succ_iter(dest);
|
||||
iter->first();
|
||||
todo.emplace(dest, iter);
|
||||
inc_depth();
|
||||
continue;
|
||||
}
|
||||
dest->destroy();
|
||||
// Are we going to a new state?
|
||||
auto p = ecs_->h.emplace(dest, num + 1);
|
||||
if (p.second)
|
||||
{
|
||||
// Yes. Bump number, stack the stack, and register its
|
||||
// successors for later processing.
|
||||
ecs_->root.push(++num);
|
||||
arc.push(acc);
|
||||
twa_succ_iterator* iter = ecs_->aut->succ_iter(dest);
|
||||
iter->first();
|
||||
todo.emplace(dest, iter);
|
||||
inc_depth();
|
||||
continue;
|
||||
}
|
||||
dest->destroy();
|
||||
|
||||
// If we have reached a dead component, ignore it.
|
||||
if (p.first->second == -1)
|
||||
continue;
|
||||
// If we have reached a dead component, ignore it.
|
||||
if (p.first->second == -1)
|
||||
continue;
|
||||
|
||||
// Now this is the most interesting case. We have reached a
|
||||
// state S1 which is already part of a non-dead SCC. Any such
|
||||
// non-dead SCC has necessarily been crossed by our path to
|
||||
// this state: there is a state S2 in our path which belongs
|
||||
// to this SCC too. We are going to merge all states between
|
||||
// this S1 and S2 into this SCC.
|
||||
//
|
||||
// This merge is easy to do because the order of the SCC in
|
||||
// ROOT is ascending: we just have to merge all SCCs from the
|
||||
// top of ROOT that have an index greater to the one of
|
||||
// the SCC of S2 (called the "threshold").
|
||||
int threshold = p.first->second;
|
||||
std::list<const state*> rem;
|
||||
while (threshold < ecs_->root.top().index)
|
||||
{
|
||||
assert(!ecs_->root.empty());
|
||||
assert(!arc.empty());
|
||||
acc |= ecs_->root.top().condition;
|
||||
acc |= arc.top();
|
||||
rem.splice(rem.end(), ecs_->root.rem());
|
||||
ecs_->root.pop();
|
||||
arc.pop();
|
||||
}
|
||||
// Note that we do not always have
|
||||
// threshold == ecs_->root.top().index
|
||||
// after this loop, the SCC whose index is threshold might have
|
||||
// been merged with a lower SCC.
|
||||
// Now this is the most interesting case. We have reached a
|
||||
// state S1 which is already part of a non-dead SCC. Any such
|
||||
// non-dead SCC has necessarily been crossed by our path to
|
||||
// this state: there is a state S2 in our path which belongs
|
||||
// to this SCC too. We are going to merge all states between
|
||||
// this S1 and S2 into this SCC.
|
||||
//
|
||||
// This merge is easy to do because the order of the SCC in
|
||||
// ROOT is ascending: we just have to merge all SCCs from the
|
||||
// top of ROOT that have an index greater to the one of
|
||||
// the SCC of S2 (called the "threshold").
|
||||
int threshold = p.first->second;
|
||||
std::list<const state*> rem;
|
||||
while (threshold < ecs_->root.top().index)
|
||||
{
|
||||
assert(!ecs_->root.empty());
|
||||
assert(!arc.empty());
|
||||
acc |= ecs_->root.top().condition;
|
||||
acc |= arc.top();
|
||||
rem.splice(rem.end(), ecs_->root.rem());
|
||||
ecs_->root.pop();
|
||||
arc.pop();
|
||||
}
|
||||
// Note that we do not always have
|
||||
// threshold == ecs_->root.top().index
|
||||
// after this loop, the SCC whose index is threshold might have
|
||||
// been merged with a lower SCC.
|
||||
|
||||
// Accumulate all acceptance conditions into the merged SCC.
|
||||
ecs_->root.top().condition |= acc;
|
||||
ecs_->root.rem().splice(ecs_->root.rem().end(), rem);
|
||||
// Accumulate all acceptance conditions into the merged SCC.
|
||||
ecs_->root.top().condition |= acc;
|
||||
ecs_->root.rem().splice(ecs_->root.rem().end(), rem);
|
||||
|
||||
if (ecs_->aut->acc().accepting(ecs_->root.top().condition))
|
||||
{
|
||||
// We have found an accepting SCC.
|
||||
// Release all iterators in TODO.
|
||||
while (!todo.empty())
|
||||
{
|
||||
ecs_->aut->release_iter(todo.top().second);
|
||||
todo.pop();
|
||||
dec_depth();
|
||||
}
|
||||
// Use this state to start the computation of an accepting
|
||||
// cycle.
|
||||
ecs_->cycle_seed = p.first->first;
|
||||
if (ecs_->aut->acc().accepting(ecs_->root.top().condition))
|
||||
{
|
||||
// We have found an accepting SCC.
|
||||
// Release all iterators in TODO.
|
||||
while (!todo.empty())
|
||||
{
|
||||
ecs_->aut->release_iter(todo.top().second);
|
||||
todo.pop();
|
||||
dec_depth();
|
||||
}
|
||||
// Use this state to start the computation of an accepting
|
||||
// cycle.
|
||||
ecs_->cycle_seed = p.first->first;
|
||||
set_states(ecs_->states());
|
||||
return std::make_shared<couvreur99_check_result>(ecs_, options());
|
||||
}
|
||||
return std::make_shared<couvreur99_check_result>(ecs_, options());
|
||||
}
|
||||
}
|
||||
// This automaton recognizes no word.
|
||||
set_states(ecs_->states());
|
||||
|
|
@ -314,20 +314,20 @@ namespace spot
|
|||
//////////////////////////////////////////////////////////////////////
|
||||
|
||||
couvreur99_check_shy::todo_item::todo_item(const state* s, int n,
|
||||
couvreur99_check_shy* shy)
|
||||
: s(s), n(n)
|
||||
couvreur99_check_shy* shy)
|
||||
: s(s), n(n)
|
||||
{
|
||||
for (auto iter: shy->ecs_->aut->succ(s))
|
||||
{
|
||||
q.emplace_back(iter->acc(),
|
||||
iter->dst());
|
||||
shy->inc_depth();
|
||||
shy->inc_transitions();
|
||||
q.emplace_back(iter->acc(),
|
||||
iter->dst());
|
||||
shy->inc_depth();
|
||||
shy->inc_transitions();
|
||||
}
|
||||
}
|
||||
|
||||
couvreur99_check_shy::couvreur99_check_shy(const const_twa_ptr& a,
|
||||
option_map o)
|
||||
option_map o)
|
||||
: couvreur99_check(a, o), num(1)
|
||||
{
|
||||
group_ = o.get("group", 1);
|
||||
|
|
@ -353,17 +353,17 @@ namespace spot
|
|||
// unless they are used as keys in H.
|
||||
while (!todo.empty())
|
||||
{
|
||||
succ_queue& queue = todo.back().q;
|
||||
for (auto& q: queue)
|
||||
{
|
||||
// Destroy the state if it is a clone of a state in the
|
||||
// heap or if it is an unknown state.
|
||||
auto i = ecs_->h.find(q.s);
|
||||
if (i == ecs_->h.end() || i->first != q.s)
|
||||
q.s->destroy();
|
||||
}
|
||||
dec_depth(todo.back().q.size() + 1);
|
||||
todo.pop_back();
|
||||
succ_queue& queue = todo.back().q;
|
||||
for (auto& q: queue)
|
||||
{
|
||||
// Destroy the state if it is a clone of a state in the
|
||||
// heap or if it is an unknown state.
|
||||
auto i = ecs_->h.find(q.s);
|
||||
if (i == ecs_->h.end() || i->first != q.s)
|
||||
q.s->destroy();
|
||||
}
|
||||
dec_depth(todo.back().q.size() + 1);
|
||||
todo.pop_back();
|
||||
}
|
||||
dec_depth(ecs_->root.clear_rem());
|
||||
assert(depth() == 0);
|
||||
|
|
@ -376,17 +376,17 @@ namespace spot
|
|||
unsigned pos = 0;
|
||||
for (auto& ti: todo)
|
||||
{
|
||||
++pos;
|
||||
os << '#' << pos << " s:" << ti.s << " n:" << ti.n
|
||||
<< " q:{";
|
||||
for (auto qi = ti.q.begin(); qi != ti.q.end();)
|
||||
{
|
||||
os << qi->s;
|
||||
++qi;
|
||||
if (qi != ti.q.end())
|
||||
os << ", ";
|
||||
}
|
||||
os << "}\n";
|
||||
++pos;
|
||||
os << '#' << pos << " s:" << ti.s << " n:" << ti.n
|
||||
<< " q:{";
|
||||
for (auto qi = ti.q.begin(); qi != ti.q.end();)
|
||||
{
|
||||
os << qi->s;
|
||||
++qi;
|
||||
if (qi != ti.q.end())
|
||||
os << ", ";
|
||||
}
|
||||
os << "}\n";
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -396,10 +396,10 @@ namespace spot
|
|||
{
|
||||
auto acc = ecs_->aut->acc();
|
||||
if (acc.get_acceptance().is_f())
|
||||
return nullptr;
|
||||
return nullptr;
|
||||
if (acc.uses_fin_acceptance())
|
||||
throw std::runtime_error
|
||||
("Fin acceptance is not supported by couvreur99()");
|
||||
throw std::runtime_error
|
||||
("Fin acceptance is not supported by couvreur99()");
|
||||
}
|
||||
// Position in the loop seeking known successors.
|
||||
pos = todo.back().q.begin();
|
||||
|
|
@ -407,202 +407,202 @@ namespace spot
|
|||
for (;;)
|
||||
{
|
||||
#ifdef TRACE
|
||||
dump_queue();
|
||||
dump_queue();
|
||||
#endif
|
||||
|
||||
assert(ecs_->root.size() == 1 + arc.size());
|
||||
assert(ecs_->root.size() == 1 + arc.size());
|
||||
|
||||
// Get the successors of the current state.
|
||||
succ_queue& queue = todo.back().q;
|
||||
// Get the successors of the current state.
|
||||
succ_queue& queue = todo.back().q;
|
||||
|
||||
// If there is no more successor, backtrack.
|
||||
if (queue.empty())
|
||||
{
|
||||
trace << "backtrack" << std::endl;
|
||||
// If there is no more successor, backtrack.
|
||||
if (queue.empty())
|
||||
{
|
||||
trace << "backtrack" << std::endl;
|
||||
|
||||
// We have explored all successors of state CURR.
|
||||
const state* curr = todo.back().s;
|
||||
int index = todo.back().n;
|
||||
// We have explored all successors of state CURR.
|
||||
const state* curr = todo.back().s;
|
||||
int index = todo.back().n;
|
||||
|
||||
// Backtrack TODO.
|
||||
todo.pop_back();
|
||||
dec_depth();
|
||||
// Backtrack TODO.
|
||||
todo.pop_back();
|
||||
dec_depth();
|
||||
|
||||
if (todo.empty())
|
||||
{
|
||||
// This automaton recognizes no word.
|
||||
set_states(ecs_->states());
|
||||
assert(poprem_ || depth() == 0);
|
||||
return nullptr;
|
||||
}
|
||||
if (todo.empty())
|
||||
{
|
||||
// This automaton recognizes no word.
|
||||
set_states(ecs_->states());
|
||||
assert(poprem_ || depth() == 0);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
pos = todo.back().q.begin();
|
||||
pos = todo.back().q.begin();
|
||||
|
||||
// If poprem is used, fill rem with any component removed,
|
||||
// so that remove_component() does not have to traverse
|
||||
// the SCC again.
|
||||
if (poprem_)
|
||||
{
|
||||
auto i = ecs_->h.find(curr);
|
||||
assert(i != ecs_->h.end());
|
||||
assert(i->first == curr);
|
||||
ecs_->root.rem().push_front(i->first);
|
||||
inc_depth();
|
||||
}
|
||||
// If poprem is used, fill rem with any component removed,
|
||||
// so that remove_component() does not have to traverse
|
||||
// the SCC again.
|
||||
if (poprem_)
|
||||
{
|
||||
auto i = ecs_->h.find(curr);
|
||||
assert(i != ecs_->h.end());
|
||||
assert(i->first == curr);
|
||||
ecs_->root.rem().push_front(i->first);
|
||||
inc_depth();
|
||||
}
|
||||
|
||||
// When backtracking the root of an SCC, we must also
|
||||
// remove that SCC from the ARC/ROOT stacks. We must
|
||||
// discard from H all reachable states from this SCC.
|
||||
assert(!ecs_->root.empty());
|
||||
if (ecs_->root.top().index == index)
|
||||
{
|
||||
assert(!arc.empty());
|
||||
arc.pop();
|
||||
remove_component(curr);
|
||||
ecs_->root.pop();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
// When backtracking the root of an SCC, we must also
|
||||
// remove that SCC from the ARC/ROOT stacks. We must
|
||||
// discard from H all reachable states from this SCC.
|
||||
assert(!ecs_->root.empty());
|
||||
if (ecs_->root.top().index == index)
|
||||
{
|
||||
assert(!arc.empty());
|
||||
arc.pop();
|
||||
remove_component(curr);
|
||||
ecs_->root.pop();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// We always make a first pass over the successors of a state
|
||||
// to check whether it contains some state we have already seen.
|
||||
// This way we hope to merge the most SCCs before stacking new
|
||||
// states.
|
||||
//
|
||||
// So are we checking for known states ? If yes, POS tells us
|
||||
// which state we are considering. Otherwise just pick the
|
||||
// first one.
|
||||
succ_queue::iterator old;
|
||||
if (pos == queue.end())
|
||||
old = queue.begin();
|
||||
else
|
||||
old = pos;
|
||||
if (pos != queue.end())
|
||||
++pos;
|
||||
//int* i = sip.second;
|
||||
// We always make a first pass over the successors of a state
|
||||
// to check whether it contains some state we have already seen.
|
||||
// This way we hope to merge the most SCCs before stacking new
|
||||
// states.
|
||||
//
|
||||
// So are we checking for known states ? If yes, POS tells us
|
||||
// which state we are considering. Otherwise just pick the
|
||||
// first one.
|
||||
succ_queue::iterator old;
|
||||
if (pos == queue.end())
|
||||
old = queue.begin();
|
||||
else
|
||||
old = pos;
|
||||
if (pos != queue.end())
|
||||
++pos;
|
||||
//int* i = sip.second;
|
||||
|
||||
successor succ = *old;
|
||||
trace << "picked state " << succ.s << '\n';
|
||||
auto i = ecs_->h.find(succ.s);
|
||||
successor succ = *old;
|
||||
trace << "picked state " << succ.s << '\n';
|
||||
auto i = ecs_->h.find(succ.s);
|
||||
|
||||
if (i == ecs_->h.end())
|
||||
{
|
||||
// It's a new state.
|
||||
// If we are seeking known states, just skip it.
|
||||
if (pos != queue.end())
|
||||
continue;
|
||||
if (i == ecs_->h.end())
|
||||
{
|
||||
// It's a new state.
|
||||
// If we are seeking known states, just skip it.
|
||||
if (pos != queue.end())
|
||||
continue;
|
||||
|
||||
trace << "new state\n";
|
||||
trace << "new state\n";
|
||||
|
||||
// Otherwise, number it and stack it so we recurse.
|
||||
queue.erase(old);
|
||||
dec_depth();
|
||||
ecs_->h[succ.s] = ++num;
|
||||
ecs_->root.push(num);
|
||||
arc.push(succ.acc);
|
||||
todo.emplace_back(succ.s, num, this);
|
||||
pos = todo.back().q.begin();
|
||||
inc_depth();
|
||||
continue;
|
||||
}
|
||||
// Otherwise, number it and stack it so we recurse.
|
||||
queue.erase(old);
|
||||
dec_depth();
|
||||
ecs_->h[succ.s] = ++num;
|
||||
ecs_->root.push(num);
|
||||
arc.push(succ.acc);
|
||||
todo.emplace_back(succ.s, num, this);
|
||||
pos = todo.back().q.begin();
|
||||
inc_depth();
|
||||
continue;
|
||||
}
|
||||
|
||||
// It's an known state. Use i->first from now on.
|
||||
succ.s->destroy();
|
||||
// It's an known state. Use i->first from now on.
|
||||
succ.s->destroy();
|
||||
|
||||
queue.erase(old);
|
||||
dec_depth();
|
||||
queue.erase(old);
|
||||
dec_depth();
|
||||
|
||||
// Skip dead states.
|
||||
if (i->second == -1)
|
||||
{
|
||||
trace << "dead state\n";
|
||||
continue;
|
||||
}
|
||||
// Skip dead states.
|
||||
if (i->second == -1)
|
||||
{
|
||||
trace << "dead state\n";
|
||||
continue;
|
||||
}
|
||||
|
||||
trace << "merging...\n";
|
||||
trace << "merging...\n";
|
||||
|
||||
// Now this is the most interesting case. We have
|
||||
// reached a state S1 which is already part of a
|
||||
// non-dead SCC. Any such non-dead SCC has
|
||||
// necessarily been crossed by our path to this
|
||||
// state: there is a state S2 in our path which
|
||||
// belongs to this SCC too. We are going to merge
|
||||
// all states between this S1 and S2 into this
|
||||
// SCC.
|
||||
//
|
||||
// This merge is easy to do because the order of
|
||||
// the SCC in ROOT is ascending: we just have to
|
||||
// merge all SCCs from the top of ROOT that have
|
||||
// an index greater to the one of the SCC of S2
|
||||
// (called the "threshold").
|
||||
int threshold = i->second;
|
||||
std::list<const state*> rem;
|
||||
acc_cond::mark_t acc = succ.acc;
|
||||
while (threshold < ecs_->root.top().index)
|
||||
{
|
||||
assert(!ecs_->root.empty());
|
||||
assert(!arc.empty());
|
||||
acc |= ecs_->root.top().condition;
|
||||
acc |= arc.top();
|
||||
rem.splice(rem.end(), ecs_->root.rem());
|
||||
ecs_->root.pop();
|
||||
arc.pop();
|
||||
}
|
||||
// Note that we do not always have
|
||||
// threshold == ecs_->root.top().index
|
||||
// after this loop, the SCC whose index is threshold
|
||||
// might have been merged with a lower SCC.
|
||||
// Now this is the most interesting case. We have
|
||||
// reached a state S1 which is already part of a
|
||||
// non-dead SCC. Any such non-dead SCC has
|
||||
// necessarily been crossed by our path to this
|
||||
// state: there is a state S2 in our path which
|
||||
// belongs to this SCC too. We are going to merge
|
||||
// all states between this S1 and S2 into this
|
||||
// SCC.
|
||||
//
|
||||
// This merge is easy to do because the order of
|
||||
// the SCC in ROOT is ascending: we just have to
|
||||
// merge all SCCs from the top of ROOT that have
|
||||
// an index greater to the one of the SCC of S2
|
||||
// (called the "threshold").
|
||||
int threshold = i->second;
|
||||
std::list<const state*> rem;
|
||||
acc_cond::mark_t acc = succ.acc;
|
||||
while (threshold < ecs_->root.top().index)
|
||||
{
|
||||
assert(!ecs_->root.empty());
|
||||
assert(!arc.empty());
|
||||
acc |= ecs_->root.top().condition;
|
||||
acc |= arc.top();
|
||||
rem.splice(rem.end(), ecs_->root.rem());
|
||||
ecs_->root.pop();
|
||||
arc.pop();
|
||||
}
|
||||
// Note that we do not always have
|
||||
// threshold == ecs_->root.top().index
|
||||
// after this loop, the SCC whose index is threshold
|
||||
// might have been merged with a lower SCC.
|
||||
|
||||
// Accumulate all acceptance conditions into the
|
||||
// merged SCC.
|
||||
ecs_->root.top().condition |= acc;
|
||||
ecs_->root.rem().splice(ecs_->root.rem().end(), rem);
|
||||
// Accumulate all acceptance conditions into the
|
||||
// merged SCC.
|
||||
ecs_->root.top().condition |= acc;
|
||||
ecs_->root.rem().splice(ecs_->root.rem().end(), rem);
|
||||
|
||||
// Have we found all acceptance conditions?
|
||||
if (ecs_->aut->acc().accepting(ecs_->root.top().condition))
|
||||
{
|
||||
// Use this state to start the computation of an accepting
|
||||
// cycle.
|
||||
ecs_->cycle_seed = i->first;
|
||||
// Have we found all acceptance conditions?
|
||||
if (ecs_->aut->acc().accepting(ecs_->root.top().condition))
|
||||
{
|
||||
// Use this state to start the computation of an accepting
|
||||
// cycle.
|
||||
ecs_->cycle_seed = i->first;
|
||||
|
||||
// We have found an accepting SCC. Clean up TODO.
|
||||
clear_todo();
|
||||
set_states(ecs_->states());
|
||||
return std::make_shared<couvreur99_check_result>(ecs_, options());
|
||||
}
|
||||
// Group the pending successors of formed SCC if requested.
|
||||
if (group_)
|
||||
{
|
||||
assert(todo.back().s);
|
||||
while (ecs_->root.top().index < todo.back().n)
|
||||
{
|
||||
todo_list::reverse_iterator prev = todo.rbegin();
|
||||
todo_list::reverse_iterator last = prev++;
|
||||
// If group2 is used we insert the last->q in front
|
||||
// of prev->q so that the states in prev->q are checked
|
||||
// for existence again after we have processed the states
|
||||
// of last->q. Otherwise we just append to the end.
|
||||
prev->q.splice(group2_ ? prev->q.begin() : prev->q.end(),
|
||||
last->q);
|
||||
// We have found an accepting SCC. Clean up TODO.
|
||||
clear_todo();
|
||||
set_states(ecs_->states());
|
||||
return std::make_shared<couvreur99_check_result>(ecs_, options());
|
||||
}
|
||||
// Group the pending successors of formed SCC if requested.
|
||||
if (group_)
|
||||
{
|
||||
assert(todo.back().s);
|
||||
while (ecs_->root.top().index < todo.back().n)
|
||||
{
|
||||
todo_list::reverse_iterator prev = todo.rbegin();
|
||||
todo_list::reverse_iterator last = prev++;
|
||||
// If group2 is used we insert the last->q in front
|
||||
// of prev->q so that the states in prev->q are checked
|
||||
// for existence again after we have processed the states
|
||||
// of last->q. Otherwise we just append to the end.
|
||||
prev->q.splice(group2_ ? prev->q.begin() : prev->q.end(),
|
||||
last->q);
|
||||
|
||||
if (poprem_)
|
||||
{
|
||||
const state* s = todo.back().s;
|
||||
auto i = ecs_->h.find(s);
|
||||
assert(i != ecs_->h.end());
|
||||
assert(i->first == s);
|
||||
ecs_->root.rem().push_front(i->first);
|
||||
// Don't change the stack depth, since
|
||||
// we are just moving the state from TODO to REM.
|
||||
}
|
||||
else
|
||||
{
|
||||
dec_depth();
|
||||
}
|
||||
todo.pop_back();
|
||||
}
|
||||
pos = todo.back().q.begin();
|
||||
}
|
||||
if (poprem_)
|
||||
{
|
||||
const state* s = todo.back().s;
|
||||
auto i = ecs_->h.find(s);
|
||||
assert(i != ecs_->h.end());
|
||||
assert(i->first == s);
|
||||
ecs_->root.rem().push_front(i->first);
|
||||
// Don't change the stack depth, since
|
||||
// we are just moving the state from TODO to REM.
|
||||
}
|
||||
else
|
||||
{
|
||||
dec_depth();
|
||||
}
|
||||
todo.pop_back();
|
||||
}
|
||||
pos = todo.back().q.begin();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -221,7 +221,7 @@ namespace spot
|
|||
{
|
||||
const state* s;
|
||||
int n;
|
||||
succ_queue q; // Unprocessed successors of S
|
||||
succ_queue q; // Unprocessed successors of S
|
||||
todo_item(const state* s, int n, couvreur99_check_shy* shy);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -80,8 +80,8 @@ namespace spot
|
|||
unsigned n = 0;
|
||||
for (stack_type::iterator i = s.begin(); i != s.end(); ++i)
|
||||
{
|
||||
n += i->rem.size();
|
||||
i->rem.clear();
|
||||
n += i->rem.size();
|
||||
i->rem.clear();
|
||||
}
|
||||
return n;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,10 +35,10 @@ namespace spot
|
|||
auto i = h.begin();
|
||||
while (i != h.end())
|
||||
{
|
||||
// Advance the iterator before deleting the key.
|
||||
const state* s = i->first;
|
||||
++i;
|
||||
s->destroy();
|
||||
// Advance the iterator before deleting the key.
|
||||
const state* s = i->first;
|
||||
++i;
|
||||
s->destroy();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -45,11 +45,11 @@ namespace spot
|
|||
{
|
||||
struct stack_entry
|
||||
{
|
||||
const state* s; // State stored in stack entry.
|
||||
const state* s; // State stored in stack entry.
|
||||
twa_succ_iterator* lasttr; // Last transition explored from this state.
|
||||
int lowlink; // Lowlink value if this entry.
|
||||
int pre; // DFS predecessor.
|
||||
int acc; // Accepting state link.
|
||||
int lowlink; // Lowlink value if this entry.
|
||||
int pre; // DFS predecessor.
|
||||
int acc; // Accepting state link.
|
||||
};
|
||||
|
||||
struct gv04: public emptiness_check, public ec_statistics
|
||||
|
|
@ -60,342 +60,342 @@ namespace spot
|
|||
// Stack of visited states on the path.
|
||||
std::vector<stack_entry> stack;
|
||||
|
||||
int top; // Top of SCC stack.
|
||||
int dftop; // Top of DFS stack.
|
||||
bool violation; // Whether an accepting run was found.
|
||||
int top; // Top of SCC stack.
|
||||
int dftop; // Top of DFS stack.
|
||||
bool violation; // Whether an accepting run was found.
|
||||
|
||||
gv04(const const_twa_ptr& a, option_map o)
|
||||
: emptiness_check(a, o)
|
||||
: emptiness_check(a, o)
|
||||
{
|
||||
assert(a->num_sets() <= 1);
|
||||
assert(a->num_sets() <= 1);
|
||||
}
|
||||
|
||||
~gv04()
|
||||
{
|
||||
for (auto i: stack)
|
||||
a_->release_iter(i.lasttr);
|
||||
auto s = h.begin();
|
||||
while (s != h.end())
|
||||
{
|
||||
// Advance the iterator before deleting the "key" pointer.
|
||||
const state* ptr = s->first;
|
||||
++s;
|
||||
ptr->destroy();
|
||||
}
|
||||
for (auto i: stack)
|
||||
a_->release_iter(i.lasttr);
|
||||
auto s = h.begin();
|
||||
while (s != h.end())
|
||||
{
|
||||
// Advance the iterator before deleting the "key" pointer.
|
||||
const state* ptr = s->first;
|
||||
++s;
|
||||
ptr->destroy();
|
||||
}
|
||||
}
|
||||
|
||||
virtual emptiness_check_result_ptr
|
||||
check() override
|
||||
{
|
||||
top = dftop = -1;
|
||||
violation = false;
|
||||
push(a_->get_init_state(), false);
|
||||
top = dftop = -1;
|
||||
violation = false;
|
||||
push(a_->get_init_state(), false);
|
||||
|
||||
while (!violation && dftop >= 0)
|
||||
{
|
||||
trace << "Main iteration (top = " << top
|
||||
<< ", dftop = " << dftop
|
||||
<< ", s = " << a_->format_state(stack[dftop].s)
|
||||
<< ')' << std::endl;
|
||||
while (!violation && dftop >= 0)
|
||||
{
|
||||
trace << "Main iteration (top = " << top
|
||||
<< ", dftop = " << dftop
|
||||
<< ", s = " << a_->format_state(stack[dftop].s)
|
||||
<< ')' << std::endl;
|
||||
|
||||
twa_succ_iterator* iter = stack[dftop].lasttr;
|
||||
bool cont;
|
||||
if (!iter)
|
||||
{
|
||||
iter = stack[dftop].lasttr = a_->succ_iter(stack[dftop].s);
|
||||
cont = iter->first();
|
||||
}
|
||||
else
|
||||
{
|
||||
cont = iter->next();
|
||||
}
|
||||
twa_succ_iterator* iter = stack[dftop].lasttr;
|
||||
bool cont;
|
||||
if (!iter)
|
||||
{
|
||||
iter = stack[dftop].lasttr = a_->succ_iter(stack[dftop].s);
|
||||
cont = iter->first();
|
||||
}
|
||||
else
|
||||
{
|
||||
cont = iter->next();
|
||||
}
|
||||
|
||||
if (!cont)
|
||||
{
|
||||
trace << " No more successors" << std::endl;
|
||||
pop();
|
||||
}
|
||||
else
|
||||
{
|
||||
const state* s_prime = iter->dst();
|
||||
bool acc =
|
||||
a_->acc().accepting(iter->acc());
|
||||
inc_transitions();
|
||||
if (!cont)
|
||||
{
|
||||
trace << " No more successors" << std::endl;
|
||||
pop();
|
||||
}
|
||||
else
|
||||
{
|
||||
const state* s_prime = iter->dst();
|
||||
bool acc =
|
||||
a_->acc().accepting(iter->acc());
|
||||
inc_transitions();
|
||||
|
||||
trace << " Next successor: s_prime = "
|
||||
<< a_->format_state(s_prime)
|
||||
<< (acc ? " (with accepting link)" : "");
|
||||
trace << " Next successor: s_prime = "
|
||||
<< a_->format_state(s_prime)
|
||||
<< (acc ? " (with accepting link)" : "");
|
||||
|
||||
auto i = h.find(s_prime);
|
||||
auto i = h.find(s_prime);
|
||||
|
||||
if (i == h.end())
|
||||
{
|
||||
trace << " is a new state." << std::endl;
|
||||
push(s_prime, acc);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (i->second < stack.size()
|
||||
&& stack[i->second].s->compare(s_prime) == 0)
|
||||
{
|
||||
// s_prime has a clone on stack
|
||||
trace << " is on stack." << std::endl;
|
||||
// This is an addition to GV04 to support TBA.
|
||||
violation |= acc;
|
||||
lowlinkupdate(dftop, i->second);
|
||||
}
|
||||
else
|
||||
{
|
||||
trace << " has been seen, but is no longer on stack."
|
||||
<< std::endl;
|
||||
}
|
||||
if (i == h.end())
|
||||
{
|
||||
trace << " is a new state." << std::endl;
|
||||
push(s_prime, acc);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (i->second < stack.size()
|
||||
&& stack[i->second].s->compare(s_prime) == 0)
|
||||
{
|
||||
// s_prime has a clone on stack
|
||||
trace << " is on stack." << std::endl;
|
||||
// This is an addition to GV04 to support TBA.
|
||||
violation |= acc;
|
||||
lowlinkupdate(dftop, i->second);
|
||||
}
|
||||
else
|
||||
{
|
||||
trace << " has been seen, but is no longer on stack."
|
||||
<< std::endl;
|
||||
}
|
||||
|
||||
s_prime->destroy();
|
||||
}
|
||||
}
|
||||
set_states(h.size());
|
||||
}
|
||||
if (violation)
|
||||
return std::make_shared<result>(*this);
|
||||
return nullptr;
|
||||
s_prime->destroy();
|
||||
}
|
||||
}
|
||||
set_states(h.size());
|
||||
}
|
||||
if (violation)
|
||||
return std::make_shared<result>(*this);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void
|
||||
push(const state* s, bool accepting)
|
||||
{
|
||||
trace << " push(s = " << a_->format_state(s)
|
||||
<< ", accepting = " << accepting << ")\n";
|
||||
trace << " push(s = " << a_->format_state(s)
|
||||
<< ", accepting = " << accepting << ")\n";
|
||||
|
||||
h[s] = ++top;
|
||||
h[s] = ++top;
|
||||
|
||||
stack_entry ss = { s, nullptr, top, dftop, 0 };
|
||||
stack_entry ss = { s, nullptr, top, dftop, 0 };
|
||||
|
||||
if (accepting)
|
||||
ss.acc = top - 1; // This differs from GV04 to support TBA.
|
||||
else if (dftop >= 0)
|
||||
ss.acc = stack[dftop].acc;
|
||||
else
|
||||
ss.acc = -1;
|
||||
if (accepting)
|
||||
ss.acc = top - 1; // This differs from GV04 to support TBA.
|
||||
else if (dftop >= 0)
|
||||
ss.acc = stack[dftop].acc;
|
||||
else
|
||||
ss.acc = -1;
|
||||
|
||||
trace << " s.lowlink = " << top << std::endl
|
||||
<< " s.acc = " << ss.acc << std::endl;
|
||||
trace << " s.lowlink = " << top << std::endl
|
||||
<< " s.acc = " << ss.acc << std::endl;
|
||||
|
||||
stack.push_back(ss);
|
||||
dftop = top;
|
||||
inc_depth();
|
||||
stack.push_back(ss);
|
||||
dftop = top;
|
||||
inc_depth();
|
||||
}
|
||||
|
||||
void
|
||||
pop()
|
||||
{
|
||||
trace << " pop()\n";
|
||||
trace << " pop()\n";
|
||||
|
||||
int p = stack[dftop].pre;
|
||||
if (p >= 0)
|
||||
lowlinkupdate(p, dftop);
|
||||
if (stack[dftop].lowlink == dftop)
|
||||
{
|
||||
assert(static_cast<unsigned int>(top + 1) == stack.size());
|
||||
for (int i = top; i >= dftop; --i)
|
||||
{
|
||||
a_->release_iter(stack[i].lasttr);
|
||||
stack.pop_back();
|
||||
dec_depth();
|
||||
}
|
||||
top = dftop - 1;
|
||||
}
|
||||
dftop = p;
|
||||
int p = stack[dftop].pre;
|
||||
if (p >= 0)
|
||||
lowlinkupdate(p, dftop);
|
||||
if (stack[dftop].lowlink == dftop)
|
||||
{
|
||||
assert(static_cast<unsigned int>(top + 1) == stack.size());
|
||||
for (int i = top; i >= dftop; --i)
|
||||
{
|
||||
a_->release_iter(stack[i].lasttr);
|
||||
stack.pop_back();
|
||||
dec_depth();
|
||||
}
|
||||
top = dftop - 1;
|
||||
}
|
||||
dftop = p;
|
||||
}
|
||||
|
||||
void
|
||||
lowlinkupdate(int f, int t)
|
||||
{
|
||||
trace << " lowlinkupdate(f = " << f << ", t = " << t
|
||||
<< ")\n t.lowlink = " << stack[t].lowlink
|
||||
<< "\n f.lowlink = " << stack[f].lowlink
|
||||
<< "\n f.acc = " << stack[f].acc << '\n';
|
||||
int stack_t_lowlink = stack[t].lowlink;
|
||||
if (stack_t_lowlink <= stack[f].lowlink)
|
||||
{
|
||||
if (stack_t_lowlink <= stack[f].acc)
|
||||
violation = true;
|
||||
stack[f].lowlink = stack_t_lowlink;
|
||||
trace << " f.lowlink updated to "
|
||||
<< stack[f].lowlink << '\n';
|
||||
}
|
||||
trace << " lowlinkupdate(f = " << f << ", t = " << t
|
||||
<< ")\n t.lowlink = " << stack[t].lowlink
|
||||
<< "\n f.lowlink = " << stack[f].lowlink
|
||||
<< "\n f.acc = " << stack[f].acc << '\n';
|
||||
int stack_t_lowlink = stack[t].lowlink;
|
||||
if (stack_t_lowlink <= stack[f].lowlink)
|
||||
{
|
||||
if (stack_t_lowlink <= stack[f].acc)
|
||||
violation = true;
|
||||
stack[f].lowlink = stack_t_lowlink;
|
||||
trace << " f.lowlink updated to "
|
||||
<< stack[f].lowlink << '\n';
|
||||
}
|
||||
}
|
||||
|
||||
virtual std::ostream&
|
||||
print_stats(std::ostream& os) const override
|
||||
{
|
||||
os << h.size() << " unique states visited\n";
|
||||
os << transitions() << " transitions explored\n";
|
||||
os << max_depth() << " items max on stack\n";
|
||||
return os;
|
||||
os << h.size() << " unique states visited\n";
|
||||
os << transitions() << " transitions explored\n";
|
||||
os << max_depth() << " items max on stack\n";
|
||||
return os;
|
||||
}
|
||||
|
||||
struct result:
|
||||
public emptiness_check_result,
|
||||
public acss_statistics
|
||||
public emptiness_check_result,
|
||||
public acss_statistics
|
||||
{
|
||||
gv04& data;
|
||||
gv04& data;
|
||||
|
||||
result(gv04& data)
|
||||
: emptiness_check_result(data.automaton(), data.options()),
|
||||
data(data)
|
||||
{
|
||||
}
|
||||
result(gv04& data)
|
||||
: emptiness_check_result(data.automaton(), data.options()),
|
||||
data(data)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
update_lowlinks()
|
||||
{
|
||||
// Transitively update the lowlinks, so we can use them in
|
||||
// to check SCC inclusion
|
||||
for (int i = 0; i <= data.top; ++i)
|
||||
{
|
||||
int l = data.stack[i].lowlink;
|
||||
if (l < i)
|
||||
{
|
||||
int ll = data.stack[i].lowlink = data.stack[l].lowlink;
|
||||
for (int j = i - 1; data.stack[j].lowlink != ll; --j)
|
||||
data.stack[j].lowlink = ll;
|
||||
}
|
||||
}
|
||||
}
|
||||
void
|
||||
update_lowlinks()
|
||||
{
|
||||
// Transitively update the lowlinks, so we can use them in
|
||||
// to check SCC inclusion
|
||||
for (int i = 0; i <= data.top; ++i)
|
||||
{
|
||||
int l = data.stack[i].lowlink;
|
||||
if (l < i)
|
||||
{
|
||||
int ll = data.stack[i].lowlink = data.stack[l].lowlink;
|
||||
for (int j = i - 1; data.stack[j].lowlink != ll; --j)
|
||||
data.stack[j].lowlink = ll;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
virtual unsigned
|
||||
acss_states() const override
|
||||
{
|
||||
// Gross!
|
||||
const_cast<result*>(this)->update_lowlinks();
|
||||
virtual unsigned
|
||||
acss_states() const override
|
||||
{
|
||||
// Gross!
|
||||
const_cast<result*>(this)->update_lowlinks();
|
||||
|
||||
int scc = data.stack[data.dftop].lowlink;
|
||||
int j = data.dftop;
|
||||
int s = 0;
|
||||
while (j >= 0 && data.stack[j].lowlink == scc)
|
||||
{
|
||||
--j;
|
||||
++s;
|
||||
}
|
||||
assert(s > 0);
|
||||
return s;
|
||||
}
|
||||
int scc = data.stack[data.dftop].lowlink;
|
||||
int j = data.dftop;
|
||||
int s = 0;
|
||||
while (j >= 0 && data.stack[j].lowlink == scc)
|
||||
{
|
||||
--j;
|
||||
++s;
|
||||
}
|
||||
assert(s > 0);
|
||||
return s;
|
||||
}
|
||||
|
||||
virtual twa_run_ptr
|
||||
accepting_run() override
|
||||
{
|
||||
auto res = std::make_shared<twa_run>(automaton());
|
||||
virtual twa_run_ptr
|
||||
accepting_run() override
|
||||
{
|
||||
auto res = std::make_shared<twa_run>(automaton());
|
||||
|
||||
update_lowlinks();
|
||||
update_lowlinks();
|
||||
#ifdef TRACE
|
||||
for (int i = 0; i <= data.top; ++i)
|
||||
{
|
||||
trace << "state " << i << " ("
|
||||
<< data.a_->format_state(data.stack[i].s)
|
||||
<< ") has lowlink = " << data.stack[i].lowlink << std::endl;
|
||||
}
|
||||
for (int i = 0; i <= data.top; ++i)
|
||||
{
|
||||
trace << "state " << i << " ("
|
||||
<< data.a_->format_state(data.stack[i].s)
|
||||
<< ") has lowlink = " << data.stack[i].lowlink << std::endl;
|
||||
}
|
||||
#endif
|
||||
|
||||
// We will use the root of the last SCC as the start of the
|
||||
// cycle.
|
||||
int scc_root = data.stack[data.dftop].lowlink;
|
||||
assert(scc_root >= 0);
|
||||
// We will use the root of the last SCC as the start of the
|
||||
// cycle.
|
||||
int scc_root = data.stack[data.dftop].lowlink;
|
||||
assert(scc_root >= 0);
|
||||
|
||||
// Construct the prefix by unwinding the DFS stack before
|
||||
// scc_root.
|
||||
int father = data.stack[scc_root].pre;
|
||||
while (father >= 0)
|
||||
{
|
||||
twa_run::step st =
|
||||
{
|
||||
data.stack[father].s->clone(),
|
||||
data.stack[father].lasttr->cond(),
|
||||
data.stack[father].lasttr->acc()
|
||||
};
|
||||
res->prefix.push_front(st);
|
||||
father = data.stack[father].pre;
|
||||
}
|
||||
// Construct the prefix by unwinding the DFS stack before
|
||||
// scc_root.
|
||||
int father = data.stack[scc_root].pre;
|
||||
while (father >= 0)
|
||||
{
|
||||
twa_run::step st =
|
||||
{
|
||||
data.stack[father].s->clone(),
|
||||
data.stack[father].lasttr->cond(),
|
||||
data.stack[father].lasttr->acc()
|
||||
};
|
||||
res->prefix.push_front(st);
|
||||
father = data.stack[father].pre;
|
||||
}
|
||||
|
||||
// Construct the cycle in two phases. A first BFS finds the
|
||||
// shortest path from scc_root to an accepting transition.
|
||||
// A second BFS then search a path back to scc_root. If
|
||||
// there is no acceptance conditions we just use the second
|
||||
// BFS to find a cycle around scc_root.
|
||||
// Construct the cycle in two phases. A first BFS finds the
|
||||
// shortest path from scc_root to an accepting transition.
|
||||
// A second BFS then search a path back to scc_root. If
|
||||
// there is no acceptance conditions we just use the second
|
||||
// BFS to find a cycle around scc_root.
|
||||
|
||||
struct first_bfs: bfs_steps
|
||||
{
|
||||
const gv04& data;
|
||||
int scc_root;
|
||||
result* r;
|
||||
struct first_bfs: bfs_steps
|
||||
{
|
||||
const gv04& data;
|
||||
int scc_root;
|
||||
result* r;
|
||||
|
||||
first_bfs(result* r, int scc_root)
|
||||
: bfs_steps(r->data.automaton()), data(r->data),
|
||||
scc_root(scc_root), r(r)
|
||||
{
|
||||
}
|
||||
first_bfs(result* r, int scc_root)
|
||||
: bfs_steps(r->data.automaton()), data(r->data),
|
||||
scc_root(scc_root), r(r)
|
||||
{
|
||||
}
|
||||
|
||||
virtual const state*
|
||||
filter(const state* s) override
|
||||
{
|
||||
// Do not escape the SCC
|
||||
auto j = data.h.find(s);
|
||||
if (// This state was never visited so far.
|
||||
j == data.h.end()
|
||||
// Or it was discarded
|
||||
|| j->second >= data.stack.size()
|
||||
// Or it was discarded (but its stack slot reused)
|
||||
|| data.stack[j->second].s->compare(s)
|
||||
// Or it is still on the stack but not in the SCC
|
||||
|| data.stack[j->second].lowlink < scc_root)
|
||||
{
|
||||
s->destroy();
|
||||
return nullptr;
|
||||
}
|
||||
r->inc_ars_cycle_states();
|
||||
s->destroy();
|
||||
return j->first;
|
||||
}
|
||||
virtual const state*
|
||||
filter(const state* s) override
|
||||
{
|
||||
// Do not escape the SCC
|
||||
auto j = data.h.find(s);
|
||||
if (// This state was never visited so far.
|
||||
j == data.h.end()
|
||||
// Or it was discarded
|
||||
|| j->second >= data.stack.size()
|
||||
// Or it was discarded (but its stack slot reused)
|
||||
|| data.stack[j->second].s->compare(s)
|
||||
// Or it is still on the stack but not in the SCC
|
||||
|| data.stack[j->second].lowlink < scc_root)
|
||||
{
|
||||
s->destroy();
|
||||
return nullptr;
|
||||
}
|
||||
r->inc_ars_cycle_states();
|
||||
s->destroy();
|
||||
return j->first;
|
||||
}
|
||||
|
||||
virtual bool
|
||||
match(twa_run::step& step, const state*) override
|
||||
{
|
||||
return step.acc != 0U;
|
||||
}
|
||||
};
|
||||
virtual bool
|
||||
match(twa_run::step& step, const state*) override
|
||||
{
|
||||
return step.acc != 0U;
|
||||
}
|
||||
};
|
||||
|
||||
struct second_bfs final: first_bfs
|
||||
{
|
||||
const state* target;
|
||||
second_bfs(result* r, int scc_root, const state* target)
|
||||
: first_bfs(r, scc_root), target(target)
|
||||
{
|
||||
}
|
||||
struct second_bfs final: first_bfs
|
||||
{
|
||||
const state* target;
|
||||
second_bfs(result* r, int scc_root, const state* target)
|
||||
: first_bfs(r, scc_root), target(target)
|
||||
{
|
||||
}
|
||||
|
||||
virtual bool
|
||||
match(twa_run::step&, const state* s) override
|
||||
{
|
||||
return s == target;
|
||||
}
|
||||
};
|
||||
virtual bool
|
||||
match(twa_run::step&, const state* s) override
|
||||
{
|
||||
return s == target;
|
||||
}
|
||||
};
|
||||
|
||||
const state* bfs_start = data.stack[scc_root].s;
|
||||
const state* bfs_end = bfs_start;
|
||||
if (a_->num_sets() > 0)
|
||||
{
|
||||
first_bfs b1(this, scc_root);
|
||||
bfs_start = b1.search(bfs_start, res->cycle);
|
||||
assert(bfs_start);
|
||||
}
|
||||
if (bfs_start != bfs_end || res->cycle.empty())
|
||||
{
|
||||
second_bfs b2(this, scc_root, bfs_end);
|
||||
bfs_start = b2.search(bfs_start, res->cycle);
|
||||
assert(bfs_start == bfs_end);
|
||||
}
|
||||
const state* bfs_start = data.stack[scc_root].s;
|
||||
const state* bfs_end = bfs_start;
|
||||
if (a_->num_sets() > 0)
|
||||
{
|
||||
first_bfs b1(this, scc_root);
|
||||
bfs_start = b1.search(bfs_start, res->cycle);
|
||||
assert(bfs_start);
|
||||
}
|
||||
if (bfs_start != bfs_end || res->cycle.empty())
|
||||
{
|
||||
second_bfs b2(this, scc_root, bfs_end);
|
||||
bfs_start = b2.search(bfs_start, res->cycle);
|
||||
assert(bfs_start == bfs_end);
|
||||
}
|
||||
|
||||
assert(res->cycle.begin() != res->cycle.end());
|
||||
return res;
|
||||
}
|
||||
assert(res->cycle.begin() != res->cycle.end());
|
||||
return res;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -62,171 +62,171 @@ namespace spot
|
|||
sup_map sup;
|
||||
|
||||
metadata(const const_twa_graph_ptr& aut, bool implicit,
|
||||
bool state_labels)
|
||||
bool state_labels)
|
||||
{
|
||||
check_det_and_comp(aut);
|
||||
use_implicit_labels = implicit && is_deterministic && is_complete;
|
||||
use_state_labels &= state_labels;
|
||||
number_all_ap();
|
||||
check_det_and_comp(aut);
|
||||
use_implicit_labels = implicit && is_deterministic && is_complete;
|
||||
use_state_labels &= state_labels;
|
||||
number_all_ap();
|
||||
}
|
||||
|
||||
std::ostream&
|
||||
emit_acc(std::ostream& os, acc_cond::mark_t b)
|
||||
{
|
||||
// FIXME: We could use a cache for this.
|
||||
if (b == 0U)
|
||||
return os;
|
||||
os << " {";
|
||||
bool notfirst = false;
|
||||
for (auto v: b.sets())
|
||||
{
|
||||
if (notfirst)
|
||||
os << ' ';
|
||||
else
|
||||
notfirst = true;
|
||||
os << v;
|
||||
}
|
||||
os << '}';
|
||||
return os;
|
||||
// FIXME: We could use a cache for this.
|
||||
if (b == 0U)
|
||||
return os;
|
||||
os << " {";
|
||||
bool notfirst = false;
|
||||
for (auto v: b.sets())
|
||||
{
|
||||
if (notfirst)
|
||||
os << ' ';
|
||||
else
|
||||
notfirst = true;
|
||||
os << v;
|
||||
}
|
||||
os << '}';
|
||||
return os;
|
||||
}
|
||||
|
||||
void check_det_and_comp(const const_twa_graph_ptr& aut)
|
||||
{
|
||||
std::string empty;
|
||||
std::string empty;
|
||||
|
||||
unsigned ns = aut->num_states();
|
||||
bool deterministic = true;
|
||||
bool complete = true;
|
||||
bool state_acc = true;
|
||||
bool nodeadend = true;
|
||||
bool colored = aut->num_sets() >= 1;
|
||||
for (unsigned src = 0; src < ns; ++src)
|
||||
{
|
||||
bdd sum = bddfalse;
|
||||
bdd available = bddtrue;
|
||||
bool st_acc = true;
|
||||
bool notfirst = false;
|
||||
acc_cond::mark_t prev = 0U;
|
||||
bool has_succ = false;
|
||||
bdd lastcond = bddfalse;
|
||||
for (auto& t: aut->out(src))
|
||||
{
|
||||
if (has_succ)
|
||||
use_state_labels &= lastcond == t.cond;
|
||||
else
|
||||
lastcond = t.cond;
|
||||
if (complete)
|
||||
sum |= t.cond;
|
||||
if (deterministic)
|
||||
{
|
||||
if (!bdd_implies(t.cond, available))
|
||||
deterministic = false;
|
||||
else
|
||||
available -= t.cond;
|
||||
}
|
||||
sup.insert(std::make_pair(t.cond, empty));
|
||||
if (st_acc)
|
||||
{
|
||||
if (notfirst && prev != t.acc)
|
||||
{
|
||||
st_acc = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
notfirst = true;
|
||||
prev = t.acc;
|
||||
}
|
||||
}
|
||||
if (colored)
|
||||
{
|
||||
auto a = t.acc;
|
||||
if (!a || a.remove_some(1))
|
||||
colored = false;
|
||||
}
|
||||
has_succ = true;
|
||||
}
|
||||
nodeadend &= has_succ;
|
||||
if (complete)
|
||||
complete &= sum == bddtrue;
|
||||
common_acc.push_back(st_acc);
|
||||
state_acc &= st_acc;
|
||||
}
|
||||
is_deterministic = deterministic;
|
||||
is_complete = complete;
|
||||
has_state_acc = state_acc;
|
||||
// If the automaton has state-based acceptance and contain
|
||||
// some states without successors do not declare it as
|
||||
// colored.
|
||||
is_colored = colored && (!has_state_acc || nodeadend);
|
||||
// If the automaton declares that it is deterministic or
|
||||
// state-based, make sure that it really is.
|
||||
assert(deterministic || aut->prop_deterministic() != true);
|
||||
assert(state_acc || aut->prop_state_acc() != true);
|
||||
unsigned ns = aut->num_states();
|
||||
bool deterministic = true;
|
||||
bool complete = true;
|
||||
bool state_acc = true;
|
||||
bool nodeadend = true;
|
||||
bool colored = aut->num_sets() >= 1;
|
||||
for (unsigned src = 0; src < ns; ++src)
|
||||
{
|
||||
bdd sum = bddfalse;
|
||||
bdd available = bddtrue;
|
||||
bool st_acc = true;
|
||||
bool notfirst = false;
|
||||
acc_cond::mark_t prev = 0U;
|
||||
bool has_succ = false;
|
||||
bdd lastcond = bddfalse;
|
||||
for (auto& t: aut->out(src))
|
||||
{
|
||||
if (has_succ)
|
||||
use_state_labels &= lastcond == t.cond;
|
||||
else
|
||||
lastcond = t.cond;
|
||||
if (complete)
|
||||
sum |= t.cond;
|
||||
if (deterministic)
|
||||
{
|
||||
if (!bdd_implies(t.cond, available))
|
||||
deterministic = false;
|
||||
else
|
||||
available -= t.cond;
|
||||
}
|
||||
sup.insert(std::make_pair(t.cond, empty));
|
||||
if (st_acc)
|
||||
{
|
||||
if (notfirst && prev != t.acc)
|
||||
{
|
||||
st_acc = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
notfirst = true;
|
||||
prev = t.acc;
|
||||
}
|
||||
}
|
||||
if (colored)
|
||||
{
|
||||
auto a = t.acc;
|
||||
if (!a || a.remove_some(1))
|
||||
colored = false;
|
||||
}
|
||||
has_succ = true;
|
||||
}
|
||||
nodeadend &= has_succ;
|
||||
if (complete)
|
||||
complete &= sum == bddtrue;
|
||||
common_acc.push_back(st_acc);
|
||||
state_acc &= st_acc;
|
||||
}
|
||||
is_deterministic = deterministic;
|
||||
is_complete = complete;
|
||||
has_state_acc = state_acc;
|
||||
// If the automaton has state-based acceptance and contain
|
||||
// some states without successors do not declare it as
|
||||
// colored.
|
||||
is_colored = colored && (!has_state_acc || nodeadend);
|
||||
// If the automaton declares that it is deterministic or
|
||||
// state-based, make sure that it really is.
|
||||
assert(deterministic || aut->prop_deterministic() != true);
|
||||
assert(state_acc || aut->prop_state_acc() != true);
|
||||
}
|
||||
|
||||
void number_all_ap()
|
||||
{
|
||||
bdd all = bddtrue;
|
||||
for (auto& i: sup)
|
||||
all &= bdd_support(i.first);
|
||||
all_ap = all;
|
||||
bdd all = bddtrue;
|
||||
for (auto& i: sup)
|
||||
all &= bdd_support(i.first);
|
||||
all_ap = all;
|
||||
|
||||
while (all != bddtrue)
|
||||
{
|
||||
int v = bdd_var(all);
|
||||
all = bdd_high(all);
|
||||
ap.insert(std::make_pair(v, vap.size()));
|
||||
vap.push_back(v);
|
||||
}
|
||||
while (all != bddtrue)
|
||||
{
|
||||
int v = bdd_var(all);
|
||||
all = bdd_high(all);
|
||||
ap.insert(std::make_pair(v, vap.size()));
|
||||
vap.push_back(v);
|
||||
}
|
||||
|
||||
if (use_implicit_labels)
|
||||
return;
|
||||
if (use_implicit_labels)
|
||||
return;
|
||||
|
||||
for (auto& i: sup)
|
||||
{
|
||||
bdd cond = i.first;
|
||||
if (cond == bddtrue)
|
||||
{
|
||||
i.second = "t";
|
||||
continue;
|
||||
}
|
||||
if (cond == bddfalse)
|
||||
{
|
||||
i.second = "f";
|
||||
continue;
|
||||
}
|
||||
std::ostringstream s;
|
||||
bool notfirstor = false;
|
||||
for (auto& i: sup)
|
||||
{
|
||||
bdd cond = i.first;
|
||||
if (cond == bddtrue)
|
||||
{
|
||||
i.second = "t";
|
||||
continue;
|
||||
}
|
||||
if (cond == bddfalse)
|
||||
{
|
||||
i.second = "f";
|
||||
continue;
|
||||
}
|
||||
std::ostringstream s;
|
||||
bool notfirstor = false;
|
||||
|
||||
minato_isop isop(cond);
|
||||
bdd cube;
|
||||
while ((cube = isop.next()) != bddfalse)
|
||||
{
|
||||
if (notfirstor)
|
||||
s << " | ";
|
||||
bool notfirstand = false;
|
||||
while (cube != bddtrue)
|
||||
{
|
||||
if (notfirstand)
|
||||
s << '&';
|
||||
else
|
||||
notfirstand = true;
|
||||
bdd h = bdd_high(cube);
|
||||
if (h == bddfalse)
|
||||
{
|
||||
s << '!' << ap[bdd_var(cube)];
|
||||
cube = bdd_low(cube);
|
||||
}
|
||||
else
|
||||
{
|
||||
s << ap[bdd_var(cube)];
|
||||
cube = h;
|
||||
}
|
||||
}
|
||||
notfirstor = true;
|
||||
}
|
||||
i.second = s.str();
|
||||
}
|
||||
minato_isop isop(cond);
|
||||
bdd cube;
|
||||
while ((cube = isop.next()) != bddfalse)
|
||||
{
|
||||
if (notfirstor)
|
||||
s << " | ";
|
||||
bool notfirstand = false;
|
||||
while (cube != bddtrue)
|
||||
{
|
||||
if (notfirstand)
|
||||
s << '&';
|
||||
else
|
||||
notfirstand = true;
|
||||
bdd h = bdd_high(cube);
|
||||
if (h == bddfalse)
|
||||
{
|
||||
s << '!' << ap[bdd_var(cube)];
|
||||
cube = bdd_low(cube);
|
||||
}
|
||||
else
|
||||
{
|
||||
s << ap[bdd_var(cube)];
|
||||
cube = h;
|
||||
}
|
||||
}
|
||||
notfirstor = true;
|
||||
}
|
||||
i.second = s.str();
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -234,18 +234,18 @@ namespace spot
|
|||
|
||||
enum hoa_acceptance
|
||||
{
|
||||
Hoa_Acceptance_States, /// state-based acceptance if
|
||||
/// (globally) possible
|
||||
/// transition-based acceptance
|
||||
/// otherwise.
|
||||
Hoa_Acceptance_States, /// state-based acceptance if
|
||||
/// (globally) possible
|
||||
/// transition-based acceptance
|
||||
/// otherwise.
|
||||
Hoa_Acceptance_Transitions, /// transition-based acceptance globally
|
||||
Hoa_Acceptance_Mixed /// mix state-based and transition-based
|
||||
};
|
||||
|
||||
static std::ostream&
|
||||
print_hoa(std::ostream& os,
|
||||
const const_twa_graph_ptr& aut,
|
||||
const char* opt)
|
||||
const const_twa_graph_ptr& aut,
|
||||
const char* opt)
|
||||
{
|
||||
bool newline = true;
|
||||
hoa_acceptance acceptance = Hoa_Acceptance_States;
|
||||
|
|
@ -255,35 +255,35 @@ namespace spot
|
|||
|
||||
if (opt)
|
||||
while (*opt)
|
||||
{
|
||||
switch (char c = *opt++)
|
||||
{
|
||||
case 'i':
|
||||
implicit_labels = true;
|
||||
break;
|
||||
case 'k':
|
||||
state_labels = true;
|
||||
break;
|
||||
case 'l':
|
||||
newline = false;
|
||||
break;
|
||||
case 'm':
|
||||
acceptance = Hoa_Acceptance_Mixed;
|
||||
break;
|
||||
case 's':
|
||||
acceptance = Hoa_Acceptance_States;
|
||||
break;
|
||||
case 't':
|
||||
acceptance = Hoa_Acceptance_Transitions;
|
||||
break;
|
||||
case 'v':
|
||||
verbose = true;
|
||||
break;
|
||||
default:
|
||||
throw std::runtime_error
|
||||
(std::string("unknown option for print_hoa(): ") + c);
|
||||
}
|
||||
}
|
||||
{
|
||||
switch (char c = *opt++)
|
||||
{
|
||||
case 'i':
|
||||
implicit_labels = true;
|
||||
break;
|
||||
case 'k':
|
||||
state_labels = true;
|
||||
break;
|
||||
case 'l':
|
||||
newline = false;
|
||||
break;
|
||||
case 'm':
|
||||
acceptance = Hoa_Acceptance_Mixed;
|
||||
break;
|
||||
case 's':
|
||||
acceptance = Hoa_Acceptance_States;
|
||||
break;
|
||||
case 't':
|
||||
acceptance = Hoa_Acceptance_Transitions;
|
||||
break;
|
||||
case 'v':
|
||||
verbose = true;
|
||||
break;
|
||||
default:
|
||||
throw std::runtime_error
|
||||
(std::string("unknown option for print_hoa(): ") + c);
|
||||
}
|
||||
}
|
||||
|
||||
// Calling get_init_state_number() may add a state to empty
|
||||
// automata, so it has to be done first.
|
||||
|
|
@ -314,72 +314,72 @@ namespace spot
|
|||
acc_cond::acc_code acc_c = aut->acc().get_acceptance();
|
||||
if (aut->acc().is_generalized_buchi())
|
||||
{
|
||||
if (aut->acc().is_all())
|
||||
os << "acc-name: all";
|
||||
else if (aut->acc().is_buchi())
|
||||
os << "acc-name: Buchi";
|
||||
else
|
||||
os << "acc-name: generalized-Buchi " << num_acc;
|
||||
os << nl;
|
||||
if (aut->acc().is_all())
|
||||
os << "acc-name: all";
|
||||
else if (aut->acc().is_buchi())
|
||||
os << "acc-name: Buchi";
|
||||
else
|
||||
os << "acc-name: generalized-Buchi " << num_acc;
|
||||
os << nl;
|
||||
}
|
||||
else if (aut->acc().is_generalized_co_buchi())
|
||||
{
|
||||
if (aut->acc().is_none())
|
||||
os << "acc-name: none";
|
||||
else if (aut->acc().is_co_buchi())
|
||||
os << "acc-name: co-Buchi";
|
||||
else
|
||||
os << "acc-name: generalized-co-Buchi " << num_acc;
|
||||
os << nl;
|
||||
if (aut->acc().is_none())
|
||||
os << "acc-name: none";
|
||||
else if (aut->acc().is_co_buchi())
|
||||
os << "acc-name: co-Buchi";
|
||||
else
|
||||
os << "acc-name: generalized-co-Buchi " << num_acc;
|
||||
os << nl;
|
||||
}
|
||||
else
|
||||
{
|
||||
int r = aut->acc().is_rabin();
|
||||
assert(r != 0);
|
||||
if (r > 0)
|
||||
{
|
||||
os << "acc-name: Rabin " << r << nl;
|
||||
// Force the acceptance to remove any duplicate sets, and
|
||||
// make sure it is correctly ordered.
|
||||
acc_c = acc_cond::acc_code::rabin(r);
|
||||
}
|
||||
else
|
||||
{
|
||||
r = aut->acc().is_streett();
|
||||
assert(r != 0);
|
||||
if (r > 0)
|
||||
{
|
||||
os << "acc-name: Streett " << r << nl;
|
||||
// Force the acceptance to remove any duplicate sets, and
|
||||
// make sure it is correctly ordered.
|
||||
acc_c = acc_cond::acc_code::streett(r);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::vector<unsigned> pairs;
|
||||
if (aut->acc().is_generalized_rabin(pairs))
|
||||
{
|
||||
os << "acc-name: generalized-Rabin " << pairs.size();
|
||||
for (auto p: pairs)
|
||||
os << ' ' << p;
|
||||
os << nl;
|
||||
// Force the acceptance to remove any duplicate
|
||||
// sets, and make sure it is correctly ordered.
|
||||
acc_c = acc_cond::acc_code::generalized_rabin(pairs.begin(),
|
||||
pairs.end());
|
||||
}
|
||||
else
|
||||
{
|
||||
bool max = false;
|
||||
bool odd = false;
|
||||
if (aut->acc().is_parity(max, odd))
|
||||
os << "acc-name: parity "
|
||||
<< (max ? "max " : "min ")
|
||||
<< (odd ? "odd " : "even ")
|
||||
<< num_acc << nl;
|
||||
}
|
||||
}
|
||||
}
|
||||
int r = aut->acc().is_rabin();
|
||||
assert(r != 0);
|
||||
if (r > 0)
|
||||
{
|
||||
os << "acc-name: Rabin " << r << nl;
|
||||
// Force the acceptance to remove any duplicate sets, and
|
||||
// make sure it is correctly ordered.
|
||||
acc_c = acc_cond::acc_code::rabin(r);
|
||||
}
|
||||
else
|
||||
{
|
||||
r = aut->acc().is_streett();
|
||||
assert(r != 0);
|
||||
if (r > 0)
|
||||
{
|
||||
os << "acc-name: Streett " << r << nl;
|
||||
// Force the acceptance to remove any duplicate sets, and
|
||||
// make sure it is correctly ordered.
|
||||
acc_c = acc_cond::acc_code::streett(r);
|
||||
}
|
||||
else
|
||||
{
|
||||
std::vector<unsigned> pairs;
|
||||
if (aut->acc().is_generalized_rabin(pairs))
|
||||
{
|
||||
os << "acc-name: generalized-Rabin " << pairs.size();
|
||||
for (auto p: pairs)
|
||||
os << ' ' << p;
|
||||
os << nl;
|
||||
// Force the acceptance to remove any duplicate
|
||||
// sets, and make sure it is correctly ordered.
|
||||
acc_c = acc_cond::acc_code::generalized_rabin(pairs.begin(),
|
||||
pairs.end());
|
||||
}
|
||||
else
|
||||
{
|
||||
bool max = false;
|
||||
bool odd = false;
|
||||
if (aut->acc().is_parity(max, odd))
|
||||
os << "acc-name: parity "
|
||||
<< (max ? "max " : "min ")
|
||||
<< (odd ? "odd " : "even ")
|
||||
<< num_acc << nl;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
os << "Acceptance: " << num_acc << ' ';
|
||||
os << acc_c;
|
||||
|
|
@ -390,17 +390,17 @@ namespace spot
|
|||
unsigned prop_len = 60;
|
||||
auto prop = [&](const char* str)
|
||||
{
|
||||
if (newline)
|
||||
{
|
||||
auto l = strlen(str);
|
||||
if (prop_len < l)
|
||||
{
|
||||
prop_len = 60;
|
||||
os << "\nproperties:";
|
||||
}
|
||||
prop_len -= l;
|
||||
}
|
||||
os << str;
|
||||
if (newline)
|
||||
{
|
||||
auto l = strlen(str);
|
||||
if (prop_len < l)
|
||||
{
|
||||
prop_len = 60;
|
||||
os << "\nproperties:";
|
||||
}
|
||||
prop_len -= l;
|
||||
}
|
||||
os << str;
|
||||
};
|
||||
// We do not support alternating automata so far, and it's
|
||||
// probable that nobody cares about the "no-univ-branch"
|
||||
|
|
@ -452,132 +452,132 @@ namespace spot
|
|||
std::vector<acc_cond::mark_t> outm;
|
||||
if (implicit_labels)
|
||||
{
|
||||
out.resize(1UL << nap);
|
||||
if (acceptance != Hoa_Acceptance_States)
|
||||
outm.resize(1UL << nap);
|
||||
out.resize(1UL << nap);
|
||||
if (acceptance != Hoa_Acceptance_States)
|
||||
outm.resize(1UL << nap);
|
||||
}
|
||||
|
||||
os << "--BODY--" << nl;
|
||||
auto sn = aut->get_named_prop<std::vector<std::string>>("state-names");
|
||||
for (unsigned i = 0; i < num_states; ++i)
|
||||
{
|
||||
hoa_acceptance this_acc = acceptance;
|
||||
if (this_acc == Hoa_Acceptance_Mixed)
|
||||
this_acc = (md.common_acc[i] ?
|
||||
Hoa_Acceptance_States : Hoa_Acceptance_Transitions);
|
||||
hoa_acceptance this_acc = acceptance;
|
||||
if (this_acc == Hoa_Acceptance_Mixed)
|
||||
this_acc = (md.common_acc[i] ?
|
||||
Hoa_Acceptance_States : Hoa_Acceptance_Transitions);
|
||||
|
||||
os << "State: ";
|
||||
if (state_labels)
|
||||
{
|
||||
bool output = false;
|
||||
for (auto& t: aut->out(i))
|
||||
{
|
||||
os << '[' << md.sup[t.cond] << "] ";
|
||||
output = true;
|
||||
break;
|
||||
}
|
||||
if (!output)
|
||||
os << "[f] ";
|
||||
}
|
||||
os << i;
|
||||
if (sn && i < sn->size() && !(*sn)[i].empty())
|
||||
os << " \"" << (*sn)[i] << '"';
|
||||
if (this_acc == Hoa_Acceptance_States)
|
||||
{
|
||||
acc_cond::mark_t acc = 0U;
|
||||
for (auto& t: aut->out(i))
|
||||
{
|
||||
acc = t.acc;
|
||||
break;
|
||||
}
|
||||
md.emit_acc(os, acc);
|
||||
}
|
||||
os << nl;
|
||||
os << "State: ";
|
||||
if (state_labels)
|
||||
{
|
||||
bool output = false;
|
||||
for (auto& t: aut->out(i))
|
||||
{
|
||||
os << '[' << md.sup[t.cond] << "] ";
|
||||
output = true;
|
||||
break;
|
||||
}
|
||||
if (!output)
|
||||
os << "[f] ";
|
||||
}
|
||||
os << i;
|
||||
if (sn && i < sn->size() && !(*sn)[i].empty())
|
||||
os << " \"" << (*sn)[i] << '"';
|
||||
if (this_acc == Hoa_Acceptance_States)
|
||||
{
|
||||
acc_cond::mark_t acc = 0U;
|
||||
for (auto& t: aut->out(i))
|
||||
{
|
||||
acc = t.acc;
|
||||
break;
|
||||
}
|
||||
md.emit_acc(os, acc);
|
||||
}
|
||||
os << nl;
|
||||
|
||||
if (!implicit_labels && !state_labels)
|
||||
{
|
||||
if (!implicit_labels && !state_labels)
|
||||
{
|
||||
|
||||
for (auto& t: aut->out(i))
|
||||
{
|
||||
os << '[' << md.sup[t.cond] << "] " << t.dst;
|
||||
if (this_acc == Hoa_Acceptance_Transitions)
|
||||
md.emit_acc(os, t.acc);
|
||||
os << nl;
|
||||
}
|
||||
}
|
||||
else if (state_labels)
|
||||
{
|
||||
unsigned n = 0;
|
||||
for (auto& t: aut->out(i))
|
||||
{
|
||||
os << t.dst;
|
||||
if (this_acc == Hoa_Acceptance_Transitions)
|
||||
{
|
||||
md.emit_acc(os, t.acc);
|
||||
os << nl;
|
||||
}
|
||||
else
|
||||
{
|
||||
++n;
|
||||
os << (((n & 15) && t.next_succ) ? ' ' : nl);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (auto& t: aut->out(i))
|
||||
{
|
||||
bdd cond = t.cond;
|
||||
while (cond != bddfalse)
|
||||
{
|
||||
bdd one = bdd_satoneset(cond, md.all_ap, bddfalse);
|
||||
cond -= one;
|
||||
unsigned level = 1;
|
||||
unsigned pos = 0U;
|
||||
while (one != bddtrue)
|
||||
{
|
||||
bdd h = bdd_high(one);
|
||||
if (h == bddfalse)
|
||||
{
|
||||
one = bdd_low(one);
|
||||
}
|
||||
else
|
||||
{
|
||||
pos |= level;
|
||||
one = h;
|
||||
}
|
||||
level <<= 1;
|
||||
}
|
||||
out[pos] = t.dst;
|
||||
if (this_acc != Hoa_Acceptance_States)
|
||||
outm[pos] = t.acc;
|
||||
}
|
||||
}
|
||||
unsigned n = out.size();
|
||||
for (unsigned i = 0; i < n;)
|
||||
{
|
||||
os << out[i];
|
||||
if (this_acc != Hoa_Acceptance_States)
|
||||
{
|
||||
md.emit_acc(os, outm[i]) << nl;
|
||||
++i;
|
||||
}
|
||||
else
|
||||
{
|
||||
++i;
|
||||
os << (((i & 15) && i < n) ? ' ' : nl);
|
||||
}
|
||||
}
|
||||
}
|
||||
for (auto& t: aut->out(i))
|
||||
{
|
||||
os << '[' << md.sup[t.cond] << "] " << t.dst;
|
||||
if (this_acc == Hoa_Acceptance_Transitions)
|
||||
md.emit_acc(os, t.acc);
|
||||
os << nl;
|
||||
}
|
||||
}
|
||||
else if (state_labels)
|
||||
{
|
||||
unsigned n = 0;
|
||||
for (auto& t: aut->out(i))
|
||||
{
|
||||
os << t.dst;
|
||||
if (this_acc == Hoa_Acceptance_Transitions)
|
||||
{
|
||||
md.emit_acc(os, t.acc);
|
||||
os << nl;
|
||||
}
|
||||
else
|
||||
{
|
||||
++n;
|
||||
os << (((n & 15) && t.next_succ) ? ' ' : nl);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
for (auto& t: aut->out(i))
|
||||
{
|
||||
bdd cond = t.cond;
|
||||
while (cond != bddfalse)
|
||||
{
|
||||
bdd one = bdd_satoneset(cond, md.all_ap, bddfalse);
|
||||
cond -= one;
|
||||
unsigned level = 1;
|
||||
unsigned pos = 0U;
|
||||
while (one != bddtrue)
|
||||
{
|
||||
bdd h = bdd_high(one);
|
||||
if (h == bddfalse)
|
||||
{
|
||||
one = bdd_low(one);
|
||||
}
|
||||
else
|
||||
{
|
||||
pos |= level;
|
||||
one = h;
|
||||
}
|
||||
level <<= 1;
|
||||
}
|
||||
out[pos] = t.dst;
|
||||
if (this_acc != Hoa_Acceptance_States)
|
||||
outm[pos] = t.acc;
|
||||
}
|
||||
}
|
||||
unsigned n = out.size();
|
||||
for (unsigned i = 0; i < n;)
|
||||
{
|
||||
os << out[i];
|
||||
if (this_acc != Hoa_Acceptance_States)
|
||||
{
|
||||
md.emit_acc(os, outm[i]) << nl;
|
||||
++i;
|
||||
}
|
||||
else
|
||||
{
|
||||
++i;
|
||||
os << (((i & 15) && i < n) ? ' ' : nl);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
os << "--END--"; // No newline. Let the caller decide.
|
||||
os << "--END--"; // No newline. Let the caller decide.
|
||||
return os;
|
||||
}
|
||||
|
||||
std::ostream&
|
||||
print_hoa(std::ostream& os,
|
||||
const const_twa_ptr& aut,
|
||||
const char* opt)
|
||||
const const_twa_ptr& aut,
|
||||
const char* opt)
|
||||
{
|
||||
|
||||
auto a = std::dynamic_pointer_cast<const twa_graph>(aut);
|
||||
|
|
@ -588,12 +588,12 @@ namespace spot
|
|||
char* tmpopt = nullptr;
|
||||
if (std::dynamic_pointer_cast<const fair_kripke>(aut))
|
||||
{
|
||||
unsigned n = opt ? strlen(opt) : 0;
|
||||
tmpopt = new char[n + 2];
|
||||
if (opt)
|
||||
strcpy(tmpopt, opt);
|
||||
tmpopt[n] = 'k';
|
||||
tmpopt[n + 1] = 0;
|
||||
unsigned n = opt ? strlen(opt) : 0;
|
||||
tmpopt = new char[n + 2];
|
||||
if (opt)
|
||||
strcpy(tmpopt, opt);
|
||||
tmpopt[n] = 'k';
|
||||
tmpopt[n + 1] = 0;
|
||||
}
|
||||
print_hoa(os, a, tmpopt ? tmpopt : opt);
|
||||
delete[] tmpopt;
|
||||
|
|
|
|||
|
|
@ -38,6 +38,6 @@ namespace spot
|
|||
/// single-line output, (v) verbose properties.
|
||||
SPOT_API std::ostream&
|
||||
print_hoa(std::ostream& os,
|
||||
const const_twa_ptr& g,
|
||||
const char* opt = nullptr);
|
||||
const const_twa_ptr& g,
|
||||
const char* opt = nullptr);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,23 +33,23 @@ namespace spot
|
|||
unsigned nondet_states = 0;
|
||||
unsigned ns = aut->num_states();
|
||||
for (unsigned src = 0; src < ns; ++src)
|
||||
{
|
||||
bdd available = bddtrue;
|
||||
for (auto& t: aut->out(src))
|
||||
if (!bdd_implies(t.cond, available))
|
||||
{
|
||||
++nondet_states;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
available -= t.cond;
|
||||
}
|
||||
// If we are not counting non-deterministic states, abort as
|
||||
// soon as possible.
|
||||
if (!count && nondet_states)
|
||||
break;
|
||||
}
|
||||
{
|
||||
bdd available = bddtrue;
|
||||
for (auto& t: aut->out(src))
|
||||
if (!bdd_implies(t.cond, available))
|
||||
{
|
||||
++nondet_states;
|
||||
break;
|
||||
}
|
||||
else
|
||||
{
|
||||
available -= t.cond;
|
||||
}
|
||||
// If we are not counting non-deterministic states, abort as
|
||||
// soon as possible.
|
||||
if (!count && nondet_states)
|
||||
break;
|
||||
}
|
||||
return nondet_states;
|
||||
}
|
||||
}
|
||||
|
|
@ -75,11 +75,11 @@ namespace spot
|
|||
unsigned ns = aut->num_states();
|
||||
for (unsigned src = 0; src < ns; ++src)
|
||||
{
|
||||
bdd available = bddtrue;
|
||||
for (auto& t: aut->out(src))
|
||||
available -= t.cond;
|
||||
if (available != bddfalse)
|
||||
return false;
|
||||
bdd available = bddtrue;
|
||||
for (auto& t: aut->out(src))
|
||||
available -= t.cond;
|
||||
if (available != bddfalse)
|
||||
return false;
|
||||
}
|
||||
// The empty automaton is not complete since it does not have an
|
||||
// initial state.
|
||||
|
|
|
|||
|
|
@ -37,7 +37,7 @@ namespace spot
|
|||
auto prod = product(clean_a, clean_a);
|
||||
auto clean_p = scc_filter_states(prod);
|
||||
return (clean_a->num_states() == clean_p->num_states()
|
||||
&& clean_a->num_edges() == clean_p->num_edges());
|
||||
&& clean_a->num_edges() == clean_p->num_edges());
|
||||
}
|
||||
|
||||
bool check_unambiguous(const twa_graph_ptr& aut)
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ namespace spot
|
|||
keep[s] = true;
|
||||
auto sccaut = mask_keep_states(aut, keep, states.front());
|
||||
sccaut->set_acceptance(sccaut->acc().num_sets(),
|
||||
sccaut->get_acceptance().complement());
|
||||
sccaut->get_acceptance().complement());
|
||||
return !sccaut->is_empty();
|
||||
}
|
||||
|
||||
|
|
@ -67,18 +67,18 @@ namespace spot
|
|||
auto a = map.get_aut();
|
||||
for (auto s: map.states_of(scc))
|
||||
{
|
||||
bool has_succ = false;
|
||||
bdd sumall = bddfalse;
|
||||
for (auto& t: a->out(s))
|
||||
{
|
||||
has_succ = true;
|
||||
if (map.scc_of(t.dst) == scc)
|
||||
sumall |= t.cond;
|
||||
if (sumall == bddtrue)
|
||||
break;
|
||||
}
|
||||
if (!has_succ || sumall != bddtrue)
|
||||
return false;
|
||||
bool has_succ = false;
|
||||
bdd sumall = bddfalse;
|
||||
for (auto& t: a->out(s))
|
||||
{
|
||||
has_succ = true;
|
||||
if (map.scc_of(t.dst) == scc)
|
||||
sumall |= t.cond;
|
||||
if (sumall == bddtrue)
|
||||
break;
|
||||
}
|
||||
if (!has_succ || sumall != bddtrue)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
|
@ -88,7 +88,7 @@ namespace spot
|
|||
{
|
||||
// If all transitions use all acceptance conditions, the SCC is weak.
|
||||
return (map.is_accepting_scc(scc)
|
||||
&& map.used_acc_of(scc).size() == 1
|
||||
&& is_complete_scc(map, scc));
|
||||
&& map.used_acc_of(scc).size() == 1
|
||||
&& is_complete_scc(map, scc));
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,83 +38,83 @@ namespace spot
|
|||
{
|
||||
public:
|
||||
lbtt_bfs(const const_twa_ptr& a, std::ostream& os, bool sba_format)
|
||||
: twa_reachable_iterator_breadth_first(a),
|
||||
os_(os),
|
||||
sba_format_(sba_format),
|
||||
sba_(nullptr)
|
||||
: twa_reachable_iterator_breadth_first(a),
|
||||
os_(os),
|
||||
sba_format_(sba_format),
|
||||
sba_(nullptr)
|
||||
{
|
||||
// Check if the automaton can be converted into a
|
||||
// twa_graph. This makes the state_is_accepting() function
|
||||
// more efficient.
|
||||
if (a->is_sba())
|
||||
sba_ = std::dynamic_pointer_cast<const twa_graph>(a);
|
||||
// Check if the automaton can be converted into a
|
||||
// twa_graph. This makes the state_is_accepting() function
|
||||
// more efficient.
|
||||
if (a->is_sba())
|
||||
sba_ = std::dynamic_pointer_cast<const twa_graph>(a);
|
||||
}
|
||||
|
||||
acc_cond::mark_t
|
||||
state_acc_sets(const state *s) const
|
||||
{
|
||||
// If the automaton has a SBA type, it's easier to just query the
|
||||
// state_is_accepting() method.
|
||||
if (sba_)
|
||||
return sba_->state_acc_sets(sba_->state_number(s));
|
||||
// If the automaton has a SBA type, it's easier to just query the
|
||||
// state_is_accepting() method.
|
||||
if (sba_)
|
||||
return sba_->state_acc_sets(sba_->state_number(s));
|
||||
|
||||
// Otherwise, since we are dealing with a degeneralized
|
||||
// automaton nonetheless, the transitions leaving an accepting
|
||||
// state are either all accepting, or all non-accepting. So
|
||||
// we just check the acceptance of the first transition. This
|
||||
// is not terribly efficient since we have to create the
|
||||
// iterator.
|
||||
twa_succ_iterator* it = aut_->succ_iter(s);
|
||||
if (!it->first())
|
||||
return {};
|
||||
auto res = it->acc();
|
||||
aut_->release_iter(it);
|
||||
return res;
|
||||
// Otherwise, since we are dealing with a degeneralized
|
||||
// automaton nonetheless, the transitions leaving an accepting
|
||||
// state are either all accepting, or all non-accepting. So
|
||||
// we just check the acceptance of the first transition. This
|
||||
// is not terribly efficient since we have to create the
|
||||
// iterator.
|
||||
twa_succ_iterator* it = aut_->succ_iter(s);
|
||||
if (!it->first())
|
||||
return {};
|
||||
auto res = it->acc();
|
||||
aut_->release_iter(it);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
void
|
||||
process_state(const state* s, int n, twa_succ_iterator*)
|
||||
{
|
||||
--n;
|
||||
if (n == 0)
|
||||
body_ << "0 1";
|
||||
else
|
||||
body_ << "-1\n" << n << " 0";
|
||||
// Do we have state-based acceptance?
|
||||
if (sba_format_)
|
||||
{
|
||||
for (auto i: state_acc_sets(s).sets())
|
||||
body_ << ' ' << i;
|
||||
body_ << " -1";
|
||||
}
|
||||
body_ << '\n';
|
||||
--n;
|
||||
if (n == 0)
|
||||
body_ << "0 1";
|
||||
else
|
||||
body_ << "-1\n" << n << " 0";
|
||||
// Do we have state-based acceptance?
|
||||
if (sba_format_)
|
||||
{
|
||||
for (auto i: state_acc_sets(s).sets())
|
||||
body_ << ' ' << i;
|
||||
body_ << " -1";
|
||||
}
|
||||
body_ << '\n';
|
||||
}
|
||||
|
||||
void
|
||||
process_link(const state*, int,
|
||||
const state*, int out, const twa_succ_iterator* si)
|
||||
const state*, int out, const twa_succ_iterator* si)
|
||||
{
|
||||
body_ << out - 1 << ' ';
|
||||
if (!sba_format_)
|
||||
{
|
||||
for (auto s: si->acc().sets())
|
||||
body_ << s << ' ';
|
||||
body_ << "-1 ";
|
||||
}
|
||||
print_lbt_ltl(body_, bdd_to_formula(si->cond(),
|
||||
aut_->get_dict())) << '\n';
|
||||
body_ << out - 1 << ' ';
|
||||
if (!sba_format_)
|
||||
{
|
||||
for (auto s: si->acc().sets())
|
||||
body_ << s << ' ';
|
||||
body_ << "-1 ";
|
||||
}
|
||||
print_lbt_ltl(body_, bdd_to_formula(si->cond(),
|
||||
aut_->get_dict())) << '\n';
|
||||
}
|
||||
|
||||
void
|
||||
end()
|
||||
{
|
||||
os_ << seen.size() << ' ';
|
||||
if (sba_format_)
|
||||
os_ << aut_->num_sets();
|
||||
else
|
||||
os_ << aut_->num_sets() << 't';
|
||||
os_ << '\n' << body_.str() << "-1" << std::endl;
|
||||
os_ << seen.size() << ' ';
|
||||
if (sba_format_)
|
||||
os_ << aut_->num_sets();
|
||||
else
|
||||
os_ << aut_->num_sets() << 't';
|
||||
os_ << '\n' << body_.str() << "-1" << std::endl;
|
||||
}
|
||||
|
||||
private:
|
||||
|
|
@ -131,19 +131,19 @@ namespace spot
|
|||
{
|
||||
if (!g->acc().is_generalized_buchi())
|
||||
throw std::runtime_error
|
||||
("LBTT only supports generalized Büchi acceptance");
|
||||
("LBTT only supports generalized Büchi acceptance");
|
||||
|
||||
bool sba = g->prop_state_acc().is_true();
|
||||
if (opt)
|
||||
switch (char c = *opt++)
|
||||
{
|
||||
case 't':
|
||||
sba = false;
|
||||
break;
|
||||
default:
|
||||
throw std::runtime_error
|
||||
(std::string("unknown option for print_lbtt(): ") + c);
|
||||
}
|
||||
{
|
||||
case 't':
|
||||
sba = false;
|
||||
break;
|
||||
default:
|
||||
throw std::runtime_error
|
||||
(std::string("unknown option for print_lbtt(): ") + c);
|
||||
}
|
||||
|
||||
lbtt_bfs b(g, os, sba);
|
||||
b.run();
|
||||
|
|
|
|||
|
|
@ -36,5 +36,5 @@ namespace spot
|
|||
// default to state-based acceptance when the automaton is marked so.
|
||||
SPOT_API std::ostream&
|
||||
print_lbtt(std::ostream& os, const const_twa_ptr& g,
|
||||
const char* opt = nullptr);
|
||||
const char* opt = nullptr);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -33,10 +33,10 @@ namespace spot
|
|||
{
|
||||
public:
|
||||
ltl2taa_visitor(const taa_tgba_formula_ptr& res,
|
||||
language_containment_checker* lcc,
|
||||
bool refined = false, bool negated = false)
|
||||
: res_(res), refined_(refined), negated_(negated),
|
||||
lcc_(lcc), init_(), succ_()
|
||||
language_containment_checker* lcc,
|
||||
bool refined = false, bool negated = false)
|
||||
: res_(res), refined_(refined), negated_(negated),
|
||||
lcc_(lcc), init_(), succ_()
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -47,272 +47,272 @@ namespace spot
|
|||
taa_tgba_formula_ptr&
|
||||
result()
|
||||
{
|
||||
res_->set_init_state(init_);
|
||||
return res_;
|
||||
res_->set_init_state(init_);
|
||||
return res_;
|
||||
}
|
||||
|
||||
void
|
||||
visit(formula f)
|
||||
{
|
||||
init_ = f;
|
||||
switch (f.kind())
|
||||
{
|
||||
case op::ff:
|
||||
return;
|
||||
case op::tt:
|
||||
{
|
||||
std::vector<formula> empty;
|
||||
res_->create_transition(init_, empty);
|
||||
succ_state ss = { empty, f, empty };
|
||||
succ_.push_back(ss);
|
||||
return;
|
||||
}
|
||||
case op::eword:
|
||||
SPOT_UNIMPLEMENTED();
|
||||
case op::ap:
|
||||
{
|
||||
res_->register_ap(f);
|
||||
if (negated_)
|
||||
f = formula::Not(f);
|
||||
init_ = f;
|
||||
std::vector<formula> empty;
|
||||
taa_tgba::transition* t = res_->create_transition(init_, empty);
|
||||
res_->add_condition(t, f);
|
||||
succ_state ss = { empty, f, empty };
|
||||
succ_.push_back(ss);
|
||||
return;
|
||||
}
|
||||
case op::X:
|
||||
{
|
||||
ltl2taa_visitor v = recurse(f[0]);
|
||||
std::vector<formula> dst;
|
||||
std::vector<formula> a;
|
||||
if (v.succ_.empty()) // Handle X(0)
|
||||
return;
|
||||
dst.push_back(v.init_);
|
||||
res_->create_transition(init_, dst);
|
||||
succ_state ss = { dst, formula::tt(), a };
|
||||
succ_.push_back(ss);
|
||||
return;
|
||||
}
|
||||
case op::F:
|
||||
case op::G:
|
||||
SPOT_UNIMPLEMENTED(); // TBD
|
||||
return;
|
||||
case op::Not:
|
||||
{
|
||||
negated_ = true;
|
||||
ltl2taa_visitor v = recurse(f[0]);
|
||||
// Done in recurse
|
||||
succ_ = v.succ_;
|
||||
return;
|
||||
}
|
||||
case op::Closure:
|
||||
case op::NegClosure:
|
||||
case op::NegClosureMarked:
|
||||
case op::Star:
|
||||
case op::FStar:
|
||||
case op::Xor:
|
||||
case op::Implies:
|
||||
case op::Equiv:
|
||||
case op::UConcat:
|
||||
case op::EConcat:
|
||||
case op::EConcatMarked:
|
||||
case op::Concat:
|
||||
case op::Fusion:
|
||||
case op::AndNLM:
|
||||
case op::AndRat:
|
||||
case op::OrRat:
|
||||
SPOT_UNIMPLEMENTED();
|
||||
init_ = f;
|
||||
switch (f.kind())
|
||||
{
|
||||
case op::ff:
|
||||
return;
|
||||
case op::tt:
|
||||
{
|
||||
std::vector<formula> empty;
|
||||
res_->create_transition(init_, empty);
|
||||
succ_state ss = { empty, f, empty };
|
||||
succ_.push_back(ss);
|
||||
return;
|
||||
}
|
||||
case op::eword:
|
||||
SPOT_UNIMPLEMENTED();
|
||||
case op::ap:
|
||||
{
|
||||
res_->register_ap(f);
|
||||
if (negated_)
|
||||
f = formula::Not(f);
|
||||
init_ = f;
|
||||
std::vector<formula> empty;
|
||||
taa_tgba::transition* t = res_->create_transition(init_, empty);
|
||||
res_->add_condition(t, f);
|
||||
succ_state ss = { empty, f, empty };
|
||||
succ_.push_back(ss);
|
||||
return;
|
||||
}
|
||||
case op::X:
|
||||
{
|
||||
ltl2taa_visitor v = recurse(f[0]);
|
||||
std::vector<formula> dst;
|
||||
std::vector<formula> a;
|
||||
if (v.succ_.empty()) // Handle X(0)
|
||||
return;
|
||||
dst.push_back(v.init_);
|
||||
res_->create_transition(init_, dst);
|
||||
succ_state ss = { dst, formula::tt(), a };
|
||||
succ_.push_back(ss);
|
||||
return;
|
||||
}
|
||||
case op::F:
|
||||
case op::G:
|
||||
SPOT_UNIMPLEMENTED(); // TBD
|
||||
return;
|
||||
case op::Not:
|
||||
{
|
||||
negated_ = true;
|
||||
ltl2taa_visitor v = recurse(f[0]);
|
||||
// Done in recurse
|
||||
succ_ = v.succ_;
|
||||
return;
|
||||
}
|
||||
case op::Closure:
|
||||
case op::NegClosure:
|
||||
case op::NegClosureMarked:
|
||||
case op::Star:
|
||||
case op::FStar:
|
||||
case op::Xor:
|
||||
case op::Implies:
|
||||
case op::Equiv:
|
||||
case op::UConcat:
|
||||
case op::EConcat:
|
||||
case op::EConcatMarked:
|
||||
case op::Concat:
|
||||
case op::Fusion:
|
||||
case op::AndNLM:
|
||||
case op::AndRat:
|
||||
case op::OrRat:
|
||||
SPOT_UNIMPLEMENTED();
|
||||
|
||||
case op::U:
|
||||
case op::W:
|
||||
case op::R:
|
||||
case op::M:
|
||||
visit_binop(f);
|
||||
return;
|
||||
case op::And:
|
||||
case op::Or:
|
||||
visit_multop(f);
|
||||
return;
|
||||
}
|
||||
case op::U:
|
||||
case op::W:
|
||||
case op::R:
|
||||
case op::M:
|
||||
visit_binop(f);
|
||||
return;
|
||||
case op::And:
|
||||
case op::Or:
|
||||
visit_multop(f);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
visit_binop(formula f)
|
||||
{
|
||||
ltl2taa_visitor v1 = recurse(f[0]);
|
||||
ltl2taa_visitor v2 = recurse(f[1]);
|
||||
ltl2taa_visitor v1 = recurse(f[0]);
|
||||
ltl2taa_visitor v2 = recurse(f[1]);
|
||||
|
||||
std::vector<succ_state>::iterator i1;
|
||||
std::vector<succ_state>::iterator i2;
|
||||
taa_tgba::transition* t = nullptr;
|
||||
bool contained = false;
|
||||
bool strong = false;
|
||||
std::vector<succ_state>::iterator i1;
|
||||
std::vector<succ_state>::iterator i2;
|
||||
taa_tgba::transition* t = nullptr;
|
||||
bool contained = false;
|
||||
bool strong = false;
|
||||
|
||||
switch (f.kind())
|
||||
{
|
||||
case op::U:
|
||||
strong = true;
|
||||
// fall thru
|
||||
case op::W:
|
||||
if (refined_)
|
||||
contained = lcc_->contained(f[0], f[1]);
|
||||
for (i1 = v1.succ_.begin(); i1 != v1.succ_.end(); ++i1)
|
||||
{
|
||||
// Refined rule
|
||||
if (refined_ && contained)
|
||||
i1->Q.erase
|
||||
(remove(i1->Q.begin(), i1->Q.end(), v1.init_), i1->Q.end());
|
||||
switch (f.kind())
|
||||
{
|
||||
case op::U:
|
||||
strong = true;
|
||||
// fall thru
|
||||
case op::W:
|
||||
if (refined_)
|
||||
contained = lcc_->contained(f[0], f[1]);
|
||||
for (i1 = v1.succ_.begin(); i1 != v1.succ_.end(); ++i1)
|
||||
{
|
||||
// Refined rule
|
||||
if (refined_ && contained)
|
||||
i1->Q.erase
|
||||
(remove(i1->Q.begin(), i1->Q.end(), v1.init_), i1->Q.end());
|
||||
|
||||
i1->Q.push_back(init_); // Add the initial state
|
||||
if (strong)
|
||||
i1->acc.push_back(f[1]);
|
||||
t = res_->create_transition(init_, i1->Q);
|
||||
res_->add_condition(t, i1->condition);
|
||||
if (strong)
|
||||
res_->add_acceptance_condition(t, f[1]);
|
||||
else
|
||||
for (unsigned i = 0; i < i1->acc.size(); ++i)
|
||||
res_->add_acceptance_condition(t, i1->acc[i]);
|
||||
succ_.push_back(*i1);
|
||||
}
|
||||
for (i2 = v2.succ_.begin(); i2 != v2.succ_.end(); ++i2)
|
||||
{
|
||||
t = res_->create_transition(init_, i2->Q);
|
||||
res_->add_condition(t, i2->condition);
|
||||
succ_.push_back(*i2);
|
||||
}
|
||||
return;
|
||||
case op::M: // Strong Release
|
||||
strong = true;
|
||||
case op::R: // Weak Release
|
||||
if (refined_)
|
||||
contained = lcc_->contained(f[0], f[1]);
|
||||
i1->Q.push_back(init_); // Add the initial state
|
||||
if (strong)
|
||||
i1->acc.push_back(f[1]);
|
||||
t = res_->create_transition(init_, i1->Q);
|
||||
res_->add_condition(t, i1->condition);
|
||||
if (strong)
|
||||
res_->add_acceptance_condition(t, f[1]);
|
||||
else
|
||||
for (unsigned i = 0; i < i1->acc.size(); ++i)
|
||||
res_->add_acceptance_condition(t, i1->acc[i]);
|
||||
succ_.push_back(*i1);
|
||||
}
|
||||
for (i2 = v2.succ_.begin(); i2 != v2.succ_.end(); ++i2)
|
||||
{
|
||||
t = res_->create_transition(init_, i2->Q);
|
||||
res_->add_condition(t, i2->condition);
|
||||
succ_.push_back(*i2);
|
||||
}
|
||||
return;
|
||||
case op::M: // Strong Release
|
||||
strong = true;
|
||||
case op::R: // Weak Release
|
||||
if (refined_)
|
||||
contained = lcc_->contained(f[0], f[1]);
|
||||
|
||||
for (i2 = v2.succ_.begin(); i2 != v2.succ_.end(); ++i2)
|
||||
{
|
||||
for (i1 = v1.succ_.begin(); i1 != v1.succ_.end(); ++i1)
|
||||
{
|
||||
std::vector<formula> u; // Union
|
||||
std::vector<formula> a; // Acceptance conditions
|
||||
std::copy(i1->Q.begin(), i1->Q.end(), ii(u, u.end()));
|
||||
formula f = i1->condition; // Refined rule
|
||||
if (!refined_ || !contained)
|
||||
{
|
||||
std::copy(i2->Q.begin(), i2->Q.end(), ii(u, u.end()));
|
||||
f = formula::And({f, i2->condition});
|
||||
}
|
||||
t = res_->create_transition(init_, u);
|
||||
res_->add_condition(t, f);
|
||||
succ_state ss = { u, f, a };
|
||||
succ_.push_back(ss);
|
||||
}
|
||||
for (i2 = v2.succ_.begin(); i2 != v2.succ_.end(); ++i2)
|
||||
{
|
||||
for (i1 = v1.succ_.begin(); i1 != v1.succ_.end(); ++i1)
|
||||
{
|
||||
std::vector<formula> u; // Union
|
||||
std::vector<formula> a; // Acceptance conditions
|
||||
std::copy(i1->Q.begin(), i1->Q.end(), ii(u, u.end()));
|
||||
formula f = i1->condition; // Refined rule
|
||||
if (!refined_ || !contained)
|
||||
{
|
||||
std::copy(i2->Q.begin(), i2->Q.end(), ii(u, u.end()));
|
||||
f = formula::And({f, i2->condition});
|
||||
}
|
||||
t = res_->create_transition(init_, u);
|
||||
res_->add_condition(t, f);
|
||||
succ_state ss = { u, f, a };
|
||||
succ_.push_back(ss);
|
||||
}
|
||||
|
||||
if (refined_) // Refined rule
|
||||
i2->Q.erase
|
||||
(remove(i2->Q.begin(), i2->Q.end(), v2.init_), i2->Q.end());
|
||||
if (refined_) // Refined rule
|
||||
i2->Q.erase
|
||||
(remove(i2->Q.begin(), i2->Q.end(), v2.init_), i2->Q.end());
|
||||
|
||||
|
||||
i2->Q.push_back(init_); // Add the initial state
|
||||
t = res_->create_transition(init_, i2->Q);
|
||||
res_->add_condition(t, i2->condition);
|
||||
i2->Q.push_back(init_); // Add the initial state
|
||||
t = res_->create_transition(init_, i2->Q);
|
||||
res_->add_condition(t, i2->condition);
|
||||
|
||||
if (strong)
|
||||
{
|
||||
i2->acc.push_back(f[0]);
|
||||
res_->add_acceptance_condition(t, f[0]);
|
||||
}
|
||||
else if (refined_)
|
||||
for (unsigned i = 0; i < i2->acc.size(); ++i)
|
||||
res_->add_acceptance_condition(t, i2->acc[i]);
|
||||
succ_.push_back(*i2);
|
||||
}
|
||||
return;
|
||||
default:
|
||||
SPOT_UNIMPLEMENTED();
|
||||
}
|
||||
SPOT_UNREACHABLE();
|
||||
if (strong)
|
||||
{
|
||||
i2->acc.push_back(f[0]);
|
||||
res_->add_acceptance_condition(t, f[0]);
|
||||
}
|
||||
else if (refined_)
|
||||
for (unsigned i = 0; i < i2->acc.size(); ++i)
|
||||
res_->add_acceptance_condition(t, i2->acc[i]);
|
||||
succ_.push_back(*i2);
|
||||
}
|
||||
return;
|
||||
default:
|
||||
SPOT_UNIMPLEMENTED();
|
||||
}
|
||||
SPOT_UNREACHABLE();
|
||||
}
|
||||
|
||||
void
|
||||
visit_multop(formula f)
|
||||
{
|
||||
bool ok = true;
|
||||
std::vector<ltl2taa_visitor> vs;
|
||||
for (unsigned n = 0, s = f.size(); n < s; ++n)
|
||||
{
|
||||
vs.push_back(recurse(f[n]));
|
||||
if (vs[n].succ_.empty()) // Handle 0
|
||||
ok = false;
|
||||
}
|
||||
bool ok = true;
|
||||
std::vector<ltl2taa_visitor> vs;
|
||||
for (unsigned n = 0, s = f.size(); n < s; ++n)
|
||||
{
|
||||
vs.push_back(recurse(f[n]));
|
||||
if (vs[n].succ_.empty()) // Handle 0
|
||||
ok = false;
|
||||
}
|
||||
|
||||
std::vector<succ_state>::iterator i;
|
||||
taa_tgba::transition* t = nullptr;
|
||||
switch (f.kind())
|
||||
{
|
||||
case op::And:
|
||||
{
|
||||
if (!ok)
|
||||
return;
|
||||
std::vector<succ_state> p = all_n_tuples(vs);
|
||||
for (unsigned n = 0; n < p.size(); ++n)
|
||||
{
|
||||
if (refined_)
|
||||
{
|
||||
std::vector<formula> v; // All sub initial states.
|
||||
sort(p[n].Q.begin(), p[n].Q.end());
|
||||
for (unsigned m = 0; m < f.size(); ++m)
|
||||
{
|
||||
if (!binary_search(p[n].Q.begin(), p[n].Q.end(),
|
||||
vs[m].init_))
|
||||
break;
|
||||
v.push_back(vs[m].init_);
|
||||
}
|
||||
std::vector<succ_state>::iterator i;
|
||||
taa_tgba::transition* t = nullptr;
|
||||
switch (f.kind())
|
||||
{
|
||||
case op::And:
|
||||
{
|
||||
if (!ok)
|
||||
return;
|
||||
std::vector<succ_state> p = all_n_tuples(vs);
|
||||
for (unsigned n = 0; n < p.size(); ++n)
|
||||
{
|
||||
if (refined_)
|
||||
{
|
||||
std::vector<formula> v; // All sub initial states.
|
||||
sort(p[n].Q.begin(), p[n].Q.end());
|
||||
for (unsigned m = 0; m < f.size(); ++m)
|
||||
{
|
||||
if (!binary_search(p[n].Q.begin(), p[n].Q.end(),
|
||||
vs[m].init_))
|
||||
break;
|
||||
v.push_back(vs[m].init_);
|
||||
}
|
||||
|
||||
if (v.size() == f.size())
|
||||
{
|
||||
std::vector<formula> Q;
|
||||
sort(v.begin(), v.end());
|
||||
for (unsigned m = 0; m < p[n].Q.size(); ++m)
|
||||
if (!binary_search(v.begin(), v.end(), p[n].Q[m]))
|
||||
Q.push_back(p[n].Q[m]);
|
||||
Q.push_back(init_);
|
||||
t = res_->create_transition(init_, Q);
|
||||
res_->add_condition(t, p[n].condition);
|
||||
for (unsigned i = 0; i < p[n].acc.size(); ++i)
|
||||
res_->add_acceptance_condition(t, p[n].acc[i]);
|
||||
succ_.push_back(p[n]);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
t = res_->create_transition(init_, p[n].Q);
|
||||
res_->add_condition(t, p[n].condition);
|
||||
succ_.push_back(p[n]);
|
||||
}
|
||||
return;
|
||||
}
|
||||
case op::Or:
|
||||
for (unsigned n = 0, s = f.size(); n < s; ++n)
|
||||
for (auto i: vs[n].succ_)
|
||||
{
|
||||
t = res_->create_transition(init_, i.Q);
|
||||
res_->add_condition(t, i.condition);
|
||||
succ_.push_back(i);
|
||||
}
|
||||
return;
|
||||
default:
|
||||
SPOT_UNIMPLEMENTED();
|
||||
}
|
||||
SPOT_UNREACHABLE();
|
||||
if (v.size() == f.size())
|
||||
{
|
||||
std::vector<formula> Q;
|
||||
sort(v.begin(), v.end());
|
||||
for (unsigned m = 0; m < p[n].Q.size(); ++m)
|
||||
if (!binary_search(v.begin(), v.end(), p[n].Q[m]))
|
||||
Q.push_back(p[n].Q[m]);
|
||||
Q.push_back(init_);
|
||||
t = res_->create_transition(init_, Q);
|
||||
res_->add_condition(t, p[n].condition);
|
||||
for (unsigned i = 0; i < p[n].acc.size(); ++i)
|
||||
res_->add_acceptance_condition(t, p[n].acc[i]);
|
||||
succ_.push_back(p[n]);
|
||||
continue;
|
||||
}
|
||||
}
|
||||
t = res_->create_transition(init_, p[n].Q);
|
||||
res_->add_condition(t, p[n].condition);
|
||||
succ_.push_back(p[n]);
|
||||
}
|
||||
return;
|
||||
}
|
||||
case op::Or:
|
||||
for (unsigned n = 0, s = f.size(); n < s; ++n)
|
||||
for (auto i: vs[n].succ_)
|
||||
{
|
||||
t = res_->create_transition(init_, i.Q);
|
||||
res_->add_condition(t, i.condition);
|
||||
succ_.push_back(i);
|
||||
}
|
||||
return;
|
||||
default:
|
||||
SPOT_UNIMPLEMENTED();
|
||||
}
|
||||
SPOT_UNREACHABLE();
|
||||
}
|
||||
|
||||
ltl2taa_visitor
|
||||
recurse(formula f)
|
||||
{
|
||||
ltl2taa_visitor v(res_, lcc_, refined_, negated_);
|
||||
v.visit(f);
|
||||
return v;
|
||||
ltl2taa_visitor v(res_, lcc_, refined_, negated_);
|
||||
v.visit(f);
|
||||
return v;
|
||||
}
|
||||
|
||||
private:
|
||||
|
|
@ -325,9 +325,9 @@ namespace spot
|
|||
|
||||
struct succ_state
|
||||
{
|
||||
std::vector<formula> Q; // States
|
||||
formula condition;
|
||||
std::vector<formula> acc;
|
||||
std::vector<formula> Q; // States
|
||||
formula condition;
|
||||
std::vector<formula> acc;
|
||||
};
|
||||
|
||||
formula init_;
|
||||
|
|
@ -337,61 +337,61 @@ namespace spot
|
|||
std::vector<succ_state>
|
||||
all_n_tuples(const std::vector<ltl2taa_visitor>& vs)
|
||||
{
|
||||
std::vector<succ_state> product;
|
||||
std::vector<succ_state> product;
|
||||
|
||||
std::vector<int> pos(vs.size());
|
||||
for (unsigned i = 0; i < vs.size(); ++i)
|
||||
pos[i] = vs[i].succ_.size();
|
||||
std::vector<int> pos(vs.size());
|
||||
for (unsigned i = 0; i < vs.size(); ++i)
|
||||
pos[i] = vs[i].succ_.size();
|
||||
|
||||
while (pos[0] != 0)
|
||||
{
|
||||
std::vector<formula> u; // Union
|
||||
std::vector<formula> a; // Acceptance conditions
|
||||
formula f = formula::tt();
|
||||
for (unsigned i = 0; i < vs.size(); ++i)
|
||||
{
|
||||
if (vs[i].succ_.empty())
|
||||
continue;
|
||||
const succ_state& ss(vs[i].succ_[pos[i] - 1]);
|
||||
std::copy(ss.Q.begin(), ss.Q.end(), ii(u, u.end()));
|
||||
f = formula::And({ss.condition, f});
|
||||
for (unsigned i = 0; i < ss.acc.size(); ++i)
|
||||
{
|
||||
formula g = ss.acc[i];
|
||||
a.push_back(g);
|
||||
}
|
||||
}
|
||||
succ_state ss = { u, f, a };
|
||||
product.push_back(ss);
|
||||
while (pos[0] != 0)
|
||||
{
|
||||
std::vector<formula> u; // Union
|
||||
std::vector<formula> a; // Acceptance conditions
|
||||
formula f = formula::tt();
|
||||
for (unsigned i = 0; i < vs.size(); ++i)
|
||||
{
|
||||
if (vs[i].succ_.empty())
|
||||
continue;
|
||||
const succ_state& ss(vs[i].succ_[pos[i] - 1]);
|
||||
std::copy(ss.Q.begin(), ss.Q.end(), ii(u, u.end()));
|
||||
f = formula::And({ss.condition, f});
|
||||
for (unsigned i = 0; i < ss.acc.size(); ++i)
|
||||
{
|
||||
formula g = ss.acc[i];
|
||||
a.push_back(g);
|
||||
}
|
||||
}
|
||||
succ_state ss = { u, f, a };
|
||||
product.push_back(ss);
|
||||
|
||||
for (int i = vs.size() - 1; i >= 0; --i)
|
||||
{
|
||||
if (vs[i].succ_.empty())
|
||||
continue;
|
||||
if (pos[i] > 1 || (i == 0 && pos[0] == 1))
|
||||
{
|
||||
--pos[i];
|
||||
break;
|
||||
}
|
||||
else
|
||||
pos[i] = vs[i].succ_.size();
|
||||
}
|
||||
}
|
||||
return product;
|
||||
for (int i = vs.size() - 1; i >= 0; --i)
|
||||
{
|
||||
if (vs[i].succ_.empty())
|
||||
continue;
|
||||
if (pos[i] > 1 || (i == 0 && pos[0] == 1))
|
||||
{
|
||||
--pos[i];
|
||||
break;
|
||||
}
|
||||
else
|
||||
pos[i] = vs[i].succ_.size();
|
||||
}
|
||||
}
|
||||
return product;
|
||||
}
|
||||
};
|
||||
} // anonymous
|
||||
|
||||
taa_tgba_formula_ptr
|
||||
ltl_to_taa(formula f,
|
||||
const bdd_dict_ptr& dict, bool refined_rules)
|
||||
const bdd_dict_ptr& dict, bool refined_rules)
|
||||
{
|
||||
// TODO: implement translation of F and G
|
||||
auto f2 = negative_normal_form(unabbreviate(f, "^ieFG"));
|
||||
auto res = make_taa_tgba_formula(dict);
|
||||
language_containment_checker* lcc =
|
||||
new language_containment_checker(make_bdd_dict(),
|
||||
false, false, false, false);
|
||||
false, false, false, false);
|
||||
ltl2taa_visitor v(res, lcc, refined_rules);
|
||||
v.visit(f2);
|
||||
auto taa = v.result();
|
||||
|
|
|
|||
|
|
@ -49,5 +49,5 @@ namespace spot
|
|||
/// \return A spot::taa that recognizes the language of \a f.
|
||||
SPOT_API taa_tgba_formula_ptr
|
||||
ltl_to_taa(formula f, const bdd_dict_ptr& dict,
|
||||
bool refined_rules = false);
|
||||
bool refined_rules = false);
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -35,18 +35,18 @@ namespace spot
|
|||
/// This is based on the following paper.
|
||||
/** \verbatim
|
||||
@InProceedings{couvreur.99.fm,
|
||||
author = {Jean-Michel Couvreur},
|
||||
author = {Jean-Michel Couvreur},
|
||||
title = {On-the-fly Verification of Temporal Logic},
|
||||
pages = {253--271},
|
||||
editor = {Jeannette M. Wing and Jim Woodcock and Jim Davies},
|
||||
editor = {Jeannette M. Wing and Jim Woodcock and Jim Davies},
|
||||
booktitle = {Proceedings of the World Congress on Formal Methods in the
|
||||
Development of Computing Systems (FM'99)},
|
||||
Development of Computing Systems (FM'99)},
|
||||
publisher = {Springer-Verlag},
|
||||
series = {Lecture Notes in Computer Science},
|
||||
volume = {1708},
|
||||
series = {Lecture Notes in Computer Science},
|
||||
volume = {1708},
|
||||
year = {1999},
|
||||
address = {Toulouse, France},
|
||||
month = {September},
|
||||
address = {Toulouse, France},
|
||||
month = {September},
|
||||
isbn = {3-540-66587-0}
|
||||
}
|
||||
\endverbatim */
|
||||
|
|
@ -71,18 +71,18 @@ namespace spot
|
|||
/// conditions) will be merged. This correspond to an optimization
|
||||
/// described in the following paper.
|
||||
/** \verbatim
|
||||
@InProceedings{ sebastiani.03.charme,
|
||||
author = {Roberto Sebastiani and Stefano Tonetta},
|
||||
title = {"More Deterministic" vs. "Smaller" B{\"u}chi Automata for
|
||||
Efficient LTL Model Checking},
|
||||
@InProceedings{ sebastiani.03.charme,
|
||||
author = {Roberto Sebastiani and Stefano Tonetta},
|
||||
title = {"More Deterministic" vs. "Smaller" B{\"u}chi Automata for
|
||||
Efficient LTL Model Checking},
|
||||
booktitle = {Proceedings for the 12th Advanced Research Working
|
||||
Conference on Correct Hardware Design and Verification
|
||||
Methods (CHARME'03)},
|
||||
Conference on Correct Hardware Design and Verification
|
||||
Methods (CHARME'03)},
|
||||
pages = {126--140},
|
||||
year = {2003},
|
||||
editor = {G. Goos and J. Hartmanis and J. van Leeuwen},
|
||||
volume = {2860},
|
||||
series = {Lectures Notes in Computer Science},
|
||||
editor = {G. Goos and J. Hartmanis and J. van Leeuwen},
|
||||
volume = {2860},
|
||||
series = {Lectures Notes in Computer Science},
|
||||
month = {October},
|
||||
publisher = {Springer-Verlag}
|
||||
}
|
||||
|
|
@ -104,36 +104,36 @@ namespace spot
|
|||
/// spot::tl_simplifier. This idea is taken from the
|
||||
/// following paper.
|
||||
/** \verbatim
|
||||
@InProceedings{ thirioux.02.fmics,
|
||||
author = {Xavier Thirioux},
|
||||
@InProceedings{ thirioux.02.fmics,
|
||||
author = {Xavier Thirioux},
|
||||
title = {Simple and Efficient Translation from {LTL} Formulas to
|
||||
{B\"u}chi Automata},
|
||||
{B\"u}chi Automata},
|
||||
booktitle = {Proceedings of the 7th International ERCIM Workshop in
|
||||
Formal Methods for Industrial Critical Systems (FMICS'02)},
|
||||
series = {Electronic Notes in Theoretical Computer Science},
|
||||
volume = {66(2)},
|
||||
Formal Methods for Industrial Critical Systems (FMICS'02)},
|
||||
series = {Electronic Notes in Theoretical Computer Science},
|
||||
volume = {66(2)},
|
||||
publisher = {Elsevier},
|
||||
editor = {Rance Cleaveland and Hubert Garavel},
|
||||
editor = {Rance Cleaveland and Hubert Garavel},
|
||||
year = {2002},
|
||||
month = jul,
|
||||
address = {M{\'a}laga, Spain}
|
||||
address = {M{\'a}laga, Spain}
|
||||
}
|
||||
\endverbatim */
|
||||
///
|
||||
/// \param unambiguous When true, unambigous TGBA will be produced using
|
||||
/// the trick described in the following paper.
|
||||
/** \verbatim
|
||||
@InProceedings{ benedikt.13.tacas,
|
||||
author = {Michael Benedikt and Rastislav Lenhardt and James
|
||||
Worrell},
|
||||
@InProceedings{ benedikt.13.tacas,
|
||||
author = {Michael Benedikt and Rastislav Lenhardt and James
|
||||
Worrell},
|
||||
title = {{LTL} Model Checking of Interval Markov Chains},
|
||||
booktitle = {19th International Conference on Tools and Algorithms for
|
||||
the Construction and Analysis of Systems (TACAS'13)},
|
||||
the Construction and Analysis of Systems (TACAS'13)},
|
||||
year = {2013},
|
||||
pages = {32--46},
|
||||
series = {Lecture Notes in Computer Science},
|
||||
volume = {7795},
|
||||
editor = {Nir Piterman and Scott A. Smolka},
|
||||
series = {Lecture Notes in Computer Science},
|
||||
volume = {7795},
|
||||
editor = {Nir Piterman and Scott A. Smolka},
|
||||
publisher = {Springer}
|
||||
}
|
||||
\endverbatim */
|
||||
|
|
@ -141,10 +141,10 @@ namespace spot
|
|||
/// \return A spot::twa_graph that recognizes the language of \a f.
|
||||
SPOT_API twa_graph_ptr
|
||||
ltl_to_tgba_fm(formula f, const bdd_dict_ptr& dict,
|
||||
bool exprop = false, bool symb_merge = true,
|
||||
bool branching_postponement = false,
|
||||
bool fair_loop_approx = false,
|
||||
const atomic_prop_set* unobs = nullptr,
|
||||
tl_simplifier* simplifier = nullptr,
|
||||
bool unambiguous = false);
|
||||
bool exprop = false, bool symb_merge = true,
|
||||
bool branching_postponement = false,
|
||||
bool fair_loop_approx = false,
|
||||
const atomic_prop_set* unobs = nullptr,
|
||||
tl_simplifier* simplifier = nullptr,
|
||||
bool unambiguous = false);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ namespace spot
|
|||
/// \pre The automaton \a a must have at most one acceptance
|
||||
/// condition (i.e. it is a TBA).
|
||||
magic_search_(const const_twa_ptr& a, size_t size,
|
||||
option_map o = option_map())
|
||||
option_map o = option_map())
|
||||
: emptiness_check(a, o),
|
||||
h(size)
|
||||
{
|
||||
|
|
@ -89,8 +89,8 @@ namespace spot
|
|||
/// visits only a finite set of accepting paths.
|
||||
virtual emptiness_check_result_ptr check() override
|
||||
{
|
||||
auto t = std::static_pointer_cast<magic_search_>
|
||||
(this->emptiness_check::shared_from_this());
|
||||
auto t = std::static_pointer_cast<magic_search_>
|
||||
(this->emptiness_check::shared_from_this());
|
||||
if (st_red.empty())
|
||||
{
|
||||
assert(st_blue.empty());
|
||||
|
|
@ -99,7 +99,7 @@ namespace spot
|
|||
h.add_new_state(s0, BLUE);
|
||||
push(st_blue, s0, bddfalse, 0U);
|
||||
if (dfs_blue())
|
||||
return std::make_shared<magic_search_result>(t, options());
|
||||
return std::make_shared<magic_search_result>(t, options());
|
||||
}
|
||||
else
|
||||
{
|
||||
|
|
@ -130,27 +130,27 @@ namespace spot
|
|||
|
||||
virtual bool safe() const override
|
||||
{
|
||||
return heap::Safe;
|
||||
return heap::Safe;
|
||||
}
|
||||
|
||||
const heap& get_heap() const
|
||||
{
|
||||
return h;
|
||||
return h;
|
||||
}
|
||||
|
||||
const stack_type& get_st_blue() const
|
||||
{
|
||||
return st_blue;
|
||||
return st_blue;
|
||||
}
|
||||
|
||||
const stack_type& get_st_red() const
|
||||
{
|
||||
return st_red;
|
||||
return st_red;
|
||||
}
|
||||
private:
|
||||
|
||||
void push(stack_type& st, const state* s,
|
||||
const bdd& label, acc_cond::mark_t acc)
|
||||
const bdd& label, acc_cond::mark_t acc)
|
||||
{
|
||||
inc_depth();
|
||||
twa_succ_iterator* i = a_->succ_iter(s);
|
||||
|
|
@ -161,7 +161,7 @@ namespace spot
|
|||
void pop(stack_type& st)
|
||||
{
|
||||
dec_depth();
|
||||
a_->release_iter(st.front().it);
|
||||
a_->release_iter(st.front().it);
|
||||
st.pop_front();
|
||||
}
|
||||
|
||||
|
|
@ -235,7 +235,7 @@ namespace spot
|
|||
typename heap::color_ref c = h.get_color_ref(f_dest.s);
|
||||
assert(!c.is_white());
|
||||
if (!st_blue.empty() &&
|
||||
a_->acc().accepting(f_dest.acc) && c.get_color() != RED)
|
||||
a_->acc().accepting(f_dest.acc) && c.get_color() != RED)
|
||||
{
|
||||
// the test 'c.get_color() != RED' is added to limit
|
||||
// the number of runs reported by successive
|
||||
|
|
@ -380,7 +380,7 @@ namespace spot
|
|||
{
|
||||
public:
|
||||
magic_search_result(const std::shared_ptr<magic_search_>& m,
|
||||
option_map o = option_map())
|
||||
option_map o = option_map())
|
||||
: emptiness_check_result(m->automaton(), o), ms(m)
|
||||
{
|
||||
if (options()[FROM_STACK])
|
||||
|
|
@ -420,7 +420,7 @@ namespace spot
|
|||
|
||||
private:
|
||||
emptiness_check_result* computer;
|
||||
std::shared_ptr<magic_search_> ms;
|
||||
std::shared_ptr<magic_search_> ms;
|
||||
};
|
||||
};
|
||||
|
||||
|
|
@ -433,21 +433,21 @@ namespace spot
|
|||
{
|
||||
public:
|
||||
color_ref(color* c) :p(c)
|
||||
{
|
||||
}
|
||||
{
|
||||
}
|
||||
color get_color() const
|
||||
{
|
||||
return *p;
|
||||
}
|
||||
{
|
||||
return *p;
|
||||
}
|
||||
void set_color(color c)
|
||||
{
|
||||
assert(!is_white());
|
||||
*p=c;
|
||||
}
|
||||
{
|
||||
assert(!is_white());
|
||||
*p=c;
|
||||
}
|
||||
bool is_white() const
|
||||
{
|
||||
return !p;
|
||||
}
|
||||
{
|
||||
return !p;
|
||||
}
|
||||
private:
|
||||
color *p;
|
||||
};
|
||||
|
|
@ -588,7 +588,7 @@ namespace spot
|
|||
|
||||
emptiness_check_ptr
|
||||
bit_state_hashing_magic_search(const const_twa_ptr& a,
|
||||
size_t size, option_map o)
|
||||
size_t size, option_map o)
|
||||
{
|
||||
return std::make_shared<magic_search_<bsh_magic_search_heap>>(a, size, o);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -97,7 +97,7 @@ namespace spot
|
|||
/// from \c godefroid.93.pstv, not \c courcoubetis.92.fmsd.
|
||||
SPOT_API emptiness_check_ptr
|
||||
explicit_magic_search(const const_twa_ptr& a,
|
||||
option_map o = option_map());
|
||||
option_map o = option_map());
|
||||
|
||||
/// \brief Returns an emptiness checker on the spot::tgba automaton \a a.
|
||||
///
|
||||
|
|
@ -128,7 +128,7 @@ namespace spot
|
|||
///
|
||||
SPOT_API emptiness_check_ptr
|
||||
bit_state_hashing_magic_search(const const_twa_ptr& a, size_t size,
|
||||
option_map o = option_map());
|
||||
option_map o = option_map());
|
||||
|
||||
/// \brief Wrapper for the two magic_search implementations.
|
||||
///
|
||||
|
|
|
|||
|
|
@ -22,7 +22,7 @@
|
|||
namespace spot
|
||||
{
|
||||
twa_graph_ptr mask_acc_sets(const const_twa_graph_ptr& in,
|
||||
acc_cond::mark_t to_remove)
|
||||
acc_cond::mark_t to_remove)
|
||||
{
|
||||
auto res = make_twa_graph(in->get_dict());
|
||||
res->copy_ap_of(in);
|
||||
|
|
@ -31,7 +31,7 @@ namespace spot
|
|||
unsigned tr = to_remove.count();
|
||||
assert(tr <= na);
|
||||
res->set_acceptance(na - tr,
|
||||
in->get_acceptance().strip(to_remove, true));
|
||||
in->get_acceptance().strip(to_remove, true));
|
||||
transform_accessible(in, res, [&](unsigned,
|
||||
bdd& cond,
|
||||
acc_cond::mark_t& acc,
|
||||
|
|
@ -60,10 +60,10 @@ namespace spot
|
|||
bdd& cond,
|
||||
acc_cond::mark_t&,
|
||||
unsigned dst)
|
||||
{
|
||||
if (!to_keep[src] || !to_keep[dst])
|
||||
cond = bddfalse;
|
||||
}, init);
|
||||
{
|
||||
if (!to_keep[src] || !to_keep[dst])
|
||||
cond = bddfalse;
|
||||
}, init);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -49,36 +49,36 @@ namespace spot
|
|||
auto new_state =
|
||||
[&](unsigned old_state) -> unsigned
|
||||
{
|
||||
unsigned tmp = seen[old_state];
|
||||
if (tmp == -1U)
|
||||
{
|
||||
tmp = cpy->new_state();
|
||||
seen[old_state] = tmp;
|
||||
todo.push_back(old_state);
|
||||
}
|
||||
return tmp;
|
||||
unsigned tmp = seen[old_state];
|
||||
if (tmp == -1U)
|
||||
{
|
||||
tmp = cpy->new_state();
|
||||
seen[old_state] = tmp;
|
||||
todo.push_back(old_state);
|
||||
}
|
||||
return tmp;
|
||||
};
|
||||
|
||||
cpy->set_init_state(new_state(init));
|
||||
while (!todo.empty())
|
||||
{
|
||||
unsigned old_src = todo.back();
|
||||
todo.pop_back();
|
||||
unsigned old_src = todo.back();
|
||||
todo.pop_back();
|
||||
|
||||
unsigned new_src = seen[old_src];
|
||||
assert(new_src != -1U);
|
||||
unsigned new_src = seen[old_src];
|
||||
assert(new_src != -1U);
|
||||
|
||||
for (auto& t: old->out(old_src))
|
||||
{
|
||||
bdd cond = t.cond;
|
||||
acc_cond::mark_t acc = t.acc;
|
||||
trans(t.src, cond, acc, t.dst);
|
||||
for (auto& t: old->out(old_src))
|
||||
{
|
||||
bdd cond = t.cond;
|
||||
acc_cond::mark_t acc = t.acc;
|
||||
trans(t.src, cond, acc, t.dst);
|
||||
|
||||
if (cond != bddfalse)
|
||||
cpy->new_edge(new_src,
|
||||
new_state(t.dst),
|
||||
cond, acc);
|
||||
}
|
||||
if (cond != bddfalse)
|
||||
cpy->new_edge(new_src,
|
||||
new_state(t.dst),
|
||||
cond, acc);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -136,7 +136,7 @@ namespace spot
|
|||
/// \brief Remove all edges that belong to some given acceptance sets.
|
||||
SPOT_API
|
||||
twa_graph_ptr mask_acc_sets(const const_twa_graph_ptr& in,
|
||||
acc_cond::mark_t to_remove);
|
||||
acc_cond::mark_t to_remove);
|
||||
|
||||
/// \brief Keep only the states as specified by \a to_keep.
|
||||
///
|
||||
|
|
@ -144,6 +144,6 @@ namespace spot
|
|||
/// state. The initial state will be set to \a init.
|
||||
SPOT_API
|
||||
twa_graph_ptr mask_keep_states(const const_twa_graph_ptr& in,
|
||||
std::vector<bool>& to_keep,
|
||||
unsigned int init);
|
||||
std::vector<bool>& to_keep,
|
||||
unsigned int init);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -58,16 +58,16 @@ namespace spot
|
|||
{
|
||||
static std::ostream&
|
||||
dump_hash_set(const hash_set* hs,
|
||||
const const_twa_ptr& aut,
|
||||
std::ostream& out)
|
||||
const const_twa_ptr& aut,
|
||||
std::ostream& out)
|
||||
{
|
||||
out << '{';
|
||||
const char* sep = "";
|
||||
for (hash_set::const_iterator i = hs->begin(); i != hs->end(); ++i)
|
||||
{
|
||||
out << sep << aut->format_state(*i);
|
||||
sep = ", ";
|
||||
}
|
||||
{
|
||||
out << sep << aut->format_state(*i);
|
||||
sep = ", ";
|
||||
}
|
||||
out << '}';
|
||||
return out;
|
||||
}
|
||||
|
|
@ -90,32 +90,32 @@ namespace spot
|
|||
tovisit.push(init);
|
||||
seen->insert(init);
|
||||
while (!tovisit.empty())
|
||||
{
|
||||
const state* src = tovisit.front();
|
||||
tovisit.pop();
|
||||
{
|
||||
const state* src = tovisit.front();
|
||||
tovisit.pop();
|
||||
|
||||
for (auto sit: a->succ(src))
|
||||
{
|
||||
const state* dst = sit->dst();
|
||||
// Is it a new state ?
|
||||
if (seen->find(dst) == seen->end())
|
||||
{
|
||||
// Register the successor for later processing.
|
||||
tovisit.push(dst);
|
||||
seen->insert(dst);
|
||||
}
|
||||
else
|
||||
dst->destroy();
|
||||
}
|
||||
}
|
||||
for (auto sit: a->succ(src))
|
||||
{
|
||||
const state* dst = sit->dst();
|
||||
// Is it a new state ?
|
||||
if (seen->find(dst) == seen->end())
|
||||
{
|
||||
// Register the successor for later processing.
|
||||
tovisit.push(dst);
|
||||
seen->insert(dst);
|
||||
}
|
||||
else
|
||||
dst->destroy();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// From the base automaton and the list of sets, build the minimal
|
||||
// resulting automaton
|
||||
static twa_graph_ptr
|
||||
build_result(const const_twa_ptr& a,
|
||||
std::list<hash_set*>& sets,
|
||||
hash_set* final)
|
||||
std::list<hash_set*>& sets,
|
||||
hash_set* final)
|
||||
{
|
||||
auto dict = a->get_dict();
|
||||
auto res = make_twa_graph(dict);
|
||||
|
|
@ -128,76 +128,76 @@ namespace spot
|
|||
hash_map state_num;
|
||||
std::list<hash_set*>::iterator sit;
|
||||
for (sit = sets.begin(); sit != sets.end(); ++sit)
|
||||
{
|
||||
hash_set::iterator hit;
|
||||
hash_set* h = *sit;
|
||||
unsigned num = res->new_state();
|
||||
for (hit = h->begin(); hit != h->end(); ++hit)
|
||||
state_num[*hit] = num;
|
||||
}
|
||||
{
|
||||
hash_set::iterator hit;
|
||||
hash_set* h = *sit;
|
||||
unsigned num = res->new_state();
|
||||
for (hit = h->begin(); hit != h->end(); ++hit)
|
||||
state_num[*hit] = num;
|
||||
}
|
||||
|
||||
// For each transition in the initial automaton, add the corresponding
|
||||
// transition in res.
|
||||
|
||||
if (!final->empty())
|
||||
res->set_buchi();
|
||||
res->set_buchi();
|
||||
|
||||
for (sit = sets.begin(); sit != sets.end(); ++sit)
|
||||
{
|
||||
hash_set* h = *sit;
|
||||
{
|
||||
hash_set* h = *sit;
|
||||
|
||||
// Pick one state.
|
||||
const state* src = *h->begin();
|
||||
unsigned src_num = state_num[src];
|
||||
bool accepting = (final->find(src) != final->end());
|
||||
// Pick one state.
|
||||
const state* src = *h->begin();
|
||||
unsigned src_num = state_num[src];
|
||||
bool accepting = (final->find(src) != final->end());
|
||||
|
||||
// Connect it to all destinations.
|
||||
for (auto succit: a->succ(src))
|
||||
{
|
||||
const state* dst = succit->dst();
|
||||
hash_map::const_iterator i = state_num.find(dst);
|
||||
dst->destroy();
|
||||
if (i == state_num.end()) // Ignore useless destinations.
|
||||
continue;
|
||||
res->new_acc_edge(src_num, i->second,
|
||||
succit->cond(), accepting);
|
||||
}
|
||||
}
|
||||
// Connect it to all destinations.
|
||||
for (auto succit: a->succ(src))
|
||||
{
|
||||
const state* dst = succit->dst();
|
||||
hash_map::const_iterator i = state_num.find(dst);
|
||||
dst->destroy();
|
||||
if (i == state_num.end()) // Ignore useless destinations.
|
||||
continue;
|
||||
res->new_acc_edge(src_num, i->second,
|
||||
succit->cond(), accepting);
|
||||
}
|
||||
}
|
||||
res->merge_edges();
|
||||
if (res->num_states() > 0)
|
||||
{
|
||||
const state* init_state = a->get_init_state();
|
||||
unsigned init_num = state_num[init_state];
|
||||
init_state->destroy();
|
||||
res->set_init_state(init_num);
|
||||
}
|
||||
{
|
||||
const state* init_state = a->get_init_state();
|
||||
unsigned init_num = state_num[init_state];
|
||||
init_state->destroy();
|
||||
res->set_init_state(init_num);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
struct wdba_search_acc_loop final : public bfs_steps
|
||||
{
|
||||
wdba_search_acc_loop(const const_twa_ptr& det_a,
|
||||
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)
|
||||
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)
|
||||
{
|
||||
seen(dest);
|
||||
seen(dest);
|
||||
}
|
||||
|
||||
virtual const state*
|
||||
filter(const state* s) override
|
||||
{
|
||||
s = seen(s);
|
||||
if (sm.scc_of(std::static_pointer_cast<const twa_graph>(a_)
|
||||
->state_number(s)) != scc_n)
|
||||
return nullptr;
|
||||
return s;
|
||||
s = seen(s);
|
||||
if (sm.scc_of(std::static_pointer_cast<const twa_graph>(a_)
|
||||
->state_number(s)) != scc_n)
|
||||
return nullptr;
|
||||
return s;
|
||||
}
|
||||
|
||||
virtual bool
|
||||
match(twa_run::step&, const state* to) override
|
||||
{
|
||||
return to == dest;
|
||||
return to == dest;
|
||||
}
|
||||
|
||||
unsigned scc_n;
|
||||
|
|
@ -210,8 +210,8 @@ namespace spot
|
|||
|
||||
bool
|
||||
wdba_scc_is_accepting(const const_twa_graph_ptr& det_a, unsigned scc_n,
|
||||
const const_twa_graph_ptr& orig_a, scc_info& sm,
|
||||
power_map& pm)
|
||||
const const_twa_graph_ptr& orig_a, scc_info& sm,
|
||||
power_map& pm)
|
||||
{
|
||||
// Get some state from the SCC #n.
|
||||
const state* start = det_a->state_from_number(sm.one_state_of(scc_n));
|
||||
|
|
@ -230,10 +230,10 @@ namespace spot
|
|||
loop_a->new_states(loop_size);
|
||||
int n;
|
||||
for (n = 1, i = loop.begin(); n < loop_size; ++n, ++i)
|
||||
{
|
||||
loop_a->new_edge(n - 1, n, i->label);
|
||||
i->s->destroy();
|
||||
}
|
||||
{
|
||||
loop_a->new_edge(n - 1, n, i->label);
|
||||
i->s->destroy();
|
||||
}
|
||||
assert(i != loop.end());
|
||||
loop_a->new_edge(n - 1, 0, i->label);
|
||||
i->s->destroy();
|
||||
|
|
@ -246,23 +246,23 @@ namespace spot
|
|||
|
||||
// Iterate on each original state corresponding to start.
|
||||
const power_map::power_state& ps =
|
||||
pm.states_of(det_a->state_number(start));
|
||||
pm.states_of(det_a->state_number(start));
|
||||
for (auto& s: ps)
|
||||
{
|
||||
// Construct a product between LOOP_A and ORIG_A starting in
|
||||
// S. FIXME: This could be sped up a lot!
|
||||
if (!product(loop_a, orig_a, 0U, s)->is_empty())
|
||||
{
|
||||
accepting = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
{
|
||||
// Construct a product between LOOP_A and ORIG_A starting in
|
||||
// S. FIXME: This could be sped up a lot!
|
||||
if (!product(loop_a, orig_a, 0U, s)->is_empty())
|
||||
{
|
||||
accepting = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return accepting;
|
||||
}
|
||||
|
||||
static twa_graph_ptr minimize_dfa(const const_twa_graph_ptr& det_a,
|
||||
hash_set* final, hash_set* non_final)
|
||||
hash_set* final, hash_set* non_final)
|
||||
{
|
||||
typedef std::list<hash_set*> partition_t;
|
||||
partition_t cur_run;
|
||||
|
|
@ -278,53 +278,53 @@ namespace spot
|
|||
// Use bdd variables to number sets. set_num is the first variable
|
||||
// available.
|
||||
unsigned set_num =
|
||||
det_a->get_dict()->register_anonymous_variables(size, det_a);
|
||||
det_a->get_dict()->register_anonymous_variables(size, det_a);
|
||||
|
||||
std::set<int> free_var;
|
||||
for (unsigned i = set_num; i < set_num + size; ++i)
|
||||
free_var.insert(i);
|
||||
free_var.insert(i);
|
||||
std::map<int, int> used_var;
|
||||
|
||||
hash_set* final_copy;
|
||||
|
||||
if (!final->empty())
|
||||
{
|
||||
unsigned s = final->size();
|
||||
used_var[set_num] = s;
|
||||
free_var.erase(set_num);
|
||||
if (s > 1)
|
||||
cur_run.push_back(final);
|
||||
else
|
||||
done.push_back(final);
|
||||
for (hash_set::const_iterator i = final->begin();
|
||||
i != final->end(); ++i)
|
||||
state_set_map[*i] = set_num;
|
||||
{
|
||||
unsigned s = final->size();
|
||||
used_var[set_num] = s;
|
||||
free_var.erase(set_num);
|
||||
if (s > 1)
|
||||
cur_run.push_back(final);
|
||||
else
|
||||
done.push_back(final);
|
||||
for (hash_set::const_iterator i = final->begin();
|
||||
i != final->end(); ++i)
|
||||
state_set_map[*i] = set_num;
|
||||
|
||||
final_copy = new hash_set(*final);
|
||||
}
|
||||
final_copy = new hash_set(*final);
|
||||
}
|
||||
else
|
||||
{
|
||||
final_copy = final;
|
||||
}
|
||||
{
|
||||
final_copy = final;
|
||||
}
|
||||
|
||||
if (!non_final->empty())
|
||||
{
|
||||
unsigned s = non_final->size();
|
||||
unsigned num = set_num + 1;
|
||||
used_var[num] = s;
|
||||
free_var.erase(num);
|
||||
if (s > 1)
|
||||
cur_run.push_back(non_final);
|
||||
else
|
||||
done.push_back(non_final);
|
||||
for (hash_set::const_iterator i = non_final->begin();
|
||||
i != non_final->end(); ++i)
|
||||
state_set_map[*i] = num;
|
||||
}
|
||||
{
|
||||
unsigned s = non_final->size();
|
||||
unsigned num = set_num + 1;
|
||||
used_var[num] = s;
|
||||
free_var.erase(num);
|
||||
if (s > 1)
|
||||
cur_run.push_back(non_final);
|
||||
else
|
||||
done.push_back(non_final);
|
||||
for (hash_set::const_iterator i = non_final->begin();
|
||||
i != non_final->end(); ++i)
|
||||
state_set_map[*i] = num;
|
||||
}
|
||||
else
|
||||
{
|
||||
delete non_final;
|
||||
}
|
||||
{
|
||||
delete non_final;
|
||||
}
|
||||
|
||||
// A bdd_states_map is a list of formulae (in a BDD form)
|
||||
// associated with a destination set of states.
|
||||
|
|
@ -333,119 +333,119 @@ namespace spot
|
|||
bool did_split = true;
|
||||
|
||||
while (did_split)
|
||||
{
|
||||
did_split = false;
|
||||
while (!cur_run.empty())
|
||||
{
|
||||
// Get a set to process.
|
||||
hash_set* cur = cur_run.front();
|
||||
cur_run.pop_front();
|
||||
{
|
||||
did_split = false;
|
||||
while (!cur_run.empty())
|
||||
{
|
||||
// Get a set to process.
|
||||
hash_set* cur = cur_run.front();
|
||||
cur_run.pop_front();
|
||||
|
||||
trace << "processing " << format_hash_set(cur, det_a)
|
||||
<< std::endl;
|
||||
trace << "processing " << format_hash_set(cur, det_a)
|
||||
<< std::endl;
|
||||
|
||||
hash_set::iterator hi;
|
||||
bdd_states_map bdd_map;
|
||||
for (hi = cur->begin(); hi != cur->end(); ++hi)
|
||||
{
|
||||
const state* src = *hi;
|
||||
bdd f = bddfalse;
|
||||
for (auto si: det_a->succ(src))
|
||||
{
|
||||
const state* dst = si->dst();
|
||||
hash_map::const_iterator i = state_set_map.find(dst);
|
||||
dst->destroy();
|
||||
if (i == state_set_map.end())
|
||||
// The destination state is not in our
|
||||
// partition. This can happen if the initial
|
||||
// FINAL and NON_FINAL supplied to the algorithm
|
||||
// do not cover the whole automaton (because we
|
||||
// want to ignore some useless states). Simply
|
||||
// ignore these states here.
|
||||
continue;
|
||||
f |= (bdd_ithvar(i->second) & si->cond());
|
||||
}
|
||||
hash_set::iterator hi;
|
||||
bdd_states_map bdd_map;
|
||||
for (hi = cur->begin(); hi != cur->end(); ++hi)
|
||||
{
|
||||
const state* src = *hi;
|
||||
bdd f = bddfalse;
|
||||
for (auto si: det_a->succ(src))
|
||||
{
|
||||
const state* dst = si->dst();
|
||||
hash_map::const_iterator i = state_set_map.find(dst);
|
||||
dst->destroy();
|
||||
if (i == state_set_map.end())
|
||||
// The destination state is not in our
|
||||
// partition. This can happen if the initial
|
||||
// FINAL and NON_FINAL supplied to the algorithm
|
||||
// do not cover the whole automaton (because we
|
||||
// want to ignore some useless states). Simply
|
||||
// ignore these states here.
|
||||
continue;
|
||||
f |= (bdd_ithvar(i->second) & si->cond());
|
||||
}
|
||||
|
||||
// Have we already seen this formula ?
|
||||
bdd_states_map::iterator bsi = bdd_map.find(f);
|
||||
if (bsi == bdd_map.end())
|
||||
{
|
||||
// No, create a new set.
|
||||
hash_set* new_set = new hash_set;
|
||||
new_set->insert(src);
|
||||
bdd_map[f] = new_set;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Yes, add the current state to the set.
|
||||
bsi->second->insert(src);
|
||||
}
|
||||
}
|
||||
// Have we already seen this formula ?
|
||||
bdd_states_map::iterator bsi = bdd_map.find(f);
|
||||
if (bsi == bdd_map.end())
|
||||
{
|
||||
// No, create a new set.
|
||||
hash_set* new_set = new hash_set;
|
||||
new_set->insert(src);
|
||||
bdd_map[f] = new_set;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Yes, add the current state to the set.
|
||||
bsi->second->insert(src);
|
||||
}
|
||||
}
|
||||
|
||||
bdd_states_map::iterator bsi = bdd_map.begin();
|
||||
if (bdd_map.size() == 1)
|
||||
{
|
||||
// The set was not split.
|
||||
trace << "set " << format_hash_set(bsi->second, det_a)
|
||||
<< " was not split" << std::endl;
|
||||
next_run.push_back(bsi->second);
|
||||
}
|
||||
else
|
||||
{
|
||||
did_split = true;
|
||||
for (; bsi != bdd_map.end(); ++bsi)
|
||||
{
|
||||
hash_set* set = bsi->second;
|
||||
// Free the number associated to these states.
|
||||
unsigned num = state_set_map[*set->begin()];
|
||||
assert(used_var.find(num) != used_var.end());
|
||||
unsigned left = (used_var[num] -= set->size());
|
||||
// Make sure LEFT does not become negative (hence bigger
|
||||
// than SIZE when read as unsigned)
|
||||
assert(left < size);
|
||||
if (left == 0)
|
||||
{
|
||||
used_var.erase(num);
|
||||
free_var.insert(num);
|
||||
}
|
||||
// Pick a free number
|
||||
assert(!free_var.empty());
|
||||
num = *free_var.begin();
|
||||
free_var.erase(free_var.begin());
|
||||
used_var[num] = set->size();
|
||||
for (hash_set::iterator hit = set->begin();
|
||||
hit != set->end(); ++hit)
|
||||
state_set_map[*hit] = num;
|
||||
// Trivial sets can't be splitted any further.
|
||||
if (set->size() == 1)
|
||||
{
|
||||
trace << "set " << format_hash_set(set, det_a)
|
||||
<< " is minimal" << std::endl;
|
||||
done.push_back(set);
|
||||
}
|
||||
else
|
||||
{
|
||||
trace << "set " << format_hash_set(set, det_a)
|
||||
<< " should be processed further" << std::endl;
|
||||
next_run.push_back(set);
|
||||
}
|
||||
}
|
||||
}
|
||||
delete cur;
|
||||
}
|
||||
if (did_split)
|
||||
trace << "splitting did occur during this pass." << std::endl;
|
||||
else
|
||||
trace << "splitting did not occur during this pass." << std::endl;
|
||||
std::swap(cur_run, next_run);
|
||||
}
|
||||
bdd_states_map::iterator bsi = bdd_map.begin();
|
||||
if (bdd_map.size() == 1)
|
||||
{
|
||||
// The set was not split.
|
||||
trace << "set " << format_hash_set(bsi->second, det_a)
|
||||
<< " was not split" << std::endl;
|
||||
next_run.push_back(bsi->second);
|
||||
}
|
||||
else
|
||||
{
|
||||
did_split = true;
|
||||
for (; bsi != bdd_map.end(); ++bsi)
|
||||
{
|
||||
hash_set* set = bsi->second;
|
||||
// Free the number associated to these states.
|
||||
unsigned num = state_set_map[*set->begin()];
|
||||
assert(used_var.find(num) != used_var.end());
|
||||
unsigned left = (used_var[num] -= set->size());
|
||||
// Make sure LEFT does not become negative (hence bigger
|
||||
// than SIZE when read as unsigned)
|
||||
assert(left < size);
|
||||
if (left == 0)
|
||||
{
|
||||
used_var.erase(num);
|
||||
free_var.insert(num);
|
||||
}
|
||||
// Pick a free number
|
||||
assert(!free_var.empty());
|
||||
num = *free_var.begin();
|
||||
free_var.erase(free_var.begin());
|
||||
used_var[num] = set->size();
|
||||
for (hash_set::iterator hit = set->begin();
|
||||
hit != set->end(); ++hit)
|
||||
state_set_map[*hit] = num;
|
||||
// Trivial sets can't be splitted any further.
|
||||
if (set->size() == 1)
|
||||
{
|
||||
trace << "set " << format_hash_set(set, det_a)
|
||||
<< " is minimal" << std::endl;
|
||||
done.push_back(set);
|
||||
}
|
||||
else
|
||||
{
|
||||
trace << "set " << format_hash_set(set, det_a)
|
||||
<< " should be processed further" << std::endl;
|
||||
next_run.push_back(set);
|
||||
}
|
||||
}
|
||||
}
|
||||
delete cur;
|
||||
}
|
||||
if (did_split)
|
||||
trace << "splitting did occur during this pass." << std::endl;
|
||||
else
|
||||
trace << "splitting did not occur during this pass." << std::endl;
|
||||
std::swap(cur_run, next_run);
|
||||
}
|
||||
|
||||
done.splice(done.end(), cur_run);
|
||||
|
||||
#ifdef TRACE
|
||||
trace << "Final partition: ";
|
||||
for (partition_t::const_iterator i = done.begin(); i != done.end(); ++i)
|
||||
trace << format_hash_set(*i, det_a) << ' ';
|
||||
trace << format_hash_set(*i, det_a) << ' ';
|
||||
trace << std::endl;
|
||||
#endif
|
||||
|
||||
|
|
@ -456,13 +456,13 @@ namespace spot
|
|||
delete final_copy;
|
||||
hash_map::iterator hit;
|
||||
for (hit = state_set_map.begin(); hit != state_set_map.end();)
|
||||
{
|
||||
hash_map::iterator old = hit++;
|
||||
old->first->destroy();
|
||||
}
|
||||
{
|
||||
hash_map::iterator old = hit++;
|
||||
old->first->destroy();
|
||||
}
|
||||
std::list<hash_set*>::iterator it;
|
||||
for (it = done.begin(); it != done.end(); ++it)
|
||||
delete *it;
|
||||
delete *it;
|
||||
|
||||
return res;
|
||||
}
|
||||
|
|
@ -522,60 +522,60 @@ namespace spot
|
|||
// SCC are numbered in topological order
|
||||
// (but in the reverse order as Löding's)
|
||||
for (unsigned m = 0; m < scc_count; ++m)
|
||||
{
|
||||
bool is_useless = true;
|
||||
bool transient = sm.is_trivial(m);
|
||||
auto& succ = sm.succ(m);
|
||||
{
|
||||
bool is_useless = true;
|
||||
bool transient = sm.is_trivial(m);
|
||||
auto& succ = sm.succ(m);
|
||||
|
||||
if (transient && succ.empty())
|
||||
{
|
||||
// A trivial SCC without successor is useless.
|
||||
useless[m] = true;
|
||||
d[m] = k - 1;
|
||||
continue;
|
||||
}
|
||||
if (transient && succ.empty())
|
||||
{
|
||||
// A trivial SCC without successor is useless.
|
||||
useless[m] = true;
|
||||
d[m] = k - 1;
|
||||
continue;
|
||||
}
|
||||
|
||||
// Compute the minimum color l of the successors.
|
||||
// Also SCCs are useless if all their successor are
|
||||
// useless.
|
||||
unsigned l = k;
|
||||
for (unsigned j: succ)
|
||||
{
|
||||
is_useless &= useless[j];
|
||||
unsigned dj = d[j];
|
||||
if (dj < l)
|
||||
l = dj;
|
||||
}
|
||||
// Compute the minimum color l of the successors.
|
||||
// Also SCCs are useless if all their successor are
|
||||
// useless.
|
||||
unsigned l = k;
|
||||
for (unsigned j: succ)
|
||||
{
|
||||
is_useless &= useless[j];
|
||||
unsigned dj = d[j];
|
||||
if (dj < l)
|
||||
l = dj;
|
||||
}
|
||||
|
||||
if (transient)
|
||||
{
|
||||
d[m] = l;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Regular SCCs are accepting if any of their loop
|
||||
// corresponds to an accepted word in the original
|
||||
// automaton.
|
||||
if (wdba_scc_is_accepting(det_a, m, a, sm, pm))
|
||||
{
|
||||
is_useless = false;
|
||||
d[m] = l & ~1; // largest even number inferior or equal
|
||||
}
|
||||
else
|
||||
{
|
||||
d[m] = (l - 1) | 1; // largest odd number inferior or equal
|
||||
}
|
||||
}
|
||||
if (transient)
|
||||
{
|
||||
d[m] = l;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Regular SCCs are accepting if any of their loop
|
||||
// corresponds to an accepted word in the original
|
||||
// automaton.
|
||||
if (wdba_scc_is_accepting(det_a, m, a, sm, pm))
|
||||
{
|
||||
is_useless = false;
|
||||
d[m] = l & ~1; // largest even number inferior or equal
|
||||
}
|
||||
else
|
||||
{
|
||||
d[m] = (l - 1) | 1; // largest odd number inferior or equal
|
||||
}
|
||||
}
|
||||
|
||||
useless[m] = is_useless;
|
||||
useless[m] = is_useless;
|
||||
|
||||
if (!is_useless)
|
||||
{
|
||||
hash_set* dest_set = (d[m] & 1) ? non_final : final;
|
||||
for (auto s: sm.states_of(m))
|
||||
dest_set->insert(det_a->state_from_number(s));
|
||||
}
|
||||
}
|
||||
if (!is_useless)
|
||||
{
|
||||
hash_set* dest_set = (d[m] & 1) ? non_final : final;
|
||||
for (auto s: sm.states_of(m))
|
||||
dest_set->insert(det_a->state_from_number(s));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto res = minimize_dfa(det_a, final, non_final);
|
||||
|
|
@ -597,18 +597,18 @@ namespace spot
|
|||
|
||||
twa_graph_ptr
|
||||
minimize_obligation(const const_twa_graph_ptr& aut_f,
|
||||
formula f,
|
||||
const_twa_graph_ptr aut_neg_f,
|
||||
bool reject_bigger)
|
||||
formula f,
|
||||
const_twa_graph_ptr aut_neg_f,
|
||||
bool reject_bigger)
|
||||
{
|
||||
auto min_aut_f = minimize_wdba(aut_f);
|
||||
|
||||
if (reject_bigger)
|
||||
{
|
||||
// Abort if min_aut_f has more states than aut_f.
|
||||
unsigned orig_states = aut_f->num_states();
|
||||
if (orig_states < min_aut_f->num_states())
|
||||
return std::const_pointer_cast<twa_graph>(aut_f);
|
||||
// Abort if min_aut_f has more states than aut_f.
|
||||
unsigned orig_states = aut_f->num_states();
|
||||
if (orig_states < min_aut_f->num_states())
|
||||
return std::const_pointer_cast<twa_graph>(aut_f);
|
||||
}
|
||||
|
||||
// If the input automaton was already weak and deterministic, the
|
||||
|
|
@ -629,25 +629,25 @@ namespace spot
|
|||
// Build negation automaton if not supplied.
|
||||
if (!aut_neg_f)
|
||||
{
|
||||
if (f)
|
||||
{
|
||||
// If we know the formula, simply build the automaton for
|
||||
// its negation.
|
||||
aut_neg_f = ltl_to_tgba_fm(formula::Not(f), aut_f->get_dict());
|
||||
// Remove useless SCCs.
|
||||
aut_neg_f = scc_filter(aut_neg_f, true);
|
||||
}
|
||||
else if (is_deterministic(aut_f))
|
||||
{
|
||||
// If the automaton is deterministic, complementing is
|
||||
// easy.
|
||||
aut_neg_f = remove_fin(dtwa_complement(aut_f));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Otherwise, we cannot check if the minimization is safe.
|
||||
return nullptr;
|
||||
}
|
||||
if (f)
|
||||
{
|
||||
// If we know the formula, simply build the automaton for
|
||||
// its negation.
|
||||
aut_neg_f = ltl_to_tgba_fm(formula::Not(f), aut_f->get_dict());
|
||||
// Remove useless SCCs.
|
||||
aut_neg_f = scc_filter(aut_neg_f, true);
|
||||
}
|
||||
else if (is_deterministic(aut_f))
|
||||
{
|
||||
// If the automaton is deterministic, complementing is
|
||||
// easy.
|
||||
aut_neg_f = remove_fin(dtwa_complement(aut_f));
|
||||
}
|
||||
else
|
||||
{
|
||||
// Otherwise, we cannot check if the minimization is safe.
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// If the negation is a guarantee automaton, then the
|
||||
|
|
@ -659,13 +659,13 @@ namespace spot
|
|||
|
||||
if (product(min_aut_f, aut_neg_f)->is_empty())
|
||||
{
|
||||
// Complement the minimized WDBA.
|
||||
assert((bool)min_aut_f->prop_weak());
|
||||
auto neg_min_aut_f = remove_fin(dtwa_complement(min_aut_f));
|
||||
if (product(aut_f, neg_min_aut_f)->is_empty())
|
||||
// Finally, we are now sure that it was safe
|
||||
// to minimize the automaton.
|
||||
ok = true;
|
||||
// Complement the minimized WDBA.
|
||||
assert((bool)min_aut_f->prop_weak());
|
||||
auto neg_min_aut_f = remove_fin(dtwa_complement(min_aut_f));
|
||||
if (product(aut_f, neg_min_aut_f)->is_empty())
|
||||
// Finally, we are now sure that it was safe
|
||||
// to minimize the automaton.
|
||||
ok = true;
|
||||
}
|
||||
|
||||
if (ok)
|
||||
|
|
|
|||
|
|
@ -37,16 +37,16 @@ namespace spot
|
|||
///
|
||||
/// For more detail about monitors, see the following paper:
|
||||
/** \verbatim
|
||||
@InProceedings{ tabakov.10.rv,
|
||||
author = {Deian Tabakov and Moshe Y. Vardi},
|
||||
title = {Optimized Temporal Monitors for SystemC{$^*$}},
|
||||
@InProceedings{ tabakov.10.rv,
|
||||
author = {Deian Tabakov and Moshe Y. Vardi},
|
||||
title = {Optimized Temporal Monitors for SystemC{$^*$}},
|
||||
booktitle = {Proceedings of the 10th International Conferance
|
||||
on Runtime Verification},
|
||||
pages = {436--451},
|
||||
year = 2010,
|
||||
volume = {6418},
|
||||
series = {Lecture Notes in Computer Science},
|
||||
month = nov,
|
||||
pages = {436--451},
|
||||
year = 2010,
|
||||
volume = {6418},
|
||||
series = {Lecture Notes in Computer Science},
|
||||
month = nov,
|
||||
publisher = {Spring-Verlag}
|
||||
}
|
||||
\endverbatim */
|
||||
|
|
@ -76,19 +76,19 @@ namespace spot
|
|||
/// technique.
|
||||
///
|
||||
/** \verbatim
|
||||
@InProceedings{ dax.07.atva,
|
||||
author = {Christian Dax and Jochen Eisinger and Felix Klaedtke},
|
||||
@InProceedings{ dax.07.atva,
|
||||
author = {Christian Dax and Jochen Eisinger and Felix Klaedtke},
|
||||
title = {Mechanizing the Powerset Construction for Restricted
|
||||
Classes of {$\omega$}-Automata},
|
||||
Classes of {$\omega$}-Automata},
|
||||
year = 2007,
|
||||
series = {Lecture Notes in Computer Science},
|
||||
series = {Lecture Notes in Computer Science},
|
||||
publisher = {Springer-Verlag},
|
||||
volume = 4762,
|
||||
volume = 4762,
|
||||
booktitle = {Proceedings of the 5th International Symposium on
|
||||
Automated Technology for Verification and Analysis
|
||||
(ATVA'07)},
|
||||
editor = {Kedar S. Namjoshi and Tomohiro Yoneda and Teruo Higashino
|
||||
and Yoshio Okamura},
|
||||
Automated Technology for Verification and Analysis
|
||||
(ATVA'07)},
|
||||
editor = {Kedar S. Namjoshi and Tomohiro Yoneda and Teruo Higashino
|
||||
and Yoshio Okamura},
|
||||
month = oct
|
||||
}
|
||||
\endverbatim */
|
||||
|
|
@ -101,19 +101,19 @@ namespace spot
|
|||
/// by the following paper:
|
||||
///
|
||||
/** \verbatim
|
||||
@InProceedings{ dax.07.atva,
|
||||
author = {Christian Dax and Jochen Eisinger and Felix Klaedtke},
|
||||
@InProceedings{ dax.07.atva,
|
||||
author = {Christian Dax and Jochen Eisinger and Felix Klaedtke},
|
||||
title = {Mechanizing the Powerset Construction for Restricted
|
||||
Classes of {$\omega$}-Automata},
|
||||
Classes of {$\omega$}-Automata},
|
||||
year = 2007,
|
||||
series = {Lecture Notes in Computer Science},
|
||||
series = {Lecture Notes in Computer Science},
|
||||
publisher = {Springer-Verlag},
|
||||
volume = 4762,
|
||||
volume = 4762,
|
||||
booktitle = {Proceedings of the 5th International Symposium on
|
||||
Automated Technology for Verification and Analysis
|
||||
(ATVA'07)},
|
||||
editor = {Kedar S. Namjoshi and Tomohiro Yoneda and Teruo Higashino
|
||||
and Yoshio Okamura},
|
||||
Automated Technology for Verification and Analysis
|
||||
(ATVA'07)},
|
||||
editor = {Kedar S. Namjoshi and Tomohiro Yoneda and Teruo Higashino
|
||||
and Yoshio Okamura},
|
||||
month = oct
|
||||
}
|
||||
\endverbatim */
|
||||
|
|
@ -151,8 +151,8 @@ namespace spot
|
|||
/// that the minimized WDBA is correct.
|
||||
SPOT_API twa_graph_ptr
|
||||
minimize_obligation(const const_twa_graph_ptr& aut_f,
|
||||
formula f = nullptr,
|
||||
const_twa_graph_ptr aut_neg_f = nullptr,
|
||||
bool reject_bigger = false);
|
||||
formula f = nullptr,
|
||||
const_twa_graph_ptr aut_neg_f = nullptr,
|
||||
bool reject_bigger = false);
|
||||
/// @}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -79,8 +79,8 @@ namespace spot
|
|||
virtual unsigned
|
||||
acss_states() const override
|
||||
{
|
||||
// all visited states are in the state space search
|
||||
return static_cast<const T*>(this)->h_.size();
|
||||
// all visited states are in the state space search
|
||||
return static_cast<const T*>(this)->h_.size();
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -127,7 +127,7 @@ namespace spot
|
|||
if (i == str.end())
|
||||
i = stb.begin();
|
||||
transition t = { i->s->clone(), j->label, j->acc,
|
||||
j->s->clone() };
|
||||
j->s->clone() };
|
||||
assert(h_.has_been_visited(t.source));
|
||||
assert(h_.has_been_visited(t.dest));
|
||||
acc_trans.push_back(t);
|
||||
|
|
@ -139,7 +139,7 @@ namespace spot
|
|||
|
||||
i = j = stb.rbegin(); ++j;
|
||||
while (i->s->compare(start) != 0)
|
||||
++i, ++j;
|
||||
++i, ++j;
|
||||
|
||||
stack_type::const_reverse_iterator end = stb.rend();
|
||||
for (; j != end; ++i, ++j)
|
||||
|
|
@ -147,7 +147,7 @@ namespace spot
|
|||
if ((covered_acc & j->acc) != j->acc)
|
||||
{
|
||||
transition t = { i->s->clone(), j->label, j->acc,
|
||||
j->s->clone() };
|
||||
j->s->clone() };
|
||||
assert(h_.has_been_visited(t.source));
|
||||
assert(h_.has_been_visited(t.dest));
|
||||
acc_trans.push_back(t);
|
||||
|
|
@ -159,11 +159,11 @@ namespace spot
|
|||
if ((covered_acc & j->acc) != j->acc)
|
||||
{
|
||||
transition t = { i->s->clone(), j->label, j->acc,
|
||||
j->s->clone() };
|
||||
j->s->clone() };
|
||||
assert(h_.has_been_visited(t.source));
|
||||
assert(h_.has_been_visited(t.dest));
|
||||
acc_trans.push_back(t);
|
||||
covered_acc |= j->acc;
|
||||
covered_acc |= j->acc;
|
||||
}
|
||||
|
||||
i = j; ++j;
|
||||
|
|
@ -173,7 +173,7 @@ namespace spot
|
|||
if ((covered_acc & j->acc) != j->acc)
|
||||
{
|
||||
transition t = { i->s->clone(), j->label, j->acc,
|
||||
j->s->clone() };
|
||||
j->s->clone() };
|
||||
assert(h_.has_been_visited(t.source));
|
||||
assert(h_.has_been_visited(t.dest));
|
||||
acc_trans.push_back(t);
|
||||
|
|
@ -203,7 +203,7 @@ namespace spot
|
|||
construct_prefix(run);
|
||||
|
||||
for (typename accepting_transitions_list::const_iterator i =
|
||||
acc_trans.begin(); i != acc_trans.end(); ++i)
|
||||
acc_trans.begin(); i != acc_trans.end(); ++i)
|
||||
{
|
||||
i->source->destroy();
|
||||
i->dest->destroy();
|
||||
|
|
@ -227,32 +227,32 @@ namespace spot
|
|||
typedef std::list<transition> accepting_transitions_list;
|
||||
|
||||
typedef std::unordered_set<const state*,
|
||||
state_ptr_hash, state_ptr_equal> state_set;
|
||||
state_ptr_hash, state_ptr_equal> state_set;
|
||||
|
||||
void clean(const const_twa_ptr& a, stack_type& st1,
|
||||
state_set& seen, state_set& dead)
|
||||
state_set& seen, state_set& dead)
|
||||
{
|
||||
while (!st1.empty())
|
||||
{
|
||||
a->release_iter(st1.front().it);
|
||||
st1.pop_front();
|
||||
}
|
||||
{
|
||||
a->release_iter(st1.front().it);
|
||||
st1.pop_front();
|
||||
}
|
||||
for (state_set::iterator i = seen.begin(); i != seen.end();)
|
||||
{
|
||||
const state* s = *i;
|
||||
++i;
|
||||
s->destroy();
|
||||
}
|
||||
{
|
||||
const state* s = *i;
|
||||
++i;
|
||||
s->destroy();
|
||||
}
|
||||
for (state_set::iterator i = dead.begin(); i != dead.end();)
|
||||
{
|
||||
const state* s = *i;
|
||||
++i;
|
||||
s->destroy();
|
||||
}
|
||||
{
|
||||
const state* s = *i;
|
||||
++i;
|
||||
s->destroy();
|
||||
}
|
||||
}
|
||||
|
||||
bool dfs(const state* target, accepting_transitions_list& acc_trans,
|
||||
acc_cond::mark_t& covered_acc)
|
||||
acc_cond::mark_t& covered_acc)
|
||||
{
|
||||
assert(h_.has_been_visited(target));
|
||||
stack_type st1;
|
||||
|
|
@ -276,7 +276,7 @@ namespace spot
|
|||
ndfsr_trace << " Visit the successor: "
|
||||
<< a_->format_state(s_prime) << std::endl;
|
||||
bdd label = f.it->cond();
|
||||
auto acc = f.it->acc();
|
||||
auto acc = f.it->acc();
|
||||
f.it->next();
|
||||
if (h_.has_been_visited(s_prime))
|
||||
{
|
||||
|
|
@ -287,7 +287,7 @@ namespace spot
|
|||
}
|
||||
else if (seen.find(s_prime) == seen.end())
|
||||
{
|
||||
this->inc_ars_cycle_states();
|
||||
this->inc_ars_cycle_states();
|
||||
ndfsr_trace << " it is not seen, go down" << std::endl;
|
||||
seen.insert(s_prime);
|
||||
twa_succ_iterator* i = a_->succ_iter(s_prime);
|
||||
|
|
@ -296,13 +296,13 @@ namespace spot
|
|||
}
|
||||
else if ((acc & covered_acc) != acc)
|
||||
{
|
||||
this->inc_ars_cycle_states();
|
||||
this->inc_ars_cycle_states();
|
||||
ndfsr_trace << " a propagation is needed, "
|
||||
<< "start a search" << std::endl;
|
||||
if (search(s_prime, target, dead))
|
||||
{
|
||||
transition t = { f.s->clone(), label, acc,
|
||||
s_prime->clone() };
|
||||
s_prime->clone() };
|
||||
assert(h_.has_been_visited(t.source));
|
||||
assert(h_.has_been_visited(t.dest));
|
||||
acc_trans.push_back(t);
|
||||
|
|
@ -342,13 +342,13 @@ namespace spot
|
|||
<< std::endl;
|
||||
if (search(f_dest.s, target, dead))
|
||||
{
|
||||
transition t = { st1.front().s->clone(),
|
||||
f_dest.label, f_dest.acc,
|
||||
f_dest.s->clone() };
|
||||
transition t = { st1.front().s->clone(),
|
||||
f_dest.label, f_dest.acc,
|
||||
f_dest.s->clone() };
|
||||
assert(h_.has_been_visited(t.source));
|
||||
assert(h_.has_been_visited(t.dest));
|
||||
acc_trans.push_back(t);
|
||||
covered_acc |= f_dest.acc;
|
||||
covered_acc |= f_dest.acc;
|
||||
if (a_->acc().accepting(covered_acc))
|
||||
{
|
||||
clean(a_, st1, seen, dead);
|
||||
|
|
@ -372,8 +372,8 @@ namespace spot
|
|||
{
|
||||
public:
|
||||
test_path(ars_statistics* ars,
|
||||
const const_twa_ptr& a, const state* t,
|
||||
const state_set& d, const heap& h)
|
||||
const const_twa_ptr& a, const state* t,
|
||||
const state_set& d, const heap& h)
|
||||
: bfs_steps(a), ars(ars), target(t), dead(d), h(h)
|
||||
{
|
||||
}
|
||||
|
|
@ -401,20 +401,20 @@ namespace spot
|
|||
const state* filter(const state* s)
|
||||
{
|
||||
if (!h.has_been_visited(s)
|
||||
|| seen.find(s) != seen.end()
|
||||
|| dead.find(s) != dead.end())
|
||||
|| seen.find(s) != seen.end()
|
||||
|| dead.find(s) != dead.end())
|
||||
{
|
||||
s->destroy();
|
||||
return nullptr;
|
||||
}
|
||||
ars->inc_ars_cycle_states();
|
||||
ars->inc_ars_cycle_states();
|
||||
seen.insert(s);
|
||||
return s;
|
||||
}
|
||||
|
||||
void finalize(const std::map<const state*, twa_run::step,
|
||||
state_ptr_less_than>&,
|
||||
const twa_run::step&, const state*, twa_run::steps&)
|
||||
state_ptr_less_than>&,
|
||||
const twa_run::step&, const state*, twa_run::steps&)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -440,35 +440,35 @@ namespace spot
|
|||
{
|
||||
twa_run::steps path;
|
||||
if (start->compare(target) == 0)
|
||||
return true;
|
||||
return true;
|
||||
|
||||
test_path s(this, a_, target, dead, h_);
|
||||
const state* res = s.search(start->clone(), path);
|
||||
if (res)
|
||||
{
|
||||
assert(res->compare(target) == 0);
|
||||
return true;
|
||||
}
|
||||
{
|
||||
assert(res->compare(target) == 0);
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
state_set::const_iterator it;
|
||||
for (it = s.get_seen().begin(); it != s.get_seen().end(); ++it)
|
||||
dead.insert((*it)->clone());
|
||||
return false;
|
||||
}
|
||||
{
|
||||
state_set::const_iterator it;
|
||||
for (it = s.get_seen().begin(); it != s.get_seen().end(); ++it)
|
||||
dead.insert((*it)->clone());
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
typedef std::unordered_multimap<const state*, transition,
|
||||
state_ptr_hash,
|
||||
state_ptr_equal> m_source_trans;
|
||||
state_ptr_hash,
|
||||
state_ptr_equal> m_source_trans;
|
||||
|
||||
template<bool cycle>
|
||||
class min_path: public bfs_steps
|
||||
{
|
||||
public:
|
||||
min_path(ars_statistics* ars,
|
||||
const const_twa_ptr& a,
|
||||
const m_source_trans& target, const heap& h)
|
||||
const const_twa_ptr& a,
|
||||
const m_source_trans& target, const heap& h)
|
||||
: bfs_steps(a), ars(ars), target(target), h(h)
|
||||
{
|
||||
}
|
||||
|
|
@ -506,10 +506,10 @@ namespace spot
|
|||
return nullptr;
|
||||
}
|
||||
ndfsr_trace << " OK" << std::endl;
|
||||
if (cycle)
|
||||
ars->inc_ars_cycle_states();
|
||||
else
|
||||
ars->inc_ars_prefix_states();
|
||||
if (cycle)
|
||||
ars->inc_ars_cycle_states();
|
||||
else
|
||||
ars->inc_ars_prefix_states();
|
||||
seen.insert(s);
|
||||
return s;
|
||||
}
|
||||
|
|
@ -529,92 +529,92 @@ namespace spot
|
|||
};
|
||||
|
||||
void construct_cycle(twa_run_ptr run,
|
||||
const accepting_transitions_list& acc_trans)
|
||||
const accepting_transitions_list& acc_trans)
|
||||
{
|
||||
assert(!acc_trans.empty());
|
||||
transition current = acc_trans.front();
|
||||
// insert the first accepting transition in the cycle
|
||||
ndfsr_trace << "the initial accepting transition is from "
|
||||
<< a_->format_state(current.source) << " to "
|
||||
<< a_->format_state(current.dest) << std::endl;
|
||||
<< a_->format_state(current.source) << " to "
|
||||
<< a_->format_state(current.dest) << std::endl;
|
||||
const state* begin = current.source;
|
||||
|
||||
m_source_trans target;
|
||||
typename accepting_transitions_list::const_iterator i =
|
||||
acc_trans.begin();
|
||||
acc_trans.begin();
|
||||
ndfsr_trace << "targets are the source states: ";
|
||||
for (++i; i != acc_trans.end(); ++i)
|
||||
{
|
||||
if (i->source->compare(begin) == 0 &&
|
||||
i->source->compare(i->dest) == 0)
|
||||
{
|
||||
ndfsr_trace << "(self loop " << a_->format_state(i->source)
|
||||
<< " -> " << a_->format_state(i->dest)
|
||||
<< " ignored) ";
|
||||
twa_run::step st = { i->source->clone(), i->label, i->acc };
|
||||
run->cycle.push_back(st);
|
||||
}
|
||||
else
|
||||
{
|
||||
ndfsr_trace << a_->format_state(i->source) << " (-> "
|
||||
<< a_->format_state(i->dest) << ") ";
|
||||
target.emplace(i->source, *i);
|
||||
}
|
||||
}
|
||||
{
|
||||
if (i->source->compare(begin) == 0 &&
|
||||
i->source->compare(i->dest) == 0)
|
||||
{
|
||||
ndfsr_trace << "(self loop " << a_->format_state(i->source)
|
||||
<< " -> " << a_->format_state(i->dest)
|
||||
<< " ignored) ";
|
||||
twa_run::step st = { i->source->clone(), i->label, i->acc };
|
||||
run->cycle.push_back(st);
|
||||
}
|
||||
else
|
||||
{
|
||||
ndfsr_trace << a_->format_state(i->source) << " (-> "
|
||||
<< a_->format_state(i->dest) << ") ";
|
||||
target.emplace(i->source, *i);
|
||||
}
|
||||
}
|
||||
ndfsr_trace << std::endl;
|
||||
|
||||
twa_run::step st = { current.source->clone(), current.label,
|
||||
current.acc };
|
||||
current.acc };
|
||||
run->cycle.push_back(st);
|
||||
|
||||
while (!target.empty())
|
||||
{
|
||||
// find a minimal path from current.dest to any source state in
|
||||
// target.
|
||||
ndfsr_trace << "looking for a path from "
|
||||
<< a_->format_state(current.dest) << std::endl;
|
||||
typename m_source_trans::iterator i = target.find(current.dest);
|
||||
if (i == target.end())
|
||||
{
|
||||
min_path<true> s(this, a_, target, h_);
|
||||
const state* res = s.search(current.dest->clone(), run->cycle);
|
||||
// init current to the corresponding transition.
|
||||
assert(res);
|
||||
ndfsr_trace << a_->format_state(res) << " reached" << std::endl;
|
||||
i = target.find(res);
|
||||
assert(i != target.end());
|
||||
}
|
||||
else
|
||||
{
|
||||
ndfsr_trace << "this is a target" << std::endl;
|
||||
}
|
||||
current = i->second;
|
||||
// complete the path with the corresponding transition
|
||||
twa_run::step st = { current.source->clone(), current.label,
|
||||
current.acc };
|
||||
run->cycle.push_back(st);
|
||||
// remove this source state of target
|
||||
target.erase(i);
|
||||
}
|
||||
{
|
||||
// find a minimal path from current.dest to any source state in
|
||||
// target.
|
||||
ndfsr_trace << "looking for a path from "
|
||||
<< a_->format_state(current.dest) << std::endl;
|
||||
typename m_source_trans::iterator i = target.find(current.dest);
|
||||
if (i == target.end())
|
||||
{
|
||||
min_path<true> s(this, a_, target, h_);
|
||||
const state* res = s.search(current.dest->clone(), run->cycle);
|
||||
// init current to the corresponding transition.
|
||||
assert(res);
|
||||
ndfsr_trace << a_->format_state(res) << " reached" << std::endl;
|
||||
i = target.find(res);
|
||||
assert(i != target.end());
|
||||
}
|
||||
else
|
||||
{
|
||||
ndfsr_trace << "this is a target" << std::endl;
|
||||
}
|
||||
current = i->second;
|
||||
// complete the path with the corresponding transition
|
||||
twa_run::step st = { current.source->clone(), current.label,
|
||||
current.acc };
|
||||
run->cycle.push_back(st);
|
||||
// remove this source state of target
|
||||
target.erase(i);
|
||||
}
|
||||
|
||||
if (current.dest->compare(begin) != 0)
|
||||
{
|
||||
// close the cycle by adding a path from the destination of the
|
||||
// last inserted transition to the source of the first one
|
||||
ndfsr_trace << std::endl << "looking for a path from "
|
||||
<< a_->format_state(current.dest) << " to "
|
||||
<< a_->format_state(begin) << std::endl;
|
||||
transition tmp;
|
||||
// Initialize to please GCC 4.0.1 (Darwin).
|
||||
tmp.source = tmp.dest = nullptr;
|
||||
tmp.acc = 0U;
|
||||
target.emplace(begin, tmp);
|
||||
min_path<true> s(this, a_, target, h_);
|
||||
const state* res = s.search(current.dest->clone(), run->cycle);
|
||||
assert(res);
|
||||
assert(res->compare(begin) == 0);
|
||||
(void)res;
|
||||
}
|
||||
{
|
||||
// close the cycle by adding a path from the destination of the
|
||||
// last inserted transition to the source of the first one
|
||||
ndfsr_trace << std::endl << "looking for a path from "
|
||||
<< a_->format_state(current.dest) << " to "
|
||||
<< a_->format_state(begin) << std::endl;
|
||||
transition tmp;
|
||||
// Initialize to please GCC 4.0.1 (Darwin).
|
||||
tmp.source = tmp.dest = nullptr;
|
||||
tmp.acc = 0U;
|
||||
target.emplace(begin, tmp);
|
||||
min_path<true> s(this, a_, target, h_);
|
||||
const state* res = s.search(current.dest->clone(), run->cycle);
|
||||
assert(res);
|
||||
assert(res->compare(begin) == 0);
|
||||
(void)res;
|
||||
}
|
||||
}
|
||||
|
||||
void construct_prefix(twa_run_ptr run)
|
||||
|
|
@ -626,7 +626,7 @@ namespace spot
|
|||
|
||||
// Register all states from the cycle as target of the BFS.
|
||||
for (twa_run::steps::const_iterator i = run->cycle.begin();
|
||||
i != run->cycle.end(); ++i)
|
||||
i != run->cycle.end(); ++i)
|
||||
target.emplace(i->s, tmp);
|
||||
|
||||
const state* prefix_start = a_->get_init_state();
|
||||
|
|
@ -655,8 +655,8 @@ namespace spot
|
|||
// Locate cycle_entry_point on the cycle.
|
||||
twa_run::steps::iterator cycle_ep_it;
|
||||
for (cycle_ep_it = run->cycle.begin();
|
||||
cycle_ep_it != run->cycle.end()
|
||||
&& cycle_entry_point->compare(cycle_ep_it->s); ++cycle_ep_it)
|
||||
cycle_ep_it != run->cycle.end()
|
||||
&& cycle_entry_point->compare(cycle_ep_it->s); ++cycle_ep_it)
|
||||
continue;
|
||||
assert(cycle_ep_it != run->cycle.end());
|
||||
cycle_entry_point->destroy();
|
||||
|
|
|
|||
|
|
@ -47,167 +47,167 @@ namespace spot
|
|||
|
||||
public:
|
||||
never_claim_output(std::ostream& os, const char* options)
|
||||
: os_(os)
|
||||
: os_(os)
|
||||
{
|
||||
if (options)
|
||||
while (char c = *options++)
|
||||
switch (c)
|
||||
{
|
||||
case '6':
|
||||
opt_624_ = true;
|
||||
break;
|
||||
case 'c':
|
||||
opt_comments_ = true;
|
||||
break;
|
||||
default:
|
||||
throw std::runtime_error
|
||||
(std::string("unknown option for print_never_claim(): ")
|
||||
+ c);
|
||||
}
|
||||
if (options)
|
||||
while (char c = *options++)
|
||||
switch (c)
|
||||
{
|
||||
case '6':
|
||||
opt_624_ = true;
|
||||
break;
|
||||
case 'c':
|
||||
opt_comments_ = true;
|
||||
break;
|
||||
default:
|
||||
throw std::runtime_error
|
||||
(std::string("unknown option for print_never_claim(): ")
|
||||
+ c);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
start() const
|
||||
{
|
||||
os_ << "never {";
|
||||
auto n = aut_->get_named_prop<std::string>("automaton-name");
|
||||
if (n)
|
||||
os_ << " /* " << *n << " */";
|
||||
os_ << '\n';
|
||||
os_ << "never {";
|
||||
auto n = aut_->get_named_prop<std::string>("automaton-name");
|
||||
if (n)
|
||||
os_ << " /* " << *n << " */";
|
||||
os_ << '\n';
|
||||
}
|
||||
|
||||
void
|
||||
end() const
|
||||
{
|
||||
if (need_accept_all_)
|
||||
{
|
||||
os_ << "accept_all:";
|
||||
print_comment(accept_all_);
|
||||
os_ << "\n skip\n";
|
||||
}
|
||||
os_ << '}' << std::endl;
|
||||
if (need_accept_all_)
|
||||
{
|
||||
os_ << "accept_all:";
|
||||
print_comment(accept_all_);
|
||||
os_ << "\n skip\n";
|
||||
}
|
||||
os_ << '}' << std::endl;
|
||||
}
|
||||
|
||||
bool is_sink(unsigned n) const
|
||||
{
|
||||
auto ts = aut_->out(n);
|
||||
assert(ts.begin() != ts.end());
|
||||
auto it = ts.begin();
|
||||
return (it->cond == bddtrue) && (it->dst == n) && (++it == ts.end());
|
||||
auto ts = aut_->out(n);
|
||||
assert(ts.begin() != ts.end());
|
||||
auto it = ts.begin();
|
||||
return (it->cond == bddtrue) && (it->dst == n) && (++it == ts.end());
|
||||
}
|
||||
|
||||
void
|
||||
print_comment(unsigned n) const
|
||||
{
|
||||
if (sn_)
|
||||
if (n < sn_->size() && !(*sn_)[n].empty())
|
||||
os_ << " /* " << (*sn_)[n] << " */";
|
||||
if (sn_)
|
||||
if (n < sn_->size() && !(*sn_)[n].empty())
|
||||
os_ << " /* " << (*sn_)[n] << " */";
|
||||
}
|
||||
|
||||
void
|
||||
print_state(unsigned n) const
|
||||
{
|
||||
bool acc = aut_->state_is_accepting(n);
|
||||
if (n == aut_->get_init_state_number())
|
||||
{
|
||||
if (acc)
|
||||
os_ << "accept_init";
|
||||
else
|
||||
os_ << "T0_init";
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!acc)
|
||||
os_ << "T0_S" << n;
|
||||
else if (is_sink(n))
|
||||
os_ << "accept_all";
|
||||
else
|
||||
os_ << "accept_S" << n;
|
||||
}
|
||||
bool acc = aut_->state_is_accepting(n);
|
||||
if (n == aut_->get_init_state_number())
|
||||
{
|
||||
if (acc)
|
||||
os_ << "accept_init";
|
||||
else
|
||||
os_ << "T0_init";
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!acc)
|
||||
os_ << "T0_S" << n;
|
||||
else if (is_sink(n))
|
||||
os_ << "accept_all";
|
||||
else
|
||||
os_ << "accept_S" << n;
|
||||
}
|
||||
}
|
||||
|
||||
void process_state(unsigned n)
|
||||
{
|
||||
if (aut_->state_is_accepting(n) && is_sink(n)
|
||||
&& n != aut_->get_init_state_number())
|
||||
{
|
||||
// We want the accept_all state at the end of the never claim.
|
||||
need_accept_all_ = true;
|
||||
accept_all_ = n;
|
||||
return;
|
||||
}
|
||||
if (aut_->state_is_accepting(n) && is_sink(n)
|
||||
&& n != aut_->get_init_state_number())
|
||||
{
|
||||
// We want the accept_all state at the end of the never claim.
|
||||
need_accept_all_ = true;
|
||||
accept_all_ = n;
|
||||
return;
|
||||
}
|
||||
|
||||
print_state(n);
|
||||
os_ << ':';
|
||||
print_comment(n);
|
||||
os_ << (opt_624_ ? "\n do\n" : "\n if\n");
|
||||
bool did_output = false;
|
||||
for (auto& t: aut_->out(n))
|
||||
{
|
||||
did_output = true;
|
||||
bool atom =
|
||||
opt_624_ && aut_->state_is_accepting(t.dst) && is_sink(t.dst);
|
||||
if (atom)
|
||||
os_ << " :: atomic { (";
|
||||
else
|
||||
os_ << " :: (";
|
||||
formula f = bdd_to_formula(t.cond, aut_->get_dict());
|
||||
// This is actually a Boolean formula, but the LTL printer
|
||||
// is all we have.
|
||||
print_spin_ltl(os_, f, true);
|
||||
if (atom)
|
||||
{
|
||||
os_ << ") -> assert(!(";
|
||||
print_spin_ltl(os_, f, true);
|
||||
os_ << ")) }";
|
||||
}
|
||||
else
|
||||
{
|
||||
os_ << ") -> goto ";
|
||||
print_state(t.dst);
|
||||
}
|
||||
os_ << '\n';
|
||||
}
|
||||
if (!did_output)
|
||||
{
|
||||
if (opt_624_)
|
||||
{
|
||||
os_ << " :: atomic { (false) -> assert(!(false)) }";
|
||||
}
|
||||
else
|
||||
{
|
||||
os_ << " :: (false) -> goto ";
|
||||
print_state(n);
|
||||
}
|
||||
os_ << '\n';
|
||||
}
|
||||
os_ << (opt_624_ ? " od;\n" : " fi;\n");
|
||||
print_state(n);
|
||||
os_ << ':';
|
||||
print_comment(n);
|
||||
os_ << (opt_624_ ? "\n do\n" : "\n if\n");
|
||||
bool did_output = false;
|
||||
for (auto& t: aut_->out(n))
|
||||
{
|
||||
did_output = true;
|
||||
bool atom =
|
||||
opt_624_ && aut_->state_is_accepting(t.dst) && is_sink(t.dst);
|
||||
if (atom)
|
||||
os_ << " :: atomic { (";
|
||||
else
|
||||
os_ << " :: (";
|
||||
formula f = bdd_to_formula(t.cond, aut_->get_dict());
|
||||
// This is actually a Boolean formula, but the LTL printer
|
||||
// is all we have.
|
||||
print_spin_ltl(os_, f, true);
|
||||
if (atom)
|
||||
{
|
||||
os_ << ") -> assert(!(";
|
||||
print_spin_ltl(os_, f, true);
|
||||
os_ << ")) }";
|
||||
}
|
||||
else
|
||||
{
|
||||
os_ << ") -> goto ";
|
||||
print_state(t.dst);
|
||||
}
|
||||
os_ << '\n';
|
||||
}
|
||||
if (!did_output)
|
||||
{
|
||||
if (opt_624_)
|
||||
{
|
||||
os_ << " :: atomic { (false) -> assert(!(false)) }";
|
||||
}
|
||||
else
|
||||
{
|
||||
os_ << " :: (false) -> goto ";
|
||||
print_state(n);
|
||||
}
|
||||
os_ << '\n';
|
||||
}
|
||||
os_ << (opt_624_ ? " od;\n" : " fi;\n");
|
||||
}
|
||||
|
||||
void print(const const_twa_graph_ptr& aut)
|
||||
{
|
||||
aut_ = aut;
|
||||
if (opt_comments_)
|
||||
sn_ = aut->get_named_prop<std::vector<std::string>>("state-names");
|
||||
start();
|
||||
unsigned init = aut_->get_init_state_number();
|
||||
unsigned ns = aut_->num_states();
|
||||
process_state(init);
|
||||
for (unsigned n = 0; n < ns; ++n)
|
||||
if (n != init)
|
||||
process_state(n);
|
||||
end();
|
||||
aut_ = aut;
|
||||
if (opt_comments_)
|
||||
sn_ = aut->get_named_prop<std::vector<std::string>>("state-names");
|
||||
start();
|
||||
unsigned init = aut_->get_init_state_number();
|
||||
unsigned ns = aut_->num_states();
|
||||
process_state(init);
|
||||
for (unsigned n = 0; n < ns; ++n)
|
||||
if (n != init)
|
||||
process_state(n);
|
||||
end();
|
||||
}
|
||||
};
|
||||
} // anonymous namespace
|
||||
|
||||
std::ostream&
|
||||
print_never_claim(std::ostream& os, const const_twa_ptr& g,
|
||||
const char* options)
|
||||
const char* options)
|
||||
{
|
||||
if (!(g->acc().is_buchi() || g->acc().is_all()))
|
||||
throw std::runtime_error
|
||||
("Never claim output only supports Büchi acceptance");
|
||||
("Never claim output only supports Büchi acceptance");
|
||||
never_claim_output d(os, options);
|
||||
auto aut = std::dynamic_pointer_cast<const twa_graph>(g);
|
||||
if (!aut)
|
||||
|
|
|
|||
|
|
@ -40,6 +40,6 @@ namespace spot
|
|||
/// \param opt a string of option: 'c' to comment each state
|
||||
SPOT_API std::ostream&
|
||||
print_never_claim(std::ostream& os,
|
||||
const const_twa_ptr& g,
|
||||
const char* opt = nullptr);
|
||||
const const_twa_ptr& g,
|
||||
const char* opt = nullptr);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,11 +43,11 @@ namespace spot
|
|||
ensure_ba(twa_graph_ptr& a)
|
||||
{
|
||||
if (a->num_sets() == 0)
|
||||
{
|
||||
auto m = a->set_buchi();
|
||||
for (auto& t: a->edges())
|
||||
t.acc = m;
|
||||
}
|
||||
{
|
||||
auto m = a->set_buchi();
|
||||
for (auto& t: a->edges())
|
||||
t.acc = m;
|
||||
}
|
||||
return a;
|
||||
}
|
||||
}
|
||||
|
|
@ -56,36 +56,36 @@ namespace spot
|
|||
{
|
||||
if (opt)
|
||||
{
|
||||
degen_order_ = opt->get("degen-order", 0);
|
||||
degen_reset_ = opt->get("degen-reset", 1);
|
||||
degen_cache_ = opt->get("degen-lcache", 1);
|
||||
degen_lskip_ = opt->get("degen-lskip", 1);
|
||||
degen_lowinit_ = opt->get("degen-lowinit", 0);
|
||||
det_scc_ = opt->get("det-scc", 1);
|
||||
det_simul_ = opt->get("det-simul", 1);
|
||||
det_stutter_ = opt->get("det-stutter", 1);
|
||||
simul_ = opt->get("simul", -1);
|
||||
scc_filter_ = opt->get("scc-filter", -1);
|
||||
ba_simul_ = opt->get("ba-simul", -1);
|
||||
tba_determinisation_ = opt->get("tba-det", 0);
|
||||
sat_minimize_ = opt->get("sat-minimize", 0);
|
||||
sat_acc_ = opt->get("sat-acc", 0);
|
||||
sat_states_ = opt->get("sat-states", 0);
|
||||
state_based_ = opt->get("state-based", 0);
|
||||
wdba_minimize_ = opt->get("wdba-minimize", 1);
|
||||
degen_order_ = opt->get("degen-order", 0);
|
||||
degen_reset_ = opt->get("degen-reset", 1);
|
||||
degen_cache_ = opt->get("degen-lcache", 1);
|
||||
degen_lskip_ = opt->get("degen-lskip", 1);
|
||||
degen_lowinit_ = opt->get("degen-lowinit", 0);
|
||||
det_scc_ = opt->get("det-scc", 1);
|
||||
det_simul_ = opt->get("det-simul", 1);
|
||||
det_stutter_ = opt->get("det-stutter", 1);
|
||||
simul_ = opt->get("simul", -1);
|
||||
scc_filter_ = opt->get("scc-filter", -1);
|
||||
ba_simul_ = opt->get("ba-simul", -1);
|
||||
tba_determinisation_ = opt->get("tba-det", 0);
|
||||
sat_minimize_ = opt->get("sat-minimize", 0);
|
||||
sat_acc_ = opt->get("sat-acc", 0);
|
||||
sat_states_ = opt->get("sat-states", 0);
|
||||
state_based_ = opt->get("state-based", 0);
|
||||
wdba_minimize_ = opt->get("wdba-minimize", 1);
|
||||
|
||||
if (sat_acc_ && sat_minimize_ == 0)
|
||||
sat_minimize_ = 1; // 2?
|
||||
if (sat_states_ && sat_minimize_ == 0)
|
||||
sat_minimize_ = 1;
|
||||
if (sat_minimize_)
|
||||
{
|
||||
tba_determinisation_ = 1;
|
||||
if (sat_acc_ <= 0)
|
||||
sat_acc_ = -1;
|
||||
if (sat_states_ <= 0)
|
||||
sat_states_ = -1;
|
||||
}
|
||||
if (sat_acc_ && sat_minimize_ == 0)
|
||||
sat_minimize_ = 1; // 2?
|
||||
if (sat_states_ && sat_minimize_ == 0)
|
||||
sat_minimize_ = 1;
|
||||
if (sat_minimize_)
|
||||
{
|
||||
tba_determinisation_ = 1;
|
||||
if (sat_acc_ <= 0)
|
||||
sat_acc_ = -1;
|
||||
if (sat_states_ <= 0)
|
||||
sat_states_ = -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -97,14 +97,14 @@ namespace spot
|
|||
switch (opt)
|
||||
{
|
||||
case 0:
|
||||
return a;
|
||||
return a;
|
||||
case 1:
|
||||
return simulation(a);
|
||||
return simulation(a);
|
||||
case 2:
|
||||
return cosimulation(a);
|
||||
return cosimulation(a);
|
||||
case 3:
|
||||
default:
|
||||
return iterated_simulations(a);
|
||||
return iterated_simulations(a);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -116,14 +116,14 @@ namespace spot
|
|||
switch (opt)
|
||||
{
|
||||
case 0:
|
||||
return a;
|
||||
return a;
|
||||
case 1:
|
||||
return simulation_sba(a);
|
||||
return simulation_sba(a);
|
||||
case 2:
|
||||
return cosimulation_sba(a);
|
||||
return cosimulation_sba(a);
|
||||
case 3:
|
||||
default:
|
||||
return iterated_simulations_sba(a);
|
||||
return iterated_simulations_sba(a);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -131,9 +131,9 @@ namespace spot
|
|||
postprocessor::do_degen(const twa_graph_ptr& a)
|
||||
{
|
||||
auto d = degeneralize(a,
|
||||
degen_reset_, degen_order_,
|
||||
degen_cache_, degen_lskip_,
|
||||
degen_lowinit_);
|
||||
degen_reset_, degen_order_,
|
||||
degen_cache_, degen_lskip_,
|
||||
degen_lowinit_);
|
||||
return do_sba_simul(d, ba_simul_);
|
||||
}
|
||||
|
||||
|
|
@ -145,7 +145,7 @@ namespace spot
|
|||
// If the automaton is weak, using transition-based acceptance
|
||||
// won't help, so let's preserve it.
|
||||
if ((state_based_ || a->prop_inherently_weak().is_true())
|
||||
&& a->prop_state_acc().is_true())
|
||||
&& a->prop_state_acc().is_true())
|
||||
return scc_filter_states(a, arg);
|
||||
else
|
||||
return scc_filter(a, arg);
|
||||
|
|
@ -175,22 +175,22 @@ namespace spot
|
|||
|
||||
if (type_ != Generic && !a->acc().is_generalized_buchi())
|
||||
{
|
||||
a = to_generalized_buchi(a);
|
||||
if (PREF_ == Any && level_ == Low)
|
||||
a = do_scc_filter(a, true);
|
||||
a = to_generalized_buchi(a);
|
||||
if (PREF_ == Any && level_ == Low)
|
||||
a = do_scc_filter(a, true);
|
||||
}
|
||||
|
||||
if (PREF_ == Any && level_ == Low
|
||||
&& (type_ == Generic
|
||||
|| type_ == TGBA
|
||||
|| (type_ == BA && a->is_sba())
|
||||
|| (type_ == Monitor && a->num_sets() == 0)))
|
||||
&& (type_ == Generic
|
||||
|| type_ == TGBA
|
||||
|| (type_ == BA && a->is_sba())
|
||||
|| (type_ == Monitor && a->num_sets() == 0)))
|
||||
{
|
||||
if (COMP_)
|
||||
a = complete(a);
|
||||
if (SBACC_)
|
||||
a = sbacc(a);
|
||||
return a;
|
||||
if (COMP_)
|
||||
a = complete(a);
|
||||
if (SBACC_)
|
||||
a = sbacc(a);
|
||||
return a;
|
||||
}
|
||||
|
||||
int original_acc = a->num_sets();
|
||||
|
|
@ -205,38 +205,38 @@ namespace spot
|
|||
|
||||
if (type_ == Monitor)
|
||||
{
|
||||
if (PREF_ == Deterministic)
|
||||
a = minimize_monitor(a);
|
||||
else
|
||||
strip_acceptance_here(a);
|
||||
if (PREF_ == Deterministic)
|
||||
a = minimize_monitor(a);
|
||||
else
|
||||
strip_acceptance_here(a);
|
||||
|
||||
if (PREF_ == Any)
|
||||
return a;
|
||||
if (PREF_ == Any)
|
||||
return a;
|
||||
|
||||
a = do_simul(a, simul_);
|
||||
a = do_simul(a, simul_);
|
||||
|
||||
// For Small,High we return the smallest between the output of
|
||||
// the simulation, and that of the deterministic minimization.
|
||||
if (PREF_ == Small && level_ == High && simul_)
|
||||
{
|
||||
auto m = minimize_monitor(a);
|
||||
if (m->num_states() < a->num_states())
|
||||
a = m;
|
||||
}
|
||||
if (COMP_)
|
||||
a = complete(a);
|
||||
return a;
|
||||
// For Small,High we return the smallest between the output of
|
||||
// the simulation, and that of the deterministic minimization.
|
||||
if (PREF_ == Small && level_ == High && simul_)
|
||||
{
|
||||
auto m = minimize_monitor(a);
|
||||
if (m->num_states() < a->num_states())
|
||||
a = m;
|
||||
}
|
||||
if (COMP_)
|
||||
a = complete(a);
|
||||
return a;
|
||||
}
|
||||
|
||||
if (PREF_ == Any)
|
||||
{
|
||||
if (type_ == BA)
|
||||
a = do_degen(a);
|
||||
if (COMP_)
|
||||
a = complete(a);
|
||||
if (SBACC_)
|
||||
a = sbacc(a);
|
||||
return a;
|
||||
if (type_ == BA)
|
||||
a = do_degen(a);
|
||||
if (COMP_)
|
||||
a = complete(a);
|
||||
if (SBACC_)
|
||||
a = sbacc(a);
|
||||
return a;
|
||||
}
|
||||
|
||||
bool dba_is_wdba = false;
|
||||
|
|
@ -248,45 +248,45 @@ namespace spot
|
|||
// WDBA-minimization.
|
||||
if ((PREF_ != Small || level_ != Low) && wdba_minimize_)
|
||||
{
|
||||
bool reject_bigger = (PREF_ == Small) && (level_ == Medium);
|
||||
dba = minimize_obligation(a, f, nullptr, reject_bigger);
|
||||
if (dba
|
||||
&& dba->prop_inherently_weak().is_true()
|
||||
&& dba->prop_deterministic().is_true())
|
||||
{
|
||||
// The WDBA is a BA, so no degeneralization is required.
|
||||
// We just need to add an acceptance set if there is none.
|
||||
dba_is_minimal = dba_is_wdba = true;
|
||||
if (type_ == BA)
|
||||
ensure_ba(dba);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Minimization failed.
|
||||
dba = nullptr;
|
||||
}
|
||||
bool reject_bigger = (PREF_ == Small) && (level_ == Medium);
|
||||
dba = minimize_obligation(a, f, nullptr, reject_bigger);
|
||||
if (dba
|
||||
&& dba->prop_inherently_weak().is_true()
|
||||
&& dba->prop_deterministic().is_true())
|
||||
{
|
||||
// The WDBA is a BA, so no degeneralization is required.
|
||||
// We just need to add an acceptance set if there is none.
|
||||
dba_is_minimal = dba_is_wdba = true;
|
||||
if (type_ == BA)
|
||||
ensure_ba(dba);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Minimization failed.
|
||||
dba = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
// Run a simulation when wdba failed (or was not run), or
|
||||
// at hard levels if we want a small output.
|
||||
if (!dba || (level_ == High && PREF_ == Small))
|
||||
{
|
||||
if (((SBACC_ && a->prop_state_acc().is_true())
|
||||
|| (type_ == BA && a->is_sba()))
|
||||
&& !tba_determinisation_)
|
||||
{
|
||||
sim = do_sba_simul(a, ba_simul_);
|
||||
}
|
||||
else
|
||||
{
|
||||
sim = do_simul(a, simul_);
|
||||
// Degeneralize the result of the simulation if needed.
|
||||
// No need to do that if tba_determinisation_ will be used.
|
||||
if (type_ == BA && !tba_determinisation_)
|
||||
sim = do_degen(sim);
|
||||
else if (SBACC_ && !tba_determinisation_)
|
||||
sim = sbacc(sim);
|
||||
}
|
||||
if (((SBACC_ && a->prop_state_acc().is_true())
|
||||
|| (type_ == BA && a->is_sba()))
|
||||
&& !tba_determinisation_)
|
||||
{
|
||||
sim = do_sba_simul(a, ba_simul_);
|
||||
}
|
||||
else
|
||||
{
|
||||
sim = do_simul(a, simul_);
|
||||
// Degeneralize the result of the simulation if needed.
|
||||
// No need to do that if tba_determinisation_ will be used.
|
||||
if (type_ == BA && !tba_determinisation_)
|
||||
sim = do_degen(sim);
|
||||
else if (SBACC_ && !tba_determinisation_)
|
||||
sim = sbacc(sim);
|
||||
}
|
||||
}
|
||||
|
||||
// If WDBA failed, but the simulation returned a deterministic
|
||||
|
|
@ -294,80 +294,80 @@ namespace spot
|
|||
assert(dba || sim);
|
||||
if (!dba && is_deterministic(sim))
|
||||
{
|
||||
std::swap(sim, dba);
|
||||
// We postponed degeneralization above i case we would need
|
||||
// to perform TBA-determinisation, but now it is clear
|
||||
// that we won't perform it. So do degeneralize.
|
||||
if (tba_determinisation_)
|
||||
{
|
||||
if (type_ == BA)
|
||||
{
|
||||
dba = do_degen(dba);
|
||||
assert(is_deterministic(dba));
|
||||
}
|
||||
else if (SBACC_)
|
||||
{
|
||||
dba = sbacc(dba);
|
||||
assert(is_deterministic(dba));
|
||||
}
|
||||
}
|
||||
std::swap(sim, dba);
|
||||
// We postponed degeneralization above i case we would need
|
||||
// to perform TBA-determinisation, but now it is clear
|
||||
// that we won't perform it. So do degeneralize.
|
||||
if (tba_determinisation_)
|
||||
{
|
||||
if (type_ == BA)
|
||||
{
|
||||
dba = do_degen(dba);
|
||||
assert(is_deterministic(dba));
|
||||
}
|
||||
else if (SBACC_)
|
||||
{
|
||||
dba = sbacc(dba);
|
||||
assert(is_deterministic(dba));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// If we don't have a DBA, attempt tba-determinization if requested.
|
||||
if (tba_determinisation_ && !dba)
|
||||
{
|
||||
twa_graph_ptr tmpd = nullptr;
|
||||
if (PREF_ == Deterministic
|
||||
&& f
|
||||
&& f.is_syntactic_recurrence()
|
||||
&& sim->num_sets() > 1)
|
||||
tmpd = degeneralize_tba(sim);
|
||||
twa_graph_ptr tmpd = nullptr;
|
||||
if (PREF_ == Deterministic
|
||||
&& f
|
||||
&& f.is_syntactic_recurrence()
|
||||
&& sim->num_sets() > 1)
|
||||
tmpd = degeneralize_tba(sim);
|
||||
|
||||
auto in = tmpd ? tmpd : sim;
|
||||
auto in = tmpd ? tmpd : sim;
|
||||
|
||||
// These thresholds are arbitrary.
|
||||
//
|
||||
// For producing Small automata, we assume that a
|
||||
// deterministic automaton that is twice the size of the
|
||||
// original will never get reduced to a smaller one. We also
|
||||
// do not want more than 2^13 cycles in an SCC.
|
||||
//
|
||||
// For Deterministic automata, we accept automata that
|
||||
// are 8 times bigger, with no more that 2^15 cycle per SCC.
|
||||
// The cycle threshold is the most important limit here. You
|
||||
// may up it if you want to try producing larger automata.
|
||||
auto tmp =
|
||||
tba_determinize_check(in,
|
||||
(PREF_ == Small) ? 2 : 8,
|
||||
1 << ((PREF_ == Small) ? 13 : 15),
|
||||
f);
|
||||
if (tmp && tmp != in)
|
||||
{
|
||||
// There is no point in running the reverse simulation on
|
||||
// a deterministic automaton, since all prefixes are
|
||||
// unique.
|
||||
dba = simulation(tmp);
|
||||
}
|
||||
if (dba && PREF_ == Deterministic)
|
||||
{
|
||||
// disregard the result of the simulation.
|
||||
sim = nullptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
// degeneralize sim, because we did not do it earlier
|
||||
if (type_ == BA)
|
||||
sim = do_degen(sim);
|
||||
}
|
||||
// These thresholds are arbitrary.
|
||||
//
|
||||
// For producing Small automata, we assume that a
|
||||
// deterministic automaton that is twice the size of the
|
||||
// original will never get reduced to a smaller one. We also
|
||||
// do not want more than 2^13 cycles in an SCC.
|
||||
//
|
||||
// For Deterministic automata, we accept automata that
|
||||
// are 8 times bigger, with no more that 2^15 cycle per SCC.
|
||||
// The cycle threshold is the most important limit here. You
|
||||
// may up it if you want to try producing larger automata.
|
||||
auto tmp =
|
||||
tba_determinize_check(in,
|
||||
(PREF_ == Small) ? 2 : 8,
|
||||
1 << ((PREF_ == Small) ? 13 : 15),
|
||||
f);
|
||||
if (tmp && tmp != in)
|
||||
{
|
||||
// There is no point in running the reverse simulation on
|
||||
// a deterministic automaton, since all prefixes are
|
||||
// unique.
|
||||
dba = simulation(tmp);
|
||||
}
|
||||
if (dba && PREF_ == Deterministic)
|
||||
{
|
||||
// disregard the result of the simulation.
|
||||
sim = nullptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
// degeneralize sim, because we did not do it earlier
|
||||
if (type_ == BA)
|
||||
sim = do_degen(sim);
|
||||
}
|
||||
}
|
||||
|
||||
if (PREF_ == Deterministic && type_ == Generic && !dba)
|
||||
{
|
||||
dba = tgba_determinize(to_generalized_buchi(sim),
|
||||
false, det_scc_, det_simul_, det_stutter_);
|
||||
if (level_ != Low)
|
||||
dba = simulation(dba);
|
||||
sim = nullptr;
|
||||
dba = tgba_determinize(to_generalized_buchi(sim),
|
||||
false, det_scc_, det_simul_, det_stutter_);
|
||||
if (level_ != Low)
|
||||
dba = simulation(dba);
|
||||
sim = nullptr;
|
||||
}
|
||||
|
||||
// Now dba contains either the result of WDBA-minimization (in
|
||||
|
|
@ -379,101 +379,101 @@ namespace spot
|
|||
// value in sat_minimize_ can force its use for debugging.
|
||||
if (sat_minimize_ && dba && (!dba_is_wdba || sat_minimize_ < 0))
|
||||
{
|
||||
if (type_ == Generic)
|
||||
throw std::runtime_error
|
||||
("postproc() no yet updated to mix sat-minimize and Generic");
|
||||
unsigned target_acc;
|
||||
if (type_ == BA)
|
||||
target_acc = 1;
|
||||
else if (sat_acc_ != -1)
|
||||
target_acc = sat_acc_;
|
||||
else
|
||||
// Take the number of acceptance conditions from the input
|
||||
// automaton, not from dba, because dba often has been
|
||||
// degeneralized by tba_determinize_check(). Make sure it
|
||||
// is at least 1.
|
||||
target_acc = original_acc > 0 ? original_acc : 1;
|
||||
if (type_ == Generic)
|
||||
throw std::runtime_error
|
||||
("postproc() no yet updated to mix sat-minimize and Generic");
|
||||
unsigned target_acc;
|
||||
if (type_ == BA)
|
||||
target_acc = 1;
|
||||
else if (sat_acc_ != -1)
|
||||
target_acc = sat_acc_;
|
||||
else
|
||||
// Take the number of acceptance conditions from the input
|
||||
// automaton, not from dba, because dba often has been
|
||||
// degeneralized by tba_determinize_check(). Make sure it
|
||||
// is at least 1.
|
||||
target_acc = original_acc > 0 ? original_acc : 1;
|
||||
|
||||
const_twa_graph_ptr in = nullptr;
|
||||
if (target_acc == 1)
|
||||
{
|
||||
// If we are seeking a minimal DBA with unknown number of
|
||||
// states, then we should start from the degeneralized,
|
||||
// because the input TBA might be smaller.
|
||||
if (state_based_)
|
||||
in = degeneralize(dba);
|
||||
else
|
||||
in = degeneralize_tba(dba);
|
||||
}
|
||||
else
|
||||
{
|
||||
in = dba;
|
||||
}
|
||||
const_twa_graph_ptr in = nullptr;
|
||||
if (target_acc == 1)
|
||||
{
|
||||
// If we are seeking a minimal DBA with unknown number of
|
||||
// states, then we should start from the degeneralized,
|
||||
// because the input TBA might be smaller.
|
||||
if (state_based_)
|
||||
in = degeneralize(dba);
|
||||
else
|
||||
in = degeneralize_tba(dba);
|
||||
}
|
||||
else
|
||||
{
|
||||
in = dba;
|
||||
}
|
||||
|
||||
twa_graph_ptr res = complete(in);
|
||||
if (target_acc == 1)
|
||||
{
|
||||
if (sat_states_ != -1)
|
||||
res = dtba_sat_synthetize(res, sat_states_, state_based_);
|
||||
else if (sat_minimize_ == 1 || sat_minimize_ == -1)
|
||||
res = dtba_sat_minimize(res, state_based_);
|
||||
else // sat_minimize_ == 2
|
||||
res = dtba_sat_minimize_dichotomy(res, state_based_);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (sat_states_ != -1)
|
||||
res = dtwa_sat_synthetize
|
||||
(res, target_acc,
|
||||
acc_cond::acc_code::generalized_buchi(target_acc),
|
||||
sat_states_, state_based_);
|
||||
else if (sat_minimize_ == 1 || sat_minimize_ == -1)
|
||||
res = dtwa_sat_minimize
|
||||
(res, target_acc,
|
||||
acc_cond::acc_code::generalized_buchi(target_acc),
|
||||
state_based_);
|
||||
else // sat_minimize_ == 2
|
||||
res = dtwa_sat_minimize_dichotomy
|
||||
(res, target_acc,
|
||||
acc_cond::acc_code::generalized_buchi(target_acc),
|
||||
state_based_);
|
||||
}
|
||||
twa_graph_ptr res = complete(in);
|
||||
if (target_acc == 1)
|
||||
{
|
||||
if (sat_states_ != -1)
|
||||
res = dtba_sat_synthetize(res, sat_states_, state_based_);
|
||||
else if (sat_minimize_ == 1 || sat_minimize_ == -1)
|
||||
res = dtba_sat_minimize(res, state_based_);
|
||||
else // sat_minimize_ == 2
|
||||
res = dtba_sat_minimize_dichotomy(res, state_based_);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (sat_states_ != -1)
|
||||
res = dtwa_sat_synthetize
|
||||
(res, target_acc,
|
||||
acc_cond::acc_code::generalized_buchi(target_acc),
|
||||
sat_states_, state_based_);
|
||||
else if (sat_minimize_ == 1 || sat_minimize_ == -1)
|
||||
res = dtwa_sat_minimize
|
||||
(res, target_acc,
|
||||
acc_cond::acc_code::generalized_buchi(target_acc),
|
||||
state_based_);
|
||||
else // sat_minimize_ == 2
|
||||
res = dtwa_sat_minimize_dichotomy
|
||||
(res, target_acc,
|
||||
acc_cond::acc_code::generalized_buchi(target_acc),
|
||||
state_based_);
|
||||
}
|
||||
|
||||
if (res)
|
||||
{
|
||||
dba = do_scc_filter(res, true);
|
||||
dba_is_minimal = true;
|
||||
}
|
||||
if (res)
|
||||
{
|
||||
dba = do_scc_filter(res, true);
|
||||
dba_is_minimal = true;
|
||||
}
|
||||
}
|
||||
|
||||
// Degeneralize the dba resulting from tba-determinization or
|
||||
// sat-minimization (which is a TBA) if requested and needed.
|
||||
if (dba && !dba_is_wdba && type_ == BA
|
||||
&& !(dba_is_minimal && state_based_ && dba->num_sets() == 1))
|
||||
&& !(dba_is_minimal && state_based_ && dba->num_sets() == 1))
|
||||
dba = degeneralize(dba);
|
||||
|
||||
if (dba && sim)
|
||||
{
|
||||
if (dba->num_states() > sim->num_states())
|
||||
dba = nullptr;
|
||||
else
|
||||
sim = nullptr;
|
||||
if (dba->num_states() > sim->num_states())
|
||||
dba = nullptr;
|
||||
else
|
||||
sim = nullptr;
|
||||
}
|
||||
|
||||
if (level_ == High && scc_filter_ != 0)
|
||||
{
|
||||
if (dba)
|
||||
{
|
||||
// Do that even for WDBA, to remove marks from transitions
|
||||
// leaving trivial SCCs.
|
||||
dba = do_scc_filter(dba, true);
|
||||
assert(!sim);
|
||||
}
|
||||
else if (sim)
|
||||
{
|
||||
sim = do_scc_filter(sim, true);
|
||||
assert(!dba);
|
||||
}
|
||||
if (dba)
|
||||
{
|
||||
// Do that even for WDBA, to remove marks from transitions
|
||||
// leaving trivial SCCs.
|
||||
dba = do_scc_filter(dba, true);
|
||||
assert(!sim);
|
||||
}
|
||||
else if (sim)
|
||||
{
|
||||
sim = do_scc_filter(sim, true);
|
||||
assert(!dba);
|
||||
}
|
||||
}
|
||||
|
||||
sim = dba ? dba : sim;
|
||||
|
|
|
|||
|
|
@ -95,10 +95,10 @@ namespace spot
|
|||
enum
|
||||
{
|
||||
Any = 0,
|
||||
Small = 1, // Small and Deterministic
|
||||
Deterministic = 2, // are exclusive choices.
|
||||
Small = 1, // Small and Deterministic
|
||||
Deterministic = 2, // are exclusive choices.
|
||||
Complete = 4,
|
||||
SBAcc = 8, // State-based acceptance.
|
||||
SBAcc = 8, // State-based acceptance.
|
||||
Unambiguous = 16,
|
||||
};
|
||||
typedef int output_pref;
|
||||
|
|
|
|||
|
|
@ -50,10 +50,10 @@ namespace spot
|
|||
unsigned nap = 0;
|
||||
int v = vin.id();
|
||||
while (v != 1)
|
||||
{
|
||||
v = bdd_high(v);
|
||||
++nap;
|
||||
}
|
||||
{
|
||||
v = bdd_high(v);
|
||||
++nap;
|
||||
}
|
||||
return nap;
|
||||
}
|
||||
|
||||
|
|
@ -63,8 +63,8 @@ namespace spot
|
|||
power_map::power_state ps;
|
||||
unsigned ns = in->size();
|
||||
for (unsigned pos = 0; pos < ns; ++pos)
|
||||
if (in->get(pos))
|
||||
ps.insert(pos);
|
||||
if (in->get(pos))
|
||||
ps.insert(pos);
|
||||
return ps;
|
||||
}
|
||||
|
||||
|
|
@ -72,7 +72,7 @@ namespace spot
|
|||
{
|
||||
size_t operator()(const bitvect* bv) const
|
||||
{
|
||||
return bv->hash();
|
||||
return bv->hash();
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -80,7 +80,7 @@ namespace spot
|
|||
{
|
||||
bool operator()(const bitvect* bvl, const bitvect* bvr) const
|
||||
{
|
||||
return *bvl == *bvr;
|
||||
return *bvl == *bvr;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
@ -94,9 +94,9 @@ namespace spot
|
|||
sup_map sup;
|
||||
// Record occurrences of all guards
|
||||
for (auto& t: aut->edges())
|
||||
sup.emplace(t.cond);
|
||||
sup.emplace(t.cond);
|
||||
for (auto& i: sup)
|
||||
allap &= bdd_support(i);
|
||||
allap &= bdd_support(i);
|
||||
}
|
||||
|
||||
unsigned nap = number_of_variables(allap);
|
||||
|
|
@ -118,13 +118,13 @@ namespace spot
|
|||
bdd all = bddtrue;
|
||||
while (all != bddfalse)
|
||||
{
|
||||
bdd one = bdd_satoneset(all, allap, bddfalse);
|
||||
all -= one;
|
||||
bdd2num.emplace(one, num2bdd.size());
|
||||
num2bdd.push_back(one);
|
||||
bdd one = bdd_satoneset(all, allap, bddfalse);
|
||||
all -= one;
|
||||
bdd2num.emplace(one, num2bdd.size());
|
||||
num2bdd.push_back(one);
|
||||
}
|
||||
|
||||
size_t nc = num2bdd.size(); // number of conditions
|
||||
size_t nc = num2bdd.size(); // number of conditions
|
||||
assert(nc == (1UL << nap));
|
||||
|
||||
// An array of bit vectors of size 'ns'. Each original state is
|
||||
|
|
@ -134,18 +134,18 @@ namespace spot
|
|||
|
||||
for (unsigned src = 0; src < ns; ++src)
|
||||
{
|
||||
size_t base = src * nc;
|
||||
for (auto& t: aut->out(src))
|
||||
{
|
||||
bdd all = t.cond;
|
||||
while (all != bddfalse)
|
||||
{
|
||||
bdd one = bdd_satoneset(all, allap, bddfalse);
|
||||
all -= one;
|
||||
unsigned num = bdd2num[one];
|
||||
bv->at(base + num).set(t.dst);
|
||||
}
|
||||
}
|
||||
size_t base = src * nc;
|
||||
for (auto& t: aut->out(src))
|
||||
{
|
||||
bdd all = t.cond;
|
||||
while (all != bddfalse)
|
||||
{
|
||||
bdd one = bdd_satoneset(all, allap, bddfalse);
|
||||
all -= one;
|
||||
unsigned num = bdd2num[one];
|
||||
bv->at(base + num).set(t.dst);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
typedef power_map::power_state power_state;
|
||||
|
|
@ -175,39 +175,39 @@ namespace spot
|
|||
|
||||
for (unsigned src_num = 0; src_num < res->num_states(); ++src_num)
|
||||
{
|
||||
om->clear_all();
|
||||
om->clear_all();
|
||||
|
||||
const power_state& src = pm.states_of(src_num);
|
||||
const power_state& src = pm.states_of(src_num);
|
||||
|
||||
for (auto s: src)
|
||||
{
|
||||
size_t base = s * nc;
|
||||
for (unsigned c = 0; c < nc; ++c)
|
||||
om->at(c) |= bv->at(base + c);
|
||||
}
|
||||
for (unsigned c = 0; c < nc; ++c)
|
||||
{
|
||||
auto dst = &om->at(c);
|
||||
if (dst->is_fully_clear())
|
||||
continue;
|
||||
auto i = seen.find(dst);
|
||||
unsigned dst_num;
|
||||
if (i != seen.end())
|
||||
{
|
||||
dst_num = i->second;
|
||||
}
|
||||
else
|
||||
{
|
||||
dst_num = res->new_state();
|
||||
auto dst2 = dst->clone();
|
||||
seen[dst2] = dst_num;
|
||||
toclean.push_back(dst2);
|
||||
auto ps = bv_to_ps(dst);
|
||||
assert(pm.map_.size() == dst_num);
|
||||
pm.map_.emplace_back(std::move(ps));
|
||||
}
|
||||
res->new_edge(src_num, dst_num, num2bdd[c]);
|
||||
}
|
||||
for (auto s: src)
|
||||
{
|
||||
size_t base = s * nc;
|
||||
for (unsigned c = 0; c < nc; ++c)
|
||||
om->at(c) |= bv->at(base + c);
|
||||
}
|
||||
for (unsigned c = 0; c < nc; ++c)
|
||||
{
|
||||
auto dst = &om->at(c);
|
||||
if (dst->is_fully_clear())
|
||||
continue;
|
||||
auto i = seen.find(dst);
|
||||
unsigned dst_num;
|
||||
if (i != seen.end())
|
||||
{
|
||||
dst_num = i->second;
|
||||
}
|
||||
else
|
||||
{
|
||||
dst_num = res->new_state();
|
||||
auto dst2 = dst->clone();
|
||||
seen[dst2] = dst_num;
|
||||
toclean.push_back(dst2);
|
||||
auto ps = bv_to_ps(dst);
|
||||
assert(pm.map_.size() == dst_num);
|
||||
pm.map_.emplace_back(std::move(ps));
|
||||
}
|
||||
res->new_edge(src_num, dst_num, num2bdd[c]);
|
||||
}
|
||||
}
|
||||
|
||||
for (auto v: toclean)
|
||||
|
|
@ -238,133 +238,133 @@ namespace spot
|
|||
protected:
|
||||
const_twa_graph_ptr ref_;
|
||||
power_map& refmap_;
|
||||
edge_set reject_; // set of rejecting edges
|
||||
set_set accept_; // set of cycles that are accepting
|
||||
edge_set all_; // all non rejecting edges
|
||||
unsigned threshold_; // maximum count of enumerated cycles
|
||||
unsigned cycles_left_; // count of cycles left to explore
|
||||
edge_set reject_; // set of rejecting edges
|
||||
set_set accept_; // set of cycles that are accepting
|
||||
edge_set all_; // all non rejecting edges
|
||||
unsigned threshold_; // maximum count of enumerated cycles
|
||||
unsigned cycles_left_; // count of cycles left to explore
|
||||
|
||||
public:
|
||||
fix_scc_acceptance(const scc_info& sm, const_twa_graph_ptr ref,
|
||||
power_map& refmap, unsigned threshold)
|
||||
: enumerate_cycles(sm), ref_(ref), refmap_(refmap),
|
||||
threshold_(threshold)
|
||||
power_map& refmap, unsigned threshold)
|
||||
: enumerate_cycles(sm), ref_(ref), refmap_(refmap),
|
||||
threshold_(threshold)
|
||||
{
|
||||
}
|
||||
|
||||
bool fix_scc(const int m)
|
||||
{
|
||||
reject_.clear();
|
||||
accept_.clear();
|
||||
cycles_left_ = threshold_;
|
||||
run(m);
|
||||
reject_.clear();
|
||||
accept_.clear();
|
||||
cycles_left_ = threshold_;
|
||||
run(m);
|
||||
|
||||
// std::cerr << "SCC #" << m << '\n';
|
||||
// std::cerr << "REJECT: ";
|
||||
// print_set(std::cerr, reject_) << '\n';
|
||||
// std::cerr << "ALL: ";
|
||||
// print_set(std::cerr, all_) << '\n';
|
||||
// for (set_set::const_iterator j = accept_.begin();
|
||||
// j != accept_.end(); ++j)
|
||||
// {
|
||||
// std::cerr << "ACCEPT: ";
|
||||
// print_set(std::cerr, *j) << '\n';
|
||||
// }
|
||||
// std::cerr << "SCC #" << m << '\n';
|
||||
// std::cerr << "REJECT: ";
|
||||
// print_set(std::cerr, reject_) << '\n';
|
||||
// std::cerr << "ALL: ";
|
||||
// print_set(std::cerr, all_) << '\n';
|
||||
// for (set_set::const_iterator j = accept_.begin();
|
||||
// j != accept_.end(); ++j)
|
||||
// {
|
||||
// std::cerr << "ACCEPT: ";
|
||||
// print_set(std::cerr, *j) << '\n';
|
||||
// }
|
||||
|
||||
auto acc = aut_->acc().all_sets();
|
||||
for (auto i: all_)
|
||||
i->acc = acc;
|
||||
return threshold_ != 0 && cycles_left_ == 0;
|
||||
auto acc = aut_->acc().all_sets();
|
||||
for (auto i: all_)
|
||||
i->acc = acc;
|
||||
return threshold_ != 0 && cycles_left_ == 0;
|
||||
}
|
||||
|
||||
bool is_cycle_accepting(cycle_iter begin, edge_set& ts) const
|
||||
{
|
||||
auto a = std::const_pointer_cast<twa_graph>(aut_);
|
||||
auto a = std::const_pointer_cast<twa_graph>(aut_);
|
||||
|
||||
// Build an automaton representing this loop.
|
||||
auto loop_a = make_twa_graph(aut_->get_dict());
|
||||
int loop_size = std::distance(begin, dfs_.end());
|
||||
loop_a->new_states(loop_size);
|
||||
int n;
|
||||
cycle_iter i;
|
||||
for (n = 1, i = begin; n <= loop_size; ++n, ++i)
|
||||
{
|
||||
trans* t = &a->edge_data(i->succ);
|
||||
loop_a->new_edge(n - 1, n % loop_size, t->cond);
|
||||
if (reject_.find(t) == reject_.end())
|
||||
ts.insert(t);
|
||||
}
|
||||
assert(i == dfs_.end());
|
||||
// Build an automaton representing this loop.
|
||||
auto loop_a = make_twa_graph(aut_->get_dict());
|
||||
int loop_size = std::distance(begin, dfs_.end());
|
||||
loop_a->new_states(loop_size);
|
||||
int n;
|
||||
cycle_iter i;
|
||||
for (n = 1, i = begin; n <= loop_size; ++n, ++i)
|
||||
{
|
||||
trans* t = &a->edge_data(i->succ);
|
||||
loop_a->new_edge(n - 1, n % loop_size, t->cond);
|
||||
if (reject_.find(t) == reject_.end())
|
||||
ts.insert(t);
|
||||
}
|
||||
assert(i == dfs_.end());
|
||||
|
||||
unsigned loop_a_init = loop_a->get_init_state_number();
|
||||
assert(loop_a_init == 0);
|
||||
unsigned loop_a_init = loop_a->get_init_state_number();
|
||||
assert(loop_a_init == 0);
|
||||
|
||||
// Check if the loop is accepting in the original automaton.
|
||||
bool accepting = false;
|
||||
// Check if the loop is accepting in the original automaton.
|
||||
bool accepting = false;
|
||||
|
||||
// Iterate on each original state corresponding to the
|
||||
// start of the loop in the determinized automaton.
|
||||
for (auto s: refmap_.states_of(begin->s))
|
||||
{
|
||||
// Check the product between LOOP_A, and ORIG_A starting
|
||||
// in S.
|
||||
if (!product(loop_a, ref_, loop_a_init, s)->is_empty())
|
||||
{
|
||||
accepting = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return accepting;
|
||||
// Iterate on each original state corresponding to the
|
||||
// start of the loop in the determinized automaton.
|
||||
for (auto s: refmap_.states_of(begin->s))
|
||||
{
|
||||
// Check the product between LOOP_A, and ORIG_A starting
|
||||
// in S.
|
||||
if (!product(loop_a, ref_, loop_a_init, s)->is_empty())
|
||||
{
|
||||
accepting = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return accepting;
|
||||
}
|
||||
|
||||
std::ostream&
|
||||
print_set(std::ostream& o, const edge_set& s) const
|
||||
{
|
||||
o << "{ ";
|
||||
for (auto i: s)
|
||||
o << i << ' ';
|
||||
o << '}';
|
||||
return o;
|
||||
o << "{ ";
|
||||
for (auto i: s)
|
||||
o << i << ' ';
|
||||
o << '}';
|
||||
return o;
|
||||
}
|
||||
|
||||
virtual bool
|
||||
cycle_found(unsigned start) override
|
||||
{
|
||||
cycle_iter i = dfs_.begin();
|
||||
while (i->s != start)
|
||||
++i;
|
||||
edge_set ts;
|
||||
bool is_acc = is_cycle_accepting(i, ts);
|
||||
do
|
||||
++i;
|
||||
while (i != dfs_.end());
|
||||
if (is_acc)
|
||||
{
|
||||
accept_.push_back(ts);
|
||||
all_.insert(ts.begin(), ts.end());
|
||||
}
|
||||
else
|
||||
{
|
||||
for (auto t: ts)
|
||||
{
|
||||
reject_.insert(t);
|
||||
for (auto& j: accept_)
|
||||
j.erase(t);
|
||||
all_.erase(t);
|
||||
}
|
||||
}
|
||||
cycle_iter i = dfs_.begin();
|
||||
while (i->s != start)
|
||||
++i;
|
||||
edge_set ts;
|
||||
bool is_acc = is_cycle_accepting(i, ts);
|
||||
do
|
||||
++i;
|
||||
while (i != dfs_.end());
|
||||
if (is_acc)
|
||||
{
|
||||
accept_.push_back(ts);
|
||||
all_.insert(ts.begin(), ts.end());
|
||||
}
|
||||
else
|
||||
{
|
||||
for (auto t: ts)
|
||||
{
|
||||
reject_.insert(t);
|
||||
for (auto& j: accept_)
|
||||
j.erase(t);
|
||||
all_.erase(t);
|
||||
}
|
||||
}
|
||||
|
||||
// Abort this algorithm if we have seen too much cycles, i.e.,
|
||||
// when cycle_left_ *reaches* 0. (If cycle_left_ == 0, that
|
||||
// means we had no limit.)
|
||||
return (cycles_left_ == 0) || --cycles_left_;
|
||||
// Abort this algorithm if we have seen too much cycles, i.e.,
|
||||
// when cycle_left_ *reaches* 0. (If cycle_left_ == 0, that
|
||||
// means we had no limit.)
|
||||
return (cycles_left_ == 0) || --cycles_left_;
|
||||
}
|
||||
};
|
||||
|
||||
static bool
|
||||
fix_dba_acceptance(twa_graph_ptr det,
|
||||
const_twa_graph_ptr ref, power_map& refmap,
|
||||
unsigned threshold)
|
||||
const_twa_graph_ptr ref, power_map& refmap,
|
||||
unsigned threshold)
|
||||
{
|
||||
det->copy_acceptance_of(ref);
|
||||
|
||||
|
|
@ -375,16 +375,16 @@ namespace spot
|
|||
fix_scc_acceptance fsa(sm, ref, refmap, threshold);
|
||||
|
||||
for (unsigned m = 0; m < scc_count; ++m)
|
||||
if (!sm.is_trivial(m))
|
||||
if (fsa.fix_scc(m))
|
||||
return true;
|
||||
if (!sm.is_trivial(m))
|
||||
if (fsa.fix_scc(m))
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
twa_graph_ptr
|
||||
tba_determinize(const const_twa_graph_ptr& aut,
|
||||
unsigned threshold_states, unsigned threshold_cycles)
|
||||
unsigned threshold_states, unsigned threshold_cycles)
|
||||
{
|
||||
power_map pm;
|
||||
// Do not merge edges in the deterministic automaton. If we
|
||||
|
|
@ -393,7 +393,7 @@ namespace spot
|
|||
auto det = tgba_powerset(aut, pm, false);
|
||||
|
||||
if ((threshold_states > 0)
|
||||
&& (pm.map_.size() > aut->num_states() * threshold_states))
|
||||
&& (pm.map_.size() > aut->num_states() * threshold_states))
|
||||
return nullptr;
|
||||
if (fix_dba_acceptance(det, aut, pm, threshold_cycles))
|
||||
return nullptr;
|
||||
|
|
@ -403,10 +403,10 @@ namespace spot
|
|||
|
||||
twa_graph_ptr
|
||||
tba_determinize_check(const twa_graph_ptr& aut,
|
||||
unsigned threshold_states,
|
||||
unsigned threshold_cycles,
|
||||
formula f,
|
||||
const_twa_graph_ptr neg_aut)
|
||||
unsigned threshold_states,
|
||||
unsigned threshold_cycles,
|
||||
formula f,
|
||||
const_twa_graph_ptr neg_aut)
|
||||
{
|
||||
if (f == nullptr && neg_aut == nullptr)
|
||||
return nullptr;
|
||||
|
|
@ -420,17 +420,17 @@ namespace spot
|
|||
|
||||
if (neg_aut == nullptr)
|
||||
{
|
||||
neg_aut = ltl_to_tgba_fm(formula::Not(f), aut->get_dict());
|
||||
// Remove useless SCCs.
|
||||
neg_aut = scc_filter(neg_aut, true);
|
||||
neg_aut = ltl_to_tgba_fm(formula::Not(f), aut->get_dict());
|
||||
// Remove useless SCCs.
|
||||
neg_aut = scc_filter(neg_aut, true);
|
||||
}
|
||||
|
||||
if (product(det, neg_aut)->is_empty())
|
||||
// Complement the DBA.
|
||||
if (product(aut, remove_fin(dtwa_complement(det)))->is_empty())
|
||||
// Finally, we are now sure that it was safe
|
||||
// to determinize the automaton.
|
||||
return det;
|
||||
// Finally, we are now sure that it was safe
|
||||
// to determinize the automaton.
|
||||
return det;
|
||||
|
||||
return aut;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -56,7 +56,7 @@ namespace spot
|
|||
//@{
|
||||
SPOT_API twa_graph_ptr
|
||||
tgba_powerset(const const_twa_graph_ptr& aut,
|
||||
power_map& pm, bool merge = true);
|
||||
power_map& pm, bool merge = true);
|
||||
SPOT_API twa_graph_ptr
|
||||
tgba_powerset(const const_twa_graph_ptr& aut);
|
||||
//@}
|
||||
|
|
@ -71,19 +71,19 @@ namespace spot
|
|||
///
|
||||
/// The construction is adapted from Section 3.2 of:
|
||||
/// \verbatim
|
||||
/// @InProceedings{ dax.07.atva,
|
||||
/// author = {Christian Dax and Jochen Eisinger and Felix Klaedtke},
|
||||
/// @InProceedings{ dax.07.atva,
|
||||
/// author = {Christian Dax and Jochen Eisinger and Felix Klaedtke},
|
||||
/// title = {Mechanizing the Powerset Construction for Restricted
|
||||
/// Classes of {$\omega$}-Automata},
|
||||
/// Classes of {$\omega$}-Automata},
|
||||
/// year = 2007,
|
||||
/// series = {Lecture Notes in Computer Science},
|
||||
/// series = {Lecture Notes in Computer Science},
|
||||
/// publisher = {Springer-Verlag},
|
||||
/// volume = 4762,
|
||||
/// volume = 4762,
|
||||
/// booktitle = {Proceedings of the 5th International Symposium on
|
||||
/// Automated Technology for Verification and Analysis
|
||||
/// (ATVA'07)},
|
||||
/// editor = {Kedar S. Namjoshi and Tomohiro Yoneda and Teruo Higashino
|
||||
/// and Yoshio Okamura},
|
||||
/// Automated Technology for Verification and Analysis
|
||||
/// (ATVA'07)},
|
||||
/// editor = {Kedar S. Namjoshi and Tomohiro Yoneda and Teruo Higashino
|
||||
/// and Yoshio Okamura},
|
||||
/// month = oct
|
||||
/// }
|
||||
/// \endverbatim
|
||||
|
|
@ -99,8 +99,8 @@ namespace spot
|
|||
/// threshold_cycles cycles.
|
||||
SPOT_API twa_graph_ptr
|
||||
tba_determinize(const const_twa_graph_ptr& aut,
|
||||
unsigned threshold_states = 0,
|
||||
unsigned threshold_cycles = 0);
|
||||
unsigned threshold_states = 0,
|
||||
unsigned threshold_cycles = 0);
|
||||
|
||||
/// \brief Determinize a TBA and make sure it is correct.
|
||||
///
|
||||
|
|
@ -131,9 +131,9 @@ namespace spot
|
|||
/// were supplied.
|
||||
SPOT_API twa_graph_ptr
|
||||
tba_determinize_check(const twa_graph_ptr& aut,
|
||||
unsigned threshold_states = 0,
|
||||
unsigned threshold_cycles = 0,
|
||||
formula f = nullptr,
|
||||
const_twa_graph_ptr neg_aut = nullptr);
|
||||
unsigned threshold_states = 0,
|
||||
unsigned threshold_cycles = 0,
|
||||
formula f = nullptr,
|
||||
const_twa_graph_ptr neg_aut = nullptr);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,120 +41,120 @@ namespace spot
|
|||
|
||||
static
|
||||
twa_graph_ptr product_aux(const const_twa_graph_ptr& left,
|
||||
const const_twa_graph_ptr& right,
|
||||
unsigned left_state,
|
||||
unsigned right_state,
|
||||
bool and_acc)
|
||||
const const_twa_graph_ptr& right,
|
||||
unsigned left_state,
|
||||
unsigned right_state,
|
||||
bool and_acc)
|
||||
{
|
||||
std::unordered_map<product_state, unsigned, product_state_hash> s2n;
|
||||
std::deque<std::pair<product_state, unsigned>> todo;
|
||||
|
||||
if (left->get_dict() != right->get_dict())
|
||||
throw std::runtime_error("product: left and right automata should "
|
||||
"share their bdd_dict");
|
||||
throw std::runtime_error("product: left and right automata should "
|
||||
"share their bdd_dict");
|
||||
auto res = make_twa_graph(left->get_dict());
|
||||
res->copy_ap_of(left);
|
||||
res->copy_ap_of(right);
|
||||
auto left_num = left->num_sets();
|
||||
auto right_acc = right->get_acceptance() << left_num;
|
||||
if (and_acc)
|
||||
right_acc &= left->get_acceptance();
|
||||
right_acc &= left->get_acceptance();
|
||||
else
|
||||
right_acc |= left->get_acceptance();
|
||||
right_acc |= left->get_acceptance();
|
||||
res->set_acceptance(left_num + right->num_sets(), right_acc);
|
||||
|
||||
auto v = new product_states;
|
||||
res->set_named_prop("product-states", v);
|
||||
|
||||
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;
|
||||
};
|
||||
[&](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;
|
||||
};
|
||||
|
||||
res->set_init_state(new_state(left_state, right_state));
|
||||
if (right_acc.is_f())
|
||||
// Do not bother doing any work if the resulting acceptance is
|
||||
// false.
|
||||
return res;
|
||||
// Do not bother doing any work if the resulting acceptance is
|
||||
// false.
|
||||
return res;
|
||||
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_edge(top.second, dst, cond,
|
||||
l.acc | (r.acc << left_num));
|
||||
// If right is deterministic, we can abort immediately!
|
||||
}
|
||||
}
|
||||
{
|
||||
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_edge(top.second, dst, cond,
|
||||
l.acc | (r.acc << left_num));
|
||||
// If right is deterministic, we can abort immediately!
|
||||
}
|
||||
}
|
||||
|
||||
res->prop_deterministic(left->prop_deterministic()
|
||||
&& right->prop_deterministic());
|
||||
&& right->prop_deterministic());
|
||||
res->prop_stutter_invariant(left->prop_stutter_invariant()
|
||||
&& right->prop_stutter_invariant());
|
||||
&& right->prop_stutter_invariant());
|
||||
// The product of X!a and Xa, two stutter-sentive formulas,
|
||||
// is stutter-invariant.
|
||||
//res->prop_stutter_sensitive(left->prop_stutter_sensitive()
|
||||
// && right->prop_stutter_sensitive());
|
||||
// && right->prop_stutter_sensitive());
|
||||
res->prop_inherently_weak(left->prop_inherently_weak()
|
||||
&& right->prop_inherently_weak());
|
||||
&& right->prop_inherently_weak());
|
||||
res->prop_weak(left->prop_weak()
|
||||
&& right->prop_weak());
|
||||
&& right->prop_weak());
|
||||
res->prop_terminal(left->prop_terminal()
|
||||
&& right->prop_terminal());
|
||||
&& right->prop_terminal());
|
||||
res->prop_state_acc(left->prop_state_acc()
|
||||
&& right->prop_state_acc());
|
||||
&& right->prop_state_acc());
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
||||
twa_graph_ptr product(const const_twa_graph_ptr& left,
|
||||
const const_twa_graph_ptr& right,
|
||||
unsigned left_state,
|
||||
unsigned right_state)
|
||||
const const_twa_graph_ptr& right,
|
||||
unsigned left_state,
|
||||
unsigned right_state)
|
||||
{
|
||||
return product_aux(left, right, left_state, right_state, true);
|
||||
}
|
||||
|
||||
twa_graph_ptr product(const const_twa_graph_ptr& left,
|
||||
const const_twa_graph_ptr& right)
|
||||
const const_twa_graph_ptr& right)
|
||||
{
|
||||
return product(left, right,
|
||||
left->get_init_state_number(),
|
||||
right->get_init_state_number());
|
||||
left->get_init_state_number(),
|
||||
right->get_init_state_number());
|
||||
}
|
||||
|
||||
twa_graph_ptr product_or(const const_twa_graph_ptr& left,
|
||||
const const_twa_graph_ptr& right,
|
||||
unsigned left_state,
|
||||
unsigned right_state)
|
||||
const const_twa_graph_ptr& right,
|
||||
unsigned left_state,
|
||||
unsigned right_state)
|
||||
{
|
||||
return product_aux(complete(left),
|
||||
complete(right),
|
||||
left_state, right_state, false);
|
||||
complete(right),
|
||||
left_state, right_state, false);
|
||||
}
|
||||
|
||||
twa_graph_ptr product_or(const const_twa_graph_ptr& left,
|
||||
const const_twa_graph_ptr& right)
|
||||
const const_twa_graph_ptr& right)
|
||||
{
|
||||
return product_or(left, right,
|
||||
left->get_init_state_number(),
|
||||
right->get_init_state_number());
|
||||
left->get_init_state_number(),
|
||||
right->get_init_state_number());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -32,22 +32,22 @@ namespace spot
|
|||
|
||||
SPOT_API
|
||||
twa_graph_ptr product(const const_twa_graph_ptr& left,
|
||||
const const_twa_graph_ptr& right);
|
||||
const const_twa_graph_ptr& right);
|
||||
|
||||
SPOT_API
|
||||
twa_graph_ptr product(const const_twa_graph_ptr& left,
|
||||
const const_twa_graph_ptr& right,
|
||||
unsigned left_state,
|
||||
unsigned right_state);
|
||||
const const_twa_graph_ptr& right,
|
||||
unsigned left_state,
|
||||
unsigned right_state);
|
||||
|
||||
SPOT_API
|
||||
twa_graph_ptr product_or(const const_twa_graph_ptr& left,
|
||||
const const_twa_graph_ptr& right);
|
||||
const const_twa_graph_ptr& right);
|
||||
|
||||
SPOT_API
|
||||
twa_graph_ptr product_or(const const_twa_graph_ptr& left,
|
||||
const const_twa_graph_ptr& right,
|
||||
unsigned left_state,
|
||||
unsigned right_state);
|
||||
const const_twa_graph_ptr& right,
|
||||
unsigned left_state,
|
||||
unsigned right_state);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,16 +29,16 @@ namespace spot
|
|||
|
||||
twa_run_ptr
|
||||
project_twa_run(const const_twa_ptr& a_run,
|
||||
const const_twa_ptr& a_proj,
|
||||
const const_twa_run_ptr& run)
|
||||
const const_twa_ptr& a_proj,
|
||||
const const_twa_run_ptr& run)
|
||||
{
|
||||
auto res = std::make_shared<twa_run>(a_proj);
|
||||
for (auto& i: run->prefix)
|
||||
res->prefix.emplace_back(a_run->project_state(i.s, a_proj),
|
||||
i.label, i.acc);
|
||||
i.label, i.acc);
|
||||
for (auto& i: run->cycle)
|
||||
res->prefix.emplace_back(a_run->project_state(i.s, a_proj),
|
||||
i.label, i.acc);
|
||||
i.label, i.acc);
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,6 +43,6 @@ namespace spot
|
|||
/// \return true iff the run could be completed
|
||||
SPOT_API twa_run_ptr
|
||||
project_twa_run(const const_twa_ptr& a_run,
|
||||
const const_twa_ptr& a_proj,
|
||||
const const_twa_run_ptr& run);
|
||||
const const_twa_ptr& a_proj,
|
||||
const const_twa_run_ptr& run);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,33 +37,33 @@ namespace spot
|
|||
{
|
||||
static unsigned
|
||||
random_deterministic_labels_rec(std::vector<bdd>& labels, int *props,
|
||||
int props_n, bdd current, unsigned n)
|
||||
int props_n, bdd current, unsigned n)
|
||||
{
|
||||
if (n > 1 && props_n >= 1)
|
||||
{
|
||||
bdd ap = bdd_ithvar(*props);
|
||||
bdd ap = bdd_ithvar(*props);
|
||||
++props;
|
||||
--props_n;
|
||||
--props_n;
|
||||
|
||||
// There are m labels generated from "current & ap"
|
||||
// and n - m labels generated from "current & !ap"
|
||||
// There are m labels generated from "current & ap"
|
||||
// and n - m labels generated from "current & !ap"
|
||||
unsigned m = rrand(1, n - 1);
|
||||
if (2 * m < n)
|
||||
{
|
||||
m = n - m;
|
||||
ap = !ap;
|
||||
}
|
||||
if (2 * m < n)
|
||||
{
|
||||
m = n - m;
|
||||
ap = !ap;
|
||||
}
|
||||
|
||||
unsigned res = random_deterministic_labels_rec(labels, props,
|
||||
props_n,
|
||||
current & ap, m);
|
||||
res += random_deterministic_labels_rec(labels, props, props_n,
|
||||
current & !ap, n - res);
|
||||
unsigned res = random_deterministic_labels_rec(labels, props,
|
||||
props_n,
|
||||
current & ap, m);
|
||||
res += random_deterministic_labels_rec(labels, props, props_n,
|
||||
current & !ap, n - res);
|
||||
return res;
|
||||
}
|
||||
else
|
||||
{
|
||||
labels.push_back(current);
|
||||
labels.push_back(current);
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
|
@ -81,8 +81,8 @@ namespace spot
|
|||
{
|
||||
acc_cond::mark_t m = 0U;
|
||||
for (unsigned i = 0U; i < n_accs; ++i)
|
||||
if (drand() < a)
|
||||
m.set(i);
|
||||
if (drand() < a)
|
||||
m.set(i);
|
||||
return m;
|
||||
}
|
||||
|
||||
|
|
@ -100,21 +100,21 @@ namespace spot
|
|||
int size = 0;
|
||||
bdd p = bddtrue;
|
||||
while (props_n)
|
||||
{
|
||||
if (size == 8 * sizeof(int))
|
||||
{
|
||||
p &= bdd_ibuildcube(val, size, props);
|
||||
props += size;
|
||||
val = 0;
|
||||
size = 0;
|
||||
}
|
||||
val <<= 1;
|
||||
val |= (drand() < t);
|
||||
++size;
|
||||
--props_n;
|
||||
}
|
||||
{
|
||||
if (size == 8 * sizeof(int))
|
||||
{
|
||||
p &= bdd_ibuildcube(val, size, props);
|
||||
props += size;
|
||||
val = 0;
|
||||
size = 0;
|
||||
}
|
||||
val <<= 1;
|
||||
val |= (drand() < t);
|
||||
++size;
|
||||
--props_n;
|
||||
}
|
||||
if (size > 0)
|
||||
p &= bdd_ibuildcube(val, size, props);
|
||||
p &= bdd_ibuildcube(val, size, props);
|
||||
|
||||
return p;
|
||||
}
|
||||
|
|
@ -122,9 +122,9 @@ namespace spot
|
|||
|
||||
twa_graph_ptr
|
||||
random_graph(int n, float d,
|
||||
const atomic_prop_set* ap, const bdd_dict_ptr& dict,
|
||||
unsigned n_accs, float a, float t,
|
||||
bool deterministic, bool state_acc, bool colored)
|
||||
const atomic_prop_set* ap, const bdd_dict_ptr& dict,
|
||||
unsigned n_accs, float a, float t,
|
||||
bool deterministic, bool state_acc, bool colored)
|
||||
{
|
||||
if (n <= 0)
|
||||
throw std::invalid_argument("random_graph() requires n>0 states");
|
||||
|
|
@ -157,8 +157,8 @@ namespace spot
|
|||
|
||||
for (int i = 1; i < n; ++i)
|
||||
{
|
||||
state_randomizer[i] = i;
|
||||
unreachable_nodes.insert(i);
|
||||
state_randomizer[i] = i;
|
||||
unreachable_nodes.insert(i);
|
||||
}
|
||||
|
||||
// We want to connect each node to a number of successors between
|
||||
|
|
@ -168,57 +168,57 @@ namespace spot
|
|||
|
||||
while (!nodes_to_process.empty())
|
||||
{
|
||||
auto src = *nodes_to_process.begin();
|
||||
nodes_to_process.erase(nodes_to_process.begin());
|
||||
auto src = *nodes_to_process.begin();
|
||||
nodes_to_process.erase(nodes_to_process.begin());
|
||||
|
||||
// Choose a random number of successors (at least one), using
|
||||
// a binomial distribution.
|
||||
unsigned nsucc = 1 + bin.rand();
|
||||
// Choose a random number of successors (at least one), using
|
||||
// a binomial distribution.
|
||||
unsigned nsucc = 1 + bin.rand();
|
||||
|
||||
bool saw_unreachable = false;
|
||||
bool saw_unreachable = false;
|
||||
|
||||
// Create NSUCC random labels.
|
||||
std::vector<bdd> labels;
|
||||
if (deterministic)
|
||||
{
|
||||
labels = random_deterministic_labels(props, props_n, nsucc);
|
||||
// Create NSUCC random labels.
|
||||
std::vector<bdd> labels;
|
||||
if (deterministic)
|
||||
{
|
||||
labels = random_deterministic_labels(props, props_n, nsucc);
|
||||
|
||||
// if nsucc > 2^props_n, we cannot produce nsucc deterministic
|
||||
// edges so we set it to labels.size()
|
||||
nsucc = labels.size();
|
||||
}
|
||||
else
|
||||
for (unsigned i = 0; i < nsucc; ++i)
|
||||
labels.push_back(random_labels(props, props_n, t));
|
||||
// if nsucc > 2^props_n, we cannot produce nsucc deterministic
|
||||
// edges so we set it to labels.size()
|
||||
nsucc = labels.size();
|
||||
}
|
||||
else
|
||||
for (unsigned i = 0; i < nsucc; ++i)
|
||||
labels.push_back(random_labels(props, props_n, t));
|
||||
|
||||
int possibilities = n;
|
||||
unsigned dst;
|
||||
acc_cond::mark_t m = 0U;
|
||||
if (state_acc)
|
||||
m = colored ? random_acc1(n_accs) : random_acc(n_accs, a);
|
||||
unsigned dst;
|
||||
acc_cond::mark_t m = 0U;
|
||||
if (state_acc)
|
||||
m = colored ? random_acc1(n_accs) : random_acc(n_accs, a);
|
||||
|
||||
for (auto& l: labels)
|
||||
{
|
||||
if (!state_acc)
|
||||
m = colored ? random_acc1(n_accs) : random_acc(n_accs, a);
|
||||
for (auto& l: labels)
|
||||
{
|
||||
if (!state_acc)
|
||||
m = colored ? random_acc1(n_accs) : random_acc(n_accs, a);
|
||||
|
||||
// No connection to unreachable successors so far. This
|
||||
// is our last chance, so force it now.
|
||||
if (--nsucc == 0
|
||||
&& !unreachable_nodes.empty()
|
||||
&& !saw_unreachable)
|
||||
{
|
||||
// Pick a random unreachable node.
|
||||
int index = mrand(unreachable_nodes.size());
|
||||
node_set::const_iterator i = unreachable_nodes.begin();
|
||||
std::advance(i, index);
|
||||
// No connection to unreachable successors so far. This
|
||||
// is our last chance, so force it now.
|
||||
if (--nsucc == 0
|
||||
&& !unreachable_nodes.empty()
|
||||
&& !saw_unreachable)
|
||||
{
|
||||
// Pick a random unreachable node.
|
||||
int index = mrand(unreachable_nodes.size());
|
||||
node_set::const_iterator i = unreachable_nodes.begin();
|
||||
std::advance(i, index);
|
||||
|
||||
// Link it from src.
|
||||
res->new_edge(src, *i, l, m);
|
||||
nodes_to_process.insert(*i);
|
||||
unreachable_nodes.erase(*i);
|
||||
break;
|
||||
}
|
||||
// Link it from src.
|
||||
res->new_edge(src, *i, l, m);
|
||||
nodes_to_process.insert(*i);
|
||||
unreachable_nodes.erase(*i);
|
||||
break;
|
||||
}
|
||||
|
||||
// Pick the index of a random node.
|
||||
int index = mrand(possibilities--);
|
||||
|
|
@ -237,10 +237,10 @@ namespace spot
|
|||
unreachable_nodes.erase(j);
|
||||
saw_unreachable = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The node must have at least one successor.
|
||||
assert(res->get_graph().state_storage(src).succ);
|
||||
// The node must have at least one successor.
|
||||
assert(res->get_graph().state_storage(src).succ);
|
||||
}
|
||||
// All nodes must be reachable.
|
||||
assert(unreachable_nodes.empty());
|
||||
|
|
|
|||
|
|
@ -56,18 +56,18 @@ namespace spot
|
|||
///
|
||||
/// This algorithms is adapted from the one in Fig 6.2 page 48 of
|
||||
/** \verbatim
|
||||
@TechReport{ tauriainen.00.a66,
|
||||
author = {Heikki Tauriainen},
|
||||
@TechReport{ tauriainen.00.a66,
|
||||
author = {Heikki Tauriainen},
|
||||
title = {Automated Testing of {B\"u}chi Automata Translators for
|
||||
{L}inear {T}emporal {L}ogic},
|
||||
address = {Espoo, Finland},
|
||||
{L}inear {T}emporal {L}ogic},
|
||||
address = {Espoo, Finland},
|
||||
institution = {Helsinki University of Technology, Laboratory for
|
||||
Theoretical Computer Science},
|
||||
number = {A66},
|
||||
year = {2000},
|
||||
url = {http://citeseer.nj.nec.com/tauriainen00automated.html},
|
||||
type = {Research Report},
|
||||
note = {Reprint of Master's thesis}
|
||||
Theoretical Computer Science},
|
||||
number = {A66},
|
||||
year = {2000},
|
||||
url = {http://citeseer.nj.nec.com/tauriainen00automated.html},
|
||||
type = {Research Report},
|
||||
note = {Reprint of Master's thesis}
|
||||
}
|
||||
\endverbatim */
|
||||
///
|
||||
|
|
@ -84,10 +84,10 @@ namespace spot
|
|||
/// acceptance sets, this does not set the acceptance condition.
|
||||
SPOT_API twa_graph_ptr
|
||||
random_graph(int n, float d,
|
||||
const atomic_prop_set* ap, const bdd_dict_ptr& dict,
|
||||
unsigned n_accs = 0, float a = 0.1, float t = 0.5,
|
||||
bool deterministic = false, bool state_acc = false,
|
||||
bool colored = false);
|
||||
const atomic_prop_set* ap, const bdd_dict_ptr& dict,
|
||||
unsigned n_accs = 0, float a = 0.1, float t = 0.5,
|
||||
bool deterministic = false, bool state_acc = false,
|
||||
bool colored = false);
|
||||
|
||||
/// Build a random acceptance where each acceptance sets is used once.
|
||||
SPOT_API acc_cond::acc_code random_acceptance(unsigned n_accs);
|
||||
|
|
|
|||
|
|
@ -27,40 +27,40 @@ namespace spot
|
|||
{
|
||||
void
|
||||
randomize(twa_graph_ptr& aut, bool randomize_states,
|
||||
bool randomize_edges)
|
||||
bool randomize_edges)
|
||||
{
|
||||
if (!randomize_states && !randomize_edges)
|
||||
return;
|
||||
auto& g = aut->get_graph();
|
||||
if (randomize_states)
|
||||
{
|
||||
unsigned n = g.num_states();
|
||||
std::vector<unsigned> nums(n);
|
||||
std::iota(nums.begin(), nums.end(), 0);
|
||||
mrandom_shuffle(nums.begin(), nums.end());
|
||||
g.rename_states_(nums);
|
||||
aut->set_init_state(nums[aut->get_init_state_number()]);
|
||||
unsigned n = g.num_states();
|
||||
std::vector<unsigned> nums(n);
|
||||
std::iota(nums.begin(), nums.end(), 0);
|
||||
mrandom_shuffle(nums.begin(), nums.end());
|
||||
g.rename_states_(nums);
|
||||
aut->set_init_state(nums[aut->get_init_state_number()]);
|
||||
|
||||
if (auto sn =
|
||||
aut->get_named_prop<std::vector<std::string>>("state-names"))
|
||||
{
|
||||
unsigned sns = sn->size(); // Might be != n.
|
||||
auto nn = new std::vector<std::string>(n);
|
||||
for (unsigned i = 0; i < sns && i < n; ++i)
|
||||
(*nn)[nums[i]] = (*sn)[i];
|
||||
aut->set_named_prop("state-names", nn);
|
||||
}
|
||||
if (auto sn =
|
||||
aut->get_named_prop<std::vector<std::string>>("state-names"))
|
||||
{
|
||||
unsigned sns = sn->size(); // Might be != n.
|
||||
auto nn = new std::vector<std::string>(n);
|
||||
for (unsigned i = 0; i < sns && i < n; ++i)
|
||||
(*nn)[nums[i]] = (*sn)[i];
|
||||
aut->set_named_prop("state-names", nn);
|
||||
}
|
||||
}
|
||||
if (randomize_edges)
|
||||
{
|
||||
g.remove_dead_edges_();
|
||||
auto& v = g.edge_vector();
|
||||
mrandom_shuffle(v.begin() + 1, v.end());
|
||||
g.remove_dead_edges_();
|
||||
auto& v = g.edge_vector();
|
||||
mrandom_shuffle(v.begin() + 1, v.end());
|
||||
}
|
||||
|
||||
typedef twa_graph::graph_t::edge_storage_t tr_t;
|
||||
g.sort_edges_([](const tr_t& lhs, const tr_t& rhs)
|
||||
{ return lhs.src < rhs.src; });
|
||||
{ return lhs.src < rhs.src; });
|
||||
g.chain_edges_();
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -29,6 +29,6 @@ namespace spot
|
|||
/// leaving this state.
|
||||
SPOT_API void
|
||||
randomize(twa_graph_ptr& aut,
|
||||
bool randomize_states = true,
|
||||
bool randomize_edges = true);
|
||||
bool randomize_states = true,
|
||||
bool randomize_edges = true);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -38,10 +38,10 @@ namespace spot
|
|||
auto s = seen.begin();
|
||||
while (s != seen.end())
|
||||
{
|
||||
// Advance the iterator before deleting the "key" pointer.
|
||||
const state* ptr = s->first;
|
||||
++s;
|
||||
ptr->destroy();
|
||||
// Advance the iterator before deleting the "key" pointer.
|
||||
const state* ptr = s->first;
|
||||
++s;
|
||||
ptr->destroy();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -57,34 +57,34 @@ namespace spot
|
|||
const state* t;
|
||||
while ((t = next_state()))
|
||||
{
|
||||
assert(seen.find(t) != seen.end());
|
||||
int tn = seen[t];
|
||||
twa_succ_iterator* si = aut_->succ_iter(t);
|
||||
process_state(t, tn, si);
|
||||
if (si->first())
|
||||
do
|
||||
{
|
||||
const state* current = si->dst();
|
||||
auto s = seen.find(current);
|
||||
bool ws = want_state(current);
|
||||
if (s == seen.end())
|
||||
{
|
||||
seen[current] = ++n;
|
||||
if (ws)
|
||||
{
|
||||
add_state(current);
|
||||
process_link(t, tn, current, n, si);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ws)
|
||||
process_link(t, tn, s->first, s->second, si);
|
||||
current->destroy();
|
||||
}
|
||||
}
|
||||
while (si->next());
|
||||
aut_->release_iter(si);
|
||||
assert(seen.find(t) != seen.end());
|
||||
int tn = seen[t];
|
||||
twa_succ_iterator* si = aut_->succ_iter(t);
|
||||
process_state(t, tn, si);
|
||||
if (si->first())
|
||||
do
|
||||
{
|
||||
const state* current = si->dst();
|
||||
auto s = seen.find(current);
|
||||
bool ws = want_state(current);
|
||||
if (s == seen.end())
|
||||
{
|
||||
seen[current] = ++n;
|
||||
if (ws)
|
||||
{
|
||||
add_state(current);
|
||||
process_link(t, tn, current, n, si);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (ws)
|
||||
process_link(t, tn, s->first, s->second, si);
|
||||
current->destroy();
|
||||
}
|
||||
}
|
||||
while (si->next());
|
||||
aut_->release_iter(si);
|
||||
}
|
||||
end();
|
||||
}
|
||||
|
|
@ -107,14 +107,14 @@ namespace spot
|
|||
|
||||
void
|
||||
twa_reachable_iterator::process_state(const state*, int,
|
||||
twa_succ_iterator*)
|
||||
twa_succ_iterator*)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
twa_reachable_iterator::process_link(const state*, int,
|
||||
const state*, int,
|
||||
const twa_succ_iterator*)
|
||||
const state*, int,
|
||||
const twa_succ_iterator*)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -157,10 +157,10 @@ namespace spot
|
|||
auto s = seen.begin();
|
||||
while (s != seen.end())
|
||||
{
|
||||
// Advance the iterator before deleting the "key" pointer.
|
||||
const state* ptr = s->first;
|
||||
++s;
|
||||
ptr->destroy();
|
||||
// Advance the iterator before deleting the "key" pointer.
|
||||
const state* ptr = s->first;
|
||||
++s;
|
||||
ptr->destroy();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -195,46 +195,46 @@ namespace spot
|
|||
const state* dst;
|
||||
while (!todo.empty())
|
||||
{
|
||||
twa_succ_iterator* si = todo.back().it;
|
||||
if (si->done())
|
||||
{
|
||||
pop();
|
||||
continue;
|
||||
}
|
||||
twa_succ_iterator* si = todo.back().it;
|
||||
if (si->done())
|
||||
{
|
||||
pop();
|
||||
continue;
|
||||
}
|
||||
|
||||
dst = si->dst();
|
||||
auto res = seen.emplace(dst, n);
|
||||
if (!res.second)
|
||||
{
|
||||
// The state has already been seen.
|
||||
dst->destroy();
|
||||
// 0-numbered states are not wanted.
|
||||
if (res.first->second == 0)
|
||||
{
|
||||
si->next();
|
||||
continue;
|
||||
}
|
||||
dst = res.first->first;
|
||||
}
|
||||
else if (!want_state(dst))
|
||||
{
|
||||
// Mark this state as non-wanted in case we see it again.
|
||||
res.first->second = 0;
|
||||
si->next();
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
++n;
|
||||
}
|
||||
dst = si->dst();
|
||||
auto res = seen.emplace(dst, n);
|
||||
if (!res.second)
|
||||
{
|
||||
// The state has already been seen.
|
||||
dst->destroy();
|
||||
// 0-numbered states are not wanted.
|
||||
if (res.first->second == 0)
|
||||
{
|
||||
si->next();
|
||||
continue;
|
||||
}
|
||||
dst = res.first->first;
|
||||
}
|
||||
else if (!want_state(dst))
|
||||
{
|
||||
// Mark this state as non-wanted in case we see it again.
|
||||
res.first->second = 0;
|
||||
si->next();
|
||||
continue;
|
||||
}
|
||||
else
|
||||
{
|
||||
++n;
|
||||
}
|
||||
|
||||
int dst_n = res.first->second;
|
||||
process_link(todo.back().src, todo.back().src_n, dst, dst_n, si);
|
||||
int dst_n = res.first->second;
|
||||
process_link(todo.back().src, todo.back().src_n, dst, dst_n, si);
|
||||
|
||||
if (res.second)
|
||||
push(dst, dst_n);
|
||||
else
|
||||
si->next();
|
||||
if (res.second)
|
||||
push(dst, dst_n);
|
||||
else
|
||||
si->next();
|
||||
}
|
||||
end();
|
||||
}
|
||||
|
|
@ -257,14 +257,14 @@ namespace spot
|
|||
|
||||
void
|
||||
twa_reachable_iterator_depth_first::process_state(const state*, int,
|
||||
twa_succ_iterator*)
|
||||
twa_succ_iterator*)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
twa_reachable_iterator_depth_first::process_link(const state*, int,
|
||||
const state*, int,
|
||||
const twa_succ_iterator*)
|
||||
const state*, int,
|
||||
const twa_succ_iterator*)
|
||||
{
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -84,13 +84,13 @@ namespace spot
|
|||
/// spot::twa_reachable_iterator instance and destroyed when the
|
||||
/// instance is destroyed.
|
||||
virtual void process_link(const state* in_s, int in,
|
||||
const state* out_s, int out,
|
||||
const twa_succ_iterator* si);
|
||||
const state* out_s, int out,
|
||||
const twa_succ_iterator* si);
|
||||
|
||||
protected:
|
||||
const_twa_ptr aut_; ///< The spot::tgba to explore.
|
||||
const_twa_ptr aut_; ///< The spot::tgba to explore.
|
||||
|
||||
state_map<int> seen; ///< States already seen.
|
||||
state_map<int> seen; ///< States already seen.
|
||||
};
|
||||
|
||||
/// \ingroup twa_generic
|
||||
|
|
@ -152,13 +152,13 @@ namespace spot
|
|||
/// spot::twa_reachable_iterator instance and destroyed when the
|
||||
/// instance is destroyed.
|
||||
virtual void process_link(const state* in_s, int in,
|
||||
const state* out_s, int out,
|
||||
const twa_succ_iterator* si);
|
||||
const state* out_s, int out,
|
||||
const twa_succ_iterator* si);
|
||||
|
||||
protected:
|
||||
const_twa_ptr aut_; ///< The spot::tgba to explore.
|
||||
const_twa_ptr aut_; ///< The spot::tgba to explore.
|
||||
|
||||
state_map<int> seen; ///< States already seen.
|
||||
state_map<int> seen; ///< States already seen.
|
||||
struct stack_item
|
||||
{
|
||||
const state* src;
|
||||
|
|
|
|||
|
|
@ -30,10 +30,10 @@ namespace spot
|
|||
vars.reserve(relmap->size());
|
||||
for (auto& p: *relmap)
|
||||
{
|
||||
int oldv = aut->register_ap(p.first);
|
||||
int newv = aut->register_ap(p.second);
|
||||
bdd_setpair(pairs, oldv, newv);
|
||||
vars.push_back(oldv);
|
||||
int oldv = aut->register_ap(p.first);
|
||||
int newv = aut->register_ap(p.second);
|
||||
bdd_setpair(pairs, oldv, newv);
|
||||
vars.push_back(oldv);
|
||||
}
|
||||
for (auto& t: aut->edges())
|
||||
t.cond = bdd_replace(t.cond, pairs);
|
||||
|
|
|
|||
|
|
@ -27,5 +27,5 @@ namespace spot
|
|||
/// replace atomic propositions in an automaton
|
||||
SPOT_API void
|
||||
relabel_here(twa_graph_ptr& aut,
|
||||
relabeling_map* relmap);
|
||||
relabeling_map* relmap);
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -33,9 +33,9 @@ namespace spot
|
|||
std::ostringstream out;
|
||||
out << "unexpected ";
|
||||
if (isprint(*pos))
|
||||
out << '\'' << *pos << '\'';
|
||||
out << '\'' << *pos << '\'';
|
||||
else
|
||||
out << "character";
|
||||
out << "character";
|
||||
out << " at position " << pos - arg << " in '";
|
||||
out << arg << '\'';
|
||||
throw std::invalid_argument(out.str());
|
||||
|
|
@ -48,77 +48,77 @@ namespace spot
|
|||
auto start = arg;
|
||||
while (*start)
|
||||
{
|
||||
while (*start == ' ' || *start == '\t')
|
||||
++start;
|
||||
if (!*start)
|
||||
break;
|
||||
if (*start == ',' || *start == '=')
|
||||
unexpected_char(arg, start);
|
||||
formula the_ap = nullptr;
|
||||
while (*start == ' ' || *start == '\t')
|
||||
++start;
|
||||
if (!*start)
|
||||
break;
|
||||
if (*start == ',' || *start == '=')
|
||||
unexpected_char(arg, start);
|
||||
formula the_ap = nullptr;
|
||||
|
||||
if (*start == '"')
|
||||
{
|
||||
auto end = ++start;
|
||||
while (*end && *end != '"')
|
||||
{
|
||||
if (*end == '\\')
|
||||
++end;
|
||||
++end;
|
||||
}
|
||||
if (!*end)
|
||||
{
|
||||
std::string s = "missing closing '\"' in ";
|
||||
s += arg;
|
||||
throw std::invalid_argument(s);
|
||||
}
|
||||
std::string ap(start, end - start);
|
||||
the_ap = formula::ap(ap);
|
||||
do
|
||||
++end;
|
||||
while (*end == ' ' || *end == '\t');
|
||||
start = end;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto end = start;
|
||||
while (*end && *end != ',' && *end != '=')
|
||||
++end;
|
||||
auto rend = end;
|
||||
while (rend > start && (rend[-1] == ' ' || rend[-1] == '\t'))
|
||||
--rend;
|
||||
std::string ap(start, rend - start);
|
||||
the_ap = formula::ap(ap);
|
||||
start = end;
|
||||
}
|
||||
if (*start)
|
||||
{
|
||||
if (!(*start == ',' || *start == '='))
|
||||
unexpected_char(arg, start);
|
||||
if (*start == '=')
|
||||
{
|
||||
do
|
||||
++start;
|
||||
while (*start == ' ' || *start == '\t');
|
||||
if (*start == '0')
|
||||
props_neg.insert(the_ap);
|
||||
else if (*start == '1')
|
||||
props_pos.insert(the_ap);
|
||||
else
|
||||
unexpected_char(arg, start);
|
||||
the_ap = nullptr;
|
||||
do
|
||||
++start;
|
||||
while (*start == ' ' || *start == '\t');
|
||||
}
|
||||
if (*start)
|
||||
{
|
||||
if (*start != ',')
|
||||
unexpected_char(arg, start);
|
||||
++start;
|
||||
}
|
||||
}
|
||||
if (the_ap)
|
||||
props_exist.insert(the_ap);
|
||||
if (*start == '"')
|
||||
{
|
||||
auto end = ++start;
|
||||
while (*end && *end != '"')
|
||||
{
|
||||
if (*end == '\\')
|
||||
++end;
|
||||
++end;
|
||||
}
|
||||
if (!*end)
|
||||
{
|
||||
std::string s = "missing closing '\"' in ";
|
||||
s += arg;
|
||||
throw std::invalid_argument(s);
|
||||
}
|
||||
std::string ap(start, end - start);
|
||||
the_ap = formula::ap(ap);
|
||||
do
|
||||
++end;
|
||||
while (*end == ' ' || *end == '\t');
|
||||
start = end;
|
||||
}
|
||||
else
|
||||
{
|
||||
auto end = start;
|
||||
while (*end && *end != ',' && *end != '=')
|
||||
++end;
|
||||
auto rend = end;
|
||||
while (rend > start && (rend[-1] == ' ' || rend[-1] == '\t'))
|
||||
--rend;
|
||||
std::string ap(start, rend - start);
|
||||
the_ap = formula::ap(ap);
|
||||
start = end;
|
||||
}
|
||||
if (*start)
|
||||
{
|
||||
if (!(*start == ',' || *start == '='))
|
||||
unexpected_char(arg, start);
|
||||
if (*start == '=')
|
||||
{
|
||||
do
|
||||
++start;
|
||||
while (*start == ' ' || *start == '\t');
|
||||
if (*start == '0')
|
||||
props_neg.insert(the_ap);
|
||||
else if (*start == '1')
|
||||
props_pos.insert(the_ap);
|
||||
else
|
||||
unexpected_char(arg, start);
|
||||
the_ap = nullptr;
|
||||
do
|
||||
++start;
|
||||
while (*start == ' ' || *start == '\t');
|
||||
}
|
||||
if (*start)
|
||||
{
|
||||
if (*start != ',')
|
||||
unexpected_char(arg, start);
|
||||
++start;
|
||||
}
|
||||
}
|
||||
if (the_ap)
|
||||
props_exist.insert(the_ap);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -135,37 +135,37 @@ namespace spot
|
|||
|
||||
for (auto ap: props_exist)
|
||||
{
|
||||
int v = d->has_registered_proposition(ap, aut);
|
||||
if (v >= 0)
|
||||
{
|
||||
exist &= bdd_ithvar(v);
|
||||
d->unregister_variable(v, res);
|
||||
}
|
||||
int v = d->has_registered_proposition(ap, aut);
|
||||
if (v >= 0)
|
||||
{
|
||||
exist &= bdd_ithvar(v);
|
||||
d->unregister_variable(v, res);
|
||||
}
|
||||
}
|
||||
for (auto ap: props_pos)
|
||||
{
|
||||
int v = d->has_registered_proposition(ap, aut);
|
||||
if (v >= 0)
|
||||
{
|
||||
restrict &= bdd_ithvar(v);
|
||||
d->unregister_variable(v, res);
|
||||
}
|
||||
int v = d->has_registered_proposition(ap, aut);
|
||||
if (v >= 0)
|
||||
{
|
||||
restrict &= bdd_ithvar(v);
|
||||
d->unregister_variable(v, res);
|
||||
}
|
||||
}
|
||||
for (auto ap: props_neg)
|
||||
{
|
||||
int v = d->has_registered_proposition(ap, aut);
|
||||
if (v >= 0)
|
||||
{
|
||||
restrict &= bdd_nithvar(v);
|
||||
d->unregister_variable(v, res);
|
||||
}
|
||||
int v = d->has_registered_proposition(ap, aut);
|
||||
if (v >= 0)
|
||||
{
|
||||
restrict &= bdd_nithvar(v);
|
||||
d->unregister_variable(v, res);
|
||||
}
|
||||
}
|
||||
|
||||
transform_accessible(aut, res, [&](unsigned, bdd& cond,
|
||||
acc_cond::mark_t&, unsigned)
|
||||
acc_cond::mark_t&, unsigned)
|
||||
{
|
||||
cond = bdd_restrict(bdd_exist(cond, exist),
|
||||
restrict);
|
||||
cond = bdd_restrict(bdd_exist(cond, exist),
|
||||
restrict);
|
||||
});
|
||||
return res;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -43,15 +43,15 @@ namespace spot
|
|||
auto new_state =
|
||||
[&](unsigned state, acc_cond::mark_t m) -> unsigned
|
||||
{
|
||||
pair_t x(state, m);
|
||||
auto p = s2n.emplace(x, 0);
|
||||
if (p.second) // This is a new state
|
||||
{
|
||||
unsigned s = res->new_state();
|
||||
p.first->second = s;
|
||||
todo.emplace_back(x, s);
|
||||
}
|
||||
return p.first->second;
|
||||
pair_t x(state, m);
|
||||
auto p = s2n.emplace(x, 0);
|
||||
if (p.second) // This is a new state
|
||||
{
|
||||
unsigned s = res->new_state();
|
||||
p.first->second = s;
|
||||
todo.emplace_back(x, s);
|
||||
}
|
||||
return p.first->second;
|
||||
};
|
||||
|
||||
// Find any edge going into the initial state, and use its
|
||||
|
|
@ -60,21 +60,21 @@ namespace spot
|
|||
unsigned old_init = old->get_init_state_number();
|
||||
for (auto& t: old->edges())
|
||||
if (t.dst == old_init)
|
||||
{
|
||||
init_acc = t.acc;
|
||||
break;
|
||||
}
|
||||
{
|
||||
init_acc = t.acc;
|
||||
break;
|
||||
}
|
||||
|
||||
res->set_init_state(new_state(old_init, init_acc));
|
||||
while (!todo.empty())
|
||||
{
|
||||
auto one = todo.back();
|
||||
todo.pop_back();
|
||||
for (auto& t: old->out(one.first.first))
|
||||
res->new_edge(one.second,
|
||||
new_state(t.dst, t.acc),
|
||||
t.cond,
|
||||
one.first.second);
|
||||
auto one = todo.back();
|
||||
todo.pop_back();
|
||||
for (auto& t: old->out(one.first.first))
|
||||
res->new_edge(one.second,
|
||||
new_state(t.dst, t.acc),
|
||||
t.cond,
|
||||
one.first.second);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -42,25 +42,25 @@ namespace spot
|
|||
{
|
||||
scc_info* si;
|
||||
id_filter(scc_info* si)
|
||||
: si(si)
|
||||
: si(si)
|
||||
{
|
||||
}
|
||||
|
||||
// Accept all states
|
||||
bool state(unsigned)
|
||||
{
|
||||
return true;
|
||||
return true;
|
||||
}
|
||||
|
||||
void fix_acceptance(const twa_graph_ptr& out)
|
||||
{
|
||||
out->copy_acceptance_of(this->si->get_aut());
|
||||
out->copy_acceptance_of(this->si->get_aut());
|
||||
}
|
||||
|
||||
// Accept all edges, unmodified
|
||||
filtered_trans trans(unsigned, unsigned, bdd cond, acc_cond::mark_t acc)
|
||||
{
|
||||
return filtered_trans{true, cond, acc};
|
||||
return filtered_trans{true, cond, acc};
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -70,13 +70,13 @@ namespace spot
|
|||
{
|
||||
template<typename... Args>
|
||||
state_filter(scc_info* si, Args&&... args)
|
||||
: next_filter(si, std::forward<Args>(args)...)
|
||||
: next_filter(si, std::forward<Args>(args)...)
|
||||
{
|
||||
}
|
||||
|
||||
bool state(unsigned s)
|
||||
{
|
||||
return this->next_filter::state(s) && this->si->is_useful_state(s);
|
||||
return this->next_filter::state(s) && this->si->is_useful_state(s);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -90,37 +90,37 @@ namespace spot
|
|||
|
||||
template<typename... Args>
|
||||
susp_filter(scc_info* si,
|
||||
bdd suspvars, bdd ignoredvars, bool early_susp,
|
||||
Args&&... args)
|
||||
: next_filter(si, std::forward<Args>(args)...),
|
||||
suspvars(suspvars),
|
||||
ignoredvars(ignoredvars),
|
||||
early_susp(early_susp)
|
||||
bdd suspvars, bdd ignoredvars, bool early_susp,
|
||||
Args&&... args)
|
||||
: next_filter(si, std::forward<Args>(args)...),
|
||||
suspvars(suspvars),
|
||||
ignoredvars(ignoredvars),
|
||||
early_susp(early_susp)
|
||||
{
|
||||
}
|
||||
|
||||
filtered_trans trans(unsigned src, unsigned dst,
|
||||
bdd cond, acc_cond::mark_t acc)
|
||||
bdd cond, acc_cond::mark_t acc)
|
||||
{
|
||||
bool keep;
|
||||
std::tie(keep, cond, acc) =
|
||||
this->next_filter::trans(src, dst, cond, acc);
|
||||
bool keep;
|
||||
std::tie(keep, cond, acc) =
|
||||
this->next_filter::trans(src, dst, cond, acc);
|
||||
|
||||
if (keep)
|
||||
{
|
||||
// Always remove ignored variables
|
||||
cond = bdd_exist(cond, ignoredvars);
|
||||
if (keep)
|
||||
{
|
||||
// Always remove ignored variables
|
||||
cond = bdd_exist(cond, ignoredvars);
|
||||
|
||||
// Remove the suspension variables only if
|
||||
// the destination in a rejecting SCC,
|
||||
// or if we are between SCC with early_susp unset.
|
||||
unsigned u = this->si->scc_of(dst);
|
||||
if (this->si->is_rejecting_scc(u)
|
||||
|| (!early_susp && (u != this->si->scc_of(src))))
|
||||
cond = bdd_exist(cond, suspvars);
|
||||
}
|
||||
// Remove the suspension variables only if
|
||||
// the destination in a rejecting SCC,
|
||||
// or if we are between SCC with early_susp unset.
|
||||
unsigned u = this->si->scc_of(dst);
|
||||
if (this->si->is_rejecting_scc(u)
|
||||
|| (!early_susp && (u != this->si->scc_of(src))))
|
||||
cond = bdd_exist(cond, suspvars);
|
||||
}
|
||||
|
||||
return filtered_trans(keep, cond, acc);
|
||||
return filtered_trans(keep, cond, acc);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -136,68 +136,68 @@ namespace spot
|
|||
|
||||
template<typename... Args>
|
||||
acc_filter_mask(scc_info* si, Args&&... args)
|
||||
: next_filter(si, std::forward<Args>(args)...)
|
||||
: next_filter(si, std::forward<Args>(args)...)
|
||||
{
|
||||
acc_cond::mark_t fin;
|
||||
acc_cond::mark_t inf;
|
||||
std::tie(inf, fin) =
|
||||
si->get_aut()->acc().get_acceptance().used_inf_fin_sets();
|
||||
// If an SCC is rejecting, we can mask all the sets that are
|
||||
// used only as Inf in the acceptance.
|
||||
accmask = ~(inf - fin);
|
||||
acc_cond::mark_t fin;
|
||||
acc_cond::mark_t inf;
|
||||
std::tie(inf, fin) =
|
||||
si->get_aut()->acc().get_acceptance().used_inf_fin_sets();
|
||||
// If an SCC is rejecting, we can mask all the sets that are
|
||||
// used only as Inf in the acceptance.
|
||||
accmask = ~(inf - fin);
|
||||
}
|
||||
|
||||
filtered_trans trans(unsigned src, unsigned dst,
|
||||
bdd cond, acc_cond::mark_t acc)
|
||||
bdd cond, acc_cond::mark_t acc)
|
||||
{
|
||||
bool keep;
|
||||
std::tie(keep, cond, acc) =
|
||||
this->next_filter::trans(src, dst, cond, acc);
|
||||
bool keep;
|
||||
std::tie(keep, cond, acc) =
|
||||
this->next_filter::trans(src, dst, cond, acc);
|
||||
|
||||
if (keep)
|
||||
{
|
||||
unsigned u = this->si->scc_of(src);
|
||||
unsigned v = this->si->scc_of(dst);
|
||||
// The basic rules are as follows:
|
||||
//
|
||||
// - If an edge is between two SCCs, is OK to remove
|
||||
// all acceptance sets, as this edge cannot be part
|
||||
// of any loop.
|
||||
// - If an edge is in an non-accepting SCC, we can only
|
||||
// remove the Inf sets, as removinf the Fin sets
|
||||
// might make the SCC accepting.
|
||||
//
|
||||
// The above rules are made more complex with two flags:
|
||||
//
|
||||
// - If PreserveSBA is set, we have to tree a transition
|
||||
// leaving an SCC as other transitions inside the SCC,
|
||||
// otherwise we will break the property that all
|
||||
// transitions leaving the same state have identical set
|
||||
// membership.
|
||||
// - If RemoveAll is false, we like to keep the membership
|
||||
// of transitions entering an SCC. This can only be
|
||||
// done if PreserveSBA is unset, unfortunately.
|
||||
if (u == v)
|
||||
{
|
||||
if (this->si->is_rejecting_scc(u))
|
||||
acc &= accmask;
|
||||
}
|
||||
else if (PreserveSBA && this->si->is_rejecting_scc(u))
|
||||
{
|
||||
if (!this->si->is_trivial(u))
|
||||
acc &= accmask; // No choice.
|
||||
else if (RemoveAll)
|
||||
acc = 0U;
|
||||
}
|
||||
else if (!PreserveSBA)
|
||||
{
|
||||
if (RemoveAll)
|
||||
acc = 0U;
|
||||
else if (this->si->is_rejecting_scc(v))
|
||||
acc &= accmask;
|
||||
}
|
||||
}
|
||||
return filtered_trans(keep, cond, acc);
|
||||
if (keep)
|
||||
{
|
||||
unsigned u = this->si->scc_of(src);
|
||||
unsigned v = this->si->scc_of(dst);
|
||||
// The basic rules are as follows:
|
||||
//
|
||||
// - If an edge is between two SCCs, is OK to remove
|
||||
// all acceptance sets, as this edge cannot be part
|
||||
// of any loop.
|
||||
// - If an edge is in an non-accepting SCC, we can only
|
||||
// remove the Inf sets, as removinf the Fin sets
|
||||
// might make the SCC accepting.
|
||||
//
|
||||
// The above rules are made more complex with two flags:
|
||||
//
|
||||
// - If PreserveSBA is set, we have to tree a transition
|
||||
// leaving an SCC as other transitions inside the SCC,
|
||||
// otherwise we will break the property that all
|
||||
// transitions leaving the same state have identical set
|
||||
// membership.
|
||||
// - If RemoveAll is false, we like to keep the membership
|
||||
// of transitions entering an SCC. This can only be
|
||||
// done if PreserveSBA is unset, unfortunately.
|
||||
if (u == v)
|
||||
{
|
||||
if (this->si->is_rejecting_scc(u))
|
||||
acc &= accmask;
|
||||
}
|
||||
else if (PreserveSBA && this->si->is_rejecting_scc(u))
|
||||
{
|
||||
if (!this->si->is_trivial(u))
|
||||
acc &= accmask; // No choice.
|
||||
else if (RemoveAll)
|
||||
acc = 0U;
|
||||
}
|
||||
else if (!PreserveSBA)
|
||||
{
|
||||
if (RemoveAll)
|
||||
acc = 0U;
|
||||
else if (this->si->is_rejecting_scc(v))
|
||||
acc &= accmask;
|
||||
}
|
||||
}
|
||||
return filtered_trans(keep, cond, acc);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -210,75 +210,75 @@ namespace spot
|
|||
|
||||
template<typename... Args>
|
||||
acc_filter_simplify(scc_info* si, Args&&... args)
|
||||
: next_filter(si, std::forward<Args>(args)...)
|
||||
: next_filter(si, std::forward<Args>(args)...)
|
||||
{
|
||||
}
|
||||
|
||||
void fix_acceptance(const twa_graph_ptr& out)
|
||||
{
|
||||
auto& acc = this->si->get_aut()->acc();
|
||||
if (!acc.is_generalized_buchi())
|
||||
throw std::runtime_error
|
||||
("simplification of SCC acceptance works only with "
|
||||
"generalized Büchi acceptance");
|
||||
auto& acc = this->si->get_aut()->acc();
|
||||
if (!acc.is_generalized_buchi())
|
||||
throw std::runtime_error
|
||||
("simplification of SCC acceptance works only with "
|
||||
"generalized Büchi acceptance");
|
||||
|
||||
unsigned scc_count = this->si->scc_count();
|
||||
auto used_acc = this->si->used_acc();
|
||||
assert(used_acc.size() == scc_count);
|
||||
strip_.resize(scc_count);
|
||||
std::vector<unsigned> cnt(scc_count); // # of useful sets in each SCC
|
||||
unsigned max = 0; // Max number of useful sets
|
||||
for (unsigned n = 0; n < scc_count; ++n)
|
||||
{
|
||||
if (this->si->is_rejecting_scc(n))
|
||||
continue;
|
||||
strip_[n] = acc.useless(used_acc[n].begin(), used_acc[n].end());
|
||||
cnt[n] = acc.num_sets() - strip_[n].count();
|
||||
if (cnt[n] > max)
|
||||
max = cnt[n];
|
||||
}
|
||||
// Now that we know about the max number of acceptance
|
||||
// conditions, add extra acceptance conditions to those SCC
|
||||
// that do not have enough.
|
||||
for (unsigned n = 0; n < scc_count; ++n)
|
||||
{
|
||||
if (this->si->is_rejecting_scc(n))
|
||||
continue;
|
||||
if (cnt[n] < max)
|
||||
strip_[n].remove_some(max - cnt[n]);
|
||||
}
|
||||
unsigned scc_count = this->si->scc_count();
|
||||
auto used_acc = this->si->used_acc();
|
||||
assert(used_acc.size() == scc_count);
|
||||
strip_.resize(scc_count);
|
||||
std::vector<unsigned> cnt(scc_count); // # of useful sets in each SCC
|
||||
unsigned max = 0; // Max number of useful sets
|
||||
for (unsigned n = 0; n < scc_count; ++n)
|
||||
{
|
||||
if (this->si->is_rejecting_scc(n))
|
||||
continue;
|
||||
strip_[n] = acc.useless(used_acc[n].begin(), used_acc[n].end());
|
||||
cnt[n] = acc.num_sets() - strip_[n].count();
|
||||
if (cnt[n] > max)
|
||||
max = cnt[n];
|
||||
}
|
||||
// Now that we know about the max number of acceptance
|
||||
// conditions, add extra acceptance conditions to those SCC
|
||||
// that do not have enough.
|
||||
for (unsigned n = 0; n < scc_count; ++n)
|
||||
{
|
||||
if (this->si->is_rejecting_scc(n))
|
||||
continue;
|
||||
if (cnt[n] < max)
|
||||
strip_[n].remove_some(max - cnt[n]);
|
||||
}
|
||||
|
||||
out->set_generalized_buchi(max);
|
||||
out->set_generalized_buchi(max);
|
||||
}
|
||||
|
||||
filtered_trans trans(unsigned src, unsigned dst, bdd cond,
|
||||
acc_cond::mark_t acc)
|
||||
acc_cond::mark_t acc)
|
||||
{
|
||||
bool keep;
|
||||
std::tie(keep, cond, acc) =
|
||||
this->next_filter::trans(src, dst, cond, acc);
|
||||
bool keep;
|
||||
std::tie(keep, cond, acc) =
|
||||
this->next_filter::trans(src, dst, cond, acc);
|
||||
|
||||
if (keep && acc)
|
||||
{
|
||||
unsigned u = this->si->scc_of(dst);
|
||||
if (keep && acc)
|
||||
{
|
||||
unsigned u = this->si->scc_of(dst);
|
||||
|
||||
if (this->si->is_rejecting_scc(u))
|
||||
acc = 0U;
|
||||
else
|
||||
acc = acc.strip(strip_[u]);
|
||||
}
|
||||
return filtered_trans{keep, cond, acc};
|
||||
if (this->si->is_rejecting_scc(u))
|
||||
acc = 0U;
|
||||
else
|
||||
acc = acc.strip(strip_[u]);
|
||||
}
|
||||
return filtered_trans{keep, cond, acc};
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
template<class F, typename... Args>
|
||||
twa_graph_ptr scc_filter_apply(const_twa_graph_ptr aut,
|
||||
scc_info* given_si, Args&&... args)
|
||||
scc_info* given_si, Args&&... args)
|
||||
{
|
||||
unsigned in_n = aut->num_states();
|
||||
if (in_n == 0) // nothing to filter.
|
||||
return make_twa_graph(aut, twa::prop_set::all());
|
||||
if (in_n == 0) // nothing to filter.
|
||||
return make_twa_graph(aut, twa::prop_set::all());
|
||||
|
||||
twa_graph_ptr filtered = make_twa_graph(aut->get_dict());
|
||||
filtered->copy_ap_of(aut);
|
||||
|
|
@ -286,44 +286,44 @@ namespace spot
|
|||
// Compute scc_info if not supplied.
|
||||
scc_info* si = given_si;
|
||||
if (!si)
|
||||
si = new scc_info(aut);
|
||||
si = new scc_info(aut);
|
||||
si->determine_unknown_acceptance();
|
||||
|
||||
F filter(si, std::forward<Args>(args)...);
|
||||
|
||||
// Renumber all useful states.
|
||||
unsigned out_n = 0; // Number of output states.
|
||||
unsigned out_n = 0; // Number of output states.
|
||||
std::vector<unsigned> inout; // Associate old states to new ones.
|
||||
inout.reserve(in_n);
|
||||
for (unsigned i = 0; i < in_n; ++i)
|
||||
if (filter.state(i))
|
||||
inout.push_back(out_n++);
|
||||
else
|
||||
inout.push_back(-1U);
|
||||
if (filter.state(i))
|
||||
inout.push_back(out_n++);
|
||||
else
|
||||
inout.push_back(-1U);
|
||||
|
||||
filter.fix_acceptance(filtered);
|
||||
filtered->new_states(out_n);
|
||||
for (unsigned isrc = 0; isrc < in_n; ++isrc)
|
||||
{
|
||||
unsigned osrc = inout[isrc];
|
||||
if (osrc >= out_n)
|
||||
continue;
|
||||
for (auto& t: aut->out(isrc))
|
||||
{
|
||||
unsigned odst = inout[t.dst];
|
||||
if (odst >= out_n)
|
||||
continue;
|
||||
bool want;
|
||||
bdd cond;
|
||||
acc_cond::mark_t acc;
|
||||
std::tie(want, cond, acc) =
|
||||
filter.trans(isrc, t.dst, t.cond, t.acc);
|
||||
if (want)
|
||||
filtered->new_edge(osrc, odst, cond, acc);
|
||||
}
|
||||
}
|
||||
{
|
||||
unsigned osrc = inout[isrc];
|
||||
if (osrc >= out_n)
|
||||
continue;
|
||||
for (auto& t: aut->out(isrc))
|
||||
{
|
||||
unsigned odst = inout[t.dst];
|
||||
if (odst >= out_n)
|
||||
continue;
|
||||
bool want;
|
||||
bdd cond;
|
||||
acc_cond::mark_t acc;
|
||||
std::tie(want, cond, acc) =
|
||||
filter.trans(isrc, t.dst, t.cond, t.acc);
|
||||
if (want)
|
||||
filtered->new_edge(osrc, odst, cond, acc);
|
||||
}
|
||||
}
|
||||
if (!given_si)
|
||||
delete si;
|
||||
delete si;
|
||||
// If the initial state has been filtered out, we have to create
|
||||
// a new one (not doing so may cause empty automata, which in turn
|
||||
// cause all sort of issue with algorithms assuming an automaton
|
||||
|
|
@ -338,92 +338,92 @@ namespace spot
|
|||
|
||||
twa_graph_ptr
|
||||
scc_filter_states(const const_twa_graph_ptr& aut, bool remove_all_useless,
|
||||
scc_info* given_si)
|
||||
scc_info* given_si)
|
||||
{
|
||||
twa_graph_ptr res;
|
||||
if (remove_all_useless)
|
||||
res = scc_filter_apply<state_filter
|
||||
<acc_filter_mask<true, true>>>(aut, given_si);
|
||||
<acc_filter_mask<true, true>>>(aut, given_si);
|
||||
else
|
||||
res = scc_filter_apply<state_filter
|
||||
<acc_filter_mask<false, true>>>(aut, given_si);
|
||||
<acc_filter_mask<false, true>>>(aut, given_si);
|
||||
res->prop_copy(aut, { true, true, true, true });
|
||||
return res;
|
||||
}
|
||||
|
||||
twa_graph_ptr
|
||||
scc_filter(const const_twa_graph_ptr& aut, bool remove_all_useless,
|
||||
scc_info* given_si)
|
||||
scc_info* given_si)
|
||||
{
|
||||
twa_graph_ptr res;
|
||||
// acc_filter_simplify only works for generalized Büchi
|
||||
if (aut->acc().is_generalized_buchi())
|
||||
{
|
||||
if (remove_all_useless)
|
||||
res =
|
||||
scc_filter_apply<state_filter
|
||||
<acc_filter_mask
|
||||
<true, false,
|
||||
acc_filter_simplify<>>>>(aut, given_si);
|
||||
else
|
||||
res =
|
||||
scc_filter_apply<state_filter
|
||||
<acc_filter_mask
|
||||
<false, false,
|
||||
acc_filter_simplify<>>>>(aut, given_si);
|
||||
if (remove_all_useless)
|
||||
res =
|
||||
scc_filter_apply<state_filter
|
||||
<acc_filter_mask
|
||||
<true, false,
|
||||
acc_filter_simplify<>>>>(aut, given_si);
|
||||
else
|
||||
res =
|
||||
scc_filter_apply<state_filter
|
||||
<acc_filter_mask
|
||||
<false, false,
|
||||
acc_filter_simplify<>>>>(aut, given_si);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (remove_all_useless)
|
||||
res = scc_filter_apply<state_filter
|
||||
<acc_filter_mask
|
||||
<true, false>>>(aut, given_si);
|
||||
else
|
||||
res = scc_filter_apply<state_filter
|
||||
<acc_filter_mask
|
||||
<false, false>>>(aut, given_si);
|
||||
if (remove_all_useless)
|
||||
res = scc_filter_apply<state_filter
|
||||
<acc_filter_mask
|
||||
<true, false>>>(aut, given_si);
|
||||
else
|
||||
res = scc_filter_apply<state_filter
|
||||
<acc_filter_mask
|
||||
<false, false>>>(aut, given_si);
|
||||
}
|
||||
res->merge_edges();
|
||||
res->prop_copy(aut,
|
||||
{ false, // state-based acceptance is not preserved
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
});
|
||||
{ false, // state-based acceptance is not preserved
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
});
|
||||
return res;
|
||||
}
|
||||
|
||||
twa_graph_ptr
|
||||
scc_filter_susp(const const_twa_graph_ptr& aut, bool remove_all_useless,
|
||||
bdd suspvars, bdd ignoredvars, bool early_susp,
|
||||
scc_info* given_si)
|
||||
bdd suspvars, bdd ignoredvars, bool early_susp,
|
||||
scc_info* given_si)
|
||||
{
|
||||
twa_graph_ptr res;
|
||||
if (remove_all_useless)
|
||||
res = scc_filter_apply<susp_filter
|
||||
<state_filter
|
||||
<acc_filter_mask
|
||||
<true, false,
|
||||
acc_filter_simplify<>>>>>(aut, given_si,
|
||||
suspvars,
|
||||
ignoredvars,
|
||||
early_susp);
|
||||
<state_filter
|
||||
<acc_filter_mask
|
||||
<true, false,
|
||||
acc_filter_simplify<>>>>>(aut, given_si,
|
||||
suspvars,
|
||||
ignoredvars,
|
||||
early_susp);
|
||||
else
|
||||
res = scc_filter_apply<susp_filter
|
||||
<state_filter
|
||||
<acc_filter_mask
|
||||
<false, false,
|
||||
acc_filter_simplify<>>>>>(aut, given_si,
|
||||
suspvars,
|
||||
ignoredvars,
|
||||
early_susp);
|
||||
<state_filter
|
||||
<acc_filter_mask
|
||||
<false, false,
|
||||
acc_filter_simplify<>>>>>(aut, given_si,
|
||||
suspvars,
|
||||
ignoredvars,
|
||||
early_susp);
|
||||
res->merge_edges();
|
||||
res->prop_copy(aut,
|
||||
{ false, // state-based acceptance is not preserved
|
||||
true,
|
||||
false, // determinism may not be preserved
|
||||
false, // stutter inv. of suspvars probably altered
|
||||
});
|
||||
{ false, // state-based acceptance is not preserved
|
||||
true,
|
||||
false, // determinism may not be preserved
|
||||
false, // stutter inv. of suspvars probably altered
|
||||
});
|
||||
return res;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ namespace spot
|
|||
/// instead.
|
||||
SPOT_API twa_graph_ptr
|
||||
scc_filter(const const_twa_graph_ptr& aut, bool remove_all_useless = false,
|
||||
scc_info* given_si = nullptr);
|
||||
scc_info* given_si = nullptr);
|
||||
|
||||
/// \brief Prune unaccepting SCCs.
|
||||
///
|
||||
|
|
@ -70,8 +70,8 @@ namespace spot
|
|||
/// property.
|
||||
SPOT_API twa_graph_ptr
|
||||
scc_filter_states(const const_twa_graph_ptr& aut,
|
||||
bool remove_all_useless = false,
|
||||
scc_info* given_si = nullptr);
|
||||
bool remove_all_useless = false,
|
||||
scc_info* given_si = nullptr);
|
||||
|
||||
/// \brief Prune unaccepting SCCs, superfluous acceptance
|
||||
/// sets, and suspension variables.
|
||||
|
|
@ -85,6 +85,6 @@ namespace spot
|
|||
/// other use.
|
||||
SPOT_API twa_graph_ptr
|
||||
scc_filter_susp(const const_twa_graph_ptr& aut, bool remove_all_useless,
|
||||
bdd suspvars, bdd ignoredvars, bool early_susp,
|
||||
scc_info* given_si = nullptr);
|
||||
bdd suspvars, bdd ignoredvars, bool early_susp,
|
||||
scc_info* given_si = nullptr);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,15 +35,15 @@ namespace spot
|
|||
{
|
||||
public:
|
||||
scc(int index, acc_cond::mark_t in_acc):
|
||||
in_acc(in_acc), index(index)
|
||||
in_acc(in_acc), index(index)
|
||||
{
|
||||
}
|
||||
|
||||
acc_cond::mark_t in_acc; // Acceptance sets on the incoming transition
|
||||
acc_cond::mark_t acc = 0U; // union of all acceptance sets in the SCC
|
||||
int index; // Index of the SCC
|
||||
bool trivial = true; // Whether the SCC has no cycle
|
||||
bool accepting = false; // Necessarily accepting
|
||||
int index; // Index of the SCC
|
||||
bool trivial = true; // Whether the SCC has no cycle
|
||||
bool accepting = false; // Necessarily accepting
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -54,165 +54,165 @@ namespace spot
|
|||
sccof_.resize(n, -1U);
|
||||
|
||||
std::deque<unsigned> live;
|
||||
std::deque<scc> root_; // Stack of SCC roots.
|
||||
std::deque<scc> root_; // Stack of SCC roots.
|
||||
std::vector<int> h_(n, 0);
|
||||
// Map of visited states. Values > 0 designate maximal SCC.
|
||||
// Values < 0 number states that are part of incomplete SCCs being
|
||||
// completed. 0 denotes non-visited states.
|
||||
|
||||
int num_; // Number of visited nodes, negated.
|
||||
int num_; // Number of visited nodes, negated.
|
||||
|
||||
typedef twa_graph::graph_t::const_iterator iterator;
|
||||
typedef std::pair<unsigned, iterator> pair_state_iter;
|
||||
std::stack<pair_state_iter> todo_; // DFS stack. Holds (STATE,
|
||||
// ITERATOR) pairs where
|
||||
// ITERATOR is an iterator over
|
||||
// the successors of STATE.
|
||||
// ITERATOR should always be
|
||||
// freed when TODO is popped,
|
||||
// but STATE should not because
|
||||
// it is used as a key in H.
|
||||
// ITERATOR) pairs where
|
||||
// ITERATOR is an iterator over
|
||||
// the successors of STATE.
|
||||
// ITERATOR should always be
|
||||
// freed when TODO is popped,
|
||||
// but STATE should not because
|
||||
// it is used as a key in H.
|
||||
|
||||
|
||||
// Setup depth-first search from the initial state.
|
||||
if (n > 0)
|
||||
{
|
||||
unsigned init = aut->get_init_state_number();
|
||||
num_ = -1;
|
||||
h_[init] = num_;
|
||||
root_.emplace_back(num_, 0U);
|
||||
todo_.emplace(init, aut->out(init).begin());
|
||||
live.emplace_back(init);
|
||||
unsigned init = aut->get_init_state_number();
|
||||
num_ = -1;
|
||||
h_[init] = num_;
|
||||
root_.emplace_back(num_, 0U);
|
||||
todo_.emplace(init, aut->out(init).begin());
|
||||
live.emplace_back(init);
|
||||
}
|
||||
|
||||
while (!todo_.empty())
|
||||
{
|
||||
// We are looking at the next successor in SUCC.
|
||||
iterator succ = todo_.top().second;
|
||||
// We are looking at the next successor in SUCC.
|
||||
iterator succ = todo_.top().second;
|
||||
|
||||
// If there is no more successor, backtrack.
|
||||
if (!succ)
|
||||
{
|
||||
// We have explored all successors of state CURR.
|
||||
unsigned curr = todo_.top().first;
|
||||
// If there is no more successor, backtrack.
|
||||
if (!succ)
|
||||
{
|
||||
// We have explored all successors of state CURR.
|
||||
unsigned curr = todo_.top().first;
|
||||
|
||||
// Backtrack TODO_.
|
||||
todo_.pop();
|
||||
// Backtrack TODO_.
|
||||
todo_.pop();
|
||||
|
||||
// When backtracking the root of an SCC, we must also
|
||||
// remove that SCC from the ARC/ROOT stacks. We must
|
||||
// discard from H all reachable states from this SCC.
|
||||
assert(!root_.empty());
|
||||
if (root_.back().index == h_[curr])
|
||||
{
|
||||
unsigned num = node_.size();
|
||||
auto acc = root_.back().acc;
|
||||
bool triv = root_.back().trivial;
|
||||
node_.emplace_back(acc, triv);
|
||||
// When backtracking the root of an SCC, we must also
|
||||
// remove that SCC from the ARC/ROOT stacks. We must
|
||||
// discard from H all reachable states from this SCC.
|
||||
assert(!root_.empty());
|
||||
if (root_.back().index == h_[curr])
|
||||
{
|
||||
unsigned num = node_.size();
|
||||
auto acc = root_.back().acc;
|
||||
bool triv = root_.back().trivial;
|
||||
node_.emplace_back(acc, triv);
|
||||
|
||||
// Move all elements of this SCC from the live stack
|
||||
// to the the node.
|
||||
auto i = std::find(live.rbegin(), live.rend(), curr);
|
||||
assert(i != live.rend());
|
||||
++i; // Because base() does -1
|
||||
auto& nbs = node_.back().states_;
|
||||
nbs.insert(nbs.end(), i.base(), live.end());
|
||||
live.erase(i.base(), live.end());
|
||||
// Move all elements of this SCC from the live stack
|
||||
// to the the node.
|
||||
auto i = std::find(live.rbegin(), live.rend(), curr);
|
||||
assert(i != live.rend());
|
||||
++i; // Because base() does -1
|
||||
auto& nbs = node_.back().states_;
|
||||
nbs.insert(nbs.end(), i.base(), live.end());
|
||||
live.erase(i.base(), live.end());
|
||||
|
||||
std::set<unsigned> dests;
|
||||
unsigned np1 = num + 1;
|
||||
for (unsigned s: nbs)
|
||||
{
|
||||
sccof_[s] = num;
|
||||
h_[s] = np1;
|
||||
}
|
||||
// Gather all successor SCCs
|
||||
for (unsigned s: nbs)
|
||||
for (auto& t: aut->out(s))
|
||||
{
|
||||
unsigned n = sccof_[t.dst];
|
||||
assert(n != -1U);
|
||||
if (n == num)
|
||||
continue;
|
||||
dests.insert(n);
|
||||
}
|
||||
auto& succ = node_.back().succ_;
|
||||
succ.insert(succ.end(), dests.begin(), dests.end());
|
||||
node_.back().accepting_ =
|
||||
!triv && root_.back().accepting;
|
||||
node_.back().rejecting_ =
|
||||
triv || !aut->acc().inf_satisfiable(acc);
|
||||
root_.pop_back();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
std::set<unsigned> dests;
|
||||
unsigned np1 = num + 1;
|
||||
for (unsigned s: nbs)
|
||||
{
|
||||
sccof_[s] = num;
|
||||
h_[s] = np1;
|
||||
}
|
||||
// Gather all successor SCCs
|
||||
for (unsigned s: nbs)
|
||||
for (auto& t: aut->out(s))
|
||||
{
|
||||
unsigned n = sccof_[t.dst];
|
||||
assert(n != -1U);
|
||||
if (n == num)
|
||||
continue;
|
||||
dests.insert(n);
|
||||
}
|
||||
auto& succ = node_.back().succ_;
|
||||
succ.insert(succ.end(), dests.begin(), dests.end());
|
||||
node_.back().accepting_ =
|
||||
!triv && root_.back().accepting;
|
||||
node_.back().rejecting_ =
|
||||
triv || !aut->acc().inf_satisfiable(acc);
|
||||
root_.pop_back();
|
||||
}
|
||||
continue;
|
||||
}
|
||||
|
||||
// We have a successor to look at.
|
||||
// Fetch the values we are interested in...
|
||||
unsigned dest = succ->dst;
|
||||
auto acc = succ->acc;
|
||||
++todo_.top().second;
|
||||
// We have a successor to look at.
|
||||
// Fetch the values we are interested in...
|
||||
unsigned dest = succ->dst;
|
||||
auto acc = succ->acc;
|
||||
++todo_.top().second;
|
||||
|
||||
// We do not need SUCC from now on.
|
||||
// We do not need SUCC from now on.
|
||||
|
||||
// Are we going to a new state?
|
||||
int spi = h_[dest];
|
||||
if (spi == 0)
|
||||
{
|
||||
// Yes. Number it, stack it, and register its successors
|
||||
// for later processing.
|
||||
h_[dest] = --num_;
|
||||
root_.emplace_back(num_, acc);
|
||||
todo_.emplace(dest, aut->out(dest).begin());
|
||||
live.emplace_back(dest);
|
||||
continue;
|
||||
}
|
||||
// Are we going to a new state?
|
||||
int spi = h_[dest];
|
||||
if (spi == 0)
|
||||
{
|
||||
// Yes. Number it, stack it, and register its successors
|
||||
// for later processing.
|
||||
h_[dest] = --num_;
|
||||
root_.emplace_back(num_, acc);
|
||||
todo_.emplace(dest, aut->out(dest).begin());
|
||||
live.emplace_back(dest);
|
||||
continue;
|
||||
}
|
||||
|
||||
// We already know the state.
|
||||
// We already know the state.
|
||||
|
||||
// Have we reached a maximal SCC?
|
||||
if (spi > 0)
|
||||
continue;
|
||||
// Have we reached a maximal SCC?
|
||||
if (spi > 0)
|
||||
continue;
|
||||
|
||||
// Now this is the most interesting case. We have reached a
|
||||
// state S1 which is already part of a non-dead SCC. Any such
|
||||
// non-dead SCC has necessarily been crossed by our path to
|
||||
// this state: there is a state S2 in our path which belongs
|
||||
// to this SCC too. We are going to merge all states between
|
||||
// this S1 and S2 into this SCC..
|
||||
//
|
||||
// This merge is easy to do because the order of the SCC in
|
||||
// ROOT is descending: we just have to merge all SCCs from the
|
||||
// top of ROOT that have an index lesser than the one of
|
||||
// the SCC of S2 (called the "threshold").
|
||||
int threshold = spi;
|
||||
bool is_accepting = false;
|
||||
// If this is a self-loop, check its acceptance alone.
|
||||
if (dest == succ->src)
|
||||
is_accepting = aut->acc().accepting(acc);
|
||||
// Now this is the most interesting case. We have reached a
|
||||
// state S1 which is already part of a non-dead SCC. Any such
|
||||
// non-dead SCC has necessarily been crossed by our path to
|
||||
// this state: there is a state S2 in our path which belongs
|
||||
// to this SCC too. We are going to merge all states between
|
||||
// this S1 and S2 into this SCC..
|
||||
//
|
||||
// This merge is easy to do because the order of the SCC in
|
||||
// ROOT is descending: we just have to merge all SCCs from the
|
||||
// top of ROOT that have an index lesser than the one of
|
||||
// the SCC of S2 (called the "threshold").
|
||||
int threshold = spi;
|
||||
bool is_accepting = false;
|
||||
// If this is a self-loop, check its acceptance alone.
|
||||
if (dest == succ->src)
|
||||
is_accepting = aut->acc().accepting(acc);
|
||||
|
||||
assert(!root_.empty());
|
||||
while (threshold > root_.back().index)
|
||||
{
|
||||
acc |= root_.back().acc;
|
||||
acc |= root_.back().in_acc;
|
||||
is_accepting |= root_.back().accepting;
|
||||
root_.pop_back();
|
||||
assert(!root_.empty());
|
||||
}
|
||||
assert(!root_.empty());
|
||||
while (threshold > root_.back().index)
|
||||
{
|
||||
acc |= root_.back().acc;
|
||||
acc |= root_.back().in_acc;
|
||||
is_accepting |= root_.back().accepting;
|
||||
root_.pop_back();
|
||||
assert(!root_.empty());
|
||||
}
|
||||
|
||||
// Note that we do not always have
|
||||
// threshold == root_.back().index
|
||||
// after this loop, the SCC whose index is threshold might have
|
||||
// been merged with a higher SCC.
|
||||
// Note that we do not always have
|
||||
// threshold == root_.back().index
|
||||
// after this loop, the SCC whose index is threshold might have
|
||||
// been merged with a higher SCC.
|
||||
|
||||
// Accumulate all acceptance conditions, states, SCC
|
||||
// successors, and conditions into the merged SCC.
|
||||
root_.back().acc |= acc;
|
||||
root_.back().accepting |= is_accepting
|
||||
|| aut->acc().accepting(root_.back().acc);
|
||||
// This SCC is no longer trivial.
|
||||
root_.back().trivial = false;
|
||||
// Accumulate all acceptance conditions, states, SCC
|
||||
// successors, and conditions into the merged SCC.
|
||||
root_.back().acc |= acc;
|
||||
root_.back().accepting |= is_accepting
|
||||
|| aut->acc().accepting(root_.back().acc);
|
||||
// This SCC is no longer trivial.
|
||||
root_.back().trivial = false;
|
||||
}
|
||||
|
||||
determine_usefulness();
|
||||
|
|
@ -225,18 +225,18 @@ namespace spot
|
|||
unsigned scccount = scc_count();
|
||||
for (unsigned i = 0; i < scccount; ++i)
|
||||
{
|
||||
if (!node_[i].is_rejecting())
|
||||
{
|
||||
node_[i].useful_ = true;
|
||||
continue;
|
||||
}
|
||||
node_[i].useful_ = false;
|
||||
for (unsigned j: node_[i].succ())
|
||||
if (node_[j].is_useful())
|
||||
{
|
||||
node_[i].useful_ = true;
|
||||
break;
|
||||
}
|
||||
if (!node_[i].is_rejecting())
|
||||
{
|
||||
node_[i].useful_ = true;
|
||||
continue;
|
||||
}
|
||||
node_[i].useful_ = false;
|
||||
for (unsigned j: node_[i].succ())
|
||||
if (node_[j].is_useful())
|
||||
{
|
||||
node_[i].useful_ = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -246,8 +246,8 @@ namespace spot
|
|||
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);
|
||||
if (scc_of(t.dst) == scc)
|
||||
res.insert(t.acc);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
|
@ -256,8 +256,8 @@ namespace spot
|
|||
acc_cond::mark_t res = 0U;
|
||||
for (auto src: states_of(scc))
|
||||
for (auto& t: aut_->out(src))
|
||||
if (scc_of(t.dst) == scc)
|
||||
res |= t.acc;
|
||||
if (scc_of(t.dst) == scc)
|
||||
res |= t.acc;
|
||||
return res;
|
||||
}
|
||||
|
||||
|
|
@ -268,16 +268,16 @@ namespace spot
|
|||
|
||||
for (unsigned src = 0; src < n; ++src)
|
||||
{
|
||||
unsigned src_scc = scc_of(src);
|
||||
if (src_scc == -1U || is_rejecting_scc(src_scc))
|
||||
continue;
|
||||
auto& s = result[src_scc];
|
||||
for (auto& t: aut_->out(src))
|
||||
{
|
||||
if (scc_of(t.dst) != src_scc)
|
||||
continue;
|
||||
s.insert(t.acc);
|
||||
}
|
||||
unsigned src_scc = scc_of(src);
|
||||
if (src_scc == -1U || is_rejecting_scc(src_scc))
|
||||
continue;
|
||||
auto& s = result[src_scc];
|
||||
for (auto& t: aut_->out(src))
|
||||
{
|
||||
if (scc_of(t.dst) != src_scc)
|
||||
continue;
|
||||
s.insert(t.acc);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
|
@ -297,7 +297,7 @@ namespace spot
|
|||
bdd support = bddtrue;
|
||||
for (auto s: states_of(scc))
|
||||
for (auto& t: aut_->out(s))
|
||||
support &= bdd_support(t.cond);
|
||||
support &= bdd_support(t.cond);
|
||||
return support;
|
||||
}
|
||||
|
||||
|
|
@ -308,25 +308,25 @@ namespace spot
|
|||
bool changed = false;
|
||||
for (unsigned s = 0; s < n; ++s)
|
||||
if (!is_rejecting_scc(s) && !is_accepting_scc(s))
|
||||
{
|
||||
auto& node = node_[s];
|
||||
if (k.empty())
|
||||
k.resize(aut_->num_states());
|
||||
for (auto i: node.states_)
|
||||
k[i] = true;
|
||||
if (mask_keep_states(aut_, k, node.states_.front())->is_empty())
|
||||
node.rejecting_ = true;
|
||||
else
|
||||
node.accepting_ = true;
|
||||
changed = true;
|
||||
}
|
||||
{
|
||||
auto& node = node_[s];
|
||||
if (k.empty())
|
||||
k.resize(aut_->num_states());
|
||||
for (auto i: node.states_)
|
||||
k[i] = true;
|
||||
if (mask_keep_states(aut_, k, node.states_.front())->is_empty())
|
||||
node.rejecting_ = true;
|
||||
else
|
||||
node.accepting_ = true;
|
||||
changed = true;
|
||||
}
|
||||
if (changed)
|
||||
determine_usefulness();
|
||||
}
|
||||
|
||||
std::ostream&
|
||||
dump_scc_info_dot(std::ostream& out,
|
||||
const_twa_graph_ptr aut, scc_info* sccinfo)
|
||||
const_twa_graph_ptr aut, scc_info* sccinfo)
|
||||
{
|
||||
scc_info* m = sccinfo ? sccinfo : new scc_info(aut);
|
||||
|
||||
|
|
@ -341,29 +341,29 @@ namespace spot
|
|||
q.push(start);
|
||||
while (!q.empty())
|
||||
{
|
||||
int state = q.front();
|
||||
q.pop();
|
||||
int state = q.front();
|
||||
q.pop();
|
||||
|
||||
out << " " << state << " [shape=box,"
|
||||
out << " " << state << " [shape=box,"
|
||||
<< (aut->acc().accepting(m->acc(state)) ? "style=bold," : "")
|
||||
<< "label=\"" << state;
|
||||
{
|
||||
size_t n = m->states_of(state).size();
|
||||
out << " (" << n << " state";
|
||||
if (n > 1)
|
||||
out << 's';
|
||||
out << ')';
|
||||
}
|
||||
out << "\"]\n";
|
||||
{
|
||||
size_t n = m->states_of(state).size();
|
||||
out << " (" << n << " state";
|
||||
if (n > 1)
|
||||
out << 's';
|
||||
out << ')';
|
||||
}
|
||||
out << "\"]\n";
|
||||
|
||||
for (unsigned dest: m->succ(state))
|
||||
{
|
||||
out << " " << state << " -> " << dest << '\n';
|
||||
if (seen[dest])
|
||||
continue;
|
||||
seen[dest] = true;
|
||||
q.push(dest);
|
||||
}
|
||||
for (unsigned dest: m->succ(state))
|
||||
{
|
||||
out << " " << state << " -> " << dest << '\n';
|
||||
if (seen[dest])
|
||||
continue;
|
||||
seen[dest] = true;
|
||||
q.push(dest);
|
||||
}
|
||||
}
|
||||
|
||||
out << "}\n";
|
||||
|
|
|
|||
|
|
@ -37,25 +37,25 @@ namespace spot
|
|||
acc_cond::mark_t acc_;
|
||||
std::vector<unsigned> states_; // States of the component
|
||||
bool trivial_:1;
|
||||
bool accepting_:1; // Necessarily accepting
|
||||
bool rejecting_:1; // Necessarily rejecting
|
||||
bool accepting_:1; // Necessarily accepting
|
||||
bool rejecting_:1; // Necessarily rejecting
|
||||
bool useful_:1;
|
||||
public:
|
||||
scc_node():
|
||||
acc_(0U), trivial_(true), accepting_(false),
|
||||
rejecting_(false), useful_(false)
|
||||
acc_(0U), trivial_(true), accepting_(false),
|
||||
rejecting_(false), useful_(false)
|
||||
{
|
||||
}
|
||||
|
||||
scc_node(acc_cond::mark_t acc, bool trivial):
|
||||
acc_(acc), trivial_(trivial), accepting_(false),
|
||||
rejecting_(false), useful_(false)
|
||||
acc_(acc), trivial_(trivial), accepting_(false),
|
||||
rejecting_(false), useful_(false)
|
||||
{
|
||||
}
|
||||
|
||||
bool is_trivial() const
|
||||
{
|
||||
return trivial_;
|
||||
return trivial_;
|
||||
}
|
||||
|
||||
/// \brief True if we are sure that the SCC is accepting
|
||||
|
|
@ -64,7 +64,7 @@ namespace spot
|
|||
/// false if an SCC interesects a mix of Fin and Inf sets.
|
||||
bool is_accepting() const
|
||||
{
|
||||
return accepting_;
|
||||
return accepting_;
|
||||
}
|
||||
|
||||
// True if we are sure that the SCC is rejecting
|
||||
|
|
@ -73,27 +73,27 @@ namespace spot
|
|||
/// false if an SCC interesects a mix of Fin and Inf sets.
|
||||
bool is_rejecting() const
|
||||
{
|
||||
return rejecting_;
|
||||
return rejecting_;
|
||||
}
|
||||
|
||||
bool is_useful() const
|
||||
{
|
||||
return useful_;
|
||||
return useful_;
|
||||
}
|
||||
|
||||
acc_cond::mark_t acc_marks() const
|
||||
{
|
||||
return acc_;
|
||||
return acc_;
|
||||
}
|
||||
|
||||
const std::vector<unsigned>& states() const
|
||||
{
|
||||
return states_;
|
||||
return states_;
|
||||
}
|
||||
|
||||
const scc_succs& succ() const
|
||||
{
|
||||
return succ_;
|
||||
return succ_;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -224,6 +224,6 @@ namespace spot
|
|||
/// If \a sccinfo is not given, it will be computed.
|
||||
SPOT_API std::ostream&
|
||||
dump_scc_info_dot(std::ostream& out,
|
||||
const_twa_graph_ptr aut, scc_info* sccinfo = nullptr);
|
||||
const_twa_graph_ptr aut, scc_info* sccinfo = nullptr);
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -55,7 +55,7 @@ namespace spot
|
|||
/// \pre The automaton \a a must have at most one acceptance
|
||||
/// condition (i.e. it is a TBA).
|
||||
se05_search(const const_twa_ptr a, size_t size,
|
||||
option_map o = option_map())
|
||||
option_map o = option_map())
|
||||
: emptiness_check(a, o),
|
||||
h(size)
|
||||
{
|
||||
|
|
@ -89,8 +89,8 @@ namespace spot
|
|||
/// visits only a finite set of accepting paths.
|
||||
virtual emptiness_check_result_ptr check() override
|
||||
{
|
||||
auto t = std::static_pointer_cast<se05_search>
|
||||
(this->emptiness_check::shared_from_this());
|
||||
auto t = std::static_pointer_cast<se05_search>
|
||||
(this->emptiness_check::shared_from_this());
|
||||
if (st_red.empty())
|
||||
{
|
||||
assert(st_blue.empty());
|
||||
|
|
@ -130,27 +130,27 @@ namespace spot
|
|||
|
||||
virtual bool safe() const override
|
||||
{
|
||||
return heap::Safe;
|
||||
return heap::Safe;
|
||||
}
|
||||
|
||||
const heap& get_heap() const
|
||||
{
|
||||
return h;
|
||||
return h;
|
||||
}
|
||||
|
||||
const stack_type& get_st_blue() const
|
||||
{
|
||||
return st_blue;
|
||||
return st_blue;
|
||||
}
|
||||
|
||||
const stack_type& get_st_red() const
|
||||
{
|
||||
return st_red;
|
||||
return st_red;
|
||||
}
|
||||
private:
|
||||
|
||||
void push(stack_type& st, const state* s,
|
||||
const bdd& label, acc_cond::mark_t acc)
|
||||
const bdd& label, acc_cond::mark_t acc)
|
||||
{
|
||||
inc_depth();
|
||||
twa_succ_iterator* i = a_->succ_iter(s);
|
||||
|
|
@ -201,7 +201,7 @@ namespace spot
|
|||
}
|
||||
else if (c.get_color() == CYAN && (a_->acc().accepting(acc) ||
|
||||
(f.s->compare(s_prime) != 0
|
||||
&& a_->acc().accepting(f.acc))))
|
||||
&& a_->acc().accepting(f.acc))))
|
||||
{
|
||||
trace << " It is cyan and acceptance condition "
|
||||
<< "is reached, report cycle" << std::endl;
|
||||
|
|
@ -238,7 +238,7 @@ namespace spot
|
|||
typename heap::color_ref c = h.get_color_ref(f_dest.s);
|
||||
assert(!c.is_white());
|
||||
if (!st_blue.empty() &&
|
||||
a_->acc().accepting(f_dest.acc) && c.get_color() != RED)
|
||||
a_->acc().accepting(f_dest.acc) && c.get_color() != RED)
|
||||
{
|
||||
// the test 'c.get_color() != RED' is added to limit
|
||||
// the number of runs reported by successive
|
||||
|
|
@ -383,7 +383,7 @@ namespace spot
|
|||
return 0;
|
||||
}
|
||||
private:
|
||||
std::shared_ptr<se05_search> ms_;
|
||||
std::shared_ptr<se05_search> ms_;
|
||||
};
|
||||
|
||||
# define FROM_STACK "ar:from_stack"
|
||||
|
|
@ -392,7 +392,7 @@ namespace spot
|
|||
{
|
||||
public:
|
||||
se05_result(const std::shared_ptr<se05_search>& m,
|
||||
option_map o = option_map())
|
||||
option_map o = option_map())
|
||||
: emptiness_check_result(m->automaton(), o), ms(m)
|
||||
{
|
||||
if (options()[FROM_STACK])
|
||||
|
|
@ -432,7 +432,7 @@ namespace spot
|
|||
|
||||
private:
|
||||
emptiness_check_result* computer;
|
||||
std::shared_ptr<se05_search> ms;
|
||||
std::shared_ptr<se05_search> ms;
|
||||
};
|
||||
};
|
||||
|
||||
|
|
@ -573,7 +573,7 @@ namespace spot
|
|||
{
|
||||
private:
|
||||
typedef std::unordered_set<const state*,
|
||||
state_ptr_hash, state_ptr_equal> hcyan_type;
|
||||
state_ptr_hash, state_ptr_equal> hcyan_type;
|
||||
public:
|
||||
enum { Safe = 0 };
|
||||
|
||||
|
|
@ -681,7 +681,7 @@ namespace spot
|
|||
|
||||
emptiness_check_ptr
|
||||
bit_state_hashing_se05_search(const const_twa_ptr& a,
|
||||
size_t size, option_map o)
|
||||
size_t size, option_map o)
|
||||
{
|
||||
return std::make_shared<se05_search<bsh_se05_search_heap>>(a, size, o);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -132,7 +132,7 @@ namespace spot
|
|||
///
|
||||
SPOT_API emptiness_check_ptr
|
||||
bit_state_hashing_se05_search(const const_twa_ptr& a, size_t size,
|
||||
option_map o = option_map());
|
||||
option_map o = option_map());
|
||||
|
||||
|
||||
/// \brief Wrapper for the two se05 implementations.
|
||||
|
|
|
|||
|
|
@ -48,8 +48,8 @@ namespace spot
|
|||
{
|
||||
unsigned base = aut->acc().add_sets(common.count());
|
||||
for (auto s: common.sets())
|
||||
map.emplace_back(acc_cond::mark_t({s}),
|
||||
acc_cond::mark_t({base++}));
|
||||
map.emplace_back(acc_cond::mark_t({s}),
|
||||
acc_cond::mark_t({base++}));
|
||||
}
|
||||
|
||||
// Fix the acceptance condition
|
||||
|
|
@ -60,38 +60,38 @@ namespace spot
|
|||
acc_cond::acc_word* start = &code.front();
|
||||
while (pos > start)
|
||||
{
|
||||
switch (pos->op)
|
||||
{
|
||||
case acc_cond::acc_op::Or:
|
||||
case acc_cond::acc_op::And:
|
||||
--pos;
|
||||
break;
|
||||
case acc_cond::acc_op::Fin:
|
||||
case acc_cond::acc_op::FinNeg:
|
||||
if ((pos[-1].mark & common) == 0U)
|
||||
break;
|
||||
for (auto p: map)
|
||||
if (pos[-1].mark & p.first)
|
||||
{
|
||||
pos[-1].mark -= p.first;
|
||||
pos[-1].mark |= p.second;
|
||||
}
|
||||
/* fall through */
|
||||
case acc_cond::acc_op::Inf:
|
||||
case acc_cond::acc_op::InfNeg:
|
||||
pos -= 2;
|
||||
break;
|
||||
}
|
||||
switch (pos->op)
|
||||
{
|
||||
case acc_cond::acc_op::Or:
|
||||
case acc_cond::acc_op::And:
|
||||
--pos;
|
||||
break;
|
||||
case acc_cond::acc_op::Fin:
|
||||
case acc_cond::acc_op::FinNeg:
|
||||
if ((pos[-1].mark & common) == 0U)
|
||||
break;
|
||||
for (auto p: map)
|
||||
if (pos[-1].mark & p.first)
|
||||
{
|
||||
pos[-1].mark -= p.first;
|
||||
pos[-1].mark |= p.second;
|
||||
}
|
||||
/* fall through */
|
||||
case acc_cond::acc_op::Inf:
|
||||
case acc_cond::acc_op::InfNeg:
|
||||
pos -= 2;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
// Fix the edges
|
||||
for (auto& t: aut->edges())
|
||||
{
|
||||
if ((t.acc & common) == 0U)
|
||||
continue;
|
||||
for (auto p: map)
|
||||
if (t.acc & p.first)
|
||||
t.acc |= p.second;
|
||||
if ((t.acc & common) == 0U)
|
||||
continue;
|
||||
for (auto p: map)
|
||||
if (t.acc & p.first)
|
||||
t.acc |= p.second;
|
||||
}
|
||||
return aut;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -109,8 +109,8 @@ namespace spot
|
|||
|
||||
void set_size(const twa_graph_ptr& a)
|
||||
{
|
||||
states = a->num_states();
|
||||
edges = a->num_edges();
|
||||
states = a->num_states();
|
||||
edges = a->num_edges();
|
||||
}
|
||||
|
||||
inline bool operator!=(const automaton_size& r)
|
||||
|
|
@ -166,23 +166,23 @@ namespace spot
|
|||
|
||||
bdd mark_to_bdd(acc_cond::mark_t m)
|
||||
{
|
||||
// FIXME: Use a cache.
|
||||
bdd res = bddtrue;
|
||||
for (auto n: m.sets())
|
||||
res &= bdd_ithvar(acc_vars + n);
|
||||
return res;
|
||||
// FIXME: Use a cache.
|
||||
bdd res = bddtrue;
|
||||
for (auto n: m.sets())
|
||||
res &= bdd_ithvar(acc_vars + n);
|
||||
return res;
|
||||
}
|
||||
|
||||
acc_cond::mark_t bdd_to_mark(bdd b)
|
||||
{
|
||||
// FIXME: Use a cache.
|
||||
std::vector<unsigned> res;
|
||||
while (b != bddtrue)
|
||||
{
|
||||
res.push_back(bdd_var(b) - acc_vars);
|
||||
b = bdd_high(b);
|
||||
}
|
||||
return acc_cond::mark_t(res.begin(), res.end());
|
||||
// FIXME: Use a cache.
|
||||
std::vector<unsigned> res;
|
||||
while (b != bddtrue)
|
||||
{
|
||||
res.push_back(bdd_var(b) - acc_vars);
|
||||
b = bdd_high(b);
|
||||
}
|
||||
return acc_cond::mark_t(res.begin(), res.end());
|
||||
}
|
||||
|
||||
direct_simulation(const const_twa_graph_ptr& in)
|
||||
|
|
@ -190,104 +190,104 @@ namespace spot
|
|||
all_class_var_(bddtrue),
|
||||
original_(in)
|
||||
{
|
||||
if (!has_separate_sets(in))
|
||||
throw std::runtime_error
|
||||
("direct_simulation() requires separate Inf and Fin sets");
|
||||
if (!has_separate_sets(in))
|
||||
throw std::runtime_error
|
||||
("direct_simulation() requires separate Inf and Fin sets");
|
||||
|
||||
// Call get_init_state_number() before anything else as it
|
||||
// might add a state.
|
||||
unsigned init_state_number = in->get_init_state_number();
|
||||
// Call get_init_state_number() before anything else as it
|
||||
// might add a state.
|
||||
unsigned init_state_number = in->get_init_state_number();
|
||||
scc_info_.reset(new scc_info(in));
|
||||
|
||||
unsigned ns = in->num_states();
|
||||
assert(ns > 0);
|
||||
size_a_ = ns;
|
||||
unsigned ns = in->num_states();
|
||||
assert(ns > 0);
|
||||
size_a_ = ns;
|
||||
|
||||
auto all_inf = in->get_acceptance().used_inf_fin_sets().first;
|
||||
all_inf_ = all_inf;
|
||||
auto all_inf = in->get_acceptance().used_inf_fin_sets().first;
|
||||
all_inf_ = all_inf;
|
||||
|
||||
// Replace all the acceptance conditions by their complements.
|
||||
// (In the case of Cosimulation, we also flip the edges.)
|
||||
if (Cosimulation)
|
||||
{
|
||||
a_ = make_twa_graph(in->get_dict());
|
||||
a_->copy_ap_of(in);
|
||||
a_->copy_acceptance_of(in);
|
||||
a_->new_states(ns);
|
||||
// Replace all the acceptance conditions by their complements.
|
||||
// (In the case of Cosimulation, we also flip the edges.)
|
||||
if (Cosimulation)
|
||||
{
|
||||
a_ = make_twa_graph(in->get_dict());
|
||||
a_->copy_ap_of(in);
|
||||
a_->copy_acceptance_of(in);
|
||||
a_->new_states(ns);
|
||||
|
||||
for (unsigned s = 0; s < ns; ++s)
|
||||
{
|
||||
for (auto& t: in->out(s))
|
||||
{
|
||||
acc_cond::mark_t acc;
|
||||
if (Sba)
|
||||
{
|
||||
// If the acceptance is interpreted as
|
||||
// state-based, to apply the reverse simulation
|
||||
// on a SBA, we should pull the acceptance of
|
||||
// the destination state on its incoming arcs
|
||||
// (which now become outgoing arcs after
|
||||
// transposition).
|
||||
acc = 0U;
|
||||
for (auto& td: in->out(t.dst))
|
||||
{
|
||||
acc = td.acc ^ all_inf;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
acc = t.acc ^ all_inf;
|
||||
}
|
||||
a_->new_edge(t.dst, s, t.cond, acc);
|
||||
}
|
||||
a_->set_init_state(init_state_number);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
a_ = make_twa_graph(in, twa::prop_set::all());
|
||||
for (auto& t: a_->edges())
|
||||
t.acc ^= all_inf;
|
||||
}
|
||||
assert(a_->num_states() == size_a_);
|
||||
for (unsigned s = 0; s < ns; ++s)
|
||||
{
|
||||
for (auto& t: in->out(s))
|
||||
{
|
||||
acc_cond::mark_t acc;
|
||||
if (Sba)
|
||||
{
|
||||
// If the acceptance is interpreted as
|
||||
// state-based, to apply the reverse simulation
|
||||
// on a SBA, we should pull the acceptance of
|
||||
// the destination state on its incoming arcs
|
||||
// (which now become outgoing arcs after
|
||||
// transposition).
|
||||
acc = 0U;
|
||||
for (auto& td: in->out(t.dst))
|
||||
{
|
||||
acc = td.acc ^ all_inf;
|
||||
break;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
acc = t.acc ^ all_inf;
|
||||
}
|
||||
a_->new_edge(t.dst, s, t.cond, acc);
|
||||
}
|
||||
a_->set_init_state(init_state_number);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
a_ = make_twa_graph(in, twa::prop_set::all());
|
||||
for (auto& t: a_->edges())
|
||||
t.acc ^= all_inf;
|
||||
}
|
||||
assert(a_->num_states() == size_a_);
|
||||
|
||||
// Now, we have to get the bdd which will represent the
|
||||
// class. We register one bdd by state, because in the worst
|
||||
// case, |Class| == |State|.
|
||||
unsigned set_num = a_->get_dict()
|
||||
->register_anonymous_variables(size_a_ + 1, this);
|
||||
// Now, we have to get the bdd which will represent the
|
||||
// class. We register one bdd by state, because in the worst
|
||||
// case, |Class| == |State|.
|
||||
unsigned set_num = a_->get_dict()
|
||||
->register_anonymous_variables(size_a_ + 1, this);
|
||||
|
||||
unsigned n_acc = a_->num_sets();
|
||||
acc_vars = a_->get_dict()
|
||||
->register_anonymous_variables(n_acc, this);
|
||||
unsigned n_acc = a_->num_sets();
|
||||
acc_vars = a_->get_dict()
|
||||
->register_anonymous_variables(n_acc, this);
|
||||
|
||||
all_proms_ = bddtrue;
|
||||
for (unsigned v = acc_vars; v < acc_vars + n_acc; ++v)
|
||||
all_proms_ &= bdd_ithvar(v);
|
||||
all_proms_ = bddtrue;
|
||||
for (unsigned v = acc_vars; v < acc_vars + n_acc; ++v)
|
||||
all_proms_ &= bdd_ithvar(v);
|
||||
|
||||
bdd_initial = bdd_ithvar(set_num++);
|
||||
bdd init = bdd_ithvar(set_num++);
|
||||
bdd init = bdd_ithvar(set_num++);
|
||||
|
||||
used_var_.push_back(init);
|
||||
used_var_.push_back(init);
|
||||
|
||||
// Initialize all classes to init.
|
||||
previous_class_.resize(size_a_);
|
||||
for (unsigned s = 0; s < size_a_; ++s)
|
||||
previous_class_[s] = init;
|
||||
// Initialize all classes to init.
|
||||
previous_class_.resize(size_a_);
|
||||
for (unsigned s = 0; s < size_a_; ++s)
|
||||
previous_class_[s] = init;
|
||||
|
||||
// Put all the anonymous variable in a queue, and record all
|
||||
// of these in a variable all_class_var_ which will be used
|
||||
// to understand the destination part in the signature when
|
||||
// building the resulting automaton.
|
||||
all_class_var_ = init;
|
||||
for (unsigned i = set_num; i < set_num + size_a_ - 1; ++i)
|
||||
// Put all the anonymous variable in a queue, and record all
|
||||
// of these in a variable all_class_var_ which will be used
|
||||
// to understand the destination part in the signature when
|
||||
// building the resulting automaton.
|
||||
all_class_var_ = init;
|
||||
for (unsigned i = set_num; i < set_num + size_a_ - 1; ++i)
|
||||
{
|
||||
free_var_.push(i);
|
||||
all_class_var_ &= bdd_ithvar(i);
|
||||
}
|
||||
|
||||
relation_[init] = init;
|
||||
relation_[init] = init;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -296,40 +296,40 @@ namespace spot
|
|||
// function simulation.
|
||||
virtual ~direct_simulation()
|
||||
{
|
||||
a_->get_dict()->unregister_all_my_variables(this);
|
||||
a_->get_dict()->unregister_all_my_variables(this);
|
||||
}
|
||||
|
||||
// Update the name of the classes.
|
||||
void update_previous_class()
|
||||
{
|
||||
std::list<bdd>::iterator it_bdd = used_var_.begin();
|
||||
std::list<bdd>::iterator it_bdd = used_var_.begin();
|
||||
|
||||
// We run through the map bdd/list<state>, and we update
|
||||
// the previous_class_ with the new data.
|
||||
for (auto& p: bdd_lstate_)
|
||||
// We run through the map bdd/list<state>, and we update
|
||||
// the previous_class_ with the new data.
|
||||
for (auto& p: bdd_lstate_)
|
||||
{
|
||||
// If the signature of a state is bddfalse (no
|
||||
// edges) the class of this state is bddfalse
|
||||
// instead of an anonymous variable. It allows
|
||||
// simplifications in the signature by removing a
|
||||
// edge which has as a destination a state with
|
||||
// no outgoing edge.
|
||||
if (p.first == bddfalse)
|
||||
for (auto s: p.second)
|
||||
previous_class_[s] = bddfalse;
|
||||
else
|
||||
for (auto s: p.second)
|
||||
previous_class_[s] = *it_bdd;
|
||||
++it_bdd;
|
||||
// If the signature of a state is bddfalse (no
|
||||
// edges) the class of this state is bddfalse
|
||||
// instead of an anonymous variable. It allows
|
||||
// simplifications in the signature by removing a
|
||||
// edge which has as a destination a state with
|
||||
// no outgoing edge.
|
||||
if (p.first == bddfalse)
|
||||
for (auto s: p.second)
|
||||
previous_class_[s] = bddfalse;
|
||||
else
|
||||
for (auto s: p.second)
|
||||
previous_class_[s] = *it_bdd;
|
||||
++it_bdd;
|
||||
}
|
||||
}
|
||||
|
||||
void main_loop()
|
||||
{
|
||||
unsigned int nb_partition_before = 0;
|
||||
unsigned int nb_po_before = po_size_ - 1;
|
||||
while (nb_partition_before != bdd_lstate_.size()
|
||||
|| nb_po_before != po_size_)
|
||||
unsigned int nb_partition_before = 0;
|
||||
unsigned int nb_po_before = po_size_ - 1;
|
||||
while (nb_partition_before != bdd_lstate_.size()
|
||||
|| nb_po_before != po_size_)
|
||||
{
|
||||
update_previous_class();
|
||||
nb_partition_before = bdd_lstate_.size();
|
||||
|
|
@ -340,14 +340,14 @@ namespace spot
|
|||
go_to_next_it();
|
||||
}
|
||||
|
||||
update_previous_class();
|
||||
update_previous_class();
|
||||
}
|
||||
|
||||
// The core loop of the algorithm.
|
||||
twa_graph_ptr run(std::map<int, bdd>* implications = nullptr)
|
||||
{
|
||||
main_loop();
|
||||
return build_result(implications);
|
||||
return build_result(implications);
|
||||
}
|
||||
|
||||
// Take a state and compute its signature.
|
||||
|
|
@ -359,13 +359,13 @@ namespace spot
|
|||
{
|
||||
bdd acc = mark_to_bdd(t.acc);
|
||||
|
||||
// to_add is a conjunction of the acceptance condition,
|
||||
// the label of the edge and the class of the
|
||||
// destination and all the class it implies.
|
||||
bdd to_add = acc & t.cond & relation_[previous_class_[t.dst]];
|
||||
// to_add is a conjunction of the acceptance condition,
|
||||
// the label of the edge and the class of the
|
||||
// destination and all the class it implies.
|
||||
bdd to_add = acc & t.cond & relation_[previous_class_[t.dst]];
|
||||
|
||||
res |= to_add;
|
||||
}
|
||||
res |= to_add;
|
||||
}
|
||||
|
||||
// When we Cosimulate, we add a special flag to differentiate
|
||||
// the initial state from the other.
|
||||
|
|
@ -378,20 +378,20 @@ namespace spot
|
|||
|
||||
void update_sig()
|
||||
{
|
||||
for (unsigned s = 0; s < size_a_; ++s)
|
||||
bdd_lstate_[compute_sig(s)].push_back(s);
|
||||
for (unsigned s = 0; s < size_a_; ++s)
|
||||
bdd_lstate_[compute_sig(s)].push_back(s);
|
||||
}
|
||||
|
||||
|
||||
// This method rename the color set, update the partial order.
|
||||
void go_to_next_it()
|
||||
{
|
||||
int nb_new_color = bdd_lstate_.size() - used_var_.size();
|
||||
int nb_new_color = bdd_lstate_.size() - used_var_.size();
|
||||
|
||||
|
||||
// If we have created more partitions, we need to use more
|
||||
// variables.
|
||||
for (int i = 0; i < nb_new_color; ++i)
|
||||
for (int i = 0; i < nb_new_color; ++i)
|
||||
{
|
||||
assert(!free_var_.empty());
|
||||
used_var_.push_back(bdd_ithvar(free_var_.front()));
|
||||
|
|
@ -401,7 +401,7 @@ namespace spot
|
|||
|
||||
// If we have reduced the number of partition, we 'free' them
|
||||
// in the free_var_ list.
|
||||
for (int i = 0; i > nb_new_color; --i)
|
||||
for (int i = 0; i > nb_new_color; --i)
|
||||
{
|
||||
assert(!used_var_.empty());
|
||||
free_var_.push(bdd_var(used_var_.front()));
|
||||
|
|
@ -409,36 +409,36 @@ namespace spot
|
|||
}
|
||||
|
||||
|
||||
assert((bdd_lstate_.size() == used_var_.size())
|
||||
assert((bdd_lstate_.size() == used_var_.size())
|
||||
|| (bdd_lstate_.find(bddfalse) != bdd_lstate_.end()
|
||||
&& bdd_lstate_.size() == used_var_.size() + 1));
|
||||
|
||||
// Now we make a temporary hash_table which links the tuple
|
||||
// "C^(i-1), N^(i-1)" to the new class coloring. If we
|
||||
// rename the class before updating the partial order, we
|
||||
// loose the information, and if we make it after, I can't
|
||||
// figure out how to apply this renaming on rel_.
|
||||
// It adds a data structure but it solves our problem.
|
||||
map_bdd_bdd now_to_next;
|
||||
// Now we make a temporary hash_table which links the tuple
|
||||
// "C^(i-1), N^(i-1)" to the new class coloring. If we
|
||||
// rename the class before updating the partial order, we
|
||||
// loose the information, and if we make it after, I can't
|
||||
// figure out how to apply this renaming on rel_.
|
||||
// It adds a data structure but it solves our problem.
|
||||
map_bdd_bdd now_to_next;
|
||||
|
||||
std::list<bdd>::iterator it_bdd = used_var_.begin();
|
||||
std::list<bdd>::iterator it_bdd = used_var_.begin();
|
||||
|
||||
for (auto& p: bdd_lstate_)
|
||||
for (auto& p: bdd_lstate_)
|
||||
{
|
||||
// If the signature of a state is bddfalse (no
|
||||
// edges) the class of this state is bddfalse
|
||||
// instead of an anonymous variable. It allows
|
||||
// simplifications in the signature by removing a
|
||||
// edge which has as a destination a state with
|
||||
// no outgoing edge.
|
||||
bdd acc = bddfalse;
|
||||
if (p.first != bddfalse)
|
||||
acc = *it_bdd;
|
||||
now_to_next[p.first] = acc;
|
||||
++it_bdd;
|
||||
// If the signature of a state is bddfalse (no
|
||||
// edges) the class of this state is bddfalse
|
||||
// instead of an anonymous variable. It allows
|
||||
// simplifications in the signature by removing a
|
||||
// edge which has as a destination a state with
|
||||
// no outgoing edge.
|
||||
bdd acc = bddfalse;
|
||||
if (p.first != bddfalse)
|
||||
acc = *it_bdd;
|
||||
now_to_next[p.first] = acc;
|
||||
++it_bdd;
|
||||
}
|
||||
|
||||
update_po(now_to_next, relation_);
|
||||
update_po(now_to_next, relation_);
|
||||
}
|
||||
|
||||
// This function computes the new po with previous_class_ and
|
||||
|
|
@ -451,17 +451,17 @@ namespace spot
|
|||
void update_po(const container_bdd_bdd& now_to_next,
|
||||
map_bdd_bdd& relation)
|
||||
{
|
||||
// This loop follows the pattern given by the paper.
|
||||
// foreach class do
|
||||
// | foreach class do
|
||||
// | | update po if needed
|
||||
// | od
|
||||
// od
|
||||
// This loop follows the pattern given by the paper.
|
||||
// foreach class do
|
||||
// | foreach class do
|
||||
// | | update po if needed
|
||||
// | od
|
||||
// od
|
||||
|
||||
for (typename container_bdd_bdd::const_iterator it1
|
||||
for (typename container_bdd_bdd::const_iterator it1
|
||||
= now_to_next.begin();
|
||||
it1 != now_to_next.end();
|
||||
++it1)
|
||||
it1 != now_to_next.end();
|
||||
++it1)
|
||||
{
|
||||
bdd accu = it1->second;
|
||||
for (typename container_bdd_bdd::const_iterator it2
|
||||
|
|
@ -486,31 +486,31 @@ namespace spot
|
|||
// Build the minimal resulting automaton.
|
||||
twa_graph_ptr build_result(std::map<int, bdd>* implications = nullptr)
|
||||
{
|
||||
twa_graph_ptr res = make_twa_graph(a_->get_dict());
|
||||
res->copy_ap_of(a_);
|
||||
res->copy_acceptance_of(a_);
|
||||
twa_graph_ptr res = make_twa_graph(a_->get_dict());
|
||||
res->copy_ap_of(a_);
|
||||
res->copy_acceptance_of(a_);
|
||||
|
||||
// Non atomic propositions variables (= acc and class)
|
||||
bdd nonapvars = all_proms_ & bdd_support(all_class_var_);
|
||||
// Non atomic propositions variables (= acc and class)
|
||||
bdd nonapvars = all_proms_ & bdd_support(all_class_var_);
|
||||
|
||||
auto* gb = res->create_namer<int>();
|
||||
auto* gb = res->create_namer<int>();
|
||||
|
||||
// Create one state per partition.
|
||||
for (auto& p: bdd_lstate_)
|
||||
// Create one state per partition.
|
||||
for (auto& p: bdd_lstate_)
|
||||
{
|
||||
bdd cl = previous_class_[p.second.front()];
|
||||
// A state may be referred to either by
|
||||
// its class, or by all the implied classes.
|
||||
auto s = gb->new_state(cl.id());
|
||||
gb->alias_state(s, relation_[cl].id());
|
||||
// A state may be referred to either by
|
||||
// its class, or by all the implied classes.
|
||||
auto s = gb->new_state(cl.id());
|
||||
gb->alias_state(s, relation_[cl].id());
|
||||
if (implications)
|
||||
(*implications)[s] = relation_[cl];
|
||||
}
|
||||
|
||||
// Acceptance of states. Only used if Sba && Cosimulation.
|
||||
std::vector<acc_cond::mark_t> accst;
|
||||
if (Sba && Cosimulation)
|
||||
accst.resize(res->num_states(), 0U);
|
||||
// Acceptance of states. Only used if Sba && Cosimulation.
|
||||
std::vector<acc_cond::mark_t> accst;
|
||||
if (Sba && Cosimulation)
|
||||
accst.resize(res->num_states(), 0U);
|
||||
|
||||
stat.states = bdd_lstate_.size();
|
||||
stat.edges = 0;
|
||||
|
|
@ -518,14 +518,14 @@ namespace spot
|
|||
unsigned nb_satoneset = 0;
|
||||
unsigned nb_minato = 0;
|
||||
|
||||
auto all_inf = all_inf_;
|
||||
// For each class, we will create
|
||||
// all the edges between the states.
|
||||
for (auto& p: bdd_lstate_)
|
||||
auto all_inf = all_inf_;
|
||||
// For each class, we will create
|
||||
// all the edges between the states.
|
||||
for (auto& p: bdd_lstate_)
|
||||
{
|
||||
// All states in p.second have the same class, so just
|
||||
// pick the class of the first one first one.
|
||||
bdd src = previous_class_[p.second.front()];
|
||||
// All states in p.second have the same class, so just
|
||||
// pick the class of the first one first one.
|
||||
bdd src = previous_class_[p.second.front()];
|
||||
|
||||
// Get the signature to derive successors.
|
||||
bdd sig = compute_sig(p.second.front());
|
||||
|
|
@ -544,108 +544,108 @@ namespace spot
|
|||
// proposition.
|
||||
bdd all_atomic_prop = bdd_exist(sig, nonapvars);
|
||||
|
||||
// First loop over all possible valuations atomic properties.
|
||||
// First loop over all possible valuations atomic properties.
|
||||
while (all_atomic_prop != bddfalse)
|
||||
{
|
||||
bdd one = bdd_satoneset(all_atomic_prop,
|
||||
sup_all_atomic_prop,
|
||||
bddtrue);
|
||||
all_atomic_prop -= one;
|
||||
{
|
||||
bdd one = bdd_satoneset(all_atomic_prop,
|
||||
sup_all_atomic_prop,
|
||||
bddtrue);
|
||||
all_atomic_prop -= one;
|
||||
|
||||
// For each possible valuation, iterate over all possible
|
||||
// destination classes. We use minato_isop here, because
|
||||
// if the same valuation of atomic properties can go
|
||||
// to two different classes C1 and C2, iterating on
|
||||
// C1 + C2 with the above bdd_satoneset loop will see
|
||||
// C1 then (!C1)C2, instead of C1 then C2.
|
||||
// With minatop_isop, we ensure that the no negative
|
||||
// class variable will be seen (likewise for promises).
|
||||
minato_isop isop(sig & one);
|
||||
// For each possible valuation, iterate over all possible
|
||||
// destination classes. We use minato_isop here, because
|
||||
// if the same valuation of atomic properties can go
|
||||
// to two different classes C1 and C2, iterating on
|
||||
// C1 + C2 with the above bdd_satoneset loop will see
|
||||
// C1 then (!C1)C2, instead of C1 then C2.
|
||||
// With minatop_isop, we ensure that the no negative
|
||||
// class variable will be seen (likewise for promises).
|
||||
minato_isop isop(sig & one);
|
||||
|
||||
++nb_satoneset;
|
||||
|
||||
bdd cond_acc_dest;
|
||||
while ((cond_acc_dest = isop.next()) != bddfalse)
|
||||
{
|
||||
bdd cond_acc_dest;
|
||||
while ((cond_acc_dest = isop.next()) != bddfalse)
|
||||
{
|
||||
++stat.edges;
|
||||
|
||||
++nb_minato;
|
||||
|
||||
// Take the edge, and keep only the variable which
|
||||
// are used to represent the class.
|
||||
bdd dst = bdd_existcomp(cond_acc_dest,
|
||||
all_class_var_);
|
||||
// Take the edge, and keep only the variable which
|
||||
// are used to represent the class.
|
||||
bdd dst = bdd_existcomp(cond_acc_dest,
|
||||
all_class_var_);
|
||||
|
||||
// Keep only ones who are acceptance condition.
|
||||
auto acc = bdd_to_mark(bdd_existcomp(cond_acc_dest,
|
||||
all_proms_));
|
||||
// Keep only ones who are acceptance condition.
|
||||
auto acc = bdd_to_mark(bdd_existcomp(cond_acc_dest,
|
||||
all_proms_));
|
||||
|
||||
// Keep the other!
|
||||
bdd cond = bdd_existcomp(cond_acc_dest,
|
||||
sup_all_atomic_prop);
|
||||
// Keep the other!
|
||||
bdd cond = bdd_existcomp(cond_acc_dest,
|
||||
sup_all_atomic_prop);
|
||||
|
||||
// Because we have complemented all the Inf
|
||||
// acceptance conditions on the input automaton,
|
||||
// we must revert them to create a new edge.
|
||||
acc ^= all_inf;
|
||||
// Because we have complemented all the Inf
|
||||
// acceptance conditions on the input automaton,
|
||||
// we must revert them to create a new edge.
|
||||
acc ^= all_inf;
|
||||
|
||||
if (Cosimulation)
|
||||
{
|
||||
if (Sba)
|
||||
{
|
||||
// acc should be attached to src, or rather,
|
||||
// in our edge-based representation)
|
||||
// to all edges leaving src. As we
|
||||
// can't do this here, store this in a table
|
||||
// so we can fix it later.
|
||||
accst[gb->get_state(src.id())] = acc;
|
||||
acc = 0U;
|
||||
}
|
||||
gb->new_edge(dst.id(), src.id(), cond, acc);
|
||||
}
|
||||
else
|
||||
{
|
||||
gb->new_edge(src.id(), dst.id(), cond, acc);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (Cosimulation)
|
||||
{
|
||||
if (Sba)
|
||||
{
|
||||
// acc should be attached to src, or rather,
|
||||
// in our edge-based representation)
|
||||
// to all edges leaving src. As we
|
||||
// can't do this here, store this in a table
|
||||
// so we can fix it later.
|
||||
accst[gb->get_state(src.id())] = acc;
|
||||
acc = 0U;
|
||||
}
|
||||
gb->new_edge(dst.id(), src.id(), cond, acc);
|
||||
}
|
||||
else
|
||||
{
|
||||
gb->new_edge(src.id(), dst.id(), cond, acc);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
res->set_init_state(gb->get_state(previous_class_
|
||||
[a_->get_init_state_number()].id()));
|
||||
res->set_init_state(gb->get_state(previous_class_
|
||||
[a_->get_init_state_number()].id()));
|
||||
|
||||
res->merge_edges(); // FIXME: is this really needed?
|
||||
res->merge_edges(); // FIXME: is this really needed?
|
||||
|
||||
// Mark all accepting state in a second pass, when
|
||||
// dealing with SBA in cosimulation.
|
||||
if (Sba && Cosimulation)
|
||||
{
|
||||
unsigned ns = res->num_states();
|
||||
for (unsigned s = 0; s < ns; ++s)
|
||||
{
|
||||
acc_cond::mark_t acc = accst[s];
|
||||
if (acc == 0U)
|
||||
continue;
|
||||
for (auto& t: res->out(s))
|
||||
t.acc = acc;
|
||||
}
|
||||
}
|
||||
// Mark all accepting state in a second pass, when
|
||||
// dealing with SBA in cosimulation.
|
||||
if (Sba && Cosimulation)
|
||||
{
|
||||
unsigned ns = res->num_states();
|
||||
for (unsigned s = 0; s < ns; ++s)
|
||||
{
|
||||
acc_cond::mark_t acc = accst[s];
|
||||
if (acc == 0U)
|
||||
continue;
|
||||
for (auto& t: res->out(s))
|
||||
t.acc = acc;
|
||||
}
|
||||
}
|
||||
|
||||
res->purge_unreachable_states();
|
||||
res->purge_unreachable_states();
|
||||
|
||||
delete gb;
|
||||
res->prop_copy(original_,
|
||||
{ false, // state-based acc forced below
|
||||
true, // weakness preserved,
|
||||
true, // determinism checked and overridden below
|
||||
// and "unambiguous" property preserved
|
||||
true, // stutter inv.
|
||||
});
|
||||
res->prop_copy(original_,
|
||||
{ false, // state-based acc forced below
|
||||
true, // weakness preserved,
|
||||
true, // determinism checked and overridden below
|
||||
// and "unambiguous" property preserved
|
||||
true, // stutter inv.
|
||||
});
|
||||
if (nb_minato == nb_satoneset && !Cosimulation)
|
||||
res->prop_deterministic(true);
|
||||
if (Sba)
|
||||
res->prop_state_acc(true);
|
||||
return res;
|
||||
res->prop_deterministic(true);
|
||||
if (Sba)
|
||||
res->prop_state_acc(true);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -656,21 +656,21 @@ namespace spot
|
|||
// where is the new class name.
|
||||
void print_partition()
|
||||
{
|
||||
for (auto& p: bdd_lstate_)
|
||||
for (auto& p: bdd_lstate_)
|
||||
{
|
||||
std::cerr << "partition: "
|
||||
<< bdd_format_isop(a_->get_dict(), p.first)
|
||||
<< std::endl;
|
||||
for (auto s: p.second)
|
||||
std::cerr << " - "
|
||||
<< a_->format_state(a_->state_from_number(s))
|
||||
<< '\n';
|
||||
std::cerr << " - "
|
||||
<< a_->format_state(a_->state_from_number(s))
|
||||
<< '\n';
|
||||
}
|
||||
|
||||
std::cerr << "\nPrevious iteration\n" << std::endl;
|
||||
std::cerr << "\nPrevious iteration\n" << std::endl;
|
||||
|
||||
unsigned ps = previous_class_.size();
|
||||
for (unsigned p = 0; p < ps; ++p)
|
||||
unsigned ps = previous_class_.size();
|
||||
for (unsigned p = 0; p < ps; ++p)
|
||||
{
|
||||
std::cerr << a_->format_state(a_->state_from_number(p))
|
||||
<< " was in "
|
||||
|
|
@ -781,15 +781,15 @@ namespace spot
|
|||
direct_simulation<false, Sba> simul(res ? res : t);
|
||||
res = simul.run();
|
||||
if (res->prop_deterministic())
|
||||
break;
|
||||
break;
|
||||
|
||||
direct_simulation<true, Sba> cosimul(res);
|
||||
res = cosimul.run();
|
||||
res = cosimul.run();
|
||||
|
||||
if (Sba)
|
||||
res = scc_filter_states(res, false);
|
||||
else
|
||||
res = scc_filter(res, false);
|
||||
if (Sba)
|
||||
res = scc_filter_states(res, false);
|
||||
else
|
||||
res = scc_filter(res, false);
|
||||
|
||||
next.set_size(res);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ namespace spot
|
|||
author = {Kousha Etessami and Gerard J. Holzmann},
|
||||
title = {Optimizing {B\"u}chi Automata},
|
||||
booktitle = {Proceedings of the 11th International Conference on
|
||||
Concurrency Theory (Concur'00)},
|
||||
Concurrency Theory (Concur'00)},
|
||||
pages = {153--167},
|
||||
year = {2000},
|
||||
editor = {C. Palamidessi},
|
||||
|
|
@ -87,8 +87,8 @@ namespace spot
|
|||
/// but generalized to handle TωA directly.
|
||||
/** \verbatim
|
||||
@InProceedings{ somenzi.00.cav,
|
||||
author = {Fabio Somenzi and Roderick Bloem},
|
||||
title = {Efficient {B\"u}chi Automata for {LTL} Formul{\ae}},
|
||||
author = {Fabio Somenzi and Roderick Bloem},
|
||||
title = {Efficient {B\"u}chi Automata for {LTL} Formul{\ae}},
|
||||
booktitle = {Proceedings of the 12th International Conference on
|
||||
Computer Aided Verification (CAV'00)},
|
||||
pages = {247--263},
|
||||
|
|
|
|||
|
|
@ -37,21 +37,21 @@ namespace spot
|
|||
{
|
||||
public:
|
||||
stats_bfs(const const_twa_ptr& a, twa_statistics& s)
|
||||
: twa_reachable_iterator_breadth_first(a), s_(s)
|
||||
: twa_reachable_iterator_breadth_first(a), s_(s)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
process_state(const state*, int, twa_succ_iterator*) override final
|
||||
{
|
||||
++s_.states;
|
||||
++s_.states;
|
||||
}
|
||||
|
||||
void
|
||||
process_link(const state*, int, const state*, int,
|
||||
const twa_succ_iterator*) override
|
||||
const twa_succ_iterator*) override
|
||||
{
|
||||
++s_.edges;
|
||||
++s_.edges;
|
||||
}
|
||||
|
||||
private:
|
||||
|
|
@ -62,38 +62,38 @@ namespace spot
|
|||
{
|
||||
public:
|
||||
sub_stats_bfs(const const_twa_ptr& a, twa_sub_statistics& s)
|
||||
: stats_bfs(a, s), s_(s), seen_(bddtrue)
|
||||
: stats_bfs(a, s), s_(s), seen_(bddtrue)
|
||||
{
|
||||
}
|
||||
|
||||
void
|
||||
process_link(const state*, int, const state*, int,
|
||||
const twa_succ_iterator* it) override
|
||||
const twa_succ_iterator* it) override
|
||||
{
|
||||
++s_.edges;
|
||||
++s_.edges;
|
||||
|
||||
bdd cond = it->cond();
|
||||
bdd newvars = bdd_exist(bdd_support(cond), seen_);
|
||||
if (newvars != bddtrue)
|
||||
{
|
||||
seen_ &= newvars;
|
||||
int count = 0;
|
||||
while (newvars != bddtrue)
|
||||
{
|
||||
++count;
|
||||
newvars = bdd_high(newvars);
|
||||
}
|
||||
// If we discover one new variable, that means that all
|
||||
// transitions we counted so far are actually double
|
||||
// subtransitions. If we have two new variables, they where
|
||||
// quadruple transitions, etc.
|
||||
s_.transitions <<= count;
|
||||
}
|
||||
while (cond != bddfalse)
|
||||
{
|
||||
cond -= bdd_satoneset(cond, seen_, bddtrue);
|
||||
++s_.transitions;
|
||||
}
|
||||
bdd cond = it->cond();
|
||||
bdd newvars = bdd_exist(bdd_support(cond), seen_);
|
||||
if (newvars != bddtrue)
|
||||
{
|
||||
seen_ &= newvars;
|
||||
int count = 0;
|
||||
while (newvars != bddtrue)
|
||||
{
|
||||
++count;
|
||||
newvars = bdd_high(newvars);
|
||||
}
|
||||
// If we discover one new variable, that means that all
|
||||
// transitions we counted so far are actually double
|
||||
// subtransitions. If we have two new variables, they where
|
||||
// quadruple transitions, etc.
|
||||
s_.transitions <<= count;
|
||||
}
|
||||
while (cond != bddfalse)
|
||||
{
|
||||
cond -= bdd_satoneset(cond, seen_, bddtrue);
|
||||
++s_.transitions;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
|
|
@ -153,7 +153,7 @@ namespace spot
|
|||
declare('p', &complete_);
|
||||
declare('r', &run_time_);
|
||||
declare('s', &states_);
|
||||
declare('S', &scc_); // Historical. Deprecated. Use %c instead.
|
||||
declare('S', &scc_); // Historical. Deprecated. Use %c instead.
|
||||
declare('t', &trans_);
|
||||
set_output(os);
|
||||
if (format)
|
||||
|
|
@ -162,23 +162,23 @@ namespace spot
|
|||
|
||||
std::ostream&
|
||||
stat_printer::print(const const_twa_graph_ptr& aut,
|
||||
formula f, double run_time)
|
||||
formula f, double run_time)
|
||||
{
|
||||
form_ = f;
|
||||
run_time_ = run_time;
|
||||
|
||||
if (has('t'))
|
||||
{
|
||||
twa_sub_statistics s = sub_stats_reachable(aut);
|
||||
states_ = s.states;
|
||||
edges_ = s.edges;
|
||||
trans_ = s.transitions;
|
||||
twa_sub_statistics s = sub_stats_reachable(aut);
|
||||
states_ = s.states;
|
||||
edges_ = s.edges;
|
||||
trans_ = s.transitions;
|
||||
}
|
||||
else if (has('s') || has('e'))
|
||||
{
|
||||
twa_sub_statistics s = sub_stats_reachable(aut);
|
||||
states_ = s.states;
|
||||
edges_ = s.edges;
|
||||
twa_sub_statistics s = sub_stats_reachable(aut);
|
||||
states_ = s.states;
|
||||
edges_ = s.edges;
|
||||
}
|
||||
|
||||
if (has('a'))
|
||||
|
|
@ -189,25 +189,25 @@ namespace spot
|
|||
|
||||
if (has('n'))
|
||||
{
|
||||
nondetstates_ = count_nondet_states(aut);
|
||||
deterministic_ = (nondetstates_ == 0);
|
||||
nondetstates_ = count_nondet_states(aut);
|
||||
deterministic_ = (nondetstates_ == 0);
|
||||
}
|
||||
else if (has('d'))
|
||||
{
|
||||
// This is more efficient than calling count_nondet_state().
|
||||
deterministic_ = is_deterministic(aut);
|
||||
// This is more efficient than calling count_nondet_state().
|
||||
deterministic_ = is_deterministic(aut);
|
||||
}
|
||||
|
||||
if (has('p'))
|
||||
{
|
||||
complete_ = is_complete(aut);
|
||||
complete_ = is_complete(aut);
|
||||
}
|
||||
|
||||
if (has('g'))
|
||||
{
|
||||
std::ostringstream os;
|
||||
os << aut->get_acceptance();
|
||||
gen_acc_ = os.str();
|
||||
std::ostringstream os;
|
||||
os << aut->get_acceptance();
|
||||
gen_acc_ = os.str();
|
||||
}
|
||||
|
||||
return format(format_);
|
||||
|
|
|
|||
|
|
@ -85,7 +85,7 @@ namespace spot
|
|||
/// to be output, and so is \a run_time).
|
||||
std::ostream&
|
||||
print(const const_twa_graph_ptr& aut, formula f = nullptr,
|
||||
double run_time = -1.);
|
||||
double run_time = -1.);
|
||||
|
||||
private:
|
||||
const char* format_;
|
||||
|
|
|
|||
|
|
@ -33,7 +33,7 @@ namespace spot
|
|||
// Create an scc_info if the user did not give one to us.
|
||||
bool need_si = !si;
|
||||
if (need_si)
|
||||
si = new scc_info(aut);
|
||||
si = new scc_info(aut);
|
||||
si->determine_unknown_acceptance();
|
||||
|
||||
bool is_inweak = true;
|
||||
|
|
@ -41,58 +41,58 @@ namespace spot
|
|||
bool is_term = true;
|
||||
unsigned n = si->scc_count();
|
||||
for (unsigned i = 0; i < n; ++i)
|
||||
{
|
||||
if (si->is_trivial(i))
|
||||
continue;
|
||||
bool first = true;
|
||||
acc_cond::mark_t m = 0U;
|
||||
if (is_weak)
|
||||
for (auto src: si->states_of(i))
|
||||
for (auto& t: aut->out(src))
|
||||
if (si->scc_of(t.dst) == i)
|
||||
{
|
||||
if (first)
|
||||
{
|
||||
first = false;
|
||||
m = t.acc;
|
||||
}
|
||||
else if (m != t.acc)
|
||||
{
|
||||
is_weak = false;
|
||||
if (!inweak)
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
if (!is_weak && si->is_accepting_scc(i))
|
||||
{
|
||||
assert(inweak);
|
||||
if (scc_has_rejecting_cycle(*si, i))
|
||||
{
|
||||
is_inweak = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (terminal && si->is_accepting_scc(i) && !is_complete_scc(*si, i))
|
||||
{
|
||||
is_term = false;
|
||||
if (!set)
|
||||
break;
|
||||
}
|
||||
}
|
||||
{
|
||||
if (si->is_trivial(i))
|
||||
continue;
|
||||
bool first = true;
|
||||
acc_cond::mark_t m = 0U;
|
||||
if (is_weak)
|
||||
for (auto src: si->states_of(i))
|
||||
for (auto& t: aut->out(src))
|
||||
if (si->scc_of(t.dst) == i)
|
||||
{
|
||||
if (first)
|
||||
{
|
||||
first = false;
|
||||
m = t.acc;
|
||||
}
|
||||
else if (m != t.acc)
|
||||
{
|
||||
is_weak = false;
|
||||
if (!inweak)
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
if (!is_weak && si->is_accepting_scc(i))
|
||||
{
|
||||
assert(inweak);
|
||||
if (scc_has_rejecting_cycle(*si, i))
|
||||
{
|
||||
is_inweak = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (terminal && si->is_accepting_scc(i) && !is_complete_scc(*si, i))
|
||||
{
|
||||
is_term = false;
|
||||
if (!set)
|
||||
break;
|
||||
}
|
||||
}
|
||||
exit:
|
||||
if (need_si)
|
||||
delete si;
|
||||
delete si;
|
||||
if (set)
|
||||
{
|
||||
if (terminal && is_term && is_weak)
|
||||
aut->prop_terminal(true);
|
||||
if (is_weak)
|
||||
aut->prop_weak(true);
|
||||
if (is_inweak)
|
||||
aut->prop_inherently_weak(true);
|
||||
}
|
||||
{
|
||||
if (terminal && is_term && is_weak)
|
||||
aut->prop_terminal(true);
|
||||
if (is_weak)
|
||||
aut->prop_weak(true);
|
||||
if (is_inweak)
|
||||
aut->prop_inherently_weak(true);
|
||||
}
|
||||
if (inweak)
|
||||
return is_inweak;
|
||||
return is_inweak;
|
||||
return is_weak && is_term;
|
||||
}
|
||||
}
|
||||
|
|
@ -113,7 +113,7 @@ namespace spot
|
|||
if (v.is_known())
|
||||
return v.is_true();
|
||||
return is_type_automaton<false>(std::const_pointer_cast<twa_graph>(aut),
|
||||
si);
|
||||
si);
|
||||
}
|
||||
|
||||
bool
|
||||
|
|
@ -135,11 +135,11 @@ namespace spot
|
|||
{
|
||||
if (!(aut->acc().is_buchi() || aut->acc().is_all()))
|
||||
throw std::runtime_error
|
||||
("is_safety_mwdba() should be called on a Buchi automaton");
|
||||
("is_safety_mwdba() should be called on a Buchi automaton");
|
||||
|
||||
for (auto& t: aut->edges())
|
||||
if (!aut->acc().accepting(t.acc))
|
||||
return false;
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
@ -149,7 +149,7 @@ namespace spot
|
|||
{
|
||||
if (keep_opt == nullptr || *keep_opt == 0)
|
||||
throw std::runtime_error
|
||||
(std::string("option for decompose_strength() should not be empty"));
|
||||
(std::string("option for decompose_strength() should not be empty"));
|
||||
|
||||
enum strength {
|
||||
Ignore = 0,
|
||||
|
|
@ -157,30 +157,30 @@ namespace spot
|
|||
WeakStrict = 2,
|
||||
Weak = Terminal | WeakStrict,
|
||||
Strong = 4,
|
||||
Needed = 8, // Needed SCCs are those that lead to
|
||||
// the SCCs we want to keep.
|
||||
Needed = 8, // Needed SCCs are those that lead to
|
||||
// the SCCs we want to keep.
|
||||
};
|
||||
unsigned char keep = Ignore;
|
||||
while (auto c = *keep_opt++)
|
||||
switch (c)
|
||||
{
|
||||
case 's':
|
||||
keep |= Strong;
|
||||
break;
|
||||
case 't':
|
||||
keep |= Terminal;
|
||||
break;
|
||||
case 'w':
|
||||
keep |= WeakStrict;
|
||||
break;
|
||||
default:
|
||||
throw std::runtime_error
|
||||
(std::string("unknown option for decompose_strength(): ") + c);
|
||||
}
|
||||
{
|
||||
case 's':
|
||||
keep |= Strong;
|
||||
break;
|
||||
case 't':
|
||||
keep |= Terminal;
|
||||
break;
|
||||
case 'w':
|
||||
keep |= WeakStrict;
|
||||
break;
|
||||
default:
|
||||
throw std::runtime_error
|
||||
(std::string("unknown option for decompose_strength(): ") + c);
|
||||
}
|
||||
|
||||
auto p = aut->acc().unsat_mark();
|
||||
bool all_accepting = !p.first;
|
||||
acc_cond::mark_t wacc = 0U; // Acceptance for weak SCCs
|
||||
acc_cond::mark_t wacc = 0U; // Acceptance for weak SCCs
|
||||
acc_cond::mark_t uacc = p.second; // Acceptance for "needed" SCCs, that
|
||||
// we only want to traverse.
|
||||
|
||||
|
|
@ -189,9 +189,9 @@ namespace spot
|
|||
// case syntactically) and not output any strong part.
|
||||
if (all_accepting)
|
||||
{
|
||||
keep &= ~Strong;
|
||||
if (keep == Ignore)
|
||||
return nullptr;
|
||||
keep &= ~Strong;
|
||||
if (keep == Ignore)
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
scc_info si(aut);
|
||||
|
|
@ -204,33 +204,33 @@ namespace spot
|
|||
|
||||
for (unsigned i = 0; i < n; ++i) // SCC are topologically ordered
|
||||
{
|
||||
if (si.is_accepting_scc(i))
|
||||
{
|
||||
if (all_accepting | is_inherently_weak_scc(si, i))
|
||||
{
|
||||
if (keep & Weak)
|
||||
{
|
||||
if ((keep & Weak) == Weak)
|
||||
want[i] = Weak;
|
||||
else
|
||||
want[i] = keep &
|
||||
(is_complete_scc(si, i) ? Terminal : WeakStrict);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
want[i] = keep & Strong;
|
||||
strong_seen = true;
|
||||
}
|
||||
nonempty |= want[i];
|
||||
}
|
||||
// An SCC is needed if one of its successor is.
|
||||
for (unsigned j: si.succ(i))
|
||||
if (want[j])
|
||||
{
|
||||
want[i] |= Needed;
|
||||
break;
|
||||
}
|
||||
if (si.is_accepting_scc(i))
|
||||
{
|
||||
if (all_accepting | is_inherently_weak_scc(si, i))
|
||||
{
|
||||
if (keep & Weak)
|
||||
{
|
||||
if ((keep & Weak) == Weak)
|
||||
want[i] = Weak;
|
||||
else
|
||||
want[i] = keep &
|
||||
(is_complete_scc(si, i) ? Terminal : WeakStrict);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
want[i] = keep & Strong;
|
||||
strong_seen = true;
|
||||
}
|
||||
nonempty |= want[i];
|
||||
}
|
||||
// An SCC is needed if one of its successor is.
|
||||
for (unsigned j: si.succ(i))
|
||||
if (want[j])
|
||||
{
|
||||
want[i] |= Needed;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!nonempty)
|
||||
|
|
@ -248,35 +248,35 @@ namespace spot
|
|||
auto fun = [&si, &want, uacc, wacc, keep]
|
||||
(unsigned src, bdd& cond, acc_cond::mark_t& acc, unsigned dst)
|
||||
{
|
||||
if (want[si.scc_of(dst)] == Ignore)
|
||||
{
|
||||
cond = bddfalse;
|
||||
return;
|
||||
}
|
||||
if (want[si.scc_of(src)] == Needed)
|
||||
{
|
||||
acc = uacc;
|
||||
return;
|
||||
}
|
||||
if (keep & Strong)
|
||||
return;
|
||||
acc = wacc;
|
||||
if (want[si.scc_of(dst)] == Ignore)
|
||||
{
|
||||
cond = bddfalse;
|
||||
return;
|
||||
}
|
||||
if (want[si.scc_of(src)] == Needed)
|
||||
{
|
||||
acc = uacc;
|
||||
return;
|
||||
}
|
||||
if (keep & Strong)
|
||||
return;
|
||||
acc = wacc;
|
||||
};
|
||||
|
||||
transform_accessible(aut, res, fun);
|
||||
|
||||
if (!(keep & Strong))
|
||||
{
|
||||
res->prop_weak(true);
|
||||
if (!(keep & WeakStrict))
|
||||
{
|
||||
assert(keep & Terminal);
|
||||
res->prop_terminal(true);
|
||||
}
|
||||
res->prop_weak(true);
|
||||
if (!(keep & WeakStrict))
|
||||
{
|
||||
assert(keep & Terminal);
|
||||
res->prop_terminal(true);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
res->prop_weak(!strong_seen);
|
||||
res->prop_weak(!strong_seen);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -59,7 +59,7 @@ namespace spot
|
|||
/// will be built otherwise).
|
||||
SPOT_API bool
|
||||
is_inherently_weak_automaton(const const_twa_graph_ptr& aut,
|
||||
scc_info* sm = nullptr);
|
||||
scc_info* sm = nullptr);
|
||||
|
||||
/// \brief Whether a minimized WDBA represents a safety property.
|
||||
///
|
||||
|
|
@ -105,19 +105,19 @@ namespace spot
|
|||
/** \verbatim
|
||||
@inproceedings{renault.13.tacas,
|
||||
author = {Etienne Renault and Alexandre Duret-Lutz and Fabrice
|
||||
Kordon and Denis Poitrenaud},
|
||||
Kordon and Denis Poitrenaud},
|
||||
title = {Strength-Based Decomposition of the Property {B\"u}chi
|
||||
Automaton for Faster Model Checking},
|
||||
Automaton for Faster Model Checking},
|
||||
booktitle = {Proceedings of the 19th International Conference on Tools
|
||||
and Algorithms for the Construction and Analysis of Systems
|
||||
(TACAS'13)},
|
||||
and Algorithms for the Construction and Analysis of Systems
|
||||
(TACAS'13)},
|
||||
editor = {Nir Piterman and Scott A. Smolka},
|
||||
year = {2013},
|
||||
month = mar,
|
||||
pages = {580--593},
|
||||
publisher = {Springer},
|
||||
series = {Lecture Notes in Computer Science},
|
||||
volume = {7795},
|
||||
year = {2013},
|
||||
month = mar,
|
||||
pages = {580--593},
|
||||
publisher = {Springer},
|
||||
series = {Lecture Notes in Computer Science},
|
||||
volume = {7795},
|
||||
doi = {10.1007/978-3-642-36742-7_42}
|
||||
}
|
||||
\endverbatim */
|
||||
|
|
|
|||
|
|
@ -27,7 +27,7 @@ namespace spot
|
|||
unsigned n = a->num_states();
|
||||
for (unsigned s = 0; s < n; ++s)
|
||||
for (auto& t: a->out(s))
|
||||
t.acc = 0U;
|
||||
t.acc = 0U;
|
||||
a->set_generalized_buchi(0);
|
||||
a->release_named_properties();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -98,7 +98,7 @@ namespace spot
|
|||
{
|
||||
public:
|
||||
twasl_succ_iterator(twa_succ_iterator* it, const state_tgbasl* state,
|
||||
bdd_dict_ptr d, bdd atomic_propositions)
|
||||
bdd_dict_ptr d, bdd atomic_propositions)
|
||||
: it_(it), state_(state), aps_(atomic_propositions), d_(d)
|
||||
{
|
||||
}
|
||||
|
|
@ -207,33 +207,33 @@ namespace spot
|
|||
{
|
||||
public:
|
||||
tgbasl(const const_twa_ptr& a, bdd atomic_propositions)
|
||||
: twa(a->get_dict()), a_(a), aps_(atomic_propositions)
|
||||
: twa(a->get_dict()), a_(a), aps_(atomic_propositions)
|
||||
{
|
||||
get_dict()->register_all_propositions_of(&a_, this);
|
||||
assert(num_sets() == 0);
|
||||
set_generalized_buchi(a_->num_sets());
|
||||
get_dict()->register_all_propositions_of(&a_, this);
|
||||
assert(num_sets() == 0);
|
||||
set_generalized_buchi(a_->num_sets());
|
||||
}
|
||||
|
||||
virtual const state* get_init_state() const override
|
||||
{
|
||||
return new state_tgbasl(a_->get_init_state(), bddfalse);
|
||||
return new state_tgbasl(a_->get_init_state(), bddfalse);
|
||||
}
|
||||
|
||||
virtual twa_succ_iterator* succ_iter(const state* state) const override
|
||||
{
|
||||
const state_tgbasl* s = down_cast<const state_tgbasl*>(state);
|
||||
assert(s);
|
||||
return new twasl_succ_iterator(a_->succ_iter(s->real_state()), s,
|
||||
a_->get_dict(), aps_);
|
||||
const state_tgbasl* s = down_cast<const state_tgbasl*>(state);
|
||||
assert(s);
|
||||
return new twasl_succ_iterator(a_->succ_iter(s->real_state()), s,
|
||||
a_->get_dict(), aps_);
|
||||
}
|
||||
|
||||
virtual std::string format_state(const state* state) const override
|
||||
{
|
||||
const state_tgbasl* s = down_cast<const state_tgbasl*>(state);
|
||||
assert(s);
|
||||
return (a_->format_state(s->real_state())
|
||||
+ ", "
|
||||
+ bdd_format_formula(a_->get_dict(), s->cond()));
|
||||
const state_tgbasl* s = down_cast<const state_tgbasl*>(state);
|
||||
assert(s);
|
||||
return (a_->format_state(s->real_state())
|
||||
+ ", "
|
||||
+ bdd_format_formula(a_->get_dict(), s->cond()));
|
||||
}
|
||||
|
||||
private:
|
||||
|
|
@ -257,13 +257,13 @@ namespace spot
|
|||
size_t
|
||||
operator()(const stutter_state& s) const
|
||||
{
|
||||
return wang32_hash(s.first) ^ wang32_hash(s.second.id());
|
||||
return wang32_hash(s.first) ^ wang32_hash(s.second.id());
|
||||
}
|
||||
};
|
||||
|
||||
// Associate the stutter state to its number.
|
||||
typedef std::unordered_map<stutter_state, unsigned,
|
||||
stutter_state_hash> ss2num_map;
|
||||
stutter_state_hash> ss2num_map;
|
||||
|
||||
// Queue of state to be processed.
|
||||
typedef std::deque<stutter_state> queue_t;
|
||||
|
|
@ -303,43 +303,43 @@ namespace spot
|
|||
|
||||
while (!todo.empty())
|
||||
{
|
||||
s = todo.front();
|
||||
todo.pop_front();
|
||||
unsigned src = ss2num[s];
|
||||
s = todo.front();
|
||||
todo.pop_front();
|
||||
unsigned src = ss2num[s];
|
||||
|
||||
bool self_loop_needed = true;
|
||||
bool self_loop_needed = true;
|
||||
|
||||
for (auto& t : a->out(s.first))
|
||||
{
|
||||
bdd all = t.cond;
|
||||
while (all != bddfalse)
|
||||
{
|
||||
bdd one = bdd_satoneset(all, atomic_propositions, bddtrue);
|
||||
all -= one;
|
||||
for (auto& t : a->out(s.first))
|
||||
{
|
||||
bdd all = t.cond;
|
||||
while (all != bddfalse)
|
||||
{
|
||||
bdd one = bdd_satoneset(all, atomic_propositions, bddtrue);
|
||||
all -= one;
|
||||
|
||||
stutter_state d(t.dst, one);
|
||||
stutter_state d(t.dst, one);
|
||||
|
||||
auto r = ss2num.emplace(d, ss2num.size());
|
||||
unsigned dest = r.first->second;
|
||||
auto r = ss2num.emplace(d, ss2num.size());
|
||||
unsigned dest = r.first->second;
|
||||
|
||||
if (r.second)
|
||||
{
|
||||
todo.push_back(d);
|
||||
unsigned u = res->new_state();
|
||||
assert(u == dest);
|
||||
(void)u;
|
||||
}
|
||||
if (r.second)
|
||||
{
|
||||
todo.push_back(d);
|
||||
unsigned u = res->new_state();
|
||||
assert(u == dest);
|
||||
(void)u;
|
||||
}
|
||||
|
||||
// Create the edge.
|
||||
res->new_edge(src, dest, one, t.acc);
|
||||
// Create the edge.
|
||||
res->new_edge(src, dest, one, t.acc);
|
||||
|
||||
if (src == dest)
|
||||
self_loop_needed = false;
|
||||
}
|
||||
}
|
||||
if (src == dest)
|
||||
self_loop_needed = false;
|
||||
}
|
||||
}
|
||||
|
||||
if (self_loop_needed && s.second != bddfalse)
|
||||
res->new_edge(src, src, s.second, 0U);
|
||||
if (self_loop_needed && s.second != bddfalse)
|
||||
res->new_edge(src, src, s.second, 0U);
|
||||
}
|
||||
res->merge_edges();
|
||||
return res;
|
||||
|
|
@ -358,53 +358,53 @@ namespace spot
|
|||
// state.
|
||||
for (auto& t: a->edges())
|
||||
if (t.src == t.dst)
|
||||
selfloops[t.src] |= t.cond;
|
||||
selfloops[t.src] |= t.cond;
|
||||
for (unsigned t = 1; t <= num_edges; ++t)
|
||||
{
|
||||
auto& td = a->edge_storage(t);
|
||||
if (a->is_dead_edge(td))
|
||||
continue;
|
||||
auto& td = a->edge_storage(t);
|
||||
if (a->is_dead_edge(td))
|
||||
continue;
|
||||
|
||||
unsigned src = td.src;
|
||||
unsigned dst = td.dst;
|
||||
if (src != dst)
|
||||
{
|
||||
bdd all = td.cond;
|
||||
// If there is a self-loop with the whole condition on
|
||||
// either end of the edge, do not bother with it.
|
||||
if (bdd_implies(all, selfloops[src])
|
||||
|| bdd_implies(all, selfloops[dst]))
|
||||
continue;
|
||||
// Do not use td in the loop because the new_edge()
|
||||
// might invalidate it.
|
||||
auto acc = td.acc;
|
||||
while (all != bddfalse)
|
||||
{
|
||||
bdd one = bdd_satoneset(all, atomic_propositions, bddtrue);
|
||||
all -= one;
|
||||
// Skip if there is a loop for this particular letter.
|
||||
if (bdd_implies(one, selfloops[src])
|
||||
|| bdd_implies(one, selfloops[dst]))
|
||||
continue;
|
||||
auto p = newstates.emplace(std::make_pair(dst, one.id()), 0);
|
||||
if (p.second)
|
||||
p.first->second = a->new_state();
|
||||
unsigned tmp = p.first->second; // intermediate state
|
||||
unsigned i = a->new_edge(src, tmp, one, acc);
|
||||
assert(i > num_edges);
|
||||
i = a->new_edge(tmp, tmp, one, 0U);
|
||||
assert(i > num_edges);
|
||||
// No acceptance here to preserve the state-based property.
|
||||
i = a->new_edge(tmp, dst, one, 0U);
|
||||
assert(i > num_edges);
|
||||
(void)i;
|
||||
}
|
||||
}
|
||||
unsigned src = td.src;
|
||||
unsigned dst = td.dst;
|
||||
if (src != dst)
|
||||
{
|
||||
bdd all = td.cond;
|
||||
// If there is a self-loop with the whole condition on
|
||||
// either end of the edge, do not bother with it.
|
||||
if (bdd_implies(all, selfloops[src])
|
||||
|| bdd_implies(all, selfloops[dst]))
|
||||
continue;
|
||||
// Do not use td in the loop because the new_edge()
|
||||
// might invalidate it.
|
||||
auto acc = td.acc;
|
||||
while (all != bddfalse)
|
||||
{
|
||||
bdd one = bdd_satoneset(all, atomic_propositions, bddtrue);
|
||||
all -= one;
|
||||
// Skip if there is a loop for this particular letter.
|
||||
if (bdd_implies(one, selfloops[src])
|
||||
|| bdd_implies(one, selfloops[dst]))
|
||||
continue;
|
||||
auto p = newstates.emplace(std::make_pair(dst, one.id()), 0);
|
||||
if (p.second)
|
||||
p.first->second = a->new_state();
|
||||
unsigned tmp = p.first->second; // intermediate state
|
||||
unsigned i = a->new_edge(src, tmp, one, acc);
|
||||
assert(i > num_edges);
|
||||
i = a->new_edge(tmp, tmp, one, 0U);
|
||||
assert(i > num_edges);
|
||||
// No acceptance here to preserve the state-based property.
|
||||
i = a->new_edge(tmp, dst, one, 0U);
|
||||
assert(i > num_edges);
|
||||
(void)i;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (num_states != a->num_states())
|
||||
a->prop_keep({true, // state_based
|
||||
false, // inherently_weak
|
||||
false, // deterministic
|
||||
a->prop_keep({true, // state_based
|
||||
false, // inherently_weak
|
||||
false, // deterministic
|
||||
false, // stutter inv.
|
||||
});
|
||||
a->merge_edges();
|
||||
|
|
@ -415,16 +415,16 @@ namespace spot
|
|||
sl2(const const_twa_graph_ptr& a, bdd atomic_propositions)
|
||||
{
|
||||
return sl2(make_twa_graph(a, twa::prop_set::all()),
|
||||
atomic_propositions);
|
||||
atomic_propositions);
|
||||
}
|
||||
|
||||
|
||||
twa_graph_ptr
|
||||
closure(twa_graph_ptr&& a)
|
||||
{
|
||||
a->prop_keep({false, // state_based
|
||||
false, // inherently_weak
|
||||
false, // deterministic
|
||||
a->prop_keep({false, // state_based
|
||||
false, // inherently_weak
|
||||
false, // deterministic
|
||||
false, // stutter inv.
|
||||
});
|
||||
|
||||
|
|
@ -434,26 +434,26 @@ namespace spot
|
|||
|
||||
for (unsigned state = 0; state < n; ++state)
|
||||
{
|
||||
auto trans = a->out(state);
|
||||
auto trans = a->out(state);
|
||||
|
||||
for (auto it = trans.begin(); it != trans.end(); ++it)
|
||||
{
|
||||
todo.push_back(it.trans());
|
||||
dst2trans[it->dst].push_back(it.trans());
|
||||
}
|
||||
for (auto it = trans.begin(); it != trans.end(); ++it)
|
||||
{
|
||||
todo.push_back(it.trans());
|
||||
dst2trans[it->dst].push_back(it.trans());
|
||||
}
|
||||
|
||||
while (!todo.empty())
|
||||
{
|
||||
auto t1 = a->edge_storage(todo.back());
|
||||
todo.pop_back();
|
||||
while (!todo.empty())
|
||||
{
|
||||
auto t1 = a->edge_storage(todo.back());
|
||||
todo.pop_back();
|
||||
|
||||
for (auto& t2 : a->out(t1.dst))
|
||||
{
|
||||
bdd cond = t1.cond & t2.cond;
|
||||
if (cond != bddfalse)
|
||||
{
|
||||
for (auto& t2 : a->out(t1.dst))
|
||||
{
|
||||
bdd cond = t1.cond & t2.cond;
|
||||
if (cond != bddfalse)
|
||||
{
|
||||
bool need_new_trans = true;
|
||||
acc_cond::mark_t acc = t1.acc | t2.acc;
|
||||
acc_cond::mark_t acc = t1.acc | t2.acc;
|
||||
for (auto& t: dst2trans[t2.dst])
|
||||
{
|
||||
auto& ts = a->edge_storage(t);
|
||||
|
|
@ -467,34 +467,34 @@ namespace spot
|
|||
todo.push_back(t);
|
||||
}
|
||||
need_new_trans = false;
|
||||
break;
|
||||
break;
|
||||
}
|
||||
else if (cond == ts.cond)
|
||||
{
|
||||
acc |= ts.acc;
|
||||
if (ts.acc != acc)
|
||||
{
|
||||
ts.acc = acc;
|
||||
if (std::find(todo.begin(), todo.end(), t)
|
||||
== todo.end())
|
||||
todo.push_back(t);
|
||||
}
|
||||
else if (cond == ts.cond)
|
||||
{
|
||||
acc |= ts.acc;
|
||||
if (ts.acc != acc)
|
||||
{
|
||||
ts.acc = acc;
|
||||
if (std::find(todo.begin(), todo.end(), t)
|
||||
== todo.end())
|
||||
todo.push_back(t);
|
||||
}
|
||||
need_new_trans = false;
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (need_new_trans)
|
||||
{
|
||||
// Load t2.dst first, because t2 can be
|
||||
// invalidated by new_edge().
|
||||
auto dst = t2.dst;
|
||||
// Load t2.dst first, because t2 can be
|
||||
// invalidated by new_edge().
|
||||
auto dst = t2.dst;
|
||||
auto i = a->new_edge(state, dst, cond, acc);
|
||||
dst2trans[dst].push_back(i);
|
||||
todo.push_back(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for (auto& it: dst2trans)
|
||||
it.clear();
|
||||
}
|
||||
|
|
@ -514,16 +514,16 @@ namespace spot
|
|||
static const char* stutter_check = getenv("SPOT_STUTTER_CHECK");
|
||||
if (stutter_check)
|
||||
{
|
||||
char* endptr;
|
||||
long res = strtol(stutter_check, &endptr, 10);
|
||||
if (*endptr || res < 0 || res > 9)
|
||||
throw
|
||||
std::runtime_error("invalid value for SPOT_STUTTER_CHECK.");
|
||||
return res;
|
||||
char* endptr;
|
||||
long res = strtol(stutter_check, &endptr, 10);
|
||||
if (*endptr || res < 0 || res > 9)
|
||||
throw
|
||||
std::runtime_error("invalid value for SPOT_STUTTER_CHECK.");
|
||||
return res;
|
||||
}
|
||||
else
|
||||
{
|
||||
return 8; // The best variant, according to our benchmarks.
|
||||
return 8; // The best variant, according to our benchmarks.
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -538,23 +538,23 @@ namespace spot
|
|||
if (algo == 0 || algo == 9)
|
||||
// Etessami's check via syntactic transformation.
|
||||
{
|
||||
if (!f.is_ltl_formula())
|
||||
throw std::runtime_error("Cannot use the syntactic "
|
||||
"stutter-invariance check "
|
||||
"for non-LTL formulas");
|
||||
formula g = remove_x(f);
|
||||
bool res;
|
||||
if (algo == 0) // Equivalence check
|
||||
{
|
||||
tl_simplifier ls;
|
||||
res = ls.are_equivalent(f, g);
|
||||
}
|
||||
else
|
||||
{
|
||||
formula h = formula::Xor(f, g);
|
||||
res = ltl_to_tgba_fm(h, make_bdd_dict())->is_empty();
|
||||
}
|
||||
return res;
|
||||
if (!f.is_ltl_formula())
|
||||
throw std::runtime_error("Cannot use the syntactic "
|
||||
"stutter-invariance check "
|
||||
"for non-LTL formulas");
|
||||
formula g = remove_x(f);
|
||||
bool res;
|
||||
if (algo == 0) // Equivalence check
|
||||
{
|
||||
tl_simplifier ls;
|
||||
res = ls.are_equivalent(f, g);
|
||||
}
|
||||
else
|
||||
{
|
||||
formula h = formula::Xor(f, g);
|
||||
res = ltl_to_tgba_fm(h, make_bdd_dict())->is_empty();
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
// Prepare for an automata-based check.
|
||||
|
|
@ -575,33 +575,33 @@ namespace spot
|
|||
switch (algo)
|
||||
{
|
||||
case 1: // sl(aut_f) x sl(aut_nf)
|
||||
return product(sl(std::move(aut_f), aps),
|
||||
sl(std::move(aut_nf), aps))->is_empty();
|
||||
return product(sl(std::move(aut_f), aps),
|
||||
sl(std::move(aut_nf), aps))->is_empty();
|
||||
case 2: // sl(cl(aut_f)) x aut_nf
|
||||
return product(sl(closure(std::move(aut_f)), aps),
|
||||
std::move(aut_nf))->is_empty();
|
||||
return product(sl(closure(std::move(aut_f)), aps),
|
||||
std::move(aut_nf))->is_empty();
|
||||
case 3: // (cl(sl(aut_f)) x aut_nf
|
||||
return product(closure(sl(std::move(aut_f), aps)),
|
||||
std::move(aut_nf))->is_empty();
|
||||
return product(closure(sl(std::move(aut_f), aps)),
|
||||
std::move(aut_nf))->is_empty();
|
||||
case 4: // sl2(aut_f) x sl2(aut_nf)
|
||||
return product(sl2(std::move(aut_f), aps),
|
||||
sl2(std::move(aut_nf), aps))->is_empty();
|
||||
return product(sl2(std::move(aut_f), aps),
|
||||
sl2(std::move(aut_nf), aps))->is_empty();
|
||||
case 5: // sl2(cl(aut_f)) x aut_nf
|
||||
return product(sl2(closure(std::move(aut_f)), aps),
|
||||
std::move(aut_nf))->is_empty();
|
||||
return product(sl2(closure(std::move(aut_f)), aps),
|
||||
std::move(aut_nf))->is_empty();
|
||||
case 6: // (cl(sl2(aut_f)) x aut_nf
|
||||
return product(closure(sl2(std::move(aut_f), aps)),
|
||||
std::move(aut_nf))->is_empty();
|
||||
return product(closure(sl2(std::move(aut_f), aps)),
|
||||
std::move(aut_nf))->is_empty();
|
||||
case 7: // on-the-fly sl(aut_f) x sl(aut_nf)
|
||||
return otf_product(make_tgbasl(aut_f, aps),
|
||||
make_tgbasl(aut_nf, aps))->is_empty();
|
||||
return otf_product(make_tgbasl(aut_f, aps),
|
||||
make_tgbasl(aut_nf, aps))->is_empty();
|
||||
case 8: // cl(aut_f) x cl(aut_nf)
|
||||
return product(closure(std::move(aut_f)),
|
||||
closure(std::move(aut_nf)))->is_empty();
|
||||
return product(closure(std::move(aut_f)),
|
||||
closure(std::move(aut_nf)))->is_empty();
|
||||
default:
|
||||
throw std::runtime_error("invalid algorithm number for "
|
||||
"is_stutter_invariant()");
|
||||
SPOT_UNREACHABLE();
|
||||
throw std::runtime_error("invalid algorithm number for "
|
||||
"is_stutter_invariant()");
|
||||
SPOT_UNREACHABLE();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -615,20 +615,20 @@ namespace spot
|
|||
twa_graph_ptr neg = nullptr;
|
||||
if (f)
|
||||
{
|
||||
neg = translator(aut->get_dict()).run(formula::Not(f));
|
||||
neg = translator(aut->get_dict()).run(formula::Not(f));
|
||||
}
|
||||
else
|
||||
{
|
||||
// If the automaton is deterministic, we
|
||||
// know how to complement it.
|
||||
aut->prop_deterministic(is_deterministic(aut));
|
||||
if (!aut->prop_deterministic())
|
||||
return trival::maybe();
|
||||
neg = remove_fin(dtwa_complement(aut));
|
||||
// If the automaton is deterministic, we
|
||||
// know how to complement it.
|
||||
aut->prop_deterministic(is_deterministic(aut));
|
||||
if (!aut->prop_deterministic())
|
||||
return trival::maybe();
|
||||
neg = remove_fin(dtwa_complement(aut));
|
||||
}
|
||||
|
||||
is_stut = is_stutter_invariant(make_twa_graph(aut, twa::prop_set::all()),
|
||||
std::move(neg), aut->ap_vars());
|
||||
std::move(neg), aut->ap_vars());
|
||||
aut->prop_stutter_invariant(is_stut);
|
||||
return is_stut;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -55,8 +55,8 @@ namespace spot
|
|||
|
||||
SPOT_API bool
|
||||
is_stutter_invariant(twa_graph_ptr&& aut_f,
|
||||
twa_graph_ptr&& aut_nf, bdd aps,
|
||||
int algo = 0);
|
||||
twa_graph_ptr&& aut_nf, bdd aps,
|
||||
int algo = 0);
|
||||
|
||||
/// \brief Check whether \a aut is stutter-invariant
|
||||
///
|
||||
|
|
@ -68,5 +68,5 @@ namespace spot
|
|||
/// the result will be returned as trival::maybe().
|
||||
SPOT_API trival
|
||||
check_stutter_invariance(const twa_graph_ptr& aut,
|
||||
formula f = nullptr);
|
||||
formula f = nullptr);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -91,8 +91,8 @@ namespace spot
|
|||
inc_states();
|
||||
h.add_new_state(s0, BLUE);
|
||||
push(st_blue, s0, bddfalse, 0U);
|
||||
auto t = std::static_pointer_cast<tau03_search>
|
||||
(this->emptiness_check::shared_from_this());
|
||||
auto t = std::static_pointer_cast<tau03_search>
|
||||
(this->emptiness_check::shared_from_this());
|
||||
if (dfs_blue())
|
||||
return std::make_shared<ndfs_result<tau03_search<heap>, heap>>(t);
|
||||
return nullptr;
|
||||
|
|
@ -123,7 +123,7 @@ namespace spot
|
|||
|
||||
private:
|
||||
void push(stack_type& st, const state* s,
|
||||
const bdd& label, acc_cond::mark_t acc)
|
||||
const bdd& label, acc_cond::mark_t acc)
|
||||
{
|
||||
inc_depth();
|
||||
twa_succ_iterator* i = a_->succ_iter(s);
|
||||
|
|
@ -187,7 +187,7 @@ namespace spot
|
|||
<< std::endl;
|
||||
typename heap::color_ref c = h.get_color_ref(f.s);
|
||||
assert(!c.is_white());
|
||||
for (auto i: a_->succ(f.s))
|
||||
for (auto i: a_->succ(f.s))
|
||||
{
|
||||
inc_transitions();
|
||||
const state *s_prime = i->dst();
|
||||
|
|
@ -198,7 +198,7 @@ namespace spot
|
|||
auto acc = i->acc();
|
||||
typename heap::color_ref c_prime = h.get_color_ref(s_prime);
|
||||
assert(!c_prime.is_white());
|
||||
auto acu = acc | c.get_acc();
|
||||
auto acu = acc | c.get_acc();
|
||||
if ((c_prime.get_acc() & acu) != acu)
|
||||
{
|
||||
trace << " a propagation is needed, go down"
|
||||
|
|
@ -250,7 +250,7 @@ namespace spot
|
|||
trace << " It is white, pop it" << std::endl;
|
||||
s_prime->destroy();
|
||||
}
|
||||
else if ((c_prime.get_acc() & acu) != acu)
|
||||
else if ((c_prime.get_acc() & acu) != acu)
|
||||
{
|
||||
trace << " It is blue and propagation "
|
||||
<< "is needed, go down" << std::endl;
|
||||
|
|
@ -302,7 +302,7 @@ namespace spot
|
|||
void cumulate_acc(acc_cond::mark_t a)
|
||||
{
|
||||
assert(!is_white());
|
||||
*acc |= a;
|
||||
*acc |= a;
|
||||
}
|
||||
bool is_white() const
|
||||
{
|
||||
|
|
@ -310,7 +310,7 @@ namespace spot
|
|||
}
|
||||
private:
|
||||
color *p;
|
||||
acc_cond::mark_t* acc;
|
||||
acc_cond::mark_t* acc;
|
||||
};
|
||||
|
||||
explicit_tau03_search_heap(size_t)
|
||||
|
|
@ -346,8 +346,8 @@ namespace spot
|
|||
{
|
||||
assert(h.find(s) == h.end());
|
||||
h.emplace(std::piecewise_construct,
|
||||
std::forward_as_tuple(s),
|
||||
std::forward_as_tuple(c, 0U));
|
||||
std::forward_as_tuple(s),
|
||||
std::forward_as_tuple(c, 0U));
|
||||
}
|
||||
|
||||
void pop_notify(const state*) const
|
||||
|
|
|
|||
|
|
@ -69,10 +69,10 @@ namespace spot
|
|||
: emptiness_check(a, o),
|
||||
current_weight(a->acc()),
|
||||
h(size),
|
||||
use_condition_stack(o.get("condstack")),
|
||||
use_ordering(use_condition_stack && o.get("ordering")),
|
||||
use_weights(o.get("weights", 1)),
|
||||
use_red_weights(use_weights && o.get("redweights", 1))
|
||||
use_condition_stack(o.get("condstack")),
|
||||
use_ordering(use_condition_stack && o.get("ordering")),
|
||||
use_weights(o.get("weights", 1)),
|
||||
use_red_weights(use_weights && o.get("redweights", 1))
|
||||
{
|
||||
}
|
||||
|
||||
|
|
@ -106,8 +106,8 @@ namespace spot
|
|||
inc_states();
|
||||
h.add_new_state(s0, CYAN, current_weight);
|
||||
push(st_blue, s0, bddfalse, 0U);
|
||||
auto t = std::static_pointer_cast<tau03_opt_search>
|
||||
(this->emptiness_check::shared_from_this());
|
||||
auto t = std::static_pointer_cast<tau03_opt_search>
|
||||
(this->emptiness_check::shared_from_this());
|
||||
if (dfs_blue())
|
||||
return std::make_shared<ndfs_result<tau03_opt_search<heap>, heap>>(t);
|
||||
return nullptr;
|
||||
|
|
@ -138,7 +138,7 @@ namespace spot
|
|||
|
||||
private:
|
||||
void push(stack_type& st, const state* s,
|
||||
const bdd& label, acc_cond::mark_t acc)
|
||||
const bdd& label, acc_cond::mark_t acc)
|
||||
{
|
||||
inc_depth();
|
||||
twa_succ_iterator* i = a_->succ_iter(s);
|
||||
|
|
@ -155,14 +155,14 @@ namespace spot
|
|||
|
||||
acc_cond::mark_t project_acc(acc_cond::mark_t acc) const
|
||||
{
|
||||
if (!use_ordering)
|
||||
return acc;
|
||||
// FIXME: This should be improved.
|
||||
std::vector<unsigned> res;
|
||||
unsigned max = a_->num_sets();
|
||||
for (unsigned n = 0; n < max && acc.has(n); ++n)
|
||||
res.push_back(n);
|
||||
return acc_cond::mark_t(res.begin(), res.end());
|
||||
if (!use_ordering)
|
||||
return acc;
|
||||
// FIXME: This should be improved.
|
||||
std::vector<unsigned> res;
|
||||
unsigned max = a_->num_sets();
|
||||
for (unsigned n = 0; n < max && acc.has(n); ++n)
|
||||
res.push_back(n);
|
||||
return acc_cond::mark_t(res.begin(), res.end());
|
||||
}
|
||||
|
||||
/// \brief weight of the state on top of the blue stack.
|
||||
|
|
@ -208,8 +208,8 @@ namespace spot
|
|||
if (c_prime.is_white())
|
||||
{
|
||||
trace << " It is white, go down" << std::endl;
|
||||
if (use_weights)
|
||||
current_weight.add(acc);
|
||||
if (use_weights)
|
||||
current_weight.add(acc);
|
||||
inc_states();
|
||||
h.add_new_state(s_prime, CYAN, current_weight);
|
||||
push(st_blue, s_prime, label, acc);
|
||||
|
|
@ -219,9 +219,9 @@ namespace spot
|
|||
typename heap::color_ref c = h.get_color_ref(f.s);
|
||||
assert(!c.is_white());
|
||||
if (c_prime.get_color() == CYAN
|
||||
&& a_->acc().accepting
|
||||
(current_weight.diff(a_->acc(), c_prime. get_weight())
|
||||
| c.get_acc() | acc | c_prime.get_acc()))
|
||||
&& a_->acc().accepting
|
||||
(current_weight.diff(a_->acc(), c_prime. get_weight())
|
||||
| c.get_acc() | acc | c_prime.get_acc()))
|
||||
{
|
||||
trace << " It is cyan and acceptance condition "
|
||||
<< "is reached, report cycle" << std::endl;
|
||||
|
|
@ -259,8 +259,8 @@ namespace spot
|
|||
trace << " All the successors have been visited" << std::endl;
|
||||
stack_item f_dest(f);
|
||||
pop(st_blue);
|
||||
if (use_weights)
|
||||
current_weight.sub(f_dest.acc);
|
||||
if (use_weights)
|
||||
current_weight.sub(f_dest.acc);
|
||||
typename heap::color_ref c_prime = h.get_color_ref(f_dest.s);
|
||||
assert(!c_prime.is_white());
|
||||
c_prime.set_color(BLUE);
|
||||
|
|
@ -303,11 +303,11 @@ namespace spot
|
|||
{
|
||||
assert(!st_red.empty());
|
||||
|
||||
// These are useful only when USE_CONDITION_STACK is set.
|
||||
typedef std::pair<acc_cond::mark_t, unsigned> cond_level;
|
||||
std::stack<cond_level> condition_stack;
|
||||
unsigned depth = 1;
|
||||
condition_stack.emplace(0U, 0);
|
||||
// These are useful only when USE_CONDITION_STACK is set.
|
||||
typedef std::pair<acc_cond::mark_t, unsigned> cond_level;
|
||||
std::stack<cond_level> condition_stack;
|
||||
unsigned depth = 1;
|
||||
condition_stack.emplace(0U, 0);
|
||||
|
||||
while (!st_red.empty())
|
||||
{
|
||||
|
|
@ -330,49 +330,49 @@ namespace spot
|
|||
s_prime->destroy();
|
||||
continue;
|
||||
}
|
||||
else if (c_prime.get_color() == CYAN &&
|
||||
a_->acc().accepting
|
||||
(acc | acu | c_prime.get_acc() |
|
||||
(use_red_weights ?
|
||||
current_weight.diff(a_->acc(),
|
||||
c_prime.
|
||||
get_weight())
|
||||
: acc_cond::mark_t(0U))))
|
||||
{
|
||||
trace << " It is cyan and acceptance condition "
|
||||
<< "is reached, report cycle" << std::endl;
|
||||
c_prime.cumulate_acc(a_->acc().all_sets());
|
||||
push(st_red, s_prime, label, acc);
|
||||
return true;
|
||||
}
|
||||
acc_cond::mark_t acp;
|
||||
if (use_ordering)
|
||||
acp = project_acc(acu | acc | c_prime.get_acc());
|
||||
else if (use_condition_stack)
|
||||
acp = acu | acc;
|
||||
else
|
||||
acp = acu;
|
||||
if ((c_prime.get_acc() & acp) != acp)
|
||||
{
|
||||
trace << " It is cyan or blue and propagation "
|
||||
<< "is needed, go down"
|
||||
<< std::endl;
|
||||
c_prime.cumulate_acc(acp);
|
||||
push(st_red, s_prime, label, acc);
|
||||
if (use_condition_stack)
|
||||
{
|
||||
auto old = acu;
|
||||
acu |= acc;
|
||||
condition_stack.emplace(acu - old, depth);
|
||||
}
|
||||
++depth;
|
||||
}
|
||||
else
|
||||
{
|
||||
trace << " It is cyan or blue and no propagation "
|
||||
<< "is needed , pop it" << std::endl;
|
||||
h.pop_notify(s_prime);
|
||||
}
|
||||
else if (c_prime.get_color() == CYAN &&
|
||||
a_->acc().accepting
|
||||
(acc | acu | c_prime.get_acc() |
|
||||
(use_red_weights ?
|
||||
current_weight.diff(a_->acc(),
|
||||
c_prime.
|
||||
get_weight())
|
||||
: acc_cond::mark_t(0U))))
|
||||
{
|
||||
trace << " It is cyan and acceptance condition "
|
||||
<< "is reached, report cycle" << std::endl;
|
||||
c_prime.cumulate_acc(a_->acc().all_sets());
|
||||
push(st_red, s_prime, label, acc);
|
||||
return true;
|
||||
}
|
||||
acc_cond::mark_t acp;
|
||||
if (use_ordering)
|
||||
acp = project_acc(acu | acc | c_prime.get_acc());
|
||||
else if (use_condition_stack)
|
||||
acp = acu | acc;
|
||||
else
|
||||
acp = acu;
|
||||
if ((c_prime.get_acc() & acp) != acp)
|
||||
{
|
||||
trace << " It is cyan or blue and propagation "
|
||||
<< "is needed, go down"
|
||||
<< std::endl;
|
||||
c_prime.cumulate_acc(acp);
|
||||
push(st_red, s_prime, label, acc);
|
||||
if (use_condition_stack)
|
||||
{
|
||||
auto old = acu;
|
||||
acu |= acc;
|
||||
condition_stack.emplace(acu - old, depth);
|
||||
}
|
||||
++depth;
|
||||
}
|
||||
else
|
||||
{
|
||||
trace << " It is cyan or blue and no propagation "
|
||||
<< "is needed , pop it" << std::endl;
|
||||
h.pop_notify(s_prime);
|
||||
}
|
||||
}
|
||||
else // Backtrack
|
||||
{
|
||||
|
|
@ -380,16 +380,16 @@ namespace spot
|
|||
<< std::endl;
|
||||
h.pop_notify(f.s);
|
||||
pop(st_red);
|
||||
--depth;
|
||||
if (condition_stack.top().second == depth)
|
||||
{
|
||||
acu -= condition_stack.top().first;
|
||||
condition_stack.pop();
|
||||
}
|
||||
--depth;
|
||||
if (condition_stack.top().second == depth)
|
||||
{
|
||||
acu -= condition_stack.top().first;
|
||||
condition_stack.pop();
|
||||
}
|
||||
}
|
||||
}
|
||||
assert(depth == 0);
|
||||
assert(condition_stack.empty());
|
||||
assert(depth == 0);
|
||||
assert(condition_stack.empty());
|
||||
return false;
|
||||
}
|
||||
|
||||
|
|
@ -404,7 +404,7 @@ namespace spot
|
|||
{
|
||||
public:
|
||||
color_ref(hash_type* h, hcyan_type* hc, const state* s,
|
||||
const weight* w, acc_cond::mark_t* a)
|
||||
const weight* w, acc_cond::mark_t* a)
|
||||
: is_cyan(true), w(w), ph(h), phc(hc), ps(s), acc(a)
|
||||
{
|
||||
}
|
||||
|
|
@ -442,7 +442,7 @@ namespace spot
|
|||
*pc=c;
|
||||
}
|
||||
}
|
||||
acc_cond::mark_t get_acc() const
|
||||
acc_cond::mark_t get_acc() const
|
||||
{
|
||||
assert(!is_white());
|
||||
return *acc;
|
||||
|
|
@ -463,8 +463,8 @@ namespace spot
|
|||
hcyan_type* phc; // point to the hash table hcyan
|
||||
const state* ps; // point to the state in hcyan
|
||||
color *pc; // point to the color of a state stored in main hash table
|
||||
acc_cond::mark_t* acc; // point to the acc set of a state stored
|
||||
// in main hash table or hcyan
|
||||
acc_cond::mark_t* acc; // point to the acc set of a state stored
|
||||
// in main hash table or hcyan
|
||||
};
|
||||
|
||||
explicit_tau03_opt_search_heap(size_t)
|
||||
|
|
@ -513,7 +513,7 @@ namespace spot
|
|||
}
|
||||
// cyan state
|
||||
return color_ref(&h, &hc, ic->first,
|
||||
&ic->second.first, &ic->second.second);
|
||||
&ic->second.first, &ic->second.second);
|
||||
}
|
||||
|
||||
void add_new_state(const state* s, color c, const weight& w)
|
||||
|
|
@ -522,8 +522,8 @@ namespace spot
|
|||
assert(c == CYAN);
|
||||
(void)c;
|
||||
hc.emplace(std::piecewise_construct,
|
||||
std::forward_as_tuple(s),
|
||||
std::forward_as_tuple(w, 0U));
|
||||
std::forward_as_tuple(s),
|
||||
std::forward_as_tuple(w, 0U));
|
||||
}
|
||||
|
||||
void pop_notify(const state*) const
|
||||
|
|
@ -562,7 +562,7 @@ namespace spot
|
|||
{
|
||||
return
|
||||
std::make_shared<tau03_opt_search<explicit_tau03_opt_search_heap>>(a,
|
||||
0, o);
|
||||
0, o);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
|||
|
|
@ -98,7 +98,7 @@ namespace spot
|
|||
///
|
||||
SPOT_API emptiness_check_ptr
|
||||
explicit_tau03_opt_search(const const_twa_ptr& a,
|
||||
option_map o = option_map());
|
||||
option_map o = option_map());
|
||||
|
||||
/// @}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,7 +35,7 @@ namespace spot
|
|||
unsigned s;
|
||||
|
||||
st2gba_state(unsigned st, acc_cond::mark_t bv = -1U):
|
||||
pend(bv), s(st)
|
||||
pend(bv), s(st)
|
||||
{
|
||||
}
|
||||
};
|
||||
|
|
@ -45,8 +45,8 @@ namespace spot
|
|||
size_t
|
||||
operator()(const st2gba_state& s) const
|
||||
{
|
||||
std::hash<acc_cond::mark_t> h;
|
||||
return s.s ^ h(s.pend);
|
||||
std::hash<acc_cond::mark_t> h;
|
||||
return s.s ^ h(s.pend);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -56,9 +56,9 @@ namespace spot
|
|||
operator()(const st2gba_state& left,
|
||||
const st2gba_state& right) const
|
||||
{
|
||||
if (left.s != right.s)
|
||||
return false;
|
||||
return left.pend == right.pend;
|
||||
if (left.s != right.s)
|
||||
return false;
|
||||
return left.pend == right.pend;
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -71,21 +71,21 @@ namespace spot
|
|||
auto pos = &code.back();
|
||||
auto end = &code.front();
|
||||
if (pos->op == acc_cond::acc_op::And)
|
||||
--pos;
|
||||
--pos;
|
||||
while (pos >= end)
|
||||
{
|
||||
auto term_end = pos - 1 - pos->size;
|
||||
if (pos->op == acc_cond::acc_op::Or)
|
||||
--pos;
|
||||
acc_cond::mark_t m = 0U;
|
||||
while (pos > term_end)
|
||||
{
|
||||
assert(pos->op == acc_cond::acc_op::Inf);
|
||||
m |= pos[-1].mark;
|
||||
pos -= 2;
|
||||
}
|
||||
res.push_back(m);
|
||||
}
|
||||
{
|
||||
auto term_end = pos - 1 - pos->size;
|
||||
if (pos->op == acc_cond::acc_op::Or)
|
||||
--pos;
|
||||
acc_cond::mark_t m = 0U;
|
||||
while (pos > term_end)
|
||||
{
|
||||
assert(pos->op == acc_cond::acc_op::Inf);
|
||||
m |= pos[-1].mark;
|
||||
pos -= 2;
|
||||
}
|
||||
res.push_back(m);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
|
@ -119,7 +119,7 @@ namespace spot
|
|||
int p = in->acc().is_streett();
|
||||
if (p <= 0)
|
||||
throw std::runtime_error("streett_to_generalized_buchi() should only be"
|
||||
" called on automata with Streett acceptance");
|
||||
" called on automata with Streett acceptance");
|
||||
|
||||
// In Streett acceptance, inf sets are odd, while fin sets are
|
||||
// even.
|
||||
|
|
@ -136,12 +136,12 @@ namespace spot
|
|||
sccfi.reserve(nscc);
|
||||
for (unsigned s = 0; s < nscc; ++s)
|
||||
{
|
||||
auto acc = si.acc_sets_of(s); // {0,1,2,3,4,6,7,9}
|
||||
auto acc_fin = acc & fin; // {0, 2, 4,6}
|
||||
auto acc_inf = acc & inf; // { 1, 3, 7,9}
|
||||
auto fin_wo_inf = acc_fin - (acc_inf >> 1U); // {4}
|
||||
auto inf_wo_fin = acc_inf - (acc_fin << 1U); // {9}
|
||||
sccfi.emplace_back(fin_wo_inf, inf_wo_fin, acc_fin == 0U);
|
||||
auto acc = si.acc_sets_of(s); // {0,1,2,3,4,6,7,9}
|
||||
auto acc_fin = acc & fin; // {0, 2, 4,6}
|
||||
auto acc_inf = acc & inf; // { 1, 3, 7,9}
|
||||
auto fin_wo_inf = acc_fin - (acc_inf >> 1U); // {4}
|
||||
auto inf_wo_fin = acc_inf - (acc_fin << 1U); // {9}
|
||||
sccfi.emplace_back(fin_wo_inf, inf_wo_fin, acc_fin == 0U);
|
||||
}
|
||||
|
||||
auto out = make_twa_graph(in->get_dict());
|
||||
|
|
@ -152,8 +152,8 @@ namespace spot
|
|||
|
||||
// Map st2gba pairs to the state numbers used in out.
|
||||
typedef std::unordered_map<st2gba_state, unsigned,
|
||||
st2gba_state_hash,
|
||||
st2gba_state_equal> bs2num_map;
|
||||
st2gba_state_hash,
|
||||
st2gba_state_equal> bs2num_map;
|
||||
bs2num_map bs2num;
|
||||
|
||||
// Queue of states to be processed.
|
||||
|
|
@ -171,115 +171,115 @@ namespace spot
|
|||
|
||||
while (!todo.empty())
|
||||
{
|
||||
s = todo.front();
|
||||
todo.pop_front();
|
||||
unsigned src = bs2num[s];
|
||||
s = todo.front();
|
||||
todo.pop_front();
|
||||
unsigned src = bs2num[s];
|
||||
|
||||
unsigned scc_src = si.scc_of(s.s);
|
||||
bool maybe_acc_scc = !si.is_rejecting_scc(scc_src);
|
||||
unsigned scc_src = si.scc_of(s.s);
|
||||
bool maybe_acc_scc = !si.is_rejecting_scc(scc_src);
|
||||
|
||||
acc_cond::mark_t scc_fin_wo_inf;
|
||||
acc_cond::mark_t scc_inf_wo_fin;
|
||||
bool no_fin;
|
||||
std::tie(scc_fin_wo_inf, scc_inf_wo_fin, no_fin) = sccfi[scc_src];
|
||||
acc_cond::mark_t scc_fin_wo_inf;
|
||||
acc_cond::mark_t scc_inf_wo_fin;
|
||||
bool no_fin;
|
||||
std::tie(scc_fin_wo_inf, scc_inf_wo_fin, no_fin) = sccfi[scc_src];
|
||||
|
||||
for (auto& t: in->out(s.s))
|
||||
{
|
||||
acc_cond::mark_t pend = s.pend;
|
||||
acc_cond::mark_t acc = 0U;
|
||||
for (auto& t: in->out(s.s))
|
||||
{
|
||||
acc_cond::mark_t pend = s.pend;
|
||||
acc_cond::mark_t acc = 0U;
|
||||
|
||||
bool maybe_acc = maybe_acc_scc && (scc_src == si.scc_of(t.dst));
|
||||
bool maybe_acc = maybe_acc_scc && (scc_src == si.scc_of(t.dst));
|
||||
|
||||
if (pend != orig_copy)
|
||||
{
|
||||
if (!maybe_acc)
|
||||
continue;
|
||||
// No point going to some place we will never leave
|
||||
if (t.acc & scc_fin_wo_inf)
|
||||
continue;
|
||||
// For any Fin set we see, we want to see the
|
||||
// corresponding Inf set.
|
||||
pend |= (t.acc & fin) << 1U;
|
||||
pend -= t.acc & inf;
|
||||
// Label this transition with all non-pending
|
||||
// inf sets. The strip will shift everything
|
||||
// to the correct numbers in the targets.
|
||||
acc = (inf - pend).strip(fin) & outall;
|
||||
// Adjust the pending sets to what will be necessary
|
||||
// required on the destination state.
|
||||
if (sbacc)
|
||||
{
|
||||
auto a = in->state_acc_sets(t.dst);
|
||||
if (a & scc_fin_wo_inf)
|
||||
continue;
|
||||
pend |= (a & fin) << 1U;
|
||||
pend -= a & inf;
|
||||
}
|
||||
pend |= scc_inf_wo_fin;
|
||||
}
|
||||
else if (no_fin && maybe_acc)
|
||||
{
|
||||
assert(maybe_acc);
|
||||
acc = outall;
|
||||
}
|
||||
if (pend != orig_copy)
|
||||
{
|
||||
if (!maybe_acc)
|
||||
continue;
|
||||
// No point going to some place we will never leave
|
||||
if (t.acc & scc_fin_wo_inf)
|
||||
continue;
|
||||
// For any Fin set we see, we want to see the
|
||||
// corresponding Inf set.
|
||||
pend |= (t.acc & fin) << 1U;
|
||||
pend -= t.acc & inf;
|
||||
// Label this transition with all non-pending
|
||||
// inf sets. The strip will shift everything
|
||||
// to the correct numbers in the targets.
|
||||
acc = (inf - pend).strip(fin) & outall;
|
||||
// Adjust the pending sets to what will be necessary
|
||||
// required on the destination state.
|
||||
if (sbacc)
|
||||
{
|
||||
auto a = in->state_acc_sets(t.dst);
|
||||
if (a & scc_fin_wo_inf)
|
||||
continue;
|
||||
pend |= (a & fin) << 1U;
|
||||
pend -= a & inf;
|
||||
}
|
||||
pend |= scc_inf_wo_fin;
|
||||
}
|
||||
else if (no_fin && maybe_acc)
|
||||
{
|
||||
assert(maybe_acc);
|
||||
acc = outall;
|
||||
}
|
||||
|
||||
st2gba_state d(t.dst, pend);
|
||||
// Have we already seen this destination?
|
||||
unsigned dest;
|
||||
auto dres = bs2num.emplace(d, 0);
|
||||
if (!dres.second)
|
||||
{
|
||||
dest = dres.first->second;
|
||||
}
|
||||
else // No, this is a new state
|
||||
{
|
||||
dest = dres.first->second = out->new_state();
|
||||
todo.push_back(d);
|
||||
}
|
||||
out->new_edge(src, dest, t.cond, acc);
|
||||
st2gba_state d(t.dst, pend);
|
||||
// Have we already seen this destination?
|
||||
unsigned dest;
|
||||
auto dres = bs2num.emplace(d, 0);
|
||||
if (!dres.second)
|
||||
{
|
||||
dest = dres.first->second;
|
||||
}
|
||||
else // No, this is a new state
|
||||
{
|
||||
dest = dres.first->second = out->new_state();
|
||||
todo.push_back(d);
|
||||
}
|
||||
out->new_edge(src, dest, t.cond, acc);
|
||||
|
||||
// Nondeterministically jump to level ∅. We need to do
|
||||
// that only once per cycle. As an approximation, we
|
||||
// only to that for transition where t.src >= t.dst as
|
||||
// this has to occur at least once per cycle.
|
||||
if (pend == orig_copy && (t.src >= t.dst) && maybe_acc && !no_fin)
|
||||
{
|
||||
acc_cond::mark_t pend = 0U;
|
||||
if (sbacc)
|
||||
{
|
||||
auto a = in->state_acc_sets(t.dst);
|
||||
if (a & scc_fin_wo_inf)
|
||||
continue;
|
||||
pend = (a & fin) << 1U;
|
||||
pend -= a & inf;
|
||||
}
|
||||
st2gba_state d(t.dst, pend | scc_inf_wo_fin);
|
||||
// Have we already seen this destination?
|
||||
unsigned dest;
|
||||
auto dres = bs2num.emplace(d, 0);
|
||||
if (!dres.second)
|
||||
{
|
||||
dest = dres.first->second;
|
||||
}
|
||||
else // No, this is a new state
|
||||
{
|
||||
dest = dres.first->second = out->new_state();
|
||||
todo.push_back(d);
|
||||
}
|
||||
out->new_edge(src, dest, t.cond);
|
||||
}
|
||||
}
|
||||
// Nondeterministically jump to level ∅. We need to do
|
||||
// that only once per cycle. As an approximation, we
|
||||
// only to that for transition where t.src >= t.dst as
|
||||
// this has to occur at least once per cycle.
|
||||
if (pend == orig_copy && (t.src >= t.dst) && maybe_acc && !no_fin)
|
||||
{
|
||||
acc_cond::mark_t pend = 0U;
|
||||
if (sbacc)
|
||||
{
|
||||
auto a = in->state_acc_sets(t.dst);
|
||||
if (a & scc_fin_wo_inf)
|
||||
continue;
|
||||
pend = (a & fin) << 1U;
|
||||
pend -= a & inf;
|
||||
}
|
||||
st2gba_state d(t.dst, pend | scc_inf_wo_fin);
|
||||
// Have we already seen this destination?
|
||||
unsigned dest;
|
||||
auto dres = bs2num.emplace(d, 0);
|
||||
if (!dres.second)
|
||||
{
|
||||
dest = dres.first->second;
|
||||
}
|
||||
else // No, this is a new state
|
||||
{
|
||||
dest = dres.first->second = out->new_state();
|
||||
todo.push_back(d);
|
||||
}
|
||||
out->new_edge(src, dest, t.cond);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// for (auto s: bs2num)
|
||||
// {
|
||||
// std::cerr << s.second << " ("
|
||||
// << s.first.s << ", ";
|
||||
// if (s.first.pend == orig_copy)
|
||||
// std::cerr << "-)\n";
|
||||
// else
|
||||
// std::cerr << s.first.pend << ")\n";
|
||||
// std::cerr << s.second << " ("
|
||||
// << s.first.s << ", ";
|
||||
// if (s.first.pend == orig_copy)
|
||||
// std::cerr << "-)\n";
|
||||
// else
|
||||
// std::cerr << s.first.pend << ")\n";
|
||||
// }
|
||||
return out;
|
||||
}
|
||||
|
|
@ -290,11 +290,11 @@ namespace spot
|
|||
static int min = [&]() {
|
||||
const char* c = getenv("SPOT_STREETT_CONV_MIN");
|
||||
if (!c)
|
||||
return 3;
|
||||
return 3;
|
||||
errno = 0;
|
||||
int val = strtol(c, nullptr, 10);
|
||||
if (val < 0 || errno != 0)
|
||||
throw std::runtime_error("unexpected value for SPOT_STREETT_CONV_MIN");
|
||||
throw std::runtime_error("unexpected value for SPOT_STREETT_CONV_MIN");
|
||||
return val;
|
||||
}();
|
||||
if (min == 0 || min > in->acc().is_streett())
|
||||
|
|
@ -319,11 +319,11 @@ namespace spot
|
|||
auto cnf = res->get_acceptance().to_cnf();
|
||||
// If we are very lucky, building a CNF actually gave us a GBA...
|
||||
if (cnf.empty() ||
|
||||
(cnf.size() == 2 && cnf.back().op == acc_cond::acc_op::Inf))
|
||||
(cnf.size() == 2 && cnf.back().op == acc_cond::acc_op::Inf))
|
||||
{
|
||||
res->set_acceptance(res->num_sets(), cnf);
|
||||
cleanup_acceptance_here(res);
|
||||
return res;
|
||||
res->set_acceptance(res->num_sets(), cnf);
|
||||
cleanup_acceptance_here(res);
|
||||
return res;
|
||||
}
|
||||
|
||||
// Handle false specifically. We want the output
|
||||
|
|
@ -331,14 +331,14 @@ namespace spot
|
|||
// state without successor.
|
||||
if (cnf.size() == 2 && cnf.back().op == acc_cond::acc_op::Fin)
|
||||
{
|
||||
assert(cnf.front().mark == 0U);
|
||||
res = make_twa_graph(aut->get_dict());
|
||||
res->set_init_state(res->new_state());
|
||||
res->prop_state_acc(true);
|
||||
res->prop_weak(true);
|
||||
res->prop_deterministic(true);
|
||||
res->prop_stutter_invariant(true);
|
||||
return res;
|
||||
assert(cnf.front().mark == 0U);
|
||||
res = make_twa_graph(aut->get_dict());
|
||||
res->set_init_state(res->new_state());
|
||||
res->prop_state_acc(true);
|
||||
res->prop_weak(true);
|
||||
res->prop_deterministic(true);
|
||||
res->prop_stutter_invariant(true);
|
||||
return res;
|
||||
}
|
||||
|
||||
auto terms = cnf_terms(cnf);
|
||||
|
|
@ -348,12 +348,12 @@ namespace spot
|
|||
|
||||
for (auto& t: res->edges())
|
||||
{
|
||||
acc_cond::mark_t cur_m = t.acc;
|
||||
acc_cond::mark_t new_m = 0U;
|
||||
for (unsigned n = 0; n < nterms; ++n)
|
||||
if (cur_m & terms[n])
|
||||
new_m.set(n);
|
||||
t.acc = new_m;
|
||||
acc_cond::mark_t cur_m = t.acc;
|
||||
acc_cond::mark_t new_m = 0U;
|
||||
for (unsigned n = 0; n < nterms; ++n)
|
||||
if (cur_m & terms[n])
|
||||
new_m.set(n);
|
||||
t.acc = new_m;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -35,9 +35,9 @@ namespace spot
|
|||
comp_susp_ = opt->get("comp-susp", 0);
|
||||
if (comp_susp_ == 1)
|
||||
{
|
||||
early_susp_ = opt->get("early-susp", 0);
|
||||
skel_wdba_ = opt->get("skel-wdba", -1);
|
||||
skel_simul_ = opt->get("skel-simul", 1);
|
||||
early_susp_ = opt->get("early-susp", 0);
|
||||
skel_wdba_ = opt->get("skel-wdba", -1);
|
||||
skel_simul_ = opt->get("skel-simul", 1);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -47,16 +47,16 @@ namespace spot
|
|||
switch (level_)
|
||||
{
|
||||
case High:
|
||||
options.containment_checks = true;
|
||||
options.containment_checks_stronger = true;
|
||||
// fall through
|
||||
options.containment_checks = true;
|
||||
options.containment_checks_stronger = true;
|
||||
// fall through
|
||||
case Medium:
|
||||
options.synt_impl = true;
|
||||
// fall through
|
||||
options.synt_impl = true;
|
||||
// fall through
|
||||
case Low:
|
||||
options.reduce_basics = true;
|
||||
options.event_univ = true;
|
||||
// fall through
|
||||
options.reduce_basics = true;
|
||||
options.event_univ = true;
|
||||
// fall through
|
||||
}
|
||||
simpl_owned_ = simpl_ = new tl_simplifier(options, dict);
|
||||
}
|
||||
|
|
@ -66,10 +66,10 @@ namespace spot
|
|||
bool unambiguous = (pref_ & postprocessor::Unambiguous);
|
||||
if (unambiguous && type_ == postprocessor::Monitor)
|
||||
{
|
||||
// Deterministic monitor are unambiguous, so the unambiguous
|
||||
// option is not really relevant for monitors.
|
||||
unambiguous = false;
|
||||
set_pref(pref_ | postprocessor::Deterministic);
|
||||
// Deterministic monitor are unambiguous, so the unambiguous
|
||||
// option is not really relevant for monitors.
|
||||
unambiguous = false;
|
||||
set_pref(pref_ | postprocessor::Deterministic);
|
||||
}
|
||||
|
||||
formula r = simpl_->simplify(*f);
|
||||
|
|
@ -82,21 +82,21 @@ namespace spot
|
|||
twa_graph_ptr aut;
|
||||
if (comp_susp_ > 0)
|
||||
{
|
||||
// FIXME: Handle unambiguous_ automata?
|
||||
int skel_wdba = skel_wdba_;
|
||||
if (skel_wdba < 0)
|
||||
skel_wdba = (pref_ == postprocessor::Deterministic) ? 1 : 2;
|
||||
// FIXME: Handle unambiguous_ automata?
|
||||
int skel_wdba = skel_wdba_;
|
||||
if (skel_wdba < 0)
|
||||
skel_wdba = (pref_ == postprocessor::Deterministic) ? 1 : 2;
|
||||
|
||||
aut = compsusp(r, simpl_->get_dict(), skel_wdba == 0,
|
||||
skel_simul_ == 0, early_susp_ != 0,
|
||||
comp_susp_ == 2, skel_wdba == 2, false);
|
||||
aut = compsusp(r, simpl_->get_dict(), skel_wdba == 0,
|
||||
skel_simul_ == 0, early_susp_ != 0,
|
||||
comp_susp_ == 2, skel_wdba == 2, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
bool exprop = unambiguous || level_ == postprocessor::High;
|
||||
aut = ltl_to_tgba_fm(r, simpl_->get_dict(), exprop,
|
||||
true, false, false, nullptr, nullptr,
|
||||
unambiguous);
|
||||
bool exprop = unambiguous || level_ == postprocessor::High;
|
||||
aut = ltl_to_tgba_fm(r, simpl_->get_dict(), exprop,
|
||||
true, false, false, nullptr, nullptr,
|
||||
unambiguous);
|
||||
}
|
||||
aut = this->postprocessor::run(aut, r);
|
||||
return aut;
|
||||
|
|
|
|||
|
|
@ -54,12 +54,12 @@ namespace spot
|
|||
{
|
||||
bdd all = bddtrue;
|
||||
for (auto& i: cycle)
|
||||
all &= i;
|
||||
all &= i;
|
||||
if (all != bddfalse)
|
||||
{
|
||||
cycle.clear();
|
||||
cycle.push_back(all);
|
||||
}
|
||||
{
|
||||
cycle.clear();
|
||||
cycle.push_back(all);
|
||||
}
|
||||
}
|
||||
// If the last formula of the prefix is compatible with the
|
||||
// last formula of the cycle, we can shift the cycle and
|
||||
|
|
@ -71,12 +71,12 @@ namespace spot
|
|||
// !a|!b; cycle{a&b}
|
||||
while (!prefix.empty())
|
||||
{
|
||||
bdd a = prefix.back() & cycle.back();
|
||||
if (a == bddfalse)
|
||||
break;
|
||||
prefix.pop_back();
|
||||
cycle.pop_back();
|
||||
cycle.push_front(a);
|
||||
bdd a = prefix.back() & cycle.back();
|
||||
if (a == bddfalse)
|
||||
break;
|
||||
prefix.pop_back();
|
||||
cycle.pop_back();
|
||||
cycle.push_front(a);
|
||||
}
|
||||
// Get rid of any disjunction.
|
||||
//
|
||||
|
|
@ -98,18 +98,18 @@ namespace spot
|
|||
auto d = w.get_dict();
|
||||
if (!w.prefix.empty())
|
||||
for (auto& i: w.prefix)
|
||||
{
|
||||
bdd_print_formula(os, d, i);
|
||||
os << "; ";
|
||||
}
|
||||
{
|
||||
bdd_print_formula(os, d, i);
|
||||
os << "; ";
|
||||
}
|
||||
bool notfirst = false;
|
||||
os << "cycle{";
|
||||
for (auto& i: w.cycle)
|
||||
{
|
||||
if (notfirst)
|
||||
os << "; ";
|
||||
notfirst = true;
|
||||
bdd_print_formula(os, d, i);
|
||||
if (notfirst)
|
||||
os << "; ";
|
||||
notfirst = true;
|
||||
bdd_print_formula(os, d, i);
|
||||
}
|
||||
os << '}';
|
||||
return os;
|
||||
|
|
@ -118,7 +118,7 @@ namespace spot
|
|||
namespace
|
||||
{
|
||||
static void word_parse_error(const std::string& word,
|
||||
size_t i, parsed_formula pf)
|
||||
size_t i, parsed_formula pf)
|
||||
{
|
||||
std::ostringstream os;
|
||||
pf.format_errors(os, word, i);
|
||||
|
|
@ -126,14 +126,14 @@ namespace spot
|
|||
}
|
||||
|
||||
static void word_parse_error(const std::string& word, size_t i,
|
||||
const std::string& message)
|
||||
const std::string& message)
|
||||
{
|
||||
if (i == std::string::npos)
|
||||
i = word.size();
|
||||
i = word.size();
|
||||
std::ostringstream s;
|
||||
s << ">>> " << word << '\n';
|
||||
for (auto j = i + 4; j > 0; --j)
|
||||
s << ' ';
|
||||
s << ' ';
|
||||
s << '^' << '\n';
|
||||
s << message << '\n';
|
||||
throw parse_error(s.str());
|
||||
|
|
@ -144,17 +144,17 @@ namespace spot
|
|||
bool quoted = false;
|
||||
auto size = word.size();
|
||||
for (auto j = begin; j < size; ++j)
|
||||
{
|
||||
auto c = word[j];
|
||||
if (!quoted && (c == ';' || c == '}'))
|
||||
return j;
|
||||
if (c == '"')
|
||||
quoted = !quoted;
|
||||
else if (quoted && c == '\\')
|
||||
++j;
|
||||
}
|
||||
{
|
||||
auto c = word[j];
|
||||
if (!quoted && (c == ';' || c == '}'))
|
||||
return j;
|
||||
if (c == '"')
|
||||
quoted = !quoted;
|
||||
else if (quoted && c == '\\')
|
||||
++j;
|
||||
}
|
||||
if (quoted)
|
||||
word_parse_error(word, word.size(), "Unclosed string");
|
||||
word_parse_error(word, word.size(), "Unclosed string");
|
||||
return std::string::npos;
|
||||
}
|
||||
}
|
||||
|
|
@ -170,49 +170,49 @@ namespace spot
|
|||
auto extract_bdd =
|
||||
[&](typename twa_word::seq_t& seq)
|
||||
{
|
||||
auto sub = word.substr(i, ind - i);
|
||||
auto pf = spot::parse_infix_boolean(sub);
|
||||
if (!pf.errors.empty())
|
||||
word_parse_error(word, i, pf);
|
||||
atomic_prop_collect(pf.f, &aps);
|
||||
seq.push_back(tls.as_bdd(pf.f));
|
||||
if (word[ind] == '}')
|
||||
return true;
|
||||
// Skip blanks after semi-colon
|
||||
i = word.find_first_not_of(' ', ind + 1);
|
||||
return false;
|
||||
auto sub = word.substr(i, ind - i);
|
||||
auto pf = spot::parse_infix_boolean(sub);
|
||||
if (!pf.errors.empty())
|
||||
word_parse_error(word, i, pf);
|
||||
atomic_prop_collect(pf.f, &aps);
|
||||
seq.push_back(tls.as_bdd(pf.f));
|
||||
if (word[ind] == '}')
|
||||
return true;
|
||||
// Skip blanks after semi-colon
|
||||
i = word.find_first_not_of(' ', ind + 1);
|
||||
return false;
|
||||
};
|
||||
|
||||
// Parse the prefix part. Can be empty.
|
||||
while (word.substr(i, 6) != std::string("cycle{"))
|
||||
{
|
||||
ind = skip_next_formula(word, i);
|
||||
if (ind == std::string::npos)
|
||||
word_parse_error(word, word.size(),
|
||||
"A twa_word must contain a cycle");
|
||||
if (word[ind] == '}')
|
||||
word_parse_error(word, ind, "Expected ';' delimiter: "
|
||||
"'}' stands for ending a cycle");
|
||||
// Exract formula, convert it to bdd and add it to the prefix sequence
|
||||
extract_bdd(tw->prefix);
|
||||
if (i == std::string::npos)
|
||||
word_parse_error(word, ind + 1, "Missing cycle in formula");
|
||||
ind = skip_next_formula(word, i);
|
||||
if (ind == std::string::npos)
|
||||
word_parse_error(word, word.size(),
|
||||
"A twa_word must contain a cycle");
|
||||
if (word[ind] == '}')
|
||||
word_parse_error(word, ind, "Expected ';' delimiter: "
|
||||
"'}' stands for ending a cycle");
|
||||
// Exract formula, convert it to bdd and add it to the prefix sequence
|
||||
extract_bdd(tw->prefix);
|
||||
if (i == std::string::npos)
|
||||
word_parse_error(word, ind + 1, "Missing cycle in formula");
|
||||
}
|
||||
// Consume "cycle{"
|
||||
i += 6;
|
||||
while (true)
|
||||
{
|
||||
ind = skip_next_formula(word, i);
|
||||
if (ind == std::string::npos)
|
||||
word_parse_error(word, word.size(),
|
||||
"Missing ';' or '}' after formula");
|
||||
// Extract formula, convert it to bdd and add it to the cycle sequence
|
||||
// Break if an '}' is encountered
|
||||
if (extract_bdd(tw->cycle))
|
||||
break;
|
||||
if (i == std::string::npos)
|
||||
word_parse_error(word, ind + 1,
|
||||
"Missing end of cycle character: '}'");
|
||||
ind = skip_next_formula(word, i);
|
||||
if (ind == std::string::npos)
|
||||
word_parse_error(word, word.size(),
|
||||
"Missing ';' or '}' after formula");
|
||||
// Extract formula, convert it to bdd and add it to the cycle sequence
|
||||
// Break if an '}' is encountered
|
||||
if (extract_bdd(tw->cycle))
|
||||
break;
|
||||
if (i == std::string::npos)
|
||||
word_parse_error(word, ind + 1,
|
||||
"Missing end of cycle character: '}'");
|
||||
}
|
||||
if (ind != word.size() - 1)
|
||||
word_parse_error(word, ind + 1, "Input should be finished after cycle");
|
||||
|
|
@ -232,31 +232,31 @@ namespace spot
|
|||
{
|
||||
bdd support = bddtrue;
|
||||
for (auto b: prefix)
|
||||
support &= bdd_support(b);
|
||||
support &= bdd_support(b);
|
||||
for (auto b: cycle)
|
||||
support &= bdd_support(b);
|
||||
support &= bdd_support(b);
|
||||
while (support != bddtrue)
|
||||
{
|
||||
int v = bdd_var(support);
|
||||
support = bdd_high(support);
|
||||
aut->register_ap(dict_->bdd_map[v].f);
|
||||
}
|
||||
{
|
||||
int v = bdd_var(support);
|
||||
support = bdd_high(support);
|
||||
aut->register_ap(dict_->bdd_map[v].f);
|
||||
}
|
||||
}
|
||||
|
||||
size_t i = 0;
|
||||
aut->new_states(prefix.size() + cycle.size());
|
||||
for (auto b: prefix)
|
||||
{
|
||||
aut->new_edge(i, i + 1, b);
|
||||
++i;
|
||||
aut->new_edge(i, i + 1, b);
|
||||
++i;
|
||||
}
|
||||
size_t j = i;
|
||||
auto b = cycle.begin();
|
||||
auto end = --cycle.end();
|
||||
for (; b != end; ++b)
|
||||
{
|
||||
aut->new_edge(i, i + 1, *b);
|
||||
++i;
|
||||
aut->new_edge(i, i + 1, *b);
|
||||
++i;
|
||||
}
|
||||
// Close the loop
|
||||
aut->new_edge(i, j, *b);
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue