bin: introduce autcross
Fixes #252. * NEWS: Mention it. * bin/autcross.cc, bin/man/autcross.x, doc/org/autcross.org: New files. * bin/Makefile.am, bin/man/Makefile.am, doc/org/tools.org, doc/Makefile.am: Add them. * bin/autfilt.cc: Use is_universal() instead of is_deterministic(). * bin/common_hoaread.hh, bin/common_trans.cc, bin/common_trans.hh, bin/ltlcross.cc, bin/ltldo.cc: Factor some bits common between ltlcross, ltldo and autcross. * tests/core/autcross.test, tests/core/autcross2.test: New files. * tests/Makefile.am: Add them. * tests/core/dra2dba.test, tests/core/sbacc.test, tests/core/streett.test: Use autcross.
This commit is contained in:
parent
b9fff6a4b1
commit
0cf250d839
21 changed files with 1726 additions and 53 deletions
11
NEWS
11
NEWS
|
|
@ -2,6 +2,14 @@ New in spot 2.3.5.dev (not yet released)
|
|||
|
||||
Tools:
|
||||
|
||||
- genaut is a new binary that produces families of automata defined
|
||||
in the literature (in the same way as we have genltl for LTL
|
||||
formulas).
|
||||
|
||||
- autcross is a new binary that compares the output of tools
|
||||
transforming automata (in the same way as we have ltlcross for
|
||||
LTL translators).
|
||||
|
||||
- autfilt learned to build the union (--sum) or the intersection
|
||||
(--sum-and) of two language by putting two automata side-by-side
|
||||
and fiddling with the initial states. This complement the already
|
||||
|
|
@ -11,9 +19,6 @@ New in spot 2.3.5.dev (not yet released)
|
|||
- autfilt learned to complement any alternating automaton with
|
||||
option --dualize.
|
||||
|
||||
- genaut is a binary to produce families of automata defined in the
|
||||
literature (in the same way as we have genltl for LTL formulas).
|
||||
|
||||
- autfilt learned --split-edges to convert labels that are Boolean
|
||||
formulas into labels that are min-terms. (See spot::split_edges()
|
||||
below.)
|
||||
|
|
|
|||
1
bin/.gitignore
vendored
1
bin/.gitignore
vendored
|
|
@ -1,3 +1,4 @@
|
|||
autcross
|
||||
autfilt
|
||||
dstar2tgba
|
||||
genaut
|
||||
|
|
|
|||
|
|
@ -59,6 +59,7 @@ libcommon_a_SOURCES = \
|
|||
common_trans.hh
|
||||
|
||||
bin_PROGRAMS = \
|
||||
autcross \
|
||||
autfilt \
|
||||
dstar2tgba \
|
||||
genaut \
|
||||
|
|
@ -77,6 +78,7 @@ bin_PROGRAMS = \
|
|||
# version number that is automatically updated).
|
||||
noinst_PROGRAMS = spot-x spot
|
||||
|
||||
autcross_SOURCES = autcross.cc
|
||||
autfilt_SOURCES = autfilt.cc
|
||||
ltlfilt_SOURCES = ltlfilt.cc
|
||||
genaut_SOURCES = genaut.cc
|
||||
|
|
|
|||
898
bin/autcross.cc
Normal file
898
bin/autcross.cc
Normal file
|
|
@ -0,0 +1,898 @@
|
|||
// -*- coding: utf-8 -*-
|
||||
// Copyright (C) 2017 Laboratoire de Recherche et Développement de
|
||||
// l'Epita (LRDE).
|
||||
//
|
||||
// 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 "common_sys.hh"
|
||||
|
||||
#include <string>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <cstdlib>
|
||||
#include <cstdio>
|
||||
#include <argp.h>
|
||||
#include <unistd.h>
|
||||
#include <cmath>
|
||||
#include <sys/wait.h>
|
||||
#include <iomanip>
|
||||
#include "error.h"
|
||||
#include "argmatch.h"
|
||||
|
||||
#include "common_setup.hh"
|
||||
#include "common_hoaread.hh"
|
||||
#include "common_finput.hh"
|
||||
#include "common_color.hh"
|
||||
#include "common_trans.hh"
|
||||
#include "common_cout.hh"
|
||||
#include "common_aoutput.hh"
|
||||
#include "common_post.hh"
|
||||
|
||||
#include <spot/twaalgos/hoa.hh>
|
||||
#include <spot/twaalgos/postproc.hh>
|
||||
#include <spot/twaalgos/isdet.hh>
|
||||
#include <spot/twaalgos/determinize.hh>
|
||||
#include <spot/twaalgos/dualize.hh>
|
||||
#include <spot/twaalgos/alternation.hh>
|
||||
#include <spot/twaalgos/cleanacc.hh>
|
||||
#include <spot/twaalgos/remfin.hh>
|
||||
#include <spot/twaalgos/product.hh>
|
||||
#include <spot/misc/escape.hh>
|
||||
|
||||
const char argp_program_doc[] ="\
|
||||
Call several tools that process automata and cross-compare their output \
|
||||
to detect bugs, or to gather statistics. The list of automata to use \
|
||||
should be supplied on standard input, or using the -f or -F options.\v\
|
||||
Exit status:\n\
|
||||
0 everything went fine (timeouts are OK too)\n\
|
||||
1 some tools failed to output something we understand, or failed\n\
|
||||
sanity checks (statistics were output nonetheless)\n\
|
||||
2 autcross aborted on error\n\
|
||||
";
|
||||
|
||||
enum {
|
||||
OPT_BOGUS = 256,
|
||||
OPT_CSV,
|
||||
OPT_HIGH,
|
||||
OPT_IGNORE_EXEC_FAIL,
|
||||
OPT_LANG,
|
||||
OPT_LOW,
|
||||
OPT_MEDIUM,
|
||||
OPT_NOCHECKS,
|
||||
OPT_OMIT,
|
||||
OPT_STOP_ERR,
|
||||
OPT_VERBOSE,
|
||||
};
|
||||
|
||||
static const argp_option options[] =
|
||||
{
|
||||
{ nullptr, 0, nullptr, 0, "Input:", 1 },
|
||||
{ "file", 'F', "FILENAME", 0,
|
||||
"process automata from FILENAME", 0 },
|
||||
/**************************************************/
|
||||
{ nullptr, 0, nullptr, 0, "autcross behavior:", 5 },
|
||||
{ "stop-on-error", OPT_STOP_ERR, nullptr, 0,
|
||||
"stop on first execution error or failure to pass"
|
||||
" sanity checks (timeouts are OK)", 0 },
|
||||
{ "ignore-execution-failures", OPT_IGNORE_EXEC_FAIL, nullptr, 0,
|
||||
"ignore automata from translators that return with a non-zero exit code,"
|
||||
" but do not flag this as an error", 0 },
|
||||
{ "language-preserved", OPT_LANG, nullptr, 0,
|
||||
"expect that each tool preserves the input language", 0 },
|
||||
{ "no-checks", OPT_NOCHECKS, nullptr, 0,
|
||||
"do not perform any sanity checks", 0 },
|
||||
/**************************************************/
|
||||
{ nullptr, 0, nullptr, 0, "Statistics output:", 7 },
|
||||
{ "csv", OPT_CSV, "[>>]FILENAME", OPTION_ARG_OPTIONAL,
|
||||
"output statistics as CSV in FILENAME or on standard output "
|
||||
"(if '>>' is used to request append mode, the header line is "
|
||||
"not output)", 0 },
|
||||
{ "omit-missing", OPT_OMIT, nullptr, 0,
|
||||
"do not output statistics for timeouts or failed translations", 0 },
|
||||
/**************************************************/
|
||||
{ nullptr, 0, nullptr, 0, "Simplification level (for complementation):",
|
||||
21 },
|
||||
{ "low", OPT_LOW, nullptr, 0, "minimal optimizations (fast)", 0 },
|
||||
{ "medium", OPT_MEDIUM, nullptr, 0, "moderate optimizations", 0 },
|
||||
{ "high", OPT_HIGH, nullptr, 0,
|
||||
"all available optimizations (slow, default)", 0 },
|
||||
/**************************************************/
|
||||
{ nullptr, 0, nullptr, 0, "Miscellaneous options:", -2 },
|
||||
{ "save-bogus", OPT_BOGUS, "[>>]FILENAME", 0,
|
||||
"save formulas for which problems were detected in FILENAME", 0 },
|
||||
{ "verbose", OPT_VERBOSE, nullptr, 0,
|
||||
"print what is being done, for debugging", 0 },
|
||||
{ nullptr, 0, nullptr, 0, nullptr, 0 }
|
||||
};
|
||||
|
||||
const struct argp_child children[] =
|
||||
{
|
||||
{ &autproc_argp, 0, nullptr, 0 },
|
||||
{ &hoaread_argp, 0, "Parsing of automata:", 4 },
|
||||
{ &misc_argp, 0, nullptr, -2 },
|
||||
{ &color_argp, 0, nullptr, 0 },
|
||||
{ nullptr, 0, nullptr, 0 }
|
||||
};
|
||||
|
||||
static bool verbose = false;
|
||||
static bool ignore_exec_fail = false;
|
||||
static unsigned ignored_exec_fail = 0;
|
||||
static bool stop_on_error = false;
|
||||
static bool no_checks = false;
|
||||
static bool opt_language_preserved = false;
|
||||
static bool opt_omit = false;
|
||||
static const char* csv_output = nullptr;
|
||||
static unsigned round_num = 0;
|
||||
static const char* bogus_output_filename = nullptr;
|
||||
static output_file* bogus_output = nullptr;
|
||||
|
||||
static int
|
||||
parse_opt(int key, char* arg, struct argp_state*)
|
||||
{
|
||||
switch (key)
|
||||
{
|
||||
case 'F':
|
||||
jobs.emplace_back(arg, true);
|
||||
break;
|
||||
case OPT_BOGUS:
|
||||
{
|
||||
bogus_output = new output_file(arg);
|
||||
bogus_output_filename = arg;
|
||||
break;
|
||||
}
|
||||
case OPT_CSV:
|
||||
csv_output = arg ? arg : "-";
|
||||
break;
|
||||
case OPT_HIGH:
|
||||
level = spot::postprocessor::High;
|
||||
level_set = true;
|
||||
break;
|
||||
case OPT_IGNORE_EXEC_FAIL:
|
||||
ignore_exec_fail = true;
|
||||
break;
|
||||
case OPT_LANG:
|
||||
opt_language_preserved = true;
|
||||
break;
|
||||
case OPT_LOW:
|
||||
level = spot::postprocessor::Low;
|
||||
level_set = true;
|
||||
break;
|
||||
case OPT_MEDIUM:
|
||||
level = spot::postprocessor::Medium;
|
||||
level_set = true;
|
||||
break;
|
||||
case OPT_NOCHECKS:
|
||||
no_checks = true;
|
||||
break;
|
||||
case OPT_OMIT:
|
||||
opt_omit = true;
|
||||
break;
|
||||
case OPT_STOP_ERR:
|
||||
stop_on_error = true;
|
||||
break;
|
||||
case OPT_VERBOSE:
|
||||
verbose = true;
|
||||
break;
|
||||
case ARGP_KEY_ARG:
|
||||
if (arg[0] == '-' && !arg[1])
|
||||
jobs.emplace_back(arg, true);
|
||||
else
|
||||
tools_push_autproc(arg);
|
||||
break;
|
||||
default:
|
||||
return ARGP_ERR_UNKNOWN;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool global_error_flag = false;
|
||||
// static unsigned oom_count = 0U;
|
||||
|
||||
static std::ostream&
|
||||
global_error()
|
||||
{
|
||||
global_error_flag = true;
|
||||
std::cerr << bright_red;
|
||||
return std::cerr;
|
||||
}
|
||||
|
||||
static void
|
||||
end_error()
|
||||
{
|
||||
std::cerr << reset_color;
|
||||
}
|
||||
|
||||
static std::ostream&
|
||||
example()
|
||||
{
|
||||
std::cerr << bold_std;
|
||||
return std::cerr;
|
||||
}
|
||||
|
||||
|
||||
struct aut_statistics
|
||||
{
|
||||
unsigned ap;
|
||||
unsigned states;
|
||||
unsigned edges;
|
||||
unsigned transitions;
|
||||
unsigned acc_sets;
|
||||
unsigned scc;
|
||||
unsigned nondetstates;
|
||||
bool nondeterministic;
|
||||
bool alternating;
|
||||
|
||||
aut_statistics()
|
||||
{
|
||||
}
|
||||
|
||||
void set(const spot::const_twa_graph_ptr& aut)
|
||||
{
|
||||
if (!csv_output)
|
||||
// Do not waste time.
|
||||
return;
|
||||
ap = aut->ap().size();
|
||||
spot::twa_sub_statistics s = sub_stats_reachable(aut);
|
||||
states = s.states;
|
||||
edges = s.edges;
|
||||
transitions = s.transitions;
|
||||
spot::scc_info m(aut);
|
||||
acc_sets = aut->num_sets();
|
||||
unsigned c = m.scc_count();
|
||||
scc = c;
|
||||
nondetstates = spot::count_nondet_states(aut);
|
||||
nondeterministic = nondetstates != 0;
|
||||
alternating = !aut->is_existential();
|
||||
}
|
||||
|
||||
void to_csv(std::ostream& os) const
|
||||
{
|
||||
os << ap << ','
|
||||
<< states << ','
|
||||
<< edges << ','
|
||||
<< transitions << ','
|
||||
<< acc_sets << ','
|
||||
<< scc << ','
|
||||
<< nondetstates << ','
|
||||
<< nondeterministic << ','
|
||||
<< alternating;
|
||||
}
|
||||
|
||||
void empty(std::ostream& os) const
|
||||
{
|
||||
os << ",,,,,,,,";
|
||||
}
|
||||
|
||||
static void fields(std::ostream& os, const char* prefix)
|
||||
{
|
||||
os << '"'
|
||||
<< prefix << "ap\",\""
|
||||
<< prefix << "states\",\""
|
||||
<< prefix << "edges\",\""
|
||||
<< prefix << "transitions\",\""
|
||||
<< prefix << "acc_sets\",\""
|
||||
<< prefix << "scc\",\""
|
||||
<< prefix << "nondetstates\",\""
|
||||
<< prefix << "nondeterministic\",\""
|
||||
<< prefix << "alternating\"";
|
||||
}
|
||||
};
|
||||
|
||||
struct in_statistics
|
||||
{
|
||||
std::string input_source;
|
||||
std::string input_name;
|
||||
aut_statistics input;
|
||||
|
||||
static void fields(std::ostream& os)
|
||||
{
|
||||
os << "\"input.source\",\"input.name\",";
|
||||
aut_statistics::fields(os, "input.");
|
||||
}
|
||||
|
||||
void to_csv(std::ostream& os) const
|
||||
{
|
||||
spot::escape_rfc4180(os << '"', input_source) << "\",";
|
||||
if (!input_name.empty())
|
||||
spot::escape_rfc4180(os << '"', input_name) << "\",";
|
||||
else
|
||||
os << ',';
|
||||
input.to_csv(os);
|
||||
}
|
||||
};
|
||||
|
||||
struct out_statistics
|
||||
{
|
||||
|
||||
// If OK is false, output statistics are not available.
|
||||
bool ok;
|
||||
const char* status_str;
|
||||
int status_code;
|
||||
double time;
|
||||
aut_statistics output;
|
||||
|
||||
out_statistics()
|
||||
: ok(false),
|
||||
status_str(nullptr),
|
||||
status_code(0),
|
||||
time(0)
|
||||
{
|
||||
}
|
||||
|
||||
static void fields(std::ostream& os)
|
||||
{
|
||||
os << "\"exit_status\",\"exit_code\",\"time\",";
|
||||
aut_statistics::fields(os, "output.");
|
||||
}
|
||||
|
||||
void to_csv(std::ostream& os) const
|
||||
{
|
||||
os << '"' << status_str << "\"," << status_code << ','
|
||||
<< time << ',';
|
||||
if (ok)
|
||||
output.to_csv(os);
|
||||
else
|
||||
output.empty(os);
|
||||
}
|
||||
};
|
||||
|
||||
std::vector<in_statistics> input_statistics;
|
||||
typedef std::vector<out_statistics> vector_tool_statistics;
|
||||
std::vector<vector_tool_statistics> output_statistics;
|
||||
|
||||
namespace
|
||||
{
|
||||
class autcross_runner final: public autproc_runner
|
||||
{
|
||||
spot::bdd_dict_ptr dict;
|
||||
public:
|
||||
autcross_runner(spot::bdd_dict_ptr dict)
|
||||
: dict(dict)
|
||||
{
|
||||
}
|
||||
|
||||
spot::twa_graph_ptr
|
||||
run_tool(unsigned int tool_num, char l, bool& problem,
|
||||
out_statistics& stats)
|
||||
{
|
||||
output.reset(tool_num);
|
||||
|
||||
std::ostringstream command;
|
||||
format(command, tools[tool_num].cmd);
|
||||
|
||||
std::string cmd = command.str();
|
||||
std::cerr << "Running [" << l << tool_num << "]: "
|
||||
<< cmd << std::endl;
|
||||
process_timer timer;
|
||||
timer.start();
|
||||
int es = exec_with_timeout(cmd.c_str());
|
||||
timer.stop();
|
||||
const char* status_str = nullptr;
|
||||
|
||||
spot::twa_graph_ptr res = nullptr;
|
||||
if (timed_out)
|
||||
{
|
||||
// This is not considered to be a global error.
|
||||
std::cerr << "warning: timeout during execution of command\n";
|
||||
++timeout_count;
|
||||
status_str = "timeout";
|
||||
problem = false; // A timeout is not a sign of a bug
|
||||
es = -1;
|
||||
}
|
||||
else if (WIFSIGNALED(es))
|
||||
{
|
||||
status_str = "signal";
|
||||
problem = true;
|
||||
es = WTERMSIG(es);
|
||||
global_error() << "error: execution terminated by signal "
|
||||
<< es << ".\n";
|
||||
end_error();
|
||||
}
|
||||
else if (WIFEXITED(es) && WEXITSTATUS(es) != 0)
|
||||
{
|
||||
es = WEXITSTATUS(es);
|
||||
status_str = "exit code";
|
||||
if (!ignore_exec_fail)
|
||||
{
|
||||
problem = true;
|
||||
global_error() << "error: execution returned exit code "
|
||||
<< es << ".\n";
|
||||
end_error();
|
||||
}
|
||||
else
|
||||
{
|
||||
problem = false;
|
||||
std::cerr << "warning: execution returned exit code "
|
||||
<< es << ".\n";
|
||||
++ignored_exec_fail;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
status_str = "ok";
|
||||
problem = false;
|
||||
es = 0;
|
||||
|
||||
auto aut = spot::parse_aut(output.val()->name(), dict,
|
||||
spot::default_environment::instance(),
|
||||
opt_parse);
|
||||
if (!aut->errors.empty())
|
||||
{
|
||||
status_str = "parse error";
|
||||
problem = true;
|
||||
es = -1;
|
||||
std::ostream& err = global_error();
|
||||
err << "error: failed to parse the produced automaton.\n";
|
||||
aut->format_errors(err);
|
||||
end_error();
|
||||
res = nullptr;
|
||||
}
|
||||
else if (aut->aborted)
|
||||
{
|
||||
status_str = "aborted";
|
||||
problem = true;
|
||||
es = -1;
|
||||
global_error() << "error: aborted HOA file.\n";
|
||||
end_error();
|
||||
res = nullptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
res = aut->aut;
|
||||
}
|
||||
}
|
||||
output.cleanup();
|
||||
|
||||
stats.status_str = status_str;
|
||||
stats.status_code = es;
|
||||
stats.time = timer.get_lap_sw();
|
||||
if (res)
|
||||
{
|
||||
stats.ok = true;
|
||||
stats.output.set(res);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
};
|
||||
|
||||
static std::string
|
||||
autname(unsigned i, bool complemented = false)
|
||||
{
|
||||
std::ostringstream str;
|
||||
if (complemented)
|
||||
str << "Comp(";
|
||||
if (i < tools.size())
|
||||
str << 'A' << i;
|
||||
else
|
||||
str << "input";
|
||||
if (complemented)
|
||||
str << ')';
|
||||
return str.str();
|
||||
}
|
||||
|
||||
static bool
|
||||
check_empty_prod(const spot::const_twa_graph_ptr& aut_i,
|
||||
const spot::const_twa_graph_ptr& aut_j,
|
||||
size_t i, size_t j)
|
||||
{
|
||||
if (aut_i->num_sets() + aut_j->num_sets()
|
||||
> 8 * sizeof(spot::acc_cond::mark_t::value_t))
|
||||
{
|
||||
std::cerr << "info: building " << autname(i)
|
||||
<< '*' << autname(j, true)
|
||||
<< " requires more acceptance sets than supported\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
auto prod = spot::product(aut_i, aut_j);
|
||||
|
||||
if (verbose)
|
||||
std::cerr << "info: check_empty "
|
||||
<< autname(i) << '*' << autname(j, true) << '\n';
|
||||
|
||||
auto w = prod->accepting_word();
|
||||
if (w)
|
||||
{
|
||||
std::ostream& err = global_error();
|
||||
err << "error: " << autname(i) << '*' << autname(j, true)
|
||||
<< (" is nonempty; both automata accept the infinite word:\n"
|
||||
" ");
|
||||
example() << *w << '\n';
|
||||
end_error();
|
||||
}
|
||||
return !!w;
|
||||
}
|
||||
|
||||
class autcross_processor final: public hoa_processor
|
||||
{
|
||||
autcross_runner runner;
|
||||
public:
|
||||
autcross_processor()
|
||||
: hoa_processor(spot::make_bdd_dict()), runner(dict_)
|
||||
{
|
||||
}
|
||||
|
||||
int
|
||||
process_automaton(const spot::const_parsed_aut_ptr& haut) override
|
||||
{
|
||||
auto printsize = [](const spot::const_twa_graph_ptr& aut,
|
||||
bool props)
|
||||
{
|
||||
std::cerr << '(' << aut->num_states() << " st.,"
|
||||
<< aut->num_edges() << " ed.,"
|
||||
<< aut->num_sets() << " sets)";
|
||||
if (props)
|
||||
{
|
||||
if (!aut->is_existential())
|
||||
std::cerr << " univ-edges";
|
||||
if (is_deterministic(aut))
|
||||
std::cerr << " deterministic";
|
||||
if (is_complete(aut))
|
||||
std::cerr << " complete";
|
||||
std::cerr << '\n';
|
||||
}
|
||||
};
|
||||
|
||||
spot::twa_graph_ptr input = haut->aut;
|
||||
runner.round_automaton(input, round_num);
|
||||
|
||||
std::string source = [&]()
|
||||
{
|
||||
std::ostringstream src;
|
||||
src << haut->filename << ':' << haut->loc;
|
||||
return src.str();
|
||||
}();
|
||||
|
||||
input_statistics.push_back(in_statistics());
|
||||
|
||||
std::cerr << bold << source << reset_color;
|
||||
input_statistics[round_num].input_source = std::move(source);
|
||||
if (auto name = input->get_named_prop<std::string>("automaton-name"))
|
||||
{
|
||||
std::cerr << '\t' << *name;
|
||||
input_statistics[round_num].input_name = *name;
|
||||
}
|
||||
std::cerr << '\n';
|
||||
input_statistics[round_num].input.set(input);
|
||||
|
||||
int problems = 0;
|
||||
size_t m = tools.size();
|
||||
size_t mi = m + opt_language_preserved;
|
||||
std::vector<spot::twa_graph_ptr> pos(mi);
|
||||
std::vector<spot::twa_graph_ptr> neg(mi);
|
||||
vector_tool_statistics stats(m);
|
||||
|
||||
if (opt_language_preserved)
|
||||
pos[mi - 1] = input;
|
||||
|
||||
if (verbose)
|
||||
{
|
||||
std::cerr << "info: input\t";
|
||||
printsize(input, true);
|
||||
}
|
||||
|
||||
for (size_t n = 0; n < m; ++n)
|
||||
{
|
||||
bool prob;
|
||||
pos[n] = runner.run_tool(n, 'A', prob, stats[n]);
|
||||
problems += prob;
|
||||
}
|
||||
spot::cleanup_tmpfiles();
|
||||
++round_num;
|
||||
output_statistics.push_back(std::move(stats));
|
||||
|
||||
if (verbose)
|
||||
{
|
||||
std::cerr << "info: collected automata:\n";
|
||||
for (unsigned i = 0; i < m; ++i)
|
||||
if (pos[i])
|
||||
{
|
||||
std::cerr << "info: A" << i << '\t';
|
||||
printsize(pos[i], true);
|
||||
}
|
||||
}
|
||||
|
||||
if (!no_checks)
|
||||
{
|
||||
std::cerr << "Performing sanity checks and gathering statistics..."
|
||||
<< std::endl;
|
||||
|
||||
{
|
||||
bool print_first = true;
|
||||
for (unsigned i = 0; i < mi; ++i)
|
||||
{
|
||||
if (!pos[i])
|
||||
continue;
|
||||
if (!pos[i]->is_existential())
|
||||
{
|
||||
if (verbose)
|
||||
{
|
||||
if (print_first)
|
||||
{
|
||||
std::cerr <<
|
||||
"info: getting rid of universal edges...\n";
|
||||
print_first = false;
|
||||
}
|
||||
std::cerr << "info: "
|
||||
<< std::setw(8) << autname(i) << '\t';
|
||||
printsize(pos[i], false);
|
||||
std::cerr << " -> ";
|
||||
}
|
||||
pos[i] = remove_alternation(pos[i]);
|
||||
if (verbose)
|
||||
{
|
||||
printsize(pos[i], false);
|
||||
std::cerr << '\n';
|
||||
}
|
||||
}
|
||||
if (is_universal(pos[i]))
|
||||
neg[i] = dualize(pos[i]);
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
bool print_first = verbose;
|
||||
for (unsigned i = 0; i < mi; ++i)
|
||||
{
|
||||
if (pos[i] && !neg[i])
|
||||
{
|
||||
if (print_first)
|
||||
{
|
||||
std::cerr << "info: complementing non-deterministic "
|
||||
"automata via determinization...\n";
|
||||
print_first = false;
|
||||
}
|
||||
spot::postprocessor p;
|
||||
p.set_type(spot::postprocessor::Generic);
|
||||
p.set_pref(spot::postprocessor::Deterministic);
|
||||
p.set_level(level);
|
||||
neg[i] = dualize(p.run(pos[i]));
|
||||
if (verbose)
|
||||
{
|
||||
std::cerr << "info: "
|
||||
<< std::setw(8) << autname(i) << '\t';
|
||||
printsize(pos[i], false);
|
||||
std::cerr << " -> ";
|
||||
printsize(neg[i], false);
|
||||
std::cerr << '\t' << autname(i, true) << '\n';
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
{
|
||||
bool print_first = true;
|
||||
auto tmp = [&](std::vector<spot::twa_graph_ptr>& x, unsigned i,
|
||||
bool neg)
|
||||
{
|
||||
if (!x[i])
|
||||
return;
|
||||
if (x[i]->acc().uses_fin_acceptance())
|
||||
{
|
||||
if (verbose)
|
||||
{
|
||||
if (print_first)
|
||||
{
|
||||
std::cerr <<
|
||||
"info: getting rid of any Fin acceptance...\n";
|
||||
print_first = false;
|
||||
}
|
||||
std::cerr << "info: "
|
||||
<< std::setw(8) << autname(i, neg) << '\t';
|
||||
printsize(x[i], false);
|
||||
std::cerr << " ->";
|
||||
}
|
||||
cleanup_acceptance_here(x[i]);
|
||||
x[i] = remove_fin(x[i]);
|
||||
if (verbose)
|
||||
{
|
||||
std::cerr << ' ';
|
||||
printsize(x[i], false);
|
||||
std::cerr << '\n';
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Remove useless sets nonetheless.
|
||||
cleanup_acceptance_here(x[i]);
|
||||
}
|
||||
};
|
||||
for (unsigned i = 0; i < mi; ++i)
|
||||
{
|
||||
tmp(pos, i, false);
|
||||
tmp(neg, i, true);
|
||||
}
|
||||
}
|
||||
|
||||
// Just make a circular implication check
|
||||
// A0 <= A1, A1 <= A2, ..., AN <= A0
|
||||
unsigned ok = 0;
|
||||
for (size_t i = 0; i < mi; ++i)
|
||||
if (pos[i])
|
||||
{
|
||||
size_t j = ((i + 1) % mi);
|
||||
if (i != j && neg[j])
|
||||
{
|
||||
int res = check_empty_prod(pos[i], neg[j], i, j);
|
||||
problems += res;
|
||||
ok += !res;
|
||||
}
|
||||
}
|
||||
// If the circular check failed, do the rest of all
|
||||
// mi(mi-1)/2 checks, as this will help diagnose the issue.
|
||||
if (ok != mi)
|
||||
for (size_t i = 0; i < mi; ++i)
|
||||
if (pos[i])
|
||||
{
|
||||
size_t k = ((i + 1) % mi);
|
||||
for (size_t j = 0; j < mi; ++j)
|
||||
if (i != j && j != k && neg[j])
|
||||
problems += check_empty_prod(pos[i], neg[j], i, j);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cerr << "Gathering statistics..." << std::endl;
|
||||
}
|
||||
|
||||
|
||||
if (problems && bogus_output)
|
||||
print_hoa(bogus_output->ostream(), input) << std::endl;
|
||||
|
||||
std::cerr << std::endl;
|
||||
|
||||
// Shall we stop processing now?
|
||||
abort_run = global_error_flag && stop_on_error;
|
||||
return problems;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
// Output an RFC4180-compatible CSV file.
|
||||
static void
|
||||
print_stats_csv(const char* filename)
|
||||
{
|
||||
if (verbose)
|
||||
std::cerr << "info: writing CSV to " << filename << '\n';
|
||||
|
||||
output_file outf(filename);
|
||||
std::ostream& out = outf.ostream();
|
||||
|
||||
unsigned ntools = tools.size();
|
||||
assert(round_num == output_statistics.size());
|
||||
assert(round_num == input_statistics.size());
|
||||
|
||||
if (!outf.append())
|
||||
{
|
||||
// Do not output the header line if we append to a file.
|
||||
// (Even if that file was empty initially.)
|
||||
in_statistics::fields(out);
|
||||
out << ",\"tool\",";
|
||||
out_statistics::fields(out);
|
||||
out << '\n';
|
||||
}
|
||||
for (unsigned r = 0; r < round_num; ++r)
|
||||
for (unsigned t = 0; t < ntools; ++t)
|
||||
if (!opt_omit || output_statistics[r][t].ok)
|
||||
{
|
||||
input_statistics[r].to_csv(out);
|
||||
out << ",\"";
|
||||
spot::escape_rfc4180(out, tools[t].name);
|
||||
out << "\",";
|
||||
output_statistics[r][t].to_csv(out);
|
||||
out << '\n';
|
||||
}
|
||||
outf.close(filename);
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char** argv)
|
||||
{
|
||||
setup(argv);
|
||||
|
||||
const argp ap = { options, parse_opt, "[COMMANDFMT...]",
|
||||
argp_program_doc, children, nullptr, nullptr };
|
||||
|
||||
if (int err = argp_parse(&ap, argc, argv, ARGP_NO_HELP, nullptr, nullptr))
|
||||
exit(err);
|
||||
|
||||
check_no_automaton();
|
||||
|
||||
auto s = tools.size();
|
||||
if (s == 0)
|
||||
error(2, 0, "No tool to run? Run '%s --help' for usage.",
|
||||
program_name);
|
||||
|
||||
if (s == 1 && !opt_language_preserved && !no_checks)
|
||||
error(2, 0, "Since --language-preserved is not used, you need "
|
||||
"at least two tools to compare.");
|
||||
|
||||
|
||||
setup_color();
|
||||
setup_sig_handler();
|
||||
|
||||
autcross_processor p;
|
||||
if (p.run())
|
||||
return 2;
|
||||
|
||||
if (round_num == 0)
|
||||
{
|
||||
error(2, 0, "no automaton to translate");
|
||||
}
|
||||
else
|
||||
{
|
||||
if (global_error_flag)
|
||||
{
|
||||
std::ostream& err = global_error();
|
||||
if (bogus_output)
|
||||
err << ("error: some error was detected during the above runs.\n"
|
||||
" Check file ")
|
||||
<< bogus_output_filename
|
||||
<< " for problematic automata.";
|
||||
else
|
||||
err << ("error: some error was detected during the above runs,\n"
|
||||
" please search for 'error:' messages in the above"
|
||||
" trace.");
|
||||
err << std::endl;
|
||||
end_error();
|
||||
}
|
||||
else if (timeout_count == 0 && ignored_exec_fail == 0)
|
||||
{
|
||||
std::cerr << "No problem detected." << std::endl;
|
||||
}
|
||||
else
|
||||
{
|
||||
std::cerr << "No major problem detected." << std::endl;
|
||||
}
|
||||
|
||||
unsigned additional_errors = 0U;
|
||||
additional_errors += timeout_count > 0;
|
||||
additional_errors += ignored_exec_fail > 0;
|
||||
if (additional_errors)
|
||||
{
|
||||
std::cerr << (global_error_flag ? "Additionally, " : "However, ");
|
||||
if (timeout_count)
|
||||
{
|
||||
if (additional_errors > 1)
|
||||
std::cerr << "\n - ";
|
||||
if (timeout_count == 1)
|
||||
std::cerr << "1 timeout occurred";
|
||||
else
|
||||
std::cerr << timeout_count << " timeouts occurred";
|
||||
}
|
||||
|
||||
if (ignored_exec_fail)
|
||||
{
|
||||
if (additional_errors > 1)
|
||||
std::cerr << "\n - ";
|
||||
if (ignored_exec_fail == 1)
|
||||
std::cerr << "1 non-zero exit status was ignored";
|
||||
else
|
||||
std::cerr << ignored_exec_fail
|
||||
<< " non-zero exit statuses were ignored";
|
||||
}
|
||||
if (additional_errors == 1)
|
||||
std::cerr << '.';
|
||||
std::cerr << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
if (csv_output)
|
||||
print_stats_csv(csv_output);
|
||||
|
||||
return global_error_flag;
|
||||
}
|
||||
|
|
@ -515,7 +515,7 @@ static bool opt_highlight_languages = false;
|
|||
static spot::twa_graph_ptr
|
||||
ensure_deterministic(const spot::twa_graph_ptr& aut, bool nonalt = false)
|
||||
{
|
||||
if ((!nonalt || aut->is_existential()) && spot::is_deterministic(aut))
|
||||
if ((!nonalt || aut->is_existential()) && spot::is_universal(aut))
|
||||
return aut;
|
||||
spot::postprocessor p;
|
||||
p.set_type(spot::postprocessor::Generic);
|
||||
|
|
|
|||
|
|
@ -43,6 +43,7 @@ read_automaton(const char* filename, spot::bdd_dict_ptr& dict);
|
|||
|
||||
class hoa_processor: public job_processor
|
||||
{
|
||||
protected:
|
||||
spot::bdd_dict_ptr dict_;
|
||||
public:
|
||||
|
||||
|
|
|
|||
|
|
@ -33,14 +33,17 @@
|
|||
#include <spot/tl/unabbrev.hh>
|
||||
#include "common_conv.hh"
|
||||
#include <spot/misc/escape.hh>
|
||||
#include <spot/twaalgos/hoa.hh>
|
||||
#include <spot/twaalgos/lbtt.hh>
|
||||
#include <spot/twaalgos/neverclaim.hh>
|
||||
|
||||
// A set of tools for which we know the correct output
|
||||
static struct shorthands_t
|
||||
struct shorthands_t
|
||||
{
|
||||
const char* prefix;
|
||||
const char* suffix;
|
||||
}
|
||||
shorthands[] = {
|
||||
};
|
||||
static shorthands_t shorthands_ltl[] = {
|
||||
{ "lbt", " <%L>%O" },
|
||||
{ "ltl2ba", " -f %s>%O" },
|
||||
{ "ltl2da", " %f>%O" },
|
||||
|
|
@ -55,25 +58,30 @@ static struct shorthands_t
|
|||
{ "spin", " -f %s>%O" },
|
||||
};
|
||||
|
||||
static void show_shorthands()
|
||||
static shorthands_t shorthands_autproc[] = {
|
||||
{ "autfilt", " %H>%O" },
|
||||
{ "dstar2tgba", " %H>%O" },
|
||||
{ "ltl2dstar", " -B %H %O" },
|
||||
{ "nba2ldpa", " <%H>%O" },
|
||||
{ "seminator", " %H>%O" },
|
||||
};
|
||||
|
||||
static void show_shorthands(shorthands_t* begin, shorthands_t* end)
|
||||
{
|
||||
std::cout
|
||||
<< ("If a COMMANDFMT does not use any %-sequence, and starts with one of\n"
|
||||
"the following words, then the string on the right is appended.\n\n");
|
||||
for (auto& s: shorthands)
|
||||
std::cout << " "
|
||||
<< std::left << std::setw(12) << s.prefix
|
||||
<< s.suffix << '\n';
|
||||
std::cout
|
||||
<< ("\nAny {name} and directory component is skipped for the purpose of\n"
|
||||
"matching those prefixes. So for instance\n"
|
||||
" '{DRA} ~/mytools/ltl2dstar-0.5.2'\n"
|
||||
"will changed into\n"
|
||||
" '{DRA} ~/mytools/ltl2dstar-0.5.2 --output-format=hoa %[MR]L %O'\n");
|
||||
while (begin != end)
|
||||
{
|
||||
std::cout << " "
|
||||
<< std::left << std::setw(12) << begin->prefix
|
||||
<< begin->suffix << '\n';
|
||||
++begin;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
tool_spec::tool_spec(const char* spec)
|
||||
tool_spec::tool_spec(const char* spec, shorthands_t* begin, shorthands_t* end)
|
||||
: spec(spec), cmd(spec), name(spec)
|
||||
{
|
||||
if (*cmd == '{')
|
||||
|
|
@ -114,8 +122,9 @@ tool_spec::tool_spec(const char* spec)
|
|||
++pos;
|
||||
}
|
||||
// Match a shorthand.
|
||||
for (auto& p: shorthands)
|
||||
while (begin != end)
|
||||
{
|
||||
auto& p = *begin++;
|
||||
int n = strlen(p.prefix);
|
||||
if (strncmp(basename, p.prefix, n) == 0)
|
||||
{
|
||||
|
|
@ -165,6 +174,20 @@ tool_spec::~tool_spec()
|
|||
|
||||
std::vector<tool_spec> tools;
|
||||
|
||||
void tools_push_trans(const char* trans)
|
||||
{
|
||||
tools.emplace_back(trans,
|
||||
std::begin(shorthands_ltl),
|
||||
std::end(shorthands_ltl));
|
||||
}
|
||||
|
||||
void tools_push_autproc(const char* proc)
|
||||
{
|
||||
tools.emplace_back(proc,
|
||||
std::begin(shorthands_autproc),
|
||||
std::end(shorthands_autproc));
|
||||
}
|
||||
|
||||
void
|
||||
quoted_string::print(std::ostream& os, const char*) const
|
||||
{
|
||||
|
|
@ -254,16 +277,8 @@ printable_result_filename::print(std::ostream& os, const char*) const
|
|||
spot::quote_shell_string(os, val()->name());
|
||||
}
|
||||
|
||||
void
|
||||
filed_formula::print(std::ostream& os, const char* pos) const
|
||||
{
|
||||
std::ostringstream ss;
|
||||
f_.print(ss, pos);
|
||||
os << '\'' << string_to_tmp(ss.str(), serial_) << '\'';
|
||||
}
|
||||
|
||||
std::string
|
||||
filed_formula::string_to_tmp(const std::string str, unsigned n) const
|
||||
static std::string
|
||||
string_to_tmp(const std::string str, unsigned n)
|
||||
{
|
||||
char prefix[30];
|
||||
snprintf(prefix, sizeof prefix, "lcr-i%u-", n);
|
||||
|
|
@ -278,6 +293,43 @@ filed_formula::string_to_tmp(const std::string str, unsigned n) const
|
|||
return tmpname;
|
||||
}
|
||||
|
||||
void
|
||||
filed_formula::print(std::ostream& os, const char* pos) const
|
||||
{
|
||||
std::ostringstream ss;
|
||||
f_.print(ss, pos);
|
||||
os << '\'' << string_to_tmp(ss.str(), serial_) << '\'';
|
||||
}
|
||||
|
||||
void
|
||||
filed_automaton::print(std::ostream& os, const char* pos) const
|
||||
{
|
||||
std::ostringstream ss;
|
||||
char* opt = nullptr;
|
||||
if (*pos == '[')
|
||||
{
|
||||
++pos;
|
||||
auto end = strchr(pos, ']');
|
||||
opt = strndup(pos, end - pos);
|
||||
pos = end + 1;
|
||||
}
|
||||
switch (*pos)
|
||||
{
|
||||
case 'H':
|
||||
spot::print_hoa(ss, aut_, opt);
|
||||
break;
|
||||
case 'S':
|
||||
spot::print_never_claim(ss, aut_, opt);
|
||||
break;
|
||||
case 'L':
|
||||
spot::print_lbtt(ss, aut_, opt);
|
||||
break;
|
||||
}
|
||||
if (opt)
|
||||
free(opt);
|
||||
os << '\'' << string_to_tmp(ss.str(), serial_) << '\'';
|
||||
}
|
||||
|
||||
translator_runner::translator_runner(spot::bdd_dict_ptr dict,
|
||||
bool no_output_allowed)
|
||||
: dict(dict)
|
||||
|
|
@ -345,6 +397,42 @@ translator_runner::round_formula(spot::formula f, unsigned serial)
|
|||
filename_formula.new_round(serial);
|
||||
}
|
||||
|
||||
|
||||
autproc_runner::autproc_runner(bool no_output_allowed)
|
||||
{
|
||||
declare('H', &filename_automaton);
|
||||
declare('S', &filename_automaton);
|
||||
declare('L', &filename_automaton);
|
||||
declare('O', &output);
|
||||
|
||||
size_t s = tools.size();
|
||||
assert(s);
|
||||
for (size_t n = 0; n < s; ++n)
|
||||
{
|
||||
// Check that each translator uses at least one input and
|
||||
// one output.
|
||||
std::vector<bool> has(256);
|
||||
const tool_spec& t = tools[n];
|
||||
scan(t.cmd, has);
|
||||
if (!(has['H'] || has['S'] || has['L']))
|
||||
error(2, 0, "no input %%-sequence in '%s'.\n Use "
|
||||
"one of %%H,%%S,%%L to indicate the input automaton filename.",
|
||||
t.spec);
|
||||
if (!no_output_allowed && !has['O'])
|
||||
error(2, 0, "no output %%-sequence in '%s'.\n Use "
|
||||
"%%O to indicate where the automaton is output.",
|
||||
t.spec);
|
||||
// Remember the %-sequences used by all tools.
|
||||
prime(t.cmd);
|
||||
}
|
||||
}
|
||||
|
||||
void
|
||||
autproc_runner::round_automaton(spot::const_twa_graph_ptr aut, unsigned serial)
|
||||
{
|
||||
filename_automaton.new_round(aut, serial);
|
||||
}
|
||||
|
||||
volatile bool timed_out = false;
|
||||
unsigned timeout_count = 0;
|
||||
|
||||
|
|
@ -553,7 +641,7 @@ exec_command(const char* cmd)
|
|||
int fd0 = open(stdin, O_RDONLY, 0644);
|
||||
if (fd0 < 0)
|
||||
error(2, errno, "failed to open '%s'", stdin);
|
||||
if (dup2(fd0, 0) < 0)
|
||||
if (dup2(fd0, STDIN_FILENO) < 0)
|
||||
error(2, errno, "dup2() failed");
|
||||
if (close(fd0) < 0)
|
||||
error(2, errno, "close() failed");
|
||||
|
|
@ -563,7 +651,7 @@ exec_command(const char* cmd)
|
|||
int fd1 = creat(stdout, 0644);
|
||||
if (fd1 < 0)
|
||||
error(2, errno, "failed to open '%s'", stdout);
|
||||
if (dup2(fd1, 1) < 0)
|
||||
if (dup2(fd1, STDOUT_FILENO) < 0)
|
||||
error(2, errno, "dup2() failed");
|
||||
if (close(fd1) < 0)
|
||||
error(2, errno, "close() failed");
|
||||
|
|
@ -602,6 +690,11 @@ exec_with_timeout(const char* cmd)
|
|||
if (child_pid == 0)
|
||||
{
|
||||
setpgid(0, 0);
|
||||
// Close stdin so that children may not read our input. We had
|
||||
// this nice surprise with Seminator, who greedily consumes its
|
||||
// stdin (which was also ours) even if it does not use it
|
||||
// because it was given a file.
|
||||
close(STDIN_FILENO);
|
||||
exec_command(cmd);
|
||||
// never reached
|
||||
return -1;
|
||||
|
|
@ -675,7 +768,7 @@ static int parse_opt_trans(int key, char* arg, struct argp_state*)
|
|||
switch (key)
|
||||
{
|
||||
case 't':
|
||||
tools.push_back(arg);
|
||||
tools_push_trans(arg);
|
||||
break;
|
||||
case 'T':
|
||||
timeout = to_pos_int(arg);
|
||||
|
|
@ -685,7 +778,14 @@ static int parse_opt_trans(int key, char* arg, struct argp_state*)
|
|||
#endif
|
||||
break;
|
||||
case OPT_LIST:
|
||||
show_shorthands();
|
||||
show_shorthands(std::begin(shorthands_ltl), std::end(shorthands_ltl));
|
||||
std::cout
|
||||
<< ("\nAny {name} and directory component is skipped for the purpose "
|
||||
"of\nmatching those prefixes. So for instance\n "
|
||||
"'{DRA} ~/mytools/ltl2dstar-0.5.2'\n"
|
||||
"will be changed into\n "
|
||||
"'{DRA} ~/mytools/ltl2dstar-0.5.2 --output-format=hoa %[MW]L %O'"
|
||||
"\n");
|
||||
exit(0);
|
||||
case OPT_RELABEL:
|
||||
opt_relabel = true;
|
||||
|
|
@ -698,3 +798,61 @@ static int parse_opt_trans(int key, char* arg, struct argp_state*)
|
|||
|
||||
const struct argp trans_argp = { options, parse_opt_trans, nullptr, nullptr,
|
||||
nullptr, nullptr, nullptr };
|
||||
|
||||
|
||||
|
||||
static const argp_option options_aut[] =
|
||||
{
|
||||
/**************************************************/
|
||||
{ nullptr, 0, nullptr, 0, "Specifying tools to call:", 2 },
|
||||
{ "tool", 't', "COMMANDFMT", 0,
|
||||
"register one tool to call", 0 },
|
||||
{ "timeout", 'T', "NUMBER", 0, "kill tools after NUMBER seconds", 0 },
|
||||
{ "list-shorthands", OPT_LIST, nullptr, 0,
|
||||
"list availabled shorthands to use in COMMANDFMT", 0},
|
||||
/**************************************************/
|
||||
{ nullptr, 0, nullptr, 0,
|
||||
"COMMANDFMT should specify input and output arguments using the "
|
||||
"following character sequences:", 3 },
|
||||
{ "%H,%S,%L", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
|
||||
"filename for the input automaton in (H) HOA, (S) Spin's neverclaim, "
|
||||
"or (L) LBTT's format", 0 },
|
||||
{ "%O", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE,
|
||||
"filename for the automaton output in HOA, never claim, LBTT, or "
|
||||
"ltl2dstar's format", 0 },
|
||||
{ "%%", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE, "a single %", 0 },
|
||||
{ nullptr, 0, nullptr, 0, nullptr, 0 }
|
||||
};
|
||||
|
||||
static int parse_opt_autproc(int key, char* arg, struct argp_state*)
|
||||
{
|
||||
switch (key)
|
||||
{
|
||||
case 't':
|
||||
tools_push_autproc(arg);
|
||||
break;
|
||||
case 'T':
|
||||
timeout = to_pos_int(arg);
|
||||
#if !ENABLE_TIMEOUT
|
||||
std::cerr << "warning: setting a timeout is not supported "
|
||||
<< "on your platform" << std::endl;
|
||||
#endif
|
||||
break;
|
||||
case OPT_LIST:
|
||||
show_shorthands(std::begin(shorthands_autproc),
|
||||
std::end(shorthands_autproc));
|
||||
std::cout
|
||||
<< ("\nAny {name} and directory component is skipped for the purpose "
|
||||
"of\nmatching those prefixes. So for instance\n "
|
||||
"'{AF} ~/mytools/autfilt-2.4 --remove-fin'\n"
|
||||
"will be changed into\n "
|
||||
"'{AF} ~/mytools/autfilt-2.4 --remove-fin %H>%O'\n");
|
||||
exit(0);
|
||||
default:
|
||||
return ARGP_ERR_UNKNOWN;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
const struct argp autproc_argp = { options_aut, parse_opt_autproc, nullptr,
|
||||
nullptr, nullptr, nullptr, nullptr };
|
||||
|
|
|
|||
|
|
@ -28,9 +28,13 @@
|
|||
#include <spot/twa/twagraph.hh>
|
||||
|
||||
|
||||
extern const struct argp trans_argp;
|
||||
extern const struct argp trans_argp; // ltlcross, ltldo
|
||||
extern const struct argp autproc_argp; // autcross
|
||||
|
||||
extern bool opt_relabel;
|
||||
|
||||
struct shorthands_t;
|
||||
|
||||
struct tool_spec
|
||||
{
|
||||
// The translator command, as specified on the command-line.
|
||||
|
|
@ -44,7 +48,7 @@ struct tool_spec
|
|||
// name of the translator (or spec)
|
||||
const char* name;
|
||||
|
||||
tool_spec(const char* spec);
|
||||
tool_spec(const char* spec, shorthands_t* begin, shorthands_t* end);
|
||||
tool_spec(const tool_spec& other);
|
||||
tool_spec& operator=(const tool_spec& other);
|
||||
~tool_spec();
|
||||
|
|
@ -52,6 +56,9 @@ struct tool_spec
|
|||
|
||||
extern std::vector<tool_spec> tools;
|
||||
|
||||
void tools_push_trans(const char* trans);
|
||||
void tools_push_autproc(const char* proc);
|
||||
|
||||
struct quoted_string final: public spot::printable_value<std::string>
|
||||
{
|
||||
using spot::printable_value<std::string>::operator=;
|
||||
|
|
@ -80,7 +87,25 @@ struct filed_formula final: public spot::printable
|
|||
private:
|
||||
const quoted_formula& f_;
|
||||
unsigned serial_;
|
||||
std::string string_to_tmp(const std::string str, unsigned n) const;
|
||||
};
|
||||
|
||||
struct filed_automaton final: public spot::printable
|
||||
{
|
||||
filed_automaton()
|
||||
{
|
||||
}
|
||||
|
||||
void print(std::ostream& os, const char* pos) const override;
|
||||
|
||||
void new_round(spot::const_twa_graph_ptr aut, unsigned serial)
|
||||
{
|
||||
aut_ = aut;
|
||||
serial_ = serial;
|
||||
}
|
||||
|
||||
private:
|
||||
spot::const_twa_graph_ptr aut_;
|
||||
unsigned serial_;
|
||||
};
|
||||
|
||||
struct printable_result_filename final:
|
||||
|
|
@ -118,6 +143,23 @@ public:
|
|||
};
|
||||
|
||||
|
||||
class autproc_runner: protected spot::formater
|
||||
{
|
||||
protected:
|
||||
// Round-specific variables
|
||||
filed_automaton filename_automaton;
|
||||
// Run-specific variables
|
||||
printable_result_filename output;
|
||||
public:
|
||||
using spot::formater::has;
|
||||
|
||||
autproc_runner(// whether we accept the absence of output
|
||||
// specifier
|
||||
bool no_output_allowed = false);
|
||||
void round_automaton(spot::const_twa_graph_ptr aut, unsigned serial);
|
||||
};
|
||||
|
||||
|
||||
// Disable handling of timeout on systems that miss kill() or alarm().
|
||||
// For instance MinGW.
|
||||
#if HAVE_KILL && HAVE_ALARM
|
||||
|
|
|
|||
|
|
@ -219,7 +219,6 @@ example()
|
|||
return std::cerr;
|
||||
}
|
||||
|
||||
|
||||
static void
|
||||
end_error()
|
||||
{
|
||||
|
|
@ -423,7 +422,7 @@ parse_opt(int key, char* arg, struct argp_state*)
|
|||
if (arg[0] == '-' && !arg[1])
|
||||
jobs.emplace_back(arg, true);
|
||||
else
|
||||
tools.push_back(arg);
|
||||
tools_push_trans(arg);
|
||||
break;
|
||||
case OPT_AUTOMATA:
|
||||
opt_automata = true;
|
||||
|
|
@ -1111,7 +1110,7 @@ namespace
|
|||
std::vector<spot::twa_graph_ptr>& comp,
|
||||
unsigned i)
|
||||
{
|
||||
if (!no_complement && x[i] && is_deterministic(x[i]))
|
||||
if (!no_complement && x[i] && is_universal(x[i]))
|
||||
comp[i] = dualize(x[i]);
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -171,7 +171,7 @@ parse_opt(int key, char* arg, struct argp_state*)
|
|||
if (arg[0] == '-' && !arg[1])
|
||||
jobs.emplace_back(arg, true);
|
||||
else
|
||||
tools.push_back(arg);
|
||||
tools_push_trans(arg);
|
||||
break;
|
||||
default:
|
||||
return ARGP_ERR_UNKNOWN;
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ convman7 = ARGP_HELP_FMT=header-col=0 $(SHELL) "$(x_to_1)" \
|
|||
"$(PERL)" "$(top_srcdir)/tools/help2man -s7 -N"
|
||||
|
||||
dist_man1_MANS = \
|
||||
autcross.1 \
|
||||
autfilt.1 \
|
||||
dstar2tgba.1 \
|
||||
genaut.1 \
|
||||
|
|
@ -44,6 +45,9 @@ dist_man7_MANS = \
|
|||
MAINTAINERCLEANFILES = $(dist_man1_MANS) $(dist_man7_MANS)
|
||||
EXTRA_DIST = $(dist_man1_MANS:.1=.x) $(dist_man7_MANS:.7=.x)
|
||||
|
||||
autcross.1: $(common_dep) $(srcdir)/autcross.x $(srcdir)/../autcross.cc
|
||||
$(convman) ../autcross$(EXEEXT) $(srcdir)/autcross.x $@
|
||||
|
||||
autfilt.1: $(common_dep) $(srcdir)/autfilt.x $(srcdir)/../autfilt.cc
|
||||
$(convman) ../autfilt$(EXEEXT) $(srcdir)/autfilt.x $@
|
||||
|
||||
|
|
|
|||
97
bin/man/autcross.x
Normal file
97
bin/man/autcross.x
Normal file
|
|
@ -0,0 +1,97 @@
|
|||
.\" -*- coding: utf-8 -*-
|
||||
[NAME]
|
||||
autcross \- cross-compare tools that process automata
|
||||
|
||||
[ENVIRONMENT VARIABLES]
|
||||
.TP
|
||||
\fBSPOT_TMPDIR\fR, \fBTMPDIR\fR
|
||||
These variables control in which directory temporary files (e.g.,
|
||||
those who contain the input and output when interfacing with
|
||||
translators) are created. \fBTMPDIR\fR is only read if
|
||||
\fBSPOT_TMPDIR\fR does not exist. If none of these environment
|
||||
variables exist, or if their value is empty, files are created in the
|
||||
current directory.
|
||||
.TP
|
||||
\fBSPOT_TMPKEEP\fR
|
||||
When this variable is defined, temporary files are not removed.
|
||||
This is mostly useful for debugging.
|
||||
|
||||
[OUTPUT DATA]
|
||||
The following columns are output in the CSV files.
|
||||
.TP
|
||||
\fBinput.source\fR
|
||||
Location of the input automaton fed to the tool.
|
||||
.TP
|
||||
\fBinput.name\fR
|
||||
Name of the input automaton, if any. This is supported
|
||||
by the HOA format.
|
||||
.TP
|
||||
\fBinput.ap\fR,\fBoutput.ap\fR,
|
||||
Number of atomic proposition in the input and output automata.
|
||||
.TP
|
||||
\fBinput.states\fR,\fBoutput.states\fR
|
||||
Number of states in the input and output automata.
|
||||
.TP
|
||||
\fBinput.edges\fR,\fBoutput.edges\fR
|
||||
Number of edges in the input and output automata.
|
||||
.TP
|
||||
\fBinput.transitions\fR,\fBoutput.transitions\fR
|
||||
Number of transitions in the input and output automata.
|
||||
.TP
|
||||
\fBinput.acc_sets\fR,\fBoutput.acc_sets\fR
|
||||
Number of acceptance sets in the input and output automata.
|
||||
.TP
|
||||
\fBinput.scc\fR,\fBoutput.scc\fR
|
||||
Number of strongly connected components in the input and output automata.
|
||||
.TP
|
||||
\fBinput.nondetstates\fR,\fBoutput.nondetstates\fR
|
||||
Number of nondeterministic states in the input and output automata.
|
||||
.TP
|
||||
\fBinput.nondeterministic\fR,\fBoutput.nondetstates\fR
|
||||
1 if the automaton is nondeterministic, 0 if it is deterministic.
|
||||
.TP
|
||||
\fBinput.alternating\fR,\fBoutput.alternating\fR
|
||||
1 if the automaton has some universal branching, 0 otherwise.
|
||||
|
||||
\fBexit_status\fR, \fBexit_code\fR
|
||||
Information about how the execution of the tool went.
|
||||
\fBexit_status\fR is a string that can take the following
|
||||
values:
|
||||
.RS
|
||||
.TP
|
||||
\f(CW"ok"\fR
|
||||
The tool ran succesfully (this does not imply that the produced
|
||||
automaton is correct) and autcross could parse the resulting
|
||||
automaton. In this case \fBexit_code\fR is always 0.
|
||||
.TP
|
||||
\f(CW"timeout"\fR
|
||||
The tool ran for more than the number of seconds
|
||||
specified with the \fB\-\-timeout\fR option. In this
|
||||
case \fBexit_code\fR is always -1.
|
||||
.TP
|
||||
\f(CW"exit code"\fR
|
||||
The tool terminated with a non-zero exit code.
|
||||
\fBexit_code\fR contains that value.
|
||||
.TP
|
||||
\f(CW"signal"\fR
|
||||
The tool terminated with a signal.
|
||||
\fBexit_code\fR contains that signal's number.
|
||||
.TP
|
||||
\f(CW"parse error"\fR
|
||||
The tool terminated normally, but autcross could not
|
||||
parse its output. In this case \fBexit_code\fR is always -1.
|
||||
.TP
|
||||
\f(CW"no output"\fR
|
||||
The tool terminated normally, but without creating the specified
|
||||
output file. In this case \fBexit_code\fR is always -1.
|
||||
.RE
|
||||
.TP
|
||||
\fBtime\fR
|
||||
A floating point number giving the run time of the tool in seconds.
|
||||
This is reported for all executions, even failling ones.
|
||||
|
||||
[SEE ALSO]
|
||||
.BR randaut (1),
|
||||
.BR genaut (1),
|
||||
.BR autfilt (1),
|
||||
.BR ltlcross (1)
|
||||
|
|
@ -78,6 +78,7 @@ ORG_FILES = \
|
|||
org/spot.css \
|
||||
org/arch.tex \
|
||||
$(srcdir)/org/arch.png \
|
||||
org/autcross.org \
|
||||
org/autfilt.org \
|
||||
org/csv.org \
|
||||
org/citing.org \
|
||||
|
|
|
|||
388
doc/org/autcross.org
Normal file
388
doc/org/autcross.org
Normal file
|
|
@ -0,0 +1,388 @@
|
|||
# -*- coding: utf-8 -*-
|
||||
#+TITLE: =autcross=
|
||||
#+DESCRIPTION: Spot command-line tool for cross-comparing the output of automaton processors.
|
||||
#+SETUPFILE: setup.org
|
||||
#+HTML_LINK_UP: tools.html
|
||||
|
||||
=autcross= is a tool for cross-comparing the output of tools that
|
||||
transform automata. It works similarly to [[file:ltlcross.org][=ltlcross=]] except that
|
||||
inputs are automata.
|
||||
|
||||
The core of =autcross= is a loop that does the following steps:
|
||||
- Input an automaton
|
||||
- Process that input automaton with all the configured tools.
|
||||
If there are 3 translators, the automata produced by those tools
|
||||
will be named =A0=, =A1=, and =A2=.
|
||||
- Ensure that all produced automata are equivalent.
|
||||
|
||||
Statistics about the results of each tools can optionally be saved in
|
||||
a CSV file. And in case only those statistics matters, it is also
|
||||
possible to disable the equivalence checks.
|
||||
|
||||
* Input automata
|
||||
|
||||
The input automata can be supplied either on standard input, or in
|
||||
files specified with option =-F=.
|
||||
|
||||
If an input automaton is expressed in the HOA format and has a name,
|
||||
that name will be displayed by =autcross= when processing the automaton,
|
||||
and will appear in the CSV file if such a file is output.
|
||||
|
||||
* Specifying the tools to test
|
||||
|
||||
Each tool should be specified as a string that uses some of the
|
||||
following character sequences:
|
||||
|
||||
#+BEGIN_SRC sh :results verbatim :exports results
|
||||
autcross --help | sed -n '/character sequences:/,/^$/p' | sed '1d;$d'
|
||||
#+END_SRC
|
||||
#+RESULTS:
|
||||
: %% a single %
|
||||
: %H,%S,%L filename for the input automaton in (H) HOA, (S)
|
||||
: Spin's neverclaim, or (L) LBTT's format
|
||||
: %O filename for the automaton output in HOA, never
|
||||
: claim, LBTT, or ltl2dstar's format
|
||||
|
||||
For instance we can use =autfilt --complement %H >%O= to indicate that
|
||||
=autfilt= reads one file (=%H=) in the HOA format, and to redirect the
|
||||
output in file so that =autcross= can find it. The output format is
|
||||
automatically detected, so a generic =%O= is used for the output file
|
||||
regardless of its format.
|
||||
|
||||
Another tool that can complement automata is =ltl2dstar=, using
|
||||
the syntax =ltl2dstar -B --complement-input=yes %H %O=. So to
|
||||
compare the results of these two tools we could use:
|
||||
|
||||
#+BEGIN_SRC sh :results verbatim :exports code
|
||||
randaut -B -n 3 a b |
|
||||
autcross 'autfilt --complement %H >%O' 'ltl2dstar --complement-input=yes -B %H %O'
|
||||
#+END_SRC
|
||||
|
||||
#+BEGIN_SRC sh :results verbatim :exports output
|
||||
randaut -B -n 3 a b |
|
||||
autcross 'autfilt --complement %H >%O' 'ltl2dstar --complement-input=yes -B %H %O' 2>&1
|
||||
#+END_SRC
|
||||
|
||||
#+RESULTS:
|
||||
#+begin_example
|
||||
-:1.1-45.7
|
||||
Running [A0]: autfilt --complement 'lcr-i0-Nr1xZO' >'lcr-o0-urHakt'
|
||||
Running [A1]: ltl2dstar -B --complement-input=yes 'lcr-i0-mmkgH7' 'lcr-o1-ABdm4L'
|
||||
Performing sanity checks and gathering statistics...
|
||||
|
||||
-:46.1-92.7
|
||||
Running [A0]: autfilt --complement 'lcr-i1-5kMYrq' >'lcr-o0-9kvBP4'
|
||||
Running [A1]: ltl2dstar -B --complement-input=yes 'lcr-i1-lVlGfJ' 'lcr-o1-BexLFn'
|
||||
Performing sanity checks and gathering statistics...
|
||||
|
||||
-:93.1-137.7
|
||||
Running [A0]: autfilt --complement 'lcr-i2-rjvy61' >'lcr-o0-rKKlxG'
|
||||
Running [A1]: ltl2dstar -B --complement-input=yes 'lcr-i2-Musr0k' 'lcr-o1-LyAxtZ'
|
||||
Performing sanity checks and gathering statistics...
|
||||
|
||||
No problem detected.
|
||||
#+end_example
|
||||
|
||||
In this example, we generate 3 random Büchi automata (because
|
||||
=ltl2dstar= expects Büchi automata as input) using [[file:randaut.org][=randaut=]], and pipe
|
||||
them to =autcross=. For each of those automata, =autcross= display
|
||||
the source location of that automaton (here =-= indicate that the
|
||||
automaton is read from standard input, and this is followed by
|
||||
=beginline.column-endline.colum= specifying the position of that
|
||||
automaton in the input. If the automata had names, they would
|
||||
be displayed as well.
|
||||
|
||||
Then, each tool is called using temporary files to exchange the
|
||||
automata, and the resulting automata are then compared. The last line
|
||||
specifies that no problem was detected.
|
||||
|
||||
|
||||
To simplify the use of some known tools, a set of predefined
|
||||
shorthands are available. Those can be listed with the
|
||||
=--list-shorthands= option.
|
||||
|
||||
#+BEGIN_SRC sh :results verbatim :exports both
|
||||
autcross --list-shorthands
|
||||
#+END_SRC
|
||||
#+RESULTS:
|
||||
#+begin_example
|
||||
If a COMMANDFMT does not use any %-sequence, and starts with one of
|
||||
the following words, then the string on the right is appended.
|
||||
|
||||
autfilt %H>%O
|
||||
seminator %H>%O
|
||||
ltl2dstar -B %H %O
|
||||
nba2ldpa <%H>%O
|
||||
|
||||
Any {name} and directory component is skipped for the purpose of
|
||||
matching those prefixes. So for instance
|
||||
'{AF} ~/mytools/autfilt-2.4 --remove-fin'
|
||||
will be changed into
|
||||
'{AF} ~/mytools/autfilt-2.4 --remove-fin %H>%O'
|
||||
#+end_example
|
||||
|
||||
What this implies is our previous example could be shortened to:
|
||||
|
||||
#+BEGIN_SRC sh :results verbatim :exports code
|
||||
randaut -B -n 3 a b |
|
||||
autcross 'autfilt --complement' 'ltl2dstar --complement-input=yes'
|
||||
#+END_SRC
|
||||
|
||||
* Getting statistics
|
||||
|
||||
Detailed statistics about the result of each translation, and the
|
||||
product of that resulting automaton with the random state-space, can
|
||||
be obtained using the =--csv=FILE= option.
|
||||
|
||||
** CSV output
|
||||
|
||||
Let's take our example and modify it in two ways. First we pass a
|
||||
=--name= option to =randaut= to give each automaton a name (in
|
||||
=randaut=, =%L= denotes the serial number of the automaton); this is
|
||||
mostly a cosmetic change, so that =autcross= will display names.
|
||||
Second, we pass a =--csv= option to =autcross= to save statistics in a
|
||||
file.
|
||||
|
||||
|
||||
#+BEGIN_SRC sh :results verbatim :exports code
|
||||
randaut -B -n 3 a b --name="automaton %L" |
|
||||
autcross 'autfilt --complement' 'ltl2dstar --complement-input=yes' --csv=autcross.csv
|
||||
#+END_SRC
|
||||
|
||||
#+BEGIN_SRC sh :results verbatim :exports results
|
||||
randaut -B -n 3 a b --name="automaton %L" |
|
||||
autcross 'autfilt --complement' 'ltl2dstar --complement-input=yes' --csv=autcross.csv 2>&1
|
||||
#+END_SRC
|
||||
|
||||
#+RESULTS:
|
||||
#+begin_example
|
||||
-:1.1-46.7 automaton 0
|
||||
Running [A0]: autfilt --complement 'lcr-i0-QHReWu'>'lcr-o0-0eTOmZ'
|
||||
Running [A1]: ltl2dstar --complement-input=yes -B 'lcr-i0-jsoPPt' 'lcr-o1-66bQiY'
|
||||
Performing sanity checks and gathering statistics...
|
||||
|
||||
-:47.1-94.7 automaton 1
|
||||
Running [A0]: autfilt --complement 'lcr-i1-IubpMs'>'lcr-o0-dfmYfX'
|
||||
Running [A1]: ltl2dstar --complement-input=yes -B 'lcr-i1-13NXLr' 'lcr-o1-zSwXhW'
|
||||
Performing sanity checks and gathering statistics...
|
||||
|
||||
-:95.1-140.7 automaton 2
|
||||
Running [A0]: autfilt --complement 'lcr-i2-g5bDOq'>'lcr-o0-X71ilV'
|
||||
Running [A1]: ltl2dstar --complement-input=yes -B 'lcr-i2-og1mUp' 'lcr-o1-QVurtU'
|
||||
Performing sanity checks and gathering statistics...
|
||||
|
||||
No problem detected.
|
||||
#+end_example
|
||||
|
||||
After this execution, the file =autcross.csv= contains the following:
|
||||
|
||||
#+BEGIN_SRC sh :results verbatim :exports results
|
||||
cat autcross.csv
|
||||
#+END_SRC
|
||||
#+RESULTS:
|
||||
: "input.source","input.name","input.ap","input.states","input.edges","input.transitions","input.acc_sets","input.scc","input.nondetstates","input.nondeterministic","input.alternating","tool","exit_status","exit_code","time","output.ap","output.states","output.edges","output.transitions","output.acc_sets","output.scc","output.nondetstates","output.nondeterministic","output.alternating"
|
||||
: "-:1.1-46.7","automaton 0",2,10,26,26,1,1,6,0,0,"autfilt --complement","ok",0,0.0129685,2,27,95,108,3,2,0,0,0
|
||||
: "-:1.1-46.7","automaton 0",2,10,26,26,1,1,6,0,0,"ltl2dstar --complement-input=yes","ok",0,0.00202496,2,34,121,136,6,2,0,9,0
|
||||
: "-:47.1-94.7","automaton 1",2,10,28,28,1,1,4,0,0,"autfilt --complement","ok",0,0.0128458,2,58,216,232,3,2,0,9,0
|
||||
: "-:47.1-94.7","automaton 1",2,10,28,28,1,1,4,0,0,"ltl2dstar --complement-input=yes","ok",0,0.0026184,2,74,268,296,6,2,0,8,0
|
||||
: "-:95.1-140.7","automaton 2",2,10,26,26,1,2,6,0,0,"autfilt --complement","ok",0,0.0127144,2,21,69,84,2,4,0,20,0
|
||||
: "-:95.1-140.7","automaton 2",2,10,26,26,1,2,6,0,0,"ltl2dstar --complement-input=yes","ok",0,0.00180888,2,24,74,96,2,4,0,35,0
|
||||
|
||||
This file can then be loaded in any spreadsheet or statistical application.
|
||||
|
||||
When computing such statistics, you should be aware that inputs for
|
||||
which a tool failed to generate an automaton (e.g. it crashed, or it
|
||||
was killed if you used =autcross='s =--timeout= option to limit run
|
||||
time) will appear with empty columns at the end of the CSV line.
|
||||
Those lines with missing data can be omitted with the =--omit-missing=
|
||||
option.
|
||||
|
||||
However data for bogus automata are still included: as shown below
|
||||
=autcross= will report inconsistencies between automata as errors, but
|
||||
it does not try to guess who is incorrect.
|
||||
|
||||
The column names should be almost self-explanatory. See the [[./man/autcross.1.html][man page]]
|
||||
for a description of the columns.
|
||||
|
||||
** Changing the name of the translators
|
||||
|
||||
By default, the names used in the CSV output to designate the
|
||||
tools are the command specified on the command line. Like with
|
||||
=ltlcross=, this can be adjusted by using a command specification of
|
||||
the form ={short name}actual command=.
|
||||
|
||||
For instance
|
||||
|
||||
#+BEGIN_SRC sh :results verbatim :exports both
|
||||
randaut -B -n 3 a b --name="automaton %L" |
|
||||
autcross '{AF}autfilt --complement' '{L2D}ltl2dstar --complement-input=yes' --csv
|
||||
#+END_SRC
|
||||
|
||||
#+RESULTS:
|
||||
: "input.source","input.name","input.ap","input.states","input.edges","input.transitions","input.acc_sets","input.scc","input.nondetstates","input.nondeterministic","input.alternating","tool","exit_status","exit_code","time","output.ap","output.states","output.edges","output.transitions","output.acc_sets","output.scc","output.nondetstates","output.nondeterministic","output.alternating"
|
||||
: "-:1.1-46.7","automaton 0",2,10,26,26,1,1,6,0,0,"AF","ok",0,0.039722,2,27,95,108,3,2,0,0,0
|
||||
: "-:1.1-46.7","automaton 0",2,10,26,26,1,1,6,0,0,"L2D","ok",0,0.00640371,2,34,121,136,6,2,0,9,0
|
||||
: "-:47.1-94.7","automaton 1",2,10,28,28,1,1,4,0,0,"AF","ok",0,0.0400776,2,58,216,232,3,2,0,9,0
|
||||
: "-:47.1-94.7","automaton 1",2,10,28,28,1,1,4,0,0,"L2D","ok",0,0.00489558,2,74,268,296,6,2,0,20,0
|
||||
: "-:95.1-140.7","automaton 2",2,10,26,26,1,2,6,0,0,"AF","ok",0,0.0220585,2,21,69,84,2,4,0,7,0
|
||||
: "-:95.1-140.7","automaton 2",2,10,26,26,1,2,6,0,0,"L2D","ok",0,0.00329786,2,24,74,96,2,4,0,23,0
|
||||
|
||||
* Language preserving transformation
|
||||
|
||||
By default =autcross= assumes that for a given input the automata
|
||||
produced by all tools should be equivalent. However it does not
|
||||
assume that those language should be equivalent to the input (it is
|
||||
clearly not the case in our complementation test above).
|
||||
|
||||
If the transformation being tested does preserve the language of an
|
||||
automaton, it is worth to pass the =--language-preserved= option to
|
||||
=autfilt=. Doing so a bit like adding =cat %H>%O= as another tool: it
|
||||
will also ensure that the output is equivalent to the input.
|
||||
|
||||
* Detecting problems
|
||||
:PROPERTIES:
|
||||
:CUSTOM_ID: checks
|
||||
:END:
|
||||
|
||||
If a translator exits with a non-zero status code, or fails to output
|
||||
an automaton =autcross= can read, and error will be displayed and the
|
||||
result of the tool will be discarded.
|
||||
|
||||
Otherwise =autcross= performs equivalence checks between each pair of
|
||||
automata. This is done in two steps. First, all produced automata
|
||||
=A0=, =A1=, etc. are complemented: the complement automata are
|
||||
named =Comp(A0)=, =Comp(A1)= etc. Second, =autcross= ensures
|
||||
that =Ai*Comp(Aj)= is empty for all =i= and =j=.
|
||||
|
||||
If the =--language-preserved= option is passed, the =input= automaton
|
||||
also participate to these equivalence checks.
|
||||
|
||||
|
||||
To simulate a problem, let's compare pretend we want verify that
|
||||
=autfilt --complement= preserves the input language (clearly it does
|
||||
not, since it actually complement the language of the automaton).
|
||||
|
||||
#+BEGIN_SRC sh :results verbatim :exports code
|
||||
randaut -B -n 3 a b --name="automaton %L" |
|
||||
autcross --language-preserved 'autfilt --complement'
|
||||
#+END_SRC
|
||||
|
||||
#+BEGIN_SRC sh :results verbatim :exports output
|
||||
randaut -B -n 3 a b --name="automaton %L" |
|
||||
autcross --language-preserved 'autfilt --complement' 2>&1
|
||||
#+END_SRC
|
||||
|
||||
#+RESULTS:
|
||||
#+begin_example
|
||||
-:1.1-46.7 automaton 0
|
||||
Running [A0]: autfilt --complement 'lcr-i0-3gRqrd'>'lcr-o0-ubUpHb'
|
||||
Performing sanity checks and gathering statistics...
|
||||
error: A0*Comp(input) is nonempty; both automata accept the infinite word:
|
||||
!a & !b; a & !b; cycle{!a & !b; a & !b; !a & b; !a & b; !a & !b; !a & b; !a & b; !a & !b}
|
||||
error: input*Comp(A0) is nonempty; both automata accept the infinite word:
|
||||
!a & !b; !a & !b; cycle{!a & !b; !a & b; a & b}
|
||||
|
||||
-:47.1-94.7 automaton 1
|
||||
Running [A0]: autfilt --complement 'lcr-i1-JxFi09'>'lcr-o0-oQGbj8'
|
||||
Performing sanity checks and gathering statistics...
|
||||
error: A0*Comp(input) is nonempty; both automata accept the infinite word:
|
||||
!a & b; cycle{a & !b}
|
||||
error: input*Comp(A0) is nonempty; both automata accept the infinite word:
|
||||
!a & b; a & !b; cycle{!a & b; a & b}
|
||||
|
||||
-:95.1-140.7 automaton 2
|
||||
Running [A0]: autfilt --complement 'lcr-i2-SQT7E6'>'lcr-o0-kWt404'
|
||||
Performing sanity checks and gathering statistics...
|
||||
error: A0*Comp(input) is nonempty; both automata accept the infinite word:
|
||||
a & !b; !a & !b; cycle{a & !b; !a & b}
|
||||
error: input*Comp(A0) is nonempty; both automata accept the infinite word:
|
||||
cycle{!a & b; !a & b; !a & !b; a & !b; a & !b; !a & !b}
|
||||
|
||||
error: some error was detected during the above runs,
|
||||
please search for 'error:' messages in the above trace.
|
||||
#+end_example
|
||||
|
||||
Here, for each automaton, we get an example of word that is accepted
|
||||
by the automaton and rejected by the input (i.e., accepted by
|
||||
=Comp(input)=), as well as an example of word accepted by the input
|
||||
but rejected by the automaton (i.e. accepted by =Comp(A0)=). Those
|
||||
examples would not exit if the language was really preserved by the
|
||||
tool.
|
||||
|
||||
Incoherence between the output of several tools (even with
|
||||
=--language-preserved=) are reported in a similar way.
|
||||
|
||||
* Miscellaneous options
|
||||
|
||||
** =--stop-on-error=
|
||||
|
||||
The =--stop-on-error= option will cause =autcross= to abort on the
|
||||
first detected error. This include failure to start some tool,
|
||||
read its output, or failure to passe the sanity checks. Timeouts are
|
||||
allowed.
|
||||
|
||||
One use for this option is when =autcross= is used in combination with
|
||||
=randaut= to check tools on an infinite stream of formulas.
|
||||
|
||||
** =--save-bogus=FILENAME=
|
||||
|
||||
The =--save-bogus=FILENAME= will save any automaton for which an error
|
||||
was detected (either some tool failed, or some problem was
|
||||
detected using the resulting automata) in =FILENAME=. Again, timeouts
|
||||
are not considered to be errors, and therefore not reported in this
|
||||
file.
|
||||
|
||||
The main use for this feature is in conjunction with =randaut='s
|
||||
generation of random formulas. For instance the following command
|
||||
will run the translators on an infinite number of formulas, saving
|
||||
any problematic formula in =bugs.ltl=.
|
||||
|
||||
** =--no-check=
|
||||
|
||||
The =--no-check= option disables all sanity checks. This
|
||||
makes sense if you only care about the output of =--csv=.
|
||||
|
||||
** =--verbose=
|
||||
|
||||
The verbose option can be useful to troubleshoot problems or simply
|
||||
follow the list of transformations and tests performed by =autcross=.
|
||||
|
||||
#+BEGIN_SRC sh :results verbatim :exports code
|
||||
randaut -B -n 1 a b --name="automaton %L" |
|
||||
autcross 'autfilt --complement' 'ltl2dstar --complement-input=yes' --verbose
|
||||
#+END_SRC
|
||||
|
||||
#+BEGIN_SRC sh :results verbatim :exports results
|
||||
randaut -B -n 1 a b --name="automaton %L" |
|
||||
autcross 'autfilt --complement' 'ltl2dstar --complement-input=yes' --verbose 2>&1
|
||||
#+END_SRC
|
||||
|
||||
#+RESULTS:
|
||||
#+begin_example
|
||||
-:1.1-46.7 automaton 0
|
||||
info: input (10 st.,26 ed.,1 sets)
|
||||
Running [A0]: autfilt --complement 'lcr-i0-oZm5P2'>'lcr-o0-O4P0IB'
|
||||
Running [A1]: ltl2dstar --complement-input=yes -B 'lcr-i0-gEwlJa' 'lcr-o1-wSaHJJ'
|
||||
info: collected automata:
|
||||
info: A0 (27 st.,95 ed.,3 sets) deterministic complete
|
||||
info: A1 (34 st.,121 ed.,6 sets) deterministic complete
|
||||
Performing sanity checks and gathering statistics...
|
||||
info: getting rid of any Fin acceptance...
|
||||
info: A0 (27 st.,95 ed.,3 sets) -> (58 st.,203 ed.,2 sets)
|
||||
info: Comp(A0) (27 st.,95 ed.,3 sets) -> (51 st.,188 ed.,1 sets)
|
||||
info: A1 (34 st.,121 ed.,6 sets) -> (64 st.,228 ed.,3 sets)
|
||||
info: Comp(A1) (34 st.,121 ed.,6 sets) -> (34 st.,121 ed.,1 sets)
|
||||
info: check_empty A0*Comp(A1)
|
||||
info: check_empty A1*Comp(A0)
|
||||
|
||||
No problem detected.
|
||||
#+end_example
|
||||
|
||||
# LocalWords: utf autcross SETUPFILE html HOA neverclaim dstar's Nr
|
||||
# LocalWords: autfilt dstar randaut lcr xZO urHakt mmkgH ABdm kMYrq
|
||||
# LocalWords: kvBP lVlGfJ BexLFn rjvy rKKlxG Musr LyAxtZ shorthands
|
||||
# LocalWords: COMMANDFMT seminator nba ldpa QHReWu eTOmZ jsoPPt ilV
|
||||
# LocalWords: bQiY IubpMs dfmYfX NXLr zSwXhW bDOq og mUp QVurtU ap
|
||||
# LocalWords: ok complementation Ai Aj gRqrd ubUpHb JxFi oQGbj SQT
|
||||
# LocalWords: kWt Eo Xsc WXgCB vLwKMQ tI SXF qqlE KXplk ZFTCz PNY
|
||||
# LocalWords: hUAK IjnFhD cWys ZqjdQh
|
||||
|
|
@ -47,13 +47,14 @@ corresponding commands are hidden.
|
|||
- [[file:ltlcross.org][=ltlcross=]] Cross-compare LTL/PSL-to-automata translators.
|
||||
- [[file:ltlgrind.org][=ltlgrind=]] List formulas similar to but simpler than a given LTL/PSL
|
||||
formula.
|
||||
- [[file:ltldo.org][=ltldo=]] Run LTL/PSL formulas through other tools using common [[file:ioltl.org][input]]
|
||||
and [[file:oaut.org][output]] interfaces.
|
||||
- [[file:dstar2tgba.org][=dstar2tgba=]] Convert \omega-automata with any acceptance into
|
||||
variants of Büchi automata.
|
||||
- [[file:randaut.org][=randaut=]] Generate random \omega-automata.
|
||||
- [[file:genaut.org][=genaut=]] Generate ω-automata from scalable patterns.
|
||||
- [[file:autfilt.org][=autfilt=]] Filter, convert, and transform \omega-automata.
|
||||
- [[file:ltldo.org][=ltldo=]] Run LTL/PSL formulas through other tools using common [[file:ioltl.org][input]]
|
||||
and [[file:oaut.org][output]] interfaces.
|
||||
- [[file:autcross.org][=autcross=]] Cross-compare tools that process automata.
|
||||
|
||||
* Man pages
|
||||
|
||||
|
|
@ -63,6 +64,7 @@ automatically from the =--help= output of each tool, and often
|
|||
completed with additional text (like examples or bibliography). For
|
||||
convenience, you can browse their HTML versions:
|
||||
|
||||
[[./man/autcross.1.html][=autcross=]](1),
|
||||
[[./man/autfilt.1.html][=autfilt=]](1),
|
||||
[[./man/dstar2tgba.1.html][=dstar2tgba=]](1),
|
||||
[[./man/genaut.1.html][=genaut=]](1),
|
||||
|
|
|
|||
|
|
@ -293,6 +293,8 @@ TESTS_twa = \
|
|||
core/ltlcross.test \
|
||||
core/spotlbtt2.test \
|
||||
core/ltlcross2.test \
|
||||
core/autcross.test \
|
||||
core/autcross2.test \
|
||||
core/complementation.test \
|
||||
core/randpsl.test \
|
||||
core/cycles.test \
|
||||
|
|
|
|||
45
tests/core/autcross.test
Executable file
45
tests/core/autcross.test
Executable file
|
|
@ -0,0 +1,45 @@
|
|||
#!/bin/sh
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (C) 2017 Laboratoire de Recherche et Développement de
|
||||
# l'Epita (LRDE).
|
||||
#
|
||||
# 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/>.
|
||||
|
||||
. ./defs
|
||||
set -e
|
||||
|
||||
# Skip this test if ltl2dstar is not installed.
|
||||
(ltl2dstar --version) || exit 77
|
||||
|
||||
randaut -n10 2 |
|
||||
autcross 'ltl2dstar --complement-input=yes' 'autfilt --complement' \
|
||||
--csv=out.csv 2>stderr
|
||||
|
||||
# 5 lines per input automaton, and 1 "No problem detected" line.
|
||||
test 51 = `wc -l < stderr`
|
||||
grep -q 'sanity check' stderr
|
||||
|
||||
# --no-check still allows to build a CSV
|
||||
randaut -n10 2 |
|
||||
autcross 'ltl2dstar --complement-input=yes' 'autfilt --complement' \
|
||||
--csv=out2.csv --no-check 2>stderr
|
||||
test 51 = `wc -l < stderr`
|
||||
grep -q 'sanity check' stderr && exit 1
|
||||
|
||||
for f in out.csv out2.csv; do
|
||||
sed 's/,[0-9]*\.[0-9]*,/,TIME,/' $f > _$f
|
||||
done
|
||||
diff _out.csv _out2.csv
|
||||
30
tests/core/autcross2.test
Executable file
30
tests/core/autcross2.test
Executable file
|
|
@ -0,0 +1,30 @@
|
|||
#!/bin/sh
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (C) 2017 Laboratoire de Recherche et Développement de
|
||||
# l'Epita (LRDE).
|
||||
#
|
||||
# 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/>.
|
||||
|
||||
. ./defs
|
||||
set -e
|
||||
|
||||
randaut -n10 2 | tee input |
|
||||
autcross --language-preserve 'autfilt' 'autfilt --complement' \
|
||||
--save-bogus=bogus.hoa 2>stderr && exit 1
|
||||
cat stderr
|
||||
# We should have 4 counterexample per input automaton
|
||||
test 40 = `grep 'both automata accept the infinite word:' stderr | wc -l`
|
||||
diff input bogus.hoa
|
||||
|
|
@ -1,7 +1,7 @@
|
|||
#!/bin/sh
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (C) 2014 Laboratoire de Recherche et Développement de
|
||||
# l'Epita (LRDE).
|
||||
# Copyright (C) 2014, 2017 Laboratoire de Recherche et Développement
|
||||
# de l'Epita (LRDE).
|
||||
#
|
||||
# This file is part of Spot, a model checking library.
|
||||
#
|
||||
|
|
@ -329,5 +329,5 @@ Acc-Sig: +2
|
|||
13
|
||||
EOF
|
||||
|
||||
test `dstar2tgba -D in.dra --stats="%d:%s:%e"` = "1:23:143"
|
||||
|
||||
autcross 'dstar2tgba -D' --language-preserved -F in.dra --csv=out.csv
|
||||
grep '3,23,143,184,1,2,0,0,0$' out.csv
|
||||
|
|
|
|||
|
|
@ -163,7 +163,7 @@ test 4 = `ltl2tgba -S 'F(a & X(!a &Xb))' --any --stats=%s`
|
|||
|
||||
# Test case from Thibaud Michaud. This used to fail, because sbacc()
|
||||
# would remove acceptance marks from all rejecting SCCS...
|
||||
cat >tm.hoa <<EOF
|
||||
autcross 'autfilt --sbacc' --language-preserve <<EOF
|
||||
HOA: v1
|
||||
States: 5
|
||||
Start: 1
|
||||
|
|
@ -188,8 +188,6 @@ State: 4
|
|||
[!2] 3 {1}
|
||||
--END--
|
||||
EOF
|
||||
autfilt --sbacc tm.hoa > out.hoa
|
||||
autfilt -q --equivalent-to out.hoa tm.hoa
|
||||
|
||||
# Tautological acceptances are converted to "t"
|
||||
cat >taut.hoa <<EOF
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
#!/bin/sh
|
||||
# -*- coding: utf-8 -*-
|
||||
# Copyright (C) 2016 Laboratoire de Recherche et Développement
|
||||
# Copyright (C) 2016, 2017 Laboratoire de Recherche et Développement
|
||||
# de l'Epita (LRDE).
|
||||
#
|
||||
# This file is part of Spot, a model checking library.
|
||||
|
|
@ -110,6 +110,6 @@ State: 7 {0 2 4}
|
|||
--END--
|
||||
EOF
|
||||
|
||||
run 0 autfilt --tgba streett.hoa > out.hoa
|
||||
autfilt -q --equivalent-to streett.hoa out.hoa
|
||||
run 0 autcross --language-preserved \
|
||||
'autfilt --tgba %H | tee out.hoa >%O' -F streett.hoa
|
||||
grep 'generalized-Buchi 2' out.hoa
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue