rename tgba files as twa

Automatic mass renaming.

* src/graphtest/tgbagraph.cc, src/tgba/acc.cc, src/tgba/acc.hh,
src/tgba/bdddict.cc, src/tgba/bdddict.hh, src/tgba/bddprint.cc,
src/tgba/bddprint.hh, src/tgba/formula2bdd.cc,
src/tgba/formula2bdd.hh, src/tgba/fwd.hh, src/tgba/Makefile.am,
src/tgba/taatgba.cc, src/tgba/taatgba.hh, src/tgba/tgba.cc,
src/tgba/tgbagraph.cc, src/tgba/tgbagraph.hh, src/tgba/tgba.hh,
src/tgba/tgbamask.cc, src/tgba/tgbamask.hh, src/tgba/tgbaproduct.cc,
src/tgba/tgbaproduct.hh, src/tgba/tgbaproxy.cc, src/tgba/tgbaproxy.hh,
src/tgba/tgbasafracomplement.cc, src/tgba/tgbasafracomplement.hh,
src/tgba/.cvsignore: Rename as...
* src/graphtest/twagraph.cc, src/twa/acc.cc, src/twa/acc.hh,
src/twa/bdddict.cc, src/twa/bdddict.hh, src/twa/bddprint.cc,
src/twa/bddprint.hh, src/twa/formula2bdd.cc, src/twa/formula2bdd.hh,
src/twa/fwd.hh, src/twa/Makefile.am, src/twa/taatgba.cc,
src/twa/taatgba.hh, src/twa/twa.cc, src/twa/twagraph.cc,
src/twa/twagraph.hh, src/twa/twa.hh, src/twa/twamask.cc,
src/twa/twamask.hh, src/twa/twaproduct.cc, src/twa/twaproduct.hh,
src/twa/twaproxy.cc, src/twa/twaproxy.hh,
src/twa/twasafracomplement.cc, src/twa/twasafracomplement.hh,
src/twa/.cvsignore: ... these.
* README, bench/stutter/stutter_invariance_randomgraph.cc,
configure.ac, iface/ltsmin/modelcheck.cc, src/Makefile.am,
src/bin/common_aoutput.cc, src/bin/common_conv.hh,
src/bin/common_trans.hh, src/bin/dstar2tgba.cc, src/bin/ltl2tgta.cc,
src/bin/randaut.cc, src/dstarparse/dra2ba.cc,
src/dstarparse/public.hh, src/graphtest/Makefile.am,
src/graphtest/ngraph.cc, src/hoaparse/hoaparse.yy,
src/hoaparse/public.hh, src/kripke/fairkripke.hh,
src/kripke/kripkeexplicit.cc, src/kripke/kripkeprint.cc,
src/kripkeparse/kripkeparse.yy, src/ltlvisit/apcollect.cc,
src/ltlvisit/apcollect.hh, src/ltlvisit/exclusive.hh,
src/ltlvisit/simplify.cc, src/ltlvisit/simplify.hh,
src/priv/accmap.hh, src/ta/ta.hh, src/ta/taexplicit.cc,
src/ta/taexplicit.hh, src/ta/tgta.hh, src/ta/tgtaexplicit.cc,
src/ta/tgtaexplicit.hh, src/ta/tgtaproduct.hh, src/taalgos/dotty.cc,
src/taalgos/emptinessta.cc, src/taalgos/minimize.cc,
src/taalgos/tgba2ta.cc, src/taalgos/tgba2ta.hh,
src/tgbaalgos/are_isomorphic.cc, src/tgbaalgos/are_isomorphic.hh,
src/tgbaalgos/bfssteps.cc, src/tgbaalgos/canonicalize.cc,
src/tgbaalgos/canonicalize.hh, src/tgbaalgos/cleanacc.hh,
src/tgbaalgos/complete.hh, src/tgbaalgos/compsusp.cc,
src/tgbaalgos/compsusp.hh, src/tgbaalgos/degen.cc,
src/tgbaalgos/degen.hh, src/tgbaalgos/dotty.cc,
src/tgbaalgos/dotty.hh, src/tgbaalgos/dtbasat.cc,
src/tgbaalgos/dtbasat.hh, src/tgbaalgos/dtgbacomp.hh,
src/tgbaalgos/dtgbasat.cc, src/tgbaalgos/dtgbasat.hh,
src/tgbaalgos/dupexp.cc, src/tgbaalgos/dupexp.hh,
src/tgbaalgos/emptiness.cc, src/tgbaalgos/emptiness.hh,
src/tgbaalgos/gtec/sccstack.hh, src/tgbaalgos/gtec/status.hh,
src/tgbaalgos/gv04.cc, src/tgbaalgos/gv04.hh, src/tgbaalgos/hoa.cc,
src/tgbaalgos/hoa.hh, src/tgbaalgos/isdet.hh, src/tgbaalgos/lbtt.cc,
src/tgbaalgos/lbtt.hh, src/tgbaalgos/ltl2taa.hh,
src/tgbaalgos/ltl2tgba_fm.cc, src/tgbaalgos/ltl2tgba_fm.hh,
src/tgbaalgos/magic.cc, src/tgbaalgos/magic.hh, src/tgbaalgos/mask.hh,
src/tgbaalgos/minimize.hh, src/tgbaalgos/ndfs_result.hxx,
src/tgbaalgos/neverclaim.cc, src/tgbaalgos/neverclaim.hh,
src/tgbaalgos/postproc.hh, src/tgbaalgos/powerset.cc,
src/tgbaalgos/powerset.hh, src/tgbaalgos/product.cc,
src/tgbaalgos/product.hh, src/tgbaalgos/projrun.cc,
src/tgbaalgos/projrun.hh, src/tgbaalgos/randomgraph.cc,
src/tgbaalgos/randomgraph.hh, src/tgbaalgos/randomize.hh,
src/tgbaalgos/reachiter.hh, src/tgbaalgos/reducerun.cc,
src/tgbaalgos/reducerun.hh, src/tgbaalgos/relabel.hh,
src/tgbaalgos/remfin.hh, src/tgbaalgos/remprop.hh,
src/tgbaalgos/replayrun.cc, src/tgbaalgos/replayrun.hh,
src/tgbaalgos/sbacc.hh, src/tgbaalgos/scc.cc, src/tgbaalgos/scc.hh,
src/tgbaalgos/sccfilter.hh, src/tgbaalgos/sccinfo.cc,
src/tgbaalgos/sccinfo.hh, src/tgbaalgos/se05.cc,
src/tgbaalgos/se05.hh, src/tgbaalgos/simulation.cc,
src/tgbaalgos/simulation.hh, src/tgbaalgos/stats.cc,
src/tgbaalgos/stats.hh, src/tgbaalgos/stripacc.hh,
src/tgbaalgos/stutter.cc, src/tgbaalgos/stutter.hh,
src/tgbaalgos/tau03.cc, src/tgbaalgos/tau03.hh,
src/tgbaalgos/tau03opt.cc, src/tgbaalgos/tau03opt.hh,
src/tgbaalgos/totgba.cc, src/tgbaalgos/totgba.hh,
src/tgbaalgos/weight.hh, src/tgbaalgos/word.cc, src/tgbatest/acc.cc,
src/tgbatest/complementation.cc, src/tgbatest/emptchk.cc,
src/tgbatest/ltl2tgba.cc, src/tgbatest/taatgba.cc,
wrap/python/spot_impl.i: Adjust.
This commit is contained in:
Alexandre Duret-Lutz 2015-04-21 20:03:06 +02:00
parent 8839f50a0f
commit 703fbd0e99
154 changed files with 232 additions and 231 deletions

6
src/twa/.cvsignore Normal file
View file

@ -0,0 +1,6 @@
.deps
.libs
*.lo
*.la
Makefile
Makefile.in

6
src/twa/.gitignore vendored Normal file
View file

@ -0,0 +1,6 @@
.deps
.libs
*.lo
*.la
Makefile
Makefile.in

54
src/twa/Makefile.am Normal file
View file

@ -0,0 +1,54 @@
## -*- coding: utf-8 -*-
## Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014, 2015 Laboratoire
## de Recherche et Développement de l'Epita (LRDE).
## Copyright (C) 2003, 2004 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 <http://www.gnu.org/licenses/>.
AM_CPPFLAGS = -I$(srcdir)/.. -I.. $(BUDDY_CPPFLAGS)
AM_CXXFLAGS = $(WARNING_CXXFLAGS)
twadir = $(pkgincludedir)/twa
twa_HEADERS = \
acc.hh \
bdddict.hh \
bddprint.hh \
formula2bdd.hh \
fwd.hh \
taatgba.hh \
twa.hh \
twagraph.hh \
twamask.hh \
twaproxy.hh \
twaproduct.hh \
twasafracomplement.hh
noinst_LTLIBRARIES = libtwa.la
libtwa_la_SOURCES = \
acc.cc \
bdddict.cc \
bddprint.cc \
formula2bdd.cc \
taatgba.cc \
twa.cc \
twagraph.cc \
twaproduct.cc \
twamask.cc \
twaproxy.cc \
twasafracomplement.cc

1015
src/twa/acc.cc Normal file

File diff suppressed because it is too large Load diff

1003
src/twa/acc.hh Normal file

File diff suppressed because it is too large Load diff

465
src/twa/bdddict.cc Normal file
View file

@ -0,0 +1,465 @@
// -*- coding: utf-8 -*-
// Copyright (C) 2009, 2012, 2013, 2014, 2015 Laboratoire de Recherche
// et Développement de l'Epita (LRDE).
// Copyright (C) 2003, 2004, 2005, 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 <http://www.gnu.org/licenses/>.
#include <ostream>
#include <sstream>
#include <cassert>
#include <ltlvisit/tostring.hh>
#include <ltlvisit/tostring.hh>
#include <ltlast/atomic_prop.hh>
#include <ltlenv/defaultenv.hh>
#include "priv/bddalloc.hh"
#include "bdddict.hh"
namespace spot
{
// Empty anonymous namespace so that the style checker does no
// complain about bdd_dict_priv (which should not be in an anonymous
// namespace).
namespace
{
}
class bdd_dict_priv: public bdd_allocator
{
public:
class anon_free_list : public spot::free_list
{
public:
// WARNING: We need a default constructor so this can be used in
// a hash; but we should ensure that no object in the hash is
// constructed with p==0.
anon_free_list(bdd_dict_priv* p = 0)
: priv_(p)
{
}
virtual int
extend(int n)
{
assert(priv_);
int b = priv_->allocate_variables(n);
free_anonymous_list_of_type::iterator i;
for (i = priv_->free_anonymous_list_of.begin();
i != priv_->free_anonymous_list_of.end(); ++i)
if (&i->second != this)
i->second.insert(b, n);
return b;
}
private:
bdd_dict_priv* priv_;
};
bdd_dict_priv()
{
free_anonymous_list_of[0] = anon_free_list(this);
}
/// List of unused anonymous variable number for each automaton.
typedef std::map<const void*, anon_free_list> free_anonymous_list_of_type;
free_anonymous_list_of_type free_anonymous_list_of;
};
bdd_dict::bdd_dict()
// Initialize priv_ first, because it also initializes BuDDy
: priv_(new bdd_dict_priv()),
bdd_map(bdd_varnum())
{
}
bdd_dict::~bdd_dict()
{
assert_emptiness();
delete priv_;
}
int
bdd_dict::register_proposition(const ltl::formula* f, const void* for_me)
{
int num;
// Do not build a variable that already exists.
fv_map::iterator sii = var_map.find(f);
if (sii != var_map.end())
{
num = sii->second;
}
else
{
f = f->clone();
num = priv_->allocate_variables(1);
var_map[f] = num;
bdd_map.resize(bdd_varnum());
bdd_map[num].type = var;
bdd_map[num].f = f;
}
bdd_map[num].refs.insert(for_me);
return num;
}
int
bdd_dict::has_registered_proposition(const ltl::formula* f,
const void* me)
{
auto ssi = var_map.find(f);
if (ssi == var_map.end())
return -1;
int num = ssi->second;
auto& r = bdd_map[num].refs;
if (r.find(me) == r.end())
return -1;
return num;
}
void
bdd_dict::register_propositions(bdd f, const void* for_me)
{
if (f == bddtrue || f == bddfalse)
return;
int v = bdd_var(f);
assert(unsigned(v) < bdd_map.size());
bdd_info& i = bdd_map[v];
assert(i.type == var);
i.refs.insert(for_me);
register_propositions(bdd_high(f), for_me);
register_propositions(bdd_low(f), for_me);
}
int
bdd_dict::register_acceptance_variable(const ltl::formula* f,
const void* for_me)
{
int num;
// Do not build an acceptance variable that already exists.
fv_map::iterator sii = acc_map.find(f);
if (sii != acc_map.end())
{
num = sii->second;
}
else
{
f = f->clone();
num = priv_->allocate_variables(1);
acc_map[f] = num;
bdd_map.resize(bdd_varnum());
bdd_info& i = bdd_map[num];
i.type = acc;
i.f = f;
i.clone_counts = 0;
}
bdd_map[num].refs.insert(for_me);
return num;
}
void
bdd_dict::register_acceptance_variables(bdd f, const void* for_me)
{
if (f == bddtrue || f == bddfalse)
return;
int v = bdd_var(f);
assert(unsigned(v) < bdd_map.size());
bdd_info& i = bdd_map[v];
assert(i.type == acc);
i.refs.insert(for_me);
register_acceptance_variables(bdd_high(f), for_me);
register_acceptance_variables(bdd_low(f), for_me);
}
int
bdd_dict::register_clone_acc(int v, const void* for_me)
{
assert(unsigned(v) < bdd_map.size());
bdd_info& i = bdd_map[v];
assert(i.type == acc);
std::ostringstream s;
// FIXME: We could be smarter and reuse unused "$n" numbers.
s << ltl::to_string(i.f) << '$' << ++i.clone_counts;
const ltl::formula* f =
ltl::atomic_prop::instance(s.str(),
ltl::default_environment::instance());
int res = register_acceptance_variable(f, for_me);
f->destroy();
return res;
}
const ltl::formula*
bdd_dict::oneacc_to_formula(int var) const
{
assert(unsigned(var) < bdd_map.size());
const bdd_info& i = bdd_map[var];
assert(i.type == acc);
return i.f;
}
const ltl::formula*
bdd_dict::oneacc_to_formula(bdd oneacc) const
{
assert(oneacc != bddfalse);
while (bdd_high(oneacc) == bddfalse)
{
oneacc = bdd_low(oneacc);
assert(oneacc != bddfalse);
}
return oneacc_to_formula(bdd_var(oneacc));
}
int
bdd_dict::register_anonymous_variables(int n, const void* for_me)
{
typedef bdd_dict_priv::free_anonymous_list_of_type fal;
fal::iterator i = priv_->free_anonymous_list_of.find(for_me);
if (i == priv_->free_anonymous_list_of.end())
{
i = (priv_->free_anonymous_list_of.insert
(fal::value_type(for_me,
priv_->free_anonymous_list_of[0]))).first;
}
int res = i->second.register_n(n);
bdd_map.resize(bdd_varnum());
while (n--)
{
bdd_map[res + n].type = anon;
bdd_map[res + n].refs.insert(for_me);
}
return res;
}
void
bdd_dict::register_all_variables_of(const void* from_other,
const void* for_me)
{
auto j = priv_->free_anonymous_list_of.find(from_other);
if (j != priv_->free_anonymous_list_of.end())
priv_->free_anonymous_list_of[for_me] = j->second;
for (auto& i: bdd_map)
{
ref_set& s = i.refs;
if (s.find(from_other) != s.end())
s.insert(for_me);
}
}
void
bdd_dict::register_all_propositions_of(const void* from_other,
const void* for_me)
{
for (auto& i: bdd_map)
{
if (i.type != var_type::var)
continue;
ref_set& s = i.refs;
if (s.find(from_other) != s.end())
s.insert(for_me);
}
}
void
bdd_dict::unregister_variable(int v, const void* me)
{
assert(unsigned(v) < bdd_map.size());
ref_set& s = bdd_map[v].refs;
// If the variable is not owned by me, ignore it.
ref_set::iterator si = s.find(me);
if (si == s.end())
return;
s.erase(si);
// If var is anonymous, we should reinsert it into the free list
// of ME's anonymous variables.
if (bdd_map[v].type == anon)
priv_->free_anonymous_list_of[me].release_n(v, 1);
if (!s.empty())
return;
// ME was the last user of this variable.
// Let's free it. First, we need to find
// if this is a Var or an Acc variable.
int n = 1;
const ltl::formula* f = 0;
switch (bdd_map[v].type)
{
case var:
f = bdd_map[v].f;
var_map.erase(f);
break;
case acc:
f = bdd_map[v].f;
acc_map.erase(f);
break;
case anon:
{
bdd_dict_priv::free_anonymous_list_of_type::iterator i;
// Nobody use this variable as an anonymous variable
// anymore, so remove it entirely from the anonymous
// free list so it can be used for something else.
for (i = priv_->free_anonymous_list_of.begin();
i != priv_->free_anonymous_list_of.end(); ++i)
i->second.remove(v, n);
break;
}
}
// Actually release the associated BDD variables, and the
// formula itself.
priv_->release_variables(v, n);
if (f)
f->destroy();
while (n--)
bdd_map[v + n].type = anon;
}
void
bdd_dict::unregister_all_my_variables(const void* me)
{
unsigned s = bdd_map.size();
for (unsigned i = 0; i < s; ++i)
unregister_variable(i, me);
priv_->free_anonymous_list_of.erase(me);
}
void
bdd_dict::unregister_all_typed_variables(var_type type, const void* me)
{
unsigned s = bdd_map.size();
for (unsigned i = 0; i < s; ++i)
if (bdd_map[i].type == type)
unregister_variable(i, me);
if (type == anon)
priv_->free_anonymous_list_of.erase(me);
}
std::ostream&
bdd_dict::dump(std::ostream& os) const
{
os << "Variable Map:\n";
unsigned s = bdd_map.size();
for (unsigned i = 0; i < s; ++i)
{
os << ' ' << i << ' ';
const bdd_info& r = bdd_map[i];
switch (r.type)
{
case anon:
os << (r.refs.empty() ? "Free" : "Anon");
break;
case acc:
os << "Acc[" << to_string(r.f) << ']';
break;
case var:
os << "Var[" << to_string(r.f) << ']';
break;
}
if (!r.refs.empty())
{
os << " x" << r.refs.size() << " {";
for (ref_set::const_iterator si = r.refs.begin();
si != r.refs.end(); ++si)
os << ' ' << *si;
os << " }";
}
os << '\n';
}
os << "Anonymous lists:\n";
bdd_dict_priv::free_anonymous_list_of_type::const_iterator ai;
for (ai = priv_->free_anonymous_list_of.begin();
ai != priv_->free_anonymous_list_of.end(); ++ai)
{
os << " [" << ai->first << "] ";
ai->second.dump_free_list(os) << std::endl;
}
os << "Free list:\n";
priv_->dump_free_list(os);
os << '\n';
return os;
}
void
bdd_dict::assert_emptiness() const
{
bool fail = false;
bool var_seen = false;
bool acc_seen = false;
bool refs_seen = false;
unsigned s = bdd_map.size();
for (unsigned i = 0; i < s; ++i)
{
switch (bdd_map[i].type)
{
case var:
var_seen = true;
break;
case acc:
acc_seen = true;
break;
case anon:
break;
}
refs_seen |= !bdd_map[i].refs.empty();
}
if (var_map.empty() && acc_map.empty())
{
if (var_seen)
{
std::cerr << "var_map is empty but Var in map" << std::endl;
fail = true;
}
if (acc_seen)
{
std::cerr << "acc_map is empty but Acc in map" << std::endl;
fail = true;
}
if (refs_seen)
{
std::cerr << "maps are empty but var_refs is not" << std::endl;
fail = true;
}
if (!fail)
return;
}
else
{
std::cerr << "some maps are not empty" << std::endl;
}
dump(std::cerr);
abort();
}
}

357
src/twa/bdddict.hh Normal file
View file

@ -0,0 +1,357 @@
// -*- coding: utf-8 -*-
// Copyright (C) 2011, 2012, 2013, 2014, 2015 Laboratoire de Recherche
// et Développement de l'Epita (LRDE).
// 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 <http://www.gnu.org/licenses/>.
#pragma once
#include <list>
#include <set>
#include <map>
#include <iosfwd>
#include <bddx.h>
#include <vector>
#include <memory>
#include "ltlast/formula.hh"
namespace spot
{
/// \brief Private data for bdd_dict.
class bdd_dict_priv;
/// \ingroup twa_essentials
/// \brief Map BDD variables to formulae.
///
/// The BDD library uses integers to designate Boolean variables in
/// its decision diagrams. This class is used to map such integers
/// to objects actually used in Spot. These objects are usually
/// atomic propositions, but they can also be acceptance conditions.
///
/// When a BDD variable is registered using a bdd_dict, it is always
/// associated to a "user" (or "owner") object. This is done by
/// supplying the bdd_dict with a pointer to the intended user of
/// the variable. When the user object dies, it should release the
/// BDD variables it was using by calling (for instance)
/// unregister_all_my_variables(), giving the same pointer.
/// Variables can also by unregistered one by one using
/// unregister_variable().
class SPOT_API bdd_dict
{
bdd_dict_priv* priv_;
public:
bdd_dict();
/// \brief Destroy the BDD dict.
///
/// This always calls assert_emptiness() to diagnose cases where
/// variables have not been unregistered.
~bdd_dict();
/// Formula-to-BDD-variable maps.
typedef std::map<const ltl::formula*, int> fv_map;
/// BDD-variable-to-formula maps.
typedef std::map<int, const ltl::formula*> vf_map;
fv_map var_map; ///< Maps atomic propositions to BDD variables
fv_map acc_map; ///< Maps acceptance conditions to BDD variables
/// BDD-variable reference counts.
typedef std::set<const void*> ref_set;
enum var_type { anon = 0, var, acc };
struct bdd_info {
bdd_info() : type(anon) {}
var_type type;
const ltl::formula* f; // Used unless t==anon.
ref_set refs;
int clone_counts;
};
typedef std::vector<bdd_info> bdd_info_map;
// Map BDD variables to their meaning.
bdd_info_map bdd_map;
/// \brief Register an atomic proposition.
///
/// Return (and maybe allocate) a BDD variable designating formula
/// \a f. The \a for_me argument should point to the object using
/// this BDD variable, this is used for reference counting. It is
/// perfectly safe to call this function several time with the same
/// arguments.
///
/// \return The variable number. Use bdd_ithvar() or bdd_nithvar()
/// to convert this to a BDD.
/// @{
int register_proposition(const ltl::formula* f, const void* for_me);
template <typename T>
int register_proposition(const ltl::formula* f,
std::shared_ptr<T> for_me)
{
return register_proposition(f, for_me.get());
}
/// @}
/// \brief Register BDD variables as atomic propositions.
///
/// Register all variables occurring in \a f as atomic propositions
/// used by \a for_me. This assumes that these atomic propositions
/// are already known from the dictionary (i.e., they have already
/// been registered by register_proposition() for another
/// automaton).
/// @{
void register_propositions(bdd f, const void* for_me);
template <typename T>
void register_propositions(bdd f, std::shared_ptr<T> for_me)
{
register_propositions(f, for_me.get());
}
/// @}
/// \brief whether a proposition has already been registered
///
/// If \a f has been registered for \a me, this returns
/// a non-negative value that is the BDD variable number.
/// Otherwise this returns -1.
/// @{
int has_registered_proposition(const ltl::formula* f,
const void* me);
template <typename T>
int has_registered_proposition(const ltl::formula* f,
std::shared_ptr<T> for_me)
{
return has_registered_proposition(f, for_me.get());
}
/// @}
/// \brief Register an acceptance variable.
///
/// Return (and maybe allocate) a BDD variable designating an
/// acceptance set associated to formula \a f. The \a for_me
/// argument should point to the object using this BDD variable,
/// this is used for reference counting. It is perfectly safe to
/// call this function several time with the same arguments.
///
/// \return The variable number. Use bdd_ithvar() or bdd_nithvar()
/// to convert this to a BDD.
/// @{
int register_acceptance_variable(const ltl::formula* f, const void* for_me);
template <typename T>
int register_acceptance_variable(const ltl::formula* f,
std::shared_ptr<T> for_me)
{
return register_acceptance_variable(f, for_me.get());
}
/// @}
/// \brief Clone an acceptance variable VAR for FOR_ME.
///
/// This is used in products TGBAs when both operands share the
/// same acceptance variables but they need to be distinguished in
/// the result.
/// @{
int register_clone_acc(int var, const void* for_me);
template <typename T>
int register_clone_acc(int var, std::shared_ptr<T> for_me)
{
return register_clone_acc(var, for_me.get());
}
/// @}
/// \brief Register BDD variables as acceptance variables.
///
/// Register all variables occurring in \a f as acceptance variables
/// used by \a for_me. This assumes that these acceptance variables
/// are already known from the dictionary (i.e., they have already
/// been registered by register_acceptance_variable() for another
/// automaton).
/// @{
void register_acceptance_variables(bdd f, const void* for_me);
template <typename T>
void register_acceptance_variables(bdd f, std::shared_ptr<T> for_me)
{
register_acceptance_variables(f, for_me.get());
}
/// @}
/// \brief Convert one acceptance condition into the associated
/// formula.
///
/// This version accepts a conjunction of Acc variables, in which
/// only one must be positive. This positive variable will be
/// converted back into the associated formula.
///
/// The returned formula is not cloned, and is valid until the BDD
/// variable used in \a oneacc are unregistered.
const ltl::formula* oneacc_to_formula(bdd oneacc) const;
/// \brief Convert one acceptance condition into the associated
/// formula.
///
/// This version takes the number of a BDD variable that must has
/// been returned by a call to register_acceptance_variable().
///
/// The returned formula is not cloned, and is valid until the BDD
/// variable \a var is unregistered.
const ltl::formula* oneacc_to_formula(int var) const;
/// \brief Register anonymous BDD variables.
///
/// Return (and maybe allocate) \a n consecutive BDD variables which
/// will be used only by \a for_me.
///
/// \return The variable number. Use bdd_ithvar() or bdd_nithvar()
/// to convert this to a BDD.
/// @{
int register_anonymous_variables(int n, const void* for_me);
template <typename T>
int register_anonymous_variables(int n, std::shared_ptr<T> for_me)
{
return register_anonymous_variables(n, for_me.get());
}
/// @}
/// \brief Duplicate the variable usage of another object.
///
/// This tells this dictionary that the \a for_me object will be
/// using the same BDD variables as the \a from_other objects.
/// This ensures that the variables won't be freed when \a
/// from_other is deleted if \a from_other is still alive.
/// @{
void register_all_variables_of(const void* from_other, const void* for_me);
template <typename T>
void register_all_variables_of(const void* from_other,
std::shared_ptr<T> for_me)
{
register_all_variables_of(from_other, for_me.get());
}
template <typename T>
void register_all_variables_of(std::shared_ptr<T> from_other,
const void* for_me)
{
register_all_variables_of(from_other.get(), for_me);
}
template <typename T, typename U>
void register_all_variables_of(std::shared_ptr<T> from_other,
std::shared_ptr<U> for_me)
{
register_all_variables_of(from_other.get(), for_me.get());
}
/// @}
/// \brief Register the same propositions as another object.
///
/// This tells this dictionary that the \a for_me object will be
/// using the same BDD variable used for atomic propositions by
/// the \a from_other object. This ensures that the variables
/// won't be freed when \a from_other is deleted if \a from_other
/// is still alive.
/// @{
void register_all_propositions_of(const void* from_other,
const void* for_me);
template <typename T>
void register_all_propositions_of(const void* from_other,
std::shared_ptr<T> for_me)
{
register_all_propositions_of(from_other, for_me.get());
}
template <typename T>
void register_all_propositions_of(std::shared_ptr<T> from_other,
const void* for_me)
{
register_all_propositions_of(from_other.get(), for_me);
}
template <typename T, typename U>
void register_all_propositions_of(std::shared_ptr<T> from_other,
std::shared_ptr<U> for_me)
{
register_all_propositions_of(from_other.get(), for_me.get());
}
/// @}
/// \brief Release all variables used by an object.
///
/// Usually called in the destructor if \a me.
void unregister_all_my_variables(const void* me);
/// \brief Release all variables of a given type, used by an
/// object.
/// @{
void unregister_all_typed_variables(var_type type, const void* me);
template <typename T>
void unregister_all_typed_variables(var_type type, std::shared_ptr<T> me)
{
unregister_all_typed_variables(type, me.get());
}
/// @}
/// \brief Release a variable used by \a me.
/// @{
void unregister_variable(int var, const void* me);
template <typename T>
void unregister_variable(int var, std::shared_ptr<T> me)
{
unregister_variable(var, me.get());
}
/// @}
/// \brief Dump all variables for debugging.
/// \param os The output stream.
std::ostream& dump(std::ostream& os) const;
/// \brief Make sure the dictionary is empty.
///
/// This will print diagnostics if the dictionary is not empty.
/// Use for debugging. This is called automatically by the
/// destructor. When Spot is compiled in development mode (i.e.,
/// with <code>./configure --enable-devel</code>), this function
/// will abort if the dictionary is not empty.
///
/// The errors detected by this function usually indicate missing
/// calls to unregister_variable() or
/// unregister_all_my_variables().
void assert_emptiness() const;
private:
// Disallow copy.
bdd_dict(const bdd_dict& other) SPOT_DELETED;
bdd_dict& operator=(const bdd_dict& other) SPOT_DELETED;
};
typedef std::shared_ptr<bdd_dict> bdd_dict_ptr;
inline bdd_dict_ptr make_bdd_dict()
{
return std::make_shared<bdd_dict>();
}
}

261
src/twa/bddprint.cc Normal file
View file

@ -0,0 +1,261 @@
// -*- coding: utf-8 -*-
// Copyright (C) 2009, 2012, 2014 Laboratoire de Recherche et
// Développement de l'Epita (LRDE).
// Copyright (C) 2003, 2004 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 <http://www.gnu.org/licenses/>.
#include <sstream>
#include <cassert>
#include <ostream>
#include "bddprint.hh"
#include "ltlvisit/tostring.hh"
#include "formula2bdd.hh"
#include "misc/minato.hh"
namespace spot
{
/// Global dictionary used by print_handler() to lookup variables.
static bdd_dict_ptr dict;
/// Global flag to enable Acc[x] output (instead of `x').
static bool want_acc;
/// Global flag to enable UTF-8 output.
static bool utf8;
static
std::ostream& print_ltl(const ltl::formula* f, std::ostream& o)
{
if (utf8)
ltl::to_utf8_string(f, o);
else
ltl::to_string(f, o);
return o;
}
/// Stream handler used by Buddy to display BDD variables.
static void
print_handler(std::ostream& o, int v)
{
assert(unsigned(v) < dict->bdd_map.size());
const bdd_dict::bdd_info& ref = dict->bdd_map[v];
switch (ref.type)
{
case bdd_dict::var:
to_string(ref.f, o);
break;
case bdd_dict::acc:
if (want_acc)
{
o << "Acc[";
print_ltl(ref.f, o) << ']';
}
else
{
o << '"';
print_ltl(ref.f, o) << '"';
}
break;
case bdd_dict::anon:
o << '?' << v;
}
}
static std::ostream* where;
static void
print_sat_handler(char* varset, int size)
{
bool not_first = false;
for (int v = 0; v < size; ++v)
{
if (varset[v] < 0)
continue;
if (not_first)
*where << ' ';
else
not_first = true;
if (varset[v] == 0)
*where << "! ";
print_handler(*where, v);
}
}
std::ostream&
bdd_print_sat(std::ostream& os, const bdd_dict_ptr& d, bdd b)
{
dict = d;
where = &os;
want_acc = false;
assert(bdd_satone(b) == b);
bdd_allsat(b, print_sat_handler);
return os;
}
static void
print_acc_handler(char* varset, int size)
{
for (int v = 0; v < size; ++v)
if (varset[v] > 0)
{
*where << ' ';
print_handler(*where, v);
}
}
std::ostream&
bdd_print_acc(std::ostream& os, const bdd_dict_ptr& d, bdd b)
{
dict = d;
where = &os;
want_acc = false;
bdd_allsat(b, print_acc_handler);
return os;
}
static bool first_done = false;
static void
print_accset_handler(char* varset, int size)
{
for (int v = 0; v < size; ++v)
if (varset[v] > 0)
{
*where << (first_done ? ", " : "{");
print_handler(*where, v);
first_done = true;
}
}
std::ostream&
bdd_print_accset(std::ostream& os, const bdd_dict_ptr& d, bdd b)
{
dict = d;
where = &os;
want_acc = true;
first_done = false;
bdd_allsat(b, print_accset_handler);
if (first_done)
*where << '}';
return os;
}
std::string
bdd_format_accset(const bdd_dict_ptr& d, bdd b)
{
std::ostringstream os;
bdd_print_accset(os, d, b);
return os.str();
}
std::string
bdd_format_sat(const bdd_dict_ptr& d, bdd b)
{
std::ostringstream os;
bdd_print_sat(os, d, b);
return os.str();
}
std::ostream&
bdd_print_set(std::ostream& os, const bdd_dict_ptr& d, bdd b)
{
dict = d;
want_acc = true;
bdd_strm_hook(print_handler);
os << bddset << b;
bdd_strm_hook(0);
return os;
}
std::string
bdd_format_set(const bdd_dict_ptr& d, bdd b)
{
std::ostringstream os;
bdd_print_set(os, d, b);
return os.str();
}
std::ostream&
bdd_print_formula(std::ostream& os, const bdd_dict_ptr& d, bdd b)
{
const ltl::formula* f = bdd_to_formula(b, d);
print_ltl(f, os);
f->destroy();
return os;
}
std::string
bdd_format_formula(const bdd_dict_ptr& d, bdd b)
{
std::ostringstream os;
bdd_print_formula(os, d, b);
return os.str();
}
std::ostream&
bdd_print_dot(std::ostream& os, const bdd_dict_ptr& d, bdd b)
{
dict = d;
want_acc = true;
bdd_strm_hook(print_handler);
os << bdddot << b;
bdd_strm_hook(0);
return os;
}
std::ostream&
bdd_print_table(std::ostream& os, const bdd_dict_ptr& d, bdd b)
{
dict = d;
want_acc = true;
bdd_strm_hook(print_handler);
os << bddtable << b;
bdd_strm_hook(0);
return os;
}
void
enable_utf8()
{
utf8 = true;
}
std::ostream&
bdd_print_isop(std::ostream& os, const bdd_dict_ptr& d, bdd b)
{
dict = d;
want_acc = true;
minato_isop isop(b);
bdd p;
while ((p = isop.next()) != bddfalse)
{
os << bdd_format_set(d, p);
}
return os;
}
std::string
bdd_format_isop(const bdd_dict_ptr& d, bdd b)
{
std::ostringstream os;
bdd_print_isop(os, d, b);
return os.str();
}
}

140
src/twa/bddprint.hh Normal file
View file

@ -0,0 +1,140 @@
// -*- coding: utf-8 -*-
// Copyright (C) 2012, 2013, 2014 Laboratoire de Recherche et Développement
// de l'Epita (LRDE).
// Copyright (C) 2003, 2004 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 <http://www.gnu.org/licenses/>.
#pragma once
#include <string>
#include <iosfwd>
#include "bdddict.hh"
#include <bddx.h>
namespace spot
{
/// \brief Print a BDD as a list of literals.
///
/// This assumes that \a b is a conjunction of literals.
/// \param os The output stream.
/// \param dict The dictionary to use, to lookup variables.
/// \param b The BDD to print.
SPOT_API std::ostream&
bdd_print_sat(std::ostream& os, const bdd_dict_ptr& dict, bdd b);
/// \brief Format a BDD as a list of literals.
///
/// This assumes that \a b is a conjunction of literals.
/// \param dict The dictionary to use, to lookup variables.
/// \param b The BDD to print.
/// \return The BDD formated as a string.
SPOT_API std::string
bdd_format_sat(const bdd_dict_ptr& dict, bdd b);
/// \brief Print a BDD as a list of acceptance conditions.
///
/// This is used when saving a TGBA.
/// \param os The output stream.
/// \param dict The dictionary to use, to lookup variables.
/// \param b The BDD to print.
/// \return The BDD formated as a string.
SPOT_API std::ostream&
bdd_print_acc(std::ostream& os, const bdd_dict_ptr& dict, bdd b);
/// \brief Print a BDD as a set of acceptance conditions.
///
/// This is used when saving a TGBA.
/// \param os The output stream.
/// \param dict The dictionary to use, to lookup variables.
/// \param b The BDD to print.
/// \return The BDD formated as a string.
SPOT_API std::ostream&
bdd_print_accset(std::ostream& os, const bdd_dict_ptr& dict, bdd b);
/// \brief Format a BDD as a set of acceptance conditions.
///
/// This is used when saving a TGBA.
/// \param dict The dictionary to use, to lookup variables.
/// \param b The BDD to print.
/// \return The BDD formated as a string.
SPOT_API std::string
bdd_format_accset(const bdd_dict_ptr& dict, bdd b);
/// \brief Print a BDD as a set.
/// \param os The output stream.
/// \param dict The dictionary to use, to lookup variables.
/// \param b The BDD to print.
SPOT_API std::ostream&
bdd_print_set(std::ostream& os, const bdd_dict_ptr& dict, bdd b);
/// \brief Format a BDD as a set.
/// \param dict The dictionary to use, to lookup variables.
/// \param b The BDD to print.
/// \return The BDD formated as a string.
SPOT_API std::string
bdd_format_set(const bdd_dict_ptr& dict, bdd b);
/// \brief Print a BDD as a formula.
/// \param os The output stream.
/// \param dict The dictionary to use, to lookup variables.
/// \param b The BDD to print.
SPOT_API std::ostream&
bdd_print_formula(std::ostream& os, const bdd_dict_ptr& dict, bdd b);
/// \brief Format a BDD as a formula.
/// \param dict The dictionary to use, to lookup variables.
/// \param b The BDD to print.
/// \return The BDD formated as a string.
SPOT_API std::string
bdd_format_formula(const bdd_dict_ptr& dict, bdd b);
/// \brief Print a BDD as a diagram in dotty format.
/// \param os The output stream.
/// \param dict The dictionary to use, to lookup variables.
/// \param b The BDD to print.
SPOT_API std::ostream&
bdd_print_dot(std::ostream& os, const bdd_dict_ptr& dict, bdd b);
/// \brief Print a BDD as a table.
/// \param os The output stream.
/// \param dict The dictionary to use, to lookup variables.
/// \param b The BDD to print.
SPOT_API std::ostream&
bdd_print_table(std::ostream& os, const bdd_dict_ptr& dict, bdd b);
/// \brief Enable UTF-8 output for bdd printers.
SPOT_API void enable_utf8();
/// \brief Format a BDD as an irredundant sum of product.
/// \param dict The dictionary to use, to lookup variables.
/// \param b The BDD to print.
/// \return The BDD formated as a string.
SPOT_API std::string
bdd_format_isop(const bdd_dict_ptr& dict, bdd b);
/// \brief Print a BDD as an irredundant sum of product.
/// \param os The output stream.
/// \param dict The dictionary to use, to lookup variables.
/// \param b The BDD to print.
SPOT_API std::ostream&
bdd_print_isop(std::ostream& os, const bdd_dict_ptr& dict, bdd b);
}

231
src/twa/formula2bdd.cc Normal file
View file

@ -0,0 +1,231 @@
// -*- coding: utf-8 -*-
// Copyright (C) 2009, 2010, 2012, 2014 Laboratoire de Recherche et
// Développement de l'Epita (LRDE).
// Copyright (C) 2003, 2004 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 <http://www.gnu.org/licenses/>.
#include <cassert>
#include "formula2bdd.hh"
#include "ltlast/allnodes.hh"
#include "ltlast/visitor.hh"
#include "misc/minato.hh"
namespace spot
{
using namespace ltl;
namespace
{
class formula_to_bdd_visitor: public ltl::visitor
{
public:
formula_to_bdd_visitor(const bdd_dict_ptr& d, void* owner)
: d_(d), owner_(owner)
{
}
virtual
~formula_to_bdd_visitor()
{
}
virtual void
visit(const atomic_prop* node)
{
res_ = bdd_ithvar(d_->register_proposition(node, owner_));
}
virtual void
visit(const constant* node)
{
switch (node->val())
{
case constant::True:
res_ = bddtrue;
return;
case constant::False:
res_ = bddfalse;
return;
case constant::EmptyWord:
SPOT_UNIMPLEMENTED();
}
SPOT_UNREACHABLE();
}
virtual void
visit(const bunop*)
{
SPOT_UNIMPLEMENTED();
}
virtual void
visit(const unop* node)
{
switch (node->op())
{
case unop::F:
case unop::G:
case unop::X:
case unop::Closure:
case unop::NegClosure:
case unop::NegClosureMarked:
SPOT_UNIMPLEMENTED();
case unop::Not:
{
res_ = bdd_not(recurse(node->child()));
return;
}
}
SPOT_UNREACHABLE();
}
virtual void
visit(const binop* node)
{
bdd f1 = recurse(node->first());
bdd f2 = recurse(node->second());
switch (node->op())
{
case binop::Xor:
res_ = bdd_apply(f1, f2, bddop_xor);
return;
case binop::Implies:
res_ = bdd_apply(f1, f2, bddop_imp);
return;
case binop::Equiv:
res_ = bdd_apply(f1, f2, bddop_biimp);
return;
case binop::U:
case binop::R:
case binop::W:
case binop::M:
case binop::UConcat:
case binop::EConcat:
case binop::EConcatMarked:
SPOT_UNIMPLEMENTED();
}
SPOT_UNREACHABLE();
}
virtual void
visit(const multop* node)
{
int op = -1;
switch (node->op())
{
case multop::And:
op = bddop_and;
res_ = bddtrue;
break;
case multop::Or:
op = bddop_or;
res_ = bddfalse;
break;
case multop::Concat:
case multop::Fusion:
case multop::AndNLM:
case multop::OrRat:
case multop::AndRat:
SPOT_UNIMPLEMENTED();
}
assert(op != -1);
unsigned s = node->size();
for (unsigned n = 0; n < s; ++n)
{
res_ = bdd_apply(res_, recurse(node->nth(n)), op);
}
}
bdd
result() const
{
return res_;
}
bdd
recurse(const formula* f) const
{
return formula_to_bdd(f, d_, owner_);
}
private:
bdd_dict_ptr d_;
void* owner_;
bdd res_;
};
// Convert a BDD which is known to be a conjonction into a formula.
static const ltl::formula*
conj_to_formula(bdd b, const bdd_dict_ptr d)
{
if (b == bddfalse)
return constant::false_instance();
multop::vec* v = new multop::vec;
while (b != bddtrue)
{
int var = bdd_var(b);
const bdd_dict::bdd_info& i = d->bdd_map[var];
assert(i.type == bdd_dict::var);
const formula* res = i.f->clone();
bdd high = bdd_high(b);
if (high == bddfalse)
{
res = unop::instance(unop::Not, res);
b = bdd_low(b);
}
else
{
// If bdd_low is not false, then b was not a conjunction.
assert(bdd_low(b) == bddfalse);
b = high;
}
assert(b != bddfalse);
v->push_back(res);
}
return multop::instance(multop::And, v);
}
} // anonymous
bdd
formula_to_bdd(const formula* f, const bdd_dict_ptr& d, void* for_me)
{
formula_to_bdd_visitor v(d, for_me);
f->accept(v);
return v.result();
}
const formula*
bdd_to_formula(bdd f, const bdd_dict_ptr d)
{
if (f == bddfalse)
return constant::false_instance();
multop::vec* v = new multop::vec;
minato_isop isop(f);
bdd cube;
while ((cube = isop.next()) != bddfalse)
v->push_back(conj_to_formula(cube, d));
return multop::instance(multop::Or, v);
}
}

63
src/twa/formula2bdd.hh Normal file
View file

@ -0,0 +1,63 @@
// -*- coding: utf-8 -*-
// Copyright (C) 2012, 2013, 2014 Laboratoire de Recherche et Développement de
// l'Epita (LRDE).
// Copyright (C) 2003 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 <http://www.gnu.org/licenses/>.
#pragma once
#include "bdddict.hh"
namespace spot
{
/// \brief Convert a Boolean formula into a BDD.
///
/// Convert the Boolean formula \a f into a BDD, using existing
/// variables from \a d, and registering new ones as necessary. \a
/// for_me, the address of the user of these BDD variables will be
/// passed to \a d when registering the variables.
///
/// If you only use the BDD representation temporarily, for instance
/// passing it right away to bdd_to_formula(), you should not forget
/// to unregister the variables that have been registered for \a
/// for_me. See bdd_dict::unregister_all_my_variables().
/// @{
SPOT_API bdd
formula_to_bdd(const ltl::formula* f, const bdd_dict_ptr& d, void* for_me);
template<typename T>
SPOT_API bdd
formula_to_bdd(const ltl::formula* f, const bdd_dict_ptr& d,
const std::shared_ptr<T>& for_me)
{
return formula_to_bdd(f, d, for_me.get());
}
/// @}
/// \brief Convert a BDD into a formula.
///
/// Format the BDD as an irredundant sum of product (see the
/// minato_isop class for details) and map the BDD variables back
/// into their atomic propositions. This works only for Boolean
/// formulas, and all the BDD variables used in \a f should have
/// been registered in \a d. Although the result has type
/// ltl::formula*, it obviously does not use any temporal operator.
SPOT_API const
ltl::formula* bdd_to_formula(bdd f, const bdd_dict_ptr d);
}

37
src/twa/fwd.hh Normal file
View file

@ -0,0 +1,37 @@
// -*- coding: utf-8 -*-
// Copyright (C) 2014, 2015 Laboratoire de Recherche et Développement
// de l'Epita (LRDE).
//
// 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 <http://www.gnu.org/licenses/>.
#pragma once
#include <memory>
namespace spot
{
class twa;
typedef std::shared_ptr<twa> twa_ptr;
typedef std::shared_ptr<const twa> const_twa_ptr;
class twa_graph;
typedef std::shared_ptr<const twa_graph> const_twa_graph_ptr;
typedef std::shared_ptr<twa_graph> twa_graph_ptr;
class twa_product;
typedef std::shared_ptr<const twa_product> const_twa_product_ptr;
typedef std::shared_ptr<twa_product> twa_product_ptr;
}

371
src/twa/taatgba.cc Normal file
View file

@ -0,0 +1,371 @@
// -*- coding: utf-8 -*-
// Copyright (C) 2009, 2010, 2011, 2012, 2013, 2014, 2015 Laboratoire
// de Recherche et Développement de l'Epita (LRDE)
//
// 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 <http://www.gnu.org/licenses/>.
#include <map>
#include <algorithm>
#include <iterator>
#include <iostream>
#include "twa/formula2bdd.hh"
#include "ltlvisit/tostring.hh"
#include "ltlvisit/clone.hh"
#include "taatgba.hh"
namespace spot
{
/*--------.
| taa_tgba |
`--------*/
taa_tgba::taa_tgba(const bdd_dict_ptr& dict)
: twa(dict),
init_(0), state_set_vec_()
{
}
taa_tgba::~taa_tgba()
{
ss_vec::iterator j;
for (j = state_set_vec_.begin(); j != state_set_vec_.end(); ++j)
delete *j;
get_dict()->unregister_all_my_variables(this);
}
void
taa_tgba::add_condition(transition* t, const ltl::formula* f)
{
t->condition &= formula_to_bdd(f, get_dict(), this);
f->destroy();
}
state*
taa_tgba::get_init_state() const
{
assert(init_);
return new spot::set_state(init_);
}
twa_succ_iterator*
taa_tgba::succ_iter(const spot::state* state) const
{
const spot::set_state* s = down_cast<const spot::set_state*>(state);
assert(s);
return new taa_succ_iterator(s->get_state(), acc_);
}
bdd
taa_tgba::compute_support_conditions(const spot::state* s) const
{
const spot::set_state* se = down_cast<const spot::set_state*>(s);
assert(se);
const state_set* ss = se->get_state();
bdd res = bddtrue;
taa_tgba::state_set::const_iterator i;
taa_tgba::state::const_iterator j;
for (i = ss->begin(); i != ss->end(); ++i)
for (j = (*i)->begin(); j != (*i)->end(); ++j)
res |= (*j)->condition;
return res;
}
/*----------.
| state_set |
`----------*/
const taa_tgba::state_set*
set_state::get_state() const
{
return s_;
}
int
set_state::compare(const spot::state* other) const
{
const set_state* o = down_cast<const set_state*>(other);
assert(o);
const taa_tgba::state_set* s1 = get_state();
const taa_tgba::state_set* s2 = o->get_state();
if (s1->size() != s2->size())
return s1->size() - s2->size();
taa_tgba::state_set::const_iterator it1 = s1->begin();
taa_tgba::state_set::const_iterator it2 = s2->begin();
while (it2 != s2->end())
{
int i = *it1++ - *it2++;
if (i != 0)
return i;
}
return 0;
}
size_t
set_state::hash() const
{
size_t res = 0;
taa_tgba::state_set::const_iterator it = s_->begin();
while (it != s_->end())
{
res ^= reinterpret_cast<const char*>(*it++) - static_cast<char*>(0);
res = wang32_hash(res);
}
return res;
}
set_state*
set_state::clone() const
{
if (delete_me_ && s_)
return new spot::set_state(new taa_tgba::state_set(*s_), true);
else
return new spot::set_state(s_, false);
}
/*--------------.
| taa_succ_iter |
`--------------*/
taa_succ_iterator::taa_succ_iterator(const taa_tgba::state_set* s,
const acc_cond& acc)
: seen_(), acc_(acc)
{
if (s->empty())
{
taa_tgba::transition* t = new taa_tgba::transition;
t->condition = bddtrue;
t->acceptance_conditions = 0U;
t->dst = new taa_tgba::state_set;
succ_.push_back(t);
return;
}
bounds_t bounds;
for (auto& i: *s)
bounds.emplace_back(i->begin(), i->end());
/// Sorting might make the cartesian product faster by not
/// exploring all possibilities.
std::sort(bounds.begin(), bounds.end(), distance_sort());
std::vector<iterator> pos;
pos.reserve(bounds.size());
for (auto i: bounds)
pos.push_back(i.first);
while (pos[0] != bounds[0].second)
{
taa_tgba::transition* t = new taa_tgba::transition;
t->condition = bddtrue;
t->acceptance_conditions = 0U;
taa_tgba::state_set* ss = new taa_tgba::state_set;
unsigned p;
for (p = 0; p < pos.size() && t->condition != bddfalse; ++p)
{
taa_tgba::state_set::const_iterator j;
for (j = (*pos[p])->dst->begin(); j != (*pos[p])->dst->end(); ++j)
if ((*j)->size() > 0) // Remove sink states.
ss->insert(*j);
// Fill the new transition.
t->condition &= (*pos[p])->condition;
t->acceptance_conditions |= (*pos[p])->acceptance_conditions;
} // If p != pos.size() we have found a contradiction
assert(p > 0);
t->dst = ss;
// Boxing to be able to insert ss in the map directly.
spot::set_state* b = new spot::set_state(ss);
// If no contradiction, then look for another transition to
// merge with the new one.
seen_map::iterator i = seen_.end(); // Initialize to silent a g++ warning.
std::vector<taa_tgba::transition*>::iterator j;
if (t->condition != bddfalse)
{
i = seen_.find(b);
if (i != seen_.end())
for (j = i->second.begin(); j != i->second.end(); ++j)
{
taa_tgba::transition* current = *j;
if (*current->dst == *t->dst
&& current->condition == t->condition)
{
current->acceptance_conditions &= t->acceptance_conditions;
break;
}
if (*current->dst == *t->dst
&& current->acceptance_conditions == t->acceptance_conditions)
{
current->condition |= t->condition;
break;
}
}
}
// Mark the new transition as seen and keep it if we have not
// found any contradiction and no other transition to merge
// with, or delete it otherwise.
if (t->condition != bddfalse
&& (i == seen_.end() || j == i->second.end()))
{
seen_[b].push_back(t);
if (i != seen_.end())
delete b;
succ_.push_back(t);
}
else
{
delete t->dst;
delete t;
delete b;
}
for (int i = pos.size() - 1; i >= 0; --i)
{
if ((i < int(p))
&& (std::distance(pos[i], bounds[i].second) > 1
|| (i == 0 && std::distance(pos[i], bounds[i].second) == 1)))
{
++pos[i];
break;
}
else
pos[i] = bounds[i].first;
}
}
}
taa_succ_iterator::~taa_succ_iterator()
{
for (seen_map::iterator i = seen_.begin(); i != seen_.end();)
{
// Advance the iterator before deleting the state set.
const spot::set_state* s = i->first;
++i;
delete s;
}
for (unsigned i = 0; i < succ_.size(); ++i)
{
delete succ_[i]->dst;
delete succ_[i];
}
}
bool
taa_succ_iterator::first()
{
i_ = succ_.begin();
return i_ != succ_.end();
}
bool
taa_succ_iterator::next()
{
++i_;
return i_ != succ_.end();
}
bool
taa_succ_iterator::done() const
{
return i_ == succ_.end();
}
spot::set_state*
taa_succ_iterator::current_state() const
{
assert(!done());
return new spot::set_state(new taa_tgba::state_set(*(*i_)->dst), true);
}
bdd
taa_succ_iterator::current_condition() const
{
assert(!done());
return (*i_)->condition;
}
acc_cond::mark_t
taa_succ_iterator::current_acceptance_conditions() const
{
assert(!done());
return acc_.comp((*i_)->acceptance_conditions);
}
/*----------------.
| taa_tgba_string |
`----------------*/
taa_tgba_string::~taa_tgba_string()
{
ns_map::iterator i;
for (i = name_state_map_.begin(); i != name_state_map_.end(); ++i)
{
taa_tgba::state::iterator i2;
for (i2 = i->second->begin(); i2 != i->second->end(); ++i2)
delete *i2;
delete i->second;
}
}
std::string
taa_tgba_string::label_to_string(const label_t& label) const
{
return label;
}
std::string
taa_tgba_string::clone_if(const label_t& label) const
{
return label;
}
/*-----------------.
| taa_tgba_formula |
`-----------------*/
taa_tgba_formula::~taa_tgba_formula()
{
ns_map::iterator i;
for (i = name_state_map_.begin(); i != name_state_map_.end();)
{
taa_tgba::state::iterator i2;
for (i2 = i->second->begin(); i2 != i->second->end(); ++i2)
delete *i2;
// Advance the iterator before destroying the formula.
const ltl::formula* s = i->first;
delete i->second;
++i;
s->destroy();
}
}
std::string
taa_tgba_formula::label_to_string(const label_t& label) const
{
return ltl::to_string(label);
}
const ltl::formula*
taa_tgba_formula::clone_if(const label_t& label) const
{
return label->clone();
}
}

356
src/twa/taatgba.hh Normal file
View file

@ -0,0 +1,356 @@
// -*- coding: utf-8 -*-
// Copyright (C) 2009, 2011, 2012, 2013, 2014, 2015 Laboratoire de
// Recherche et Développement de l'Epita (LRDE).
//
// 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 <http://www.gnu.org/licenses/>.
#pragma once
#include <set>
#include <iosfwd>
#include <vector>
#include <string>
#include "misc/hash.hh"
#include "ltlast/formula.hh"
#include "bdddict.hh"
#include "twa.hh"
#include "ltlvisit/tostring.hh"
namespace spot
{
/// \brief A self-loop Transition-based Alternating Automaton (TAA)
/// which is seen as a TGBA (abstract class, see below).
class SPOT_API taa_tgba: public twa
{
public:
taa_tgba(const bdd_dict_ptr& dict);
struct transition;
typedef std::list<transition*> state;
typedef std::set<state*> state_set;
/// Explicit transitions.
struct transition
{
bdd condition;
acc_cond::mark_t acceptance_conditions;
const state_set* dst;
};
void add_condition(transition* t, const ltl::formula* f);
/// TGBA interface.
virtual ~taa_tgba();
virtual spot::state* get_init_state() const final;
virtual twa_succ_iterator* succ_iter(const spot::state* state) const final;
virtual std::string format_state(const spot::state* state) const = 0;
protected:
virtual bdd compute_support_conditions(const spot::state* state)
const final;
typedef std::vector<taa_tgba::state_set*> ss_vec;
taa_tgba::state_set* init_;
ss_vec state_set_vec_;
std::map<const ltl::formula*, acc_cond::mark_t,
ltl::formula_ptr_less_than> acc_map_;
private:
// Disallow copy.
taa_tgba(const taa_tgba& other) SPOT_DELETED;
taa_tgba& operator=(const taa_tgba& other) SPOT_DELETED;
};
/// Set of states deriving from spot::state.
class SPOT_API set_state final: public spot::state
{
public:
set_state(const taa_tgba::state_set* s, bool delete_me = false)
: s_(s), delete_me_(delete_me)
{
}
virtual int compare(const spot::state*) const;
virtual size_t hash() const;
virtual set_state* clone() const;
virtual ~set_state()
{
if (delete_me_)
delete s_;
}
const taa_tgba::state_set* get_state() const;
private:
const taa_tgba::state_set* s_;
bool delete_me_;
};
class SPOT_API taa_succ_iterator final: public twa_succ_iterator
{
public:
taa_succ_iterator(const taa_tgba::state_set* s, const acc_cond& acc);
virtual ~taa_succ_iterator();
virtual bool first();
virtual bool next();
virtual bool done() const;
virtual set_state* current_state() const;
virtual bdd current_condition() const;
virtual acc_cond::mark_t current_acceptance_conditions() const;
private:
/// Those typedefs are used to generate all possible successors in
/// the constructor using a cartesian product.
typedef taa_tgba::state::const_iterator iterator;
typedef std::pair<iterator, iterator> iterator_pair;
typedef std::vector<iterator_pair> bounds_t;
typedef std::unordered_map<const spot::set_state*,
std::vector<taa_tgba::transition*>,
state_ptr_hash, state_ptr_equal> seen_map;
struct distance_sort :
public std::binary_function<const iterator_pair&,
const iterator_pair&, bool>
{
bool
operator()(const iterator_pair& lhs, const iterator_pair& rhs) const
{
return std::distance(lhs.first, lhs.second) <
std::distance(rhs.first, rhs.second);
}
};
std::vector<taa_tgba::transition*>::const_iterator i_;
std::vector<taa_tgba::transition*> succ_;
seen_map seen_;
const acc_cond& acc_;
};
/// A taa_tgba instance with states labeled by a given type.
/// Still an abstract class, see below.
template<typename label>
class SPOT_API taa_tgba_labelled: public taa_tgba
{
public:
taa_tgba_labelled(const bdd_dict_ptr& dict) : taa_tgba(dict) {};
~taa_tgba_labelled()
{
auto i = acc_map_.begin();
while (i != acc_map_.end())
(i++)->first->destroy();
}
void set_init_state(const label& s)
{
std::vector<label> v(1);
v[0] = s;
set_init_state(v);
}
void set_init_state(const std::vector<label>& s)
{
init_ = add_state_set(s);
}
transition*
create_transition(const label& s,
const std::vector<label>& d)
{
state* src = add_state(s);
state_set* dst = add_state_set(d);
transition* t = new transition;
t->dst = dst;
t->condition = bddtrue;
t->acceptance_conditions = 0U;
src->push_back(t);
return t;
}
transition*
create_transition(const label& s, const label& d)
{
std::vector<std::string> vec;
vec.push_back(d);
return create_transition(s, vec);
}
void add_acceptance_condition(transition* t, const ltl::formula* f)
{
auto p = acc_map_.emplace(f, 0);
if (p.second)
p.first->second = acc_.marks({acc_.add_set()});
else
f->destroy();
t->acceptance_conditions |= p.first->second;
}
/// \brief Format the state as a string for printing.
///
/// If state is a spot::set_state of only one element, then the
/// string corresponding to state->get_state() is returned.
///
/// Otherwise a string composed of each string corresponding to
/// each state->get_state() in the spot::set_state is returned,
/// e.g. like {string_1,...,string_n}.
virtual std::string format_state(const spot::state* s) const
{
const spot::set_state* se = down_cast<const spot::set_state*>(s);
assert(se);
const state_set* ss = se->get_state();
return format_state_set(ss);
}
/// \brief Output a TAA in a stream.
void output(std::ostream& os) const
{
typename ns_map::const_iterator i;
for (i = name_state_map_.begin(); i != name_state_map_.end(); ++i)
{
taa_tgba::state::const_iterator i2;
os << "State: " << label_to_string(i->first) << std::endl;
for (i2 = i->second->begin(); i2 != i->second->end(); ++i2)
{
os << ' ' << format_state_set((*i2)->dst)
<< ", C:" << (*i2)->condition
<< ", A:" << (*i2)->acceptance_conditions << std::endl;
}
}
}
protected:
typedef label label_t;
typedef std::unordered_map<label, taa_tgba::state*> ns_map;
typedef std::unordered_map<const taa_tgba::state*, label,
ptr_hash<taa_tgba::state> > sn_map;
ns_map name_state_map_;
sn_map state_name_map_;
/// \brief Return a label as a string.
virtual std::string label_to_string(const label_t& lbl) const = 0;
/// \brief Clone the label if necessary to assure it is owned by
/// this, avoiding memory issues when label is a pointer.
virtual label_t clone_if(const label_t& lbl) const = 0;
private:
/// \brief Return the taa_tgba::state for \a name, creating it
/// when it does not exist already.
taa_tgba::state* add_state(const label& name)
{
typename ns_map::iterator i = name_state_map_.find(name);
if (i == name_state_map_.end())
{
const label& name_ = clone_if(name);
taa_tgba::state* s = new taa_tgba::state;
name_state_map_[name_] = s;
state_name_map_[s] = name_;
return s;
}
return i->second;
}
/// \brief Return the taa::state_set for \a names.
taa_tgba::state_set* add_state_set(const std::vector<label>& names)
{
state_set* ss = new state_set;
for (unsigned i = 0; i < names.size(); ++i)
ss->insert(add_state(names[i]));
state_set_vec_.push_back(ss);
return ss;
}
std::string format_state_set(const taa_tgba::state_set* ss) const
{
state_set::const_iterator i1 = ss->begin();
typename sn_map::const_iterator i2;
if (ss->empty())
return std::string("{}");
if (ss->size() == 1)
{
i2 = state_name_map_.find(*i1);
assert(i2 != state_name_map_.end());
return "{" + label_to_string(i2->second) + "}";
}
else
{
std::string res("{");
while (i1 != ss->end())
{
i2 = state_name_map_.find(*i1++);
assert(i2 != state_name_map_.end());
res += label_to_string(i2->second);
res += ",";
}
res[res.size() - 1] = '}';
return res;
}
}
};
class SPOT_API taa_tgba_string final:
#ifndef SWIG
public taa_tgba_labelled<std::string>
#else
public taa_tgba
#endif
{
public:
taa_tgba_string(const bdd_dict_ptr& dict) :
taa_tgba_labelled<std::string>(dict) {};
~taa_tgba_string();
protected:
virtual std::string label_to_string(const std::string& label) const;
virtual std::string clone_if(const std::string& label) const;
};
typedef std::shared_ptr<taa_tgba_string> taa_tgba_string_ptr;
typedef std::shared_ptr<const taa_tgba_string> const_taa_tgba_string_ptr;
inline taa_tgba_string_ptr make_taa_tgba_string(const bdd_dict_ptr& dict)
{
return std::make_shared<taa_tgba_string>(dict);
}
class SPOT_API taa_tgba_formula final:
#ifndef SWIG
public taa_tgba_labelled<const ltl::formula*>
#else
public taa_tgba
#endif
{
public:
taa_tgba_formula(const bdd_dict_ptr& dict) :
taa_tgba_labelled<const ltl::formula*>(dict) {};
~taa_tgba_formula();
protected:
virtual std::string label_to_string(const label_t& label) const;
virtual const ltl::formula* clone_if(const label_t& label) const;
};
typedef std::shared_ptr<taa_tgba_formula> taa_tgba_formula_ptr;
typedef std::shared_ptr<const taa_tgba_formula> const_taa_tgba_formula_ptr;
inline taa_tgba_formula_ptr make_taa_tgba_formula(const bdd_dict_ptr& dict)
{
return std::make_shared<taa_tgba_formula>(dict);
}
}

119
src/twa/twa.cc Normal file
View file

@ -0,0 +1,119 @@
// -*- coding: utf-8 -*-
// Copyright (C) 2011, 2014, 2015 Laboratoire de Recherche et Developpement de
// l'EPITA (LRDE).
// Copyright (C) 2003, 2004, 2005 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 <http://www.gnu.org/licenses/>.
#include "twa.hh"
#include "twagraph.hh"
#include "tgbaalgos/gtec/gtec.hh"
#include "tgbaalgos/remfin.hh"
#include <utility>
namespace spot
{
twa::twa(const bdd_dict_ptr& d)
: iter_cache_(nullptr),
dict_(d),
last_support_conditions_input_(0)
{
props = 0U;
}
twa::~twa()
{
if (last_support_conditions_input_)
last_support_conditions_input_->destroy();
delete iter_cache_;
release_named_properties();
}
bdd
twa::support_conditions(const state* state) const
{
if (!last_support_conditions_input_
|| last_support_conditions_input_->compare(state) != 0)
{
last_support_conditions_output_ = compute_support_conditions(state);
if (last_support_conditions_input_)
last_support_conditions_input_->destroy();
last_support_conditions_input_ = state->clone();
}
return last_support_conditions_output_;
}
state*
twa::project_state(const state* s,
const const_twa_ptr& t) const
{
if (t.get() == this)
return s->clone();
return 0;
}
std::string
twa::transition_annotation(const twa_succ_iterator*) const
{
return "";
}
bool
twa::is_empty() const
{
// FIXME: This should be improved based on properties of the
// automaton. For instance we do not need couvreur99 is we know
// the automaton is weak.
auto a = shared_from_this();
if (a->acc().uses_fin_acceptance())
{
auto aa = std::dynamic_pointer_cast<const twa_graph>(a);
if (!aa)
aa = make_twa_graph(a, prop_set::all());
a = remove_fin(aa);
}
return !couvreur99(a)->check();
}
void
twa::set_named_prop(std::string s,
void* val, std::function<void(void*)> destructor)
{
auto p = named_prop_.emplace(std::piecewise_construct,
std::forward_as_tuple(s),
std::forward_as_tuple(val, destructor));
if (!p.second)
{
p.first->second.second(p.first->second.first);
p.first->second = std::make_pair(val, destructor);
}
}
void*
twa::get_named_prop_(std::string s) const
{
auto i = named_prop_.find(s);
if (i == named_prop_.end())
return nullptr;
return i->second.first;
}
}

871
src/twa/twa.hh Normal file
View file

@ -0,0 +1,871 @@
// -*- coding: utf-8 -*-
// Copyright (C) 2009, 2011, 2013, 2014, 2015 Laboratoire de Recherche
// et Développement de l'Epita (LRDE).
// Copyright (C) 2003, 2004, 2005 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 <http://www.gnu.org/licenses/>.
#pragma once
#include "fwd.hh"
#include "acc.hh"
#include "bdddict.hh"
#include <cassert>
#include <memory>
#include <unordered_map>
#include <functional>
#include <array>
#include "misc/casts.hh"
#include "misc/hash.hh"
namespace spot
{
/// \ingroup twa_essentials
/// \brief Abstract class for states.
class SPOT_API state
{
public:
/// \brief Compares two states (that come from the same automaton).
///
/// This method returns an integer less than, equal to, or greater
/// than zero if \a this is found, respectively, to be less than, equal
/// to, or greater than \a other according to some implicit total order.
///
/// This method should not be called to compare states from
/// different automata.
///
/// \sa spot::state_ptr_less_than
virtual int compare(const state* other) const = 0;
/// \brief Hash a state.
///
/// This method returns an integer that can be used as a
/// hash value for this state.
///
/// Note that the hash value is guaranteed to be unique for all
/// equal states (in compare()'s sense) for only has long has one
/// of these states exists. So it's OK to use a spot::state as a
/// key in a \c hash_map because the mere use of the state as a
/// key in the hash will ensure the state continues to exist.
///
/// However if you create the state, get its hash key, delete the
/// state, recreate the same state, and get its hash key, you may
/// obtain two different hash keys if the same state were not
/// already used elsewhere. In practice this weird situation can
/// occur only when the state is BDD-encoded, because BDD numbers
/// (used to build the hash value) can be reused for other
/// formulas. That probably doesn't matter, since the hash value
/// is meant to be used in a \c hash_map, but it had to be noted.
virtual size_t hash() const = 0;
/// Duplicate a state.
virtual state* clone() const = 0;
/// \brief Release a state.
///
/// Methods from the tgba or twa_succ_iterator always return a
/// new state that you should deallocate with this function.
/// Before Spot 0.7, you had to "delete" your state directly.
/// Starting with Spot 0.7, you should update your code to use
/// this function instead. destroy() usually call delete, except
/// in subclasses that destroy() to allow better memory management
/// (e.g., no memory allocation for explicit automata).
virtual void destroy() const
{
delete this;
}
protected:
/// \brief Destructor.
///
/// Note that client code should call
/// <code>s->destroy();</code> instead of <code>delete s;</code>.
virtual ~state()
{
}
};
/// \ingroup twa_essentials
/// \brief Strict Weak Ordering for \c state*.
///
/// This is meant to be used as a comparison functor for
/// STL \c map whose key are of type \c state*.
///
/// For instance here is how one could declare
/// a map of \c state*.
/// \code
/// // Remember how many times each state has been visited.
/// std::map<spot::state*, int, spot::state_ptr_less_than> seen;
/// \endcode
struct state_ptr_less_than
{
bool
operator()(const state* left, const state* right) const
{
assert(left);
return left->compare(right) < 0;
}
};
/// \ingroup twa_essentials
/// \brief An Equivalence Relation for \c state*.
///
/// This is meant to be used as a comparison functor for
/// an \c unordered_map whose key are of type \c state*.
///
/// For instance here is how one could declare
/// a map of \c state*.
/// \code
/// // Remember how many times each state has been visited.
/// std::unordered_map<spot::state*, int, spot::state_ptr_hash,
/// spot::state_ptr_equal> seen;
/// \endcode
struct state_ptr_equal
{
bool
operator()(const state* left, const state* right) const
{
assert(left);
return 0 == left->compare(right);
}
};
/// \ingroup twa_essentials
/// \ingroup hash_funcs
/// \brief Hash Function for \c state*.
///
/// This is meant to be used as a hash functor for
/// an \c unordered_map whose key are of type \c state*.
///
/// For instance here is how one could declare
/// a map of \c state*.
/// \code
/// // Remember how many times each state has been visited.
/// std::unordered_map<spot::state*, int, spot::state_ptr_hash,
/// spot::state_ptr_equal> seen;
/// \endcode
struct state_ptr_hash
{
size_t
operator()(const state* that) const
{
assert(that);
return that->hash();
}
};
typedef std::unordered_set<const state*,
state_ptr_hash, state_ptr_equal> state_set;
/// \ingroup twa_essentials
/// \brief Render state pointers unique via a hash table.
class SPOT_API state_unicity_table
{
state_set m;
public:
/// \brief Canonicalize state pointer.
///
/// If this is the first time a state is seen, this return the
/// state pointer as-is, otherwise it frees the state and returns
/// a point to the previously seen copy.
///
/// States are owned by the table and will be freed on
/// destruction.
const state* operator()(const state* s)
{
auto p = m.insert(s);
if (!p.second)
s->destroy();
return *p.first;
}
/// \brief Canonicalize state pointer.
///
/// Same as operator(), except that a nullptr
/// is returned if the state is not new.
const state* is_new(const state* s)
{
auto p = m.insert(s);
if (!p.second)
{
s->destroy();
return nullptr;
}
return *p.first;
}
~state_unicity_table()
{
for (state_set::iterator i = m.begin(); i != m.end();)
{
// Advance the iterator before destroying its key. This
// avoid issues with old g++ implementations.
state_set::iterator old = i++;
(*old)->destroy();
}
}
size_t
size()
{
return m.size();
}
};
// Functions related to shared_ptr.
//////////////////////////////////////////////////
typedef std::shared_ptr<const state> shared_state;
inline void shared_state_deleter(state* s) { s->destroy(); }
/// \ingroup twa_essentials
/// \brief Strict Weak Ordering for \c shared_state
/// (shared_ptr<const state*>).
///
/// This is meant to be used as a comparison functor for
/// STL \c map whose key are of type \c shared_state.
///
/// For instance here is how one could declare
/// a map of \c shared_state.
/// \code
/// // Remember how many times each state has been visited.
/// std::map<shared_state, int, spot::state_shared_ptr_less_than> seen;
/// \endcode
struct state_shared_ptr_less_than
{
bool
operator()(shared_state left,
shared_state right) const
{
assert(left);
return left->compare(right.get()) < 0;
}
};
/// \ingroup twa_essentials
/// \brief An Equivalence Relation for \c shared_state
/// (shared_ptr<const state*>).
///
/// This is meant to be used as a comparison functor for
/// un \c unordered_map whose key are of type \c shared_state.
///
/// For instance here is how one could declare
/// a map of \c shared_state
/// \code
/// // Remember how many times each state has been visited.
/// std::unordered_map<shared_state, int,
/// state_shared_ptr_hash,
/// state_shared_ptr_equal> seen;
/// \endcode
struct state_shared_ptr_equal
{
bool
operator()(shared_state left,
shared_state right) const
{
assert(left);
return 0 == left->compare(right.get());
}
};
/// \ingroup twa_essentials
/// \ingroup hash_funcs
/// \brief Hash Function for \c shared_state (shared_ptr<const state*>).
///
/// This is meant to be used as a hash functor for
/// an \c unordered_map whose key are of type
/// \c shared_state.
///
/// For instance here is how one could declare
/// a map of \c shared_state.
/// \code
/// // Remember how many times each state has been visited.
/// std::unordered_map<shared_state, int,
/// state_shared_ptr_hash,
/// state_shared_ptr_equal> seen;
/// \endcode
struct state_shared_ptr_hash
{
size_t
operator()(shared_state that) const
{
assert(that);
return that->hash();
}
};
typedef std::unordered_set<shared_state,
state_shared_ptr_hash,
state_shared_ptr_equal> shared_state_set;
/// \ingroup twa_essentials
/// \brief Iterate over the successors of a state.
///
/// This class provides the basic functionalities required to
/// iterate over the successors of a state, as well as querying
/// transition labels. Because transitions are never explicitely
/// encoded, labels (conditions and acceptance conditions) can only
/// be queried while iterating over the successors.
class SPOT_API twa_succ_iterator
{
public:
virtual
~twa_succ_iterator()
{
}
/// \name Iteration
//@{
/// \brief Position the iterator on the first successor (if any).
///
/// This method can be called several times to make multiple
/// passes over successors.
///
/// \warning One should always call \c done() (or better: check
/// the return value of first()) to ensure there is a successor,
/// even after \c first(). A common trap is to assume that there
/// is at least one successor: this is wrong.
///
/// \return whether there is actually a successor
virtual bool first() = 0;
/// \brief Jump to the next successor (if any).
///
/// \warning Again, one should always call \c done() (or better:
/// check the return value of next()) ensure there is a successor.
///
/// \return whether there is actually a successor
virtual bool next() = 0;
/// \brief Check whether the iteration is finished.
///
/// This function should be called after any call to \c first()
/// or \c next() and before any enquiry about the current state.
///
/// The usual way to do this is with a \c for loop.
///
/// for (s->first(); !s->done(); s->next())
/// ...
virtual bool done() const = 0;
//@}
/// \name Inspection
//@{
/// \brief Get the state of the current successor.
///
/// Note that the same state may occur at different points
/// in the iteration. These actually correspond to the same
/// destination. It just means there were several transitions,
/// with different conditions, leading to the same state.
///
/// The returned state should be destroyed (see state::destroy)
/// by the caller after it is no longer used.
virtual state* current_state() const = 0;
/// \brief Get the condition on the transition leading to this successor.
///
/// This is a boolean function of atomic propositions.
virtual bdd current_condition() const = 0;
/// \brief Get the acceptance conditions on the transition leading
/// to this successor.
virtual acc_cond::mark_t current_acceptance_conditions() const = 0;
//@}
};
namespace internal
{
struct SPOT_API succ_iterator
{
protected:
twa_succ_iterator* it_;
public:
succ_iterator(twa_succ_iterator* it):
it_(it)
{
}
bool operator==(succ_iterator o) const
{
return it_ == o.it_;
}
bool operator!=(succ_iterator o) const
{
return it_ != o.it_;
}
const twa_succ_iterator* operator*() const
{
return it_;
}
void operator++()
{
if (!it_->next())
it_ = nullptr;
}
};
}
/// \defgroup twa TωA (Transition-based ω-Automata)
///
/// Spot is centered around the spot::twa type. This type and its
/// cousins are listed \ref tgba_essentials "here". This is an
/// abstract interface. Its implementations are either \ref
/// tgba_representation "concrete representations", or \ref
/// tgba_on_the_fly_algorithms "on-the-fly algorithms". Other
/// algorithms that work on spot::twa are \ref tgba_algorithms
/// "listed separately".
/// \addtogroup twa_essentials Essential TωA types
/// \ingroup twa
/// \ingroup twa_essentials
/// \brief A Transition-based ω-Automaton.
///
/// The acronym TωA stands for Transition-based ω-automaton.
/// We may write it as TwA or twa, but never as TWA as the
/// w is just a non-utf8 replacement for ω that should not be
/// capitalized.
///
/// TωAs are transition-based automata, meanings that not-only
/// do they have labels on arcs, they also have an acceptance
/// condition defined in term of sets of transitions.
/// The acceptance condition can be anything supported by
/// the HOA format (http://adl.github.io/hoaf/). The only
/// restriction w.r.t. the format is that this class does
/// not support alternating automata
///
/// Previous version of Spot supported a type of automata called
/// TGBA, which are TωA in which the acceptance condition is a set
/// of sets of transitions that must be intersected infinitely
/// often.
///
/// In this version, TGBAs are now represented by TωAs for which
/// <code>aut->acc().is_generalized_buchi())</code> returns true.
///
/// Browsing such automaton can be achieved using two functions:
/// \c get_init_state, and \c succ. The former returns
/// the initial state while the latter lists the
/// successor states of any state.
///
/// Note that although this is a transition-based automata, we never
/// represent transitions in the API! Transition data are
/// obtained by querying the iterator over the successors of a
/// state.
class SPOT_API twa: public std::enable_shared_from_this<twa>
{
protected:
twa(const bdd_dict_ptr& d);
// Any iterator returned via release_iter.
mutable twa_succ_iterator* iter_cache_;
bdd_dict_ptr dict_;
public:
#ifndef SWIG
class succ_iterable
{
protected:
const twa* aut_;
twa_succ_iterator* it_;
public:
succ_iterable(const twa* aut, twa_succ_iterator* it)
: aut_(aut), it_(it)
{
}
succ_iterable(succ_iterable&& other)
: aut_(other.aut_), it_(other.it_)
{
other.it_ = nullptr;
}
~succ_iterable()
{
if (it_)
aut_->release_iter(it_);
}
internal::succ_iterator begin()
{
return it_->first() ? it_ : nullptr;
}
internal::succ_iterator end()
{
return nullptr;
}
};
#endif
virtual ~twa();
/// \brief Get the initial state of the automaton.
///
/// The state has been allocated with \c new. It is the
/// responsability of the caller to \c destroy it when no
/// longer needed.
virtual state* get_init_state() const = 0;
/// \brief Get an iterator over the successors of \a local_state.
///
/// The iterator has been allocated with \c new. It is the
/// responsability of the caller to \c delete it when no
/// longer needed.
virtual twa_succ_iterator*
succ_iter(const state* local_state) const = 0;
#ifndef SWIG
/// \brief Build an iterable over the successors of \a s.
///
/// This is meant to be used as
/// <code>for (auto i: aut->succ(s)) { /* i->current_state() */ }</code>.
succ_iterable
succ(const state* s) const
{
return {this, succ_iter(s)};
}
#endif
/// \brief Release an iterator after usage.
///
/// This iterator can then be reused by succ_iter() to avoid
/// memory allocation.
void release_iter(twa_succ_iterator* i) const
{
if (iter_cache_)
delete i;
else
iter_cache_ = i;
}
/// \brief Get a formula that must hold whatever successor is taken.
///
/// \return A formula which must be verified for all successors
/// of \a state.
///
/// This can be as simple as \c bddtrue, or more completely
/// the disjunction of the condition of all successors. This
/// is used as an hint by \c succ_iter() to reduce the number
/// of successor to compute in a product.
///
/// Sub classes should implement compute_support_conditions(),
/// this function is just a wrapper that will cache the
/// last return value for efficiency.
bdd support_conditions(const state* state) const;
/// \brief Get the dictionary associated to the automaton.
///
/// Atomic propositions and acceptance conditions are represented
/// as BDDs. The dictionary allows to map BDD variables back to
/// formulae, and vice versa. This is useful when dealing with
/// several automata (which may use the same BDD variable for
/// different formula), or simply when printing.
bdd_dict_ptr get_dict() const
{
return dict_;
}
/// \brief Format the state as a string for printing.
///
/// This formating is the responsability of the automata
/// that owns the state.
virtual std::string format_state(const state* state) const = 0;
/// \brief Return a possible annotation for the transition
/// pointed to by the iterator.
///
/// You may decide to use annotations when building a tgba class
/// that represents the state space of a model, for instance to
/// indicate how the tgba transitions relate to the original model
/// (e.g. the annotation could be the name of a PetriNet
/// transition, or the line number of some textual formalism).
///
/// Implementing this method is optional; the default annotation
/// is the empty string.
///
/// This method is used for instance in dotty_reachable(),
/// and replay_tgba_run().
///
/// \param t a non-done twa_succ_iterator for this automaton
virtual std::string
transition_annotation(const twa_succ_iterator* t) const;
/// \brief Project a state on an automaton.
///
/// This converts \a s, into that corresponding spot::state for \a
/// t. This is useful when you have the state of a product, and
/// want restrict this state to a specific automata occuring in
/// the product.
///
/// It goes without saying that \a s and \a t should be compatible
/// (i.e., \a s is a state of \a t).
///
/// \return 0 if the projection fails (\a s is unrelated to \a t),
/// or a new \c state* (the projected state) that must be
/// destroyed by the caller.
virtual state* project_state(const state* s,
const const_twa_ptr& t) const;
const acc_cond& acc() const
{
return acc_;
}
acc_cond& acc()
{
return acc_;
}
virtual bool is_empty() const;
protected:
acc_cond acc_;
void set_num_sets_(unsigned num)
{
if (num < acc_.num_sets())
{
acc_.~acc_cond();
new (&acc_) acc_cond;
}
acc_.add_sets(num - acc_.num_sets());
}
public:
const acc_cond::acc_code& get_acceptance() const
{
return acc_.get_acceptance();
}
void set_acceptance(unsigned num, const acc_cond::acc_code& c)
{
set_num_sets_(num);
acc_.set_acceptance(c);
if (num == 0)
prop_state_based_acc();
}
/// \brief Copy the acceptance condition of another tgba.
void copy_acceptance_of(const const_twa_ptr& a)
{
acc_ = a->acc();
unsigned num = acc_.num_sets();
if (num == 0)
prop_state_based_acc();
}
void copy_ap_of(const const_twa_ptr& a)
{
get_dict()->register_all_propositions_of(a, this);
}
void set_generalized_buchi(unsigned num)
{
set_num_sets_(num);
acc_.set_generalized_buchi();
if (num == 0)
prop_state_based_acc();
}
acc_cond::mark_t set_buchi()
{
set_generalized_buchi(1);
return acc_.mark(0);
}
protected:
/// Do the actual computation of tgba::support_conditions().
virtual bdd compute_support_conditions(const state* state) const = 0;
mutable const state* last_support_conditions_input_;
private:
mutable bdd last_support_conditions_output_;
protected:
// Boolean properties. Beware: true means that the property
// holds, but false means the property is unknown.
struct bprop
{
bool state_based_acc:1; // State-based acceptance.
bool inherently_weak:1; // Weak automaton.
bool deterministic:1; // Deterministic automaton.
bool stutter_inv:1; // Stutter invariant
};
union
{
unsigned props;
bprop is;
};
#ifndef SWIG
// Dynamic properties, are given with a name and a destructor function.
std::unordered_map<std::string,
std::pair<void*,
std::function<void(void*)>>> named_prop_;
#endif
void* get_named_prop_(std::string s) const;
public:
#ifndef SWIG
void set_named_prop(std::string s,
void* val, std::function<void(void*)> destructor);
template<typename T>
void set_named_prop(std::string s, T* val)
{
set_named_prop(s, val, [](void *p) { delete static_cast<T*>(p); });
}
template<typename T>
T* get_named_prop(std::string s) const
{
void* p = get_named_prop_(s);
if (!p)
return nullptr;
return static_cast<T*>(p);
}
#endif
void release_named_properties()
{
// Destroy all named properties.
for (auto& np: named_prop_)
np.second.second(np.second.first);
named_prop_.clear();
}
bool has_state_based_acc() const
{
return is.state_based_acc;
}
void prop_state_based_acc(bool val = true)
{
is.state_based_acc = val;
}
bool is_sba() const
{
return has_state_based_acc() && acc().is_buchi();
}
bool is_inherently_weak() const
{
return is.inherently_weak;
}
void prop_inherently_weak(bool val = true)
{
is.inherently_weak = val;
}
bool is_deterministic() const
{
return is.deterministic;
}
void prop_deterministic(bool val = true)
{
is.deterministic = val;
}
bool is_stutter_invariant() const
{
return is.stutter_inv;
}
void prop_stutter_invariant(bool val = true)
{
is.stutter_inv = val;
}
struct prop_set
{
bool state_based;
bool inherently_weak;
bool deterministic;
bool stutter_inv;
static prop_set all()
{
return { true, true, true, true };
}
};
// There is no default value here on purpose. This way any time we
// add a new property we have to update every call to prop_copy().
void prop_copy(const const_twa_ptr& other, prop_set p)
{
if (p.state_based)
prop_state_based_acc(other->has_state_based_acc());
if (p.inherently_weak)
prop_inherently_weak(other->is_inherently_weak());
if (p.deterministic)
prop_deterministic(other->is_deterministic());
if (p.stutter_inv)
prop_stutter_invariant(other->is_stutter_invariant());
}
void prop_keep(prop_set p)
{
if (!p.state_based)
prop_state_based_acc(false);
if (!p.inherently_weak)
prop_inherently_weak(false);
if (!p.deterministic)
prop_deterministic(false);
}
};
/// \addtogroup twa_representation TGBA representations
/// \ingroup twa
/// \addtogroup twa_algorithms TGBA algorithms
/// \ingroup twa
/// \addtogroup twa_on_the_fly_algorithms TGBA on-the-fly algorithms
/// \ingroup twa_algorithms
/// \addtogroup twa_io Input/Output of TGBA
/// \ingroup twa_algorithms
/// \addtogroup twa_ltl Translating LTL formulae into TGBA
/// \ingroup twa_algorithms
/// \addtogroup twa_generic Algorithm patterns
/// \ingroup twa_algorithms
/// \addtogroup twa_reduction TGBA simplifications
/// \ingroup twa_algorithms
/// \addtogroup twa_misc Miscellaneous algorithms on TGBA
/// \ingroup twa_algorithms
}

251
src/twa/twagraph.cc Normal file
View file

@ -0,0 +1,251 @@
// -*- coding: utf-8 -*-
// Copyright (C) 2014, 2015 Laboratoire de Recherche et Développement de
// l'Epita.
//
// 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 <http://www.gnu.org/licenses/>.
#include "twagraph.hh"
#include "ltlast/constant.hh"
namespace spot
{
void twa_graph::merge_transitions()
{
g_.remove_dead_transitions_();
typedef graph_t::trans_storage_t tr_t;
g_.sort_transitions_([](const tr_t& lhs, const tr_t& rhs)
{
if (lhs.src < rhs.src)
return true;
if (lhs.src > rhs.src)
return false;
if (lhs.dst < rhs.dst)
return true;
if (lhs.dst > rhs.dst)
return false;
return lhs.acc < rhs.acc;
// Do not sort on conditions, we'll merge
// them.
});
auto& trans = this->transition_vector();
unsigned tend = trans.size();
unsigned out = 0;
unsigned in = 1;
// Skip any leading false transition.
while (in < tend && trans[in].cond == bddfalse)
++in;
if (in < tend)
{
++out;
if (out != in)
trans[out] = trans[in];
for (++in; in < tend; ++in)
{
if (trans[in].cond == bddfalse) // Unusable transition
continue;
// Merge transitions with the same source, destination, and
// acceptance. (We test the source last, because this is the
// most likely test to be true as transitions are ordered by
// sources and then destinations.)
if (trans[out].dst == trans[in].dst
&& trans[out].acc == trans[in].acc
&& trans[out].src == trans[in].src)
{
trans[out].cond |= trans[in].cond;
}
else
{
++out;
if (in != out)
trans[out] = trans[in];
}
}
}
if (++out != tend)
trans.resize(out);
tend = out;
out = in = 2;
// FIXME: We could should also merge transitions when using
// fin_acceptance, but the rule for Fin sets are different than
// those for Inf sets, (and we need to be careful if a set is used
// both as Inf and Fin)
if ((in < tend) && !acc().uses_fin_acceptance())
{
typedef graph_t::trans_storage_t tr_t;
g_.sort_transitions_([](const tr_t& lhs, const tr_t& rhs)
{
if (lhs.src < rhs.src)
return true;
if (lhs.src > rhs.src)
return false;
if (lhs.dst < rhs.dst)
return true;
if (lhs.dst > rhs.dst)
return false;
return lhs.cond.id() < rhs.cond.id();
// Do not sort on acceptance, we'll merge
// them.
});
for (; in < tend; ++in)
{
// Merge transitions with the same source, destination,
// and conditions. (We test the source last, for the
// same reason as above.)
if (trans[out].dst == trans[in].dst
&& trans[out].cond.id() == trans[in].cond.id()
&& trans[out].src == trans[in].src)
{
trans[out].acc |= trans[in].acc;
}
else
{
++out;
if (in != out)
trans[out] = trans[in];
}
}
if (++out != tend)
trans.resize(out);
}
g_.chain_transitions_();
}
void twa_graph::purge_unreachable_states()
{
unsigned num_states = g_.num_states();
if (SPOT_UNLIKELY(num_states == 0))
return;
// The TODO vector serves two purposes:
// - it is a stack of state to process,
// - it is a set of processed states.
// The lower 31 bits of each entry is a state on the stack. (The
// next usable entry on the stack is indicated by todo_pos.) The
// 32th bit (i.e., the sign bit) of todo[x] indicates whether
// states number x has been seen.
std::vector<unsigned> todo(num_states, 0);
const unsigned seen = 1 << (sizeof(unsigned)*8-1);
const unsigned mask = seen - 1;
todo[0] = init_number_;
todo[init_number_] |= seen;
unsigned todo_pos = 1;
do
{
unsigned cur = todo[--todo_pos] & mask;
todo[todo_pos] ^= cur; // Zero the state
for (auto& t: g_.out(cur))
if (!(todo[t.dst] & seen))
{
todo[t.dst] |= seen;
todo[todo_pos++] |= t.dst;
}
}
while (todo_pos > 0);
// Now renumber each used state.
unsigned current = 0;
for (auto& v: todo)
if (!(v & seen))
v = -1U;
else
v = current++;
if (current == todo.size())
return; // No unreachable state.
init_number_ = todo[init_number_];
g_.defrag_states(std::move(todo), current);
}
void twa_graph::purge_dead_states()
{
unsigned num_states = g_.num_states();
if (num_states == 0)
return;
std::vector<unsigned> useful(num_states, 0);
// Make a DFS to compute a topological order.
std::vector<unsigned> order;
order.reserve(num_states);
std::vector<std::pair<unsigned, unsigned>> todo; // state, trans
useful[init_number_] = 1;
todo.emplace_back(init_number_, g_.state_storage(init_number_).succ);
do
{
unsigned src;
unsigned tid;
std::tie(src, tid) = todo.back();
if (tid == 0U)
{
todo.pop_back();
order.push_back(src);
continue;
}
auto& t = g_.trans_storage(tid);
todo.back().second = t.next_succ;
unsigned dst = t.dst;
if (useful[dst] != 1)
{
todo.emplace_back(dst, g_.state_storage(dst).succ);
useful[dst] = 1;
}
}
while (!todo.empty());
// Process states in topological order
for (auto s: order)
{
auto t = g_.out_iteraser(s);
bool useless = true;
while (t)
{
// Erase any transition to a useless state.
if (!useful[t->dst])
{
t.erase();
continue;
}
// if we have a transition to a useful state, then the
// state is useful.
useless = false;
++t;
}
if (useless)
useful[s] = 0;
}
// Make sure the initial state is useful (even if it has been
// marked as useless by the previous loop because it has no
// successor).
useful[init_number_] = 1;
// Now renumber each used state.
unsigned current = 0;
for (unsigned s = 0; s < num_states; ++s)
if (useful[s])
useful[s] = current++;
else
useful[s] = -1U;
if (current == num_states)
return; // No useless state.
init_number_ = useful[init_number_];
g_.defrag_states(std::move(useful), current);
}
}

482
src/twa/twagraph.hh Normal file
View file

@ -0,0 +1,482 @@
// -*- coding: utf-8 -*-
// Copyright (C) 2014, 2015 Laboratoire de Recherche et Développement
// de l'Epita.
//
// 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 <http://www.gnu.org/licenses/>.
#pragma once
#include "fwd.hh"
#include "graph/graph.hh"
#include "graph/ngraph.hh"
#include "twa/bdddict.hh"
#include "twa/twa.hh"
#include "tgbaalgos/dupexp.hh"
#include <sstream>
namespace spot
{
struct SPOT_API twa_graph_state: public spot::state
{
public:
twa_graph_state():
spot::state()
{
}
virtual ~twa_graph_state() noexcept
{
}
virtual int compare(const spot::state* other) const
{
auto o = down_cast<const twa_graph_state*>(other);
assert(o);
// Do not simply return "other - this", it might not fit in an int.
if (o < this)
return -1;
if (o > this)
return 1;
return 0;
}
virtual size_t hash() const
{
return
reinterpret_cast<const char*>(this) - static_cast<const char*>(nullptr);
}
virtual twa_graph_state*
clone() const
{
return const_cast<twa_graph_state*>(this);
}
virtual void destroy() const
{
}
};
struct SPOT_API twa_graph_trans_data
{
bdd cond;
acc_cond::mark_t acc;
explicit twa_graph_trans_data()
: cond(bddfalse), acc(0)
{
}
twa_graph_trans_data(bdd cond, acc_cond::mark_t acc = 0U)
: cond(cond), acc(acc)
{
}
bool operator<(const twa_graph_trans_data& other) const
{
if (cond.id() < other.cond.id())
return true;
if (cond.id() > other.cond.id())
return false;
return acc < other.acc;
}
bool operator==(const twa_graph_trans_data& other) const
{
return cond.id() == other.cond.id() &&
acc == other.acc;
}
};
template<class Graph>
class SPOT_API twa_graph_succ_iterator final:
public twa_succ_iterator
{
private:
typedef typename Graph::transition transition;
typedef typename Graph::state_data_t state;
const Graph* g_;
transition t_;
transition p_;
public:
twa_graph_succ_iterator(const Graph* g, transition t)
: g_(g), t_(t)
{
}
virtual void recycle(transition t)
{
t_ = t;
}
virtual bool first()
{
p_ = t_;
return p_;
}
virtual bool next()
{
p_ = g_->trans_storage(p_).next_succ;
return p_;
}
virtual bool done() const
{
return !p_;
}
virtual twa_graph_state* current_state() const
{
assert(!done());
return const_cast<twa_graph_state*>
(&g_->state_data(g_->trans_storage(p_).dst));
}
virtual bdd current_condition() const
{
assert(!done());
return g_->trans_data(p_).cond;
}
virtual acc_cond::mark_t current_acceptance_conditions() const
{
assert(!done());
return g_->trans_data(p_).acc;
}
transition pos() const
{
return p_;
}
};
class SPOT_API twa_graph final: public twa
{
public:
typedef digraph<twa_graph_state, twa_graph_trans_data> graph_t;
typedef graph_t::trans_storage_t trans_storage_t;
protected:
graph_t g_;
mutable unsigned init_number_;
public:
twa_graph(const bdd_dict_ptr& dict)
: twa(dict),
init_number_(0)
{
}
explicit twa_graph(const const_twa_graph_ptr& other, prop_set p)
: twa(other->get_dict()),
g_(other->g_), init_number_(other->init_number_)
{
copy_acceptance_of(other);
copy_ap_of(other);
prop_copy(other, p);
}
virtual ~twa_graph()
{
get_dict()->unregister_all_my_variables(this);
// Prevent this state from being destroyed by ~twa(),
// as the state will be destroyed when g_ is destroyed.
last_support_conditions_input_ = 0;
}
// FIXME: Once we ditch GCC 4.6, we can using a template aliases
// (supported from GCC 4.7 onward) instead of this.
template <typename State_Name,
typename Name_Hash = std::hash<State_Name>,
typename Name_Equal = std::equal_to<State_Name>>
struct namer
{
typedef named_graph<graph_t, State_Name, Name_Hash, Name_Equal> type;
};
template <typename State_Name,
typename Name_Hash = std::hash<State_Name>,
typename Name_Equal = std::equal_to<State_Name>>
typename namer<State_Name, Name_Hash, Name_Equal>::type*
create_namer()
{
return new named_graph<graph_t, State_Name, Name_Hash, Name_Equal>(g_);
}
graph_t& get_graph()
{
return g_;
}
const graph_t& get_graph() const
{
return g_;
}
unsigned num_states() const
{
return g_.num_states();
}
unsigned num_transitions() const
{
return g_.num_transitions();
}
void set_init_state(graph_t::state s)
{
assert(s < num_states());
init_number_ = s;
}
void set_init_state(const state* s)
{
set_init_state(state_number(s));
}
graph_t::state get_init_state_number() const
{
if (num_states() == 0)
const_cast<graph_t&>(g_).new_state();
return init_number_;
}
// FIXME: The return type ought to be const.
virtual twa_graph_state* get_init_state() const
{
if (num_states() == 0)
const_cast<graph_t&>(g_).new_state();
return const_cast<twa_graph_state*>(state_from_number(init_number_));
}
virtual twa_succ_iterator*
succ_iter(const state* st) const
{
auto s = down_cast<const typename graph_t::state_storage_t*>(st);
assert(s);
assert(!s->succ || g_.valid_trans(s->succ));
if (this->iter_cache_)
{
auto it =
down_cast<twa_graph_succ_iterator<graph_t>*>(this->iter_cache_);
it->recycle(s->succ);
this->iter_cache_ = nullptr;
return it;
}
return new twa_graph_succ_iterator<graph_t>(&g_, s->succ);
}
graph_t::state
state_number(const state* st) const
{
auto s = down_cast<const typename graph_t::state_storage_t*>(st);
assert(s);
return s - &g_.state_storage(0);
}
const twa_graph_state*
state_from_number(graph_t::state n) const
{
return &g_.state_data(n);
}
std::string format_state(unsigned n) const
{
std::stringstream ss;
ss << n;
return ss.str();
}
virtual std::string format_state(const state* st) const
{
return format_state(state_number(st));
}
twa_graph_trans_data& trans_data(const twa_succ_iterator* it)
{
auto* i = down_cast<const twa_graph_succ_iterator<graph_t>*>(it);
return g_.trans_data(i->pos());
}
twa_graph_trans_data& trans_data(unsigned t)
{
return g_.trans_data(t);
}
const twa_graph_trans_data& trans_data(const twa_succ_iterator* it) const
{
auto* i = down_cast<const twa_graph_succ_iterator<graph_t>*>(it);
return g_.trans_data(i->pos());
}
const twa_graph_trans_data& trans_data(unsigned t) const
{
return g_.trans_data(t);
}
trans_storage_t& trans_storage(const twa_succ_iterator* it)
{
auto* i = down_cast<const twa_graph_succ_iterator<graph_t>*>(it);
return g_.trans_storage(i->pos());
}
trans_storage_t& trans_storage(unsigned t)
{
return g_.trans_storage(t);
}
const trans_storage_t
trans_storage(const twa_succ_iterator* it) const
{
auto* i = down_cast<const twa_graph_succ_iterator<graph_t>*>(it);
return g_.trans_storage(i->pos());
}
const trans_storage_t trans_storage(unsigned t) const
{
return g_.trans_storage(t);
}
unsigned new_state()
{
return g_.new_state();
}
unsigned new_states(unsigned n)
{
return g_.new_states(n);
}
unsigned new_transition(unsigned src, unsigned dst,
bdd cond, acc_cond::mark_t acc = 0U)
{
return g_.new_transition(src, dst, cond, acc);
}
unsigned new_acc_transition(unsigned src, unsigned dst,
bdd cond, bool acc = true)
{
if (acc)
return g_.new_transition(src, dst, cond, acc_.all_sets());
else
return g_.new_transition(src, dst, cond);
}
#ifndef SWIG
auto out(unsigned src) const
SPOT_RETURN(g_.out(src));
auto out(unsigned src)
SPOT_RETURN(g_.out(src));
auto states() const
SPOT_RETURN(g_.states());
auto states()
SPOT_RETURN(g_.states());
auto transitions() const
SPOT_RETURN(g_.transitions());
auto transitions()
SPOT_RETURN(g_.transitions());
auto transition_vector() const
SPOT_RETURN(g_.transition_vector());
auto transition_vector()
SPOT_RETURN(g_.transition_vector());
auto is_dead_transition(const graph_t::trans_storage_t& t) const
SPOT_RETURN(g_.is_dead_transition(t));
#endif
virtual bdd compute_support_conditions(const state* s) const
{
bdd sum = bddfalse;
for (auto& t: out(state_number(s)))
sum |= t.cond;
return sum;
}
/// Iterate over all transitions, and merge those with compatible
/// extremities and acceptance.
void merge_transitions();
/// Remove all states without successors.
void purge_dead_states();
/// Remove all unreachable states.
void purge_unreachable_states();
bool state_is_accepting(unsigned s) const
{
assert(has_state_based_acc() || acc_.num_sets() == 0);
for (auto& t: g_.out(s))
// Stop at the first transition, since the remaining should be
// labeled identically.
return acc_.accepting(t.acc);
return false;
}
bool state_is_accepting(const state* s) const
{
return state_is_accepting(state_number(s));
}
bool operator==(const twa_graph& aut) const
{
if (num_states() != aut.num_states() ||
num_transitions() != aut.num_transitions() ||
acc().num_sets() != aut.acc().num_sets())
return false;
auto& trans1 = transition_vector();
auto& trans2 = aut.transition_vector();
return std::equal(trans1.begin() + 1, trans1.end(),
trans2.begin() + 1);
}
};
inline twa_graph_ptr make_twa_graph(const bdd_dict_ptr& dict)
{
return std::make_shared<twa_graph>(dict);
}
inline twa_graph_ptr make_twa_graph(const twa_graph_ptr& aut,
twa::prop_set p)
{
return std::make_shared<twa_graph>(aut, p);
}
inline twa_graph_ptr make_twa_graph(const const_twa_graph_ptr& aut,
twa::prop_set p)
{
return std::make_shared<twa_graph>(aut, p);
}
inline twa_graph_ptr make_twa_graph(const const_twa_ptr& aut,
twa::prop_set p)
{
auto a = std::dynamic_pointer_cast<const twa_graph>(aut);
if (a)
return std::make_shared<twa_graph>(a, p);
else
return tgba_dupexp_dfs(aut, p);
}
}

231
src/twa/twamask.cc Normal file
View file

@ -0,0 +1,231 @@
// -*- coding: utf-8 -*-
// Copyright (C) 2013, 2014 Laboratoire de Recherche et Développement
// de l'Epita (LRDE).
//
// 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 <http://www.gnu.org/licenses/>.
#include "twamask.hh"
#include <vector>
namespace spot
{
namespace
{
struct transition
{
const state* dest;
bdd cond;
acc_cond::mark_t acc;
};
typedef std::vector<transition> transitions;
struct succ_iter_filtered: public twa_succ_iterator
{
~succ_iter_filtered()
{
for (auto& t: trans_)
t.dest->destroy();
}
bool first()
{
it_ = trans_.begin();
return it_ != trans_.end();
}
bool next()
{
++it_;
return it_ != trans_.end();
}
bool done() const
{
return it_ == trans_.end();
}
state* current_state() const
{
return it_->dest->clone();
}
bdd current_condition() const
{
return it_->cond;
}
acc_cond::mark_t current_acceptance_conditions() const
{
return it_->acc;
}
transitions trans_;
transitions::const_iterator it_;
};
/// \ingroup twa_on_the_fly_algorithms
/// \brief A masked TGBA (abstract).
///
/// Ignores some states from a TGBA. What state are preserved or
/// ignored is controlled by the wanted() method.
///
/// This is an abstract class. You should inherit from it and
/// supply a wanted() method to specify which states to keep.
class twa_mask: public twa_proxy
{
protected:
/// \brief Constructor.
/// \param masked The automaton to mask
/// \param init Any state to use as initial state. This state will be
/// destroyed by the destructor.
twa_mask(const const_twa_ptr& masked, const state* init = 0):
twa_proxy(masked),
init_(init)
{
if (!init)
init_ = masked->get_init_state();
}
public:
virtual ~twa_mask()
{
init_->destroy();
}
virtual state* get_init_state() const
{
return init_->clone();
}
virtual twa_succ_iterator*
succ_iter(const state* local_state) const
{
succ_iter_filtered* res;
if (iter_cache_)
{
res = down_cast<succ_iter_filtered*>(iter_cache_);
res->trans_.clear();
iter_cache_ = nullptr;
}
else
{
res = new succ_iter_filtered;
}
for (auto it: original_->succ(local_state))
{
const spot::state* s = it->current_state();
auto acc = it->current_acceptance_conditions();
if (!wanted(s, acc))
{
s->destroy();
continue;
}
res->trans_.emplace_back
(transition {s, it->current_condition(), acc});
}
return res;
}
virtual bool wanted(const state* s, acc_cond::mark_t acc) const = 0;
protected:
const state* init_;
};
class twa_mask_keep: public twa_mask
{
const state_set& mask_;
public:
twa_mask_keep(const const_twa_ptr& masked,
const state_set& mask,
const state* init)
: twa_mask(masked, init),
mask_(mask)
{
}
bool wanted(const state* s, const acc_cond::mark_t) const
{
state_set::const_iterator i = mask_.find(s);
return i != mask_.end();
}
};
class twa_mask_ignore: public twa_mask
{
const state_set& mask_;
public:
twa_mask_ignore(const const_twa_ptr& masked,
const state_set& mask,
const state* init)
: twa_mask(masked, init),
mask_(mask)
{
}
bool wanted(const state* s, const acc_cond::mark_t) const
{
state_set::const_iterator i = mask_.find(s);
return i == mask_.end();
}
};
class twa_mask_acc_ignore: public twa_mask
{
unsigned mask_;
public:
twa_mask_acc_ignore(const const_twa_ptr& masked,
unsigned mask,
const state* init)
: twa_mask(masked, init),
mask_(mask)
{
}
bool wanted(const state*, const acc_cond::mark_t acc) const
{
return !acc.has(mask_);
}
};
}
const_twa_ptr
build_twa_mask_keep(const const_twa_ptr& to_mask,
const state_set& to_keep,
const state* init)
{
return std::make_shared<twa_mask_keep>(to_mask, to_keep, init);
}
const_twa_ptr
build_twa_mask_ignore(const const_twa_ptr& to_mask,
const state_set& to_ignore,
const state* init)
{
return std::make_shared<twa_mask_ignore>(to_mask, to_ignore, init);
}
const_twa_ptr
build_twa_mask_acc_ignore(const const_twa_ptr& to_mask,
unsigned to_ignore,
const state* init)
{
return std::make_shared<twa_mask_acc_ignore>(to_mask, to_ignore, init);
}
}

66
src/twa/twamask.hh Normal file
View file

@ -0,0 +1,66 @@
// -*- coding: utf-8 -*-
// Copyright (C) 2013, 2014 Laboratoire de Recherche et Développement
// de l'Epita (LRDE).
//
// 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 <http://www.gnu.org/licenses/>.
#pragma once
#include <bddx.h>
#include "twaproxy.hh"
namespace spot
{
/// \ingroup twa_on_the_fly_algorithms
/// \brief Mask a TGBA, keeping a given set of states.
///
/// Mask the TGBA \a to_mask, keeping only the
/// states from \a to_keep. The initial state
/// can optionally be reset to \a init.
SPOT_API const_twa_ptr
build_twa_mask_keep(const const_twa_ptr& to_mask,
const state_set& to_keep,
const state* init = 0);
/// \ingroup twa_on_the_fly_algorithms
/// \brief Mask a TGBA, rejecting a given set of states.
///
/// Mask the TGBA \a to_mask, keeping only the states that are not
/// in \a to_ignore. The initial state can optionally be reset to
/// \a init.
SPOT_API const_twa_ptr
build_twa_mask_ignore(const const_twa_ptr& to_mask,
const state_set& to_ignore,
const state* init = 0);
/// \ingroup twa_on_the_fly_algorithms
/// \brief Mask a TGBA, rejecting some acceptance set of transitions.
///
/// This will ignore all transitions that have the TO_IGNORE
/// acceptance mark. The initial state can optionally be reset to
/// \a init.
///
/// Note that the acceptance condition of the automaton (i.e. the
/// set of all acceptance set) is not changed, because so far this
/// function is only needed in graph algorithms that do not call
/// all_acceptance_conditions().
SPOT_API const_twa_ptr
build_twa_mask_acc_ignore(const const_twa_ptr& to_mask,
unsigned to_ignore,
const state* init = 0);
}

445
src/twa/twaproduct.cc Normal file
View file

@ -0,0 +1,445 @@
// -*- coding: utf-8 -*-
// Copyright (C) 2009, 2011, 2012, 2014, 2015 Laboratoire de Recherche et
// Développement de l'Epita (LRDE).
// 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 <http://www.gnu.org/licenses/>.
#include "twaproduct.hh"
#include <string>
#include <cassert>
#include "misc/hashfunc.hh"
#include "kripke/kripke.hh"
namespace spot
{
////////////////////////////////////////////////////////////
// state_product
state_product::~state_product()
{
left_->destroy();
right_->destroy();
}
void
state_product::destroy() const
{
if (--count_)
return;
fixed_size_pool* p = pool_;
this->~state_product();
p->deallocate(this);
}
int
state_product::compare(const state* other) const
{
const state_product* o = down_cast<const state_product*>(other);
assert(o);
int res = left_->compare(o->left());
if (res != 0)
return res;
return right_->compare(o->right());
}
size_t
state_product::hash() const
{
// We assume that size_t is 32-bit wide.
return wang32_hash(left_->hash()) ^ wang32_hash(right_->hash());
}
state_product*
state_product::clone() const
{
++count_;
return const_cast<state_product*>(this);
}
////////////////////////////////////////////////////////////
// twa_succ_iterator_product
namespace
{
class twa_succ_iterator_product_common: public twa_succ_iterator
{
public:
twa_succ_iterator_product_common(twa_succ_iterator* left,
twa_succ_iterator* right,
const twa_product* prod,
fixed_size_pool* pool)
: left_(left), right_(right), prod_(prod), pool_(pool)
{
}
void recycle(const const_twa_ptr& l, twa_succ_iterator* left,
const_twa_ptr r, twa_succ_iterator* right)
{
l->release_iter(left_);
left_ = left;
r->release_iter(right_);
right_ = right;
}
virtual ~twa_succ_iterator_product_common()
{
delete left_;
delete right_;
}
virtual bool next_non_false_() = 0;
bool first()
{
if (!right_)
return false;
// If one of the two successor sets is empty initially, we
// reset right_, so that done() can detect this situation
// easily. (We choose to reset right_ because this variable
// is already used by done().)
if (!(left_->first() && right_->first()))
{
delete right_;
right_ = 0;
return false;
}
return next_non_false_();
}
bool done() const
{
return !right_ || right_->done();
}
state_product* current_state() const
{
return new(pool_->allocate()) state_product(left_->current_state(),
right_->current_state(),
pool_);
}
protected:
twa_succ_iterator* left_;
twa_succ_iterator* right_;
const twa_product* prod_;
fixed_size_pool* pool_;
friend class spot::twa_product;
};
/// \brief Iterate over the successors of a product computed on the fly.
class twa_succ_iterator_product: public twa_succ_iterator_product_common
{
public:
twa_succ_iterator_product(twa_succ_iterator* left,
twa_succ_iterator* right,
const twa_product* prod,
fixed_size_pool* pool)
: twa_succ_iterator_product_common(left, right, prod, pool)
{
}
virtual ~twa_succ_iterator_product()
{
}
bool step_()
{
if (left_->next())
return true;
left_->first();
return right_->next();
}
bool next_non_false_()
{
assert(!done());
do
{
bdd l = left_->current_condition();
bdd r = right_->current_condition();
bdd current_cond = l & r;
if (current_cond != bddfalse)
{
current_cond_ = current_cond;
return true;
}
}
while (step_());
return false;
}
bool next()
{
if (step_())
return next_non_false_();
return false;
}
bdd current_condition() const
{
return current_cond_;
}
acc_cond::mark_t current_acceptance_conditions() const
{
return
prod_->acc().join(prod_->left_acc(),
left_->current_acceptance_conditions(),
prod_->right_acc(),
right_->current_acceptance_conditions());
}
protected:
bdd current_cond_;
};
/// Iterate over the successors of a product computed on the fly.
/// This one assumes that LEFT is an iterator over a Kripke structure
class twa_succ_iterator_product_kripke:
public twa_succ_iterator_product_common
{
public:
twa_succ_iterator_product_kripke(twa_succ_iterator* left,
twa_succ_iterator* right,
const twa_product* prod,
fixed_size_pool* pool)
: twa_succ_iterator_product_common(left, right, prod, pool)
{
}
virtual ~twa_succ_iterator_product_kripke()
{
}
bool next_non_false_()
{
// All the transitions of left_ iterator have the
// same label, because it is a Kripke structure.
bdd l = left_->current_condition();
assert(!right_->done());
do
{
bdd r = right_->current_condition();
bdd current_cond = l & r;
if (current_cond != bddfalse)
{
current_cond_ = current_cond;
return true;
}
}
while (right_->next());
return false;
}
bool next()
{
if (left_->next())
return true;
left_->first();
if (right_->next())
return next_non_false_();
return false;
}
bdd current_condition() const
{
return current_cond_;
}
acc_cond::mark_t current_acceptance_conditions() const
{
return right_->current_acceptance_conditions();
}
protected:
bdd current_cond_;
};
} // anonymous
////////////////////////////////////////////////////////////
// twa_product
twa_product::twa_product(const const_twa_ptr& left,
const const_twa_ptr& right)
: twa(left->get_dict()), left_(left), right_(right),
pool_(sizeof(state_product))
{
assert(get_dict() == right_->get_dict());
// If one of the side is a Kripke structure, it is easier to deal
// with (we don't have to fix the acceptance conditions, and
// computing the successors can be improved a bit).
if (dynamic_cast<const kripke*>(left_.get()))
{
left_kripke_ = true;
}
else if (dynamic_cast<const kripke*>(right_.get()))
{
std::swap(left_, right_);
left_kripke_ = true;
}
else
{
left_kripke_ = false;
}
auto d = get_dict();
d->register_all_propositions_of(&left_, this);
d->register_all_propositions_of(&right_, this);
assert(acc_.num_sets() == 0);
auto left_num = left->acc().num_sets();
auto right_acc = right->get_acceptance();
right_acc.shift_left(left_num);
right_acc.append_and(left->get_acceptance());
set_acceptance(left_num + right->acc().num_sets(), right_acc);
}
twa_product::~twa_product()
{
get_dict()->unregister_all_my_variables(this);
// Prevent these states from being destroyed by ~tgba(): they
// will be destroyed before when the pool is destructed.
if (last_support_conditions_input_)
{
last_support_conditions_input_->destroy();
last_support_conditions_input_ = 0;
}
}
state*
twa_product::get_init_state() const
{
fixed_size_pool* p = const_cast<fixed_size_pool*>(&pool_);
return new(p->allocate()) state_product(left_->get_init_state(),
right_->get_init_state(), p);
}
twa_succ_iterator*
twa_product::succ_iter(const state* state) const
{
const state_product* s = down_cast<const state_product*>(state);
assert(s);
twa_succ_iterator* li = left_->succ_iter(s->left());
twa_succ_iterator* ri = right_->succ_iter(s->right());
if (iter_cache_)
{
twa_succ_iterator_product_common* it =
down_cast<twa_succ_iterator_product_common*>(iter_cache_);
it->recycle(left_, li, right_, ri);
iter_cache_ = nullptr;
return it;
}
fixed_size_pool* p = const_cast<fixed_size_pool*>(&pool_);
if (left_kripke_)
return new twa_succ_iterator_product_kripke(li, ri, this, p);
else
return new twa_succ_iterator_product(li, ri, this, p);
}
bdd
twa_product::compute_support_conditions(const state* in) const
{
const state_product* s = down_cast<const state_product*>(in);
assert(s);
bdd lsc = left_->support_conditions(s->left());
bdd rsc = right_->support_conditions(s->right());
return lsc & rsc;
}
const acc_cond& twa_product::left_acc() const
{
return left_->acc();
}
const acc_cond& twa_product::right_acc() const
{
return right_->acc();
}
std::string
twa_product::format_state(const state* state) const
{
const state_product* s = down_cast<const state_product*>(state);
assert(s);
return (left_->format_state(s->left())
+ " * "
+ right_->format_state(s->right()));
}
state*
twa_product::project_state(const state* s, const const_twa_ptr& t) const
{
const state_product* s2 = down_cast<const state_product*>(s);
assert(s2);
if (t.get() == this)
return s2->clone();
state* res = left_->project_state(s2->left(), t);
if (res)
return res;
return right_->project_state(s2->right(), t);
}
std::string
twa_product::transition_annotation(const twa_succ_iterator* t) const
{
const twa_succ_iterator_product_common* i =
down_cast<const twa_succ_iterator_product_common*>(t);
assert(i);
std::string left = left_->transition_annotation(i->left_);
std::string right = right_->transition_annotation(i->right_);
if (left == "")
return right;
if (right == "")
return left;
return "<" + left + ", " + right + ">";
}
//////////////////////////////////////////////////////////////////////
// twa_product_init
twa_product_init::twa_product_init(const const_twa_ptr& left,
const const_twa_ptr& right,
const state* left_init,
const state* right_init)
: twa_product(left, right),
left_init_(left_init), right_init_(right_init)
{
if (left_ != left)
std::swap(left_init_, right_init_);
}
state*
twa_product_init::get_init_state() const
{
fixed_size_pool* p = const_cast<fixed_size_pool*>(&pool_);
return new(p->allocate()) state_product(left_init_->clone(),
right_init_->clone(), p);
}
}

149
src/twa/twaproduct.hh Normal file
View file

@ -0,0 +1,149 @@
// -*- coding: utf-8 -*-
// Copyright (C) 2011, 2013, 2014, 2015 Laboratoire de Recherche et
// Développement de l'Epita (LRDE).
// 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 <http://www.gnu.org/licenses/>.
#pragma once
#include "twa.hh"
#include "misc/fixpool.hh"
namespace spot
{
/// \ingroup twa_on_the_fly_algorithms
/// \brief A state for spot::twa_product.
///
/// This state is in fact a pair of state: the state from the left
/// automaton and that of the right.
class SPOT_API state_product final: public state
{
public:
/// \brief Constructor
/// \param left The state from the left automaton.
/// \param right The state from the right automaton.
/// \param pool The pool from which the state was allocated.
/// These states are acquired by spot::state_product, and will
/// be destroyed on destruction.
state_product(state* left, state* right, fixed_size_pool* pool)
: left_(left), right_(right), count_(1), pool_(pool)
{
}
virtual void destroy() const;
state*
left() const
{
return left_;
}
state*
right() const
{
return right_;
}
virtual int compare(const state* other) const;
virtual size_t hash() const;
virtual state_product* clone() const;
private:
state* left_; ///< State from the left automaton.
state* right_; ///< State from the right automaton.
mutable unsigned count_;
fixed_size_pool* pool_;
virtual ~state_product();
state_product(const state_product& o); // No implementation.
};
/// \brief A lazy product. (States are computed on the fly.)
class SPOT_API twa_product: public twa
{
public:
/// \brief Constructor.
/// \param left The left automata in the product.
/// \param right The right automata in the product.
/// Do not be fooled by these arguments: a product is commutative.
twa_product(const const_twa_ptr& left, const const_twa_ptr& right);
virtual ~twa_product();
virtual state* get_init_state() const;
virtual twa_succ_iterator*
succ_iter(const state* state) const;
virtual std::string format_state(const state* state) const;
virtual std::string
transition_annotation(const twa_succ_iterator* t) const;
virtual state* project_state(const state* s, const const_twa_ptr& t) const;
const acc_cond& left_acc() const;
const acc_cond& right_acc() const;
protected:
virtual bdd compute_support_conditions(const state* state) const;
protected:
const_twa_ptr left_;
const_twa_ptr right_;
bool left_kripke_;
fixed_size_pool pool_;
private:
// Disallow copy.
twa_product(const twa_product&) SPOT_DELETED;
twa_product& operator=(const twa_product&) SPOT_DELETED;
};
/// \brief A lazy product with different initial states.
class SPOT_API twa_product_init final: public twa_product
{
public:
twa_product_init(const const_twa_ptr& left, const const_twa_ptr& right,
const state* left_init, const state* right_init);
virtual state* get_init_state() const;
protected:
const state* left_init_;
const state* right_init_;
};
/// \brief on-the-fly TGBA product
inline twa_product_ptr otf_product(const const_twa_ptr& left,
const const_twa_ptr& right)
{
return std::make_shared<twa_product>(left, right);
}
/// \brief on-the-fly TGBA product with forced initial states
inline twa_product_ptr otf_product_at(const const_twa_ptr& left,
const const_twa_ptr& right,
const state* left_init,
const state* right_init)
{
return std::make_shared<twa_product_init>(left, right,
left_init, right_init);
}
}

75
src/twa/twaproxy.cc Normal file
View file

@ -0,0 +1,75 @@
// -*- coding: utf-8 -*-
// Copyright (C) 2013, 2014, 2015 Laboratoire de Recherche et
// Développement de l'Epita (LRDE).
//
// 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 <http://www.gnu.org/licenses/>.
#include "twaproxy.hh"
namespace spot
{
twa_proxy::twa_proxy(const const_twa_ptr& original)
: twa(original->get_dict()), original_(original)
{
get_dict()->register_all_variables_of(original, this);
acc_.add_sets(original->acc().num_sets());
}
twa_proxy::~twa_proxy()
{
get_dict()->unregister_all_my_variables(this);
}
state* twa_proxy::get_init_state() const
{
return original_->get_init_state();
}
twa_succ_iterator*
twa_proxy::succ_iter(const state* state) const
{
if (iter_cache_)
{
original_->release_iter(iter_cache_);
iter_cache_ = nullptr;
}
return original_->succ_iter(state);
}
std::string
twa_proxy::format_state(const state* state) const
{
return original_->format_state(state);
}
std::string
twa_proxy::transition_annotation(const twa_succ_iterator* t) const
{
return original_->transition_annotation(t);
}
state*
twa_proxy::project_state(const state* s, const const_twa_ptr& t) const
{
return original_->project_state(s, t);
}
bdd
twa_proxy::compute_support_conditions(const state* state) const
{
return original_->support_conditions(state);
}
}

59
src/twa/twaproxy.hh Normal file
View file

@ -0,0 +1,59 @@
// -*- coding: utf-8 -*-
// Copyright (C) 2013, 2014, 2015 Laboratoire de Recherche et
// Développement de l'Epita (LRDE).
//
// 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 <http://www.gnu.org/licenses/>.
#pragma once
#include "twa.hh"
namespace spot
{
/// \ingroup twa_on_the_fly_algorithms
/// \brief A TGBA proxy.
///
/// This implements a simple proxy to an existing
/// TGBA, forwarding all methods to the original.
/// By itself this class is pointless: better use the
/// original automaton right away. However it is useful
/// to inherit from this class and override some of its
/// methods to implement some on-the-fly algorithm.
class SPOT_API twa_proxy: public twa
{
protected:
twa_proxy(const const_twa_ptr& original);
public:
virtual ~twa_proxy();
virtual state* get_init_state() const;
virtual twa_succ_iterator*
succ_iter(const state* state) const;
virtual std::string format_state(const state* state) const;
virtual std::string
transition_annotation(const twa_succ_iterator* t) const;
virtual state* project_state(const state* s, const const_twa_ptr& t) const;
protected:
virtual bdd compute_support_conditions(const state* state) const;
const_twa_ptr original_;
};
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,95 @@
// -*- coding: utf-8 -*-
// Copyright (C) 2009, 2010, 2011, 2013, 2014, 2015 Laboratoire de
// Recherche et Développement de l'Epita (LRDE).
//
// 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 <http://www.gnu.org/licenses/>.
#pragma once
#include <vector>
#include "twa.hh"
#ifndef TRANSFORM_TO_TBA
# define TRANSFORM_TO_TBA 0
#endif
#define TRANSFORM_TO_TGBA (!TRANSFORM_TO_TBA)
namespace spot
{
/// \ingroup twa_on_the_fly_algorithms
/// \brief Build a complemented automaton.
///
/// It creates an automaton that recognizes the
/// negated language of \a aut.
///
/// 1. First Safra construction algorithm produces a
/// deterministic Rabin automaton.
/// 2. Interpreting this deterministic Rabin automaton as a
/// deterministic Streett will produce a complemented automaton.
/// 3. Then we use a transformation from deterministic Streett
/// automaton to nondeterministic Büchi automaton.
///
/// Safra construction is done in \a tgba_complement, the transformation
/// is done on-the-fly when successors are called.
///
/// \sa safra_determinisation, tgba_safra_complement::succ_iter.
class SPOT_API tgba_safra_complement : public twa
{
public:
tgba_safra_complement(const const_twa_graph_ptr& a);
virtual ~tgba_safra_complement();
// tgba interface.
virtual state* get_init_state() const;
virtual twa_succ_iterator* succ_iter(const state* state) const;
virtual std::string format_state(const state* state) const;
void* get_safra() const
{
return safra_;
}
protected:
virtual bdd compute_support_conditions(const state* state) const;
private:
const_twa_graph_ptr automaton_;
void* safra_;
#if TRANSFORM_TO_TBA
acc_cond::mark_t the_acceptance_cond_;
#endif
#if TRANSFORM_TO_TGBA
// Map to i the i-th acceptance condition of the final automaton.
std::vector<acc_cond::mark_t> acceptance_cond_vec_;
#endif
};
typedef std::shared_ptr<tgba_safra_complement> tgba_safra_complement_ptr;
typedef std::shared_ptr<const tgba_safra_complement>
const_tgba_safra_complement_ptr;
inline tgba_safra_complement_ptr
make_safra_complement(const const_twa_graph_ptr& a)
{
return std::make_shared<tgba_safra_complement>(a);
}
/// \brief Produce a dot output of the Safra automaton associated
/// to \a a.
///
/// \param a The \c tgba_safra_complement with an intermediate Safra
/// automaton to display
void SPOT_API display_safra(const const_tgba_safra_complement_ptr& a);
}