introduce output_aborter, and use it in ltlcross
* spot/twaalgos/alternation.cc, spot/twaalgos/alternation.hh, spot/twaalgos/complement.cc, spot/twaalgos/complement.hh, spot/twaalgos/determinize.cc, spot/twaalgos/determinize.hh, spot/twaalgos/minimize.cc, spot/twaalgos/minimize.hh, spot/twaalgos/postproc.cc, spot/twaalgos/postproc.hh, spot/twaalgos/powerset.cc, spot/twaalgos/powerset.hh, spot/twaalgos/product.cc, spot/twaalgos/product.hh: Use an output_aborter argument to abort if the output is too large. * bin/ltlcross.cc: Use complement() with an output_aborter so that ltlcross will not attempt to build complement larger than 500 states or 5000 edges. Add --determinize-max-states and --determinize-max-edges options. * tests/core/ltlcross3.test, tests/core/ltlcrossce2.test, tests/core/sccsimpl.test, tests/core/wdba2.test, tests/python/stutter-inv.ipynb: Adjust test cases. * NEWS: Document this. * bin/spot-x.cc: Add documentation for postprocessor's det-max-states and det-max-edges arguments. * doc/org/ltlcross.org: Update description.
This commit is contained in:
parent
5c3a33f720
commit
a85045091b
23 changed files with 568 additions and 287 deletions
179
bin/ltlcross.cc
179
bin/ltlcross.cc
|
|
@ -1,5 +1,5 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2012-2018 Laboratoire de Recherche et Développement
|
||||
// Copyright (C) 2012-2019 Laboratoire de Recherche et Développement
|
||||
// de l'Epita (LRDE).
|
||||
//
|
||||
// This file is part of Spot, a model checking library.
|
||||
|
|
@ -55,7 +55,7 @@
|
|||
#include <spot/twaalgos/sccinfo.hh>
|
||||
#include <spot/twaalgos/isweakscc.hh>
|
||||
#include <spot/twaalgos/word.hh>
|
||||
#include <spot/twaalgos/dualize.hh>
|
||||
#include <spot/twaalgos/complement.hh>
|
||||
#include <spot/twaalgos/cleanacc.hh>
|
||||
#include <spot/twaalgos/alternation.hh>
|
||||
#include <spot/misc/formater.hh>
|
||||
|
|
@ -86,6 +86,8 @@ enum {
|
|||
OPT_BOGUS,
|
||||
OPT_CSV,
|
||||
OPT_DENSITY,
|
||||
OPT_DET_MAX_STATES,
|
||||
OPT_DET_MAX_EDGES,
|
||||
OPT_DUPS,
|
||||
OPT_FAIL_ON_TIMEOUT,
|
||||
OPT_GRIND,
|
||||
|
|
@ -118,8 +120,18 @@ static const argp_option options[] =
|
|||
{ "no-complement", OPT_NOCOMP, nullptr, 0,
|
||||
"do not complement deterministic automata to perform extra checks", 0 },
|
||||
{ "determinize", 'D', nullptr, 0,
|
||||
"determinize non-deterministic automata so that they"
|
||||
"always determinize non-deterministic automata so that they"
|
||||
"can be complemented; also implicitly sets --products=0", 0 },
|
||||
{ "determinize-max-states", OPT_DET_MAX_STATES, "N", 0,
|
||||
"attempt to determinize non-deterministic automata so they can be "
|
||||
"complemented, unless the deterministic automaton would have more "
|
||||
"than N states. Without this option or -D, determinizations "
|
||||
"are attempted up to 500 states.", 0 },
|
||||
{ "determinize-max-edges", OPT_DET_MAX_EDGES, "N", 0,
|
||||
"attempt to determinize non-deterministic automata so they can be "
|
||||
"complemented, unless the deterministic automaton would have more "
|
||||
"than N edges. Without this option or -D, determinizations "
|
||||
"are attempted up to 5000 edges.", 0 },
|
||||
{ "stop-on-error", OPT_STOP_ERR, nullptr, 0,
|
||||
"stop on first execution error or failure to pass"
|
||||
" sanity checks (timeouts are OK)", 0 },
|
||||
|
|
@ -195,6 +207,10 @@ static bool allow_dups = false;
|
|||
static bool no_checks = false;
|
||||
static bool no_complement = false;
|
||||
static bool determinize = false;
|
||||
static bool max_det_states_given = false;
|
||||
static bool max_det_edges_given = false;
|
||||
static unsigned max_det_states = 500U;
|
||||
static unsigned max_det_edges = 5000U;
|
||||
static bool stop_on_error = false;
|
||||
static int seed = 0;
|
||||
static unsigned products = 1;
|
||||
|
|
@ -427,6 +443,28 @@ parse_opt(int key, char* arg, struct argp_state*)
|
|||
case 'D':
|
||||
determinize = true;
|
||||
products = 0;
|
||||
max_det_states = -1U;
|
||||
max_det_edges = -1U;
|
||||
if (max_det_states_given)
|
||||
error(2, 0, "Options --determinize-max-states and "
|
||||
"--determinize are incompatible.");
|
||||
if (max_det_edges_given)
|
||||
error(2, 0, "Options --determinize-max-edges and "
|
||||
"--determinize are incompatible.");
|
||||
break;
|
||||
case OPT_DET_MAX_EDGES:
|
||||
max_det_edges_given = true;
|
||||
max_det_states = to_pos_int(arg, "--determinize-max-edges");
|
||||
if (determinize)
|
||||
error(2, 0, "Options --determinize-max-edges and "
|
||||
"--determinize are incompatible.");
|
||||
break;
|
||||
case OPT_DET_MAX_STATES:
|
||||
max_det_states_given = true;
|
||||
max_det_states = to_pos_int(arg, "--determinize-max-states");
|
||||
if (determinize)
|
||||
error(2, 0, "Options --determinize-max-states and "
|
||||
"--determinize are incompatible.");
|
||||
break;
|
||||
case ARGP_KEY_ARG:
|
||||
if (arg[0] == '-' && !arg[1])
|
||||
|
|
@ -434,6 +472,9 @@ parse_opt(int key, char* arg, struct argp_state*)
|
|||
else
|
||||
tools_push_trans(arg);
|
||||
break;
|
||||
case OPT_AMBIGUOUS:
|
||||
opt_ambiguous = true;
|
||||
break;
|
||||
case OPT_AUTOMATA:
|
||||
opt_automata = true;
|
||||
break;
|
||||
|
|
@ -466,16 +507,6 @@ parse_opt(int key, char* arg, struct argp_state*)
|
|||
want_stats = true;
|
||||
json_output = arg ? arg : "-";
|
||||
break;
|
||||
case OPT_PRODUCTS:
|
||||
if (*arg == '+')
|
||||
{
|
||||
products_avg = false;
|
||||
++arg;
|
||||
}
|
||||
products = to_pos_int(arg, "--products");
|
||||
if (products == 0)
|
||||
products_avg = false;
|
||||
break;
|
||||
case OPT_NOCHECKS:
|
||||
no_checks = true;
|
||||
no_complement = true;
|
||||
|
|
@ -486,6 +517,16 @@ parse_opt(int key, char* arg, struct argp_state*)
|
|||
case OPT_OMIT:
|
||||
opt_omit = true;
|
||||
break;
|
||||
case OPT_PRODUCTS:
|
||||
if (*arg == '+')
|
||||
{
|
||||
products_avg = false;
|
||||
++arg;
|
||||
}
|
||||
products = to_pos_int(arg, "--products");
|
||||
if (products == 0)
|
||||
products_avg = false;
|
||||
break;
|
||||
case OPT_REFERENCE:
|
||||
tools_push_trans(arg, true);
|
||||
break;
|
||||
|
|
@ -501,9 +542,6 @@ parse_opt(int key, char* arg, struct argp_state*)
|
|||
case OPT_STRENGTH:
|
||||
opt_strength = true;
|
||||
break;
|
||||
case OPT_AMBIGUOUS:
|
||||
opt_ambiguous = true;
|
||||
break;
|
||||
case OPT_VERBOSE:
|
||||
verbose = true;
|
||||
break;
|
||||
|
|
@ -1094,6 +1132,8 @@ namespace
|
|||
}
|
||||
}
|
||||
|
||||
bool missing_complement = true;
|
||||
|
||||
if (!no_checks)
|
||||
{
|
||||
std::cerr << "Performing sanity checks and gathering statistics..."
|
||||
|
|
@ -1122,7 +1162,7 @@ namespace
|
|||
return smallest_ref;
|
||||
};
|
||||
|
||||
// This are not our definitive choice for reference
|
||||
// These are not our definitive choice for reference
|
||||
// automata, because the sizes might change after we remove
|
||||
// alternation and Fin acceptance. But we need to know now
|
||||
// if we will have a pair of reference automata in order to
|
||||
|
|
@ -1158,32 +1198,27 @@ namespace
|
|||
std::cerr << ")\n";
|
||||
}
|
||||
};
|
||||
auto complement = [&](const std::vector<spot::twa_graph_ptr>& x,
|
||||
std::vector<spot::twa_graph_ptr>& comp,
|
||||
unsigned i)
|
||||
{
|
||||
if (!no_complement && x[i] && is_universal(x[i]))
|
||||
comp[i] = dualize(x[i]);
|
||||
};
|
||||
|
||||
// Remove alternation
|
||||
for (size_t i = 0; i < m; ++i)
|
||||
{
|
||||
unalt(pos, i, 'P');
|
||||
unalt(neg, i, 'N');
|
||||
// Do not complement reference automata if we have a
|
||||
// reference pair.
|
||||
if (smallest_pos_ref >= 0 && tools[i].reference)
|
||||
continue;
|
||||
complement(pos, comp_pos, i);
|
||||
complement(neg, comp_neg, i);
|
||||
}
|
||||
|
||||
if (determinize && !no_complement)
|
||||
// Complement
|
||||
if (!no_complement)
|
||||
{
|
||||
spot::output_aborter aborter_(max_det_states,
|
||||
max_det_edges);
|
||||
spot::output_aborter* aborter = nullptr;
|
||||
if (max_det_states != -1U || max_det_edges != -1U)
|
||||
aborter = &aborter_;
|
||||
|
||||
print_first = verbose;
|
||||
auto tmp = [&](std::vector<spot::twa_graph_ptr>& from,
|
||||
std::vector<spot::twa_graph_ptr>& to, int i,
|
||||
char prefix)
|
||||
auto comp = [&](std::vector<spot::twa_graph_ptr>& from,
|
||||
std::vector<spot::twa_graph_ptr>& to, int i,
|
||||
char prefix)
|
||||
{
|
||||
if (from[i] && !to[i])
|
||||
{
|
||||
|
|
@ -1193,29 +1228,41 @@ namespace
|
|||
return;
|
||||
if (print_first)
|
||||
{
|
||||
std::cerr << "info: complementing non-deterministic "
|
||||
"automata via determinization...\n";
|
||||
std::cerr << "info: complementing automata...\n";
|
||||
print_first = false;
|
||||
}
|
||||
spot::postprocessor p;
|
||||
p.set_type(spot::postprocessor::Generic);
|
||||
p.set_pref(spot::postprocessor::Deterministic);
|
||||
p.set_level(spot::postprocessor::Low);
|
||||
to[i] = dualize(p.run(from[i]));
|
||||
if (verbose)
|
||||
std::cerr << "info: " << prefix << i;
|
||||
if (aborter && aborter->too_large(from[i])
|
||||
&& !spot::is_universal(from[i]))
|
||||
missing_complement = true;
|
||||
else
|
||||
to[i] = spot::complement(from[i], aborter);
|
||||
if (verbose)
|
||||
{
|
||||
std::cerr << "info: " << prefix << i << "\t(";
|
||||
printsize(from[i]);
|
||||
std::cerr << ") -> (";
|
||||
printsize(to[i]);
|
||||
std::cerr << ")\tComp(" << prefix << i << ")\n";
|
||||
if (to[i])
|
||||
{
|
||||
std::cerr << "\t(";
|
||||
printsize(from[i]);
|
||||
std::cerr << ") -> (";
|
||||
printsize(to[i]);
|
||||
std::cerr << ")\tComp(" << prefix << i << ")\n";
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cerr << "\tnot complemented";
|
||||
if (aborter)
|
||||
aborter->print_reason(std::cerr << " (") << ')';
|
||||
std::cerr << '\n';
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
missing_complement = false;
|
||||
for (unsigned i = 0; i < m; ++i)
|
||||
{
|
||||
tmp(pos, comp_pos, i, 'P');
|
||||
tmp(neg, comp_neg, i, 'N');
|
||||
comp(pos, comp_pos, i, 'P');
|
||||
comp(neg, comp_neg, i, 'N');
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1362,15 +1409,41 @@ namespace
|
|||
(*nstats)[i].product_scc.reserve(products);
|
||||
}
|
||||
}
|
||||
for (unsigned p = 0; p < products; ++p)
|
||||
// Decide if we need products with state-space.
|
||||
unsigned actual_products = products;
|
||||
if (actual_products)
|
||||
{
|
||||
if (missing_complement)
|
||||
{
|
||||
if (verbose)
|
||||
std::cerr
|
||||
<< ("info: complements not computed for some automata\ninfo: "
|
||||
"continuing with cross_checks and consistency_checks\n");
|
||||
}
|
||||
else if (want_stats)
|
||||
{
|
||||
if (verbose)
|
||||
std::cerr
|
||||
<< ("info: running cross_checks and consistency_checks"
|
||||
"just for statistics\n");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (verbose)
|
||||
std::cerr
|
||||
<< "info: cross_checks and consistency_checks unnecessary\n";
|
||||
actual_products = 0;
|
||||
}
|
||||
}
|
||||
for (unsigned p = 0; p < actual_products; ++p)
|
||||
{
|
||||
// build a random state-space.
|
||||
spot::srand(seed);
|
||||
|
||||
if (verbose)
|
||||
std::cerr << "info: building state-space #" << p << '/' << products
|
||||
<< " of " << states << " states with seed " << seed
|
||||
<< '\n';
|
||||
std::cerr << "info: building state-space #" << (p+1) << '/'
|
||||
<< products << " of " << states
|
||||
<< " states with seed " << seed << '\n';
|
||||
|
||||
auto statespace = spot::random_graph(states, density, ap, dict);
|
||||
|
||||
|
|
@ -1408,7 +1481,7 @@ namespace
|
|||
std::cerr << ("warning: not enough memory to build "
|
||||
"product of P") << i << " with state-space";
|
||||
if (products > 1)
|
||||
std::cerr << " #" << p << '/' << products << '\n';
|
||||
std::cerr << " #" << (p+1) << '/' << products << '\n';
|
||||
std::cerr << '\n';
|
||||
++oom_count;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -103,6 +103,14 @@ for the presence of an accepting self-loop.") },
|
|||
{ DOC("degen-remscc", "If non-zero (the default), make sure the output \
|
||||
of the degenalization has as many SCCs as the input, by removing superfluous \
|
||||
ones.") },
|
||||
{ DOC("det-max-states", "When defined to a positive integer N, \
|
||||
determinizations will be aborted whenever the number of generated \
|
||||
states would exceed N. In this case a non-deterministic automaton \
|
||||
will be returned.")},
|
||||
{ DOC("det-max-edges", "When defined to a positive integer N, \
|
||||
determinizations will be aborted whenever the number of generated \
|
||||
edges would exceed N. In this case a non-deterministic automaton \
|
||||
will be returned.")},
|
||||
{ DOC("det-scc", "Set to 0 to disable scc-based optimizations in \
|
||||
the determinization algorithm.") },
|
||||
{ DOC("det-simul", "Set to 0 to disable simulation-based optimizations in \
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue