remove universal transitions on the fly
* spot/twaalgos/alternation.cc, spot/twaalgos/alternation.hh: Implement remove_univ_otf. * tests/python/alternating.py: Test it. * python/spot/impl.i: Bindings. * NEWS: Document it.
This commit is contained in:
parent
a0e89652b6
commit
a13a4e7d23
5 changed files with 332 additions and 0 deletions
4
NEWS
4
NEWS
|
|
@ -92,6 +92,10 @@ New in spot 2.4.0.dev (not yet released)
|
|||
compute respectively the intersection and the union of two automata with
|
||||
parity acceptance condition and keep the parity acceptance condition.
|
||||
|
||||
- The new function spot::remove_univ_otf() removes universal
|
||||
transitions on the fly from an alternating Büchi automaton
|
||||
using Miyano and Hayashi's breakpoint algorithm.
|
||||
|
||||
Deprecation notices:
|
||||
|
||||
(These functions still work but compilers emit warnings.)
|
||||
|
|
|
|||
|
|
@ -63,6 +63,7 @@
|
|||
%shared_ptr(spot::taa_tgba_formula)
|
||||
%shared_ptr(spot::twa_run)
|
||||
%shared_ptr(spot::twa_word)
|
||||
%shared_ptr(spot::twa_univ_remover)
|
||||
%shared_ptr(spot::emptiness_check_result)
|
||||
%shared_ptr(spot::emptiness_check)
|
||||
%shared_ptr(spot::emptiness_check_instantiator)
|
||||
|
|
|
|||
|
|
@ -487,4 +487,237 @@ namespace spot
|
|||
return ar.run(named_states);
|
||||
}
|
||||
|
||||
|
||||
// On-the-fly alternating Büchi automaton to nondeterministic Büchi automaton
|
||||
// using the breakpoint construction.
|
||||
|
||||
univ_remover_state::univ_remover_state(const std::set<unsigned>& states)
|
||||
: is_reset_(false)
|
||||
{
|
||||
is_reset_ = std::all_of(states.begin(), states.end(),
|
||||
[] (unsigned s) { return int(s) < 0; });
|
||||
// When all states are marked (i.e. Q0 is empty), the state is in reset
|
||||
// mode, meaning that it is an accepting state.
|
||||
// The states can immediately be unmarked, since the acceptance mark is
|
||||
// held by the incoming transition.
|
||||
if (is_reset_)
|
||||
for (unsigned s : states)
|
||||
states_.insert(~s);
|
||||
else
|
||||
states_ = states;
|
||||
}
|
||||
|
||||
int univ_remover_state::compare(const state* other) const
|
||||
{
|
||||
const auto o = down_cast<const univ_remover_state*>(other);
|
||||
assert(o);
|
||||
if (states_ < o->states())
|
||||
return -1;
|
||||
if (states_ > o->states())
|
||||
return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
size_t univ_remover_state::hash() const
|
||||
{
|
||||
size_t hash = 0;
|
||||
for (unsigned s : states_)
|
||||
hash ^= wang32_hash(s);
|
||||
return hash;
|
||||
}
|
||||
|
||||
state* univ_remover_state::clone() const
|
||||
{
|
||||
return new univ_remover_state(*this);
|
||||
}
|
||||
|
||||
const std::set<unsigned>& univ_remover_state::states() const
|
||||
{
|
||||
return states_;
|
||||
}
|
||||
|
||||
bool univ_remover_state::is_reset() const
|
||||
{
|
||||
return is_reset_;
|
||||
}
|
||||
|
||||
class univ_remover_succ_iterator : public twa_succ_iterator
|
||||
{
|
||||
private:
|
||||
bdd transitions_;
|
||||
bdd all_states_;
|
||||
bdd ap_;
|
||||
bdd all_letters_;
|
||||
bdd transition_;
|
||||
minato_isop isop_;
|
||||
const std::map<int, unsigned>& var_to_state_;
|
||||
univ_remover_state* dst_;
|
||||
|
||||
public:
|
||||
univ_remover_succ_iterator(const_twa_graph_ptr aut,
|
||||
const univ_remover_state* state,
|
||||
const std::vector<int>& state_to_var,
|
||||
const std::map<int, unsigned>& var_to_state,
|
||||
bdd all_states)
|
||||
: transitions_(bddtrue), all_states_(all_states), transition_(bddfalse),
|
||||
isop_(bddfalse), var_to_state_(var_to_state)
|
||||
{
|
||||
// Build the bdd transitions_, from which we extract the successors.
|
||||
for (unsigned s : state->states())
|
||||
{
|
||||
bdd state_bdd = bddfalse;
|
||||
bool marked = (int)s < 0;
|
||||
if (marked)
|
||||
s = ~s;
|
||||
for (const auto& e : aut->out(s))
|
||||
{
|
||||
bdd univ_bdd = bddtrue;
|
||||
for (const auto& d : aut->univ_dests(e.dst))
|
||||
univ_bdd &= bdd_ithvar(state_to_var[d] + (marked || e.acc));
|
||||
state_bdd |= e.cond & univ_bdd;
|
||||
}
|
||||
transitions_ &= state_bdd;
|
||||
}
|
||||
ap_ = bdd_exist(bdd_support(transitions_), all_states_);
|
||||
all_letters_ = bdd_exist(transitions_, all_states_);
|
||||
}
|
||||
|
||||
std::set<unsigned> bdd_to_state(bdd b) const
|
||||
{
|
||||
std::set<unsigned> state;
|
||||
while (b != bddtrue)
|
||||
{
|
||||
assert(bdd_low(b) == bddfalse);
|
||||
int v = bdd_var(b);
|
||||
b = bdd_high(b);
|
||||
auto it = var_to_state_.find(v);
|
||||
assert(it != var_to_state_.end());
|
||||
state.insert(it->second);
|
||||
}
|
||||
return state;
|
||||
}
|
||||
|
||||
void one_transition()
|
||||
{
|
||||
transition_ = isop_.next();
|
||||
if (transition_ != bddfalse || all_letters_ != bddfalse)
|
||||
{
|
||||
// If it was the last transition, try the next letter.
|
||||
if (transition_ == bddfalse)
|
||||
{
|
||||
bdd oneletter = bdd_satoneset(all_letters_, ap_, bddtrue);
|
||||
all_letters_ -= oneletter;
|
||||
// Get a sum of possible transitions matching this letter.
|
||||
isop_ = minato_isop(oneletter & transitions_);
|
||||
transition_ = isop_.next();
|
||||
}
|
||||
bdd dest_bdd = bdd_exist(transition_, ap_);
|
||||
std::set<unsigned> dest = bdd_to_state(dest_bdd);
|
||||
dst_ = new univ_remover_state(dest);
|
||||
}
|
||||
}
|
||||
|
||||
virtual bool first() override
|
||||
{
|
||||
one_transition();
|
||||
return transition_ != bddfalse;
|
||||
}
|
||||
|
||||
virtual bool next() override
|
||||
{
|
||||
one_transition();
|
||||
return transition_ != bddfalse;
|
||||
}
|
||||
|
||||
virtual bool done() const override
|
||||
{
|
||||
return transition_ == bddfalse && all_letters_ == bddfalse;
|
||||
}
|
||||
|
||||
virtual const state* dst() const override
|
||||
{
|
||||
return dst_;
|
||||
}
|
||||
|
||||
virtual bdd cond() const override
|
||||
{
|
||||
return bdd_exist(transition_, all_states_);
|
||||
}
|
||||
|
||||
virtual acc_cond::mark_t acc() const override
|
||||
{
|
||||
assert(dst_);
|
||||
return dst_->is_reset();
|
||||
}
|
||||
};
|
||||
|
||||
twa_univ_remover::twa_univ_remover(const const_twa_graph_ptr& aut)
|
||||
: twa(aut->get_dict()), aut_(aut)
|
||||
{
|
||||
assert(aut->num_sets() == 1);
|
||||
allocate_state_vars();
|
||||
}
|
||||
|
||||
void twa_univ_remover::allocate_state_vars()
|
||||
{
|
||||
auto d = aut_->get_dict();
|
||||
unsigned ns = aut_->num_states();
|
||||
state_to_var_.reserve(ns);
|
||||
all_states_ = bddtrue;
|
||||
bdd all_vars = bddtrue;
|
||||
for (unsigned s = 0; s < ns; ++s)
|
||||
{
|
||||
int v = d->register_anonymous_variables(2, this);
|
||||
// v and v + 1 respectively correspond to state s and ~s, i.e. s \in Q0
|
||||
// or s \in Q1 using the original paper's notation.
|
||||
all_states_ &= bdd_ithvar(v) & bdd_ithvar(v + 1);
|
||||
state_to_var_.push_back(v);
|
||||
var_to_state_[v] = s;
|
||||
var_to_state_[v + 1] = ~s;
|
||||
}
|
||||
}
|
||||
|
||||
const state* twa_univ_remover::get_init_state() const
|
||||
{
|
||||
std::set<unsigned> state;
|
||||
for (unsigned i : aut_->univ_dests(aut_->get_init_state_number()))
|
||||
state.insert(i);
|
||||
return new univ_remover_state(state);
|
||||
}
|
||||
|
||||
twa_succ_iterator* twa_univ_remover::succ_iter(const state* s) const
|
||||
{
|
||||
auto as = down_cast<const univ_remover_state*>(s);
|
||||
assert(as);
|
||||
return new univ_remover_succ_iterator(aut_, as, state_to_var_,
|
||||
var_to_state_, all_states_);
|
||||
}
|
||||
|
||||
std::string twa_univ_remover::format_state(const state* s) const
|
||||
{
|
||||
auto as = down_cast<const univ_remover_state*>(s);
|
||||
std::ostringstream fmt;
|
||||
bool first = true;
|
||||
for (unsigned s : as->states())
|
||||
{
|
||||
if (!first)
|
||||
fmt << ',';
|
||||
else
|
||||
first = false;
|
||||
if ((int) s < 0)
|
||||
fmt << '~' << ~s;
|
||||
else
|
||||
fmt << s;
|
||||
}
|
||||
return fmt.str();
|
||||
}
|
||||
|
||||
twa_univ_remover_ptr remove_univ_otf(const const_twa_graph_ptr& aut)
|
||||
{
|
||||
assert(aut->acc().is_buchi());
|
||||
auto res = std::make_shared<twa_univ_remover>(aut);
|
||||
res->copy_ap_of(aut);
|
||||
res->copy_acceptance_of(aut);
|
||||
return res;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -102,4 +102,60 @@ namespace spot
|
|||
SPOT_API
|
||||
twa_graph_ptr remove_alternation(const const_twa_graph_ptr& aut,
|
||||
bool named_states = false);
|
||||
|
||||
|
||||
// Remove universal edges on the fly.
|
||||
|
||||
class SPOT_API univ_remover_state: public state
|
||||
{
|
||||
protected:
|
||||
std::set<unsigned> states_;
|
||||
bool is_reset_;
|
||||
|
||||
public:
|
||||
univ_remover_state(const std::set<unsigned>& states);
|
||||
int compare(const state* other) const override;
|
||||
size_t hash() const override;
|
||||
state* clone() const override;
|
||||
const std::set<unsigned>& states() const;
|
||||
bool is_reset() const;
|
||||
};
|
||||
|
||||
class SPOT_API twa_univ_remover: public twa
|
||||
{
|
||||
|
||||
private:
|
||||
const_twa_graph_ptr aut_;
|
||||
std::vector<int> state_to_var_;
|
||||
std::map<int, unsigned> var_to_state_;
|
||||
bdd all_states_;
|
||||
|
||||
public:
|
||||
twa_univ_remover(const const_twa_graph_ptr& aut);
|
||||
void allocate_state_vars();
|
||||
const state* get_init_state() const override;
|
||||
twa_succ_iterator* succ_iter(const state* s) const override;
|
||||
std::string format_state(const state* s) const override;
|
||||
};
|
||||
|
||||
typedef std::shared_ptr<twa_univ_remover> twa_univ_remover_ptr;
|
||||
|
||||
/// \brief Remove universal edges on the fly from an automaton.
|
||||
///
|
||||
/// This function uses the Myiano & Hayashi (TCS 1984) breakpoint
|
||||
/// algorithm to construct a non-deterministic Büchi automaton from an
|
||||
/// alternating Büchi automaton on the fly.
|
||||
///
|
||||
/// \verbatim
|
||||
/// @Article{ miyano.84.tcs,
|
||||
/// title = "Alternating finite automata on ω-words",
|
||||
/// journal = "Theoretical Computer Science",
|
||||
/// volume = "32",
|
||||
/// number = "3",
|
||||
/// pages = "321 - 330",
|
||||
/// year = "1984",
|
||||
/// author = "Satoru Miyano and Takeshi Hayashi",
|
||||
/// }
|
||||
SPOT_API
|
||||
twa_univ_remover_ptr remove_univ_otf(const const_twa_graph_ptr& aut);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -169,3 +169,41 @@ State: 4
|
|||
State: 5
|
||||
[0&1] 0&1&2
|
||||
--END--"""
|
||||
|
||||
|
||||
# remove_univ_otf
|
||||
|
||||
aut = spot.automaton("""
|
||||
HOA: v1
|
||||
States: 3
|
||||
Start: 0 & 1
|
||||
Acceptance: 1 Inf(0)
|
||||
AP: 2 "a" "b"
|
||||
--BODY--
|
||||
State: 0
|
||||
[0] 0 {0}
|
||||
State: 1
|
||||
[1] 2 {0}
|
||||
State: 2
|
||||
[1] 2
|
||||
--END--
|
||||
""")
|
||||
|
||||
out = """HOA: v1
|
||||
States: 3
|
||||
Start: 0
|
||||
AP: 2 "a" "b"
|
||||
acc-name: Buchi
|
||||
Acceptance: 1 Inf(0)
|
||||
properties: trans-labels explicit-labels state-acc deterministic
|
||||
--BODY--
|
||||
State: 0 {0}
|
||||
[0&1] 1
|
||||
State: 1
|
||||
[0&1] 2
|
||||
State: 2
|
||||
[0&1] 2
|
||||
--END--"""
|
||||
|
||||
desalt = spot.remove_univ_otf(aut)
|
||||
assert(desalt.to_str('hoa') == out)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue