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:
parent
703fbd0e99
commit
de529df59f
159 changed files with 260 additions and 272 deletions
6
src/twaalgos/gtec/.gitignore
vendored
Normal file
6
src/twaalgos/gtec/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
.deps
|
||||
.libs
|
||||
*.lo
|
||||
*.la
|
||||
Makefile
|
||||
Makefile.in
|
||||
39
src/twaalgos/gtec/Makefile.am
Normal file
39
src/twaalgos/gtec/Makefile.am
Normal 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
236
src/twaalgos/gtec/ce.cc
Normal 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
56
src/twaalgos/gtec/ce.hh
Normal 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
617
src/twaalgos/gtec/gtec.cc
Normal 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
244
src/twaalgos/gtec/gtec.hh
Normal 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><Sigma, Q, delta, q, F></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
|
||||
|
||||
/// @}
|
||||
}
|
||||
90
src/twaalgos/gtec/sccstack.cc
Normal file
90
src/twaalgos/gtec/sccstack.cc
Normal 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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
79
src/twaalgos/gtec/sccstack.hh
Normal file
79
src/twaalgos/gtec/sccstack.hh
Normal 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;
|
||||
};
|
||||
}
|
||||
58
src/twaalgos/gtec/status.cc
Normal file
58
src/twaalgos/gtec/status.cc
Normal 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();
|
||||
}
|
||||
}
|
||||
58
src/twaalgos/gtec/status.hh
Normal file
58
src/twaalgos/gtec/status.hh
Normal 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;
|
||||
};
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue