Commit cab3be97 authored by Alexandre Duret-Lutz's avatar Alexandre Duret-Lutz
Browse files

Before this change, all automata would construct their own

dictionaries (map of BDD variables to LTL formulae).  This was
cumbersome, because to multiply two automata we had to build a
common dictionary (the union of the two LTL formula spaces), and
install wrappers to translate each automaton's BDD answers into
the common dictionary.  This translation, that had to be repeated
when several products were nested, was time consuming and was a
hindrance for some optimizations.
In the new scheme, all automata involved in a product must
share the same dictionary.  An empty dictionary should be
constructed by the user and passed to the automaton' constructors
as necessary.
This huge change removes most code than it adds.

* src/Makefile.am (libspot_la_LIBADD): Add misc/libmisc.la.
* src/misc/bddalloc.hh, src/misc/bddalloc.cc: New files.  These
partly replace src/tgba/bddfactory.hh and src/tgba/bddfactory.cc.
* src/misc/Makefile.am: Adjust to build bddalloc.hh and bddalloc.cc.
* src/tgba/bddfactory.hh, src/tgba/bddfactory.cc,
src/tgba/dictunion.hh, src/tgba/dictunion.cc,
src/tgba/tgbabdddict.hh, src/tgba/tgbabdddict.cc,
src/tgba/tgbabddtranslatefactory.hh,
src/tgba/tgbabddtranslatefactory.cc,
src/tgba/tgbatranslateproxy.hh, src/tgba/tgbatranslateproxy.cc:
Delete.
* src/tgba/bdddict.hh, src/tgba/bdddict.cc: New files.  These
replaces tgbabdddict.hh and tgbabdddict.cc, and also part of
bddfactory.hh and bddfactory.cc.
* src/tgba/bddprint.cc, src/tgba/bddprint.hh: Adjust to
use bdd_dict* instead of tgba_bdd_dict&.
* src/tgba/succiterconcrete.cc (succ_iter_concrete::next()):
Get next_to_now from the dictionary.
* src/tgba/tgba.hh (tgba::get_dict): Return a bdd_dict*,
not a const tgba_bdd_dict*.
* src/tgba/tgbabddconcrete.cc, src/tgba/tgbabddconcrete.hh:
Adjust to use the new dictionary, stored in data_.
* src/tgba/tgbabddconcretefactory.cc,
src/tgba/tgbabddconcretefactory.hh: Likewise.  Plus
now_to_next_ is now also stored in the dictionary.
* src/tgba/tgbabddconcreteproduct.cc: Likewise.  Now
that both operand share the same product, there is not
point in using tgba_bdd_translate_factory.
* src/tgba/tgbabddcoredata.cc, src/tgba/tgbabddcoredata.hh:
Store a bdd_dict (taken as constructor argument).
(tgba_bdd_core_data::~tgba_bdd_core_data): Remove.
(tgba_bdd_core_data::translate): Remove.
(tgba_bdd_core_data::next_to_now): Remove (now in dict).
(tgba_bdd_core_data::dict): New pointer.
* src/tgba/tgbabddfactory.hh: (tgba_bdd_factory::get_dict): Remove.
* src/tgba/tgbaexplicit.cc, src/tgba/tgbaexplicit.hh:
Adjust to use the new dictionary.
* src/tgba/tgbaproduct.cc, src/tgba/tgbaproduct.hh: Likewise.  Do
not use tgba_bdd_dict_union and tgba_bdd_translate_proxy anymore.
* src/tgbaalgos/lbtt.cc, src/tgbaalgos/save.cc: Adjust to
use bdd_dict* instead of tgba_bdd_dict&.
* src/tgbaalgos/ltl2tgba.cc, src/tgbaalgos/ltl2tgba.cc: Likewise.
(ltl_to_tgba): Take a dict argument.
* src/tgbaparse/public.hh (tgba_parse): Take a dict argument.
* src/tgbaparse/tgbaparse.yy (tgba_parse): Take a dict argument.
* src/tgbatest/explicit.cc, src/tgbatest/explprod.cc,
src/tgbatest/ltlprod.cc, src/tgbatest/mixprod.cc,
src/tgbatest/readsave.cc, src/tgbatest/spotlbtt.cc,
src/tgbatest/tgbaread.cc, src/tgbatest/tripprod.cc: Instantiate
a dictionary, and pass it to the automata' constructors.
* src/tgbatest/ltl2tgba.cc: Likewise, and remove the -o (defrag)
option.
* iface/gspn/gspn.hh (tgba_gspn::tgba_gspn): Take a bdd_dict argument.
(tgba_gspn::get_dict): Adjust return type.
* iface/gspn/gspn.cc: Do not use bdd_factory, adjust to
use the new dictionary instead.
parent 3013ba8d
2003-07-13 Alexandre Duret-Lutz <adl@gnu.org>
2003-07-14 Alexandre Duret-Lutz <aduret@src.lip6.fr>
Before this change, all automata would construct their own
dictionaries (map of BDD variables to LTL formulae). This was
cumbersome, because to multiply two automata we had to build a
common dictionary (the union of the two LTL formula spaces), and
install wrappers to translate each automaton's BDD answers into
the common dictionary. This translation, that had to be repeated
when several products were nested, was time consuming and was a
hindrance for some optimizations.
In the new scheme, all automata involved in a product must
share the same dictionary. An empty dictionary should be
constructed by the user and passed to the automaton' constructors
as necessary.
This huge change removes most code than it adds.
* src/Makefile.am (libspot_la_LIBADD): Add misc/libmisc.la.
* src/misc/bddalloc.hh, src/misc/bddalloc.cc: New files. These
partly replace src/tgba/bddfactory.hh and src/tgba/bddfactory.cc.
* src/misc/Makefile.am: Adjust to build bddalloc.hh and bddalloc.cc.
* src/tgba/bddfactory.hh, src/tgba/bddfactory.cc,
src/tgba/dictunion.hh, src/tgba/dictunion.cc,
src/tgba/tgbabdddict.hh, src/tgba/tgbabdddict.cc,
src/tgba/tgbabddtranslatefactory.hh,
src/tgba/tgbabddtranslatefactory.cc,
src/tgba/tgbatranslateproxy.hh, src/tgba/tgbatranslateproxy.cc:
Delete.
* src/tgba/bdddict.hh, src/tgba/bdddict.cc: New files. These
replaces tgbabdddict.hh and tgbabdddict.cc, and also part of
bddfactory.hh and bddfactory.cc.
* src/tgba/bddprint.cc, src/tgba/bddprint.hh: Adjust to
use bdd_dict* instead of tgba_bdd_dict&.
* src/tgba/succiterconcrete.cc (succ_iter_concrete::next()):
Get next_to_now from the dictionary.
* src/tgba/tgba.hh (tgba::get_dict): Return a bdd_dict*,
not a const tgba_bdd_dict*.
* src/tgba/tgbabddconcrete.cc, src/tgba/tgbabddconcrete.hh:
Adjust to use the new dictionary, stored in data_.
* src/tgba/tgbabddconcretefactory.cc,
src/tgba/tgbabddconcretefactory.hh: Likewise. Plus
now_to_next_ is now also stored in the dictionary.
* src/tgba/tgbabddconcreteproduct.cc: Likewise. Now
that both operand share the same product, there is not
point in using tgba_bdd_translate_factory.
* src/tgba/tgbabddcoredata.cc, src/tgba/tgbabddcoredata.hh:
Store a bdd_dict (taken as constructor argument).
(tgba_bdd_core_data::~tgba_bdd_core_data): Remove.
(tgba_bdd_core_data::translate): Remove.
(tgba_bdd_core_data::next_to_now): Remove (now in dict).
(tgba_bdd_core_data::dict): New pointer.
* src/tgba/tgbabddfactory.hh: (tgba_bdd_factory::get_dict): Remove.
* src/tgba/tgbaexplicit.cc, src/tgba/tgbaexplicit.hh:
Adjust to use the new dictionary.
* src/tgba/tgbaproduct.cc, src/tgba/tgbaproduct.hh: Likewise. Do
not use tgba_bdd_dict_union and tgba_bdd_translate_proxy anymore.
* src/tgbaalgos/lbtt.cc, src/tgbaalgos/save.cc: Adjust to
use bdd_dict* instead of tgba_bdd_dict&.
* src/tgbaalgos/ltl2tgba.cc, src/tgbaalgos/ltl2tgba.cc: Likewise.
(ltl_to_tgba): Take a dict argument.
* src/tgbaparse/public.hh (tgba_parse): Take a dict argument.
* src/tgbaparse/tgbaparse.yy (tgba_parse): Take a dict argument.
* src/tgbatest/explicit.cc, src/tgbatest/explprod.cc,
src/tgbatest/ltlprod.cc, src/tgbatest/mixprod.cc,
src/tgbatest/readsave.cc, src/tgbatest/spotlbtt.cc,
src/tgbatest/tgbaread.cc, src/tgbatest/tripprod.cc: Instantiate
a dictionary, and pass it to the automata' constructors.
* src/tgbatest/ltl2tgba.cc: Likewise, and remove the -o (defrag)
option.
* iface/gspn/gspn.hh (tgba_gspn::tgba_gspn): Take a bdd_dict argument.
(tgba_gspn::get_dict): Adjust return type.
* iface/gspn/gspn.cc: Do not use bdd_factory, adjust to
use the new dictionary instead.
2003-07-13 Alexandre Duret-Lutz <aduret@src.lip6.fr>
* configure.ac: Bump version to 0.0e.
......
......@@ -4,7 +4,6 @@
#include "gspnlib.h"
#include "gspn.hh"
#include "ltlvisit/destroy.hh"
#include "tgba/bddfactory.hh"
namespace spot
{
......@@ -12,52 +11,59 @@ namespace spot
// tgba_gspn_private_
//////////////////////////////////////////////////////////////////////
struct tgba_gspn_private_ : public bdd_factory
struct tgba_gspn_private_
{
int refs; // reference count
tgba_bdd_dict dict;
bdd_dict* dict;
typedef std::pair<AtomicPropKind, bdd> ab_pair;
typedef std::map<AtomicProp, ab_pair> prop_map;
prop_map prop_dict;
AtomicProp *all_indexes;
size_t index_count;
tgba_gspn_private_(const gspn_environment& env)
: refs(0)
tgba_gspn_private_(bdd_dict* dict, const gspn_environment& env)
: refs(0), dict(dict)
{
const gspn_environment::prop_map& p = env.get_prop_map();
for (gspn_environment::prop_map::const_iterator i = p.begin();
i != p.end(); ++i)
try
{
int var = create_node();
i->second->ref();
dict.var_map[i->second] = var;
dict.var_formula_map[var] = i->second;
AtomicProp index;
int err = prop_index(i->first.c_str(), &index);
if (err)
throw gspn_exeption(err);
AtomicPropKind kind;
err = prop_kind(index, &kind);
if (err)
throw gspn_exeption(err);
prop_dict[index] = ab_pair(kind, ithvar(var));
for (gspn_environment::prop_map::const_iterator i = p.begin();
i != p.end(); ++i)
{
int var = dict->register_proposition(i->second, this);
AtomicProp index;
int err = prop_index(i->first.c_str(), &index);
if (err)
throw gspn_exeption(err);
AtomicPropKind kind;
err = prop_kind(index, &kind);
if (err)
throw gspn_exeption(err);
prop_dict[index] = ab_pair(kind, bdd_ithvar(var));
}
index_count = prop_dict.size();
all_indexes = new AtomicProp[index_count];
unsigned idx = 0;
for (prop_map::const_iterator i = prop_dict.begin();
i != prop_dict.end(); ++i)
all_indexes[idx++] = i->first;
}
// If an exception occurs during the loop, dict will
// be cleaned and that will dereferenciate the formula
// it contains. No need to handle anything explicitely.
index_count = prop_dict.size();
all_indexes = new AtomicProp[index_count];
catch (...)
{
// If an exception occurs during the loop, we need to clean
// all BDD variables which have been registered so far.
dict->unregister_all_my_variables(this);
}
}
unsigned idx = 0;
for (prop_map::const_iterator i = prop_dict.begin();
i != prop_dict.end(); ++i)
all_indexes[idx++] = i->first;
tgba_gspn_private_::~tgba_gspn_private_()
{
dict->unregister_all_my_variables(this);
}
bdd index_to_bdd(AtomicProp index) const
......@@ -239,9 +245,9 @@ namespace spot
//////////////////////////////////////////////////////////////////////
tgba_gspn::tgba_gspn(const gspn_environment& env)
tgba_gspn::tgba_gspn(bdd_dict* dict, const gspn_environment& env)
{
data_ = new tgba_gspn_private_(env);
data_ = new tgba_gspn_private_(dict, env);
}
tgba_gspn::tgba_gspn(const tgba_gspn& other)
......@@ -299,7 +305,7 @@ namespace spot
return new tgba_succ_iterator_gspn(state_conds, s->get_state(), data_);
}
const tgba_bdd_dict&
bdd_dict*
tgba_gspn::get_dict() const
{
return data_->dict;
......
......@@ -2,7 +2,7 @@
# define SPOT_IFACE_GSPN_HH
// Try not to include gspnlib.h here, or it will polute the user's
// namespace with internal C symbols.
// namespace with internal C symbols.
# include <string>
# include "tgba/tgba.hh"
......@@ -20,7 +20,7 @@ namespace spot
: err(err)
{
}
int
get_err() const
{
......@@ -29,7 +29,7 @@ namespace spot
private:
int err;
};
class gspn_environment : public ltl::environment
{
......@@ -42,11 +42,11 @@ namespace spot
bool declare(const std::string& prop_str);
virtual ltl::formula* require(const std::string& prop_str);
/// Get the name of the environment.
virtual const std::string& name();
typedef std::map<const std::string, ltl::atomic_prop*> prop_map;
typedef std::map<const std::string, ltl::atomic_prop*> prop_map;
/// Get the map of atomic proposition known to this environment.
const prop_map& get_prop_map() const;
......@@ -62,20 +62,20 @@ namespace spot
class tgba_gspn: public tgba
{
public:
tgba_gspn(const gspn_environment& env);
tgba_gspn(bdd_dict* dict, const gspn_environment& env);
tgba_gspn(const tgba_gspn& other);
tgba_gspn& operator=(const tgba_gspn& other);
virtual ~tgba_gspn();
virtual state* get_init_state() const;
virtual tgba_succ_iterator* succ_iter(const state* state) const;
virtual const tgba_bdd_dict& get_dict() const;
virtual bdd_dict* get_dict() const;
virtual std::string format_state(const state* state) const;
virtual bdd all_accepting_conditions() const;
virtual bdd neg_accepting_conditions() const;
private:
tgba_gspn_private_* data_;
};
}
#endif // SPOT_IFACE_GSPN_HH
......@@ -9,6 +9,7 @@ lib_LTLIBRARIES = libspot.la
libspot_la_SOURCES =
libspot_la_LDFLAGS = $(BUDDY_LDFLAGS)
libspot_la_LIBADD = \
misc/libmisc.la \
ltlenv/libltlenv.la \
ltlparse/libltlparse.la \
ltlvisit/libltlvisit.la \
......
Makefile
Makefile.in
.deps
.libs
*.lo
*.la
AM_CPPFLAGS = -I$(srcdir)/.. $(BUDDY_CPPFLAGS)
AM_CXXFLAGS = $(WARNING_CXXFLAGS)
miscdir = $(pkgincludedir)/misc
misc_HEADERS = const_sel.hh
misc_HEADERS = \
bddalloc.hh \
const_sel.hh
noinst_LTLIBRARIES = libmisc.la
libmisc_la_SOURCES = \
bddalloc.cc
#include <bdd.h>
#include <cassert>
#include "bddalloc.hh"
namespace spot
{
bool bdd_allocator::initialized = false;
int bdd_allocator::varnum = 2;
bdd_allocator::bdd_allocator()
{
initialize();
free_list.push_front(pos_lenght_pair(0, varnum));
}
void
bdd_allocator::initialize()
{
if (initialized)
return;
initialized = true;
bdd_init(50000, 5000);
bdd_setvarnum(varnum);
}
int
bdd_allocator::allocate_variables(int n)
{
// Browse the free list until we find N consecutive variables. We
// try not to fragment the list my allocating the variables in the
// smallest free range we find.
free_list_type::iterator best = free_list.end();
free_list_type::iterator cur;
for (cur = free_list.begin(); cur != free_list.end(); ++cur)
{
if (cur->second < n)
continue;
if (n == cur->second)
{
best = cur;
break;
}
if (best == free_list.end()
|| cur->second < best->second)
best = cur;
}
// We have found enough free variables.
if (best != free_list.end())
{
int result = best->first;
best->second -= n;
assert(best->second >= 0);
// Erase the range if it's now empty.
if (best->second == 0)
free_list.erase(best);
else
best->first += n;
return result;
}
// We haven't found enough adjacent free variables;
// ask BuDDy for some more.
// If we already have some free variable at the end
// of the variable space, allocate just the difference.
if (free_list.size() > 0
&& free_list.back().first + free_list.back().second == varnum)
{
int res = free_list.back().first;
int endvar = free_list.back().second;
assert(n > endvar);
bdd_extvarnum(n - endvar);
varnum += n - endvar;
free_list.pop_back();
return res;
}
else
{
// Otherwise, allocate as much variables as we need.
int res = varnum;
bdd_extvarnum(n);
varnum += n;
return res;
}
}
void
bdd_allocator::release_variables(int base, int n)
{
free_list_type::iterator cur;
int end = base + n;
for (cur = free_list.begin(); cur != free_list.end(); ++cur)
{
// Append to a range ...
if (cur->first + cur->second == base)
{
cur->second += n;
// Maybe the next item on the list can be merged.
free_list_type::iterator next = cur;
++next;
if (next != free_list.end()
&& next->first == end)
{
cur->second += next->second;
free_list.erase(next);
}
return;
}
// ... or prepend to a range ...
if (cur->first == end)
{
cur->first -= n;
cur->second += n;
return;
}
// ... or insert a new range.
if (cur->first > end)
break;
}
free_list.insert(cur, pos_lenght_pair(base, n));
}
}
#ifndef SPOT_MISC_BDDALLOC_HH
# define SPOT_MISC_BDDALLOC_HH
#include <list>
#include <utility>
namespace spot
{
/// Manage ranges of variables.
class bdd_allocator
{
protected:
/// Default constructor.
bdd_allocator();
/// Initialize the BDD library.
static void initialize();
/// Allocate \a n BDD variables.
int allocate_variables(int n);
/// Release \a n BDD variables starting at \a base.
void release_variables(int base, int n);
static bool initialized; ///< Whether the BDD library has been initialized.
static int varnum; ///< number of variable in use in the BDD library.
typedef std::pair<int, int> pos_lenght_pair;
typedef std::list<pos_lenght_pair> free_list_type;
free_list_type free_list; ///< Tracks unused BDD variables.
};
}
#endif // SPOT_MISC_BDDALLOC_HH
......@@ -4,9 +4,8 @@ AM_CXXFLAGS = $(WARNING_CXXFLAGS)
tgbadir = $(pkgincludedir)/tgba
tgba_HEADERS = \
bddfactory.hh \
bdddict.hh \
bddprint.hh \
dictunion.hh \
public.hh \
state.hh \
statebdd.hh \
......@@ -17,26 +16,19 @@ tgba_HEADERS = \
tgbabddconcretefactory.hh \
tgbabddconcreteproduct.hh \
tgbabddcoredata.hh \
tgbabdddict.hh \
tgbabddfactory.hh \
tgbabddtranslatefactory.hh \
tgbaexplicit.hh \
tgbaproduct.hh \
tgbatranslateproxy.hh
tgbaproduct.hh
noinst_LTLIBRARIES = libtgba.la
libtgba_la_SOURCES = \
bddfactory.cc \
bdddict.cc \
bddprint.cc \
dictunion.cc \
statebdd.cc \
succiterconcrete.cc \
tgbabddconcrete.cc \
tgbabddconcretefactory.cc \
tgbabddconcreteproduct.cc \
tgbabddcoredata.cc \
tgbabdddict.cc \
tgbabddtranslatefactory.cc \
tgbaexplicit.cc \
tgbaproduct.cc \
tgbatranslateproxy.cc
tgbaproduct.cc
#include <ltlvisit/clone.hh>
#include <ltlvisit/destroy.hh>
#include <ltlvisit/tostring.hh>
#include <cassert>
#include "bdddict.hh"
namespace spot
{
bdd_dict::bdd_dict()
: bdd_allocator(),
next_to_now(bdd_newpair()),
now_to_next(bdd_newpair())
{
}
bdd_dict::~bdd_dict()
{
assert_emptiness();
bdd_freepair(next_to_now);
bdd_freepair(now_to_next);
}
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 = clone(f);
num = allocate_variables(1);
var_map[f] = num;
var_formula_map[num] = f;
}
var_refs[num].insert(for_me);
return num;
}
int
bdd_dict::register_state(const ltl::formula* f, const void* for_me)
{
int num;
// Do not build a state that already exists.
fv_map::iterator sii = now_map.find(f);
if (sii != now_map.end())
{
num = sii->second;
}
else
{
f = ltl::clone(f);
num = allocate_variables(2);
now_map[f] = num;
now_formula_map[num] = f;
// Record that num+1 should be renamed as num when
// the next state becomes current.
bdd_setpair(next_to_now, num + 1, num);
bdd_setpair(now_to_next, num, num + 1);
}
var_refs[num].insert(for_me);
return num;
}
int
bdd_dict::register_accepting_variable(const ltl::formula* f,
const void* for_me)
{
int num;
// Do not build an accepting variable that already exists.
fv_map::iterator sii = acc_map.find(f);
if (sii != acc_map.end())
{
num = sii->second;
}
else
{
f = clone(f);
num = allocate_variables(1);
acc_map[f] = num;
acc_formula_map[num] = f;
}
var_refs[num].insert(for_me);
return num;
}
void
bdd_dict::register_all_variables_of(const void* from_other,
const void* for_me)
{
vr_map::iterator i;
for (i = var_refs.begin(); i != var_refs.end(); ++i)
{
ref_set& s = i->second;
if (s.find(from_other) != s.end())
s.insert(for_me);
}
}
void
bdd_dict::unregister_all_my_variables(const void* me)
{
vr_map::iterator i;
for (i = var_refs.begin(); i != var_refs.end();)
{
// Increment i++ now, we will possibly erase
// the current node (which would invalidate the iterator).
vr_map::iterator cur = i++;
ref_set& s = cur->second;
ref_set::iterator si = s.find(me);
if (si == s.end())
continue;
s.erase(si);
if (! s.empty())
continue;
// ME was the last user of this variable.
// Let's free it. First, we need to find
// if this is a Now, a Var, or an Acc variable.
int var = cur->first;
int n = 1;
const ltl::formula* f;
vf_map::iterator vi = var_formula_map.find(var);
if (vi != var_formula_map.end())
{
f = vi->second;
var_map.erase(f);
var_formula_map.erase(vi);
}
else
{
vi = now_formula_map.find(var);
if (vi != now_formula_map.end())
{
f = vi->second;
now_map.erase(f);
now_formula_map.erase(vi);