* src/tgbatest/ltl2tgba.cc: Add some option for the reduction of

automata.
* src/tgbatest/spotlbtt.test, src/tgbatest/Makefile.am: Add some
test for reduction of automata.
* src/tgbaalgos/reductgba_sim_del.cc, src/tgbaalgos/reductgba_sim.cc,
src/tgbaalgos/reductgba_sim.hh: Compute some simulation relation
to reduce a tgba.
* src/tgba/tgbareduc.cc, src/tgba/tgbareduc.hh: A implementation
of tgba for the reduction.
* src/tgbaalgos/Makefile.am, src/tgba/Makefile.am:
Add the reduction of automata.
* src/ltlvisit/syntimpl.cc, src/ltlvisit/basereduc.cc:
Lot of mistake are corrected.
* src/ltlvisit/syntimpl.hh, src/ltlvisit/reducform.cc,
src/ltlvisit/reducform.hh, src/ltltest/reduc.cc: Adjust.
* src/ltltest/equals.cc, src/ltltest/reduccmp.test,
src/ltltest/Makefile.am: Add a test for reduction.
This commit is contained in:
martinez 2004-06-15 16:24:02 +00:00
parent 383f7e170a
commit 8d3606ff07
20 changed files with 3155 additions and 133 deletions

View file

@ -37,7 +37,8 @@ tgbaalgos_HEADERS = \
powerset.hh \
reachiter.hh \
save.hh \
stats.hh
stats.hh \
reductgba_sim.hh
noinst_LTLIBRARIES = libtgbaalgos.la
libtgbaalgos_la_SOURCES = \
@ -51,6 +52,8 @@ libtgbaalgos_la_SOURCES = \
powerset.cc \
reachiter.cc \
save.cc \
stats.cc
stats.cc \
reductgba_sim.cc \
reductgba_sim_del.cc
libtgbaalgos_la_LIBADD = gtec/libgtec.la

View file

@ -0,0 +1,655 @@
// Copyright (C) 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 2 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 Spot; see the file COPYING. If not, write to the Free
// Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
// 02111-1307, USA.
#include "reductgba_sim.hh"
#include "tgba/bddprint.hh"
namespace spot
{
///////////////////////////////////////////////////////////////////////
// spoiler_node
spoiler_node::spoiler_node(const state* d_node,
const state* s_node,
int num)
{
num_ = num;
sc_ = new state_couple(d_node, s_node);
//lnode_succ = new Sgi::vector<spoiler_node*>;
lnode_succ = new sn_v;
lnode_pred = new sn_v;
this->not_win = false;
}
spoiler_node::~spoiler_node()
{
lnode_succ->clear();
lnode_pred->clear();
delete lnode_succ;
delete lnode_pred;
delete sc_;
}
void
spoiler_node::add_succ(spoiler_node* n)
{
lnode_succ->push_back(n);
}
void
spoiler_node::del_succ(spoiler_node* n)
{
//std::cout << "del_succ : begin" << std::endl;
for (sn_v::iterator i = lnode_succ->begin();
i != lnode_succ->end();)
{
if (*i == n)
{
//std::cout << "erase" << std::endl;
i = lnode_succ->erase(i);
}
else
++i;
}
//std::cout << "del_succ : end" << std::endl;
}
void
spoiler_node::add_pred(spoiler_node* n)
{
lnode_pred->push_back(n);
}
void
spoiler_node::del_pred()
{
for (sn_v::iterator i = lnode_pred->begin();
i != lnode_pred->end(); ++i)
(*i)->del_succ(this);
}
bool
spoiler_node::set_win()
{
bool change = not_win;
for (Sgi::vector<spoiler_node*>::iterator i = lnode_succ->begin();
i != lnode_succ->end(); ++i)
{
not_win |= (*i)->not_win;
}
return (change != not_win);
}
std::string
spoiler_node::to_string(const tgba* a)
{
std::ostringstream os;
// print the node.
os << num_
<< " [shape=box, label=\"("
<< a->format_state(sc_->first)
<< ", "
<< a->format_state(sc_->second)
<< ")\"]"
<< std::endl;
return os.str();
}
std::string
spoiler_node::succ_to_string()
{
std::ostringstream os;
sn_v::iterator i;
for (i = lnode_succ->begin(); i != lnode_succ->end(); ++i)
{
os << num_ << " -> " << (*i)->num_ << std::endl;
}
return os.str();
}
int
spoiler_node::get_nb_succ()
{
return lnode_succ->size();
}
const state*
spoiler_node::get_spoiler_node()
{
return sc_->first;
}
const state*
spoiler_node::get_duplicator_node()
{
return sc_->second;
}
state_couple*
spoiler_node::get_pair()
{
return sc_;
}
///////////////////////////////////////////////////////////////////////
// duplicator_node
duplicator_node::duplicator_node(const state* d_node,
const state* s_node,
bdd l,
bdd a,
int num)
: spoiler_node(d_node, s_node, num),
label_(l),
acc_(a)
{
}
duplicator_node::~duplicator_node()
{
}
bool
duplicator_node::set_win()
{
bool change = not_win;
if (!this->get_nb_succ())
not_win = true;
else
{
not_win = true;
for (Sgi::vector<spoiler_node*>::iterator i = lnode_succ->begin();
i != lnode_succ->end(); ++i)
{
not_win &= (*i)->not_win;
}
}
return (change != not_win);
}
std::string
duplicator_node::to_string(const tgba* a)
{
std::ostringstream os;
// print the node.
os << num_
<< " [shape=box, label=\"("
<< a->format_state(sc_->first)
<< ", "
<< a->format_state(sc_->second)
<< ", ";
bdd_print_acc(os, a->get_dict(), acc_);
os << ")\"]"
<< std::endl;
return os.str();
}
bool
duplicator_node::match(bdd l, bdd a)
{
return ((l == label_) && (a == acc_));
}
bool
duplicator_node::implies(bdd l, bdd a)
{
// if (a | !b) == true then (a => b).
return (((l | !label_) == bddtrue) &&
((a | !acc_) == bddtrue));
}
///////////////////////////////////////////////////////////////////////
// parity_game_graph
void
parity_game_graph::start()
{
}
void
parity_game_graph::end()
{
}
void
parity_game_graph::process_state(const state* s,
int ,
tgba_succ_iterator*)
{
tgba_state_.push_back(s);
}
void
parity_game_graph::process_link(int ,
int ,
const tgba_succ_iterator*)
{
}
void
parity_game_graph::print(std::ostream& os)
{
Sgi::vector<spoiler_node*>::iterator i1;
Sgi::vector<duplicator_node*>::iterator i2;
int n = 0;
os << "digraph G {" << std::endl;
os << "{" << std::endl
<< "rank = same;" << std::endl
<< "node [color=red];" << std::endl;
for (i1 = spoiler_vertice_.begin();
i1 != spoiler_vertice_.end(); ++i1)
{
os << (*i1)->to_string(automata_);
n++;
if (n > 20)
{
n = 0;
os << "}" << std::endl << std::endl
<< "{" << std::endl
<< "rank = same" << std::endl
<< "node [color=red];" << std::endl;
}
}
os << "}" << std::endl;
n = 0;
os << "{" << std::endl
<< "rank = same;" << std::endl
<< "node [color=green];" << std::endl;
for (i2 = duplicator_vertice_.begin();
i2 != duplicator_vertice_.end(); ++i2)
{
os << (*i2)->to_string(automata_);
n++;
if (n > 20)
{
n = 0;
os << "}" << std::endl << std::endl
<< "{" << std::endl
<< "rank = same" << std::endl
<< "node [color=green];" << std::endl;
}
}
os << "}" << std::endl << std::endl;
os << "edge [color=red];" << std::endl;
for (i1 = spoiler_vertice_.begin();
i1 != spoiler_vertice_.end(); ++i1)
{
os << (*i1)->succ_to_string();
}
os << std::endl
<< "edge [color=green];" << std::endl;
for (i2 = duplicator_vertice_.begin();
i2 != duplicator_vertice_.end(); ++i2)
{
os << (*i2)->succ_to_string();
}
os << "}" << std::endl;
}
parity_game_graph::~parity_game_graph()
{
Sgi::vector<spoiler_node*>::iterator i1;
Sgi::vector<duplicator_node*>::iterator i2;
for (i1 = spoiler_vertice_.begin();
i1 != spoiler_vertice_.end(); ++i1)
{
delete *i1;
}
for (i2 = duplicator_vertice_.begin();
i2 != duplicator_vertice_.end(); ++i2)
{
delete *i2;
}
spoiler_vertice_.clear();
duplicator_vertice_.clear();
}
parity_game_graph::parity_game_graph(const tgba* a)
: tgba_reachable_iterator_breadth_first(a)
{
this->run();
nb_node_parity_game = 0;
}
///////////////////////////////////////////////////////////////////////
// parity_game_graph_direct
void
parity_game_graph_direct::build_couple()
{
tgba_succ_iterator* si = NULL;
typedef Sgi::pair<bdd, bdd> couple_bdd;
couple_bdd *p = NULL;
Sgi::vector<couple_bdd*>* trans = NULL;
bool exist = false;
spot::state* s = NULL;
for (Sgi::vector<const state*>::iterator i = tgba_state_.begin();
i != tgba_state_.end(); ++i)
{
// spoiler node are all state couple (i,j)
for (Sgi::vector<const state*>::iterator j = tgba_state_.begin();
j != tgba_state_.end(); ++j)
{
spoiler_node* n1 = new spoiler_node(*i,
*j,
nb_node_parity_game++);
spoiler_vertice_.push_back(n1);
}
// duplicator node are all state couple where
// the first state i are reachable.
trans = new Sgi::vector<couple_bdd*>;
for (Sgi::vector<const state*>::iterator j = tgba_state_.begin();
j != tgba_state_.end(); ++j)
{
si = automata_->succ_iter(*j);
for (si->first(); !si->done(); si->next())
{
// if there exist a predecessor of i named j
s = si->current_state();
if (s->compare(*i) == 0)
{
// p is the label of the transition j->i
p = new couple_bdd(si->current_condition(),
si->current_acceptance_conditions());
// If an other predecessor of i has the same label p
// to reach i, then we don't compute the duplicator node.
exist = false;
for (Sgi::vector<couple_bdd*>::iterator v
= trans->begin();
v != trans->end(); ++v)
{
if ((si->current_condition() == (*v)->first) &&
(si->current_acceptance_conditions()
== (*v)->second))
exist = true;
}
if (!exist)
{
// We build all the state couple with the label p.
trans->push_back(p);
for (Sgi::vector<const state*>::iterator s
= tgba_state_.begin();
s != tgba_state_.end(); ++s)
{
duplicator_node* n2
= new duplicator_node(*i,
*s,
si->current_condition(),
si->current_acceptance_conditions(),
nb_node_parity_game++);
duplicator_vertice_.push_back(n2);
}
}
else
delete p;
}
delete s;
}
delete si;
}
Sgi::vector<couple_bdd*>::iterator i2;
for (i2 = trans->begin(); i2 != trans->end(); ++i2)
{
delete *i2;
}
delete trans;
}
}
void
parity_game_graph_direct::build_link()
{
int nb_ds = 0;
int nb_sd = 0;
spot::state* s = NULL;
// for each couple of (spoiler, duplicator)
for (Sgi::vector<spoiler_node*>::iterator i
= spoiler_vertice_.begin(); i != spoiler_vertice_.end(); ++i)
{
for (Sgi::vector<duplicator_node*>::iterator j
= duplicator_vertice_.begin();
j != duplicator_vertice_.end(); ++j)
{
// We add a link between a duplicator and a spoiler.
if ((*j)->get_spoiler_node()->compare((*i)->get_spoiler_node()) == 0)
{
tgba_succ_iterator* si
= automata_->succ_iter((*j)->get_duplicator_node());
for (si->first(); !si->done(); si->next())
{
s = si->current_state();
if ((s->compare((*i)->get_duplicator_node()) == 0) &&
(*j)->implies(si->current_condition(),
si->current_acceptance_conditions()))
{
(*j)->add_succ(*i);
nb_ds++;
}
delete s;
}
delete si;
}
// We add a link between a spoiler and a duplicator.
if ((*j)->get_duplicator_node()->compare((*i)->get_duplicator_node()) == 0)
{
tgba_succ_iterator* si
= automata_->succ_iter((*i)->get_spoiler_node());
for (si->first(); !si->done(); si->next())
{
s = si->current_state();
if ((s->compare((*j)->get_spoiler_node()) == 0) &&
(*j)->match(si->current_condition(),
si->current_acceptance_conditions()))
{
(*i)->add_succ(*j);
nb_sd++;
}
delete s;
}
delete si;
}
}
}
}
void
parity_game_graph_direct::prune()
{
bool change = true;
while (change)
{
change = false;
for (Sgi::vector<duplicator_node*>::iterator i
= duplicator_vertice_.begin();
i != duplicator_vertice_.end(); ++i)
{
change |= (*i)->set_win();
}
for (Sgi::vector<spoiler_node*>::iterator i
= spoiler_vertice_.begin();
i != spoiler_vertice_.end(); ++i)
{
change |= (*i)->set_win();
}
}
}
simulation_relation*
parity_game_graph_direct::get_relation()
{
simulation_relation* rel = new simulation_relation();
state_couple* p = NULL;
seen_map::iterator j;
for (Sgi::vector<spoiler_node*>::iterator i
= spoiler_vertice_.begin();
i != spoiler_vertice_.end(); ++i)
{
if (!(*i)->not_win)
{
p = new state_couple((*i)->get_spoiler_node(),
(*i)->get_duplicator_node());
rel->push_back(p);
// We remove the state in rel from seen
// because the destructor of
// tgba_reachable_iterator_breadth_first
// delete all the state.
if ((j = seen.find(p->first)) != seen.end())
seen.erase(j);
if ((j = seen.find(p->second)) != seen.end())
seen.erase(j);
}
}
return rel;
}
parity_game_graph_direct::~parity_game_graph_direct()
{
}
parity_game_graph_direct::parity_game_graph_direct(const tgba* a)
: parity_game_graph(a)
{
this->build_couple();
this->build_link();
this->prune();
}
///////////////////////////////////////////////////////////////////////
simulation_relation*
get_direct_relation_simulation(const tgba* f, int opt)
{
parity_game_graph_direct* G = new parity_game_graph_direct(f);
simulation_relation* rel = G->get_relation();
if (opt == 1)
G->print(std::cout);
delete G;
return rel;
}
void
free_relation_simulation(simulation_relation* rel)
{
if (rel == NULL)
return;
Sgi::hash_map<const spot::state*, int,
state_ptr_hash, state_ptr_equal> seen;
Sgi::hash_map<const spot::state*, int,
state_ptr_hash, state_ptr_equal>::iterator j;
simulation_relation::iterator i;
for (i = rel->begin(); i != rel->end(); ++i)
{
if ((j = seen.find((*i)->first)) == seen.end())
seen[(*i)->first] = 0;
if ((j = seen.find((*i)->second)) == seen.end())
seen[(*i)->second] = 0;
delete *i;
}
delete rel;
rel = NULL;
for (j = seen.begin(); j != seen.end();)
{
const state* ptr = j->first;
++j;
delete(ptr);
}
}
bool
is_include(const tgba*, const tgba*)
{
return false;
}
tgba*
reduc_tgba_sim(const tgba* f, int opt)
{
spot::tgba_reduc* automatareduc = new spot::tgba_reduc(f);
if (opt & Reduce_Dir_Sim)
{
simulation_relation* rel
= get_direct_relation_simulation(automatareduc);
automatareduc->display_rel_sim(rel, std::cout);
automatareduc->prune_automata(rel);
free_relation_simulation(rel);
}
else
if (opt & Reduce_Del_Sim)
{
simulation_relation* rel
= get_delayed_relation_simulation(automatareduc);
automatareduc->display_rel_sim(rel, std::cout);
automatareduc->prune_automata(rel);
free_relation_simulation(rel);
}
if (opt & Reduce_Scc)
{
automatareduc->compute_scc();
automatareduc->prune_scc();
}
return automatareduc;
}
}

View file

@ -0,0 +1,314 @@
// Copyright (C) 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 2 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 Spot; see the file COPYING. If not, write to the Free
// Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
// 02111-1307, USA.
#ifndef SPOT_REDUC_TGBA_SIM_HH
#define SPOT_REDUC_TGBA_SIM_HH
#include "tgba/tgbareduc.hh"
#include "tgbaalgos/reachiter.hh"
#include <vector>
#include <list>
#include <sstream>
namespace spot
{
/// Options for reduce.
enum reduce_tgba_options
{
/// No reduction.
Reduce_None = 0,
/// Reduction using direct simulation relation.
Reduce_Dir_Sim = 1,
/// Reduction using delayed simulation relation.
Reduce_Del_Sim = 2,
/// Reduction using SCC.
Reduce_Scc = 4,
/// All reductions.
Reduce_All = -1U
};
/// \brief Remove some node of the automata using a simulation
/// relation.
///
/// \param a the automata to reduce.
/// \param opt a conjonction of spot::reduce_tgba_options specifying
// which optimizations to apply.
/// \return the reduced automata.
tgba* reduc_tgba_sim(const tgba* a, int opt = Reduce_All);
/// \brief Compute a direct simulation relation on state of tgba \a f.
simulation_relation* get_direct_relation_simulation(const tgba* a,
int opt = -1);
/// Compute a delayed simulation relation on state of tgba \a f.
/// FIXME : this method is incorrect !!
/// Don't use it !!
simulation_relation* get_delayed_relation_simulation(const tgba* a,
int opt = -1);
/// To free a simulation relation.
void free_relation_simulation(simulation_relation* rel);
/// Test if the initial state of a2 fair simulate this of a1.
/// Not implemented.
bool is_include(const tgba* a1, const tgba* a2);
///////////////////////////////////////////////////////////////////////
// simulation.
class spoiler_node;
class duplicator_node;
typedef Sgi::vector<spoiler_node*> sn_v;
typedef Sgi::vector<duplicator_node*> dn_v;
typedef Sgi::vector<const state*> s_v;
/// \brief Parity game graph which compute a simulation relation.
class parity_game_graph : public tgba_reachable_iterator_breadth_first
{
public:
parity_game_graph(const tgba* a);
virtual ~parity_game_graph();
virtual simulation_relation* get_relation() = 0;
void print(std::ostream& os);
protected:
sn_v spoiler_vertice_;
dn_v duplicator_vertice_;
s_v tgba_state_;
int nb_node_parity_game;
void start();
void end();
void process_state(const state* s, int n, tgba_succ_iterator* si);
void process_link(int in, int out, const tgba_succ_iterator* si);
/// \brief Compute each node of the graph.
virtual void build_couple() = 0;
/// \brief Compute the link of the graph.
/// Successor of spoiler node (resp. duplicator node)
/// are duplicator node (resp. spoiler node).
virtual void build_link() = 0;
/// \brief Remove edge from spoiler to duplicator that make
/// duplicator loose.
/// Spoiler node whose still have some link, reveal
/// a direct simulation relation.
virtual void prune() = 0;
};
///////////////////////////////////////////////////////////////////////
// Direct simulation.
/// Spoiler node of parity game graph.
class spoiler_node
{
public:
spoiler_node(const state* d_node,
const state* s_node,
int num);
virtual ~spoiler_node();
void add_succ(spoiler_node* n);
void del_succ(spoiler_node* n);
virtual void add_pred(spoiler_node* n);
virtual void del_pred();
int get_nb_succ();
bool prune();
virtual bool set_win();
virtual std::string to_string(const tgba* a);
virtual std::string succ_to_string();
const state* get_spoiler_node();
const state* get_duplicator_node();
state_couple* get_pair();
bool not_win;
int num_; // for the dot display.
protected:
sn_v* lnode_succ;
sn_v* lnode_pred;
//Sgi::vector<spoiler_node*>* lnode_succ;
state_couple* sc_;
};
/// Duplicator node of parity game graph.
class duplicator_node : public spoiler_node
{
public:
duplicator_node(const state* d_node,
const state* s_node,
bdd l,
bdd a,
int num);
virtual ~duplicator_node();
virtual bool set_win();
virtual std::string to_string(const tgba* a);
bool match(bdd l, bdd a);
bool implies(bdd l, bdd a);
protected:
bdd label_;
bdd acc_;
};
/// Parity game graph which compute the direct simulation relation.
class parity_game_graph_direct : public parity_game_graph
{
public:
parity_game_graph_direct(const tgba* a);
~parity_game_graph_direct();
virtual simulation_relation* get_relation();
protected:
virtual void build_couple();
virtual void build_link();
virtual void prune();
};
///////////////////////////////////////////////////////////////////////
// Delayed simulation.
/// Spoiler node of parity game graph for delayed simulation.
class spoiler_node_delayed : public spoiler_node
{
public:
spoiler_node_delayed(const state* d_node,
const state* s_node,
bdd a,
int num);
~spoiler_node_delayed();
/// Return true if the progress_measure has changed.
bool set_win();
bdd get_acceptance_condition_visited();
virtual std::string to_string(const tgba* a);
int get_progress_measure();
protected:
/// a Bdd for retain all the acceptance condition
/// that a node has visited.
bdd acceptance_condition_visited_;
int progress_measure_;
};
/// Duplicator node of parity game graph for delayed simulation.
class duplicator_node_delayed : public duplicator_node
{
public:
duplicator_node_delayed(const state* d_node,
const state* s_node,
bdd l,
bdd a,
int num);
~duplicator_node_delayed();
/// Return true if the progress_measure has changed.
bool set_win();
virtual std::string to_string(const tgba* a);
bool implies_label(bdd l);
bool implies_acc(bdd a);
int get_progress_measure();
protected:
int progress_measure_;
};
/// Parity game graph which compute the delayed simulation relation
/// as explain in
/// @inproceedings{ icalp2001,
/// AUTHOR = {Etessami, Thomas Wilke, Rebecca A. Schuller},
/// TITLE = {Fair Simulation Relations, Parity Games, and State Space
/// Reduction for Buchi Automata},
/// BOOKTITLE = {Automata, Languages and Programming,
/// 28th international collquium},
/// PAGES = {694--707},
/// YEAR = 2001,
/// EDITOR = {Orejas, Fernando and Spirakis, Paul G. and van Leeuwen, Jan},
/// VOLUME = 2076,
/// SERIES = {Lecture Notes in Computer Science},
/// ADDRESS = {Crete, Greece},
/// MONTH = JUL,
/// PUBLISHER = {Springer},
/// url = {citeseer.ist.psu.edu/472661.html}
/// }
class parity_game_graph_delayed : public parity_game_graph
{
public:
parity_game_graph_delayed(const tgba* a);
~parity_game_graph_delayed();
virtual simulation_relation* get_relation();
private:
/// Vector which contain all the sub-set of the set
/// of acceptance condition.
typedef Sgi::vector<bdd> bdd_v;
bdd_v sub_set_acc_cond_;
/// Return the number of acceptance condition.
int nb_set_acc_cond();
/// Compute sub_set_acc_cond_;
void build_sub_set_acc_cond();
/// Add a duplicator node, and
/// all his successor (spoiler node) which
/// have a acceptance_condition_visited_ != bddfalse
void add_dup_node(state* ss,
state* sd,
bdd l,
bdd a);
/// \brief Compute the couple as for direct simulation,
virtual void build_couple();
virtual void build_link();
void build_recurse_successor_spoiler(spoiler_node* sn);
void build_recurse_successor_duplicator(duplicator_node* dn);
/// \brief The Jurdzinski's lifting algorithm.
void lift();
/// \brief Remove all node so as to there is no dead ends (terminal node).
virtual void prune();
};
}
#endif

View file

@ -0,0 +1,816 @@
// Copyright (C) 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 2 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 Spot; see the file COPYING. If not, write to the Free
// Software Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
// 02111-1307, USA.
#include "reductgba_sim.hh"
#include "tgba/bddprint.hh"
namespace spot
{
/// Number of spoiler node with a one priority (see icalp2001).
/// The one priority is represent by a \a acceptance_condition_visited_
/// which differ of bddfalse.
/// This spoiler node are looser for the duplicator.
static int nb_spoiler_loose_;
static int nb_spoiler;
static int nb_duplicator;
//static int nb_node;
///////////////////////////////////////////////////////////////////////
// spoiler_node_delayed
spoiler_node_delayed::spoiler_node_delayed(const state* d_node,
const state* s_node,
bdd a,
int num)
: spoiler_node(d_node, s_node, num),
acceptance_condition_visited_(a)
{
nb_spoiler++;
progress_measure_ = 0;
if (acceptance_condition_visited_ == bddfalse)
nb_spoiler_loose_++;
}
spoiler_node_delayed::~spoiler_node_delayed()
{
}
bool
spoiler_node_delayed::set_win()
{
// We take the max of the progress measure of the successor node
// because we are on a spoiler.
//std::cout << "spoiler_node_delayed::set_win" << std::endl;
if (lnode_succ->size() == 0)
progress_measure_ = nb_spoiler_loose_;
if (progress_measure_ >= nb_spoiler_loose_)
return false;
bool change;
int tmpmax = 0;
int tmp = 0;
sn_v::iterator i = lnode_succ->begin();
if (i != lnode_succ->end())
{
tmpmax =
dynamic_cast<duplicator_node_delayed*>(*i)->get_progress_measure();
++i;
}
for (; i != lnode_succ->end(); ++i)
{
tmp =
dynamic_cast<duplicator_node_delayed*>(*i)->get_progress_measure();
if (tmp > tmpmax)
tmpmax = tmp;
}
// If the priority of the node is 1
// acceptance_condition_visited_ != bddfalse
// then we increment the progress measure of 1.
if (acceptance_condition_visited_ != bddfalse)
tmpmax++;
change = (progress_measure_ < tmpmax);
progress_measure_ = tmpmax;
return change;
}
std::string
spoiler_node_delayed::to_string(const tgba* a)
{
std::ostringstream os;
// print the node.
os << num_
<< " [shape=box, label=\"("
<< a->format_state(sc_->first)
<< ", "
<< a->format_state(sc_->second)
<< ", ";
//bdd_print_acc(os, a->get_dict(), acceptance_condition_visited_);
if (acceptance_condition_visited_ == bddfalse)
{
os << "false";
}
else
{
os << "ACC";
}
os << ")"
<< " pm = " << progress_measure_ << "\"]"
<< std::endl;
return os.str();
}
bdd
spoiler_node_delayed::get_acceptance_condition_visited()
{
return acceptance_condition_visited_;
}
int
spoiler_node_delayed::get_progress_measure()
{
if ((acceptance_condition_visited_ == bddfalse) &&
(progress_measure_ != (nb_spoiler_loose_ + 1)))
return 0;
else
return progress_measure_;
}
///////////////////////////////////////////////////////////////////////
// duplicator_node_delayed
duplicator_node_delayed::duplicator_node_delayed(const state* d_node,
const state* s_node,
bdd l,
bdd a,
int num)
: duplicator_node(d_node, s_node, l, a, num)
{
nb_duplicator++;
progress_measure_ = 0;
}
duplicator_node_delayed::~duplicator_node_delayed()
{
}
bool
duplicator_node_delayed::set_win()
{
// We take the min of the progress measure of the successor node
// because we are on a duplicator.
//std::cout << "duplicator_node_delayed::set_win" << std::endl;
//bool debug = true;
if (progress_measure_ == nb_spoiler_loose_)
return false;
bool change;
int tmpmin = 0;
int tmp = 0;
sn_v::iterator i = lnode_succ->begin();
if (i != lnode_succ->end())
{
tmpmin = dynamic_cast<spoiler_node_delayed*>(*i)->get_progress_measure();
/*
debug &= (dynamic_cast<spoiler_node_delayed*>(*i)
->get_acceptance_condition_visited()
!= bddfalse);
*/
++i;
}
for (; i != lnode_succ->end(); ++i)
{
/*
debug &= (dynamic_cast<spoiler_node_delayed*>(*i)
->get_acceptance_condition_visited()
!= bddfalse);
*/
tmp = dynamic_cast<spoiler_node_delayed*>(*i)->get_progress_measure();
if (tmp < tmpmin)
tmpmin = tmp;
}
/*
if (debug)
std::cout << "All successor p = 1" << std::endl;
else
std::cout << "Not All successor p = 1" << std::endl;
*/
change = (progress_measure_ < tmpmin);
progress_measure_ = tmpmin;
return change;
}
std::string
duplicator_node_delayed::to_string(const tgba* a)
{
std::ostringstream os;
// print the node.
os << num_
<< " [shape=box, label=\"("
<< a->format_state(sc_->first)
<< ", "
<< a->format_state(sc_->second);
//<< ", ";
//bdd_print_acc(os, a->get_dict(), acc_);
os << ")"
<< " pm = " << progress_measure_ << "\"]"
<< std::endl;
return os.str();
}
bool
duplicator_node_delayed::implies_label(bdd l)
{
return ((l | !label_) == bddtrue);
}
bool
duplicator_node_delayed::implies_acc(bdd a)
{
return ((a | !acc_) == bddtrue);
}
int
duplicator_node_delayed::get_progress_measure()
{
return progress_measure_;
}
///////////////////////////////////////////////////////////////////////
// parity_game_graph_delayed
int
parity_game_graph_delayed::nb_set_acc_cond()
{
bdd acc, all;
acc = all = automata_->all_acceptance_conditions();
int count = 0;
while (all != bddfalse)
{
sub_set_acc_cond_.push_back(bdd_satone(all));
all -= bdd_satone(all);
count++;
}
return count;
}
void
parity_game_graph_delayed::build_sub_set_acc_cond()
{
// compute the number of acceptance conditions
bdd acc, all;
acc = all = automata_->all_acceptance_conditions();
int count = 0;
while (all != bddfalse)
{
//std::cout << "add acc" << std::endl;
sub_set_acc_cond_.push_back(bdd_satone(all));
all -= bdd_satone(all);
count++;
}
// sub_set_acc_cond_ contains all the acceptance condition.
// but we must have all the sub-set of acceptance condition.
// In fact we must have 2^count sub-set.
if (count == 2)
{
sub_set_acc_cond_.push_back(acc);
sub_set_acc_cond_.push_back(bddfalse);
}
/*
bdd_v::iterator i;
bdd_v::iterator j;
for (i = sub_set_acc_cond_.begin(); i != sub_set_acc_cond_.end(); ++i)
for (j = sub_set_acc_cond_.begin(); j != sub_set_acc_cond_.end(); ++j)
sub_set_acc_cond_.push_back(*i | *j);
std::cout << std::endl;
for (i = sub_set_acc_cond_.begin(); i != sub_set_acc_cond_.end();)
{
bdd_print_acc(std::cout, automata_->get_dict(), *i);
std::cout << " // " << std::endl;
++i;
}
std::cout << std::endl;
*/
}
void
parity_game_graph_delayed::build_couple()
{
//std::cout << "build couple" << std::endl;
nb_spoiler = 0;
nb_duplicator = 0;
tgba_succ_iterator* si = NULL;
typedef Sgi::pair<bdd, bdd> couple_bdd;
couple_bdd *p = NULL;
Sgi::vector<couple_bdd*>* trans = NULL;
bool exist = false;
spot::state* s = NULL;
s_v::iterator i;
for (i = tgba_state_.begin(); i != tgba_state_.end(); ++i)
{
// for each sub-set of the set of acceptance condition.
bdd_v::iterator i2;
for (i2 = sub_set_acc_cond_.begin();
i2 != sub_set_acc_cond_.end(); ++i2)
{
// spoiler node are all state couple (i,j)
// multiply by 2^(|F|)
s_v::iterator i3;
for (i3 = tgba_state_.begin();
i3 != tgba_state_.end(); ++i3)
{
//nb_spoiler++;
spoiler_node_delayed* n1
= new spoiler_node_delayed(*i,
*i3,
*i2,
nb_node_parity_game++);
spoiler_vertice_.push_back(n1);
}
// duplicator node are all state couple where
// the first state i are reachable.
trans = new Sgi::vector<couple_bdd*>;
for (i3 = tgba_state_.begin();
i3 != tgba_state_.end(); ++i3)
{
si = automata_->succ_iter(*i3);
for (si->first(); !si->done(); si->next())
{
// if there exist a predecessor of i named j
s = si->current_state();
if (s->compare(*i) == 0)
{
// p is the label of the transition j->i
p = new couple_bdd(si->current_condition(),
si->current_acceptance_conditions());
// If an other predecessor of i has the same label p
// to reach i, then we don't compute the
// duplicator node.
exist = false;
Sgi::vector<couple_bdd*>::iterator i4;
for (i4 = trans->begin();
i4 != trans->end(); ++i4)
{
if ((si->current_condition() == (*i4)->first))
// We don't need the acceptance condition
//&&
//(si->current_acceptance_conditions()
//== (*i4)->second))
exist = true;
}
if (!exist)
{
// We build all the state couple with the label p.
// multiply by 2^(|F|)
trans->push_back(p);
Sgi::vector<const state*>::iterator i5;
for (i5 = tgba_state_.begin();
i5 != tgba_state_.end(); ++i5)
{
//nb_duplicator++;
int nb = nb_node_parity_game++;
duplicator_node_delayed* n2
= new
duplicator_node_delayed(*i,
*i5,
si->
current_condition(),
*i2,
nb);
duplicator_vertice_.push_back(n2);
}
}
else
delete p;
}
delete s;
}
delete si;
}
Sgi::vector<couple_bdd*>::iterator i6;
for (i6 = trans->begin(); i6 != trans->end(); ++i6)
{
delete *i6;
}
delete trans;
}
}
nb_spoiler_loose_++;
//std::cout << "spoiler node : " << nb_spoiler << std::endl;
//std::cout << "duplicator node : " << nb_duplicator << std::endl;
//std::cout << "nb_spoiler_loose_ : " << nb_spoiler_loose_ << std::endl;
}
void
parity_game_graph_delayed::build_link()
{
//std::cout << "build link" << std::endl;
int nb_ds = 0;
int nb_sd = 0;
spot::state* s = NULL;
// for each couple of (spoiler, duplicator)
sn_v::iterator i;
for (i = spoiler_vertice_.begin(); i != spoiler_vertice_.end(); ++i)
{
dn_v::iterator i2;
for (i2 = duplicator_vertice_.begin();
i2 != duplicator_vertice_.end(); ++i2)
{
// We add a link between a duplicator and a spoiler.
if ((*i2)->get_spoiler_node()->compare((*i)
->get_spoiler_node()) == 0)
{
tgba_succ_iterator* si
= automata_->succ_iter((*i2)->get_duplicator_node());
for (si->first(); !si->done(); si->next())
{
s = si->current_state();
bdd btmp2 = dynamic_cast<spoiler_node_delayed*>(*i)->
get_acceptance_condition_visited();
bdd btmp = btmp2 - si->current_acceptance_conditions();
//if ((s->compare((*i)->get_duplicator_node()) == 0) &&
//dynamic_cast<duplicator_node_delayed*>(*i2)->
// implies_label(si->current_condition()) &&
//(btmp == btmp2))
if ((s->compare((*i)->get_duplicator_node()) == 0) &&
dynamic_cast<duplicator_node_delayed*>(*i2)->
implies_label(si->current_condition()) &&
(dynamic_cast<spoiler_node_delayed*>(*i)->
get_acceptance_condition_visited() != bddfalse))
{
//std::cout << "add duplicator -> spoiler" << std::endl;
(*i2)->add_succ(*i);
(*i)->add_pred(*i2);
nb_ds++;
}
delete s;
}
delete si;
}
// We add a link between a spoiler and a duplicator.
if ((*i2)->get_duplicator_node()
->compare((*i)->get_duplicator_node()) == 0)
{
tgba_succ_iterator* si
= automata_->succ_iter((*i)->get_spoiler_node());
for (si->first(); !si->done(); si->next())
{
s = si->current_state();
bdd btmp = si->current_acceptance_conditions() |
dynamic_cast<spoiler_node_delayed*>(*i)->
get_acceptance_condition_visited();
if ((s->compare((*i2)->get_spoiler_node()) == 0) &&
(*i2)->match(si->current_condition(), btmp))
{
//std::cout << "add spoiler -> duplicator" << std::endl;
(*i)->add_succ(*i2);
(*i2)->add_pred(*i);
nb_sd++;
}
delete s;
}
delete si;
}
}
}
}
/*
// We build only node which are reachable
void
parity_game_graph_delayed::build_couple()
{
// We build only some "basic" spoiler node.
s_v::iterator i;
for (i = tgba_state_.begin(); i != tgba_state_.end(); ++i)
{
// spoiler node are all state couple (i,j)
s_v::iterator i2;
for (i2 = tgba_state_.begin();
i2 != tgba_state_.end(); ++i2)
{
std::cout << "add spoiler node" << std::endl;
nb_spoiler++;
spoiler_node_delayed* n1
= new spoiler_node_delayed(*i, *i2,
bddfalse,
nb_node_parity_game++);
spoiler_vertice_.push_back(n1);
}
}
}
void
parity_game_graph_delayed::build_link()
{
// We create when it's possible a duplicator node
// and recursively his successor.
spot::state* s1 = NULL;
bool exist_pred = false;
sn_v::iterator i1;
for (i1 = spoiler_vertice_.begin(); i1 != spoiler_vertice_.end(); ++i1)
{
exist_pred = false;
// We check if there is a predecessor only if the duplicator
// is the initial state.
s1 = automata_->get_init_state();
if (s1->compare((*i1)->get_duplicator_node()) == 0)
{
tgba_succ_iterator* si;
s_v::iterator i2;
spot::state* s2 = NULL;
for (i2 = tgba_state_.begin();
i2 != tgba_state_.end(); ++i2)
{
si = automata_->succ_iter(*i2);
s2 = si->current_state();
if (s2->compare(s1) == 0)
exist_pred = true;
delete s2;
}
}
else
exist_pred = true;
delete s1;
if (!exist_pred)
continue;
// We add a link between a spoiler and a (new) duplicator.
// The acc of the duplicator must contains the
// acceptance_condition_visited_ of the spoiler.
build_recurse_successor_spoiler(*i1);
}
}
void
parity_game_graph_delayed::build_recurse_successor_spoiler(spoiler_node* sn)
{
tgba_succ_iterator* si = automata_->succ_iter(sn->get_spoiler_node());
for (si->first(); !si->done(); si->next())
{
bdd btmp = si->current_acceptance_conditions() |
dynamic_cast<spoiler_node_delayed*>(sn)->
get_acceptance_condition_visited();
s_v::iterator i1;
state* s;
for (i1 = tgba_state_.begin();
i1 != tgba_state_.end(); ++i1)
{
s = si->current_state();
if (s->compare(*i1) == 0)
{
duplicator_node_delayed* dn
= new duplicator_node_delayed(*i1,
sn->get_duplicator_node(),
si->current_condition(),
btmp,
nb_node_parity_game++);
duplicator_vertice_.push_back(dn);
sn->add_succ(dn);
(dn)->add_pred(sn);
build_recurse_successor_duplicator(dn);
}
delete s;
}
}
delete si;
}
void
parity_game_graph_delayed::
build_recurse_successor_duplicator(duplicator_node* dn)
{
tgba_succ_iterator* si = automata_->succ_iter(sn->get_spoiler_node());
for (si->first(); !si->done(); si->next())
{
bdd btmp =
dynamic_cast<spoiler_node_delayed*>(sn)->
get_acceptance_condition_visited();
bdd btmp2 = btmp - si->current_acceptance_conditions();
s_v::iterator i1;
state* s;
for (i1 = tgba_state_.begin();
i1 != tgba_state_.end(); ++i1)
{
s = si->current_state();
if (s->compare(*i1) == 0)
{
spoiler_node_delayed* sn
= new spoiler_node_delayed(sn->get_spoiler_node(),
*i1,
bddtmp2,
nb_node_parity_game++);
spoiler_vertice_.push_back(n1);
sn->add_succ(dn);
(dn)->add_pred(sn);
build_recurse_successor_spoiler(sn);
}
delete s;
}
}
delete si;
}
*/
void
parity_game_graph_delayed::add_dup_node(state*,
state*,
bdd,
bdd)
{
}
void
parity_game_graph_delayed::prune()
{
bool change = true;
while (change)
{
change = false;
for (Sgi::vector<duplicator_node*>::iterator i
= duplicator_vertice_.begin();
i != duplicator_vertice_.end();)
{
if ((*i)->get_nb_succ() == 0)
{
(*i)->del_pred();
delete *i;
i = duplicator_vertice_.erase(i);
change = true;
}
else
++i;
}
for (Sgi::vector<spoiler_node*>::iterator i
= spoiler_vertice_.begin();
i != spoiler_vertice_.end();)
{
if ((*i)->get_nb_succ() == 0)
{
(*i)->del_pred();
delete *i;
i = spoiler_vertice_.erase(i);
change = true;
}
else
++i;
}
}
}
void
parity_game_graph_delayed::lift()
{
// Jurdzinski's algorithm
//int iter = 0;
bool change = true;
while (change)
{
change = false;
for (Sgi::vector<duplicator_node*>::iterator i
= duplicator_vertice_.begin();
i != duplicator_vertice_.end(); ++i)
{
change |= (*i)->set_win();
}
for (Sgi::vector<spoiler_node*>::iterator i
= spoiler_vertice_.begin();
i != spoiler_vertice_.end(); ++i)
{
change |= (*i)->set_win();
}
}
}
simulation_relation*
parity_game_graph_delayed::get_relation()
{
simulation_relation* rel = new simulation_relation();
state_couple* p = NULL;
seen_map::iterator j;
for (Sgi::vector<spoiler_node*>::iterator i
= spoiler_vertice_.begin();
i != spoiler_vertice_.end(); ++i)
{
if (dynamic_cast<spoiler_node_delayed*>(*i)->get_progress_measure()
< nb_spoiler_loose_)
{
p = new state_couple((*i)->get_spoiler_node(),
(*i)->get_duplicator_node());
rel->push_back(p);
// We remove the state in rel from seen
// because the destructor of
// tgba_reachable_iterator_breadth_first
// delete all the state.
if ((j = seen.find(p->first)) != seen.end())
seen.erase(j);
if ((j = seen.find(p->second)) != seen.end())
seen.erase(j);
}
}
return rel;
}
parity_game_graph_delayed::~parity_game_graph_delayed()
{
}
parity_game_graph_delayed::parity_game_graph_delayed(const tgba* a)
: parity_game_graph(a)
{
nb_spoiler_loose_ = 0;
/*
if (this->nb_set_acc_cond() > 2)
return;
this->build_sub_set_acc_cond();
*/
this->build_couple();
this->build_link();
this->prune();
this->lift();
//this->print(std::cout);
}
///////////////////////////////////////////
simulation_relation*
get_delayed_relation_simulation(const tgba* f, int opt)
{
/// FIXME : this method is incorrect !!
/// Don't use it !!
parity_game_graph_delayed* G = new parity_game_graph_delayed(f);
simulation_relation* rel = G->get_relation();
if (opt == 1)
G->print(std::cout);
delete G;
return rel;
}
}