// -*- coding: utf-8 -*-
// Copyright (C) 2009, 2010, 2011, 2012, 2013 Laboratoire de Recherche
// et Développement de l'Epita.
// Copyright (C) 2003, 2004, 2006 Laboratoire d'Informatique de Paris
// 6 (LIP6), département Systèmes Répartis Coopératifs (SRC),
// Université Pierre et Marie Curie.
//
// This file is part of Spot, a model checking library.
//
// Spot is free software; you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 3 of the License, or
// (at your option) any later version.
//
// Spot is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
// License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see .
#ifndef SPOT_TGBA_TGBAEXPLICIT_HH
# define SPOT_TGBA_TGBAEXPLICIT_HH
#include
#include
#include "tgba.hh"
#include "sba.hh"
#include "tgba/formula2bdd.hh"
#include "misc/hash.hh"
#include "misc/bddop.hh"
#include "ltlast/formula.hh"
#include "ltlvisit/tostring.hh"
namespace spot
{
// How to destroy the label of a state.
template
struct destroy_key
{
void destroy(T t)
{
(void) t;
}
};
template<>
struct destroy_key
{
void destroy(const ltl::formula* t)
{
t->destroy();
}
};
/// States used by spot::explicit_graph implementation
/// \ingroup tgba_representation
template
class state_explicit: public spot::state
{
public:
state_explicit()
{
}
state_explicit(const Label& l):
label_(l)
{
}
virtual ~state_explicit()
{
}
virtual void destroy() const
{
}
typedef Label label_t;
typedef label_hash label_hash_t;
struct transition
{
bdd condition;
bdd acceptance_conditions;
const state_explicit* dest;
};
typedef std::list transitions_t;
transitions_t successors;
const Label& label() const
{
return label_;
}
bool empty() const
{
return successors.empty();
}
virtual int compare(const state* other) const
{
const state_explicit* s =
down_cast*>(other);
assert (s);
// Do not simply return "o - this", it might not fit in an int.
if (s < this)
return -1;
if (s > this)
return 1;
return 0;
}
virtual size_t hash() const
{
return
reinterpret_cast(this) - static_cast(0);
}
virtual state_explicit*
clone() const
{
return const_cast*>(this);
}
protected:
Label label_;
};
/// States labeled by an int
/// \ingroup tgba_representation
class state_explicit_number:
public state_explicit >
{
public:
state_explicit_number()
: state_explicit >()
{
}
state_explicit_number(int label)
: state_explicit >(label)
{
}
static const int default_val;
};
/// States labeled by a string
/// \ingroup tgba_representation
class state_explicit_string:
public state_explicit
{
public:
state_explicit_string():
state_explicit()
{
}
state_explicit_string(const std::string& label)
: state_explicit(label)
{
}
static const std::string default_val;
};
/// States labeled by a formula
/// \ingroup tgba_representation
class state_explicit_formula:
public state_explicit
{
public:
state_explicit_formula():
state_explicit()
{
}
state_explicit_formula(const ltl::formula* label)
: state_explicit(label)
{
}
static const ltl::formula* default_val;
};
/// Successor iterators used by spot::tgba_explicit.
/// \ingroup tgba_representation
template
class tgba_explicit_succ_iterator: public tgba_succ_iterator
{
public:
tgba_explicit_succ_iterator(const State* start,
bdd all_acc)
: start_(start),
all_acceptance_conditions_(all_acc)
{
}
virtual void first()
{
it_ = start_->successors.begin();
}
virtual void next()
{
++it_;
}
virtual bool done() const
{
return it_ == start_->successors.end();
}
virtual State* current_state() const
{
assert(!done());
const State* res = down_cast(it_->dest);
assert(res);
return const_cast(res);
}
virtual bdd current_condition() const
{
assert(!done());
return it_->condition;
}
virtual bdd current_acceptance_conditions() const
{
assert(!done());
return it_->acceptance_conditions;
}
typename State::transitions_t::const_iterator
get_iterator() const
{
return it_;
}
private:
const State* start_;
typename State::transitions_t::const_iterator it_;
bdd all_acceptance_conditions_;
};
/// Graph implementation for explicit automaton
/// \ingroup tgba_representation
template
class explicit_graph: public Type
{
public:
typedef typename State::label_t label_t;
typedef typename State::label_hash_t label_hash_t;
typedef typename State::transitions_t transitions_t;
typedef typename State::transition transition;
typedef State state;
protected:
typedef Sgi::hash_map ls_map;
typedef Sgi::hash_map alias_map;
typedef Sgi::hash_map > sl_map;
public:
explicit_graph(bdd_dict* dict)
: ls_(),
sl_(),
init_(0),
dict_(dict),
all_acceptance_conditions_(bddfalse),
all_acceptance_conditions_computed_(false),
neg_acceptance_conditions_(bddtrue)
{
}
State* add_default_init()
{
return add_state(State::default_val);
}
size_t num_states() const
{
return sl_.size();
}
transition*
create_transition(State* source, const State* dest)
{
transition t;
t.dest = dest;
t.condition = bddtrue;
t.acceptance_conditions = bddfalse;
typename transitions_t::iterator i =
source->successors.insert(source->successors.end(), t);
return &*i;
}
transition*
create_transition(const label_t& source, const label_t& dest)
{
// It's important that the source be created before the
// destination, so the first source encountered becomes the
// default initial state.
State* s = add_state(source);
return create_transition(s, add_state(dest));
}
transition*
get_transition(const tgba_explicit_succ_iterator* si)
{
return const_cast(&(*(si->get_iterator())));
}
transition*
get_transition(const tgba_succ_iterator* si)
{
const tgba_explicit_succ_iterator* tmp
= down_cast*>(si);
assert(tmp);
return get_transition(tmp);
}
void add_condition(transition* t, const ltl::formula* f)
{
t->condition &= formula_to_bdd(f, dict_, this);
f->destroy();
}
/// This assumes that all variables in \a f are known from dict.
void add_conditions(transition* t, bdd f)
{
dict_->register_propositions(f, this);
t->condition &= f;
}
bool has_acceptance_condition(const ltl::formula* f) const
{
return dict_->is_registered_acceptance_variable(f, this);
}
//old tgba explicit labeled interface
bool has_state(const label_t& name)
{
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
{
typename sl_map::const_iterator i = sl_.find(s);
assert(i != sl_.end());
return i->second;
}
const label_t& get_label(const spot::state* s) const
{
const State* se = down_cast(s);
assert(se);
return get_label(se);
}
void
complement_all_acceptance_conditions()
{
bdd all = this->all_acceptance_conditions();
typename ls_map::iterator i;
for (i = ls_.begin(); i != ls_.end(); ++i)
{
typename transitions_t::iterator i2;
for (i2 = i->second.successors.begin();
i2 != i->second.successors.end(); ++i2)
i2->acceptance_conditions = all - i2->acceptance_conditions;
}
}
void
merge_transitions()
{
typedef typename transitions_t::iterator trans_t;
typedef std::map acc_map;
typedef Sgi::hash_map > dest_map;
typename ls_map::iterator i;
for (i = ls_.begin(); i != ls_.end(); ++i)
{
const spot::state* last_dest = 0;
dest_map dm;
typename dest_map::iterator dmi = dm.end();
typename transitions_t::iterator t1 = i->second.successors.begin();
// Loop over all outgoing transitions (cond,acc,dest), and
// store them into dest_map[dest][acc] so that we can merge
// conditions.
while (t1 != i->second.successors.end())
{
const spot::state* dest = t1->dest;
if (dest != last_dest)
{
last_dest = dest;
dmi = dm.find(dest);
if (dmi == dm.end())
dmi = dm.insert(std::make_pair(dest, acc_map())).first;
}
int acc = t1->acceptance_conditions.id();
typename acc_map::iterator it = dmi->second.find(acc);
if (it == dmi->second.end())
{
dmi->second[acc] = t1;
++t1;
}
else
{
it->second->condition |= t1->condition;
t1 = i->second.successors.erase(t1);
}
}
}
}
/// Return the state_explicit for \a name, creating the state if
/// it does not exist.
State* add_state(const label_t& name)
{
typename ls_map::iterator i = ls_.find(name);
if (i == ls_.end())
{
typename alias_map::iterator j = alias_.find(name);
if (j != alias_.end())
return j->second;
State* res =
&(ls_.insert(std::make_pair(name, State(name))).first->second);
sl_[res] = name;
// The first state we add is the initial state.
// It can also be overridden with set_init_state().
if (!init_)
init_ = res;
return res;
}
return &(i->second);
}
State*
set_init_state(const label_t& state)
{
State* s = add_state(state);
init_ = s;
return s;
}
// tgba interface
virtual ~explicit_graph()
{
typename ls_map::iterator i = ls_.begin();
while (i != ls_.end())
{
label_t s = i->first;
i->second.destroy();
++i;
destroy_key dest;
dest.destroy(s);
}
this->dict_->unregister_all_my_variables(this);
// These have already been destroyed by subclasses.
// Prevent destroying by tgba::~tgba.
this->last_support_conditions_input_ = 0;
this->last_support_variables_input_ = 0;
}
virtual State* get_init_state() const
{
if (!init_)
const_cast*>(this)->add_default_init();
return init_;
}
virtual tgba_explicit_succ_iterator*
succ_iter(const spot::state* state,
const spot::state* global_state = 0,
const tgba* global_automaton = 0) const
{
const State* s = down_cast(state);
assert(s);
(void) global_state;
(void) global_automaton;
return
new tgba_explicit_succ_iterator(s,
this
->all_acceptance_conditions());
}
typedef std::string (*to_string_func_t)(const label_t& t);
void set_to_string_func(to_string_func_t f)
{
to_string_func_ = f;
}
to_string_func_t get_to_string_func() const
{
return to_string_func_;
}
virtual std::string format_state(const spot::state* state) const
{
const State* se = down_cast(state);
assert(se);
typename sl_map::const_iterator i = sl_.find(se);
assert(i != sl_.end());
assert(to_string_func_);
return to_string_func_(i->second);
}
/// Create an alias for a state. Any reference to \a alias_name
/// will act as a reference to \a real_name.
void add_state_alias(const label_t& alias, const label_t& real)
{
alias_[alias] = add_state(real);
}
/// \brief Copy the acceptance conditions of a tgba.
///
/// If used, this function should be called before creating any
/// transition.
void copy_acceptance_conditions_of(const tgba *a)
{
assert(neg_acceptance_conditions_ == bddtrue);
assert(all_acceptance_conditions_computed_ == false);
bdd f = a->neg_acceptance_conditions();
this->dict_->register_acceptance_variables(f, this);
neg_acceptance_conditions_ = f;
}
/// Acceptance conditions handling
void set_acceptance_conditions(bdd acc)
{
assert(neg_acceptance_conditions_ == bddtrue);
assert(all_acceptance_conditions_computed_ == false);
this->dict_->register_acceptance_variables(bdd_support(acc), this);
neg_acceptance_conditions_ = compute_neg_acceptance_conditions(acc);
all_acceptance_conditions_computed_ = true;
all_acceptance_conditions_ = acc;
}
void add_acceptance_condition(transition* t, const ltl::formula* f)
{
bdd c = get_acceptance_condition(f);
t->acceptance_conditions |= c;
}
/// This assumes that all acceptance conditions in \a f are known from
/// dict.
void add_acceptance_conditions(transition* t, bdd f)
{
bdd sup = bdd_support(f);
this->dict_->register_acceptance_variables(sup, this);
while (sup != bddtrue)
{
neg_acceptance_conditions_ &= bdd_nithvar(bdd_var(sup));
sup = bdd_high(sup);
}
t->acceptance_conditions |= f;
}
virtual bdd all_acceptance_conditions() const
{
if (!all_acceptance_conditions_computed_)
{
all_acceptance_conditions_ =
compute_all_acceptance_conditions(neg_acceptance_conditions_);
all_acceptance_conditions_computed_ = true;
}
return all_acceptance_conditions_;
}
virtual bdd_dict* get_dict() const
{
return this->dict_;
}
virtual bdd neg_acceptance_conditions() const
{
return neg_acceptance_conditions_;
}
void
declare_acceptance_condition(const ltl::formula* f)
{
int v = this->dict_->register_acceptance_variable(f, this);
f->destroy();
bdd neg = bdd_nithvar(v);
neg_acceptance_conditions_ &= neg;
// Append neg to all acceptance conditions.
// FIXME: Declaring acceptance conditions after the automaton
// has been constructed is very slow because we traverse the
// entire automaton for each new acceptance condition. It would
// be better to fix the automaton in a single pass after all
// acceptance conditions have been declared.
typename ls_map::iterator i;
for (i = this->ls_.begin(); i != this->ls_.end(); ++i)
{
typename transitions_t::iterator i2;
for (i2 = i->second.successors.begin();
i2 != i->second.successors.end(); ++i2)
i2->acceptance_conditions &= neg;
}
all_acceptance_conditions_computed_ = false;
}
bdd get_acceptance_condition(const ltl::formula* f)
{
bdd_dict::fv_map::iterator i = this->dict_->acc_map.find(f);
assert(this->has_acceptance_condition(f));
f->destroy();
bdd v = bdd_ithvar(i->second);
v &= bdd_exist(neg_acceptance_conditions_, v);
return v;
}
protected:
virtual bdd compute_support_conditions(const spot::state* in) const
{
const State* s = down_cast(in);
assert(s);
const transitions_t& st = s->successors;
bdd res = bddfalse;
typename transitions_t::const_iterator i;
for (i = st.begin(); i != st.end(); ++i)
res |= i->condition;
return res;
}
virtual bdd compute_support_variables(const spot::state* in) const
{
const State* s = down_cast(in);
assert(s);
const transitions_t& st = s->successors;
bdd res = bddtrue;
typename transitions_t::const_iterator i;
for (i = st.begin(); i != st.end(); ++i)
res &= bdd_support(i->condition);
return res;
}
ls_map ls_;
alias_map alias_;
sl_map sl_;
State* init_;
bdd_dict* dict_;
mutable bdd all_acceptance_conditions_;
mutable bool all_acceptance_conditions_computed_;
bdd neg_acceptance_conditions_;
to_string_func_t to_string_func_;
};
template
class tgba_explicit: public explicit_graph
{
public:
tgba_explicit(bdd_dict* dict): explicit_graph(dict)
{
}
virtual ~tgba_explicit()
{
}
private:
// Disallow copy.
tgba_explicit(const tgba_explicit& other);
tgba_explicit& operator=(const tgba_explicit& other);
};
template
class sba_explicit: public explicit_graph
{
public:
sba_explicit(bdd_dict* dict): explicit_graph(dict)
{
}
virtual ~sba_explicit()
{
}
virtual bool state_is_accepting(const spot::state* s) const
{
const State* st = down_cast(s);
// Assume that an accepting state has only accepting output transitions
// So we need only to check one to decide
if (st->successors.empty())
return false;
return (st->successors.front().acceptance_conditions
== this->all_acceptance_conditions());
}
private:
// Disallow copy.
sba_explicit(const sba_explicit& other);
sba_explicit& operator=(const sba_explicit& other);
};
// It is tempting to write
//
// templateclass graph, typename Type>
// class explicit_conf: public graph
//
// to simplify the typedefs at the end of the file, however swig
// cannot parse this syntax.
/// Configuration of graph automata
/// \ingroup tgba_representation
template
class explicit_conf: public graph
{
public:
explicit_conf(bdd_dict* d): graph(d)
{
this->set_to_string_func(to_string);
};
static std::string to_string(const typename Type::label_t& l)
{
std::stringstream ss;
ss << l;
return ss.str();
}
};
template
class explicit_conf: public graph
{
public:
explicit_conf(bdd_dict* d): graph(d)
{
this->set_to_string_func(to_string);
};
static std::string to_string(const std::string& l)
{
return l;
}
};
template
class explicit_conf: public graph
{
public:
explicit_conf(bdd_dict* d): graph(d)
{
this->set_to_string_func(to_string);
};
// Enable UTF8 output for the formulae that label states.
void enable_utf8()
{
this->set_to_string_func(to_utf8_string);
}
static std::string to_string(const ltl::formula* const& l)
{
return ltl::to_string(l);
}
static std::string to_utf8_string(const ltl::formula* const& l)
{
return ltl::to_utf8_string(l);
}
};
// Typedefs for tgba
typedef explicit_conf,
state_explicit_string> tgba_explicit_string;
typedef explicit_conf,
state_explicit_formula> tgba_explicit_formula;
typedef explicit_conf,
state_explicit_number> tgba_explicit_number;
// Typedefs for sba
typedef explicit_conf,
state_explicit_string> sba_explicit_string;
typedef explicit_conf,
state_explicit_formula> sba_explicit_formula;
typedef explicit_conf,
state_explicit_number> sba_explicit_number;
}
#endif // SPOT_TGBA_TGBAEXPLICIT_HH