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

Use shared_ptr for the emptiness check interfaces.

At the same time, this adds a is_empty() method to the tgba class,
simplifying many places that ran emptiness checks.

* iface/dve2/dve2check.cc, src/bin/ltlcross.cc,
src/dstarparse/dra2ba.cc, src/ltlvisit/contain.cc, src/tgba/tgba.cc,
src/tgba/tgba.hh, src/tgbaalgos/emptiness.cc,
src/tgbaalgos/emptiness.hh, src/tgbaalgos/gtec/ce.cc,
src/tgbaalgos/gtec/ce.hh, src/tgbaalgos/gtec/gtec.cc,
src/tgbaalgos/gtec/gtec.hh, src/tgbaalgos/gv04.cc,
src/tgbaalgos/gv04.hh, src/tgbaalgos/magic.cc,
src/tgbaalgos/magic.hh, src/tgbaalgos/minimize.cc,
src/tgbaalgos/ndfs_result.hxx, src/tgbaalgos/powerset.cc,
src/tgbaalgos/projrun.cc, src/tgbaalgos/projrun.hh,
src/tgbaalgos/reducerun.cc, src/tgbaalgos/reducerun.hh,
src/tgbaalgos/replayrun.cc, src/tgbaalgos/replayrun.hh,
src/tgbaalgos/rundotdec.cc, src/tgbaalgos/rundotdec.hh,
src/tgbaalgos/se05.cc, src/tgbaalgos/se05.hh,
src/tgbaalgos/tau03.cc, src/tgbaalgos/tau03.hh,
src/tgbaalgos/tau03opt.cc, src/tgbaalgos/tau03opt.hh,
src/tgbaalgos/word.cc, src/tgbaalgos/word.hh,
src/tgbatest/checkpsl.cc, src/tgbatest/complementation.cc,
src/tgbatest/emptchk.cc, src/tgbatest/ltl2tgba.cc,
src/tgbatest/randtgba.cc, wrap/python/ajax/spot.in,
wrap/python/spot.i: Use shared_ptr.
parent 803e17bb
......@@ -157,16 +157,16 @@ checked_main(int argc, char **argv)
spot::ltl::atomic_prop_set ap;
auto dict = spot::make_bdd_dict();
spot::const_kripke_ptr model = 0;
spot::const_tgba_ptr prop = 0;
spot::const_tgba_ptr product = 0;
spot::emptiness_check_instantiator* echeck_inst = 0;
spot::const_kripke_ptr model = nullptr;
spot::const_tgba_ptr prop = nullptr;
spot::const_tgba_ptr product = nullptr;
spot::emptiness_check_instantiator_ptr echeck_inst = nullptr;
int exit_code = 0;
spot::postprocessor post;
const spot::ltl::formula* deadf = 0;
const spot::ltl::formula* f = 0;
const spot::ltl::formula* deadf = nullptr;
const spot::ltl::formula* f = nullptr;
if (dead == 0 || !strcasecmp(dead, "true"))
if (!dead || !strcasecmp(dead, "true"))
{
deadf = spot::ltl::constant::true_instance();
}
......@@ -182,8 +182,7 @@ checked_main(int argc, char **argv)
if (output == EmptinessCheck)
{
const char* err;
echeck_inst =
spot::emptiness_check_instantiator::construct(echeck_algo, &err);
echeck_inst = spot::make_emptiness_check_instantiator(echeck_algo, &err);
if (!echeck_inst)
{
std::cerr << "Failed to parse argument of -e/-E near `"
......@@ -265,14 +264,14 @@ checked_main(int argc, char **argv)
assert(echeck_inst);
{
spot::emptiness_check* ec = echeck_inst->instantiate(product);
auto ec = echeck_inst->instantiate(product);
bool search_many = echeck_inst->options().get("repeated");
assert(ec);
do
{
int memused = spot::memusage();
tm.start("running emptiness check");
spot::emptiness_check_result* res;
spot::emptiness_check_result_ptr res;
try
{
res = ec->check();
......@@ -315,7 +314,7 @@ checked_main(int argc, char **argv)
else if (accepting_run)
{
spot::tgba_run* run;
spot::tgba_run_ptr run;
tm.start("computing accepting run");
try
{
......@@ -337,31 +336,24 @@ checked_main(int argc, char **argv)
else
{
tm.start("reducing accepting run");
spot::tgba_run* redrun =
spot::reduce_run(res->automaton(), run);
run = spot::reduce_run(res->automaton(), run);
tm.stop("reducing accepting run");
delete run;
run = redrun;
tm.start("printing accepting run");
spot::print_tgba_run(std::cout, product, run);
tm.stop("printing accepting run");
}
delete run;
}
else
{
std::cout << "an accepting run exists "
<< "(use -C to print it)" << std::endl;
}
delete res;
}
while (search_many);
delete ec;
}
safe_exit:
delete echeck_inst;
if (f)
f->destroy();
......
......@@ -1071,9 +1071,7 @@ namespace
size_t i, size_t j, bool icomp, bool jcomp)
{
auto prod = spot::product(aut_i, aut_j);
spot::emptiness_check* ec = spot::couvreur99(prod);
spot::emptiness_check_result* res = ec->check();
auto res = spot::couvreur99(prod)->check();
if (res)
{
std::ostream& err = global_error();
......@@ -1088,17 +1086,15 @@ namespace
err << "*N" << j;
err << " is nonempty";
spot::tgba_run* run = res->accepting_run();
auto run = res->accepting_run();
if (run)
{
const spot::tgba_run* runmin = reduce_run(prod, run);
delete run;
run = reduce_run(prod, run);
std::cerr << "; both automata accept the infinite word\n"
<< " ";
spot::tgba_word w(runmin);
spot::tgba_word w(run);
w.simplify();
w.print(example(), prod->get_dict()) << '\n';
delete runmin;
}
else
{
......@@ -1106,9 +1102,7 @@ namespace
}
end_error();
}
delete res;
delete ec;
return res;
return !!res;
}
static bool
......
......@@ -148,18 +148,10 @@ namespace spot
{
state_set keep(sl.begin(), sl.end());
auto masked = build_tgba_mask_keep(dra->aut, keep, sl.front());
emptiness_check* ec = couvreur99(nra_to_nba(dra, masked));
emptiness_check_result* ecr = ec->check();
delete ecr;
delete ec;
if (ecr)
{
// This SCC is not DBA-realizable.
//std::cerr << "not DBA-realizable\n";
return false;
}
if (!nra_to_nba(dra, masked)->is_empty())
// This SCC is not DBA-realizable.
return false;
}
//std::cerr << "non-accepting\n";
for (state_list::const_iterator i = sl.begin();
i != sl.end(); ++i)
nonfinal.push_back(*i);
......
......@@ -69,21 +69,10 @@ namespace spot
if (i != l->incompatible.end())
return i->second;
auto ec = couvreur99(product(l->translation, g->translation));
auto ecr = ec->check();
if (!ecr)
{
l->incompatible[g] = true;
g->incompatible[l] = true;
}
else
{
l->incompatible[g] = false;
g->incompatible[l] = false;
delete ecr;
}
delete ec;
return !ecr;
bool res = product(l->translation, g->translation)->is_empty();
l->incompatible[g] = res;
g->incompatible[l] = res;
return res;
}
......
......@@ -21,6 +21,7 @@
// along with this program. If not, see <http://www.gnu.org/licenses/>.
#include "tgba.hh"
#include "tgbaalgos/gtec/gtec.hh"
namespace spot
{
......@@ -85,4 +86,13 @@ namespace spot
return num_acc_;
}
bool
tgba::is_empty() const
{
// FIXME: This should be improved based on properties of the
// automaton. For instance we do not need couvreur99 is we know
// the automaton is weak.
return !couvreur99(shared_from_this())->check();
}
}
......@@ -27,7 +27,6 @@
#include "fwd.hh"
#include <cassert>
#include <memory>
#include <memory>
#include "misc/casts.hh"
#include "misc/hash.hh"
......@@ -465,7 +464,7 @@ namespace spot
/// we never represent transitions! Transition informations are
/// obtained by querying the iterator over the successors of
/// a state.
class SPOT_API tgba
class SPOT_API tgba: public std::enable_shared_from_this<tgba>
{
protected:
tgba();
......@@ -641,6 +640,8 @@ namespace spot
/// the other operand.
virtual bdd neg_acceptance_conditions() const = 0;
virtual bool is_empty() const;
protected:
/// Do the actual computation of tgba::support_conditions().
virtual bdd compute_support_conditions(const state* state) const = 0;
......
......@@ -77,8 +77,8 @@ namespace spot
std::ostream&
print_tgba_run(std::ostream& os,
const_tgba_ptr a,
const tgba_run* run)
const const_tgba_ptr& a,
const const_tgba_run_ptr& run)
{
bdd_dict_ptr d = a->get_dict();
os << "Prefix:" << std::endl;
......@@ -109,10 +109,10 @@ namespace spot
// emptiness_check_result
//////////////////////////////////////////////////////////////////////
tgba_run*
tgba_run_ptr
emptiness_check_result::accepting_run()
{
return 0;
return nullptr;
}
const unsigned_statistics*
......@@ -149,6 +149,12 @@ namespace spot
return dynamic_cast<const unsigned_statistics*>(this);
}
const ec_statistics*
emptiness_check::emptiness_check_statistics() const
{
return dynamic_cast<const ec_statistics*>(this);
}
const char*
emptiness_check::parse_options(char* options)
{
......@@ -180,29 +186,23 @@ namespace spot
namespace
{
emptiness_check*
make_couvreur99(const const_tgba_ptr& a, spot::option_map o)
{
return couvreur99(a, o);
}
struct ec_algo
{
const char* name;
spot::emptiness_check* (*construct)(const const_tgba_ptr&,
spot::option_map);
emptiness_check_ptr(*construct)(const const_tgba_ptr&,
spot::option_map);
unsigned int min_acc;
unsigned int max_acc;
};
ec_algo ec_algos[] =
{
{ "Cou99", make_couvreur99, 0, -1U },
{ "CVWY90", spot::magic_search, 0, 1 },
{ "GV04", spot::explicit_gv04_check, 0, 1 },
{ "SE05", spot::se05, 0, 1 },
{ "Tau03", spot::explicit_tau03_search, 1, -1U },
{ "Tau03_opt", spot::explicit_tau03_opt_search, 0, -1U },
{ "Cou99", couvreur99, 0, -1U },
{ "CVWY90", magic_search, 0, 1 },
{ "GV04", explicit_gv04_check, 0, 1 },
{ "SE05", se05, 0, 1 },
{ "Tau03", explicit_tau03_search, 1, -1U },
{ "Tau03_opt", explicit_tau03_opt_search, 0, -1U },
};
}
......@@ -224,14 +224,14 @@ namespace spot
return static_cast<ec_algo*>(info_)->max_acc;
}
emptiness_check*
emptiness_check_ptr
emptiness_check_instantiator::instantiate(const const_tgba_ptr& a) const
{
return static_cast<ec_algo*>(info_)->construct(a, o_);
}
emptiness_check_instantiator*
emptiness_check_instantiator::construct(const char* name, const char** err)
emptiness_check_instantiator_ptr
make_emptiness_check_instantiator(const char* name, const char** err)
{
// Skip spaces.
while (*name && strchr(" \t\n", *name))
......@@ -272,17 +272,26 @@ namespace spot
if (n == info->name)
{
*err = 0;
return new emptiness_check_instantiator(o, info);
struct emptiness_check_instantiator_aux:
public emptiness_check_instantiator
{
emptiness_check_instantiator_aux(option_map o, void* i):
emptiness_check_instantiator(o, i)
{
}
};
return std::make_shared<emptiness_check_instantiator_aux>(o, info);
}
*err = name;
return 0;
return nullptr;
}
// tgba_run_to_tgba
//////////////////////////////////////////////////////////////////////
tgba_digraph_ptr
tgba_run_to_tgba(const const_tgba_ptr& a, const tgba_run* run)
tgba_run_to_tgba(const const_tgba_ptr& a, const const_tgba_run_ptr& run)
{
auto d = a->get_dict();
auto res = make_tgba_digraph(d);
......
......@@ -34,6 +34,8 @@
namespace spot
{
struct tgba_run;
typedef std::shared_ptr<tgba_run> tgba_run_ptr;
typedef std::shared_ptr<const tgba_run> const_tgba_run_ptr;
/// \addtogroup emptiness_check Emptiness-checks
/// \ingroup tgba_algorithms
......@@ -99,7 +101,7 @@ namespace spot
/// cannot produce a counter example (that does not mean there
/// is no counter-example; the mere existence of an instance of
/// this class asserts the existence of a counter-example).
virtual tgba_run* accepting_run();
virtual tgba_run_ptr accepting_run();
/// The automaton on which an accepting_run() was found.
const const_tgba_ptr&
......@@ -129,8 +131,11 @@ namespace spot
option_map o_; ///< The options.
};
typedef std::shared_ptr<emptiness_check_result> emptiness_check_result_ptr;
/// Common interface to emptiness check algorithms.
class SPOT_API emptiness_check
class SPOT_API emptiness_check:
public std::enable_shared_from_this<emptiness_check>
{
public:
emptiness_check(const const_tgba_ptr& a, option_map o = option_map())
......@@ -173,11 +178,14 @@ namespace spot
/// Some emptiness_check algorithms, especially those using bit state
/// hashing may return 0 even if the automaton is not empty.
/// \see safe()
virtual emptiness_check_result* check() = 0;
virtual emptiness_check_result_ptr check() = 0;
/// Return statistics, if available.
virtual const unsigned_statistics* statistics() const;
/// Return emptiness check statistics, if available.
virtual const ec_statistics* emptiness_check_statistics() const;
/// Print statistics, if any.
virtual std::ostream& print_stats(std::ostream& os) const;
......@@ -189,25 +197,18 @@ namespace spot
option_map o_; ///< The options
};
typedef std::shared_ptr<emptiness_check> emptiness_check_ptr;
class emptiness_check_instantiator;
typedef std::shared_ptr<emptiness_check_instantiator>
emptiness_check_instantiator_ptr;
// Dynamically create emptiness checks. Given their name and options.
class SPOT_API emptiness_check_instantiator
{
public:
/// \brief Create an emptiness-check instantiator, given the name
/// of an emptiness check.
///
/// \a name should have the form \c "name" or \c "name(options)".
///
/// On error, the function returns 0. If the name of the algorithm
/// was unknown, \c *err will be set to \c name. If some fragment of
/// the options could not be parsed, \c *err will point to that
/// fragment.
static emptiness_check_instantiator* construct(const char* name,
const char** err);
/// Actually instantiate the emptiness check, for \a a.
emptiness_check* instantiate(const const_tgba_ptr& a) const;
emptiness_check_ptr instantiate(const const_tgba_ptr& a) const;
/// Accessor to the options.
/// @{
......@@ -233,14 +234,26 @@ namespace spot
///
/// \return \c -1U if no upper bound exists.
unsigned int max_acceptance_conditions() const;
private:
protected:
emptiness_check_instantiator(option_map o, void* i);
option_map o_;
void *info_;
};
/// @}
/// \brief Create an emptiness-check instantiator, given the name
/// of an emptiness check.
///
/// \a name should have the form \c "name" or \c "name(options)".
///
/// On error, the function returns 0. If the name of the algorithm
/// was unknown, \c *err will be set to \c name. If some fragment of
/// the options could not be parsed, \c *err will point to that
/// fragment.
SPOT_API emptiness_check_instantiator_ptr
make_emptiness_check_instantiator(const char* name, const char** err);
/// @}
/// \addtogroup emptiness_check_algorithms Emptiness-check algorithms
/// \ingroup emptiness_check
......@@ -287,14 +300,16 @@ namespace spot
/// actually exists in the automaton (and will also display any
/// transition annotation).
SPOT_API std::ostream&
print_tgba_run(std::ostream& os, const_tgba_ptr a, const tgba_run* run);
print_tgba_run(std::ostream& os,
const const_tgba_ptr& a,
const const_tgba_run_ptr& run);
/// \brief Return an explicit_tgba corresponding to \a run (i.e. comparable
/// states are merged).
///
/// \pre \a run must correspond to an actual run of the automaton \a a.
SPOT_API tgba_digraph_ptr
tgba_run_to_tgba(const const_tgba_ptr& a, const tgba_run* run);
tgba_run_to_tgba(const const_tgba_ptr& a, const const_tgba_run_ptr& run);
/// @}
......
......@@ -32,7 +32,7 @@ namespace spot
{
public:
shortest_path(const state_set* t,
const couvreur99_check_status* ecs,
const std::shared_ptr<const couvreur99_check_status>& ecs,
couvreur99_check_result* r)
: bfs_steps(ecs->aut), target(t), ecs(ecs), r(r)
{
......@@ -68,13 +68,14 @@ namespace spot
private:
state_set seen;
const state_set* target;
const couvreur99_check_status* ecs;
std::shared_ptr<const couvreur99_check_status> ecs;
couvreur99_check_result* r;
};
}
couvreur99_check_result::couvreur99_check_result
(const couvreur99_check_status* ecs, option_map o)
(const std::shared_ptr<const couvreur99_check_status>& ecs,
option_map o)
: emptiness_check_result(ecs->aut, o), ecs_(ecs)
{
}
......@@ -90,10 +91,10 @@ namespace spot
return count;
}
tgba_run*
tgba_run_ptr
couvreur99_check_result::accepting_run()
{
run_ = new tgba_run;
run_ = std::make_shared<tgba_run>();
assert(!ecs_->root.empty());
......@@ -212,7 +213,7 @@ namespace spot
return false;
}
} b(ecs_, this, acc_to_traverse);
} b(ecs_.get(), this, acc_to_traverse);
substart = b.search(substart, run_->cycle);
assert(substart);
......
// -*- coding: utf-8 -*-
// Copyright (C) 2013 Laboratoire de Recherche et Développement de
// 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
......@@ -35,10 +35,11 @@ namespace spot
public acss_statistics
{
public:
couvreur99_check_result(const couvreur99_check_status* ecs,
couvreur99_check_result(const
std::shared_ptr<const couvreur99_check_status>& ecs,
option_map o = option_map());
virtual tgba_run* accepting_run();
virtual tgba_run_ptr accepting_run();
void print_stats(std::ostream& os) const;
......@@ -50,8 +51,8 @@ namespace spot
void accepting_cycle();
private:
const couvreur99_check_status* ecs_;
tgba_run* run_;
std::shared_ptr<const couvreur99_check_status> ecs_;
tgba_run_ptr run_;
};
}
......
......@@ -45,7 +45,7 @@ namespace spot
removed_components(0)
{
poprem_ = o.get("poprem", 1);
ecs_ = new couvreur99_check_status(a);
ecs_ = std::make_shared<couvreur99_check_status>(a);
stats["removed components"] =
static_cast<spot::unsigned_statistics::unsigned_fun>
(&couvreur99_check::get_removed_components);
......@@ -56,7 +56,6 @@ namespace spot
couvreur99_check::~couvreur99_check()
{
delete ecs_;
}
unsigned
......@@ -129,7 +128,7 @@ namespace spot
}
}
emptiness_check_result*
emptiness_check_result_ptr
couvreur99_check::check()
{
// We use five main data in this algorithm:
......@@ -281,15 +280,15 @@ namespace spot
// cycle.
ecs_->cycle_seed = p.first->first;
set_states(ecs_->states());
return new couvreur99_check_result(ecs_, options());
return std::make_shared<couvreur99_check_result>(ecs_, options());
}
}
// This automaton recognizes no word.
set_states(ecs_->states());
return 0;
return nullptr;
}
const couvreur99_check_status*
std::shared_ptr<const couvreur99_check_status>
couvreur99_check::result() const
{
return ecs_;
......@@ -383,7 +382,7 @@ namespace spot
}
}
emptiness_check_result*
emptiness_check_result_ptr
couvreur99_check_shy::check()
{
// Position in the loop seeking known successors.
......@@ -418,7 +417,7 @@ namespace spot
// This automaton recognizes no word.
set_states(ecs_->states());
assert(poprem_ || depth() == 0);
return 0;
return nullptr;
}
pos = todo.back().q.begin();
......@@ -554,7 +553,7 @@ namespace spot
// We have found an accepting SCC. Clean up TODO.
clear_todo();
set_states(ecs_->states());
return new couvreur99_check_result(ecs_, options());
return std::make_shared<couvreur99_check_result>(ecs_, options());
}
// Group the pending successors of formed SCC if requested.
if (group_)
......@@ -592,12 +591,12 @@ namespace spot
}