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:
|
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
|
- autfilt learned to build the union (--sum) or the intersection
|
||||||
(--sum-and) of two language by putting two automata side-by-side
|
(--sum-and) of two language by putting two automata side-by-side
|
||||||
and fiddling with the initial states. This complement the already
|
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
|
- autfilt learned to complement any alternating automaton with
|
||||||
option --dualize.
|
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
|
- autfilt learned --split-edges to convert labels that are Boolean
|
||||||
formulas into labels that are min-terms. (See spot::split_edges()
|
formulas into labels that are min-terms. (See spot::split_edges()
|
||||||
below.)
|
below.)
|
||||||
|
|
|
||||||
1
bin/.gitignore
vendored
1
bin/.gitignore
vendored
|
|
@ -1,3 +1,4 @@
|
||||||
|
autcross
|
||||||
autfilt
|
autfilt
|
||||||
dstar2tgba
|
dstar2tgba
|
||||||
genaut
|
genaut
|
||||||
|
|
|
||||||
|
|
@ -59,6 +59,7 @@ libcommon_a_SOURCES = \
|
||||||
common_trans.hh
|
common_trans.hh
|
||||||
|
|
||||||
bin_PROGRAMS = \
|
bin_PROGRAMS = \
|
||||||
|
autcross \
|
||||||
autfilt \
|
autfilt \
|
||||||
dstar2tgba \
|
dstar2tgba \
|
||||||
genaut \
|
genaut \
|
||||||
|
|
@ -77,6 +78,7 @@ bin_PROGRAMS = \
|
||||||
# version number that is automatically updated).
|
# version number that is automatically updated).
|
||||||
noinst_PROGRAMS = spot-x spot
|
noinst_PROGRAMS = spot-x spot
|
||||||
|
|
||||||
|
autcross_SOURCES = autcross.cc
|
||||||
autfilt_SOURCES = autfilt.cc
|
autfilt_SOURCES = autfilt.cc
|
||||||
ltlfilt_SOURCES = ltlfilt.cc
|
ltlfilt_SOURCES = ltlfilt.cc
|
||||||
genaut_SOURCES = genaut.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
|
static spot::twa_graph_ptr
|
||||||
ensure_deterministic(const spot::twa_graph_ptr& aut, bool nonalt = false)
|
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;
|
return aut;
|
||||||
spot::postprocessor p;
|
spot::postprocessor p;
|
||||||
p.set_type(spot::postprocessor::Generic);
|
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
|
class hoa_processor: public job_processor
|
||||||
{
|
{
|
||||||
|
protected:
|
||||||
spot::bdd_dict_ptr dict_;
|
spot::bdd_dict_ptr dict_;
|
||||||
public:
|
public:
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -33,14 +33,17 @@
|
||||||
#include <spot/tl/unabbrev.hh>
|
#include <spot/tl/unabbrev.hh>
|
||||||
#include "common_conv.hh"
|
#include "common_conv.hh"
|
||||||
#include <spot/misc/escape.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
|
// A set of tools for which we know the correct output
|
||||||
static struct shorthands_t
|
struct shorthands_t
|
||||||
{
|
{
|
||||||
const char* prefix;
|
const char* prefix;
|
||||||
const char* suffix;
|
const char* suffix;
|
||||||
}
|
};
|
||||||
shorthands[] = {
|
static shorthands_t shorthands_ltl[] = {
|
||||||
{ "lbt", " <%L>%O" },
|
{ "lbt", " <%L>%O" },
|
||||||
{ "ltl2ba", " -f %s>%O" },
|
{ "ltl2ba", " -f %s>%O" },
|
||||||
{ "ltl2da", " %f>%O" },
|
{ "ltl2da", " %f>%O" },
|
||||||
|
|
@ -55,25 +58,30 @@ static struct shorthands_t
|
||||||
{ "spin", " -f %s>%O" },
|
{ "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
|
std::cout
|
||||||
<< ("If a COMMANDFMT does not use any %-sequence, and starts with one of\n"
|
<< ("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");
|
"the following words, then the string on the right is appended.\n\n");
|
||||||
for (auto& s: shorthands)
|
while (begin != end)
|
||||||
|
{
|
||||||
std::cout << " "
|
std::cout << " "
|
||||||
<< std::left << std::setw(12) << s.prefix
|
<< std::left << std::setw(12) << begin->prefix
|
||||||
<< s.suffix << '\n';
|
<< begin->suffix << '\n';
|
||||||
std::cout
|
++begin;
|
||||||
<< ("\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");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
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)
|
: spec(spec), cmd(spec), name(spec)
|
||||||
{
|
{
|
||||||
if (*cmd == '{')
|
if (*cmd == '{')
|
||||||
|
|
@ -114,8 +122,9 @@ tool_spec::tool_spec(const char* spec)
|
||||||
++pos;
|
++pos;
|
||||||
}
|
}
|
||||||
// Match a shorthand.
|
// Match a shorthand.
|
||||||
for (auto& p: shorthands)
|
while (begin != end)
|
||||||
{
|
{
|
||||||
|
auto& p = *begin++;
|
||||||
int n = strlen(p.prefix);
|
int n = strlen(p.prefix);
|
||||||
if (strncmp(basename, p.prefix, n) == 0)
|
if (strncmp(basename, p.prefix, n) == 0)
|
||||||
{
|
{
|
||||||
|
|
@ -165,6 +174,20 @@ tool_spec::~tool_spec()
|
||||||
|
|
||||||
std::vector<tool_spec> tools;
|
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
|
void
|
||||||
quoted_string::print(std::ostream& os, const char*) const
|
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());
|
spot::quote_shell_string(os, val()->name());
|
||||||
}
|
}
|
||||||
|
|
||||||
void
|
static std::string
|
||||||
filed_formula::print(std::ostream& os, const char* pos) const
|
string_to_tmp(const std::string str, unsigned n)
|
||||||
{
|
|
||||||
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
|
|
||||||
{
|
{
|
||||||
char prefix[30];
|
char prefix[30];
|
||||||
snprintf(prefix, sizeof prefix, "lcr-i%u-", n);
|
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;
|
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,
|
translator_runner::translator_runner(spot::bdd_dict_ptr dict,
|
||||||
bool no_output_allowed)
|
bool no_output_allowed)
|
||||||
: dict(dict)
|
: dict(dict)
|
||||||
|
|
@ -345,6 +397,42 @@ translator_runner::round_formula(spot::formula f, unsigned serial)
|
||||||
filename_formula.new_round(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;
|
volatile bool timed_out = false;
|
||||||
unsigned timeout_count = 0;
|
unsigned timeout_count = 0;
|
||||||
|
|
||||||
|
|
@ -553,7 +641,7 @@ exec_command(const char* cmd)
|
||||||
int fd0 = open(stdin, O_RDONLY, 0644);
|
int fd0 = open(stdin, O_RDONLY, 0644);
|
||||||
if (fd0 < 0)
|
if (fd0 < 0)
|
||||||
error(2, errno, "failed to open '%s'", stdin);
|
error(2, errno, "failed to open '%s'", stdin);
|
||||||
if (dup2(fd0, 0) < 0)
|
if (dup2(fd0, STDIN_FILENO) < 0)
|
||||||
error(2, errno, "dup2() failed");
|
error(2, errno, "dup2() failed");
|
||||||
if (close(fd0) < 0)
|
if (close(fd0) < 0)
|
||||||
error(2, errno, "close() failed");
|
error(2, errno, "close() failed");
|
||||||
|
|
@ -563,7 +651,7 @@ exec_command(const char* cmd)
|
||||||
int fd1 = creat(stdout, 0644);
|
int fd1 = creat(stdout, 0644);
|
||||||
if (fd1 < 0)
|
if (fd1 < 0)
|
||||||
error(2, errno, "failed to open '%s'", stdout);
|
error(2, errno, "failed to open '%s'", stdout);
|
||||||
if (dup2(fd1, 1) < 0)
|
if (dup2(fd1, STDOUT_FILENO) < 0)
|
||||||
error(2, errno, "dup2() failed");
|
error(2, errno, "dup2() failed");
|
||||||
if (close(fd1) < 0)
|
if (close(fd1) < 0)
|
||||||
error(2, errno, "close() failed");
|
error(2, errno, "close() failed");
|
||||||
|
|
@ -602,6 +690,11 @@ exec_with_timeout(const char* cmd)
|
||||||
if (child_pid == 0)
|
if (child_pid == 0)
|
||||||
{
|
{
|
||||||
setpgid(0, 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);
|
exec_command(cmd);
|
||||||
// never reached
|
// never reached
|
||||||
return -1;
|
return -1;
|
||||||
|
|
@ -675,7 +768,7 @@ static int parse_opt_trans(int key, char* arg, struct argp_state*)
|
||||||
switch (key)
|
switch (key)
|
||||||
{
|
{
|
||||||
case 't':
|
case 't':
|
||||||
tools.push_back(arg);
|
tools_push_trans(arg);
|
||||||
break;
|
break;
|
||||||
case 'T':
|
case 'T':
|
||||||
timeout = to_pos_int(arg);
|
timeout = to_pos_int(arg);
|
||||||
|
|
@ -685,7 +778,14 @@ static int parse_opt_trans(int key, char* arg, struct argp_state*)
|
||||||
#endif
|
#endif
|
||||||
break;
|
break;
|
||||||
case OPT_LIST:
|
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);
|
exit(0);
|
||||||
case OPT_RELABEL:
|
case OPT_RELABEL:
|
||||||
opt_relabel = true;
|
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,
|
const struct argp trans_argp = { options, parse_opt_trans, nullptr, nullptr,
|
||||||
nullptr, 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>
|
#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;
|
extern bool opt_relabel;
|
||||||
|
|
||||||
|
struct shorthands_t;
|
||||||
|
|
||||||
struct tool_spec
|
struct tool_spec
|
||||||
{
|
{
|
||||||
// The translator command, as specified on the command-line.
|
// The translator command, as specified on the command-line.
|
||||||
|
|
@ -44,7 +48,7 @@ struct tool_spec
|
||||||
// name of the translator (or spec)
|
// name of the translator (or spec)
|
||||||
const char* name;
|
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(const tool_spec& other);
|
||||||
tool_spec& operator=(const tool_spec& other);
|
tool_spec& operator=(const tool_spec& other);
|
||||||
~tool_spec();
|
~tool_spec();
|
||||||
|
|
@ -52,6 +56,9 @@ struct tool_spec
|
||||||
|
|
||||||
extern std::vector<tool_spec> tools;
|
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>
|
struct quoted_string final: public spot::printable_value<std::string>
|
||||||
{
|
{
|
||||||
using spot::printable_value<std::string>::operator=;
|
using spot::printable_value<std::string>::operator=;
|
||||||
|
|
@ -80,7 +87,25 @@ struct filed_formula final: public spot::printable
|
||||||
private:
|
private:
|
||||||
const quoted_formula& f_;
|
const quoted_formula& f_;
|
||||||
unsigned serial_;
|
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:
|
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().
|
// Disable handling of timeout on systems that miss kill() or alarm().
|
||||||
// For instance MinGW.
|
// For instance MinGW.
|
||||||
#if HAVE_KILL && HAVE_ALARM
|
#if HAVE_KILL && HAVE_ALARM
|
||||||
|
|
|
||||||
|
|
@ -219,7 +219,6 @@ example()
|
||||||
return std::cerr;
|
return std::cerr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
static void
|
static void
|
||||||
end_error()
|
end_error()
|
||||||
{
|
{
|
||||||
|
|
@ -423,7 +422,7 @@ parse_opt(int key, char* arg, struct argp_state*)
|
||||||
if (arg[0] == '-' && !arg[1])
|
if (arg[0] == '-' && !arg[1])
|
||||||
jobs.emplace_back(arg, true);
|
jobs.emplace_back(arg, true);
|
||||||
else
|
else
|
||||||
tools.push_back(arg);
|
tools_push_trans(arg);
|
||||||
break;
|
break;
|
||||||
case OPT_AUTOMATA:
|
case OPT_AUTOMATA:
|
||||||
opt_automata = true;
|
opt_automata = true;
|
||||||
|
|
@ -1111,7 +1110,7 @@ namespace
|
||||||
std::vector<spot::twa_graph_ptr>& comp,
|
std::vector<spot::twa_graph_ptr>& comp,
|
||||||
unsigned i)
|
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]);
|
comp[i] = dualize(x[i]);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -171,7 +171,7 @@ parse_opt(int key, char* arg, struct argp_state*)
|
||||||
if (arg[0] == '-' && !arg[1])
|
if (arg[0] == '-' && !arg[1])
|
||||||
jobs.emplace_back(arg, true);
|
jobs.emplace_back(arg, true);
|
||||||
else
|
else
|
||||||
tools.push_back(arg);
|
tools_push_trans(arg);
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
return ARGP_ERR_UNKNOWN;
|
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"
|
"$(PERL)" "$(top_srcdir)/tools/help2man -s7 -N"
|
||||||
|
|
||||||
dist_man1_MANS = \
|
dist_man1_MANS = \
|
||||||
|
autcross.1 \
|
||||||
autfilt.1 \
|
autfilt.1 \
|
||||||
dstar2tgba.1 \
|
dstar2tgba.1 \
|
||||||
genaut.1 \
|
genaut.1 \
|
||||||
|
|
@ -44,6 +45,9 @@ dist_man7_MANS = \
|
||||||
MAINTAINERCLEANFILES = $(dist_man1_MANS) $(dist_man7_MANS)
|
MAINTAINERCLEANFILES = $(dist_man1_MANS) $(dist_man7_MANS)
|
||||||
EXTRA_DIST = $(dist_man1_MANS:.1=.x) $(dist_man7_MANS:.7=.x)
|
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
|
autfilt.1: $(common_dep) $(srcdir)/autfilt.x $(srcdir)/../autfilt.cc
|
||||||
$(convman) ../autfilt$(EXEEXT) $(srcdir)/autfilt.x $@
|
$(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/spot.css \
|
||||||
org/arch.tex \
|
org/arch.tex \
|
||||||
$(srcdir)/org/arch.png \
|
$(srcdir)/org/arch.png \
|
||||||
|
org/autcross.org \
|
||||||
org/autfilt.org \
|
org/autfilt.org \
|
||||||
org/csv.org \
|
org/csv.org \
|
||||||
org/citing.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: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
|
- [[file:ltlgrind.org][=ltlgrind=]] List formulas similar to but simpler than a given LTL/PSL
|
||||||
formula.
|
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
|
- [[file:dstar2tgba.org][=dstar2tgba=]] Convert \omega-automata with any acceptance into
|
||||||
variants of Büchi automata.
|
variants of Büchi automata.
|
||||||
- [[file:randaut.org][=randaut=]] Generate random \omega-automata.
|
- [[file:randaut.org][=randaut=]] Generate random \omega-automata.
|
||||||
- [[file:genaut.org][=genaut=]] Generate ω-automata from scalable patterns.
|
- [[file:genaut.org][=genaut=]] Generate ω-automata from scalable patterns.
|
||||||
- [[file:autfilt.org][=autfilt=]] Filter, convert, and transform \omega-automata.
|
- [[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]]
|
- [[file:autcross.org][=autcross=]] Cross-compare tools that process automata.
|
||||||
and [[file:oaut.org][output]] interfaces.
|
|
||||||
|
|
||||||
* Man pages
|
* 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
|
completed with additional text (like examples or bibliography). For
|
||||||
convenience, you can browse their HTML versions:
|
convenience, you can browse their HTML versions:
|
||||||
|
|
||||||
|
[[./man/autcross.1.html][=autcross=]](1),
|
||||||
[[./man/autfilt.1.html][=autfilt=]](1),
|
[[./man/autfilt.1.html][=autfilt=]](1),
|
||||||
[[./man/dstar2tgba.1.html][=dstar2tgba=]](1),
|
[[./man/dstar2tgba.1.html][=dstar2tgba=]](1),
|
||||||
[[./man/genaut.1.html][=genaut=]](1),
|
[[./man/genaut.1.html][=genaut=]](1),
|
||||||
|
|
|
||||||
|
|
@ -293,6 +293,8 @@ TESTS_twa = \
|
||||||
core/ltlcross.test \
|
core/ltlcross.test \
|
||||||
core/spotlbtt2.test \
|
core/spotlbtt2.test \
|
||||||
core/ltlcross2.test \
|
core/ltlcross2.test \
|
||||||
|
core/autcross.test \
|
||||||
|
core/autcross2.test \
|
||||||
core/complementation.test \
|
core/complementation.test \
|
||||||
core/randpsl.test \
|
core/randpsl.test \
|
||||||
core/cycles.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
|
#!/bin/sh
|
||||||
# -*- coding: utf-8 -*-
|
# -*- coding: utf-8 -*-
|
||||||
# Copyright (C) 2014 Laboratoire de Recherche et Développement de
|
# Copyright (C) 2014, 2017 Laboratoire de Recherche et Développement
|
||||||
# l'Epita (LRDE).
|
# de l'Epita (LRDE).
|
||||||
#
|
#
|
||||||
# This file is part of Spot, a model checking library.
|
# This file is part of Spot, a model checking library.
|
||||||
#
|
#
|
||||||
|
|
@ -329,5 +329,5 @@ Acc-Sig: +2
|
||||||
13
|
13
|
||||||
EOF
|
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()
|
# Test case from Thibaud Michaud. This used to fail, because sbacc()
|
||||||
# would remove acceptance marks from all rejecting SCCS...
|
# would remove acceptance marks from all rejecting SCCS...
|
||||||
cat >tm.hoa <<EOF
|
autcross 'autfilt --sbacc' --language-preserve <<EOF
|
||||||
HOA: v1
|
HOA: v1
|
||||||
States: 5
|
States: 5
|
||||||
Start: 1
|
Start: 1
|
||||||
|
|
@ -188,8 +188,6 @@ State: 4
|
||||||
[!2] 3 {1}
|
[!2] 3 {1}
|
||||||
--END--
|
--END--
|
||||||
EOF
|
EOF
|
||||||
autfilt --sbacc tm.hoa > out.hoa
|
|
||||||
autfilt -q --equivalent-to out.hoa tm.hoa
|
|
||||||
|
|
||||||
# Tautological acceptances are converted to "t"
|
# Tautological acceptances are converted to "t"
|
||||||
cat >taut.hoa <<EOF
|
cat >taut.hoa <<EOF
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
#!/bin/sh
|
#!/bin/sh
|
||||||
# -*- coding: utf-8 -*-
|
# -*- 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).
|
# de l'Epita (LRDE).
|
||||||
#
|
#
|
||||||
# This file is part of Spot, a model checking library.
|
# This file is part of Spot, a model checking library.
|
||||||
|
|
@ -110,6 +110,6 @@ State: 7 {0 2 4}
|
||||||
--END--
|
--END--
|
||||||
EOF
|
EOF
|
||||||
|
|
||||||
run 0 autfilt --tgba streett.hoa > out.hoa
|
run 0 autcross --language-preserved \
|
||||||
autfilt -q --equivalent-to streett.hoa out.hoa
|
'autfilt --tgba %H | tee out.hoa >%O' -F streett.hoa
|
||||||
grep 'generalized-Buchi 2' out.hoa
|
grep 'generalized-Buchi 2' out.hoa
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue