spot/src/tgba/tgbasafracomplement.cc
Alexandre Duret-Lutz 34e91b7656 c++11: replace Sgi::hash_* by Sgi::unordered_*.
* bench/scc-stats/stats.cc, bench/split-product/cutscc.cc,
iface/gspn/ssp.cc, src/bin/ltlcross.cc, src/bin/ltlfilt.cc,
src/bin/randltl.cc, src/dstarparse/nsa2tgba.cc, src/ltlast/formula.hh,
src/ltlast/nfa.hh, src/ltlvisit/contain.hh, src/ltlvisit/dotty.cc,
src/ltlvisit/mark.hh, src/ltlvisit/relabel.cc, src/ltlvisit/relabel.hh,
src/ltlvisit/simplify.cc, src/ltlvisit/snf.hh, src/misc/hash.hh,
src/misc/mspool.hh, src/priv/acccompl.hh, src/priv/accconv.hh,
src/saba/explicitstateconjunction.hh, src/saba/sabastate.hh,
src/sabaalgos/sabareachiter.hh, src/sanity/style.test,
src/ta/taexplicit.cc, src/ta/taexplicit.hh, src/taalgos/emptinessta.cc,
src/taalgos/minimize.cc, src/taalgos/reachiter.hh, src/tgba/state.hh,
src/tgba/taatgba.hh, src/tgba/tgbabddconcretefactory.hh,
src/tgba/tgbaexplicit.hh, src/tgba/tgbakvcomplement.cc,
src/tgba/tgbasafracomplement.cc, src/tgba/tgbatba.cc,
src/tgba/tgbatba.hh, src/tgbaalgos/cutscc.cc, src/tgbaalgos/cycles.hh,
src/tgbaalgos/degen.cc, src/tgbaalgos/dtbasat.cc,
src/tgbaalgos/dtgbasat.cc, src/tgbaalgos/eltl2tgba_lacim.cc,
src/tgbaalgos/emptiness.cc, src/tgbaalgos/gtec/explscc.hh,
src/tgbaalgos/gtec/nsheap.hh, src/tgbaalgos/gv04.cc,
src/tgbaalgos/ltl2tgba_fm.cc, src/tgbaalgos/magic.cc,
src/tgbaalgos/minimize.cc, src/tgbaalgos/ndfs_result.hxx,
src/tgbaalgos/powerset.hh, src/tgbaalgos/randomgraph.cc,
src/tgbaalgos/reachiter.hh, src/tgbaalgos/replayrun.cc,
src/tgbaalgos/safety.cc, src/tgbaalgos/scc.hh, src/tgbaalgos/se05.cc,
src/tgbaalgos/simulation.cc, src/tgbaalgos/tau03.cc,
src/tgbaalgos/tau03opt.cc: Adjust code.
* src/sanity/style.test: Remove check.
2014-02-12 14:05:04 +01:00

1372 lines
40 KiB
C++

// -*- coding: utf-8 -*-
// Copyright (C) 2009, 2010, 2011, 2012, 2013 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 <set>
#include <map>
#include <deque>
#include <cassert>
#include <sstream>
#include <boost/dynamic_bitset.hpp>
#include "bdd.h"
#include "misc/hash.hh"
#include "misc/bddlt.hh"
#include "tgba/bdddict.hh"
#include "tgba/state.hh"
#include "misc/hashfunc.hh"
#include "ltlast/formula.hh"
#include "ltlast/constant.hh"
#include "tgbaalgos/dotty.hh"
#include "tgba/tgbasafracomplement.hh"
#include "tgba/sba.hh"
#include "tgbaalgos/degen.hh"
namespace spot
{
typedef boost::dynamic_bitset<> bitset_t;
namespace
{
// forward decl.
struct safra_tree;
struct safra_tree_ptr_less_than:
public std::binary_function<const safra_tree*, const safra_tree*, bool>
{
bool
operator()(const safra_tree* left,
const safra_tree* right) const;
};
/// \brief Automaton with Safra's tree as states.
struct safra_tree_automaton
{
safra_tree_automaton(const tgba* sba);
~safra_tree_automaton();
typedef std::map<bdd, const safra_tree*, bdd_less_than> transition_list;
typedef
std::map<safra_tree*, transition_list, safra_tree_ptr_less_than>
automaton_t;
automaton_t automaton;
/// \brief The number of acceptance pairs of this Rabin (Streett)
/// automaton.
int get_nb_acceptance_pairs() const;
safra_tree* get_initial_state() const;
void set_initial_state(safra_tree* s);
const tgba* get_sba(void) const
{
return a_;
}
private:
mutable int max_nb_pairs_;
safra_tree* initial_state;
const tgba* a_;
};
/// \brief A Safra tree, used as state during the determinization
/// of a Büchi automaton
///
/// It is the key structure of the construction.
/// Each node of the tree has:
/// - A \a name,
/// - A subset of states of the original Büchi automaton (\a nodes),
/// - A flag that is \a marked to denote vertical merge of nodes,
/// - A list of \a children.
///
/// This class implements operations:
/// - To compute the successor of this tree,
/// - To retrive acceptance condition on this tree.
/// \see safra_determinisation.
struct safra_tree
{
typedef std::set<const state*, state_ptr_less_than> subset_t;
typedef std::list<safra_tree*> child_list;
typedef std::multimap<bdd, const state*, bdd_less_than> tr_cache_t;
typedef std::map<const state*, tr_cache_t,
state_ptr_less_than> cache_t;
safra_tree();
safra_tree(const safra_tree& other);
safra_tree(const subset_t& nodes, safra_tree* p, int n);
~safra_tree();
safra_tree& operator=(const safra_tree& other);
int compare(const safra_tree* other) const;
size_t hash() const;
void add_node(const state* s);
int max_name() const;
// Operations to get successors of a tree.
safra_tree* branch_accepting(const sba& a);
safra_tree* succ_create(const bdd& condition,
cache_t& cache_transition);
safra_tree* normalize_siblings();
safra_tree* remove_empty();
safra_tree* mark();
// To get Rabin/Streett acceptance conditions.
void getL(bitset_t& bitset) const;
void getU(bitset_t& bitset) const;
/// \brief Is this node the root of the tree?
bool is_root() const
{
return parent == 0;
}
bool marked;
int name;
subset_t nodes;
child_list children;
private:
void remove_node_from_children(const state* state);
int get_new_name() const;
mutable std::deque<int> free_names_; // Some free names.
safra_tree* parent;
};
safra_tree::safra_tree()
: marked(false), name(0)
{
parent = 0;
}
/// \brief Copy the tree \a other, and set \c marked to false.
safra_tree::safra_tree(const safra_tree& other)
: marked(false)
{
name = other.name;
parent = 0;
nodes = other.nodes;
for (child_list::const_iterator i = other.children.begin();
i != other.children.end(); ++i)
{
safra_tree* c = new safra_tree(**i);
c->parent = this;
children.push_back(c);
}
}
safra_tree::safra_tree(const subset_t& nodes, safra_tree* p, int n)
: marked(false), name(n), nodes(nodes)
{
parent = p;
}
safra_tree::~safra_tree()
{
for (child_list::iterator i = children.begin(); i != children.end(); ++i)
delete *i;
for (subset_t::iterator i = nodes.begin(); i != nodes.end(); ++i)
(*i)->destroy();
}
safra_tree&
safra_tree::operator=(const safra_tree& other)
{
if (this != &other)
{
this->~safra_tree();
new (this) safra_tree(other);
}
return *this;
}
/// \brief Compare two safra trees.
///
/// \param other the tree to compare too.
///
/// \return 0 if the trees are the same. Otherwise
/// a signed value.
int safra_tree::compare(const safra_tree* other) const
{
int res = name - other->name;
if (res != 0)
return res;
if (marked != other->marked)
return (marked) ? -1 : 1;
res = nodes.size() - other->nodes.size();
if (res != 0)
return res;
res = children.size() - other->children.size();
if (res != 0)
return res;
// Call compare() only as a last resort, because it takes time.
subset_t::const_iterator in1 = nodes.begin();
subset_t::const_iterator in2 = other->nodes.begin();
for (; in1 != nodes.end(); ++in1, ++in2)
if ((res = (*in1)->compare(*in2)) != 0)
return res;
child_list::const_iterator ic1 = children.begin();
child_list::const_iterator ic2 = other->children.begin();
for (; ic1 != children.end(); ++ic1, ++ic2)
if ((res = (*ic1)->compare(*ic2)) != 0)
return res;
return 0;
}
/// \brief Hash a safra tree.
size_t
safra_tree::hash() const
{
size_t hash = 0;
hash ^= wang32_hash(name);
hash ^= wang32_hash(marked);
for (subset_t::const_iterator i = nodes.begin(); i != nodes.end(); ++i)
hash ^= (*i)->hash();
for (child_list::const_iterator i = children.begin();
i != children.end();
++i)
hash ^= (*i)->hash();
return hash;
}
void
safra_tree::add_node(const state* s)
{
nodes.insert(s);
}
/*---------------------------------.
| Operations to compute successors |
`---------------------------------*/
int
safra_tree::max_name() const
{
int max_name = name;
for (child_list::const_iterator i = children.begin();
i != children.end(); ++i)
max_name = std::max(max_name, (*i)->max_name());
return max_name;
}
/// \brief Get an unused name in the tree for a new node.
///
/// The root of the tree maintains a list of unused names.
/// When this list is empty, new names are computed.
int
safra_tree::get_new_name() const
{
if (parent == 0)
{
if (free_names_.empty())
{
std::set<int> used_names;
std::deque<const safra_tree*> queue;
queue.push_back(this);
while (!queue.empty())
{
const safra_tree* current = queue.front();
queue.pop_front();
used_names.insert(current->name);
for (child_list::const_iterator i = current->children.begin();
i != current->children.end(); ++i)
queue.push_back(*i);
}
int l = 0;
int nb_found = 0;
std::set<int>::const_iterator i = used_names.begin();
while (i != used_names.end() && nb_found != 3)
{
if (l != *i)
{
free_names_.push_back(l);
++nb_found;
}
else
++i;
++l;
}
while (nb_found++ < 3)
free_names_.push_back(l++);
}
int result = free_names_.front();
free_names_.pop_front();
return result;
}
else
return parent->get_new_name();
}
/// If the node has an accepting state in its label, a new child
/// is inserted with the set of all accepting states of \c nodes
/// as label and an unused name.
safra_tree*
safra_tree::branch_accepting(const sba& a)
{
for (child_list::iterator i = children.begin(); i != children.end(); ++i)
(*i)->branch_accepting(a);
subset_t subset;
for (subset_t::const_iterator i = nodes.begin(); i != nodes.end(); ++i)
if (a.state_is_accepting(*i))
subset.insert(*i);
if (!subset.empty())
children.push_back(new safra_tree(subset, this, get_new_name()));
return this;
}
/// \brief A powerset construction.
///
/// The successors of each state in \c nodes with \a condition
/// as atomic property remplace the current \c nodes set.
///
/// @param cache_transition is a map of the form: state -> bdd -> state.
/// Only states present in \c nodes are keys of the map.
/// @param condition is an atomic property. We are looking for successors
/// of this atomic property.
safra_tree*
safra_tree::succ_create(const bdd& condition,
cache_t& cache_transition)
{
subset_t new_subset;
for (subset_t::iterator i = nodes.begin(); i != nodes.end(); ++i)
{
cache_t::const_iterator it = cache_transition.find(*i);
if (it == cache_transition.end())
continue;
const tr_cache_t& transitions = it->second;
for (tr_cache_t::const_iterator t_it = transitions.begin();
t_it != transitions.end();
++t_it)
{
if ((t_it->first & condition) != bddfalse)
{
if (new_subset.find(t_it->second) == new_subset.end())
{
const state* s = t_it->second->clone();
new_subset.insert(s);
}
}
}
}
nodes = new_subset;
for (child_list::iterator i = children.begin(); i != children.end(); ++i)
(*i)->succ_create(condition, cache_transition);
return this;
}
/// \brief Horizontal Merge
///
/// If many children share the same state in their labels, we must keep
/// only one occurrence (in the older node).
safra_tree*
safra_tree::normalize_siblings()
{
std::set<const state*, state_ptr_less_than> node_set;
for (child_list::iterator child_it = children.begin();
child_it != children.end();
++child_it)
{
subset_t::iterator node_it = (*child_it)->nodes.begin();
while (node_it != (*child_it)->nodes.end())
{
if (!node_set.insert(*node_it).second)
{
const state* s = *node_it;
(*child_it)->remove_node_from_children(*node_it);
(*child_it)->nodes.erase(node_it++);
s->destroy();
}
else
{
++node_it;
}
}
(*child_it)->normalize_siblings();
}
return this;
}
/// \brief Remove recursively all the occurrences of \c state in the label.
void
safra_tree::remove_node_from_children(const state* state)
{
for (child_list::iterator child_it = children.begin();
child_it != children.end();
++child_it)
{
subset_t::iterator it = (*child_it)->nodes.find(state);
if (it != (*child_it)->nodes.end())
{
const spot::state* s = *it;
(*child_it)->nodes.erase(it);
s->destroy();
}
(*child_it)->remove_node_from_children(state);
}
}
/// \brief Remove empty nodes
///
/// If a child of the node has an empty label, we remove this child.
safra_tree*
safra_tree::remove_empty()
{
child_list::iterator child_it = children.begin();
while (child_it != children.end())
{
if ((*child_it)->nodes.empty())
{
safra_tree* to_delete = *child_it;
child_it = children.erase(child_it);
delete to_delete;
}
else
{
(*child_it)->remove_empty();
++child_it;
}
}
return this;
}
/// \brief Vertical merge
///
/// If a parent has the same states as its childen in its label,
/// All the children a deleted and the node is marked. This mean
/// an accepting infinite run is found.
safra_tree*
safra_tree::mark()
{
std::set<const state*, state_ptr_less_than> node_set;
for (child_list::const_iterator child_it = children.begin();
child_it != children.end();
++child_it)
{
node_set.insert((*child_it)->nodes.begin(), (*child_it)->nodes.end());
(*child_it)->mark();
}
char same = node_set.size() == nodes.size();
if (same)
{
subset_t::const_iterator i = node_set.begin();
subset_t::const_iterator j = nodes.begin();
while (i != node_set.end() && j != nodes.end())
{
if ((*i)->compare(*j) != 0)
{
same = 0;
break;
}
++i;
++j;
}
}
if (same)
{
marked = true;
for (child_list::iterator i = children.begin();
i != children.end();
++i)
delete *i;
children = child_list();
}
return this;
}
/*-----------------------.
| Acceptances conditions |
`-----------------------*/
/// Returns in which sets L (the semantic differs according to Rabin or
/// Streett) the state-tree is included.
///
/// \param bitset a bitset of size \c this->get_nb_acceptance_pairs()
/// filled with FALSE bits.
/// On return bitset[i] will be set if this state-tree is included in L_i.
void
safra_tree::getL(bitset_t& bitset) const
{
assert(bitset.size() > static_cast<unsigned>(name));
if (marked && !nodes.empty())
bitset[name] = true;
for (child_list::const_iterator i = children.begin();
i != children.end();
++i)
(*i)->getL(bitset);
}
/// Returns in which sets U (the semantic differs according to Rabin or
/// Streett) the state-tree is included.
///
/// \param bitset a bitset of size \c this->get_nb_acceptance_pairs()
/// filled with TRUE bits.
/// On return bitset[i] will be set if this state-tree is included in U_i.
void
safra_tree::getU(bitset_t& bitset) const
{
assert(bitset.size() > static_cast<unsigned>(name));
if (!nodes.empty())
bitset[name] = false;
for (child_list::const_iterator i = children.begin();
i != children.end();
++i)
(*i)->getU(bitset);
}
bool
safra_tree_ptr_less_than::operator()(const safra_tree* left,
const safra_tree* right) const
{
assert(left);
return left->compare(right) < 0;
}
struct safra_tree_ptr_equal:
public std::unary_function<const safra_tree*, bool>
{
safra_tree_ptr_equal(const safra_tree* left)
: left_(left) {}
bool
operator()(const safra_tree* right) const
{
return left_->compare(right) == 0;
}
private:
const safra_tree* left_;
};
/// \brief Algorithm to determinize Büchi automaton.
///
/// Determinization of a Büchi automaton into a Rabin automaton
/// using the Safra's construction.
///
/// This construction is presented in:
/// @PhDThesis{ safra.89.phd,
/// author = {Shmuel Safra},
/// title = {Complexity of Automata on Infinite Objects},
/// school = {The Weizmann Institute of Science},
/// year = {1989},
/// address = {Rehovot, Israel},
/// month = mar
/// }
///
class safra_determinisation
{
public:
static safra_tree_automaton* create_safra_automaton(const tgba* a);
private:
typedef std::set<int> atomic_list_t;
typedef std::set<bdd, bdd_less_than> conjunction_list_t;
static void retrieve_atomics(const safra_tree* node, sba* sba_aut,
safra_tree::cache_t& cache,
atomic_list_t& atomic_list);
static void set_atomic_list(atomic_list_t& list, bdd condition);
static conjunction_list_t
get_conj_list(const atomic_list_t& atomics);
};
/// \brief The body of Safra's construction.
safra_tree_automaton*
safra_determinisation::create_safra_automaton(const tgba* a)
{
// initialization.
sba* sba_aut = degeneralize(a);
safra_tree_automaton* st = new safra_tree_automaton(sba_aut);
std::deque<safra_tree*> queue;
safra_tree* q0 = new safra_tree();
q0->add_node(sba_aut->get_init_state());
queue.push_back(q0);
st->set_initial_state(q0);
// main loop
while (!queue.empty())
{
safra_tree* current = queue.front();
// safra_tree* node = new safra_tree(*current);
// Get conjunction list and save successors.
safra_tree::cache_t cache;
atomic_list_t atomic_list;
retrieve_atomics(current, sba_aut, cache, atomic_list);
conjunction_list_t conjunction = get_conj_list(atomic_list);
// Create successors of the Safra's tree.
safra_tree_automaton::transition_list transitions;
for (conjunction_list_t::const_iterator i = conjunction.begin();
i != conjunction.end(); ++i)
{
safra_tree* successor = new safra_tree(*current);
successor->branch_accepting(*sba_aut); // Step 2
successor->succ_create(*i, cache); // Step 3
successor->normalize_siblings(); // Step 4
successor->remove_empty(); // Step 5
successor->mark(); // Step 6
bool delete_this_successor = true;
safra_tree_ptr_equal comparator(successor);
if (st->automaton.find(successor) != st->automaton.end())
{
transitions[*i] = st->automaton.find(successor)->first;
}
else
{
std::deque<safra_tree*>::iterator item_in_queue =
std::find_if(queue.begin(), queue.end(), comparator);
if (item_in_queue != queue.end())
{
transitions[*i] = *item_in_queue;
}
else
{
delete_this_successor = false;
transitions[*i] = successor;
queue.push_back(successor);
}
}
if (delete_this_successor)
delete successor;
}
if (st->automaton.find(current) == st->automaton.end())
st->automaton[current] = transitions;
queue.pop_front();
for (safra_tree::cache_t::iterator i = cache.begin();
i != cache.end();
++i)
for (safra_tree::tr_cache_t::iterator j = i->second.begin();
j != i->second.end();
++j)
j->second->destroy();
// delete node;
}
// delete sba_aut;
return st;
}
/// Retrieve all atomics properties that are in successors formulae
/// of the states in the label of the node.
void
safra_determinisation::retrieve_atomics(const safra_tree* node,
sba* sba_aut,
safra_tree::cache_t& cache,
atomic_list_t& atomic_list)
{
for (safra_tree::subset_t::iterator it = node->nodes.begin();
it != node->nodes.end();
++it)
{
safra_tree::tr_cache_t transitions;
tgba_succ_iterator* iterator = sba_aut->succ_iter(*it);
for (iterator->first(); !iterator->done(); iterator->next())
{
bdd condition = iterator->current_condition();
typedef std::pair<bdd, const state*> bdd_state;
transitions.insert(bdd_state(condition, iterator->current_state()));
set_atomic_list(atomic_list, condition);
}
delete iterator;
cache[*it] = transitions;
}
}
/// Insert in \a list atomic properties of the formula \a c.
void
safra_determinisation::set_atomic_list(atomic_list_t& list, bdd c)
{
bdd current = bdd_satone(c);
while (current != bddtrue && current != bddfalse)
{
list.insert(bdd_var(current));
bdd high = bdd_high(current);
if (high == bddfalse)
current = bdd_low(current);
else
current = high;
}
}
/// From the list of atomic properties \a atomics, create the list
/// of all the conjunctions of properties.
safra_determinisation::conjunction_list_t
safra_determinisation::get_conj_list(const atomic_list_t& atomics)
{
conjunction_list_t list;
unsigned atomics_size = atomics.size();
assert(atomics_size < 32);
for (unsigned i = 1; i <= static_cast<unsigned>(1 << atomics_size); ++i)
{
bdd result = bddtrue;
unsigned position = 1;
for (atomic_list_t::const_iterator a_it = atomics.begin();
a_it != atomics.end();
++a_it, position <<= 1)
{
bdd this_atomic;
if (position & i)
this_atomic = bdd_ithvar(*a_it);
else
this_atomic = bdd_nithvar(*a_it);
result = bdd_apply(result, this_atomic, bddop_and);
}
list.insert(result);
}
return list;
}
// Safra's test part. Dot output.
//////////////////////////////
namespace test
{
typedef std::unordered_map<const state*, int,
state_ptr_hash, state_ptr_equal> stnum_t;
void print_safra_tree(const safra_tree* this_node,
stnum_t& node_names,
int& current_node,
int nb_accepting_conditions)
{
std::string conditions;
if (this_node->is_root())
{
bitset_t l(nb_accepting_conditions);
bitset_t u(nb_accepting_conditions);
u.flip();
this_node->getL(l);
this_node->getU(u);
std::stringstream s;
s << "\\nL:" << l << ", U:" << u;
conditions = s.str();
}
std::cout << "node" << this_node << "[label=\"";
std::cout << this_node->name << "|";
for (safra_tree::subset_t::const_iterator j = this_node->nodes.begin();
j != this_node->nodes.end();
++j)
{
stnum_t::const_iterator it = node_names.find(*j);
int name;
if (it == node_names.end())
name = node_names[*j] = current_node++;
else
name = it->second;
std::cout << name << ", ";
}
std::cout << conditions;
if (this_node->marked)
std::cout << "\", style=filled, fillcolor=\"gray";
std::cout << "\"];" << std::endl;
safra_tree::child_list::const_iterator i = this_node->children.begin();
for (; i != this_node->children.end(); ++i)
{
print_safra_tree(*i, node_names, current_node,
nb_accepting_conditions);
std::cout << "node" << this_node << " -> node" << *i
<< "[color=\"red\", arrowhead=\"none\"];"
<< std::endl;
}
}
void print_safra_automaton(safra_tree_automaton* a)
{
typedef safra_tree_automaton::automaton_t::reverse_iterator
automaton_cit;
typedef safra_tree_automaton::transition_list::const_iterator
trans_cit;
stnum_t node_names;
int current_node = 0;
int nb_accepting_conditions = a->get_nb_acceptance_pairs();
std::cout << "digraph A {" << std::endl;
/// GCC 3.3 complains if a const_reverse_iterator is used.
/// error: no match for 'operator!='
for (automaton_cit i = a->automaton.rbegin();
i != a->automaton.rend();
++i)
{
std::cout << "subgraph sg" << i->first << "{" << std::endl;
print_safra_tree(i->first, node_names, current_node,
nb_accepting_conditions);
std::cout << "}" << std::endl;
// Successors.
for (trans_cit j = i->second.begin(); j != i->second.end(); ++j)
std::cout << "node" << i->first << "->"
<< "node" << j->second <<
" [label=\"" << bddset << j->first << "\"];" << std::endl;
}
// Output the real name of all states.
std::cout << "{ rank=sink; legend [shape=none,margin=0,label=<\n"
<< "<TABLE BORDER='1' CELLBORDER='0' CELLSPACING='0'>\n";
for (stnum_t::const_iterator it = node_names.begin();
it != node_names.end(); ++it)
std::cout << "<TR><TD>" << it->second << "</TD><TD>"
<< a->get_sba()->format_state(it->first)
<< "</TD></TR>\n";
std::cout << "</TABLE>\n"
<< ">]}" << std::endl;
std::cout << "}" << std::endl;
}
} // test
////////////////////////////////////////
// state_complement
/// States used by spot::tgba_safra_complement.
/// \ingroup tgba_representation
class state_complement : public state
{
public:
state_complement(bitset_t U, bitset_t L, const safra_tree* tree,
bool use_bitset = true);
state_complement(const state_complement& other);
/// \return the safra tree associated to this state.
const safra_tree* get_safra() const
{
return tree;
}
/// \return in which sets U this state is included.
bitset_t get_U() const
{
return U;
}
/// \return in which sets L this state is included.
bitset_t get_L() const
{
return L;
}
/// \return whether this state track an infinite run.
bool get_use_bitset() const
{
return use_bitset;
}
virtual int compare(const state* other) const;
virtual size_t hash() const;
virtual state_complement* clone() const;
virtual ~state_complement()
{
}
std::string to_string() const;
const state* get_state() const;
private:
bitset_t U;
bitset_t L;
const safra_tree* tree;
bool use_bitset;
};
state_complement::state_complement(bitset_t L, bitset_t U,
const safra_tree* tree,
bool use_bitset)
: state(), U(U), L(L), tree(tree), use_bitset(use_bitset)
{
}
state_complement::state_complement(const state_complement& other)
: state()
{
U = other.U;
L = other.L;
tree = other.tree;
use_bitset = other.use_bitset;
}
int
state_complement::compare(const state* other) const
{
if (other == this)
return 0;
const state_complement* s = down_cast<const state_complement*>(other);
assert(s);
#if TRANSFORM_TO_TBA
// When we transform to TBA instead of TGBA, states depend on the U set.
if (U != s->U)
return (U < s->U) ? -1 : 1;
#endif
if (L != s->L)
return (L < s->L) ? -1 : 1;
if (use_bitset != s->use_bitset)
return use_bitset - s->use_bitset;
return tree->compare(s->tree);
}
size_t
state_complement::hash() const
{
size_t hash = tree->hash();
hash ^= wang32_hash(use_bitset);
size_t size_bitset = L.size();
for (unsigned i = 0; i < size_bitset; ++i)
{
hash ^= wang32_hash(L[i]);
#if TRANSFORM_TO_TBA
hash ^= wang32_hash(U[i]);
#endif
}
return hash;
}
state_complement*
state_complement::clone() const
{
return new state_complement(*this);
}
const state*
state_complement::get_state() const
{
return this;
}
std::string
state_complement::to_string() const
{
std::stringstream ss;
ss << tree;
if (use_bitset)
{
ss << " - I:" << L;
#if TRANSFORM_TO_TBA
ss << " J:" << U;
#endif
}
return ss.str();
}
/// Successor iterators used by spot::tgba_safra_complement.
/// \ingroup tgba_representation
class tgba_safra_complement_succ_iterator: public tgba_succ_iterator
{
public:
typedef std::multimap<bdd, state_complement*, bdd_less_than> succ_list_t;
tgba_safra_complement_succ_iterator(const succ_list_t& list,
bdd the_acceptance_cond)
: list_(list), the_acceptance_cond_(the_acceptance_cond)
{
}
virtual
~tgba_safra_complement_succ_iterator()
{
for (succ_list_t::iterator i = list_.begin(); i != list_.end(); ++i)
delete i->second;
}
virtual void first();
virtual void next();
virtual bool done() const;
virtual state_complement* current_state() const;
virtual bdd current_condition() const;
virtual bdd current_acceptance_conditions() const;
private:
succ_list_t list_;
bdd the_acceptance_cond_;
succ_list_t::const_iterator it_;
};
void
tgba_safra_complement_succ_iterator::first()
{
it_ = list_.begin();
}
void
tgba_safra_complement_succ_iterator::next()
{
++it_;
}
bool
tgba_safra_complement_succ_iterator::done() const
{
return it_ == list_.end();
}
state_complement*
tgba_safra_complement_succ_iterator::current_state() const
{
assert(!done());
return new state_complement(*(it_->second));
}
bdd
tgba_safra_complement_succ_iterator::current_condition() const
{
assert(!done());
return it_->first;
}
bdd
tgba_safra_complement_succ_iterator::current_acceptance_conditions() const
{
assert(!done());
return the_acceptance_cond_;
}
} // anonymous
// safra_tree_automaton
////////////////////////
safra_tree_automaton::safra_tree_automaton(const tgba* a)
: max_nb_pairs_(-1), initial_state(0), a_(a)
{
a->get_dict()->register_all_variables_of(a, this);
}
safra_tree_automaton::~safra_tree_automaton()
{
for (automaton_t::iterator i = automaton.begin();
i != automaton.end();
++i)
{
delete i->first;
}
delete a_;
}
int
safra_tree_automaton::get_nb_acceptance_pairs() const
{
if (max_nb_pairs_ != -1)
return max_nb_pairs_;
int max = -1;
for (automaton_t::const_iterator i = automaton.begin();
i != automaton.end();
++i)
max = std::max(max, i->first->max_name());
return max_nb_pairs_ = max + 1;
}
safra_tree*
safra_tree_automaton::get_initial_state() const
{
return initial_state;
}
void
safra_tree_automaton::set_initial_state(safra_tree* s)
{
initial_state = s;
}
// End of the safra construction
//////////////////////////////////////////
// tgba_safra_complement
//////////////////////////
tgba_safra_complement::tgba_safra_complement(const tgba* a)
: automaton_(a), safra_(safra_determinisation::create_safra_automaton(a))
{
assert(safra_ || !"safra construction fails");
// We will use one acceptance condition for this automata.
// Let's call it Acc[True].
int v = get_dict()
->register_acceptance_variable(ltl::constant::true_instance(), safra_);
#if TRANSFORM_TO_TBA
the_acceptance_cond_ = bdd_ithvar(v);
#endif
#if TRANSFORM_TO_TGBA
unsigned nb_acc =
static_cast<safra_tree_automaton*>(safra_)->get_nb_acceptance_pairs();
all_acceptance_cond_ = bddfalse;
neg_acceptance_cond_ = bddtrue;
acceptance_cond_vec_.reserve(nb_acc);
for (unsigned i = 0; i < nb_acc; ++i)
{
int r = get_dict()->register_clone_acc(v, safra_);
all_acceptance_cond_ &= bdd_nithvar(r);
all_acceptance_cond_ |= bdd_ithvar(r) & neg_acceptance_cond_;
neg_acceptance_cond_ &= bdd_nithvar(r);
acceptance_cond_vec_.push_back(bdd_ithvar(r));
}
for (unsigned i = 0; i < nb_acc; ++i)
{
bdd c = acceptance_cond_vec_[i];
acceptance_cond_vec_[i] = bdd_exist(neg_acceptance_cond_, c) & c;
}
#endif
}
tgba_safra_complement::~tgba_safra_complement()
{
get_dict()->unregister_all_my_variables(safra_);
delete static_cast<safra_tree_automaton*>(safra_);
}
state*
tgba_safra_complement::get_init_state() const
{
safra_tree_automaton* a = static_cast<safra_tree_automaton*>(safra_);
bitset_t empty(a->get_nb_acceptance_pairs());
return new state_complement(empty, empty, a->get_initial_state(), false);
}
/// @brief Compute successors of the state @a local_state, and returns an
/// iterator on the successors collection.
///
/// The old algorithm is a Deterministic Streett to nondeterministic Büchi
/// transformation, presented by Christof Löding in his Diploma thesis.
///
/// @MastersThesis{ loding.98,
/// author = {Christof L\"oding},
/// title = {Methods for the Transformation of omega-Automata:
/// Complexity and Connection to Second Order Logic},
/// school = {University of Kiel},
/// year = {1998}
/// }
///
///
/// The new algorithm produce a TGBA instead of a TBA. This algorithm
/// comes from:
///
/// @InProceedings{ duret.09.atva,
/// author = {Alexandre Duret-Lutz and Denis Poitrenaud and
/// Jean-Michel Couvreur},
/// title = {On-the-fly Emptiness Check of Transition-based
/// {S}treett Automata},
/// booktitle = {Proceedings of the 7th International Symposium on
/// Automated Technology for Verification and Analysis
/// (ATVA'09)},
/// year = {2009},
/// editor = {Zhiming Liu and Anders P. Ravn},
/// series = {Lecture Notes in Computer Science},
/// publisher = {Springer-Verlag}
/// }
///
/// @param local_state the state from which we want to compute the successors.
///
tgba_succ_iterator*
tgba_safra_complement::succ_iter(const state* local_state,
const state* /* = 0 */,
const tgba* /* = 0 */) const
{
const safra_tree_automaton* a = static_cast<safra_tree_automaton*>(safra_);
const state_complement* s =
down_cast<const state_complement*>(local_state);
assert(s);
safra_tree_automaton::automaton_t::const_iterator tr =
a->automaton.find(const_cast<safra_tree*>(s->get_safra()));
typedef safra_tree_automaton::transition_list::const_iterator trans_iter;
if (tr != a->automaton.end())
{
bdd condition = bddfalse;
tgba_safra_complement_succ_iterator::succ_list_t succ_list;
int nb_acceptance_pairs = a->get_nb_acceptance_pairs();
bitset_t e(nb_acceptance_pairs);
if (!s->get_use_bitset()) // if \delta'(q, a)
{
for (trans_iter i = tr->second.begin(); i != tr->second.end(); ++i)
{
state_complement* s1 = new state_complement(e, e, i->second, false);
state_complement* s2 = new state_complement(e, e, i->second, true);
succ_list.insert(std::make_pair(i->first, s1));
succ_list.insert(std::make_pair(i->first, s2));
}
}
else
{
bitset_t l(nb_acceptance_pairs);
bitset_t u(nb_acceptance_pairs);
u.flip();
s->get_safra()->getL(l); // {i : q \in L_i}
s->get_safra()->getU(u); // {j : q \in U_i}
state_complement* st;
#if TRANSFORM_TO_TBA
bitset_t newI = s->get_L() | l; // {I' = I \cup {i : q \in L_i}}
bitset_t newJ = s->get_U() | u; // {J' = J \cup {j : q \in U_i}}
if (newI.is_subset_of(newJ)) // \delta'((q, I, J), a) if I'\subseteq J'
{
for (trans_iter i = tr->second.begin(); i != tr->second.end(); ++i)
{
st = new state_complement(e, e, i->second, true);
succ_list.insert(std::make_pair(i->first, st));
}
condition = the_acceptance_cond_;
}
else // \delta'((q, I, J), a)
{
for (trans_iter i = tr->second.begin(); i != tr->second.end(); ++i)
{
st = new state_complement(newI, newJ, i->second, true);
succ_list.insert(std::make_pair(i->first, st));
}
}
#else
bitset_t S = s->get_L();
bitset_t pending = (S | l) - u; // {pending = S \cup {i : q \in L_i}
// \setminus {j : q \in U_j})}
for (trans_iter i = tr->second.begin(); i != tr->second.end(); ++i)
{
st = new state_complement(pending, e, i->second, true);
succ_list.insert(std::make_pair(i->first, st));
}
for (unsigned i = 0; i < l.size(); ++i)
if (!S[i])
condition |= acceptance_cond_vec_[i];
#endif
}
return new tgba_safra_complement_succ_iterator(succ_list, condition);
}
assert(!"Safra automaton does not find this node");
return 0;
}
bdd_dict*
tgba_safra_complement::get_dict() const
{
return automaton_->get_dict();
}
std::string
tgba_safra_complement::format_state(const state* state) const
{
const state_complement* s =
down_cast<const state_complement*>(state);
assert(s);
return s->to_string();
}
bdd
tgba_safra_complement::all_acceptance_conditions() const
{
#if TRANSFORM_TO_TBA
return the_acceptance_cond_;
#else
return all_acceptance_cond_;
#endif
}
bdd
tgba_safra_complement::neg_acceptance_conditions() const
{
#if TRANSFORM_TO_TBA
return !the_acceptance_cond_;
#else
return neg_acceptance_cond_;
#endif
}
bdd
tgba_safra_complement::compute_support_conditions(const state* state) const
{
const safra_tree_automaton* a = static_cast<safra_tree_automaton*>(safra_);
const state_complement* s = down_cast<const state_complement*>(state);
assert(s);
typedef safra_tree_automaton::automaton_t::const_iterator auto_it;
typedef safra_tree_automaton::transition_list::const_iterator trans_it;
auto_it node(a->automaton.find(const_cast<safra_tree*>(s->get_safra())));
if (node == a->automaton.end())
return bddtrue;
bdd res = bddtrue;
trans_it i;
for (i = node->second.begin(); i != node->second.end(); ++i)
res |= i->first;
return res;
}
bdd
tgba_safra_complement::compute_support_variables(const state* state) const
{
const safra_tree_automaton* a = static_cast<safra_tree_automaton*>(safra_);
const state_complement* s = down_cast<const state_complement*>(state);
assert(s);
typedef safra_tree_automaton::automaton_t::const_iterator auto_it;
typedef safra_tree_automaton::transition_list::const_iterator trans_it;
auto_it node(a->automaton.find(const_cast<safra_tree*>(s->get_safra())));
if (node == a->automaton.end())
return bddtrue;
bdd res = bddtrue;
trans_it i;
for (i = node->second.begin(); i != node->second.end(); ++i)
res &= bdd_support(i->first);
return res;
}
// display_safra: debug routine.
//////////////////////////////
void display_safra(const tgba_safra_complement* a)
{
test::print_safra_automaton(static_cast<safra_tree_automaton*>
(a->get_safra()));
}
}