Commit e7522056 authored by Alexandre Duret-Lutz's avatar Alexandre Duret-Lutz

ltlcross: give an example of accepted word for nonempty cross-products

* src/tgbaalgos/word.cc, src/tgbaalgos/word.hh: New files.
* src/tgbaalgos/Makefile.am: Add them.
* src/tgbatest/ltlcrossce.test: New file.
* src/tgbatest/Makefile.am: Add it.
* src/bin/ltlcross.cc: Compute and display an accepted word
for nonempty cross-products.
* NEWS, doc/org/ltlcross.org: Document it.
parent 4bafa4e1
...@@ -6,6 +6,10 @@ New in spot 1.1.4a (not relased) ...@@ -6,6 +6,10 @@ New in spot 1.1.4a (not relased)
* ltlcross has a new option --color to color its output. It is enabled * ltlcross has a new option --color to color its output. It is enabled
by default when the output is a terminal. by default when the output is a terminal.
* ltlcross will give an example of infinite word accepted by the
two automata when the product between a positive automaton and
a negative automaton is non-empty.
* Environment variables SPOT_TMPDIR and SPOT_TMPKEEP control where * Environment variables SPOT_TMPDIR and SPOT_TMPKEEP control where
temporary files are created and if they should be erased. Read temporary files are created and if they should be erased. Read
the man page of ltlcross for detail. the man page of ltlcross for detail.
......
...@@ -396,28 +396,32 @@ positive and negative formulas by the ith translator). ...@@ -396,28 +396,32 @@ positive and negative formulas by the ith translator).
A single failing translator might generate a lot of lines of A single failing translator might generate a lot of lines of
the form: the form:
: error: P0*N1 is nonempty : error: P0*N1 is nonempty; both automata accept the infinite word
: error: P1*N0 is nonempty : cycle{p0 & !p1}
: error: P1*N1 is nonempty : error: P1*N0 is nonempty; both automata accept the infinite word
: error: P1*N2 is nonempty : p0; !p1; cycle{p0 & p1}
: error: P1*N3 is nonempty : error: P1*N1 is nonempty; both automata accept the infinite word
: error: P1*N4 is nonempty : p0; cycle{!p1 & !p0}
: error: P1*N5 is nonempty : error: P1*N2 is nonempty; both automata accept the infinite word
: error: P1*N6 is nonempty : p0; !p1; cycle{p0 & p1}
: error: P1*N7 is nonempty : error: P1*N3 is nonempty; both automata accept the infinite word
: error: P1*N8 is nonempty : p0; !p1; cycle{p0 & p1}
: error: P1*N9 is nonempty : error: P1*N4 is nonempty; both automata accept the infinite word
: error: P2*N1 is nonempty : p0; cycle{!p1 & !p0}
: error: P3*N1 is nonempty : error: P2*N1 is nonempty; both automata accept the infinite word
: error: P4*N1 is nonempty : p0; !p1; !p0; cycle{!p1 & !p0; p0 & !p1; !p1; !p1; p0 & !p1}
: error: P5*N1 is nonempty : error: P3*N1 is nonempty; both automata accept the infinite word
: error: P6*N1 is nonempty : p0; !p1; !p1 & !p0; cycle{p0 & !p1}
: error: P7*N1 is nonempty : error: P4*N1 is nonempty; both automata accept the infinite word
: error: P8*N1 is nonempty : p0; !p1; !p1 & !p0; cycle{p0 & !p1}
: error: P9*N1 is nonempty
In this example, translator number =1= looks clearly faulty In this example, translator number =1= looks clearly faulty
(at least the other 9 translators do not contradict each other). (at least the other 4 translators do not contradict each other).
Examples of infinite words that are accepted by both automata
always have the form of a lasso: a (possibly empty) finite prefix
followed by a cycle that should be repeated infinitely often.
The cycle part is denoted by =cycle{...}=.
- Cross-comparison checks: for some state-space $S$, - Cross-comparison checks: for some state-space $S$,
all $P_i\otimes S$ are either all empty, or all non-empty. all $P_i\otimes S$ are either all empty, or all non-empty.
......
...@@ -50,6 +50,8 @@ ...@@ -50,6 +50,8 @@
#include "tgbaalgos/scc.hh" #include "tgbaalgos/scc.hh"
#include "tgbaalgos/dotty.hh" #include "tgbaalgos/dotty.hh"
#include "tgbaalgos/isweakscc.hh" #include "tgbaalgos/isweakscc.hh"
#include "tgbaalgos/reducerun.hh"
#include "tgbaalgos/word.hh"
#include "misc/formater.hh" #include "misc/formater.hh"
#include "tgbaalgos/stats.hh" #include "tgbaalgos/stats.hh"
#include "tgbaalgos/isdet.hh" #include "tgbaalgos/isdet.hh"
...@@ -175,6 +177,7 @@ ARGMATCH_VERIFY(color_args, color_types); ...@@ -175,6 +177,7 @@ ARGMATCH_VERIFY(color_args, color_types);
color_type color_opt = color_if_tty; color_type color_opt = color_if_tty;
const char* bright_red = "\033[01;31m"; const char* bright_red = "\033[01;31m";
const char* bright_white = "\033[01;37m"; const char* bright_white = "\033[01;37m";
const char* bright_yellow = "\033[01;33m";
const char* reset_color = "\033[m"; const char* reset_color = "\033[m";
unsigned states = 200; unsigned states = 200;
...@@ -201,6 +204,15 @@ global_error() ...@@ -201,6 +204,15 @@ global_error()
return std::cerr; return std::cerr;
} }
static std::ostream&
example()
{
if (color_opt)
std::cerr << bright_yellow;
return std::cerr;
}
static void static void
end_error() end_error()
{ {
...@@ -796,14 +808,40 @@ namespace ...@@ -796,14 +808,40 @@ namespace
} }
}; };
static bool static void
is_empty(const spot::tgba* aut) check_empty_prod(const spot::tgba* aut_i, const spot::tgba* aut_j,
size_t i, size_t j)
{ {
spot::emptiness_check* ec = spot::couvreur99(aut); spot::tgba_product* prod = new spot::tgba_product(aut_i, aut_j);
spot::emptiness_check* ec = spot::couvreur99(prod);
spot::emptiness_check_result* res = ec->check(); spot::emptiness_check_result* res = ec->check();
if (res)
{
global_error() << "error: P" << i << "*N" << j
<< " is nonempty";
spot::tgba_run* run = res->accepting_run();
if (run)
{
const spot::tgba_run* runmin = reduce_run(prod, run);
delete run;
std::cerr << "; both automata accept the infinite word\n"
<< " ";
spot::tgba_word w(runmin);
w.simplify();
w.print(example(), prod->get_dict()) << "\n";
delete runmin;
}
else
{
std::cerr << "\n";
}
end_error();
}
delete res; delete res;
delete ec; delete ec;
return !res; delete prod;
} }
static void static void
...@@ -1038,17 +1076,7 @@ namespace ...@@ -1038,17 +1076,7 @@ namespace
if (pos[i]) if (pos[i])
for (size_t j = 0; j < m; ++j) for (size_t j = 0; j < m; ++j)
if (neg[j]) if (neg[j])
{ check_empty_prod(pos[i], neg[j], i, j);
spot::tgba_product* prod =
new spot::tgba_product(pos[i], neg[j]);
if (!is_empty(prod))
{
global_error() << "error: P" << i << "*N" << j
<< " is nonempty\n";
end_error();
}
delete prod;
}
} }
else else
{ {
......
...@@ -68,7 +68,8 @@ tgbaalgos_HEADERS = \ ...@@ -68,7 +68,8 @@ tgbaalgos_HEADERS = \
tau03.hh \ tau03.hh \
tau03opt.hh \ tau03opt.hh \
translate.hh \ translate.hh \
reductgba_sim.hh reductgba_sim.hh \
word.hh
noinst_LTLIBRARIES = libtgbaalgos.la noinst_LTLIBRARIES = libtgbaalgos.la
libtgbaalgos_la_SOURCES = \ libtgbaalgos_la_SOURCES = \
...@@ -114,6 +115,7 @@ libtgbaalgos_la_SOURCES = \ ...@@ -114,6 +115,7 @@ libtgbaalgos_la_SOURCES = \
translate.cc \ translate.cc \
reductgba_sim.cc \ reductgba_sim.cc \
weight.cc \ weight.cc \
weight.hh weight.hh \
word.cc
libtgbaalgos_la_LIBADD = gtec/libgtec.la libtgbaalgos_la_LIBADD = gtec/libgtec.la
// -*- coding: utf-8 -*-
// Copyright (C) 2013 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 "word.hh"
#include "tgba/bddprint.hh"
#include "tgba/bdddict.hh"
namespace spot
{
tgba_word::tgba_word(const tgba_run* run)
{
for (tgba_run::steps::const_iterator i = run->prefix.begin();
i != run->prefix.end(); ++i)
prefix.push_back(i->label);
for (tgba_run::steps::const_iterator i = run->cycle.begin();
i != run->cycle.end(); ++i)
cycle.push_back(i->label);
}
void
tgba_word::simplify()
{
// If all the formulas on the cycle are compatible, reduce the
// cycle to a simple conjunction.
//
// For instance
// !a|!b; b; a&b; cycle{a; b; a&b}
// can be reduced to
// !a|!b; b; a&b; cycle{a&b}
{
bdd all = bddtrue;
for (seq_t::const_iterator i = cycle.begin(); i != cycle.end(); ++i)
all &= *i;
if (all != bddfalse)
{
cycle.clear();
cycle.push_back(all);
}
}
// If the last formula of the prefix is compatible with the
// last formula of the cycle, we can shift the cycle and
// reduce the prefix.
//
// For instance
// !a|!b; b; a&b; cycle{a&b}
// can be reduced to
// !a|!b; cycle{a&b}
while (!prefix.empty())
{
bdd a = prefix.back() & cycle.back();
if (a == bddfalse)
break;
prefix.pop_back();
cycle.pop_back();
cycle.push_front(a);
}
// Get rid of any disjunction.
//
// For instance
// !a|!b; cycle{a&b}
// can be reduced to
// !a&!b; cycle{a&b}
for (seq_t::iterator i = prefix.begin(); i != prefix.end(); ++i)
*i = bdd_satone(*i);
for (seq_t::iterator i = cycle.begin(); i != cycle.end(); ++i)
*i = bdd_satone(*i);
}
std::ostream&
tgba_word::print(std::ostream& os, bdd_dict* d) const
{
if (!prefix.empty())
for (seq_t::const_iterator i = prefix.begin(); i != prefix.end(); ++i)
{
bdd_print_formula(os, d, *i);
os << "; ";
}
assert(!cycle.empty());
bool notfirst = false;
os << "cycle{";
for (seq_t::const_iterator i = cycle.begin(); i != cycle.end(); ++i)
{
if (notfirst)
os << "; ";
notfirst = true;
bdd_print_formula(os, d, *i);
}
os << "}";
return os;
}
}
// -*- coding: utf-8 -*-
// Copyright (C) 2013 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/>.
#ifndef SPOT_TGBAALGOS_WORD_HH
# define SPOT_TGBAALGOS_WORD_HH
# include "emptiness.hh"
namespace spot
{
class bdd_dict;
/// \brief An infinite word stored as a lasso.
struct SPOT_API tgba_word
{
tgba_word(const tgba_run* run);
void simplify();
std::ostream& print(std::ostream& os, bdd_dict* d) const;
typedef std::list<bdd> seq_t;
seq_t prefix;
seq_t cycle;
};
}
#endif // SPOT_TGBAALGOS_WORD_HH
...@@ -108,6 +108,7 @@ TESTS = \ ...@@ -108,6 +108,7 @@ TESTS = \
emptchk.test \ emptchk.test \
emptchke.test \ emptchke.test \
dfs.test \ dfs.test \
ltlcrossce.test \
emptchkr.test \ emptchkr.test \
ltlcounter.test \ ltlcounter.test \
basimul.test \ basimul.test \
......
#!/bin/sh
# -*- coding: utf-8 -*-
# Copyright (C) 2013 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
ltl2tgba=../../bin/ltl2tgba
# The following "fake" script behaves as
# version 1.5.9 of modella, when run as
# 'modella -r12 -g -e %L %T' on
# 'G(F(p0) & F(G(!p1))) | (F(G(!p0)) & G(F(p1)))'
# or its negation. The translation is bogus
# because the automata generated for this formula
# and its negation both include the language of G(!p0).
cat >fake <<\EOF
#!/bin/sh
case $1 in
"| G & F p0 F G ! p1 & F G ! p0 G F p1")
cat <<\END
7 1 0 1 -1 1 t
2 | & ! p0 ! p1 & ! p0 p1
3 t
4 | & ! p0 ! p1 & p0 ! p1
-1 1 0 -1 1 t
5 | & ! p0 ! p1 & ! p0 p1
-1 2 0 -1 5 | & ! p0 ! p1 & ! p0 p1
-1 3 0 1 -1 3 t
-1 4 0 1 -1 4 | & ! p0 ! p1 & p0 ! p1
-1 5 0 -1 5 & ! p0 ! p1
6 & ! p0 p1
-1 6 0 1 -1 2 | & ! p0 ! p1 & ! p0 p1
-1
END
;;
"! | G & F p0 F G ! p1 & F G ! p0 G F p1")
cat <<\END
12 1 0 1 -1 1 t
2 | & ! p0 ! p1 & p0 ! p1
3 t
4 t
5 | & ! p0 ! p1 & ! p0 p1
6 & ! p0 ! p1
-1 1 0 -1 1 t
2 | & ! p0 ! p1 & p0 ! p1
8 | & ! p0 ! p1 & ! p0 p1
6 & ! p0 ! p1
-1 2 0 -1 2 | & ! p0 ! p1 & p0 ! p1
6 & ! p0 ! p1
-1 3 0 -1 3 t
7 t
-1 4 0 -1 7 t
-1 5 0 -1 8 | & ! p0 ! p1 & ! p0 p1
6 & ! p0 ! p1
-1 6 0 1 -1 6 & ! p0 ! p1
-1 7 0 -1 7 | & ! p0 ! p1 & ! p0 p1
9 | & p0 ! p1 & p0 p1
-1 8 0 -1 10 | & ! p0 ! p1 & ! p0 p1
6 & ! p0 ! p1
-1 9 0 -1 11 t
-1 10 0 -1 10 | & ! p0 ! p1 & ! p0 p1
6 & ! p0 ! p1
-1 11 0 1 -1 4 t
-1
END
;;
esac
EOF
chmod +x fake
run 1 ../../bin/ltlcross -f 'G(F(p0) & F(G(!p1))) | (F(G(!p0)) & G(F(p1)))' \
"$ltl2tgba --lbtt %f >%T" "./fake %l >%T" 2> errors
cat errors
grep 'error: P0\*N1 is nonempty' errors
grep 'error: P1\*N0 is nonempty' errors
grep 'error: P1\*N1 is nonempty' errors
test `grep cycle errors | wc -l` = 3
test `grep '^error:' errors | wc -l` = 4
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment