rename src/tgbaalgos/ as src/twaalgos/

Automatic mass renaming.

* src/tgbaalgos/: Rename as...
* src/twaalgos/: ... this.
* README, configure.ac, iface/ltsmin/modelcheck.cc, src/Makefile.am,
src/bin/autfilt.cc, src/bin/common_aoutput.cc,
src/bin/common_aoutput.hh, src/bin/common_output.hh,
src/bin/common_post.hh, src/bin/dstar2tgba.cc, src/bin/ltl2tgba.cc,
src/bin/ltl2tgta.cc, src/bin/ltlcross.cc, src/bin/ltldo.cc,
src/bin/ltlfilt.cc, src/bin/randaut.cc, src/dstarparse/dra2ba.cc,
src/dstarparse/nra2nba.cc, src/dstarparse/nsa2tgba.cc,
src/graphtest/twagraph.cc, src/kripke/kripkeprint.cc,
src/ltlvisit/contain.cc, src/ltlvisit/contain.hh,
src/ltlvisit/exclusive.cc, src/taalgos/emptinessta.hh,
src/tgbatest/checkpsl.cc, src/tgbatest/checkta.cc,
src/tgbatest/complementation.cc, src/tgbatest/emptchk.cc,
src/tgbatest/ltl2tgba.cc, src/tgbatest/ltlprod.cc,
src/tgbatest/randtgba.cc, src/tgbatest/taatgba.cc, src/twa/twa.cc,
src/twa/twagraph.hh, src/twa/twasafracomplement.cc,
wrap/python/spot_impl.i: Adjust.
This commit is contained in:
Alexandre Duret-Lutz 2015-04-22 17:52:27 +02:00
parent 703fbd0e99
commit de529df59f
159 changed files with 260 additions and 272 deletions

6
src/twaalgos/gtec/.gitignore vendored Normal file
View file

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

View file

@ -0,0 +1,39 @@
## -*- coding: utf-8 -*-
## Copyright (C) 2011, 2013, 2014 Laboratoire de Recherche et Developpement
## de l'Epita (LRDE).
## 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 3 of the License, or
## (at your option) any later version.
##
## Spot is distributed in the hope that it will be useful, but WITHOUT
## ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
## or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
## License for more details.
##
## You should have received a copy of the GNU General Public License
## along with this program. If not, see <http://www.gnu.org/licenses/>.
AM_CPPFLAGS = -I$(srcdir)/../.. -I../.. $(BUDDY_CPPFLAGS)
AM_CXXFLAGS = $(WARNING_CXXFLAGS)
gtecdir = $(pkgincludedir)/tgbaalgos/gtec
gtec_HEADERS = \
ce.hh \
gtec.hh \
sccstack.hh \
status.hh
noinst_LTLIBRARIES = libgtec.la
libgtec_la_SOURCES = \
ce.cc \
gtec.cc \
sccstack.cc \
status.cc

236
src/twaalgos/gtec/ce.cc Normal file
View file

@ -0,0 +1,236 @@
// -*- coding: utf-8 -*-
// Copyright (C) 2010, 2011, 2013, 2014, 2015 Laboratoire de Recherche et
// Développement de l'Epita (LRDE).
// Copyright (C) 2004, 2005 Laboratoire d'Informatique de Paris 6 (LIP6),
// département Systèmes Répartis Coopératifs (SRC), Université Pierre
// et Marie Curie.
//
// This file is part of Spot, a model checking library.
//
// Spot is free software; you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 3 of the License, or
// (at your option) any later version.
//
// Spot is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
// License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "ce.hh"
#include "twaalgos/bfssteps.hh"
#include "misc/hash.hh"
namespace spot
{
namespace
{
class shortest_path: public bfs_steps
{
public:
shortest_path(const state_set* t,
const std::shared_ptr<const couvreur99_check_status>& ecs,
couvreur99_check_result* r)
: bfs_steps(ecs->aut), target(t), ecs(ecs), r(r)
{
}
const state*
search(const state* start, tgba_run::steps& l)
{
return this->bfs_steps::search(filter(start), l);
}
const state*
filter(const state* s)
{
r->inc_ars_prefix_states();
auto i = ecs->h.find(s);
s->destroy();
// Ignore unknown states ...
if (i == ecs->h.end())
return nullptr;
// ... as well as dead states.
if (i->second == -1)
return nullptr;
return i->first;
}
bool
match(tgba_run::step&, const state* dest)
{
return target->find(dest) != target->end();
}
private:
state_set seen;
const state_set* target;
std::shared_ptr<const couvreur99_check_status> ecs;
couvreur99_check_result* r;
};
}
couvreur99_check_result::couvreur99_check_result
(const std::shared_ptr<const couvreur99_check_status>& ecs,
option_map o)
: emptiness_check_result(ecs->aut, o), ecs_(ecs)
{
}
unsigned
couvreur99_check_result::acss_states() const
{
int scc_root = ecs_->root.top().index;
unsigned count = 0;
for (auto i: ecs_->h)
if (i.second >= scc_root)
++count;
return count;
}
tgba_run_ptr
couvreur99_check_result::accepting_run()
{
run_ = std::make_shared<tgba_run>();
assert(!ecs_->root.empty());
// Compute an accepting cycle.
accepting_cycle();
// Compute the prefix: it's the shortest path from the initial
// state of the automata to any state of the cycle.
// Register all states from the cycle as target of the BFS.
state_set ss;
for (tgba_run::steps::const_iterator i = run_->cycle.begin();
i != run_->cycle.end(); ++i)
ss.insert(i->s);
shortest_path shpath(&ss, ecs_, this);
const state* prefix_start = ecs_->aut->get_init_state();
// There are two cases: either the initial state is already on
// the cycle, or it is not. If it is, we will have to rotate
// the cycle so it begins on this position. Otherwise we will shift
// the cycle so it begins on the state that follows the prefix.
// cycle_entry_point is that state.
const state* cycle_entry_point;
state_set::const_iterator ps = ss.find(prefix_start);
if (ps != ss.end())
{
// The initial state is on the cycle.
prefix_start->destroy();
cycle_entry_point = *ps;
}
else
{
// This initial state is outside the cycle. Compute the prefix.
cycle_entry_point = shpath.search(prefix_start, run_->prefix);
}
// Locate cycle_entry_point on the cycle.
tgba_run::steps::iterator cycle_ep_it;
for (cycle_ep_it = run_->cycle.begin();
cycle_ep_it != run_->cycle.end()
&& cycle_entry_point->compare(cycle_ep_it->s); ++cycle_ep_it)
continue;
assert(cycle_ep_it != run_->cycle.end());
// Now shift the cycle so it starts on cycle_entry_point.
run_->cycle.splice(run_->cycle.end(), run_->cycle,
run_->cycle.begin(), cycle_ep_it);
return run_;
}
void
couvreur99_check_result::accepting_cycle()
{
acc_cond::mark_t acc_to_traverse =
ecs_->aut->acc().accepting_sets(ecs_->root.top().condition);
// Compute an accepting cycle using successive BFS that are
// restarted from the point reached after we have discovered a
// transition with a new acceptance conditions.
//
// This idea is taken from Product<T>::findWitness in LBTT 1.1.2,
// which in turn is probably inspired from
// @Article{ latvala.00.fi,
// author = {Timo Latvala and Keijo Heljanko},
// title = {Coping With Strong Fairness},
// journal = {Fundamenta Informaticae},
// year = {2000},
// volume = {43},
// number = {1--4},
// pages = {1--19},
// publisher = {IOS Press}
// }
const state* substart = ecs_->cycle_seed;
do
{
struct scc_bfs: bfs_steps
{
const couvreur99_check_status* ecs;
couvreur99_check_result* r;
acc_cond::mark_t& acc_to_traverse;
int scc_root;
scc_bfs(const couvreur99_check_status* ecs,
couvreur99_check_result* r, acc_cond::mark_t& acc_to_traverse)
: bfs_steps(ecs->aut), ecs(ecs), r(r),
acc_to_traverse(acc_to_traverse),
scc_root(ecs->root.top().index)
{
}
virtual const state*
filter(const state* s)
{
auto i = ecs->h.find(s);
s->destroy();
// Ignore unknown states.
if (i == ecs->h.end())
return 0;
// Stay in the final SCC.
if (i->second < scc_root)
return 0;
r->inc_ars_cycle_states();
return i->first;
}
virtual bool
match(tgba_run::step& st, const state* s)
{
acc_cond::mark_t less_acc =
acc_to_traverse - st.acc;
if (less_acc != acc_to_traverse
|| (acc_to_traverse == 0U
&& s == ecs->cycle_seed))
{
acc_to_traverse = less_acc;
return true;
}
return false;
}
} b(ecs_.get(), this, acc_to_traverse);
substart = b.search(substart, run_->cycle);
assert(substart);
}
while (acc_to_traverse != 0U || substart != ecs_->cycle_seed);
}
void
couvreur99_check_result::print_stats(std::ostream& os) const
{
ecs_->print_stats(os);
// FIXME: This is bogusly assuming run_ exists. (Even if we
// created it, the user might have deleted it.)
os << run_->prefix.size() << " states in run_->prefix" << std::endl;
os << run_->cycle.size() << " states in run_->cycle" << std::endl;
}
}

56
src/twaalgos/gtec/ce.hh Normal file
View file

@ -0,0 +1,56 @@
// -*- coding: utf-8 -*-
// Copyright (C) 2013, 2014 Laboratoire de Recherche et Développement de
// l'Epita (LRDE).
// Copyright (C) 2004, 2005 Laboratoire d'Informatique de Paris 6 (LIP6),
// département Systèmes Répartis Coopératifs (SRC), Université Pierre
// et Marie Curie.
//
// This file is part of Spot, a model checking library.
//
// Spot is free software; you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 3 of the License, or
// (at your option) any later version.
//
// Spot is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
// License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#pragma once
#include "status.hh"
#include "twaalgos/emptiness.hh"
#include "twaalgos/emptiness_stats.hh"
namespace spot
{
/// Compute a counter example from a spot::couvreur99_check_status
class SPOT_API couvreur99_check_result:
public emptiness_check_result,
public acss_statistics
{
public:
couvreur99_check_result(const
std::shared_ptr<const couvreur99_check_status>& ecs,
option_map o = option_map());
virtual tgba_run_ptr accepting_run();
void print_stats(std::ostream& os) const;
virtual unsigned acss_states() const;
protected:
/// Called by accepting_run() to find a cycle which traverses all
/// acceptance conditions in the accepted SCC.
void accepting_cycle();
private:
std::shared_ptr<const couvreur99_check_status> ecs_;
tgba_run_ptr run_;
};
}

617
src/twaalgos/gtec/gtec.cc Normal file
View file

@ -0,0 +1,617 @@
// -*- coding: utf-8 -*-
// Copyright (C) 2008, 2011, 2014, 2015 Laboratoire de Recherche et
// Développement de l'Epita (LRDE).
// Copyright (C) 2003, 2004, 2005, 2006 Laboratoire d'Informatique de
// Paris 6 (LIP6), département Systèmes Répartis Coopératifs (SRC),
// Université Pierre et Marie Curie.
//
// This file is part of Spot, a model checking library.
//
// Spot is free software; you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 3 of the License, or
// (at your option) any later version.
//
// Spot is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
// License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
// #define TRACE
#include <iostream>
#ifdef TRACE
#define trace std::cerr
#else
#define trace while (0) std::cerr
#endif
#include "gtec.hh"
#include "ce.hh"
#include "misc/memusage.hh"
namespace spot
{
namespace
{
typedef std::pair<const spot::state*, twa_succ_iterator*> pair_state_iter;
}
couvreur99_check::couvreur99_check(const const_twa_ptr& a, option_map o)
: emptiness_check(a, o),
removed_components(0)
{
poprem_ = o.get("poprem", 1);
ecs_ = std::make_shared<couvreur99_check_status>(a);
stats["removed components"] =
static_cast<spot::unsigned_statistics::unsigned_fun>
(&couvreur99_check::get_removed_components);
stats["vmsize"] =
static_cast<spot::unsigned_statistics::unsigned_fun>
(&couvreur99_check::get_vmsize);
}
couvreur99_check::~couvreur99_check()
{
}
unsigned
couvreur99_check::get_removed_components() const
{
return removed_components;
}
unsigned
couvreur99_check::get_vmsize() const
{
int size = memusage();
if (size > 0)
return size;
return 0;
}
void
couvreur99_check::remove_component(const state* from)
{
++removed_components;
// If rem has been updated, removing states is very easy.
if (poprem_)
{
assert(!ecs_->root.rem().empty());
dec_depth(ecs_->root.rem().size());
for (auto i: ecs_->root.rem())
ecs_->h[i] = -1;
// ecs_->root.rem().clear();
return;
}
// Remove from H all states which are reachable from state FROM.
// Stack of iterators towards states to remove.
std::stack<twa_succ_iterator*> to_remove;
// Remove FROM itself, and prepare to remove its successors.
// (FROM should be in H, otherwise it means all reachable
// states from FROM have already been removed and there is no
// point in calling remove_component.)
ecs_->h[from] = -1;
twa_succ_iterator* i = ecs_->aut->succ_iter(from);
for (;;)
{
// Remove each destination of this iterator.
if (i->first())
do
{
inc_transitions();
state* s = i->current_state();
auto j = ecs_->h.find(s);
assert(j != ecs_->h.end());
s->destroy();
if (j->second != -1)
{
j->second = -1;
to_remove.push(ecs_->aut->succ_iter(j->first));
}
}
while (i->next());
ecs_->aut->release_iter(i);
if (to_remove.empty())
break;
i = to_remove.top();
to_remove.pop();
}
}
emptiness_check_result_ptr
couvreur99_check::check()
{
{
auto acc = ecs_->aut->acc();
if (acc.get_acceptance().is_false())
return nullptr;
if (acc.uses_fin_acceptance())
throw std::runtime_error
("Fin acceptance is not supported by couvreur99()");
}
// We use five main data in this algorithm:
// * couvreur99_check::root, a stack of strongly connected components (SCC),
// * couvreur99_check::h, a hash of all visited nodes, with their order,
// (it is called "Hash" in Couvreur's paper)
// * arc, a stack of acceptance conditions between each of these SCC,
std::stack<acc_cond::mark_t> arc;
// * num, the number of visited nodes. Used to set the order of each
// visited node,
int num = 1;
// * todo, the depth-first search stack. This holds pairs of the
// form (STATE, ITERATOR) where ITERATOR is a twa_succ_iterator
// over the successors of STATE. In our use, ITERATOR should
// always be freed when TODO is popped, but STATE should not because
// it is also used as a key in H.
std::stack<pair_state_iter> todo;
// Setup depth-first search from the initial state.
{
state* init = ecs_->aut->get_init_state();
ecs_->h[init] = 1;
ecs_->root.push(1);
arc.push(0U);
twa_succ_iterator* iter = ecs_->aut->succ_iter(init);
iter->first();
todo.emplace(init, iter);
inc_depth();
}
while (!todo.empty())
{
assert(ecs_->root.size() == arc.size());
// We are looking at the next successor in SUCC.
twa_succ_iterator* succ = todo.top().second;
// If there is no more successor, backtrack.
if (succ->done())
{
// We have explored all successors of state CURR.
const state* curr = todo.top().first;
// Backtrack TODO.
todo.pop();
dec_depth();
// If poprem is used, fill rem with any component removed,
// so that remove_component() does not have to traverse
// the SCC again.
auto i = ecs_->h.find(curr);
assert(i != ecs_->h.end());
if (poprem_)
{
ecs_->root.rem().push_front(i->first);
inc_depth();
}
// When backtracking the root of an SCC, we must also
// remove that SCC from the ARC/ROOT stacks. We must
// discard from H all reachable states from this SCC.
assert(!ecs_->root.empty());
if (ecs_->root.top().index == i->second)
{
assert(!arc.empty());
arc.pop();
remove_component(curr);
ecs_->root.pop();
}
ecs_->aut->release_iter(succ);
// Do not destroy CURR: it is a key in H.
continue;
}
// We have a successor to look at.
inc_transitions();
// Fetch the values (destination state, acceptance conditions
// of the arc) we are interested in...
const state* dest = succ->current_state();
acc_cond::mark_t acc = succ->current_acceptance_conditions();
// ... and point the iterator to the next successor, for
// the next iteration.
succ->next();
// We do not need SUCC from now on.
// Are we going to a new state?
auto p = ecs_->h.emplace(dest, num + 1);
if (p.second)
{
// Yes. Bump number, stack the stack, and register its
// successors for later processing.
ecs_->root.push(++num);
arc.push(acc);
twa_succ_iterator* iter = ecs_->aut->succ_iter(dest);
iter->first();
todo.emplace(dest, iter);
inc_depth();
continue;
}
dest->destroy();
// If we have reached a dead component, ignore it.
if (p.first->second == -1)
continue;
// Now this is the most interesting case. We have reached a
// state S1 which is already part of a non-dead SCC. Any such
// non-dead SCC has necessarily been crossed by our path to
// this state: there is a state S2 in our path which belongs
// to this SCC too. We are going to merge all states between
// this S1 and S2 into this SCC.
//
// This merge is easy to do because the order of the SCC in
// ROOT is ascending: we just have to merge all SCCs from the
// top of ROOT that have an index greater to the one of
// the SCC of S2 (called the "threshold").
int threshold = p.first->second;
std::list<const state*> rem;
while (threshold < ecs_->root.top().index)
{
assert(!ecs_->root.empty());
assert(!arc.empty());
acc |= ecs_->root.top().condition;
acc |= arc.top();
rem.splice(rem.end(), ecs_->root.rem());
ecs_->root.pop();
arc.pop();
}
// Note that we do not always have
// threshold == ecs_->root.top().index
// after this loop, the SCC whose index is threshold might have
// been merged with a lower SCC.
// Accumulate all acceptance conditions into the merged SCC.
ecs_->root.top().condition |= acc;
ecs_->root.rem().splice(ecs_->root.rem().end(), rem);
if (ecs_->aut->acc().accepting(ecs_->root.top().condition))
{
// We have found an accepting SCC.
// Release all iterators in TODO.
while (!todo.empty())
{
ecs_->aut->release_iter(todo.top().second);
todo.pop();
dec_depth();
}
// Use this state to start the computation of an accepting
// cycle.
ecs_->cycle_seed = p.first->first;
set_states(ecs_->states());
return std::make_shared<couvreur99_check_result>(ecs_, options());
}
}
// This automaton recognizes no word.
set_states(ecs_->states());
return nullptr;
}
std::shared_ptr<const couvreur99_check_status>
couvreur99_check::result() const
{
return ecs_;
}
std::ostream&
couvreur99_check::print_stats(std::ostream& os) const
{
ecs_->print_stats(os);
os << transitions() << " transitions explored" << std::endl;
os << max_depth() << " items max in DFS search stack" << std::endl;
return os;
}
//////////////////////////////////////////////////////////////////////
couvreur99_check_shy::todo_item::todo_item(const state* s, int n,
couvreur99_check_shy* shy)
: s(s), n(n)
{
for (auto iter: shy->ecs_->aut->succ(s))
{
q.emplace_back(iter->current_acceptance_conditions(),
iter->current_state());
shy->inc_depth();
shy->inc_transitions();
}
}
couvreur99_check_shy::couvreur99_check_shy(const const_twa_ptr& a,
option_map o)
: couvreur99_check(a, o), num(1)
{
group_ = o.get("group", 1);
group2_ = o.get("group2", 0);
group_ |= group2_;
// Setup depth-first search from the initial state.
const state* i = ecs_->aut->get_init_state();
ecs_->h[i] = ++num;
ecs_->root.push(num);
todo.emplace_back(i, num, this);
inc_depth(1);
}
couvreur99_check_shy::~couvreur99_check_shy()
{
}
void
couvreur99_check_shy::clear_todo()
{
// We must destroy all states appearing in TODO
// unless they are used as keys in H.
while (!todo.empty())
{
succ_queue& queue = todo.back().q;
for (auto& q: queue)
{
// Destroy the state if it is a clone of a state in the
// heap or if it is an unknown state.
auto i = ecs_->h.find(q.s);
if (i == ecs_->h.end() || i->first != q.s)
q.s->destroy();
}
dec_depth(todo.back().q.size() + 1);
todo.pop_back();
}
dec_depth(ecs_->root.clear_rem());
assert(depth() == 0);
}
void
couvreur99_check_shy::dump_queue(std::ostream& os)
{
os << "--- TODO ---\n";
unsigned pos = 0;
for (auto& ti: todo)
{
++pos;
os << '#' << pos << " s:" << ti.s << " n:" << ti.n
<< " q:{";
for (auto qi = ti.q.begin(); qi != ti.q.end();)
{
os << qi->s;
++qi;
if (qi != ti.q.end())
os << ", ";
}
os << "}\n";
}
}
emptiness_check_result_ptr
couvreur99_check_shy::check()
{
{
auto acc = ecs_->aut->acc();
if (acc.get_acceptance().is_false())
return nullptr;
if (acc.uses_fin_acceptance())
throw std::runtime_error
("Fin acceptance is not supported by couvreur99()");
}
// Position in the loop seeking known successors.
pos = todo.back().q.begin();
for (;;)
{
#ifdef TRACE
dump_queue();
#endif
assert(ecs_->root.size() == 1 + arc.size());
// Get the successors of the current state.
succ_queue& queue = todo.back().q;
// If there is no more successor, backtrack.
if (queue.empty())
{
trace << "backtrack" << std::endl;
// We have explored all successors of state CURR.
const state* curr = todo.back().s;
int index = todo.back().n;
// Backtrack TODO.
todo.pop_back();
dec_depth();
if (todo.empty())
{
// This automaton recognizes no word.
set_states(ecs_->states());
assert(poprem_ || depth() == 0);
return nullptr;
}
pos = todo.back().q.begin();
// If poprem is used, fill rem with any component removed,
// so that remove_component() does not have to traverse
// the SCC again.
if (poprem_)
{
auto i = ecs_->h.find(curr);
assert(i != ecs_->h.end());
assert(i->first == curr);
ecs_->root.rem().push_front(i->first);
inc_depth();
}
// When backtracking the root of an SCC, we must also
// remove that SCC from the ARC/ROOT stacks. We must
// discard from H all reachable states from this SCC.
assert(!ecs_->root.empty());
if (ecs_->root.top().index == index)
{
assert(!arc.empty());
arc.pop();
remove_component(curr);
ecs_->root.pop();
}
continue;
}
// We always make a first pass over the successors of a state
// to check whether it contains some state we have already seen.
// This way we hope to merge the most SCCs before stacking new
// states.
//
// So are we checking for known states ? If yes, POS tells us
// which state we are considering. Otherwise just pick the
// first one.
succ_queue::iterator old;
if (pos == queue.end())
old = queue.begin();
else
old = pos;
if (pos != queue.end())
++pos;
//int* i = sip.second;
successor succ = *old;
trace << "picked state " << succ.s << '\n';
auto i = ecs_->h.find(succ.s);
if (i == ecs_->h.end())
{
// It's a new state.
// If we are seeking known states, just skip it.
if (pos != queue.end())
continue;
trace << "new state\n";
// Otherwise, number it and stack it so we recurse.
queue.erase(old);
dec_depth();
ecs_->h[succ.s] = ++num;
ecs_->root.push(num);
arc.push(succ.acc);
todo.emplace_back(succ.s, num, this);
pos = todo.back().q.begin();
inc_depth();
continue;
}
// It's an known state. Use i->first from now on.
succ.s->destroy();
queue.erase(old);
dec_depth();
// Skip dead states.
if (i->second == -1)
{
trace << "dead state\n";
continue;
}
trace << "merging...\n";
// Now this is the most interesting case. We have
// reached a state S1 which is already part of a
// non-dead SCC. Any such non-dead SCC has
// necessarily been crossed by our path to this
// state: there is a state S2 in our path which
// belongs to this SCC too. We are going to merge
// all states between this S1 and S2 into this
// SCC.
//
// This merge is easy to do because the order of
// the SCC in ROOT is ascending: we just have to
// merge all SCCs from the top of ROOT that have
// an index greater to the one of the SCC of S2
// (called the "threshold").
int threshold = i->second;
std::list<const state*> rem;
acc_cond::mark_t acc = succ.acc;
while (threshold < ecs_->root.top().index)
{
assert(!ecs_->root.empty());
assert(!arc.empty());
acc |= ecs_->root.top().condition;
acc |= arc.top();
rem.splice(rem.end(), ecs_->root.rem());
ecs_->root.pop();
arc.pop();
}
// Note that we do not always have
// threshold == ecs_->root.top().index
// after this loop, the SCC whose index is threshold
// might have been merged with a lower SCC.
// Accumulate all acceptance conditions into the
// merged SCC.
ecs_->root.top().condition |= acc;
ecs_->root.rem().splice(ecs_->root.rem().end(), rem);
// Have we found all acceptance conditions?
if (ecs_->aut->acc().accepting(ecs_->root.top().condition))
{
// Use this state to start the computation of an accepting
// cycle.
ecs_->cycle_seed = i->first;
// We have found an accepting SCC. Clean up TODO.
clear_todo();
set_states(ecs_->states());
return std::make_shared<couvreur99_check_result>(ecs_, options());
}
// Group the pending successors of formed SCC if requested.
if (group_)
{
assert(todo.back().s);
while (ecs_->root.top().index < todo.back().n)
{
todo_list::reverse_iterator prev = todo.rbegin();
todo_list::reverse_iterator last = prev++;
// If group2 is used we insert the last->q in front
// of prev->q so that the states in prev->q are checked
// for existence again after we have processed the states
// of last->q. Otherwise we just append to the end.
prev->q.splice(group2_ ? prev->q.begin() : prev->q.end(),
last->q);
if (poprem_)
{
const state* s = todo.back().s;
auto i = ecs_->h.find(s);
assert(i != ecs_->h.end());
assert(i->first == s);
ecs_->root.rem().push_front(i->first);
// Don't change the stack depth, since
// we are just moving the state from TODO to REM.
}
else
{
dec_depth();
}
todo.pop_back();
}
pos = todo.back().q.begin();
}
}
}
emptiness_check_ptr
couvreur99(const const_twa_ptr& a, option_map o)
{
if (o.get("shy"))
return std::make_shared<couvreur99_check_shy>(a, o);
return std::make_shared<couvreur99_check>(a, o);
}
}

244
src/twaalgos/gtec/gtec.hh Normal file
View file

@ -0,0 +1,244 @@
// -*- coding: utf-8 -*-
// Copyright (C) 2008, 2013, 2014, 2015 Laboratoire de Recherche et
// Développement de l'Epita (LRDE).
// Copyright (C) 2003, 2004, 2005, 2006 Laboratoire d'Informatique de
// Paris 6 (LIP6), département Systèmes Répartis Coopératifs (SRC),
// Université Pierre et Marie Curie.
//
// This file is part of Spot, a model checking library.
//
// Spot is free software; you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 3 of the License, or
// (at your option) any later version.
//
// Spot is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
// License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#pragma once
#include <stack>
#include "status.hh"
#include "twaalgos/emptiness.hh"
#include "twaalgos/emptiness_stats.hh"
namespace spot
{
/// \addtogroup emptiness_check_algorithms
/// @{
/// \brief Check whether the language of an automate is empty.
///
/// This is based on the following paper.
/** \verbatim
@InProceedings{couvreur.99.fm,
author = {Jean-Michel Couvreur},
title = {On-the-fly Verification of Temporal Logic},
pages = {253--271},
editor = {Jeannette M. Wing and Jim Woodcock and Jim Davies},
booktitle = {Proceedings of the World Congress on Formal Methods in
the Development of Computing Systems (FM'99)},
publisher = {Springer-Verlag},
series = {Lecture Notes in Computer Science},
volume = {1708},
year = {1999},
address = {Toulouse, France},
month = {September},
isbn = {3-540-66587-0}
}
\endverbatim */
///
/// A recursive definition of the algorithm would look as follows,
/// but the implementation is of course not recursive.
/// (<code>&lt;Sigma, Q, delta, q, F&gt;</code> is the automaton to
/// check, H is an associative array mapping each state to its
/// positive DFS order or 0 if it is dead, SCC is and ACC are two
/// stacks.)
///
/** \verbatim
check(<Sigma, Q, delta, q, F>, H, SCC, ACC)
if q is not in H // new state
H[q] = H.size + 1
SCC.push(<H[q], {}>)
forall <a, s> : <q, _, a, s> in delta
ACC.push(a)
res = check(<Sigma, Q, delta, s, F>, H, SCC, ACC)
if res
return res
<n, _> = SCC.top()
if n = H[q]
SCC.pop()
mark_reachable_states_as_dead(<Sigma, Q, delta, q, F>, H$)
return 0
else
if H[q] = 0 // dead state
ACC.pop()
return true
else // state in stack: merge SCC
all = {}
do
<n, a> = SCC.pop()
all = all union a union { ACC.pop() }
until n <= H[q]
SCC.push(<n, all>)
if all != F
return 0
return new emptiness_check_result(necessary data)
\endverbatim */
///
/// check() returns 0 iff the automaton's language is empty. It
/// returns an instance of emptiness_check_result. If the automaton
/// accept a word. (Use emptiness_check_result::accepting_run() to
/// extract an accepting run.)
///
/// There are two variants of this algorithm: spot::couvreur99_check and
/// spot::couvreur99_check_shy. They differ in their memory usage, the
/// number for successors computed before they are used and the way
/// the depth first search is directed.
///
/// spot::couvreur99_check performs a straightforward depth first search.
/// The DFS stacks store twa_succ_iterators, so that only the
/// iterators which really are explored are computed.
///
/// spot::couvreur99_check_shy tries to explore successors which are
/// visited states first. this helps to merge SCCs and generally
/// helps to produce shorter counter-examples. However this
/// algorithm cannot stores unprocessed successors as
/// twa_succ_iterators: it must compute all successors of a state
/// at once in order to decide which to explore first, and must keep
/// a list of all unexplored successors in its DFS stack.
///
/// The couvreur99() function is a wrapper around these two flavors
/// of the algorithm. \a options is an option map that specifies
/// which algorithms should be used, and how.
///
/// The following options are available.
/// \li \c "shy" : if non zero, then spot::couvreur99_check_shy is used,
/// otherwise (and by default) spot::couvreur99_check is used.
///
/// \li \c "poprem" : specifies how the algorithm should handle the
/// destruction of non-accepting maximal strongly connected
/// components. If \c poprem is non null, the algorithm will keep a
/// list of all states of a SCC that are fully processed and should
/// be removed once the MSCC is popped. If \c poprem is null (the
/// default), the MSCC will be traversed again (i.e. generating the
/// successors of the root recursively) for deletion. This is a
/// choice between memory and speed.
///
/// \li \c "group" : this options is used only by spot::couvreur99_check_shy.
/// If non null (the default), the successors of all the
/// states that belong to the same SCC will be considered when
/// choosing a successor. Otherwise, only the successor of the
/// topmost state on the DFS stack are considered.
SPOT_API emptiness_check_ptr
couvreur99(const const_twa_ptr& a, option_map options = option_map());
#ifndef SWIG
/// \brief An implementation of the Couvreur99 emptiness-check algorithm.
///
/// See the documentation for spot::couvreur99.
class SPOT_API couvreur99_check: public emptiness_check, public ec_statistics
{
public:
couvreur99_check(const const_twa_ptr& a, option_map o = option_map());
virtual ~couvreur99_check();
/// Check whether the automaton's language is empty.
virtual emptiness_check_result_ptr check();
virtual std::ostream& print_stats(std::ostream& os) const;
/// \brief Return the status of the emptiness-check.
///
/// When check() succeed, the status should be passed along
/// to spot::counter_example.
///
/// This status should not be deleted, it is a pointer
/// to a member of this class that will be deleted when
/// the couvreur99 object is deleted.
std::shared_ptr<const couvreur99_check_status> result() const;
protected:
std::shared_ptr<couvreur99_check_status> ecs_;
/// \brief Remove a strongly component from the hash.
///
/// This function remove all accessible state from a given
/// state. In other words, it removes the strongly connected
/// component that contains this state.
void remove_component(const state* start_delete);
/// Whether to store the state to be removed.
bool poprem_;
/// Number of dead SCC removed by the algorithm.
unsigned removed_components;
unsigned get_removed_components() const;
unsigned get_vmsize() const;
};
/// \brief A version of spot::couvreur99_check that tries to visit
/// known states first.
///
/// See the documentation for spot::couvreur99.
class SPOT_API couvreur99_check_shy final: public couvreur99_check
{
public:
couvreur99_check_shy(const const_twa_ptr& a, option_map o = option_map());
virtual ~couvreur99_check_shy();
virtual emptiness_check_result_ptr check();
protected:
struct successor {
acc_cond::mark_t acc;
const spot::state* s;
successor(acc_cond::mark_t acc, const spot::state* s): acc(acc), s(s) {}
};
// We use five main data in this algorithm:
// * couvreur99_check::root, a stack of strongly connected components (SCC),
// * couvreur99_check::h, a hash of all visited nodes, with their order,
// (it is called "Hash" in Couvreur's paper)
// * arc, a stack of acceptance conditions between each of these SCC,
std::stack<acc_cond::mark_t> arc;
// * num, the number of visited nodes. Used to set the order of each
// visited node,
int num;
// * todo, the depth-first search stack. This holds pairs of the
// form (STATE, SUCCESSORS) where SUCCESSORS is a list of
// (ACCEPTANCE_CONDITIONS, STATE) pairs.
typedef std::list<successor> succ_queue;
// Position in the loop seeking known successors.
succ_queue::iterator pos;
struct todo_item
{
const state* s;
int n;
succ_queue q; // Unprocessed successors of S
todo_item(const state* s, int n, couvreur99_check_shy* shy);
};
typedef std::list<todo_item> todo_list;
todo_list todo;
void clear_todo();
/// Dump the queue for debugging.
void dump_queue(std::ostream& os = std::cerr);
/// Whether successors should be grouped for states in the same SCC.
bool group_;
// If the "group2" option is set (it implies "group"), we
// reprocess the successor states of SCC that have been merged.
bool group2_;
};
#endif
/// @}
}

View file

@ -0,0 +1,90 @@
// -*- coding: utf-8 -*-
// Copyright (C) 2014 Laboratoire de Recherche et Developpement de
// l'Epita (LRDE).
// Copyright (C) 2004, 2005 Laboratoire d'Informatique de Paris 6 (LIP6),
// département Systèmes Répartis Coopératifs (SRC), Université Pierre
// et Marie Curie.
//
// This file is part of Spot, a model checking library.
//
// Spot is free software; you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 3 of the License, or
// (at your option) any later version.
//
// Spot is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
// License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "sccstack.hh"
namespace spot
{
scc_stack::connected_component::connected_component(int i)
{
index = i;
condition = 0U;
}
scc_stack::connected_component&
scc_stack::top()
{
return s.front();
}
const scc_stack::connected_component&
scc_stack::top() const
{
return s.front();
}
void
scc_stack::pop()
{
// assert(rem().empty());
s.pop_front();
}
void
scc_stack::push(int index)
{
s.emplace_front(index);
}
std::list<const state*>&
scc_stack::rem()
{
return top().rem;
}
size_t
scc_stack::size() const
{
return s.size();
}
bool
scc_stack::empty() const
{
return s.empty();
}
unsigned
scc_stack::clear_rem()
{
unsigned n = 0;
for (stack_type::iterator i = s.begin(); i != s.end(); ++i)
{
n += i->rem.size();
i->rem.clear();
}
return n;
}
}

View file

@ -0,0 +1,79 @@
// -*- coding: utf-8 -*-
// Copyright (C) 2013, 2014 Laboratoire de Recherche et Développement de
// l'Epita (LRDE).
// Copyright (C) 2004, 2005 Laboratoire d'Informatique de Paris 6 (LIP6),
// département Systèmes Répartis Coopératifs (SRC), Université Pierre
// et Marie Curie.
//
// This file is part of Spot, a model checking library.
//
// Spot is free software; you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 3 of the License, or
// (at your option) any later version.
//
// Spot is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
// License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#pragma once
#include <bddx.h>
#include <list>
#include "twa/twa.hh"
namespace spot
{
// A stack of Strongly-Connected Components, as needed by the
// Tarjan-Couvreur algorithm.
class SPOT_API scc_stack
{
public:
struct connected_component
{
public:
connected_component(int index = -1);
/// Index of the SCC.
int index;
/// The union of all acceptance marks of transitions the
/// connected component.
acc_cond::mark_t condition;
std::list<const state*> rem;
};
/// Stack a new SCC with index \a index.
void push(int index);
/// Access the top SCC.
connected_component& top();
/// Access the top SCC.
const connected_component& top() const;
/// Pop the top SCC.
void pop();
/// How many SCC are in stack.
size_t size() const;
/// The \c rem member of the top SCC.
std::list<const state*>& rem();
/// Purge all \c rem members.
///
/// \return the number of elements cleared.
unsigned clear_rem();
/// Is the stack empty?
bool empty() const;
typedef std::list<connected_component> stack_type;
stack_type s;
};
}

View file

@ -0,0 +1,58 @@
// -*- coding: utf-8 -*-
// Copyright (C) 2014 Laboratoire de Recherche et Développement de
// l'Epita (LRDE).
// 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 3 of the License, or
// (at your option) any later version.
//
// Spot is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
// License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#include <ostream>
#include "status.hh"
namespace spot
{
couvreur99_check_status::couvreur99_check_status(const const_twa_ptr& aut)
: aut(aut)
{
}
couvreur99_check_status::~couvreur99_check_status()
{
hash_type::iterator i = h.begin();
while (i != h.end())
{
// Advance the iterator before deleting the key.
const state* s = i->first;
++i;
s->destroy();
}
}
void
couvreur99_check_status::print_stats(std::ostream& os) const
{
os << h.size() << " unique states visited" << std::endl;
os << root.size()
<< " strongly connected components in search stack\n";
}
int
couvreur99_check_status::states() const
{
return h.size();
}
}

View file

@ -0,0 +1,58 @@
// -*- coding: utf-8 -*-
// Copyright (C) 2013, 2014 Laboratoire de Recherche et Développement
// de l'Epita (LRDE).
// 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 3 of the License, or
// (at your option) any later version.
//
// Spot is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
// or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public
// License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#pragma once
#include "sccstack.hh"
#include "twa/twa.hh"
#include <iosfwd>
namespace spot
{
/// \brief The status of the emptiness-check on success.
///
/// This contains everything needed to construct a counter-example:
/// the automata, the stack of SCCs traversed by the counter-example,
/// and the heap of visited states with their indexes.
class SPOT_API couvreur99_check_status
{
public:
couvreur99_check_status(const const_twa_ptr& aut);
~couvreur99_check_status();
const_twa_ptr aut;
scc_stack root;
typedef std::unordered_map<const state*, int,
state_ptr_hash, state_ptr_equal> hash_type;
hash_type h;
const state* cycle_seed;
/// Output statistics about this object.
void print_stats(std::ostream& os) const;
/// Return the number of states visited by the search
int states() const;
};
}