autcross: implement --language-complemented

Suggested by Ondřej Lengál.  Fixes #504.

* bin/autcross.cc: Implement the --language-complemented option.
* NEWS, doc/org/autcross.org: Document it.
* tests/core/autcross.test: Test it.
* THANKS: Add Ondřej.
This commit is contained in:
Alexandre Duret-Lutz 2022-04-06 15:25:44 +02:00
parent 5e1b751971
commit a211bace68
5 changed files with 62 additions and 12 deletions

3
NEWS
View file

@ -14,6 +14,9 @@ New in spot 2.10.4.dev (net yet released)
associated option --sonf-aps allows listing the newly introduced associated option --sonf-aps allows listing the newly introduced
atomic propositions. atomic propositions.
- autcross learned a --language-complemented option to assist in the
case one is testing tools that complement automata. (issue #504).
Library: Library:
- The new function suffix_operator_normal_form() implements - The new function suffix_operator_normal_form() implements

1
THANKS
View file

@ -41,6 +41,7 @@ Michael Weber
Mikuláš Klokočka Mikuláš Klokočka
Ming-Hsien Tsai Ming-Hsien Tsai
Nikos Gorogiannis Nikos Gorogiannis
Ondřej Lengál
Paul Guénézan Paul Guénézan
Reuben Rowe Reuben Rowe
Roei Nahum Roei Nahum

View file

@ -1,5 +1,5 @@
// -*- coding: utf-8 -*- // -*- coding: utf-8 -*-
// Copyright (C) 2017-2020 Laboratoire de Recherche et Développement de // Copyright (C) 2017-2020, 2022 Laboratoire de Recherche et Développement de
// l'Epita (LRDE). // l'Epita (LRDE).
// //
// This file is part of Spot, a model checking library. // This file is part of Spot, a model checking library.
@ -64,6 +64,7 @@ Exit status:\n\
enum { enum {
OPT_BOGUS = 256, OPT_BOGUS = 256,
OPT_COMPLEMENTED,
OPT_CSV, OPT_CSV,
OPT_HIGH, OPT_HIGH,
OPT_FAIL_ON_TIMEOUT, OPT_FAIL_ON_TIMEOUT,
@ -94,6 +95,8 @@ static const argp_option options[] =
"consider timeouts as errors", 0 }, "consider timeouts as errors", 0 },
{ "language-preserved", OPT_LANG, nullptr, 0, { "language-preserved", OPT_LANG, nullptr, 0,
"expect that each tool preserves the input language", 0 }, "expect that each tool preserves the input language", 0 },
{ "language-complemented", OPT_COMPLEMENTED, nullptr, 0,
"expect that each tool complements the input language", 0 },
{ "no-checks", OPT_NOCHECKS, nullptr, 0, { "no-checks", OPT_NOCHECKS, nullptr, 0,
"do not perform any sanity checks", 0 }, "do not perform any sanity checks", 0 },
/**************************************************/ /**************************************************/
@ -144,6 +147,7 @@ static bool fail_on_timeout = false;
static bool stop_on_error = false; static bool stop_on_error = false;
static bool no_checks = false; static bool no_checks = false;
static bool opt_language_preserved = false; static bool opt_language_preserved = false;
static bool opt_language_complemented = false;
static bool opt_omit = false; static bool opt_omit = false;
static const char* csv_output = nullptr; static const char* csv_output = nullptr;
static unsigned round_num = 0; static unsigned round_num = 0;
@ -170,6 +174,9 @@ parse_opt(int key, char* arg, struct argp_state*)
bogus_output_filename = arg; bogus_output_filename = arg;
break; break;
} }
case OPT_COMPLEMENTED:
opt_language_complemented = true;
break;
case OPT_CSV: case OPT_CSV:
csv_output = arg ? arg : "-"; csv_output = arg ? arg : "-";
break; break;
@ -533,25 +540,32 @@ namespace
const spot::const_twa_graph_ptr& aut_j, const spot::const_twa_graph_ptr& aut_j,
size_t i, size_t j) size_t i, size_t j)
{ {
auto is_really_comp = [lc = opt_language_complemented,
ts = tools.size()](unsigned i) {
return lc && i == ts;
};
if (aut_i->num_sets() + aut_j->num_sets() > if (aut_i->num_sets() + aut_j->num_sets() >
spot::acc_cond::mark_t::max_accsets()) spot::acc_cond::mark_t::max_accsets())
{ {
if (!quiet) if (!quiet)
std::cerr << "info: building " << autname(i) std::cerr << "info: building " << autname(i, is_really_comp(i))
<< '*' << autname(j, true) << '*' << autname(j, true ^ is_really_comp(j))
<< " requires more acceptance sets than supported\n"; << " requires more acceptance sets than supported\n";
return false; return false;
} }
if (verbose) if (verbose)
std::cerr << "info: check_empty " std::cerr << "info: check_empty "
<< autname(i) << '*' << autname(j, true) << '\n'; << autname(i, is_really_comp(i))
<< '*' << autname(j, true ^ is_really_comp(j)) << '\n';
auto w = aut_i->intersecting_word(aut_j); auto w = aut_i->intersecting_word(aut_j);
if (w) if (w)
{ {
std::ostream& err = global_error(); std::ostream& err = global_error();
err << "error: " << autname(i) << '*' << autname(j, true) err << "error: " << autname(i, is_really_comp(i))
<< '*' << autname(j, true ^ is_really_comp(j))
<< (" is nonempty; both automata accept the infinite word:\n" << (" is nonempty; both automata accept the infinite word:\n"
" "); " ");
example() << *w << '\n'; example() << *w << '\n';
@ -621,12 +635,15 @@ namespace
int problems = 0; int problems = 0;
size_t m = tools.size(); size_t m = tools.size();
size_t mi = m + opt_language_preserved; size_t mi = m + opt_language_preserved + opt_language_complemented;
std::vector<spot::twa_graph_ptr> pos(mi); std::vector<spot::twa_graph_ptr> pos(mi);
std::vector<spot::twa_graph_ptr> neg(mi); std::vector<spot::twa_graph_ptr> neg(mi);
vector_tool_statistics stats(m); vector_tool_statistics stats(m);
if (opt_language_preserved) // For --language-complemented, we store the input automata in
// pos and will compute its complement in neg. Before running
// checks we will swap both automata.
if (opt_language_preserved || opt_language_complemented)
pos[mi - 1] = input; pos[mi - 1] = input;
if (verbose) if (verbose)
@ -718,6 +735,9 @@ namespace
}; };
} }
if (opt_language_complemented)
std::swap(pos[mi - 1], neg[mi - 1]);
// Just make a circular implication check // Just make a circular implication check
// A0 <= A1, A1 <= A2, ..., AN <= A0 // A0 <= A1, A1 <= A2, ..., AN <= A0
unsigned ok = 0; unsigned ok = 0;
@ -824,10 +844,15 @@ main(int argc, char** argv)
check_no_automaton(); check_no_automaton();
if (s == 1 && !opt_language_preserved && !no_checks) if (s == 1 && !no_checks
error(2, 0, "Since --language-preserved is not used, you need " && !opt_language_preserved
"at least two tools to compare."); && !opt_language_complemented)
error(2, 0, "Since --language-preserved and --language-complemented "
"are not used, you need at least two tools to compare.");
if (opt_language_preserved && opt_language_complemented)
error(2, 0, "Options --language-preserved and --language-complemented "
"are incompatible.");
setup_color(); setup_color();
setup_sig_handler(); setup_sig_handler();

View file

@ -249,7 +249,7 @@ EOF
| -:95.1-140.7 | automaton 2 | 2 | 10 | 26 | 26 | 1 | 2 | 6 | 1 | 0 | AF | ok | 0 | 0.0211636 | 2 | 21 | 66 | 84 | 2 | 4 | 0 | 0 | 0 | | -:95.1-140.7 | automaton 2 | 2 | 10 | 26 | 26 | 1 | 2 | 6 | 1 | 0 | AF | ok | 0 | 0.0211636 | 2 | 21 | 66 | 84 | 2 | 4 | 0 | 0 | 0 |
| -:95.1-140.7 | automaton 2 | 2 | 10 | 26 | 26 | 1 | 2 | 6 | 1 | 0 | L2D | ok | 0 | 0.0028508 | 2 | 24 | 74 | 96 | 2 | 4 | 0 | 0 | 0 | | -:95.1-140.7 | automaton 2 | 2 | 10 | 26 | 26 | 1 | 2 | 6 | 1 | 0 | L2D | ok | 0 | 0.0028508 | 2 | 24 | 74 | 96 | 2 | 4 | 0 | 0 | 0 |
* Language preserving transformation * Transformation that preserve or complement languages
By default =autcross= assumes that for a given input the automata By default =autcross= assumes that for a given input the automata
produced by all tools should be equivalent. However it does not produced by all tools should be equivalent. However it does not
@ -261,6 +261,13 @@ 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 =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. will also ensure that the output is equivalent to the input.
Similarly, if the tools being tested implement complementation
algorithm, adding the =--language-complemented= will additionally
compare the outputs using this own complementation algorithm. Using
this option is more efficient than passing =autfilt --complement= as a
tool, since =autcross= can save on complementation by using the input
automaton.
* Detecting problems * Detecting problems
:PROPERTIES: :PROPERTIES:
:CUSTOM_ID: checks :CUSTOM_ID: checks

View file

@ -1,6 +1,6 @@
#!/bin/sh #!/bin/sh
# -*- coding: utf-8 -*- # -*- coding: utf-8 -*-
# Copyright (C) 2017, 2018 Laboratoire de Recherche et Développement # Copyright (C) 2017, 2018, 2022 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.
@ -46,3 +46,17 @@ for f in out.csv out2.csv; do
sed 's/,[0-9]*\.[0-9]*,/,TIME,/' $f > _$f sed 's/,[0-9]*\.[0-9]*,/,TIME,/' $f > _$f
done done
diff _out.csv _out2.csv diff _out.csv _out2.csv
# The {autfilt {complement}} name makes sure we can nest braces.
randaut -n10 2 |
autcross 'ltl2dstar --complement-input=yes' 'autfilt --complement' \
--language-complemented --csv=out3.csv --verbose 2>stderr
test 10 = `grep 'check_empty Comp(input)\*Comp(A0)' stderr | wc -l`
randaut -n1 2 |
autcross 'ltl2dstar --complement-input=yes' 'autfilt --complement' \
--language-complemented --language-preserved 2> stderr && exit 1
cat stderr
grep 'preserved.*complemented.*incompatible' stderr