Trim DFAs used when translating PSL's closure operators.
This fixes a bug where {(a&!a)[=2]} was translated either into an
universal automaton (with simplification turned off) or in an
empty automaton (with simplification turned on).
* src/tgbaalgos/ltl2tgba_fm.cc (ratexp_to_dfa::translate): Trim
the automaton.
(ratexp_to_dfa::succ, ratexp_to_dfa::get_label): Deal with trimed
states.
(ltl_trad_visitor::visit(unop::Closure)): Likewise.
* src/tgbatest/ltl2tgba.test, src/ltltest/reduccmp.test: New test
cases.
This commit is contained in:
parent
d1530de125
commit
b3cc033e92
3 changed files with 88 additions and 8 deletions
|
|
@ -196,6 +196,12 @@ for x in ../reduccmp ../reductaustr; do
|
||||||
# Because reduccmp will translate the formula,
|
# Because reduccmp will translate the formula,
|
||||||
# this also check for an old bug in ltl2tgba_fm.
|
# this also check for an old bug in ltl2tgba_fm.
|
||||||
run 0 $x '{(c&!c)[->0..1]}!' '0'
|
run 0 $x '{(c&!c)[->0..1]}!' '0'
|
||||||
|
|
||||||
|
# Tricky case that used to break the translator,
|
||||||
|
# because it was translating closer on-the-fly
|
||||||
|
# without pruning the rational automaton.
|
||||||
|
run 0 $x '{(c&!c)[=2]}' '0'
|
||||||
|
|
||||||
;;
|
;;
|
||||||
esac
|
esac
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -37,6 +37,7 @@
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include "ltl2tgba_fm.hh"
|
#include "ltl2tgba_fm.hh"
|
||||||
#include "tgba/bddprint.hh"
|
#include "tgba/bddprint.hh"
|
||||||
|
#include "tgbaalgos/scc.hh"
|
||||||
//#include "tgbaalgos/dotty.hh"
|
//#include "tgbaalgos/dotty.hh"
|
||||||
|
|
||||||
namespace spot
|
namespace spot
|
||||||
|
|
@ -891,11 +892,6 @@ namespace spot
|
||||||
const formula* now = *formulae_to_translate.begin();
|
const formula* now = *formulae_to_translate.begin();
|
||||||
formulae_to_translate.erase(formulae_to_translate.begin());
|
formulae_to_translate.erase(formulae_to_translate.begin());
|
||||||
|
|
||||||
// Add it to the set of translated formulae.
|
|
||||||
// FIXME: That's incorrect: we need to (minimize&)trim the
|
|
||||||
// automaton first.
|
|
||||||
f2a_[now] = a;
|
|
||||||
|
|
||||||
// Translate it
|
// Translate it
|
||||||
bdd res = translate_ratexp(now, dict_);
|
bdd res = translate_ratexp(now, dict_);
|
||||||
|
|
||||||
|
|
@ -921,8 +917,68 @@ namespace spot
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
//dotty_reachable(std::cerr, a);
|
//dotty_reachable(std::cerr, a);
|
||||||
automata_.push_back(a);
|
|
||||||
return a;
|
// The following code trims the automaton in a crude way by
|
||||||
|
// eliminating SCCs that are not coaccessible. It does not
|
||||||
|
// actually remove the states, it simply marks the corresponding
|
||||||
|
// formulae as associated to the null pointer in the f2a_ map.
|
||||||
|
// The method succ() and get_label() interpret this as False.
|
||||||
|
|
||||||
|
scc_map* sm = new scc_map(a);
|
||||||
|
sm->build_map();
|
||||||
|
unsigned scc_count = sm->scc_count();
|
||||||
|
// Remember whether each SCC is coaccessible.
|
||||||
|
std::vector<bool> coaccessible(scc_count);
|
||||||
|
// SCC are numbered in topological order
|
||||||
|
for (unsigned n = 0; n < scc_count; ++n)
|
||||||
|
{
|
||||||
|
bool coacc = false;
|
||||||
|
const std::list<const state*>& st = sm->states_of(n);
|
||||||
|
// The SCC is coaccessible if any of its states
|
||||||
|
// is final (i.e., it accepts [*0])...
|
||||||
|
std::list<const state*>::const_iterator it;
|
||||||
|
for (it = st.begin(); it != st.end(); ++it)
|
||||||
|
if (a->get_label(*it)->accepts_eword())
|
||||||
|
{
|
||||||
|
coacc = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (!coacc)
|
||||||
|
{
|
||||||
|
// ... or if any of its successors is coaccessible.
|
||||||
|
const scc_map::succ_type& succ = sm->succ(n);
|
||||||
|
for (scc_map::succ_type::const_iterator i = succ.begin();
|
||||||
|
i != succ.end(); ++i)
|
||||||
|
if (coaccessible[i->first])
|
||||||
|
{
|
||||||
|
coacc = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (!coacc)
|
||||||
|
{
|
||||||
|
// Mark all formulas of this SCC as useless.
|
||||||
|
for (it = st.begin(); it != st.end(); ++it)
|
||||||
|
f2a_[a->get_label(*it)] = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
for (it = st.begin(); it != st.end(); ++it)
|
||||||
|
f2a_[a->get_label(*it)] = a;
|
||||||
|
}
|
||||||
|
coaccessible[n] = coacc;
|
||||||
|
}
|
||||||
|
delete sm;
|
||||||
|
if (coaccessible[scc_count - 1])
|
||||||
|
{
|
||||||
|
automata_.push_back(a);
|
||||||
|
return a;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
delete a;
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
tgba_succ_iterator*
|
tgba_succ_iterator*
|
||||||
|
|
@ -935,6 +991,10 @@ namespace spot
|
||||||
else
|
else
|
||||||
a = translate(f);
|
a = translate(f);
|
||||||
|
|
||||||
|
// If a is nul, f has an empty language.
|
||||||
|
if (!a)
|
||||||
|
return 0;
|
||||||
|
|
||||||
assert(a->has_state(f));
|
assert(a->has_state(f));
|
||||||
// This won't create a new state.
|
// This won't create a new state.
|
||||||
const state* s = a->add_state(f);
|
const state* s = a->add_state(f);
|
||||||
|
|
@ -948,7 +1008,15 @@ namespace spot
|
||||||
f2a_t::const_iterator it = f2a_.find(f);
|
f2a_t::const_iterator it = f2a_.find(f);
|
||||||
assert(it != f2a_.end());
|
assert(it != f2a_.end());
|
||||||
tgba_explicit_formula* a = it->second;
|
tgba_explicit_formula* a = it->second;
|
||||||
return a->get_label(s)->clone();
|
assert(a != 0);
|
||||||
|
|
||||||
|
const formula* f2 = a->get_label(s);
|
||||||
|
f2a_t::const_iterator it2 = f2a_.find(f2);
|
||||||
|
assert(it2 != f2a_.end());
|
||||||
|
if (it2->second == 0)
|
||||||
|
return constant::false_instance();
|
||||||
|
|
||||||
|
return f2->clone();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
@ -1089,6 +1157,8 @@ namespace spot
|
||||||
tgba_succ_iterator* i = dict_.transdfa.succ(f);
|
tgba_succ_iterator* i = dict_.transdfa.succ(f);
|
||||||
res_ = bddfalse;
|
res_ = bddfalse;
|
||||||
|
|
||||||
|
if (!i)
|
||||||
|
break;
|
||||||
for (i->first(); !i->done(); i->next())
|
for (i->first(); !i->done(); i->next())
|
||||||
{
|
{
|
||||||
bdd label = i->current_condition();
|
bdd label = i->current_condition();
|
||||||
|
|
|
||||||
|
|
@ -99,6 +99,10 @@ check_psl '{[*]; {read[=3]} && {write[=2]}} |=>
|
||||||
check_psl '{[*]; start && comp_data_en; !comp_data_en && good_comp;
|
check_psl '{[*]; start && comp_data_en; !comp_data_en && good_comp;
|
||||||
{status_valid[->]} && {stop[=0]; true}} |-> {!data_out}' # 2.33
|
{status_valid[->]} && {stop[=0]; true}} |-> {!data_out}' # 2.33
|
||||||
|
|
||||||
|
# Some tricky cases that require the rational automaton to be pruned
|
||||||
|
# before it is used in the translation.
|
||||||
|
check_psl '{{b[*];c} | {{a && !a}}[=2]}'
|
||||||
|
check_psl '{((a&!b);((!a&!b)*))&&(!b*;(!a&b))}'
|
||||||
|
|
||||||
# Make sure 'a U (b U c)' has 3 states and 6 transitions,
|
# Make sure 'a U (b U c)' has 3 states and 6 transitions,
|
||||||
# before and after degeneralization.
|
# before and after degeneralization.
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue