introduce op::strong_X

This was prompted by reports by Andrew Wells and Yong Li.

* NEWS, doc/tl/tl.tex: Document the changes.
* THANKS: Add Andrew.
* bin/ltlfilt.cc: Match --ltl before --from-ltlf if needed.
* spot/parsetl/parsedecl.hh, spot/parsetl/parsetl.yy,
spot/parsetl/scantl.ll: Parse X[!].
* spot/tl/formula.cc, spot/tl/formula.hh: Declare the new operator.
* spot/tl/ltlf.cc: Adjust to handle op::X and op::strong_X correctly.
* spot/tl/dot.cc, spot/tl/mark.cc, spot/tl/mutation.cc,
spot/tl/print.cc, spot/tl/simplify.cc, spot/tl/snf.cc,
spot/tl/unabbrev.cc, spot/twa/formula2bdd.cc,
spot/twaalgos/ltl2taa.cc, spot/twaalgos/ltl2tgba_fm.cc,
tests/core/ltlgrind.test, tests/core/rand.test,
tests/core/sugar.test, tests/python/randltl.ipynb: Adjust.
* tests/core/ltlfilt.test, tests/core/sugar.test,
tests/core/utf8.test: More tests.
This commit is contained in:
Alexandre Duret-Lutz 2019-09-22 21:15:55 +02:00
parent b91ba58bbe
commit be389c5c25
26 changed files with 434 additions and 134 deletions

View file

@ -76,6 +76,7 @@ namespace spot
case op::ap:
case op::Not:
case op::X:
case op::strong_X:
case op::F:
case op::G:
case op::Closure:

View file

@ -242,6 +242,7 @@ namespace spot
C(Star);
C(FStar);
C(first_match);
C(strong_X);
#undef C
}
SPOT_UNREACHABLE();
@ -788,10 +789,20 @@ namespace spot
}
break;
}
case op::X:
// X(1) = 1, X(0) = 0
if (f->is_tt() || f->is_ff())
// X(1) = 1
if (f->is_tt())
return f;
// We do not have X(0)=0 because that
// is not true with finite semantics.
assert(!f->is_eword());
break;
case op::strong_X:
// X[!](0) = 0
if (f->is_ff())
return f;
// Note: with finite semantics X[!](1)≠1.
assert(!f->is_eword());
break;
@ -1176,6 +1187,7 @@ namespace spot
is_.accepting_eword = false;
break;
case op::X:
case op::strong_X:
props = children[0]->props;
is_.not_marked = true;
is_.boolean = false;
@ -1186,6 +1198,10 @@ namespace spot
// is_.syntactic_obligation inherited
// is_.syntactic_recurrence inherited
// is_.syntactic_persistence inherited
// is_.accepting_eword is currently unused outside SEREs, but
// we could make sense of it if we start supporting LTL over
// finite traces.
is_.accepting_eword = false;
break;
case op::F:

View file

@ -57,8 +57,22 @@
#include <cstddef>
#include <limits>
// The strong_X operator was introduced in Spot 2.8.2 to fix an issue
// with from_ltlf(). As adding a new operator is a backward
// incompatibility, causing new warnings from the compiler.
#if defined(SPOT_BUILD) or defined(SPOT_USES_STRONG_X)
// Use #if SPOT_HAS_STRONG_X in code that need to be backward
// compatible with older Spot versions.
# define SPOT_HAS_STRONG_X 1
// You me #define SPOT_WANT_STRONG_X yourself before including
// this file to force the use of STRONG_X
# define SPOT_WANT_STRONG_X 1
#endif
namespace spot
{
/// \ingroup tl_essentials
/// \brief Operator types
enum class op: uint8_t
@ -98,6 +112,9 @@ namespace spot
Star, ///< Star
FStar, ///< Fustion Star
first_match, ///< first_match(sere)
#ifdef SPOT_WANT_STRONG_X
strong_X, ///< strong Next
#endif
};
#ifndef SWIG
@ -899,6 +916,22 @@ namespace spot
return nested_unop_range(op::X, op::Or /* unused */, level, level, f);
}
#if SPOT_WANT_STRONG_X
/// \brief Construct a strong_X
/// @{
SPOT_DEF_UNOP(strong_X);
/// @}
/// \brief Construct a strong_X[n]
///
/// strong_X[3]f = strong_X strong_X strong_X f
static formula strong_X(unsigned level, const formula& f)
{
return nested_unop_range(op::strong_X, op::Or /* unused */,
level, level, f);
}
#endif
/// \brief Construct an F
/// @{
SPOT_DEF_UNOP(F);
@ -1662,6 +1695,9 @@ namespace spot
return *this;
case op::Not:
case op::X:
#if SPOT_HAS_STRONG_X
case op::strong_X:
#endif
case op::F:
case op::G:
case op::Closure:

View file

@ -1,6 +1,6 @@
// -*- coding: utf-8 -*-
// Copyright (C) 2016, 2018 Laboratoire de Recherche et Développement
// de l'Epita (LRDE).
// Copyright (C) 2016, 2018, 2019 Laboratoire de Recherche et
// Développement de l'Epita (LRDE).
//
// This file is part of Spot, a model checking library.
//
@ -29,11 +29,12 @@ namespace spot
auto t = [&alive] (formula f) { return from_ltlf_aux(f, alive); };
switch (auto o = f.kind())
{
case op::X:
case op::strong_X:
case op::F:
return formula::unop(o, formula::And({alive, t(f[0])}));
case op::X: // weak
case op::G:
return formula::G(formula::Or({formula::Not(alive), t(f[0])}));
return formula::unop(o, formula::Or({formula::Not(alive), t(f[0])}));
// Note that the t() function given in the proof of Theorem 1 of
// the IJCAI'13 paper by De Giacomo & Vardi has a typo.
// t(a U b) should be equal to t(a) U t(b & alive).

View file

@ -42,6 +42,7 @@ namespace spot
case op::ap:
case op::Not:
case op::X:
case op::strong_X:
case op::F:
case op::G:
case op::Closure:
@ -109,6 +110,7 @@ namespace spot
case op::ap:
case op::Not:
case op::X:
case op::strong_X:
case op::F:
case op::G:
case op::Closure:

View file

@ -81,6 +81,7 @@ namespace spot
return f;
case op::Not:
case op::X:
case op::strong_X:
case op::F:
case op::G:
case op::first_match:

View file

@ -72,6 +72,7 @@ namespace spot
KEqualBunop,
KGotoBunop,
KFirstMatch,
KStrongX,
};
const char* spot_kw[] = {
@ -112,6 +113,7 @@ namespace spot
"[=",
"[->",
"first_match",
"X[!]",
};
const char* spin_kw[] = {
@ -138,60 +140,62 @@ namespace spot
" || ",
" || ",
" && ",
" && ", // not supported
" & ", // not supported
";", // not supported
":", // not supported
"{", // not supported
"}", // not supported
"]", // not supported
"[*", // not supported
"[+]", // not supported
"[:*", // not supported
"[:+]", // not supported
"[=", // not supported
"[->", // not supported
"first_match", // not supported
" && ", // not supported
" & ", // not supported
";", // not supported
":", // not supported
"{", // not supported
"}", // not supported
"]", // not supported
"[*", // not supported
"[+]", // not supported
"[:*", // not supported
"[:+]", // not supported
"[=", // not supported
"[->", // not supported
"first_match", // not supported
"!X!",
};
const char* wring_kw[] = {
"FALSE",
"TRUE",
"[*0]", // not supported
"[*0]", // not supported
" ^ ",
" -> ",
" <-> ",
" U ",
" R ",
" W ", // rewritten
" M ", // rewritten
"<>-> ", // not supported
"<>=> ", // not supported
"<>+> ", // not supported
"<>=+> ", // not supported
"[]-> ", // not supported
"[]=> ", // not supported
" W ", // rewritten
" M ", // rewritten
"<>-> ", // not supported
"<>=> ", // not supported
"<>+> ", // not supported
"<>=+> ", // not supported
"[]-> ", // not supported
"[]=> ", // not supported
"!",
"X",
"F",
"G",
" + ",
" | ", // not supported
" | ", // not supported
" * ",
" && ", // not supported
" & ", // not supported
";", // not supported
":", // not supported
"{", // not supported
"}", // not supported
"]", // not supported
"[*", // not supported
"[+]", // not supported
"[:*", // not supported
"[:+]", // not supported
"[=", // not supported
"[->", // not supported
"first_match", // not supported
" && ", // not supported
" & ", // not supported
";", // not supported
":", // not supported
"{", // not supported
"}", // not supported
"]", // not supported
"[*", // not supported
"[+]", // not supported
"[:*", // not supported
"[:+]", // not supported
"[=", // not supported
"[->", // not supported
"first_match", // not supported
"X[!]", // not supported, FIXME: we need a syntax
};
const char* utf8_kw[] = {
@ -232,6 +236,7 @@ namespace spot
"[=",
"[->",
"first_match",
"",
};
const char* latex_kw[] = {
@ -272,6 +277,7 @@ namespace spot
"\\SereEqual{",
"\\SereGoto{",
"\\FirstMatch",
"\\StrongX",
};
const char* sclatex_kw[] = {
@ -316,6 +322,7 @@ namespace spot
"^{=",
"^{\\to",
"\\mathsf{first\\_match}",
"\\textcircled{\\mathsf{X}}",
};
static bool
@ -519,7 +526,18 @@ namespace spot
break;
}
case op::X:
emit(KX);
{
emit(KX);
bool cst = f[0].is_constant();
if (cst)
openp();
visit(f[0]);
if (cst)
closep();
break;
}
case op::strong_X:
emit(KStrongX);
visit(f[0]);
break;
case op::F:
@ -1076,6 +1094,9 @@ namespace spot
case op::X:
os_ << 'X';
break;
case op::strong_X:
os_ << 'X'; // unsupported
break;
case op::F:
os_ << 'F';
break;

View file

@ -489,7 +489,13 @@ namespace spot
result = negated ? formula::Not(f) : f;
break;
case op::X:
// !Xa == X!a
case op::strong_X:
// Currently we don't distinguish between weak and
// strong semantics, so we treat the two operators
// identically.
//
// !Xa == X!a
// !X[!]a = X!a
result = formula::X(rec(f[0], negated));
break;
case op::F:
@ -714,7 +720,10 @@ namespace spot
formula
unop_multop(op uop, op mop, vec v)
{
return formula::unop(uop, formula::multop(mop, v));
formula f = formula::unop(uop, formula::multop(mop, v));
if (f.is(op::X) && f[0].is_ff())
return formula::ff();
return f;
}
formula
@ -797,6 +806,7 @@ namespace spot
switch (f.kind())
{
case op::X:
case op::strong_X:
if (res_X && !eu)
{
res_X->emplace_back(f[0]);
@ -950,8 +960,15 @@ namespace spot
case op::FStar:
return f;
case op::X:
case op::strong_X:
{
formula c = f[0];
// The following rules are not trivial simplifications,
// because they are not true in LTLf.
// X(0)=0
// X[!](1)=1
if (c.is_constant())
return c;
// Xf = f if f is both eventual and universal.
if (c.is_universal() && c.is_eventual())
{
@ -3731,9 +3748,10 @@ namespace spot
switch (f.kind())
{
case op::X:
case op::strong_X:
if (g.is_eventual() && syntactic_implication(f[0], g))
return true;
if (g.is(op::X) && syntactic_implication(f[0], g[0]))
if (g.is(op::X, op::strong_X) && syntactic_implication(f[0], g[0]))
return true;
break;
@ -3893,6 +3911,7 @@ namespace spot
case op::G:
case op::X:
case op::strong_X:
if (f.is_universal() && syntactic_implication(f, g[0]))
return true;
break;

View file

@ -103,6 +103,7 @@ namespace spot
case op::ap:
case op::Not:
case op::X:
case op::strong_X:
case op::F:
case op::G:
case op::Closure:

View file

@ -97,6 +97,7 @@ namespace spot
case op::ap:
case op::Not:
case op::X:
case op::strong_X:
case op::Closure:
case op::NegClosure:
case op::NegClosureMarked: