Commit 0c7c9338 authored by Alexandre Duret-Lutz's avatar Alexandre Duret-Lutz

simulation: Fix co-simulation and iterated simulations of BA automata

* src/tgbaalgos/simulation.hh, src/tgbaalgos/simulation.cc
(simulation_sba, cosimulation_sba, iterated_simulations_sba): New
function.  Also speedup the existing functions by avoiding
add_acceptince_conditions() and add_conditions().  Finally, use
scc_filter_states() when dealing with degeneralized automata.
* src/tgbaalgos/postproc.cc, src/tgbaalgos/postproc.hh (do_ba_simul):
New method.  Use it after degeneralization.
* src/tgba/tgbaexplicit.hh (get_transition, get_state): New methods.
* src/tgbatest/basimul.test: New file.
* src/tgbatest/Makefile.am (TESTS): Add it.
* NEWS: Introduce the new function and summarize the bug.
parent 372790a4
...@@ -30,6 +30,15 @@ New in spot 1.1a (not yet released): ...@@ -30,6 +30,15 @@ New in spot 1.1a (not yet released):
the automaton. scc_filter_state() should be used when the automaton. scc_filter_state() should be used when
post-processing TGBAs that actually represent BAs. post-processing TGBAs that actually represent BAs.
- simulation_sba(), cosimulation_sba(), and
iterated_simulations_sba() are new functions that apply to TGBAs
that actually represent BAs. They preserve the imporant
property that if a state of the BA is is accepting, the outgoing
transitions of that state are all accepting in the TGBA that
represent the BA. This is something that was not preserved by
functions cosimultion() and iterated_simulations() as mentionned
in the bug fixes below.
- ltlcross has a new option --seed, that makes it possible to - ltlcross has a new option --seed, that makes it possible to
change the seed used by the random graph generator. change the seed used by the random graph generator.
...@@ -40,6 +49,14 @@ New in spot 1.1a (not yet released): ...@@ -40,6 +49,14 @@ New in spot 1.1a (not yet released):
- ltlfilt --stutter-invariant would trigger an assert on PSL formulas. - ltlfilt --stutter-invariant would trigger an assert on PSL formulas.
- ltl2tgba, ltl2tgta, ltlcross, and ltlfilt, would all choke on empty - ltl2tgba, ltl2tgta, ltlcross, and ltlfilt, would all choke on empty
lines in a file of formulas. They now ignore empty lines. lines in a file of formulas. They now ignore empty lines.
- The iterated simulation applied on degeneralized TGBA was bogus
for two reasons: one was that cosimulation was applied using the
generic cosimulation for TGBA, and the second is that
SCC-filtering, performed between iterations, was also a
TGBA-based algorithm. Both of these algorithms could lose the
property that if a TGBA represents a BA, all the outgoing
transitions of a state should be accepting. As a consequence, some
formulas where translated to incorrect Büchi automata.
New in spot 1.1 (2013-04-28): New in spot 1.1 (2013-04-28):
......
// -*- coding: utf-8 -*- // -*- coding: utf-8 -*-
// Copyright (C) 2009, 2010, 2011, 2012 Laboratoire de Recherche et // Copyright (C) 2009, 2010, 2011, 2012, 2013 Laboratoire de Recherche
// Développement de l'Epita. // et Développement de l'Epita.
// Copyright (C) 2003, 2004, 2006 Laboratoire d'Informatique de Paris // Copyright (C) 2003, 2004, 2006 Laboratoire d'Informatique de Paris
// 6 (LIP6), département Systèmes Répartis Coopératifs (SRC), // 6 (LIP6), département Systèmes Répartis Coopératifs (SRC),
// Université Pierre et Marie Curie. // Université Pierre et Marie Curie.
...@@ -318,6 +318,15 @@ namespace spot ...@@ -318,6 +318,15 @@ namespace spot
return const_cast<transition*>(&(*(si->get_iterator()))); return const_cast<transition*>(&(*(si->get_iterator())));
} }
transition*
get_transition(const tgba_succ_iterator* si)
{
const tgba_explicit_succ_iterator<State>* tmp
= down_cast<const tgba_explicit_succ_iterator<State>*>(si);
assert(tmp);
return get_transition(tmp);
}
void add_condition(transition* t, const ltl::formula* f) void add_condition(transition* t, const ltl::formula* f)
{ {
t->condition &= formula_to_bdd(f, dict_, this); t->condition &= formula_to_bdd(f, dict_, this);
...@@ -336,12 +345,24 @@ namespace spot ...@@ -336,12 +345,24 @@ namespace spot
return dict_->is_registered_acceptance_variable(f, this); return dict_->is_registered_acceptance_variable(f, this);
} }
//old tgba explicit labelled interface //old tgba explicit labeled interface
bool has_state(const label_t& name) bool has_state(const label_t& name)
{ {
return ls_.find(name) != ls_.end(); return ls_.find(name) != ls_.end();
} }
/// \brief Return the state associated to a given label.
///
/// This is similar to add_state(), except that it returns 0 if
/// the state does not exist.
const State* get_state(const label_t& name)
{
typename ls_map::const_iterator i = ls_.find(name);
if (i == ls_.end())
return 0;
return &i->second;
}
const label_t& get_label(const State* s) const const label_t& get_label(const State* s) const
{ {
typename sl_map::const_iterator i = sl_.find(s); typename sl_map::const_iterator i = sl_.find(s);
......
...@@ -80,6 +80,23 @@ namespace spot ...@@ -80,6 +80,23 @@ namespace spot
} }
} }
const tgba* postprocessor::do_ba_simul(const tgba* a, int opt)
{
switch (opt)
{
case 0:
return a;
case 1:
return simulation_sba(a);
case 2:
return cosimulation_sba(a);
case 3:
default:
return iterated_simulations_sba(a);
}
}
const tgba* postprocessor::do_degen(const tgba* a) const tgba* postprocessor::do_degen(const tgba* a)
{ {
const tgba* d = degeneralize(a, const tgba* d = degeneralize(a,
...@@ -90,7 +107,7 @@ namespace spot ...@@ -90,7 +107,7 @@ namespace spot
if (ba_simul_ <= 0) if (ba_simul_ <= 0)
return d; return d;
const tgba* s = do_simul(d, ba_simul_); const tgba* s = do_ba_simul(d, ba_simul_);
if (s != d) if (s != d)
delete d; delete d;
......
...@@ -91,6 +91,7 @@ namespace spot ...@@ -91,6 +91,7 @@ namespace spot
protected: protected:
const tgba* do_simul(const tgba* input, int opt); const tgba* do_simul(const tgba* input, int opt);
const tgba* do_ba_simul(const tgba* input, int opt);
const tgba* do_degen(const tgba* input); const tgba* do_degen(const tgba* input);
output_type type_; output_type type_;
......
...@@ -212,7 +212,6 @@ namespace spot ...@@ -212,7 +212,6 @@ namespace spot
return false; return false;
} }
int transitions; int transitions;
int states; int states;
}; };
...@@ -223,7 +222,7 @@ namespace spot ...@@ -223,7 +222,7 @@ namespace spot
// automaton is similar to the old one, except that the acceptance // automaton is similar to the old one, except that the acceptance
// condition on the transitions are complemented. // condition on the transitions are complemented.
// There is a specialization below. // There is a specialization below.
template <bool Cosimulation, bool ReverseComplement = false> template <bool Cosimulation, bool Sba>
class acc_compl_automaton: class acc_compl_automaton:
public tgba_reachable_iterator_depth_first public tgba_reachable_iterator_depth_first
{ {
...@@ -243,16 +242,10 @@ namespace spot ...@@ -243,16 +242,10 @@ namespace spot
const state*, int, const state*, int,
const tgba_succ_iterator* si) const tgba_succ_iterator* si)
{ {
bdd acc = ReverseComplement bdd acc = ac_.complement(si->current_acceptance_conditions());
? ac_.reverse_complement(si->current_acceptance_conditions())
: ac_.complement(si->current_acceptance_conditions());
const tgba_explicit_succ_iterator<state_explicit_number>* tmpit =
down_cast<const tgba_explicit_succ_iterator
<state_explicit_number>*>(si);
typename tgba_explicit_number::transition* t = typename tgba_explicit_number::transition* t =
ea_->get_transition(tmpit); ea_->get_transition(si);
t->acceptance_conditions = acc; t->acceptance_conditions = acc;
} }
...@@ -281,10 +274,10 @@ namespace spot ...@@ -281,10 +274,10 @@ namespace spot
acc_compl ac_; acc_compl ac_;
}; };
// The specialization for Cosimulation equals to true: We need to // The specialization for Cosimulation equals to true: We copy the
// copy. // automaton and transpose it at the same time.
template <> template <bool Sba>
class acc_compl_automaton<true>: class acc_compl_automaton<true, Sba>:
public tgba_reachable_iterator_depth_first public tgba_reachable_iterator_depth_first
{ {
public: public:
...@@ -292,13 +285,16 @@ namespace spot ...@@ -292,13 +285,16 @@ namespace spot
: tgba_reachable_iterator_depth_first(a), : tgba_reachable_iterator_depth_first(a),
size(0), size(0),
out_(new tgba_explicit_number(a->get_dict())), out_(new tgba_explicit_number(a->get_dict())),
ea_(a), ac_(a->all_acceptance_conditions(),
ac_(ea_->all_acceptance_conditions(), a->neg_acceptance_conditions()),
ea_->neg_acceptance_conditions()),
current_max(0) current_max(0)
{ {
init_ = ea_->get_init_state(); a->get_dict()->register_all_variables_of(a, out_);
out_->set_acceptance_conditions(a->all_acceptance_conditions());
const state* init_ = a->get_init_state();
out_->set_init_state(get_state(init_)); out_->set_init_state(get_state(init_));
init_->destroy();
} }
inline unsigned inline unsigned
...@@ -325,19 +321,32 @@ namespace spot ...@@ -325,19 +321,32 @@ namespace spot
unsigned src = get_state(in_s); unsigned src = get_state(in_s);
unsigned dst = get_state(out_s); unsigned dst = get_state(out_s);
// In the case of the cosimulation, we want to have all the // Note the order of src and dst: the transition is reversed.
// ingoing transition, and to keep the rest of the code
// similar, we just create equivalent transition in the other
// direction. Since we do not have to run through the
// automaton to get the signature, this is correct.
std::swap(src, dst);
bdd acc = ac_.complement(si->current_acceptance_conditions());
tgba_explicit_number::transition* t tgba_explicit_number::transition* t
= out_->create_transition(src, dst); = out_->create_transition(dst, src);
out_->add_acceptance_conditions(t, acc); t->condition = si->current_condition();
out_->add_conditions(t, si->current_condition()); if (!Sba)
{
bdd acc = ac_.complement(si->current_acceptance_conditions());
t->acceptance_conditions = acc;
}
else
{
// 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 args after
// transposition).
tgba_succ_iterator* it = out_->succ_iter(out_s);
it->first();
if (!it->done())
{
bdd acc = ac_.complement(it->current_acceptance_conditions());
t->acceptance_conditions = acc;
}
delete it;
}
} }
void process_state(const state*, int, tgba_succ_iterator*) void process_state(const state*, int, tgba_succ_iterator*)
...@@ -347,8 +356,6 @@ namespace spot ...@@ -347,8 +356,6 @@ namespace spot
~acc_compl_automaton() ~acc_compl_automaton()
{ {
// Because we don't know what get_init_state returns...
init_->destroy();
} }
public: public:
...@@ -359,16 +366,14 @@ namespace spot ...@@ -359,16 +366,14 @@ namespace spot
map_state_state old_name_; map_state_state old_name_;
private: private:
const state* init_;
const tgba* ea_;
acc_compl ac_; acc_compl ac_;
map_state_unsigned state2int; map_state_unsigned state2int;
unsigned current_max; unsigned current_max;
}; };
// The direct_simulation. If Cosimulation is true, we are doing a // The direct_simulation. If Cosimulation is true, we are doing a
// cosimulation. Seems obvious, but it's better to be clear. // cosimulation.
template <bool Cosimulation> template <bool Cosimulation, bool Sba>
class direct_simulation class direct_simulation
{ {
protected: protected:
...@@ -393,7 +398,7 @@ namespace spot ...@@ -393,7 +398,7 @@ namespace spot
scc_map_->build_map(); scc_map_->build_map();
old_a_ = a_; old_a_ = a_;
acc_compl_automaton<Cosimulation> acc_compl(a_); acc_compl_automaton<Cosimulation, Sba> acc_compl(a_);
// We'll start our work by replacing all the acceptance // We'll start our work by replacing all the acceptance
// conditions by their complement. // conditions by their complement.
...@@ -401,7 +406,7 @@ namespace spot ...@@ -401,7 +406,7 @@ namespace spot
// Contains the relation between the names of the states in // Contains the relation between the names of the states in
// the automaton returned by the complementation and the one // the automaton returned by the complementation and the one
// get in argument to the constructor of acc_compl. // passed to the constructor of acc_compl.
std::swap(old_name_, acc_compl.old_name_); std::swap(old_name_, acc_compl.old_name_);
a_ = acc_compl.out_; a_ = acc_compl.out_;
...@@ -417,9 +422,10 @@ namespace spot ...@@ -417,9 +422,10 @@ namespace spot
// class. We register one bdd by state, because in the worst // class. We register one bdd by state, because in the worst
// case, |Class| == |State|. // case, |Class| == |State|.
unsigned set_num = a_->get_dict() unsigned set_num = a_->get_dict()
->register_anonymous_variables(size_a_ + 1, a_); ->register_anonymous_variables(size_a_ + 1, this);
all_proms_ = bdd_support(a_->all_acceptance_conditions()); all_acceptance_conditions_ = a_->all_acceptance_conditions();
all_proms_ = bdd_support(all_acceptance_conditions_);
bdd_initial = bdd_ithvar(set_num++); bdd_initial = bdd_ithvar(set_num++);
bdd init = bdd_ithvar(set_num++); bdd init = bdd_ithvar(set_num++);
...@@ -451,7 +457,6 @@ namespace spot ...@@ -451,7 +457,6 @@ namespace spot
relation_[init] = init; relation_[init] = init;
std::swap(order_, acc_compl.order_); std::swap(order_, acc_compl.order_);
all_acceptance_conditions_ = a_->all_acceptance_conditions();
} }
...@@ -460,16 +465,17 @@ namespace spot ...@@ -460,16 +465,17 @@ namespace spot
// function simulation. // function simulation.
virtual ~direct_simulation() virtual ~direct_simulation()
{ {
a_->get_dict()->unregister_all_my_variables(this);
delete scc_map_; delete scc_map_;
if (!dont_delete_old_) if (!dont_delete_old_)
delete old_a_; delete old_a_;
// Since a_ is a new automaton only if we are doing a // a_ is a new automaton only if we are doing a cosimulation.
// cosimulation.
if (Cosimulation) if (Cosimulation)
delete a_; delete a_;
} }
// We update the name of the classes. // Update the name of the classes.
void update_previous_class() void update_previous_class()
{ {
std::list<bdd>::iterator it_bdd = used_var_.begin(); std::list<bdd>::iterator it_bdd = used_var_.begin();
...@@ -535,35 +541,37 @@ namespace spot ...@@ -535,35 +541,37 @@ namespace spot
{ {
const state* dst = sit->current_state(); const state* dst = sit->current_state();
bdd acc = bddtrue; bdd acc = bddtrue;
map_constraint::const_iterator it;
// We are using new_original_[old_name_[...]] because we
// give the constraints in the original automaton, so we
// need to use this heavy computation.
if (map_cst_
&& ((it = map_cst_
->find(std::make_pair(new_original_[old_name_[src]],
new_original_[old_name_[dst]])))
!= map_cst_->end()))
{
acc = it->second;
}
else
{
acc = sit->current_acceptance_conditions();
}
// to_add is a conjunction of the acceptance condition, map_constraint::const_iterator it;
// the label of the transition and the class of the // We are using new_original_[old_name_[...]] because
// destination and all the class it implies. // we have the constraints in the original automaton
bdd to_add = acc & sit->current_condition() // which has been duplicated twice to get the current
& relation_[previous_class_[dst]]; // automaton.
if (map_cst_
&& ((it = map_cst_
->find(std::make_pair(new_original_[old_name_[src]],
new_original_[old_name_[dst]])))
!= map_cst_->end()))
{
acc = it->second;
}
else
{
acc = sit->current_acceptance_conditions();
}
// to_add is a conjunction of the acceptance condition,
// the label of the transition and the class of the
// destination and all the class it implies.
bdd to_add = acc & sit->current_condition()
& relation_[previous_class_[dst]];
res |= to_add; res |= to_add;
dst->destroy(); dst->destroy();
} }
// When we Cosimulate, we add a special flag to differentiate // When we Cosimulate, we add a special flag to differentiate
// initial state. // the initial state from the other.
if (Cosimulation && initial_state == src) if (Cosimulation && initial_state == src)
res |= bdd_initial; res |= bdd_initial;
...@@ -728,10 +736,10 @@ namespace spot ...@@ -728,10 +736,10 @@ namespace spot
acc_compl reverser(all_acceptance_conditions_, acc_compl reverser(all_acceptance_conditions_,
a_->neg_acceptance_conditions()); a_->neg_acceptance_conditions());
tgba_explicit_number* res bdd_dict* d = a_->get_dict();
= new tgba_explicit_number(a_->get_dict()); tgba_explicit_number* res = new tgba_explicit_number(d);
res->set_acceptance_conditions d->register_all_variables_of(a_, res);
(all_acceptance_conditions_); res->set_acceptance_conditions(all_acceptance_conditions_);
bdd sup_all_acc = bdd_support(all_acceptance_conditions_); bdd sup_all_acc = bdd_support(all_acceptance_conditions_);
// Non atomic propositions variables (= acc and class) // Non atomic propositions variables (= acc and class)
...@@ -746,11 +754,16 @@ namespace spot ...@@ -746,11 +754,16 @@ namespace spot
// The difference between the two next lines is: // The difference between the two next lines is:
// the first says "if you see A", the second "if you // the first says "if you see A", the second "if you
// see A and all the class implied by it". // see A and all the classes implied by it".
bdd2state[part] = current_max; bdd2state[part] = current_max;
bdd2state[relation_[part]] = current_max; bdd2state[relation_[part]] = current_max;
} }
// Acceptance of states. Only used if Sba && Cosimulation.
std::vector<bdd> accst;
if (Sba && Cosimulation)
accst.resize(current_max + 1, bddfalse);
stat.states = bdd_lstate_.size(); stat.states = bdd_lstate_.size();
stat.transitions = 0; stat.transitions = 0;
...@@ -788,7 +801,7 @@ namespace spot ...@@ -788,7 +801,7 @@ namespace spot
bddtrue); bddtrue);
all_atomic_prop -= one; all_atomic_prop -= one;
// For each possible valuation, iterator over all possible // For each possible valuation, iterate over all possible
// destination classes. We use minato_isop here, because // destination classes. We use minato_isop here, because
// if the same valuation of atomic properties can go // if the same valuation of atomic properties can go
// to two different classes C1 and C2, iterating on // to two different classes C1 and C2, iterating on
...@@ -815,13 +828,13 @@ namespace spot ...@@ -815,13 +828,13 @@ namespace spot
// Keep only ones who are acceptance condition. // Keep only ones who are acceptance condition.
bdd acc = bdd_existcomp(cond_acc_dest, sup_all_acc); bdd acc = bdd_existcomp(cond_acc_dest, sup_all_acc);
// Keep the other ! // Keep the other!
bdd cond = bdd_existcomp(cond_acc_dest, bdd cond = bdd_existcomp(cond_acc_dest,
sup_all_atomic_prop); sup_all_atomic_prop);
// Because we have complemented all the acceptance // Because we have complemented all the acceptance
// condition on the input automaton, we must re // conditions on the input automaton, we must
// invert them to create a new transition. // revert them to create a new transition.
acc = reverser.reverse_complement(acc); acc = reverser.reverse_complement(acc);
// Take the id of the source and destination. To // Take the id of the source and destination. To
...@@ -842,9 +855,12 @@ namespace spot ...@@ -842,9 +855,12 @@ namespace spot
// Create the transition, add the condition and the // Create the transition, add the condition and the
// acceptance condition. // acceptance condition.
tgba_explicit_number::transition* t tgba_explicit_number::transition* t
= res->create_transition(src , dst); = res->create_transition(src, dst);
res->add_conditions(t, cond); t->condition = cond;
res->add_acceptance_conditions(t, acc); if (Sba && Cosimulation)
accst[dst] = acc;
else
t->acceptance_conditions = acc;
} }
} }
} }
...@@ -854,6 +870,23 @@ namespace spot ...@@ -854,6 +870,23 @@ namespace spot
res->merge_transitions(); res->merge_transitions();
// Mark all accepting state in a second pass, when
// dealing with SBA in cosimulation.
if (Sba && Cosimulation)
for (unsigned snum = current_max; snum > 0; --snum)
{
const state* s = res->get_state(snum);
tgba_succ_iterator* it = res->succ_iter(s);
bdd acc = accst[snum];
for (it->first(); !it->done(); it->next())
{
tgba_explicit_number::transition* t =
res->get_transition(it);
t->acceptance_conditions = acc;
}
delete it;
}