From c71691659b5baee5f1524809fd827991b0474e59 Mon Sep 17 00:00:00 2001 From: Antoine Martin Date: Tue, 9 Nov 2021 16:00:16 +0100 Subject: [PATCH 001/337] tl: implement suffix operator normal form * spot/tl/Makefile.am: New sonf files * spot/tl/sonf.cc, spot/tl/sonf.hh: Here. * python/spot/impl.i: include sonf.hh header * doc/spot.bib: add entry for the SONF paper * tests/Makefile.am: new python tests * tests/python/formulas.ipynb: show sample usage * tests/python/sonf.py: test automata equivalence before/after SONF * NEWS: mention the change --- NEWS | 4 + doc/spot.bib | 12 +++ python/spot/impl.i | 3 + spot/tl/Makefile.am | 2 + spot/tl/sonf.cc | 185 ++++++++++++++++++++++++++++++++++++ spot/tl/sonf.hh | 44 +++++++++ tests/Makefile.am | 1 + tests/python/formulas.ipynb | 56 +++++++++++ tests/python/sonf.py | 41 ++++++++ 9 files changed, 348 insertions(+) create mode 100644 spot/tl/sonf.cc create mode 100644 spot/tl/sonf.hh create mode 100644 tests/python/sonf.py diff --git a/NEWS b/NEWS index 928a25da2..cc0fe35a6 100644 --- a/NEWS +++ b/NEWS @@ -11,6 +11,10 @@ New in spot 2.10.4.dev (net yet released) Library: + - The new function suffix_operator_normal_form() implements + transformation of formulas to Suffix Operator Normal Form, + described in [cimatti.06.fmcad]. + - "original-classes" is a new named property similar to "original-states". It maps an each state to an unsigned integer such that if two classes are in the same class, they are expected diff --git a/doc/spot.bib b/doc/spot.bib index 9f18ad2a9..9d5d6b235 100644 --- a/doc/spot.bib +++ b/doc/spot.bib @@ -214,6 +214,18 @@ doi = {10.1109/DepCoS-RELCOMEX.2009.31} } +@InProceedings{ cimatti.06.fmcad, + author = {Cimatti, Alessandro and Roveri, Marco and Semprini, Simone and + Tonetta, Stefano}, + title = {From {PSL} to {NBA}: a Modular Symbolic Encoding}, + booktitle = {Proceedings of the 6th conference on Formal Methods in Computer + Aided Design (FMCAD'06)}, + pages = {125--133}, + year = {2006}, + publisher = {IEEE Computer Society}, + doi = {10.1109/FMCAD.2006.19} +} + @Article{ cimatti.08.tcad, author = {Alessandro Cimatti and Marco Roveri and Stefano Tonetta}, journal = {IEEE Transactions on Computer Aided Design of Integrated diff --git a/python/spot/impl.i b/python/spot/impl.i index 90a38a55a..7132a5cc6 100644 --- a/python/spot/impl.i +++ b/python/spot/impl.i @@ -94,6 +94,7 @@ #include #include #include +#include #include #include #include @@ -517,6 +518,7 @@ namespace std { %template(vectorbdd) vector; %template(aliasvector) vector>; %template(vectorstring) vector; + %template(pair_formula_vectorstring) pair>; %template(atomic_prop_set) set; %template(relabeling_map) map; } @@ -577,6 +579,7 @@ namespace std { %include %include %include +%include %include %include %include diff --git a/spot/tl/Makefile.am b/spot/tl/Makefile.am index b7362ae99..cdedddffd 100644 --- a/spot/tl/Makefile.am +++ b/spot/tl/Makefile.am @@ -44,6 +44,7 @@ tl_HEADERS = \ remove_x.hh \ simplify.hh \ snf.hh \ + sonf.hh \ unabbrev.hh noinst_LTLIBRARIES = libtl.la @@ -68,4 +69,5 @@ libtl_la_SOURCES = \ remove_x.cc \ simplify.cc \ snf.cc \ + sonf.cc \ unabbrev.cc diff --git a/spot/tl/sonf.cc b/spot/tl/sonf.cc new file mode 100644 index 000000000..29a319039 --- /dev/null +++ b/spot/tl/sonf.cc @@ -0,0 +1,185 @@ +// -*- coding: utf-8 -*- +// Copyright (C) 2021 Laboratoire de Recherche et Developpement 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 . + +#include "config.h" +#include +#include + +#include +#include +#include +#include + +namespace spot +{ + namespace + { + /// Uses `extractor` to extract some parts of the formula and replace them + /// with atomic propositions. + /// + /// Returns (f & g1 & g2 & .. & gn) with g1..gn the extracted subformulas. + /// + /// `extractor` should be a lambda taking the following parameters as input: + /// + /// - `formula` the formula to process + /// - `std::vector&` the vector that stores extracted subformulas + /// - `auto&&` itself, in case it needs to call itself recursively + /// (formula::map for example) + /// - `bool` a boolean indicating whether the lambda is currently being + /// called at the formula's "root" + /// - `bool` a boolean indicating whether the lambda is currently being + /// called inside a toplevel `and` construct. + /// + /// Note that the last 2 boolean arguments can be used as you see fit in + /// your recursive calls, the first one being set to true in the original + /// call, and the second one to false. + /// + /// `extractor` should return the new rewritten formula. + /// + /// auto sample_extractor = [](formula f, + /// std::vector& extracted, + /// auto&& extractor, + /// bool top_level, + /// bool in_top_level_and) -> formula + template + static formula + extract(formula f, Ext extractor) + { + std::vector extracted; + formula new_f = extractor(f, extracted, extractor, true, false); + extracted.push_back(new_f); + return formula::And(extracted); + } + } + + std::pair> + suffix_operator_normal_form(formula f, const std::string prefix) + { + // SONF can only be applied to formulas in negative normal form + f = negative_normal_form(f); + + std::unordered_set used_aps; + std::vector added_aps; + size_t count = 0; + + // identify all used ap names to avoid them when generating new ones + auto ap_indexer = [&used_aps](formula f) noexcept { + if (f.is(op::ap)) + { + used_aps.insert(f.ap_name()); + return true; + } + + return false; + }; + + f.traverse(ap_indexer); + + auto new_ap_name = + [&used_aps, &added_aps, &prefix, &count]() noexcept -> std::string + { + std::string new_name = prefix + std::to_string(count++); + while (used_aps.find(new_name) != used_aps.end()) + new_name = prefix + std::to_string(count++); + used_aps.insert(new_name); + added_aps.push_back(new_name); + return new_name; + }; + + // extracts the SERE part and replaces it with an atomic proposition, + // storing the extracted formula in `extracted` and returning the rewritten + // original formula + auto sonf_extract = [&](formula f, + std::vector& extracted, + auto&& extractor, + bool top_level, + bool in_top_level_and) noexcept -> formula + { + const auto kind = f.kind(); + + switch (kind) + { + case op::G: + { + // skip if shape is G(!ap | (regex []-> formula)) and at top level + if ((top_level || in_top_level_and) + && f[0].is(op::Or) // G(_ | _) + && f[0][0].is(op::Not) // G(!_ | _) + && f[0][0][0].is(op::ap) // G(!ap | _) + && f[0][1].is(op::EConcat, op::UConcat)) // G(!ap | (_ []-> _)) + return f; + else + return f.map(extractor, extracted, extractor, false, false); + } + case op::EConcat: + case op::UConcat: + { + // recurse into rhs first (_ []-> rhs) + formula rhs = + f[1].map(extractor, extracted, extractor, false, false); + f = formula::binop(kind, f[0], rhs); + + formula ap = formula::ap(new_ap_name()); + extracted.push_back(formula::G(formula::Or({formula::Not(ap), f}))); + return ap; + } + default: + // tracking if we're in a op::And at the formula root + in_top_level_and = top_level && f.is(op::And); + return f.map(extractor, extracted, extractor, + false, in_top_level_and); + } + }; + + f = extract(f, sonf_extract); + + auto ltl_extract = [&](formula f, + std::vector& extracted, + auto&& extractor, + [[maybe_unused]] + bool top_level, + [[maybe_unused]] + bool in_top_level_and) noexcept -> formula + { + switch (f.kind()) + { + case op::EConcat: + case op::UConcat: + { + formula rhs = f[1]; + + if (rhs.is(op::ap)) + return f; + + formula ap = formula::ap(new_ap_name()); + extracted.push_back( + formula::G(formula::Or({formula::Not(ap), rhs}))); + + return formula::binop(f.kind(), f[0], ap); + } + default: + return f.map(extractor, extracted, extractor, false, false); + } + }; + + f = extract(f, ltl_extract); + + return {f, added_aps}; + } +} diff --git a/spot/tl/sonf.hh b/spot/tl/sonf.hh new file mode 100644 index 000000000..37ef5d05d --- /dev/null +++ b/spot/tl/sonf.hh @@ -0,0 +1,44 @@ +// -*- coding: utf-8 -*- +// Copyright (C) 2021 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 . + +#pragma once + +#include +#include +#include + +#include + +namespace spot +{ + /// \ingroup tl_rewriting + /// \brief Helper to rewrite a PSL formula in Suffix Operator Normal Form. + /// + /// SONF is described in section 4 of \cite cimatti.06.fmcad + /// + /// The formula output by this function is guaranteed to be in Negative Normal + /// Form. + /// + /// \param f the PSL formula to rewrite + /// \param prefix the prefix to use to name newly introduced aps + /// \return a pair with the rewritten formula, and a vector containing the + /// names of newly introduced aps + SPOT_API std::pair> + suffix_operator_normal_form(formula f, const std::string prefix); +} diff --git a/tests/Makefile.am b/tests/Makefile.am index 1b5d63fee..a6f4ab56c 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -445,6 +445,7 @@ TESTS_python = \ python/setxor.py \ python/simplacc.py \ python/simstate.py \ + python/sonf.py \ python/split.py \ python/streett_totgba.py \ python/streett_totgba2.py \ diff --git a/tests/python/formulas.ipynb b/tests/python/formulas.ipynb index 95241be9d..7075cf653 100644 --- a/tests/python/formulas.ipynb +++ b/tests/python/formulas.ipynb @@ -976,6 +976,62 @@ "print(ap) # print as a string\n", "display(ap) # LaTeX-style, for notebooks" ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Converting to Suffix Operator Normal Form:" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "metadata": {}, + "outputs": [ + { + "data": { + "text/latex": [ + "$\\mathsf{G} (\\{x^{\\star}\\}\\mathrel{\\Box\\kern-1.7pt\\raise.4pt\\hbox{$\\mathord{\\rightarrow}$}} \\mathsf{F} a)$" + ], + "text/plain": [ + "spot.formula(\"G({x[*]}[]-> Fa)\")" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/latex": [ + "$\\mathsf{G} \\mathit{sonf\\_}_{0} \\land \\mathsf{G} (\\lnot \\mathit{sonf\\_}_{1} \\lor \\mathsf{F} a) \\land \\mathsf{G} (\\lnot \\mathit{sonf\\_}_{0} \\lor (\\{x^{\\star}\\}\\mathrel{\\Box\\kern-1.7pt\\raise.4pt\\hbox{$\\mathord{\\rightarrow}$}} \\mathit{sonf\\_}_{1}))$" + ], + "text/plain": [ + "spot.formula(\"Gsonf_0 & G(!sonf_1 | Fa) & G(!sonf_0 | ({x[*]}[]-> sonf_1))\")" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/plain": [ + "('sonf_0', 'sonf_1')" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "f = spot.formula('G({x*} []-> Fa)')\n", + "display(f)\n", + "\n", + "# In addition to the formula, returns a list of newly introduced APs\n", + "f, aps = spot.suffix_operator_normal_form(f, 'sonf_')\n", + "display(f)\n", + "display(aps)" + ] } ], "metadata": { diff --git a/tests/python/sonf.py b/tests/python/sonf.py new file mode 100644 index 000000000..558f90c63 --- /dev/null +++ b/tests/python/sonf.py @@ -0,0 +1,41 @@ +# -*- mode: python; coding: utf-8 -*- +# Copyright (C) 2020 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 . + +import spot + +formulas = """\ +{x[*]}[]-> F({y[*]}<>-> GFz) +<>(({{p12}[*0..3]}[]-> ((p9) || (!(p17)))) V ((true) U (p17))) +{{true} || {[*0]}}[]-> (false) +{{p14} & {{p0}[*]}}[]-> (p11) +{{{!{p6}} -> {!{p3}}}[*]}[]-> ((p3)V((p3) || ((X((false))) && ((p2)V(p18))))) +""" + +for f1 in formulas.splitlines(): + f1 = spot.formula(f1) + a1 = spot.translate(f1) + + f2, aps = spot.suffix_operator_normal_form(f1, 'sonf_') + a2 = spot.translate(f2) + rm = spot.remove_ap() + for ap in aps: + rm.add_ap(ap) + a2 = rm.strip(a2) + + assert(spot.are_equivalent(a1, a2)) From 93fb11017b78ff9dc8fae5e9f4eb7b981bf4bcf5 Mon Sep 17 00:00:00 2001 From: Antoine Martin Date: Wed, 8 Dec 2021 11:31:54 +0100 Subject: [PATCH 002/337] ltlfilt: add --sonf and --sonf-aps flags * bin/ltlfilt.cc: Here. * NEWS: Mention new ltlfilt flags. * tests/Makefile.am, tests/core/sonf.test: Test these flags. --- NEWS | 5 +++ bin/ltlfilt.cc | 35 ++++++++++++++++++ tests/Makefile.am | 3 +- tests/core/sonf.test | 85 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 127 insertions(+), 1 deletion(-) create mode 100644 tests/core/sonf.test diff --git a/NEWS b/NEWS index cc0fe35a6..5e7816b7b 100644 --- a/NEWS +++ b/NEWS @@ -9,6 +9,11 @@ New in spot 2.10.4.dev (net yet released) - autfilt has a new --to-finite option, illustrated on https://spot.lrde.epita.fr/tut12.html + - ltlfilt has a new --sonf option to produce a formula's Suffix + Operator Normal Form, described in [cimatti.06.fmcad]. The + associated option --sonf-aps allows listing the newly introduced + atomic propositions. + Library: - The new function suffix_operator_normal_form() implements diff --git a/bin/ltlfilt.cc b/bin/ltlfilt.cc index cc9e0f02b..af9316192 100644 --- a/bin/ltlfilt.cc +++ b/bin/ltlfilt.cc @@ -38,6 +38,7 @@ #include #include #include +#include #include #include #include @@ -100,6 +101,8 @@ enum { OPT_SIZE_MAX, OPT_SIZE_MIN, OPT_SKIP_ERRORS, + OPT_SONF, + OPT_SONF_APS, OPT_STUTTER_INSENSITIVE, OPT_SUSPENDABLE, OPT_SYNTACTIC_GUARANTEE, @@ -127,6 +130,11 @@ static const argp_option options[] = { "negate", OPT_NEGATE, nullptr, 0, "negate each formula", 0 }, { "nnf", OPT_NNF, nullptr, 0, "rewrite formulas in negative normal form", 0 }, + { "sonf", OPT_SONF, "PREFIX", OPTION_ARG_OPTIONAL, + "rewrite formulas in suffix operator normal form", 0 }, + { "sonf-aps", OPT_SONF_APS, "FILENAME", OPTION_ARG_OPTIONAL, + "when used with --sonf, output the newly introduced atomic " + "propositions", 0 }, { "relabel", OPT_RELABEL, "abc|pnn", OPTION_ARG_OPTIONAL, "relabel all atomic propositions, alphabetically unless " \ "specified otherwise", 0 }, @@ -316,6 +324,7 @@ static range opt_nth = { 0, std::numeric_limits::max() }; static int opt_max_count = -1; static long int match_count = 0; static const char* from_ltlf = nullptr; +static const char* sonf = nullptr; // We want all these variables to be destroyed when we exit main, to @@ -327,6 +336,7 @@ static struct opt_t spot::bdd_dict_ptr dict = spot::make_bdd_dict(); spot::exclusive_ap excl_ap; std::unique_ptr output_define = nullptr; + std::unique_ptr output_sonf = nullptr; spot::formula implied_by = nullptr; spot::formula imply = nullptr; spot::formula equivalent_to = nullptr; @@ -460,6 +470,12 @@ parse_opt(int key, char* arg, struct argp_state*) case OPT_NNF: nnf = true; break; + case OPT_SONF: + sonf = arg ? arg : "sonf_"; + break; + case OPT_SONF_APS: + opt->output_sonf.reset(new output_file(arg ? arg : "-")); + break; case OPT_OBLIGATION: obligation = true; break; @@ -650,6 +666,25 @@ namespace if (nnf) f = simpl.negative_normal_form(f); + if (sonf != nullptr) + { + std::vector new_aps; + std::tie(f, new_aps) = suffix_operator_normal_form(f, sonf); + + if (opt->output_sonf + && output_format != count_output + && output_format != quiet_output) + { + for (size_t i = 0; i < new_aps.size(); ++i) + { + if (i > 0) + opt->output_sonf->ostream() << ' '; + opt->output_sonf->ostream() << new_aps[i]; + } + opt->output_sonf->ostream() << '\n'; + } + } + switch (relabeling) { case ApRelabeling: diff --git a/tests/Makefile.am b/tests/Makefile.am index a6f4ab56c..afcd0c8d2 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -198,7 +198,8 @@ TESTS_tl = \ core/stutter-ltl.test \ core/hierarchy.test \ core/mempool.test \ - core/format.test + core/format.test \ + core/sonf.test TESTS_graph = \ core/graph.test \ diff --git a/tests/core/sonf.test b/tests/core/sonf.test new file mode 100644 index 000000000..0febfc342 --- /dev/null +++ b/tests/core/sonf.test @@ -0,0 +1,85 @@ +#!/bin/sh +# -*- coding: utf-8 -*- +# Copyright (C) 2021 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 . + +. ./defs +set -e + +cat >input < Fa) & G(b -> ({x[*]}[]-> c)) +{x[*]}[]-> F({y[*]}<>-> GFz) +<>(({{p12}[*0..3]}[]-> ((p9) || (!(p17)))) V ((true) U (p17))) +{{true} || {[*0]}}[]-> (false) +{{p14} & {{p0}[*]}}[]-> (p11) +{{{!{p6}} -> {!{p3}}}[*]}[]-> ((p3)V((p3) || ((X((false))) && ((p2)V(p18))))) +X({{true} || {[*0]}}[]-> ((p17) U ((p8) && (p17)))) +({{{p4} || {p5} || {{p16} <-> {{p15} -> {p11}}}}[*]}[]-> (false)) -> (p8) +{[*1..6]}[]-> ((p9) V ((p9) || (!((p4) && (p19))))) +X({{{[*0]} || {{{p10};{p14}}[:*2..3]}}[:*]}<>-> (p8)) +{{true} && {{p8}[*]}}<>-> (!(p10)) +<>(!(({{p7}[*1..2]}<>-> (p11)) V ((!(p9)) && ([]((p11) || (X(p10))))))) +<>({{!{{p5} || {{!{p2}} <-> {p7}}}} & {[*]}}<>-> (p17)) +{{p0} || {{{[*0..2]}[:*2]}[*]}}<>-> ((p1) && (p6)) +EOF + +cat >expected < c)) +s1&G(!s2|GFz)&G(!s0|({y[*]}<>-> s2))&G(!s3|Fs0)&G(!s1|({x[*]}[]-> s3)) +F(s0 R (1 U p17))&G(p9|!p17|!s1)&G(!s0|({p12[*0..3]}[]-> s1)) +s0&G!s1&G(!s0|({1|[*0]}[]-> s1)) +s0&G(!s0|({p14&p0[*]}[]-> p11)) +s0&G(!s1|(p3 R (p3|(X(0)&(p2 R p18)))))&G(!s0|({{!p3|p6}[*]}[]-> s1)) +Xs0&G(!s1|(p17 U (p8&p17)))&G(!s0|({1|[*0]}[]-> s1)) +(p8|s0)&G(!s0|({{p4|p5|{p16 && {p11|!p15}}|{!p11 && p15 && !p16}}[*]}<>-> s1)) +s0&G(!s1|(p9 R (!p4|p9|!p19)))&G(!s0|({[*1..6]}[]-> s1)) +G(!s0|({{[*0]|{p10;p14}[:*2..3]}[:*]}<>-> p8))&Xs0 +s0&G(!p10|!s1)&G(!s0|({1 && p8[*]}<>-> s1)) +F(s0 U (p9|F(!p11&X!p10)))&G(!p11|!s1)&G(!s0|({p7[*1..2]}[]-> s1)) +G(!s0|({{!p5 && {{!p2 && !p7}|{p2 && p7}}}&[*]}<>-> p17))&Fs0 +s0&G(!s1|(p1&p6))&G(!s0|({p0|[*0..2][:*2][*]}<>-> s1)) +EOF + +cat >expected-aps < stdout +diff expected stdout +diff expected-aps stdout-aps + +# check idempotence +ltlfilt -F expected --sonf=s --sonf-aps=stdout-aps \ + | sed 's/ \([|&]\) /\1/g' > stdout +diff expected stdout +# should be 14 empty lines, no new aps introduced this time +test "$(wc -l -m stdout-aps | awk '{print $1 " " $2}')" = "14 14" From 7b7e1b254b8fb699a2844dfa337d77c17c346294 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Thu, 3 Mar 2022 18:01:11 +0100 Subject: [PATCH 003/337] tests: avoid seq Partial fix for #501. * tests/core/prodchain.test: Hardcode the seq output. * tests/core/bricks.test: Use $AWK instead of seq. * tests/core/defs.in: Define $AWK. * NEWS: Mention the bug. --- NEWS | 3 +++ tests/core/bricks.test | 11 ++++++----- tests/core/defs.in | 3 ++- tests/core/prodchain.test | 5 +++-- 4 files changed, 14 insertions(+), 8 deletions(-) diff --git a/NEWS b/NEWS index 5e7816b7b..89a1d52a2 100644 --- a/NEWS +++ b/NEWS @@ -74,6 +74,9 @@ New in spot 2.10.4.dev (net yet released) - work around a portability issue in Flex 2.6.4 preventing compilation on OpenBSD. + - Do not use the seq command in test cases, it is not available + everywhere. + New in spot 2.10.4 (2022-02-01) Bug fixed: diff --git a/tests/core/bricks.test b/tests/core/bricks.test index b98c7e856..37ff57cb0 100644 --- a/tests/core/bricks.test +++ b/tests/core/bricks.test @@ -1,7 +1,7 @@ #!/bin/sh # -*- coding: utf-8 -*- -# Copyright (C) 2020 Laboratoire de Recherche et Développement de -# l'Epita (LRDE). +# Copyright (C) 2020, 2022 Laboratoire de Recherche et Développement +# de l'Epita (LRDE). # # This file is part of Spot, a model checking library. # @@ -21,12 +21,13 @@ . ./defs set -e -seq 0 1999 > expected +# The seq command is not always available, but we assume awk is. +$AWK 'BEGIN{for(x=0;x<2000;++x) print x;}' >expected ../bricks > stdout -cat stdout | head -n 2000 | awk '{print $2}' | sed 's/{//g' | \ - awk -F',' '{print $1}' | sort -n > map +cat stdout | head -n 2000 | $AWK '{print $2}' | sed 's/{//g' | \ + $AWK -F',' '{print $1}' | sort -n > map diff expected map diff --git a/tests/core/defs.in b/tests/core/defs.in index 7df6fdf77..d06a3b67d 100644 --- a/tests/core/defs.in +++ b/tests/core/defs.in @@ -1,5 +1,5 @@ # -*- mode: shell-script; coding: utf-8 -*- -# Copyright (C) 2009, 2010, 2012, 2013, 2015 Laboratoire de Recherche +# Copyright (C) 2009, 2010, 2012, 2013, 2015, 2022 Laboratoire de Recherche # et Développement de l'Epita (LRDE). # Copyright (C) 2003, 2004, 2006 Laboratoire d'Informatique de Paris 6 (LIP6), # département Systèmes Répartis Coopératifs (SRC), Université Pierre @@ -57,6 +57,7 @@ case $srcdir in *) srcdir=../$srcdir esac +AWK='@AWK@' DOT='@DOT@' LBTT="@LBTT@" LBTT_TRANSLATE="@LBTT_TRANSLATE@" diff --git a/tests/core/prodchain.test b/tests/core/prodchain.test index b5037782f..0a7f1a1d9 100755 --- a/tests/core/prodchain.test +++ b/tests/core/prodchain.test @@ -1,6 +1,6 @@ #!/bin/sh # -*- coding: utf-8 -*- -# Copyright (C) 2018 Laboratoire de Recherche et Développement +# Copyright (C) 2018, 2022 Laboratoire de Recherche et Développement # de l'Epita (LRDE). # # This file is part of Spot, a model checking library. @@ -23,7 +23,8 @@ set -e set x shift -for i in `seq 1 42`; do +for i in 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 \ + 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42; do ltl2tgba "{a[*$i]}[]->GFb" > $i.hoa done for i in *.hoa; do From 530cf7ca47c942211ee42118f0cc567102e6a86f Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Fri, 4 Mar 2022 16:59:04 +0100 Subject: [PATCH 004/337] tests: replace all "assert" by unittest assertions If the assert fails because of a comparison, it is useful that the test suite log contains a comparison of these values. unittest.assertEqual() and friends do that for us. * HACKING: Add a section about Python tests. * tests/sanity/style.test: Forbid the use of "assert" in Python tests. * tests/python/298.py, tests/python/341.py, tests/python/471.py, tests/python/accparse2.py, tests/python/aiger.py, tests/python/aliases.py, tests/python/alternating.py, tests/python/bdddict.py, tests/python/bdditer.py, tests/python/bugdet.py, tests/python/complement_semidet.py, tests/python/declenv.py, tests/python/decompose_scc.py, tests/python/det.py, tests/python/dualize.py, tests/python/ecfalse.py, tests/python/except.py, tests/python/game.py, tests/python/gen.py, tests/python/genem.py, tests/python/implies.py, tests/python/intrun.py, tests/python/kripke.py, tests/python/langmap.py, tests/python/ltl2tgba.py, tests/python/ltlf.py, tests/python/ltlparse.py, tests/python/ltlsimple.py, tests/python/mealy.py, tests/python/merge.py, tests/python/mergedge.py, tests/python/misc-ec.py, tests/python/optionmap.py, tests/python/origstate.py, tests/python/otfcrash.py, tests/python/parity.py, tests/python/parsetgba.py, tests/python/pdegen.py, tests/python/prodexpt.py, tests/python/randgen.py, tests/python/relabel.py, tests/python/remfin.py, tests/python/removeap.py, tests/python/rs_like.py, tests/python/satmin.py, tests/python/sbacc.py, tests/python/sccfilter.py, tests/python/sccinfo.py, tests/python/sccsplit.py, tests/python/semidet.py, tests/python/setacc.py, tests/python/setxor.py, tests/python/simplacc.py, tests/python/simstate.py, tests/python/sonf.py, tests/python/split.py, tests/python/streett_totgba.py, tests/python/streett_totgba2.py, tests/python/stutter.py, tests/python/sum.py, tests/python/synthesis.py, tests/python/toparity.py, tests/python/toweak.py, tests/python/tra2tba.py, tests/python/trival.py, tests/python/twagraph.py, tests/python/zlktree.py: Replace all occurrences of "assert" by calls to unittest.TestCase methods. --- HACKING | 44 +++++- tests/python/298.py | 22 +-- tests/python/341.py | 9 +- tests/python/471.py | 9 +- tests/python/accparse2.py | 126 +++++++-------- tests/python/aiger.py | 83 +++++----- tests/python/aliases.py | 12 +- tests/python/alternating.py | 52 +++--- tests/python/bdddict.py | 10 +- tests/python/bdditer.py | 22 +-- tests/python/bugdet.py | 14 +- tests/python/complement_semidet.py | 6 +- tests/python/declenv.py | 23 +-- tests/python/decompose_scc.py | 16 +- tests/python/det.py | 12 +- tests/python/dualize.py | 86 +++++----- tests/python/ecfalse.py | 26 +-- tests/python/except.py | 86 +++++----- tests/python/game.py | 10 +- tests/python/gen.py | 65 ++++---- tests/python/genem.py | 12 +- tests/python/implies.py | 38 ++--- tests/python/intrun.py | 10 +- tests/python/kripke.py | 25 +-- tests/python/langmap.py | 15 +- tests/python/ltl2tgba.py | 12 +- tests/python/ltlf.py | 6 +- tests/python/ltlparse.py | 48 +++--- tests/python/ltlsimple.py | 36 +++-- tests/python/mealy.py | 28 ++-- tests/python/merge.py | 96 +++++------ tests/python/mergedge.py | 36 +++-- tests/python/misc-ec.py | 13 +- tests/python/optionmap.py | 54 ++++--- tests/python/origstate.py | 20 +-- tests/python/otfcrash.py | 6 +- tests/python/parity.py | 54 ++++--- tests/python/parsetgba.py | 8 +- tests/python/pdegen.py | 140 ++++++++-------- tests/python/prodexpt.py | 24 +-- tests/python/randgen.py | 12 +- tests/python/relabel.py | 21 +-- tests/python/remfin.py | 24 +-- tests/python/removeap.py | 16 +- tests/python/rs_like.py | 9 +- tests/python/satmin.py | 246 +++++++++++++++-------------- tests/python/sbacc.py | 24 +-- tests/python/sccfilter.py | 6 +- tests/python/sccinfo.py | 48 +++--- tests/python/sccsplit.py | 9 +- tests/python/semidet.py | 16 +- tests/python/setacc.py | 86 +++++----- tests/python/setxor.py | 22 +-- tests/python/simplacc.py | 16 +- tests/python/simstate.py | 66 ++++---- tests/python/sonf.py | 6 +- tests/python/split.py | 24 +-- tests/python/streett_totgba.py | 14 +- tests/python/streett_totgba2.py | 14 +- tests/python/stutter.py | 18 ++- tests/python/sum.py | 10 +- tests/python/synthesis.py | 6 +- tests/python/toparity.py | 31 ++-- tests/python/toweak.py | 10 +- tests/python/tra2tba.py | 56 +++---- tests/python/trival.py | 42 ++--- tests/python/twagraph.py | 76 ++++----- tests/python/zlktree.py | 65 ++++---- tests/sanity/style.test | 23 ++- 69 files changed, 1314 insertions(+), 1116 deletions(-) diff --git a/HACKING b/HACKING index 8841b033c..c6e127a70 100644 --- a/HACKING +++ b/HACKING @@ -290,8 +290,8 @@ would understand with: make check LOG_DRIVER=$PWD/tools/test-driver-teamcity -Coding conventions -================== +C++ Coding conventions +====================== Here some of the conventions we follow in Spot, so that the code looks homogeneous. Please follow these strictly. Since this is free @@ -682,3 +682,43 @@ Other style recommandations * Always code as if the person who ends up maintaining your code is a violent psychopath who knows where you live. + + +Coding conventions for Python Tests +=================================== + +Unless you have some specific reason to write test cases in C++ (for +instance do test some specific C++ constructions, or to use valgrind), +prefer writing test cases in Python. Writing test cases in C++ +requires some compilation, which slows down the test suite. Doing the +same test in Python is therefore faster, and it has the added benefit +of ensuring that the Python bindings works. + +We have two types of Python tests: Python scripts or jupyter +notebooks. Jupyter notebooks are usually used for a sequence of +examples and comments that can also serve as part of the +documentation. Such jupyter notebooks should be added to the list of +code examples in doc/org/tut.org. Testing a notebook is done by the +tests/python/ipnbdoctest.py scripts, which evaluate each cells, and +checks that the obtainted result is equivalent to the result saved in +the notebook. The process is a bit slow, so plain Python scripts +should be prefered for most tests. + +If you do need a notebook to tests Jupyter-specific code but this +notebook should not be shown in the documentation, use a filename +starting with '_'. + +Tests written as Python scripts should follow the same convention as +shell scripts: exit 0 for PASS, exit 77 for SKIP, and any other exit +code for FAIL. + +Do not use assert() in those scripts, as (1) asserts can be disabled, +and (2) they provide poor insights in case of failures. Instead do + + from unittest import TestCase + tc = TestCase() + +and then use tc.assertTrue(...), tc.assertEqual(..., ...), +tc.assertIn(..., ...), etc. In case of failures, those will print +useful messages in the trace of the tests. For instance multiline +strings that should have been equal will be presented with a diff. diff --git a/tests/python/298.py b/tests/python/298.py index d4865c440..89ddbdb0c 100644 --- a/tests/python/298.py +++ b/tests/python/298.py @@ -1,6 +1,6 @@ # -*- mode: python; coding: utf-8 -*- -# Copyright (C) 2020 Laboratoire de Recherche et Développement de l'Epita -# (LRDE). +# Copyright (C) 2020, 2022 Laboratoire de Recherche et Développement +# de l'Epita (LRDE). # # This file is part of Spot, a model checking library. # @@ -20,21 +20,23 @@ # Test for parts of Issue #298. import spot +from unittest import TestCase +tc = TestCase() a1 = spot.automaton("""genltl --dac=51 | ltl2tgba --med |""") a1 = spot.degeneralize_tba(a1) r1 = spot.tgba_determinize(a1, True, False, False) -assert r1.num_sets() == 3 -assert a1.prop_complete().is_false(); +tc.assertEqual(r1.num_sets(), 3) +tc.assertTrue(a1.prop_complete().is_false()) # This used to fail in 2.9.5 and earlier. -assert r1.prop_complete().is_maybe(); -assert spot.is_complete(r1) +tc.assertTrue(r1.prop_complete().is_maybe()) +tc.assertTrue(spot.is_complete(r1)) a2 = spot.automaton("""genltl --dac=51 | ltl2tgba --high |""") a2 = spot.degeneralize_tba(a2) r2 = spot.tgba_determinize(a2, True, False, False) # This used to fail in 2.9.5 and earlier. -assert r2.num_sets() == 3 -assert a2.prop_complete().is_false(); -assert r2.prop_complete().is_maybe(); -assert spot.is_complete(r2) +tc.assertEqual(r2.num_sets(), 3) +tc.assertTrue(a2.prop_complete().is_false()) +tc.assertTrue(r2.prop_complete().is_maybe()) +tc.assertTrue(spot.is_complete(r2)) diff --git a/tests/python/341.py b/tests/python/341.py index 4c5937149..e828ab07c 100644 --- a/tests/python/341.py +++ b/tests/python/341.py @@ -1,6 +1,6 @@ # -*- mode: python; coding: utf-8 -*- -# Copyright (C) 2017 Laboratoire de Recherche et Développement de l'Epita -# (LRDE). +# Copyright (C) 2017, 2022 Laboratoire de Recherche et Développement +# de l'Epita (LRDE). # # This file is part of Spot, a model checking library. # @@ -19,7 +19,8 @@ import spot from subprocess import _active - +from unittest import TestCase +tc = TestCase() def two_intersecting_automata(): """return two random automata with a non-empty intersection""" @@ -34,4 +35,4 @@ for i in range(5): n = len(_active) print(n, "active processes") -assert(n == 0) +tc.assertEqual(n, 0) diff --git a/tests/python/471.py b/tests/python/471.py index 6fee3a2d3..0fe180554 100644 --- a/tests/python/471.py +++ b/tests/python/471.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Copyright (C) 2021 Laboratoire de Recherche et Développement de l'Epita +# Copyright (C) 2021, 2022 Laboratoire de Recherche et Développement de l'Epita # (LRDE). # # This file is part of Spot, a model checking library. @@ -20,9 +20,12 @@ # Test for Issue #471. import spot +from unittest import TestCase +tc = TestCase() + a = spot.translate('Fa') a = spot.to_generalized_rabin(a, False) r1 = a.intersecting_run(a) r2 = a.accepting_run() -assert str(r1) == str(r2) -assert a.prop_weak().is_true() +tc.assertEqual(str(r1), str(r2)) +tc.assertTrue(a.prop_weak().is_true()) diff --git a/tests/python/accparse2.py b/tests/python/accparse2.py index 4e6eb1cb3..d9c7274a0 100644 --- a/tests/python/accparse2.py +++ b/tests/python/accparse2.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Copyright (C) 2015, 2017-2018 Laboratoire de Recherche et Développement +# Copyright (C) 2015, 2017-2018, 2022 Laboratoire de Recherche et Développement # de l'Epita # # This file is part of Spot, a model checking library. @@ -18,99 +18,101 @@ # along with this program. If not, see . import spot +from unittest import TestCase +tc = TestCase() a = spot.acc_cond(5) a.set_acceptance(spot.acc_code('parity min odd 5')) -assert(a.is_parity() == [True, False, True]) +tc.assertEqual(a.is_parity(), [True, False, True]) a.set_acceptance('parity max even 5') -assert(a.is_parity() == [True, True, False]) +tc.assertEqual(a.is_parity(), [True, True, False]) a.set_acceptance('generalized-Buchi 5') -assert(a.is_parity()[0] == False) -assert(a.is_parity(True)[0] == False) +tc.assertEqual(a.is_parity()[0], False) +tc.assertEqual(a.is_parity(True)[0], False) a.set_acceptance('Inf(4) | (Fin(3)&Inf(2)) | (Fin(3)&Fin(1)&Inf(0))') -assert(a.is_parity()[0] == False) -assert(a.is_parity(True) == [True, True, False]) +tc.assertEqual(a.is_parity()[0], False) +tc.assertEqual(a.is_parity(True), [True, True, False]) -assert a.maybe_accepting([1, 2, 3], [0, 4]).is_true() -assert a.maybe_accepting([0], []).is_true() -assert a.maybe_accepting([0], [3]).is_false() -assert a.maybe_accepting([0, 3], []).is_maybe() -assert a.maybe_accepting([2, 3], [3]).is_false() -assert a.maybe_accepting([2, 3], []).is_maybe() -assert a.maybe_accepting([2], []).is_true() -assert a.maybe_accepting([0, 1], []).is_maybe() -assert a.maybe_accepting([0, 1], [1]).is_false() +tc.assertTrue(a.maybe_accepting([1, 2, 3], [0, 4]).is_true()) +tc.assertTrue(a.maybe_accepting([0], []).is_true()) +tc.assertTrue(a.maybe_accepting([0], [3]).is_false()) +tc.assertTrue(a.maybe_accepting([0, 3], []).is_maybe()) +tc.assertTrue(a.maybe_accepting([2, 3], [3]).is_false()) +tc.assertTrue(a.maybe_accepting([2, 3], []).is_maybe()) +tc.assertTrue(a.maybe_accepting([2], []).is_true()) +tc.assertTrue(a.maybe_accepting([0, 1], []).is_maybe()) +tc.assertTrue(a.maybe_accepting([0, 1], [1]).is_false()) a.set_acceptance('Fin(0)|Fin(1)') -assert a.maybe_accepting([0, 1], [1]).is_maybe() -assert a.maybe_accepting([0, 1], [0, 1]).is_false() -assert a.maybe_accepting([0], []).is_true() -assert a.maybe_accepting([], [0]).is_true() +tc.assertTrue(a.maybe_accepting([0, 1], [1]).is_maybe()) +tc.assertTrue(a.maybe_accepting([0, 1], [0, 1]).is_false()) +tc.assertTrue(a.maybe_accepting([0], []).is_true()) +tc.assertTrue(a.maybe_accepting([], [0]).is_true()) a = spot.acc_cond(0) a.set_acceptance('all') -assert(a.is_rabin() == -1) -assert(a.is_streett() == 0) -assert(a.is_parity() == [True, True, True]) +tc.assertEqual(a.is_rabin(), -1) +tc.assertEqual(a.is_streett(), 0) +tc.assertEqual(a.is_parity(), [True, True, True]) a.set_acceptance('none') -assert(a.is_rabin() == 0) -assert(a.is_streett() == -1) -assert(a.is_parity() == [True, True, False]) +tc.assertEqual(a.is_rabin(), 0) +tc.assertEqual(a.is_streett(), -1) +tc.assertEqual(a.is_parity(), [True, True, False]) a = spot.acc_cond('(Fin(0)&Inf(1))') -assert(a.is_rabin() == 1) -assert(a.is_streett() == -1) +tc.assertEqual(a.is_rabin(), 1) +tc.assertEqual(a.is_streett(), -1) a.set_acceptance('Inf(1)&Fin(0)') -assert(a.is_rabin() == 1) -assert(a.is_streett() == -1) +tc.assertEqual(a.is_rabin(), 1) +tc.assertEqual(a.is_streett(), -1) a.set_acceptance('(Fin(0)|Inf(1))') -assert(a.is_rabin() == -1) -assert(a.is_streett() == 1) +tc.assertEqual(a.is_rabin(), -1) +tc.assertEqual(a.is_streett(), 1) a.set_acceptance('Inf(1)|Fin(0)') -assert(a.is_rabin() == -1) -assert(a.is_streett() == 1) +tc.assertEqual(a.is_rabin(), -1) +tc.assertEqual(a.is_streett(), 1) a = spot.acc_cond('(Fin(0)&Inf(1))|(Fin(2)&Inf(3))') -assert(a.is_rabin() == 2) -assert(a.is_streett() == -1) +tc.assertEqual(a.is_rabin(), 2) +tc.assertEqual(a.is_streett(), -1) a.set_acceptance(spot.acc_code('(Inf(3)&Fin(2))|(Fin(0)&Inf(1))')) -assert(a.is_rabin() == 2) -assert(a.is_streett() == -1) +tc.assertEqual(a.is_rabin(), 2) +tc.assertEqual(a.is_streett(), -1) a.set_acceptance(spot.acc_code('(Inf(2)&Fin(3))|(Fin(0)&Inf(1))')) -assert(a.is_rabin() == -1) -assert(a.is_streett() == -1) +tc.assertEqual(a.is_rabin(), -1) +tc.assertEqual(a.is_streett(), -1) a.set_acceptance(spot.acc_code('(Inf(3)&Fin(2))|(Fin(2)&Inf(1))')) -assert(a.is_rabin() == -1) -assert(a.is_streett() == -1) +tc.assertEqual(a.is_rabin(), -1) +tc.assertEqual(a.is_streett(), -1) a.set_acceptance(spot.acc_code('(Inf(1)&Fin(0))|(Fin(0)&Inf(1))')) -assert(a.is_rabin() == -1) -assert(a.is_streett() == -1) +tc.assertEqual(a.is_rabin(), -1) +tc.assertEqual(a.is_streett(), -1) a.set_acceptance('(Fin(0)&Inf(1))|(Inf(1)&Fin(0))|(Inf(3)&Fin(2))') -assert(a.is_rabin() == 2) -assert(a.is_streett() == -1) +tc.assertEqual(a.is_rabin(), 2) +tc.assertEqual(a.is_streett(), -1) a.set_acceptance('(Fin(0)|Inf(1))&(Fin(2)|Inf(3))') -assert(a.is_rabin() == -1) -assert(a.is_streett() == 2) +tc.assertEqual(a.is_rabin(), -1) +tc.assertEqual(a.is_streett(), 2) a.set_acceptance('(Inf(3)|Fin(2))&(Fin(0)|Inf(1))') -assert(a.is_rabin() == -1) -assert(a.is_streett() == 2) +tc.assertEqual(a.is_rabin(), -1) +tc.assertEqual(a.is_streett(), 2) a.set_acceptance('(Inf(2)|Fin(3))&(Fin(0)|Inf(1))') -assert(a.is_rabin() == -1) -assert(a.is_streett() == -1) +tc.assertEqual(a.is_rabin(), -1) +tc.assertEqual(a.is_streett(), -1) a.set_acceptance('(Inf(3)|Fin(2))&(Fin(2)|Inf(1))') -assert(a.is_rabin() == -1) -assert(a.is_streett() == -1) +tc.assertEqual(a.is_rabin(), -1) +tc.assertEqual(a.is_streett(), -1) a.set_acceptance('(Inf(1)|Fin(0))&(Fin(0)|Inf(1))') -assert(a.is_rabin() == -1) -assert(a.is_streett() == -1) +tc.assertEqual(a.is_rabin(), -1) +tc.assertEqual(a.is_streett(), -1) a.set_acceptance('(Fin(0)|Inf(1))&(Inf(1)|Fin(0))&(Inf(3)|Fin(2))') -assert(a.is_rabin() == -1) -assert(a.is_streett() == 2) +tc.assertEqual(a.is_rabin(), -1) +tc.assertEqual(a.is_streett(), 2) a = spot.acc_code('Inf(0)&Inf(1)&Inf(3) | Fin(0)&(Fin(1)|Fin(3))') u = a.symmetries() -assert u[0] == 0 -assert u[1] == 1 -assert u[2] == 2 -assert u[3] == 1 +tc.assertEqual(u[0], 0) +tc.assertEqual(u[1], 1) +tc.assertEqual(u[2], 2) +tc.assertEqual(u[3], 1) diff --git a/tests/python/aiger.py b/tests/python/aiger.py index 5148fef5f..f490465b0 100644 --- a/tests/python/aiger.py +++ b/tests/python/aiger.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Copyright (C) 2021 Laboratoire de Recherche et +# Copyright (C) 2021, 2022 Laboratoire de Recherche et # Développement de l'Epita # # This file is part of Spot, a model checking library. @@ -18,6 +18,8 @@ # along with this program. If not, see . import spot, buddy +from unittest import TestCase +tc = TestCase() strats = (("""HOA: v1 States: 4 @@ -3346,7 +3348,7 @@ for strat_string, (ins_str, outs_str) in strats: print(f"Mode is {m+ss+ddx+uud}") print(f"""Strat is \n{strat_s.to_str("hoa")}""") print(f"""Aig as aut is \n{strat2_s.to_str("hoa")}""") - assert 0 + raise AssertionError("not a specialization") # Check stepwise simulation @@ -3386,7 +3388,7 @@ for (i, e_latch) in zip(ins, exp_latches): # Variable names -assert(spot.aiger_circuit("""aag 2 2 0 2 0 +tc.assertEqual(spot.aiger_circuit("""aag 2 2 0 2 0 2 4 2 @@ -3394,9 +3396,9 @@ assert(spot.aiger_circuit("""aag 2 2 0 2 0 i0 a i1 b c c -""").to_str() == 'aag 2 2 0 2 0\n2\n4\n2\n1\ni0 a\ni1 b c\no0 o0\no1 o1') +""").to_str(), 'aag 2 2 0 2 0\n2\n4\n2\n1\ni0 a\ni1 b c\no0 o0\no1 o1') -assert(spot.aiger_circuit("""aag 2 2 0 2 0 +tc.assertEqual(spot.aiger_circuit("""aag 2 2 0 2 0 2 4 2 @@ -3404,7 +3406,7 @@ assert(spot.aiger_circuit("""aag 2 2 0 2 0 o0 x o1 y c -""").to_str() == 'aag 2 2 0 2 0\n2\n4\n2\n1\ni0 i0\ni1 i1\no0 x\no1 y') +""").to_str(), 'aag 2 2 0 2 0\n2\n4\n2\n1\ni0 i0\ni1 i1\no0 x\no1 y') def report_missing_exception(): @@ -3415,7 +3417,7 @@ try: 0 """) except SyntaxError as e: - assert str(e) == "\n:1: invalid header line" + tc.assertEqual(str(e), "\n:1: invalid header line") else: report_missing_exception() @@ -3423,14 +3425,15 @@ try: spot.aiger_circuit("""aag 2 2 3 2 0 """) except SyntaxError as e: - assert str(e) == "\n:1: more variables than indicated by max var" + tc.assertEqual(str(e), + "\n:1: more variables than indicated by max var") else: report_missing_exception() try: spot.aiger_circuit("""aag 2 2 0 2 0\n""") except SyntaxError as e: - assert str(e) == "\n:2: expecting input number 2" + tc.assertEqual(str(e), "\n:2: expecting input number 2") else: report_missing_exception() @@ -3439,7 +3442,7 @@ try: 3 """) except SyntaxError as e: - assert str(e) == "\n:2: expecting input number 2" + tc.assertEqual(str(e), "\n:2: expecting input number 2") else: report_missing_exception() @@ -3448,7 +3451,7 @@ try: 3 4 5 """) except SyntaxError as e: - assert str(e) == "\n:2: invalid format for an input" + tc.assertEqual(str(e), "\n:2: invalid format for an input") else: report_missing_exception() @@ -3457,7 +3460,7 @@ try: 2 """) except SyntaxError as e: - assert str(e) == "\n:3: expecting input number 4" + tc.assertEqual(str(e), "\n:3: expecting input number 4") else: report_missing_exception() @@ -3468,7 +3471,7 @@ try: 1 """) except SyntaxError as e: - assert str(e) == "\n:4: invalid format for a latch" + tc.assertEqual(str(e), "\n:4: invalid format for a latch") else: report_missing_exception() @@ -3479,7 +3482,7 @@ try: 1 1 """) except SyntaxError as e: - assert str(e) == "\n:4: expecting latch number 6" + tc.assertEqual(str(e), "\n:4: expecting latch number 6") else: report_missing_exception() @@ -3490,7 +3493,7 @@ try: 6 1 """) except SyntaxError as e: - assert str(e) == "\n:5: expecting latch number 8" + tc.assertEqual(str(e), "\n:5: expecting latch number 8") else: report_missing_exception() @@ -3502,7 +3505,7 @@ try: 8 7 """) except SyntaxError as e: - assert str(e) == "\n:6: expecting an output" + tc.assertEqual(str(e), "\n:6: expecting an output") else: report_missing_exception() @@ -3515,7 +3518,7 @@ try: 9 9 9 """) except SyntaxError as e: - assert str(e) == "\n:6: invalid format for an output" + tc.assertEqual(str(e), "\n:6: invalid format for an output") else: report_missing_exception() @@ -3528,7 +3531,7 @@ try: 9 9 9 """) except SyntaxError as e: - assert str(e) == "\n:6: invalid format for an output" + tc.assertEqual(str(e), "\n:6: invalid format for an output") else: report_missing_exception() @@ -3541,7 +3544,7 @@ try: 9 """) except SyntaxError as e: - assert str(e) == "\n:7: expecting AND gate number 10" + tc.assertEqual(str(e), "\n:7: expecting AND gate number 10") else: report_missing_exception() @@ -3555,7 +3558,7 @@ try: 10 3 8 9 """) except SyntaxError as e: - assert str(e) == "\n:7: invalid format for an AND gate" + tc.assertEqual(str(e), "\n:7: invalid format for an AND gate") else: report_missing_exception() @@ -3569,7 +3572,7 @@ try: 10 3 """) except SyntaxError as e: - assert str(e) == "\n:7: invalid format for an AND gate" + tc.assertEqual(str(e), "\n:7: invalid format for an AND gate") else: report_missing_exception() @@ -3583,7 +3586,7 @@ try: 10 3 8 """) except SyntaxError as e: - assert str(e) == "\n:8: expecting AND gate number 12" + tc.assertEqual(str(e), "\n:8: expecting AND gate number 12") else: report_missing_exception() @@ -3599,7 +3602,7 @@ try: i0 """) except SyntaxError as e: - assert str(e) == "\n:9: could not parse as input name" + tc.assertEqual(str(e), "\n:9: could not parse as input name") else: report_missing_exception() @@ -3616,7 +3619,7 @@ i0 foo i3 bar """) except SyntaxError as e: - assert str(e) == "\n:10: value 3 exceeds input count" + tc.assertEqual(str(e), "\n:10: value 3 exceeds input count") else: report_missing_exception() @@ -3633,7 +3636,7 @@ i1 bar i0 foo """) except SyntaxError as e: - assert str(e) == "\n:9: expecting name for input 0" + tc.assertEqual(str(e), "\n:9: expecting name for input 0") else: report_missing_exception() @@ -3650,8 +3653,8 @@ i0 name with spaces i1 name with spaces """) except SyntaxError as e: - assert str(e) == \ - "\n:10: name 'name with spaces' already used" + tc.assertEqual(str(e), \ + "\n:10: name 'name with spaces' already used") else: report_missing_exception() @@ -3669,7 +3672,7 @@ i1 bar o0 """) except SyntaxError as e: - assert str(e) == "\n:11: could not parse as output name" + tc.assertEqual(str(e), "\n:11: could not parse as output name") else: report_missing_exception() @@ -3689,7 +3692,7 @@ o1 hmm o0 foo bar baz """) except SyntaxError as e: - assert str(e) == "\n:12: expecting name for output 0" + tc.assertEqual(str(e), "\n:12: expecting name for output 0") else: report_missing_exception() @@ -3709,7 +3712,7 @@ o0 hmm o2 foo bar baz """) except SyntaxError as e: - assert str(e) == "\n:13: value 2 exceeds output count" + tc.assertEqual(str(e), "\n:13: value 2 exceeds output count") else: report_missing_exception() @@ -3729,7 +3732,7 @@ o0 foo o1 foo """) except SyntaxError as e: - assert str(e) == "\n:13: name 'foo' already used" + tc.assertEqual(str(e), "\n:13: name 'foo' already used") else: report_missing_exception() @@ -3749,7 +3752,7 @@ o0 foo o1 bar """) except SyntaxError as e: - assert str(e) == "\n:13: name 'bar' already used" + tc.assertEqual(str(e), "\n:13: name 'bar' already used") else: report_missing_exception() @@ -3770,7 +3773,7 @@ o1 baz this is a bug """) except SyntaxError as e: - assert str(e) == "\n:14: unsupported line type" + tc.assertEqual(str(e), "\n:14: unsupported line type") else: report_missing_exception() @@ -3791,8 +3794,8 @@ c this is not a bug """) except SyntaxError as e: - assert str(e) == \ - "\n:10: either all or none of the inputs should be named" + tc.assertEqual(str(e), \ + "\n:10: either all or none of the inputs should be named") else: report_missing_exception() @@ -3815,8 +3818,8 @@ c this is not a bug """) except SyntaxError as e: - assert str(e) == \ - "\n:11-12: either all or none of the inputs should be named" + tc.assertEqual(str(e), \ + "\n:11-12: either all or none of the inputs should be named") else: report_missing_exception() @@ -3841,8 +3844,8 @@ c this is not a bug """) except SyntaxError as e: - assert str(e) == \ - "\n:14-16: either all or none of the outputs should be named" + tc.assertEqual(str(e), \ + "\n:14-16: either all or none of the outputs should be named") else: report_missing_exception() @@ -3866,4 +3869,4 @@ o2 bar c this is not a bug """).to_str() -assert x == spot.aiger_circuit(x).to_str() +tc.assertEqual(x, spot.aiger_circuit(x).to_str()) diff --git a/tests/python/aliases.py b/tests/python/aliases.py index 6f861a880..40dd4d0ec 100644 --- a/tests/python/aliases.py +++ b/tests/python/aliases.py @@ -20,6 +20,8 @@ # Test for parts of Issue #497. import spot +from unittest import TestCase +tc = TestCase() aut = spot.automaton(""" HOA: v1 @@ -63,11 +65,11 @@ State: 0 --END--""") s = aut.to_str('hoa') aut2 = spot.automaton(s) -assert aut.equivalent_to(aut2) +tc.assertTrue(aut.equivalent_to(aut2)) s2 = aut.to_str('hoa') -assert s == s2 +tc.assertEqual(s, s2) -assert s == """HOA: v1 +tc.assertEqual(s, """HOA: v1 States: 1 Start: 0 AP: 3 "x" "y" "z" @@ -105,7 +107,7 @@ State: 0 [@a&2 | @p1&@p0&2] 0 [@a&2] 0 [@p0&2 | @p1&2] 0 ---END--""" +--END--""") # Check what happens to aliases when an AP has been removed, but # the aliases have been preserved... @@ -115,7 +117,7 @@ aut3 = rem.strip(aut) spot.set_aliases(aut3, spot.get_aliases(aut)) s2 = aut3.to_str('hoa') # Aliases based on "x" should have disappeared. -assert(s2 == """HOA: v1 +tc.assertEqual(s2, """HOA: v1 States: 1 Start: 0 AP: 2 "y" "z" diff --git a/tests/python/alternating.py b/tests/python/alternating.py index 7b3a5d713..5b38ca378 100755 --- a/tests/python/alternating.py +++ b/tests/python/alternating.py @@ -1,7 +1,7 @@ #!/usr/bin/python3 # -*- mode: python; coding: utf-8 -*- -# Copyright (C) 2016, 2017, 2021 Laboratoire de Recherche et Développement de -# l'EPITA. +# Copyright (C) 2016-2017, 2021-2022 Laboratoire de Recherche +# et Développement de l'EPITA. # # This file is part of Spot, a model checking library. # @@ -20,6 +20,8 @@ import spot import buddy +from unittest import TestCase +tc = TestCase() aut = spot.make_twa_graph(spot._bdd_dict) @@ -38,9 +40,8 @@ aut.new_edge(2, 2, p1 | p2) tr = [(s, [[x for x in aut.univ_dests(i)] for i in aut.out(s)]) for s in range(3)] -print(tr) -assert [(0, [[1, 2], [0, 1]]), (1, [[0, 2, 1]]), (2, [[2]])] == tr -assert not aut.is_existential() +tc.assertEqual([(0, [[1, 2], [0, 1]]), (1, [[0, 2, 1]]), (2, [[2]])], tr) +tc.assertFalse(aut.is_existential()) received = False try: @@ -49,11 +50,10 @@ try: pass except RuntimeError: received = True -assert received +tc.assertTrue(received) h = aut.to_str('hoa') -print(h) -assert h == """HOA: v1 +tc.assertEqual(h, """HOA: v1 States: 3 Start: 0 AP: 2 "p1" "p2" @@ -68,22 +68,20 @@ State: 1 [0&1] 0&2&1 State: 2 [0 | 1] 2 ---END--""" +--END--""") aut2 = spot.automaton(h) h2 = aut2.to_str('hoa') -print(h2) -assert h != h2 +tc.assertNotEqual(h, h2) # This will sort destination groups aut.merge_univ_dests() h = aut.to_str('hoa') -assert h == h2 +tc.assertEqual(h, h2) aut2.set_univ_init_state([0, 1]) h3 = aut2.to_str('hoa') -print(h3) -assert h3 == """HOA: v1 +tc.assertEqual(h3, """HOA: v1 States: 3 Start: 0&1 AP: 2 "p1" "p2" @@ -98,23 +96,22 @@ State: 1 [0&1] 0&1&2 State: 2 [0 | 1] 2 ---END--""" +--END--""") st = spot.states_and(aut, [0, 2]) st2 = spot.states_and(aut, [1, st]) st3 = spot.states_and(aut, [0, 1, 2]) -assert (st, st2, st3) == (3, 4, 5) +tc.assertEqual((st, st2, st3), (3, 4, 5)) received = False try: st4 = spot.states_and(aut, []) except RuntimeError: received = True -assert received +tc.assertTrue(received) h = aut.to_str('hoa') -print(h) -assert h == """HOA: v1 +tc.assertEqual(h, """HOA: v1 States: 6 Start: 0 AP: 2 "p1" "p2" @@ -136,11 +133,10 @@ State: 4 [0&1] 0&1&2 State: 5 [0&1] 0&1&2 ---END--""" +--END--""") h = spot.split_edges(aut).to_str('hoa') -print(h) -assert h == """HOA: v1 +tc.assertEqual(h, """HOA: v1 States: 6 Start: 0 AP: 2 "p1" "p2" @@ -168,7 +164,7 @@ State: 4 [0&1] 0&1&2 State: 5 [0&1] 0&1&2 ---END--""" +--END--""") # remove_univ_otf @@ -206,11 +202,11 @@ State: 2 --END--""" desalt = spot.remove_univ_otf(aut) -assert(desalt.to_str('hoa') == out) +tc.assertEqual(desalt.to_str('hoa'), out) -assert aut.num_states() == 3 -assert aut.num_edges() == 3 +tc.assertEqual(aut.num_states(), 3) +tc.assertEqual(aut.num_edges(), 3) aut.edge_storage(3).cond = buddy.bddfalse aut.purge_dead_states() -assert aut.num_states() == 1 -assert aut.num_edges() == 0 +tc.assertEqual(aut.num_states(), 1) +tc.assertEqual(aut.num_edges(), 0) diff --git a/tests/python/bdddict.py b/tests/python/bdddict.py index d6222b58f..0172bd050 100644 --- a/tests/python/bdddict.py +++ b/tests/python/bdddict.py @@ -1,6 +1,6 @@ # -*- mode: python; coding: utf-8 -*- -# Copyright (C) 2019, 2021 Laboratoire de Recherche et Développement de l'Epita -# (LRDE). +# Copyright (C) 2019, 2021, 2022 Laboratoire de Recherche et +# Développement de l'Epita (LRDE). # # This file is part of Spot, a model checking library. # @@ -33,6 +33,8 @@ else: gc.collect() import spot +from unittest import TestCase +tc = TestCase() class bdd_holder: @@ -64,7 +66,7 @@ class bdd_holder3: def check_ok(): - assert type(bdict.varnum(spot.formula.ap("a"))) is int + tc.assertIs(type(bdict.varnum(spot.formula.ap("a"))), int) def check_nok(): @@ -123,7 +125,7 @@ debug("h2") h3 = bdd_holder3(h2) var = bdict.register_anonymous_variables(1, h3) debug("h3") -assert var == 2 +tc.assertEqual(var, 2) del h2 gcollect() debug("-h2") diff --git a/tests/python/bdditer.py b/tests/python/bdditer.py index 3d3bb7894..95cc441b3 100644 --- a/tests/python/bdditer.py +++ b/tests/python/bdditer.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Copyright (C) 2017, 2018, 2021 Laboratoire de Recherche et +# Copyright (C) 2017, 2018, 2021, 2022 Laboratoire de Recherche et # Développement de l'Epita (LRDE). # # This file is part of Spot, a model checking library. @@ -24,11 +24,13 @@ import spot import buddy import sys +from unittest import TestCase +tc = TestCase() run = spot.translate('a & !b').accepting_run() b = run.prefix[0].label c = buddy.bdd_satone(b) -assert c != buddy.bddfalse +tc.assertNotEqual(c, buddy.bddfalse) res = [] while c != buddy.bddtrue: var = buddy.bdd_var(c) @@ -40,23 +42,23 @@ while c != buddy.bddtrue: res.append(var) c = h -assert res == [0, -1] +tc.assertEqual(res, [0, -1]) res2 = [] for i in run.aut.ap(): res2.append((str(i), run.aut.register_ap(i))) -assert str(res2) == "[('a', 0), ('b', 1)]" +tc.assertEqual(str(res2), "[('a', 0), ('b', 1)]") f = spot.bdd_to_formula(b) -assert f._is(spot.op_And) -assert f[0]._is(spot.op_ap) -assert f[1]._is(spot.op_Not) -assert f[1][0]._is(spot.op_ap) -assert str(f) == 'a & !b' +tc.assertTrue(f._is(spot.op_And)) +tc.assertTrue(f[0]._is(spot.op_ap)) +tc.assertTrue(f[1]._is(spot.op_Not)) +tc.assertTrue(f[1][0]._is(spot.op_ap)) +tc.assertEqual(str(f), 'a & !b') try: f = spot.bdd_to_formula(b, spot.make_bdd_dict()) sys.exit(2) except RuntimeError as e: - assert "not in the dictionary" in str(e) + tc.assertIn("not in the dictionary", str(e)) diff --git a/tests/python/bugdet.py b/tests/python/bugdet.py index 9e06e0db3..19434c967 100644 --- a/tests/python/bugdet.py +++ b/tests/python/bugdet.py @@ -1,7 +1,7 @@ #!/usr/bin/python3 # -*- mode: python; coding: utf-8 -*- -# Copyright (C) 2016 Laboratoire de Recherche et Développement de -# l'EPITA. +# Copyright (C) 2016, 2022 Laboratoire de Recherche et Développement +# de l'EPITA. # # This file is part of Spot, a model checking library. # @@ -22,6 +22,8 @@ # sent to the Spot mailing list on 2016-10-31. import spot +from unittest import TestCase +tc = TestCase() a = spot.automaton(""" HOA: v1 @@ -80,12 +82,12 @@ State: 7 {0} # was fine. print("use_simulation=True") b1 = spot.tgba_determinize(b, False, True, True, True) -assert b1.num_states() == 5 +tc.assertEqual(b1.num_states(), 5) b1 = spot.remove_fin(spot.dualize(b1)) -assert not a.intersects(b1) +tc.assertFalse(a.intersects(b1)) print("\nuse_simulation=False") b2 = spot.tgba_determinize(b, False, True, False, True) -assert b2.num_states() == 5 +tc.assertEqual(b2.num_states(), 5) b2 = spot.remove_fin(spot.dualize(b2)) -assert not a.intersects(b2) +tc.assertFalse(a.intersects(b2)) diff --git a/tests/python/complement_semidet.py b/tests/python/complement_semidet.py index 5ab4557bc..da06749a3 100644 --- a/tests/python/complement_semidet.py +++ b/tests/python/complement_semidet.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Copyright (C) 2018 Laboratoire de Recherche et Développement de +# Copyright (C) 2018, 2022 Laboratoire de Recherche et Développement de # l'Epita (LRDE). # # This file is part of Spot, a model checking library. @@ -19,6 +19,8 @@ import spot import buddy +from unittest import TestCase +tc = TestCase() def complement(aut): @@ -35,4 +37,4 @@ for aut in spot.automata( comp = complement(aut) semidet_comp = spot.complement_semidet(aut, True) - assert(comp.equivalent_to(semidet_comp)) + tc.assertTrue(comp.equivalent_to(semidet_comp)) diff --git a/tests/python/declenv.py b/tests/python/declenv.py index 868f6ca1d..3ab47736b 100644 --- a/tests/python/declenv.py +++ b/tests/python/declenv.py @@ -1,6 +1,6 @@ # -*- mode: python; coding: utf-8 -*- -# Copyright (C) 2017 Laboratoire de Recherche et Développement de l'Epita -# (LRDE). +# Copyright (C) 2017, 2022 Laboratoire de Recherche et Développement +# de l'Epita (LRDE). # # This file is part of Spot, a model checking library. # @@ -21,6 +21,8 @@ # This file tests various error conditions on the twa API import spot +from unittest import TestCase +tc = TestCase() env = spot.declarative_environment() env.declare("a") @@ -28,26 +30,27 @@ env.declare("b") f1a = spot.parse_infix_psl("a U b") f1b = spot.parse_infix_psl("a U b", env) -assert not f1a.errors -assert not f1b.errors +tc.assertFalse(f1a.errors) +tc.assertFalse(f1b.errors) + # In the past, atomic propositions requires via different environments were # never equal, but this feature was never used and we changed that in Spot 2.0 # for the sake of simplicity. -assert f1a.f == f1b.f +tc.assertEqual(f1a.f, f1b.f) f2 = spot.parse_infix_psl("(a U b) U c", env) -assert f2.errors +tc.assertTrue(f2.errors) ostr = spot.ostringstream() f2.format_errors(ostr) err = ostr.str() -assert "unknown atomic proposition `c'" in err +tc.assertIn("unknown atomic proposition `c'", err) f3 = spot.parse_prefix_ltl("R a d", env) -assert f3.errors +tc.assertTrue(f3.errors) ostr = spot.ostringstream() f3.format_errors(ostr) err = ostr.str() -assert "unknown atomic proposition `d'" in err +tc.assertIn("unknown atomic proposition `d'", err) f4 = spot.parse_prefix_ltl("R a b", env) -assert not f4.errors +tc.assertFalse(f4.errors) diff --git a/tests/python/decompose_scc.py b/tests/python/decompose_scc.py index 5f6ad46cb..47741fb72 100644 --- a/tests/python/decompose_scc.py +++ b/tests/python/decompose_scc.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Copyright (C) 2017, 2021 Laboratoire de Recherche et +# Copyright (C) 2017, 2021, 2022 Laboratoire de Recherche et # Développement de l'Epita # # This file is part of Spot, a model checking library. @@ -18,6 +18,8 @@ # along with this program. If not, see . import spot +from unittest import TestCase +tc = TestCase() aut = spot.translate('(Ga -> Gb) W c') si = spot.scc_info(aut) @@ -26,10 +28,10 @@ si = spot.scc_info(aut) # if the generation of the automaton changes, so just scan # for it. rej = [j for j in range(si.scc_count()) if si.is_rejecting_scc(j)] -assert len(rej) == 1 +tc.assertEqual(len(rej), 1) s = spot.decompose_scc(si, rej[0]).to_str('hoa', '1.1') -assert (s == """HOA: v1.1 +tc.assertEqual(s, """HOA: v1.1 States: 3 Start: 0 AP: 3 "b" "a" "c" @@ -56,7 +58,8 @@ except RuntimeError: else: raise AssertionError -assert (spot.decompose_scc(si, 0, True).to_str('hoa', '1.1') == """HOA: v1.1 +tc.assertEqual(spot.decompose_scc(si, 0, True).to_str('hoa', '1.1'), +"""HOA: v1.1 States: 4 Start: 0 AP: 3 "b" "a" "c" @@ -81,7 +84,8 @@ State: 3 [1] 3 --END--""") -assert (spot.decompose_scc(si, 2, True).to_str('hoa', '1.1') == """HOA: v1.1 +tc.assertEqual(spot.decompose_scc(si, 2, True).to_str('hoa', '1.1'), +"""HOA: v1.1 States: 2 Start: 0 AP: 3 "b" "a" "c" @@ -103,4 +107,4 @@ try: except RuntimeError: pass else: - raise AssertionError + raise AssertionError("missing exception") diff --git a/tests/python/det.py b/tests/python/det.py index 03f07c096..36fa31ff3 100644 --- a/tests/python/det.py +++ b/tests/python/det.py @@ -1,6 +1,6 @@ #!/usr/bin/python3 # -*- mode: python; coding: utf-8 -*- -# Copyright (C) 2021 Laboratoire de Recherche et Développement de +# Copyright (C) 2021, 2022 Laboratoire de Recherche et Développement de # l'EPITA. # # This file is part of Spot, a model checking library. @@ -19,6 +19,8 @@ # along with this program. If not, see . import spot +from unittest import TestCase +tc = TestCase() a = spot.translate('FGa | FGb') @@ -26,10 +28,10 @@ a = spot.translate('FGa | FGb') d = spot.tgba_determinize(a, False, True, True, True, None, -1, True) cld = list(d.get_original_classes()) -assert [0, 1, 2, 3, 3] == cld +tc.assertEqual([0, 1, 2, 3, 3], cld) e = spot.sbacc(d) -assert e.get_original_states() is None +tc.assertIsNone(e.get_original_states()) cle = list(e.get_original_classes()) -assert len(cle) == e.num_states() -assert set(cle) == set(cld) +tc.assertEqual(len(cle), e.num_states()) +tc.assertEqual(set(cle), set(cld)) diff --git a/tests/python/dualize.py b/tests/python/dualize.py index 81d2a2b23..b870e1e5e 100755 --- a/tests/python/dualize.py +++ b/tests/python/dualize.py @@ -1,7 +1,7 @@ #!/usr/bin/python3 # -*- mode: python; coding: utf-8 -*- -# Copyright (C) 2017-2019, 2021 Laboratoire de Recherche et Développement de -# l'EPITA. +# Copyright (C) 2017-2019, 2021-2022 Laboratoire de Recherche et +# Développement de l'EPITA. # # This file is part of Spot, a model checking library. # @@ -20,6 +20,8 @@ import spot import buddy +from unittest import TestCase +tc = TestCase() match_strings = [('is_buchi', 'is_co_buchi'), ('is_generalized_buchi', 'is_generalized_co_buchi'), @@ -79,19 +81,19 @@ def test_aut(aut, d=None): def test_complement(aut): - assert aut.is_deterministic() + tc.assertTrue(aut.is_deterministic()) d = spot.dualize(aut) s = spot.product_or(aut, d) - assert spot.dualize(s).is_empty() + tc.assertTrue(spot.dualize(s).is_empty()) def test_assert(a, d=None): t = test_aut(a, d) if not t[0]: - print (t[1]) - print (a.to_str('hoa')) - print (spot.dualize(a).to_str('hoa')) - assert False + print(t[1]) + print(a.to_str('hoa')) + print(spot.dualize(a).to_str('hoa')) + tc.assertTrue(t[0]) aut = spot.translate('a') @@ -101,7 +103,7 @@ test_assert(aut) dual = spot.dualize(aut) h = dual.to_str('hoa') -assert h == """HOA: v1 +tc.assertEqual(h, """HOA: v1 States: 3 Start: 1 AP: 1 "a" @@ -117,7 +119,7 @@ State: 1 [!0] 2 State: 2 [t] 2 ---END--""" +--END--""") aut = spot.automaton(""" HOA: v1 @@ -141,7 +143,7 @@ test_assert(aut) dual = spot.dualize(aut) h = dual.to_str('hoa') -assert h == """HOA: v1 +tc.assertEqual(h, """HOA: v1 States: 4 Start: 0 AP: 2 "a" "b" @@ -161,7 +163,7 @@ State: 2 {0} [!1] 3 State: 3 [t] 3 ---END--""" +--END--""") aut = spot.automaton(""" HOA: v1 @@ -186,7 +188,7 @@ test_assert(aut) dual = spot.dualize(aut) h = dual.to_str('hoa') -assert h == """HOA: v1 +tc.assertEqual(h, """HOA: v1 States: 2 Start: 1 AP: 2 "a" "b" @@ -198,7 +200,7 @@ State: 0 [t] 0 State: 1 [!0 | !1] 0 ---END--""" +--END--""") aut = spot.automaton(""" HOA: v1 @@ -219,10 +221,10 @@ State: 3 {1} --END--""") dual = spot.dualize(aut) -assert dualtype(aut, dual) +tc.assertTrue(dualtype(aut, dual)) h = dual.to_str('hoa') -assert h == """HOA: v1 +tc.assertEqual(h, """HOA: v1 States: 2 Start: 1 AP: 2 "a" "b" @@ -234,7 +236,7 @@ State: 0 [t] 0 State: 1 [!0 | !1] 0 ---END--""" +--END--""") aut = spot.automaton(""" HOA: v1 @@ -255,10 +257,10 @@ State: 3 {0} --END--""") dual = spot.dualize(aut) -assert dualtype(aut, dual) +tc.assertTrue(dualtype(aut, dual)) h = dual.to_str('hoa') -assert h == """HOA: v1 +tc.assertEqual(h, """HOA: v1 States: 5 Start: 0 AP: 2 "a" "b" @@ -280,7 +282,7 @@ State: 3 {0} [t] 3 State: 4 [t] 4 ---END--""" +--END--""") aut = spot.automaton(""" HOA: v1 @@ -302,10 +304,10 @@ State: 2 --END--""") dual = spot.dualize(aut) -assert dualtype(aut, dual) +tc.assertTrue(dualtype(aut, dual)) h = dual.to_str('hoa') -assert h == """HOA: v1 +tc.assertEqual(h, """HOA: v1 States: 4 Start: 0 AP: 2 "a" "b" @@ -327,7 +329,7 @@ State: 2 [!0&!1] 0&2 State: 3 [t] 3 ---END--""" +--END--""") aut = spot.automaton(""" HOA: v1 @@ -348,10 +350,10 @@ State: 2 --END--""") dual = spot.dualize(aut) -assert dualtype(aut, dual) +tc.assertTrue(dualtype(aut, dual)) h = dual.to_str('hoa') -assert h == """HOA: v1 +tc.assertEqual(h, """HOA: v1 States: 1 Start: 0 AP: 1 "a" @@ -362,7 +364,7 @@ properties: deterministic terminal --BODY-- State: 0 [t] 0 ---END--""" +--END--""") aut = spot.automaton(""" HOA: v1 @@ -382,10 +384,10 @@ State: 2 --END--""") dual = spot.dualize(aut) -assert dualtype(aut, dual) +tc.assertTrue(dualtype(aut, dual)) h = dual.to_str('hoa') -assert h == """HOA: v1 +tc.assertEqual(h, """HOA: v1 States: 1 Start: 0 AP: 1 "a" @@ -396,7 +398,7 @@ properties: deterministic terminal --BODY-- State: 0 [t] 0 ---END--""" +--END--""") aut = spot.automaton(""" HOA: v1 @@ -419,7 +421,7 @@ State: 2 dual = spot.dualize(aut) h = dual.to_str('hoa') -assert h == """HOA: v1 +tc.assertEqual(h, """HOA: v1 States: 3 Start: 0 AP: 2 "a" "b" @@ -435,7 +437,7 @@ State: 1 {0} [t] 1 State: 2 [t] 2 ---END--""" +--END--""") aut = spot.automaton(""" HOA: v1 @@ -456,10 +458,10 @@ State: 2 dual = spot.dualize(aut) -assert dualtype(aut, dual) +tc.assertTrue(dualtype(aut, dual)) h = dual.to_str('hoa') -assert h == """HOA: v1 +tc.assertEqual(h, """HOA: v1 States: 2 Start: 0 AP: 1 "a" @@ -471,7 +473,7 @@ State: 0 [!0] 1 State: 1 {0} [t] 1 ---END--""" +--END--""") aut = spot.automaton(""" HOA: v1 @@ -495,10 +497,10 @@ State: 3 {0} --END--""") dual = spot.dualize(aut) -assert dualtype(aut, dual) +tc.assertTrue(dualtype(aut, dual)) h = dual.to_str('hoa') -assert h == """HOA: v1 +tc.assertEqual(h, """HOA: v1 States: 3 Start: 0 AP: 1 "a" @@ -515,7 +517,7 @@ State: 1 [0] 2 State: 2 {0} [t] 2 ---END--""" +--END--""") aut = spot.automaton(""" HOA: v1 @@ -536,10 +538,10 @@ State: 2 --END--""") dual = spot.dualize(aut) -assert dualtype(aut, dual) +tc.assertTrue(dualtype(aut, dual)) h = dual.to_str('hoa') -assert h == """HOA: v1 +tc.assertEqual(h, """HOA: v1 States: 3 Start: 0 AP: 1 "a" @@ -555,14 +557,14 @@ State: 1 {0} [t] 0 State: 2 {1} [t] 0 ---END--""" +--END--""") aut = spot.translate('G!a R XFb') test_assert(aut) dual = spot.dualize(aut) h = dual.to_str('hoa') -assert h == """HOA: v1 +tc.assertEqual(h, """HOA: v1 States: 5 Start: 0 AP: 2 "a" "b" @@ -589,7 +591,7 @@ State: 3 {0} [0] 4 State: 4 [t] 4 ---END--""" +--END--""") opts = spot.option_map() opts.set('output', spot.randltlgenerator.LTL) diff --git a/tests/python/ecfalse.py b/tests/python/ecfalse.py index 36301914b..ccbaa2693 100644 --- a/tests/python/ecfalse.py +++ b/tests/python/ecfalse.py @@ -1,6 +1,6 @@ # -*- mode: python; coding: utf-8 -*- -# Copyright (C) 2020 Laboratoire de Recherche et Développement de l'Epita -# (LRDE). +# Copyright (C) 2020, 2022 Laboratoire de Recherche et Développement +# de l'Epita (LRDE). # # This file is part of Spot, a model checking library. # @@ -19,6 +19,8 @@ import spot from buddy import bddfalse, bddtrue +from unittest import TestCase +tc = TestCase() a = spot.automaton(""" HOA: v1 @@ -43,8 +45,8 @@ for e in a.out(1): if e.dst == 0: e.cond = bddfalse -assert a.accepting_run() is None -assert a.is_empty() +tc.assertIsNone(a.accepting_run()) +tc.assertTrue(a.is_empty()) for name in ['SE05', 'CVWY90', 'GV04', 'Cou99(shy)', 'Cou99', 'Tau03']: print(name) @@ -52,13 +54,13 @@ for name in ['SE05', 'CVWY90', 'GV04', 'Cou99(shy)', 'Cou99', 'Tau03']: res = ec.check() if res is not None: print(res.accepting_run()) - assert res is None + tc.assertIsNone(res) si = spot.scc_info(a) -assert si.scc_count() == 1 # only one accessible SCC +tc.assertEqual(si.scc_count(), 1) # only one accessible SCC a.set_init_state(0) si = spot.scc_info(a) -assert si.scc_count() == 2 +tc.assertEqual(si.scc_count(), 2) a = spot.automaton("""HOA: v1 States: 11 Start: 0 AP: 2 "a" "b" Acceptance: 8 (Fin(0) | Inf(1)) & (Fin(2) | Inf(3)) & ((Fin(4) & Inf(5)) | (Fin(6) & Inf(7))) @@ -71,16 +73,16 @@ State: 5 State: 6 State: 7 [!0&!1] 1 {4 6 7} [!0&!1] 2 {5 6} State: 8 [!0&!1] 2 {4} State: 9 [!0&!1] 2 {0 4} [!0&!1] 4 {3 4} State: 10 --END-- """) r = a.accepting_run() -assert r is not None -assert r.replay(spot.get_cout()) +tc.assertIsNotNone(r) +tc.assertTrue(r.replay(spot.get_cout())) for e in a.out(7): if e.dst == 2: e.cond = bddfalse s = a.accepting_run() -assert s is not None -assert s.replay(spot.get_cout()) +tc.assertIsNotNone(s) +tc.assertTrue(s.replay(spot.get_cout())) for e in a.out(2): if e.dst == 1: e.cond = bddfalse s = a.accepting_run() -assert s is None +tc.assertIsNone(s) diff --git a/tests/python/except.py b/tests/python/except.py index 178e419b4..76f17f76c 100644 --- a/tests/python/except.py +++ b/tests/python/except.py @@ -24,6 +24,8 @@ import spot import buddy +from unittest import TestCase +tc = TestCase() def report_missing_exception(): @@ -35,7 +37,7 @@ aut.set_acceptance(spot.acc_cond("parity min even 4")) try: spot.iar(aut) except RuntimeError as e: - assert 'iar() expects Rabin-like or Streett-like input' in str(e) + tc.assertIn('iar() expects Rabin-like or Streett-like input', str(e)) else: report_missing_exception() @@ -43,7 +45,7 @@ alt = spot.dualize(spot.translate('FGa | FGb')) try: spot.tgba_determinize(alt) except RuntimeError as e: - assert 'tgba_determinize() does not support alternation' in str(e) + tc.assertIn('tgba_determinize() does not support alternation', str(e)) else: report_missing_exception() @@ -52,18 +54,18 @@ aps = aut.ap() rem = spot.remove_ap() rem.add_ap('"a"=0,b') aut = rem.strip(aut) -assert aut.ap() == aps[2:] +tc.assertEqual(aut.ap(), aps[2:]) try: rem.add_ap('"a=0,b') except ValueError as e: - assert """missing closing '"'""" in str(e) + tc.assertIn("""missing closing '"'""", str(e)) else: report_missing_exception() try: rem.add_ap('a=0=b') except ValueError as e: - assert """unexpected '=' at position 3""" in str(e) + tc.assertIn("""unexpected '=' at position 3""", str(e)) else: report_missing_exception() @@ -73,7 +75,7 @@ for meth in ('scc_has_rejecting_cycle', 'is_inherently_weak_scc', try: getattr(spot, meth)(si, 20) except ValueError as e: - assert "invalid SCC number" in str(e) + tc.assertIn("invalid SCC number", str(e)) else: report_missing_exception() @@ -89,14 +91,15 @@ si = spot.scc_info(alt) try: si.determine_unknown_acceptance() except RuntimeError as e: - assert "scc_info::determine_unknown_acceptance() does not supp" in str(e) + tc.assertIn("scc_info::determine_unknown_acceptance() does not supp", + str(e)) else: report_missing_exception() try: alt.set_init_state(999) except ValueError as e: - assert "set_init_state()" in str(e) + tc.assertIn("set_init_state()", str(e)) else: report_missing_exception() @@ -107,7 +110,7 @@ alt.set_init_state(u) try: alt.set_init_state(u - 1) except ValueError as e: - assert "set_init_state()" in str(e) + tc.assertIn("set_init_state()", str(e)) else: report_missing_exception() @@ -116,21 +119,21 @@ r = spot.twa_run(aut) try: a = r.as_twa() except RuntimeError as e: - assert "empty cycle" in str(e) + tc.assertIn("empty cycle", str(e)) else: report_missing_exception() try: a = r.replay(spot.get_cout()) except RuntimeError as e: - assert "empty cycle" in str(e) + tc.assertIn("empty cycle", str(e)) else: report_missing_exception() try: a = r.reduce() except RuntimeError as e: - assert "empty cycle" in str(e) + tc.assertIn("empty cycle", str(e)) else: report_missing_exception() @@ -138,12 +141,12 @@ a = spot.translate('Fa') a = spot.to_generalized_rabin(a, False) r = a.accepting_run() r = r.reduce() -assert r.cycle[0].acc == spot.mark_t([1]) +tc.assertEqual(r.cycle[0].acc, spot.mark_t([1])) r.cycle[0].acc = spot.mark_t([0]) try: r.reduce(); except RuntimeError as e: - assert "expects an accepting cycle" in str(e) + tc.assertIn("expects an accepting cycle", str(e)) else: report_missing_exception() @@ -151,7 +154,7 @@ f = spot.formula('GF(a | Gb)') try: spot.gf_guarantee_to_ba(f, spot._bdd_dict) except RuntimeError as e: - assert "guarantee" in str(e) + tc.assertIn("guarantee", str(e)) else: report_missing_exception() @@ -159,7 +162,7 @@ f = spot.formula('FG(a | Fb)') try: spot.fg_safety_to_dca(f, spot._bdd_dict) except RuntimeError as e: - assert "safety" in str(e) + tc.assertIn("safety", str(e)) else: report_missing_exception() @@ -168,28 +171,28 @@ m = spot.mark_t([n - 1]) try: m = spot.mark_t([0]) << n except RuntimeError as e: - assert "Too many acceptance sets" in str(e) + tc.assertIn("Too many acceptance sets", str(e)) else: report_missing_exception() try: m.set(n) except RuntimeError as e: - assert "bit index is out of bounds" in str(e) + tc.assertIn("bit index is out of bounds", str(e)) else: report_missing_exception() try: m = spot.mark_t([0, n, 1]) except RuntimeError as e: - assert "Too many acceptance sets used. The limit is" in str(e) + tc.assertIn("Too many acceptance sets used. The limit is", str(e)) else: report_missing_exception() try: spot.complement_semidet(spot.translate('Gb R a', 'ba')) except RuntimeError as e: - assert "requires a semi-deterministic input" in str(e) + tc.assertIn("requires a semi-deterministic input", str(e)) else: report_missing_exception() @@ -197,52 +200,55 @@ try: spot.translate('F(G(a | !a) & ((b <-> c) W d))', 'det', 'any') except ValueError as e: s = str(e) - assert 'det' in s - assert 'any' in s + tc.assertIn('det', s) + tc.assertIn('any', s) else: report_missing_exception() a1 = spot.translate('FGa') a2 = spot.translate('Gb') -assert not spot.is_deterministic(a1) -assert spot.is_deterministic(a2) +tc.assertFalse(spot.is_deterministic(a1)) +tc.assertTrue(spot.is_deterministic(a2)) try: spot.product_xor(a1, a2) except RuntimeError as e: - assert "product_xor() only works with deterministic automata" in str(e) + tc.assertIn("product_xor() only works with deterministic automata", str(e)) else: report_missing_exception() try: spot.product_xor(a2, a1) except RuntimeError as e: - assert "product_xor() only works with deterministic automata" in str(e) + tc.assertIn("product_xor() only works with deterministic automata", str(e)) else: report_missing_exception() try: spot.product_xnor(a1, a2) except RuntimeError as e: - assert "product_xnor() only works with deterministic automata" in str(e) + tc.assertIn("product_xnor() only works with deterministic automata", str(e)) else: report_missing_exception() try: spot.product_xnor(a2, a1) except RuntimeError as e: - assert "product_xnor() only works with deterministic automata" in str(e) + tc.assertIn("product_xnor() only works with deterministic automata", str(e)) else: report_missing_exception() try: spot.solve_safety_game(a1) except RuntimeError as e: - assert "solve_safety_game(): arena should have true acceptance" in str(e) + tc.assertIn( + "solve_safety_game(): arena should have true acceptance", + str(e)) else: report_missing_exception() try: spot.solve_parity_game(a1) except RuntimeError as e: - assert "solve_parity_game(): arena must have max-odd acceptance condition" \ - in str(e) + tc.assertIn( + "solve_parity_game(): arena must have max-odd acceptance condition", + str(e)) else: report_missing_exception() @@ -250,16 +256,16 @@ else: try: spot.formula_Star(spot.formula("a"), 10, 333) except OverflowError as e: - assert "333" in str(e) - assert "254" in str(e) + tc.assertIn("333", str(e)) + tc.assertIn("254", str(e)) else: report_missing_exception() try: spot.formula_FStar(spot.formula("a"), 333, 400) except OverflowError as e: - assert "333" in str(e) - assert "254" in str(e) + tc.assertIn("333", str(e)) + tc.assertIn("254", str(e)) else: report_missing_exception() @@ -267,15 +273,15 @@ try: spot.formula_nested_unop_range(spot.op_F, spot.op_Or, 333, 400, spot.formula("a")) except OverflowError as e: - assert "333" in str(e) - assert "254" in str(e) + tc.assertIn("333", str(e)) + tc.assertIn("254", str(e)) else: report_missing_exception() try: spot.formula_FStar(spot.formula("a"), 50, 40) except OverflowError as e: - assert "reversed" in str(e) + tc.assertIn("reversed", str(e)) else: report_missing_exception() @@ -287,5 +293,5 @@ try: a.to_str() except RuntimeError as e: se = str(e) - assert "synthesis-outputs" in se - assert "unregistered proposition" in se + tc.assertIn("synthesis-outputs", se) + tc.assertIn("unregistered proposition", se) diff --git a/tests/python/game.py b/tests/python/game.py index 9d77c153d..d7aec2f38 100644 --- a/tests/python/game.py +++ b/tests/python/game.py @@ -1,6 +1,6 @@ #!/usr/bin/python3 # -*- mode: python; coding: utf-8 -*- -# Copyright (C) 2020 Laboratoire de Recherche et Développement de +# Copyright (C) 2020, 2022 Laboratoire de Recherche et Développement de # l'EPITA. # # This file is part of Spot, a model checking library. @@ -19,6 +19,8 @@ # along with this program. If not, see . import spot +from unittest import TestCase +tc = TestCase() g = spot.automaton("""HOA: v1 States: 9 Start: 0 AP: 2 "a" "b" acc-name: Streett 1 Acceptance: 2 Fin(0) | Inf(1) properties: @@ -27,10 +29,10 @@ trans-labels explicit-labels state-acc spot-state-player: 0 1 0 1 0 1 {1} [0] 8 State: 3 {1} [1] 4 State: 4 {1} [0] 5 State: 5 {1} [0] 6 State: 6 {1} [1] 7 State: 7 State: 8 {1} [0] 2 --END--""") -assert spot.solve_parity_game(g) == False +tc.assertFalse(spot.solve_parity_game(g)) s = spot.highlight_strategy(g).to_str("HOA", "1.1") -assert s == """HOA: v1.1 +tc.assertEqual(s, """HOA: v1.1 States: 9 Start: 0 AP: 2 "a" "b" @@ -60,4 +62,4 @@ State: 6 {1} State: 7 State: 8 {1} [0] 2 ---END--""" +--END--""") diff --git a/tests/python/gen.py b/tests/python/gen.py index dd844741c..a9fed6890 100644 --- a/tests/python/gen.py +++ b/tests/python/gen.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Copyright (C) 2017 Laboratoire de Recherche et Développement de +# Copyright (C) 2017, 2022 Laboratoire de Recherche et Développement de # l'Epita (LRDE). # # This file is part of Spot, a model checking library. @@ -23,63 +23,66 @@ import spot.gen as gen from sys import exit +from unittest import TestCase +tc = TestCase() k2 = gen.aut_pattern(gen.AUT_KS_NCA, 2) -assert k2.prop_state_acc() -assert k2.num_states() == 5 -assert k2.prop_universal().is_false() -assert k2.prop_inherently_weak().is_false() -assert k2.prop_stutter_invariant().is_false() -assert k2.prop_semi_deterministic().is_false() -assert k2.prop_deterministic().is_false() -assert k2.prop_terminal().is_false() +tc.assertTrue(k2.prop_state_acc()) +tc.assertEqual(k2.num_states(), 5) +tc.assertTrue(k2.prop_universal().is_false()) +tc.assertTrue(k2.prop_inherently_weak().is_false()) +tc.assertTrue(k2.prop_stutter_invariant().is_false()) +tc.assertTrue(k2.prop_semi_deterministic().is_false()) +tc.assertTrue(k2.prop_deterministic().is_false()) +tc.assertTrue(k2.prop_terminal().is_false()) # to_str is defined in the spot package, so this makes sure # the type returned by spot.gen.ks_nca() is the correct one. -assert 'to_str' in dir(k2) +tc.assertIn('to_str', dir(k2)) k3 = gen.aut_pattern(gen.AUT_L_NBA, 3) -assert k3.num_states() == 10 -assert k3.prop_state_acc() -assert k3.prop_universal().is_false() -assert k3.prop_inherently_weak().is_false() -assert k3.prop_stutter_invariant().is_false() -assert k3.prop_semi_deterministic().is_false() -assert k3.prop_deterministic().is_false() -assert k3.prop_terminal().is_false() +tc.assertEqual(k3.num_states(), 10) +tc.assertTrue(k3.prop_state_acc()) +tc.assertTrue(k3.prop_universal().is_false()) +tc.assertTrue(k3.prop_inherently_weak().is_false()) +tc.assertTrue(k3.prop_stutter_invariant().is_false()) +tc.assertTrue(k3.prop_semi_deterministic().is_false()) +tc.assertTrue(k3.prop_deterministic().is_false()) +tc.assertTrue(k3.prop_terminal().is_false()) -assert k2.get_dict() == k3.get_dict() +tc.assertEqual(k2.get_dict(), k3.get_dict()) try: gen.aut_pattern(gen.AUT_KS_NCA, 0) except RuntimeError as e: - assert 'positive argument' in str(e) + tc.assertIn('positive argument', str(e)) else: exit(2) f = gen.ltl_pattern(gen.LTL_AND_F, 3) -assert f.size() == 3 -assert gen.ltl_pattern_name(gen.LTL_AND_F) == "and-f" +tc.assertEqual(f.size(), 3) +tc.assertEqual(gen.ltl_pattern_name(gen.LTL_AND_F), "and-f") try: gen.ltl_pattern(1000, 3) except RuntimeError as e: - assert 'unsupported pattern' in str(e) + tc.assertIn('unsupported pattern', str(e)) else: exit(2) try: gen.ltl_pattern(gen.LTL_OR_G, -10) except RuntimeError as e: - assert 'or-g' in str(e) - assert 'positive' in str(e) + tc.assertIn('or-g', str(e)) + tc.assertIn('positive', str(e)) else: exit(2) -assert 40 == sum(p.size() for p in gen.ltl_patterns((gen.LTL_OR_G, 1, 5), - (gen.LTL_GH_Q, 3), - gen.LTL_EH_PATTERNS)) +tc.assertEqual(40, sum(p.size() + for p in gen.ltl_patterns((gen.LTL_OR_G, 1, 5), + (gen.LTL_GH_Q, 3), + gen.LTL_EH_PATTERNS))) -assert 32 == sum(p.num_states() - for p in gen.aut_patterns((gen.AUT_L_NBA, 1, 3), - (gen.AUT_KS_NCA, 5))) +tc.assertEqual(32, sum(p.num_states() + for p in gen.aut_patterns((gen.AUT_L_NBA, 1, 3), + (gen.AUT_KS_NCA, 5)))) diff --git a/tests/python/genem.py b/tests/python/genem.py index 5da9ce85c..0c9d0809a 100644 --- a/tests/python/genem.py +++ b/tests/python/genem.py @@ -1,6 +1,6 @@ # -*- mode: python; coding: utf-8 -*- -# Copyright (C) 2018-2022 Laboratoire de Recherche et Développement de l'Epita -# (LRDE). +# Copyright (C) 2018-2022 Laboratoire de Recherche et Développement de +# l'Epita (LRDE). # # This file is part of Spot, a model checking library. # @@ -22,6 +22,8 @@ # are usable with methods from the spot package. import spot +from unittest import TestCase +tc = TestCase() a1 = spot.automaton(''' HOA: v1 name: "aut" States: 4 Start: 0 AP: 0 @@ -179,7 +181,7 @@ def generic_emptiness2_rec(aut): # Find some Fin set, we necessarily have one, otherwise the SCC # would have been found to be either rejecting or accepting. fo = acc.fin_one() - assert fo >= 0, acc + tc.assertTrue(fo >= 0, acc) for part in si.split_on_sets(scc, [fo]): if not generic_emptiness2(part): return False @@ -309,10 +311,10 @@ def run_bench(automata): + str(res3b)[0] + str(res3c)[0] + str(res3d)[0] + str(res4)[0] + str(res5)[0]) print(res) - assert res in ('TTTTTTTT', 'FFFFFFFF') + tc.assertIn(res, ('TTTTTTTT', 'FFFFFFFF')) if res == 'FFFFFFFF': run3 = spot.generic_accepting_run(aut) - assert run3.replay(spot.get_cout()) is True + tc.assertTrue(run3.replay(spot.get_cout())) run_bench([a1, a2, a3, a4, a5, a6, a7, a8, a9, a10, a11, a360, act]) diff --git a/tests/python/implies.py b/tests/python/implies.py index 2e4e64ddd..24d74b720 100755 --- a/tests/python/implies.py +++ b/tests/python/implies.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Copyright (C) 2012 Laboratoire de Recherche et Développement +# Copyright (C) 2012, 2022 Laboratoire de Recherche et Développement # de l'EPITA. # # This file is part of Spot, a model checking library. @@ -19,6 +19,8 @@ import sys from buddy import * +from unittest import TestCase +tc = TestCase() bdd_init(10000, 10000) bdd_setvarnum(5) @@ -33,26 +35,26 @@ e = V[1] & V[2] & -V[3] & V[4] f = V[0] & -V[3] & V[4] g = -V[0] | V[1] -assert(bdd_implies(b, a)) -assert(not bdd_implies(a, b)) -assert(not bdd_implies(c, a)) -assert(bdd_implies(a, d)) -assert(bdd_implies(b, d)) -assert(bdd_implies(c, d)) -assert(bdd_implies(d, d)) -assert(not bdd_implies(e, d)) -assert(not bdd_implies(d, e)) -assert(not bdd_implies(f, e)) -assert(not bdd_implies(e, f)) -assert(bdd_implies(bddfalse, f)) -assert(not bdd_implies(bddtrue, f)) -assert(bdd_implies(f, bddtrue)) -assert(not bdd_implies(f, bddfalse)) -assert(bdd_implies(a, g)) +tc.assertTrue(bdd_implies(b, a)) +tc.assertFalse(bdd_implies(a, b)) +tc.assertFalse(bdd_implies(c, a)) +tc.assertTrue(bdd_implies(a, d)) +tc.assertTrue(bdd_implies(b, d)) +tc.assertTrue(bdd_implies(c, d)) +tc.assertTrue(bdd_implies(d, d)) +tc.assertFalse(bdd_implies(e, d)) +tc.assertFalse(bdd_implies(d, e)) +tc.assertFalse(bdd_implies(f, e)) +tc.assertFalse(bdd_implies(e, f)) +tc.assertTrue(bdd_implies(bddfalse, f)) +tc.assertFalse(bdd_implies(bddtrue, f)) +tc.assertTrue(bdd_implies(f, bddtrue)) +tc.assertFalse(bdd_implies(f, bddfalse)) +tc.assertTrue(bdd_implies(a, g)) a = (-V[2] & (-V[1] | V[0])) | (-V[0] & V[1] & V[2]) b = V[1] | -V[2] -assert(bdd_implies(a, b)) +tc.assertTrue(bdd_implies(a, b)) # Cleanup all BDD variables before calling bdd_done(), otherwise # bdd_delref will be called after bdd_done() and this is unsafe in diff --git a/tests/python/intrun.py b/tests/python/intrun.py index c86c6d643..e3b708a95 100644 --- a/tests/python/intrun.py +++ b/tests/python/intrun.py @@ -1,6 +1,6 @@ # -*- mode: python; coding: utf-8 -*- -# Copyright (C) 2020 Laboratoire de Recherche et Développement de l'Epita -# (LRDE). +# Copyright (C) 2020, 2022 Laboratoire de Recherche et Développement +# de l'Epita (LRDE). # # This file is part of Spot, a model checking library. # @@ -18,6 +18,8 @@ # along with this program. If not, see . import spot +from unittest import TestCase +tc = TestCase() # This issue was reported by Florian Renkin. The reduce() call used in # intersecting_run() was bogus, and could incorrectly reduce a word @@ -34,5 +36,5 @@ trans-labels explicit-labels trans-acc complete properties: deterministic State: 3 [t] 1 {1 2} State: 4 [!0&1] 4 {2} [!0&!1] 3 {2} [0] 2 {0 2} --END--""") r = b.intersecting_run(spot.complement(a)); c = spot.twa_word(r).as_automaton() -assert c.intersects(b) -assert not c.intersects(a) +tc.assertTrue(c.intersects(b)) +tc.assertFalse(c.intersects(a)) diff --git a/tests/python/kripke.py b/tests/python/kripke.py index f3ce218b2..fa92b3fa9 100644 --- a/tests/python/kripke.py +++ b/tests/python/kripke.py @@ -1,6 +1,6 @@ # -*- mode: python; coding: utf-8 -*- -# Copyright (C) 2019 Laboratoire de Recherche et Développement de l'Epita -# (LRDE). +# Copyright (C) 2019, 2022 Laboratoire de Recherche et Développement +# de l'Epita (LRDE). # # This file is part of Spot, a model checking library. # @@ -19,6 +19,9 @@ import spot import buddy +from unittest import TestCase +tc = TestCase() + bdict = spot.make_bdd_dict() k = spot.make_kripke_graph(bdict) p1 = buddy.bdd_ithvar(k.register_ap("p1")) @@ -51,25 +54,25 @@ State: [0&1] 1 "0" State: [!0&!1] 2 "2" 2 1 --END--""" -assert hoa == k.to_str('HOA') -assert k.num_states() == 3 -assert k.num_edges() == 5 +tc.assertEqual(hoa, k.to_str('HOA')) +tc.assertEqual(k.num_states(), 3) +tc.assertEqual(k.num_edges(), 5) res = [] for e in k.out(s1): res.append((e.src, e.dst)) -assert res == [(1, 0), (1, 2)] +tc.assertEqual(res, [(1, 0), (1, 2)]) res = [] for e in k.edges(): res.append((e.src, e.dst)) -assert res == [(1, 0), (0, 0), (1, 2), (2, 2), (2, 0)] +tc.assertEqual(res, [(1, 0), (0, 0), (1, 2), (2, 2), (2, 0)]) res = [] for s in k.states(): res.append(s.cond()) -assert res == [cond1, cond2, cond3] +tc.assertEqual(res, [cond1, cond2, cond3]) -assert k.states()[0].cond() == cond1 -assert k.states()[1].cond() == cond2 -assert k.states()[2].cond() == cond3 +tc.assertEqual(k.states()[0].cond(), cond1) +tc.assertEqual(k.states()[1].cond(), cond2) +tc.assertEqual(k.states()[2].cond(), cond3) diff --git a/tests/python/langmap.py b/tests/python/langmap.py index 6fd860986..723a5c0d5 100644 --- a/tests/python/langmap.py +++ b/tests/python/langmap.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2016, 2017, 2020 Laboratoire de Recherche et Développement -# de l'Epita (LRDE) +# Copyright (C) 2016, 2017, 2020, 2022 Laboratoire de Recherche et +# Développement de l'Epita (LRDE) # # This file is part of Spot, a model checking library. # @@ -19,6 +19,8 @@ import spot import sys +from unittest import TestCase +tc = TestCase() def hstates(txt): @@ -31,13 +33,10 @@ def hstates(txt): def test(f, opt, expected): aut = spot.translate(f, *opt, 'deterministic') v = spot.language_map(aut) - assert len(v) == aut.num_states() + tc.assertEqual(len(v), aut.num_states()) spot.highlight_languages(aut) l = hstates(aut.to_str('hoa', '1.1')) - if l != expected: - print('for {}\nexpected: {}\n but got: {}'.format(f, expected, l), - file=sys.stderr) - exit(1) + tc.assertEqual(l, expected) test('GF(a) & GFb & c', ['Buchi', 'SBAcc'], '1 0 2 0 3 0') @@ -50,6 +49,6 @@ test('Xa', ['Buchi', 'SBAcc'], '') try: test('FGa', ['Buchi'], '') except RuntimeError as e: - assert 'language_map only works with deterministic automata'in str(e) + tc.assertIn('language_map only works with deterministic automata', str(e)) else: exit(1) diff --git a/tests/python/ltl2tgba.py b/tests/python/ltl2tgba.py index 25fff4566..913c557be 100755 --- a/tests/python/ltl2tgba.py +++ b/tests/python/ltl2tgba.py @@ -1,6 +1,6 @@ # -*- mode: python; coding: utf-8 -*- -# Copyright (C) 2009, 2010, 2012, 2014-2016, 2021 Laboratoire de Recherche -# et Développement de l'Epita (LRDE). +# Copyright (C) 2009, 2010, 2012, 2014-2016, 2021-2022 Laboratoire de +# Recherche et Développement de l'Epita (LRDE). # Copyright (C) 2003, 2004 Laboratoire d'Informatique de Paris 6 (LIP6), # département Systèmes Répartis Coopératifs (SRC), Université Pierre # et Marie Curie. @@ -98,7 +98,7 @@ if f: elif taa_opt: a = concrete = spot.ltl_to_taa(f, dict) else: - assert "unspecified translator" + raise RuntimeError("unspecified translator") if wdba: a = spot.ensure_digraph(a) @@ -117,7 +117,7 @@ if f: elif output == 6: spot.print_lbtt(cout, a) else: - assert "unknown output option" + raise RuntimeError("unknown output option") if degeneralize_opt: del degeneralized @@ -137,4 +137,6 @@ del dict # not necessary in other implementations. from platform import python_implementation if python_implementation() == 'CPython': - assert spot.fnode_instances_check() + from unittest import TestCase + tc = TestCase() + tc.assertTrue(spot.fnode_instances_check()) diff --git a/tests/python/ltlf.py b/tests/python/ltlf.py index 5676a2a1b..b13432d3e 100644 --- a/tests/python/ltlf.py +++ b/tests/python/ltlf.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Copyright (C) 2016 Laboratoire de Recherche et Développement de +# Copyright (C) 2016, 2022 Laboratoire de Recherche et Développement de # l'Epita # # This file is part of Spot, a model checking library. @@ -18,6 +18,8 @@ # along with this program. If not, see . import spot +from unittest import TestCase +tc = TestCase() lcc = spot.language_containment_checker() @@ -43,5 +45,5 @@ for f in formulas: f4 = spot.formula_And([spot.from_ltlf(f2), cst]) print("{}\t=>\t{}".format(f1, f3)) print("{}\t=>\t{}".format(f2, f4)) - assert lcc.equal(f3, f4) + tc.assertTrue(lcc.equal(f3, f4)) print() diff --git a/tests/python/ltlparse.py b/tests/python/ltlparse.py index 98562743c..208e0c321 100755 --- a/tests/python/ltlparse.py +++ b/tests/python/ltlparse.py @@ -1,6 +1,6 @@ # -*- mode: python; coding: utf-8 -*- -# Copyright (C) 2009-2012, 2014-2017, 2019, 2021 Laboratoire de Recherche et -# Développement de l'Epita (LRDE). +# Copyright (C) 2009-2012, 2014-2017, 2019, 2021-2022 Laboratoire de +# Recherche et Développement de l'Epita (LRDE). # Copyright (C) 2003, 2004 Laboratoire d'Informatique de Paris 6 (LIP6), # département Systèmes Répartis Coopératifs (SRC), Université Pierre # et Marie Curie. @@ -22,6 +22,8 @@ import sys import spot +from unittest import TestCase +tc = TestCase() e = spot.default_environment.instance() @@ -41,11 +43,11 @@ for str1, isl in l: pf = spot.parse_infix_psl(str2, e) if pf.format_errors(spot.get_cout()): sys.exit(1) - assert isl == pf.f.is_leaf() + tc.assertEqual(isl, pf.f.is_leaf()) del pf -assert spot.formula('a').is_leaf() -assert spot.formula('0').is_leaf() +tc.assertTrue(spot.formula('a').is_leaf()) +tc.assertTrue(spot.formula('0').is_leaf()) for str1 in ['a * b', 'a xor b', 'a <-> b']: pf = spot.parse_infix_boolean(str1, e, False) @@ -66,21 +68,21 @@ for (x, op) in [('a* <-> b*', "`<->'"), ('a*[=2]', "[=...]"), ('a*[->2]', "[->...]")]: f5 = spot.parse_infix_sere(x) - assert f5.errors + tc.assertTrue(f5.errors) ostr = spot.ostringstream() f5.format_errors(ostr) err = ostr.str() - assert "not a Boolean expression" in err - assert op in err - assert "SERE" in err + tc.assertIn("not a Boolean expression", err) + tc.assertIn(op, err) + tc.assertIn("SERE", err) del f5 f6 = spot.parse_infix_sere('(a <-> b -> c ^ "b\n\n\rc")[=2] & c[->2]') -assert not f6.errors +tc.assertFalse(f6.errors) del f6 f6 = spot.parse_infix_sere('-') -assert f6.errors +tc.assertTrue(f6.errors) del f6 for (x, msg) in [('{foo[->bug]}', "treating this goto block as [->]"), @@ -150,12 +152,12 @@ for (x, msg) in [('{foo[->bug]}', "treating this goto block as [->]"), ('{"X}', "missing closing brace"), ]: f7 = spot.parse_infix_psl(x) - assert f7.errors + tc.assertTrue(f7.errors) ostr = spot.ostringstream() f7.format_errors(ostr) err = ostr.str() print(err) - assert msg in err + tc.assertIn(msg, err) del f7 for (x, msg) in [('a&', "missing right operand for \"and operator\""), @@ -174,12 +176,12 @@ for (x, msg) in [('a&', "missing right operand for \"and operator\""), ('!', "missing right operand for \"not operator\""), ]: f8 = spot.parse_infix_boolean(x) - assert f8.errors + tc.assertTrue(f8.errors) ostr = spot.ostringstream() f8.format_errors(ostr) err = ostr.str() print(err) - assert msg in err + tc.assertIn(msg, err) del f8 for (x, msg) in [('a->', "missing right operand for \"implication operator\""), @@ -191,12 +193,12 @@ for (x, msg) in [('a->', "missing right operand for \"implication operator\""), ]: f9 = spot.parse_infix_psl(x, spot.default_environment.instance(), False, True) - assert f9.errors + tc.assertTrue(f9.errors) ostr = spot.ostringstream() f9.format_errors(ostr) err = ostr.str() print(err) - assert msg in err + tc.assertIn(msg, err) del f9 # force GC before fnode_instances_check(), unless it's CPython @@ -205,15 +207,15 @@ if python_implementation() != 'CPython': import gc gc.collect() -assert spot.fnode_instances_check() +tc.assertTrue(spot.fnode_instances_check()) f = spot.formula_F(2, 4, spot.formula_ap("a")) -assert f == spot.formula("XX(a | X(a | X(a)))") +tc.assertEqual(f, spot.formula("XX(a | X(a | X(a)))")) f = spot.formula_G(2, 4, spot.formula_ap("a")) -assert f == spot.formula("XX(a & X(a & X(a)))") +tc.assertEqual(f, spot.formula("XX(a & X(a & X(a)))")) f = spot.formula_X(2, spot.formula_ap("a")) -assert f == spot.formula("XX(a)") +tc.assertEqual(f, spot.formula("XX(a)")) f = spot.formula_G(2, spot.formula_unbounded(), spot.formula_ap("a")) -assert f == spot.formula("XXG(a)") +tc.assertEqual(f, spot.formula("XXG(a)")) f = spot.formula_F(2, spot.formula_unbounded(), spot.formula_ap("a")) -assert f == spot.formula("XXF(a)") +tc.assertEqual(f, spot.formula("XXF(a)")) diff --git a/tests/python/ltlsimple.py b/tests/python/ltlsimple.py index 7b88f07dc..c21c3b7f1 100755 --- a/tests/python/ltlsimple.py +++ b/tests/python/ltlsimple.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Copyright (C) 2009, 2010, 2012, 2015, 2018, 2021 Laboratoire de +# Copyright (C) 2009, 2010, 2012, 2015, 2018, 2021-2022 Laboratoire de # Recherche et Développement de l'Epita (LRDE). # Copyright (C) 2003, 2004 Laboratoire d'Informatique de Paris 6 (LIP6), # département Systemes Répartis Coopératifs (SRC), Université Pierre @@ -22,6 +22,8 @@ import spot import sys +from unittest import TestCase +tc = TestCase() # Some of the tests here assume timely destructor calls, as they occur # in the the reference-counted CPython implementation. Other @@ -35,13 +37,13 @@ b = spot.formula.ap('b') c = spot.formula.ap('c') c2 = spot.formula.ap('c') -assert c == c2 +tc.assertEqual(c, c2) op = spot.formula.And([a, b]) op2 = spot.formula.And([op, c]) op3 = spot.formula.And([a, c, b]) -assert op2 == op3 +tc.assertEqual(op2, op3) # The symbol for a subformula which hasn't been cloned is better # suppressed, so we don't attempt to reuse it elsewhere. @@ -52,12 +54,12 @@ sys.stdout.write('op2 = %s\n' % str(op2)) del a, b, c2 sys.stdout.write('op3 = %s\n' % str(op3)) -assert op2 == op3 +tc.assertEqual(op2, op3) op4 = spot.formula.Or([op2, op3]) sys.stdout.write('op4 = %s\n' % str(op4)) -assert op4 == op2 +tc.assertEqual(op4, op2) del op2, op3, op4 @@ -78,10 +80,11 @@ f5 = spot.formula.Xor(F, c) del a, b, c, T, F, f1, f2, f4, f5 if is_cpython: - assert spot.fnode_instances_check() + tc.assertTrue(spot.fnode_instances_check()) # ---------------------------------------------------------------------- -assert str([str(x) for x in spot.formula('a &b & c')]) == "['a', 'b', 'c']" +tc.assertEqual(str([str(x) for x in spot.formula('a &b & c')]), + "['a', 'b', 'c']") def switch_g_f(x): @@ -93,7 +96,7 @@ def switch_g_f(x): f = spot.formula('GFa & XFGb & Fc & G(a | b | Fd)') -assert str(switch_g_f(f)) == 'FGa & XGFb & Gc & F(a | b | Gd)' +tc.assertEqual(str(switch_g_f(f)), 'FGa & XGFb & Gc & F(a | b | Gd)') x = 0 @@ -105,7 +108,7 @@ def count_g(f): f.traverse(count_g) -assert x == 3 +tc.assertEqual(x, 3) # ---------------------------------------------------------------------- @@ -121,14 +124,14 @@ LBT for shell: echo {f:lq} | ... Default for CSV: ...,{f:c},... Wring, centered: {f:w:~^50}""".format(f=formula) -assert res == """\ +tc.assertEqual(res, """\ Default output: a U (b U "$strange[0]=name") Spin syntax: a U (b U ($strange[0]=name)) (Spin syntax): (a) U ((b) U ($strange[0]=name)) Default for shell: echo 'a U (b U "$strange[0]=name")' | ... LBT for shell: echo 'U "a" U "b" "$strange[0]=name"' | ... Default for CSV: ...,"a U (b U ""$strange[0]=name"")",... -Wring, centered: ~~~~~(a=1) U ((b=1) U ("$strange[0]=name"=1))~~~~~""" +Wring, centered: ~~~~~(a=1) U ((b=1) U ("$strange[0]=name"=1))~~~~~""") opt = spot.tl_simplifier_options(False, True, True, @@ -144,9 +147,8 @@ for (input, output) in [('(a&b)<->b', 'b->(a&b)'), ('b xor (!(a&b))', 'b->(a&b)'), ('!b xor (a&b)', 'b->(a&b)')]: f = spot.tl_simplifier(opt).simplify(input) - print(input, f, output) - assert(f == output) - assert(spot.are_equivalent(input, output)) + tc.assertEqual(f, output) + tc.assertTrue(spot.are_equivalent(input, output)) def myparse(input): @@ -157,7 +159,7 @@ def myparse(input): # This used to fail, because myparse would return a pointer # to pf.f inside the destroyed pf. -assert myparse('a U b') == spot.formula('a U b') +tc.assertEqual(myparse('a U b'), spot.formula('a U b')) -assert spot.is_liveness('a <-> GFb') -assert not spot.is_liveness('a & GFb') +tc.assertTrue(spot.is_liveness('a <-> GFb')) +tc.assertFalse(spot.is_liveness('a & GFb')) diff --git a/tests/python/mealy.py b/tests/python/mealy.py index da71d1bfb..71c7739f9 100644 --- a/tests/python/mealy.py +++ b/tests/python/mealy.py @@ -19,6 +19,8 @@ # along with this program. If not, see . import spot, buddy +from unittest import TestCase +tc = TestCase() # Testing Sat-based approach @@ -42,8 +44,8 @@ spot.set_state_players(a, [False,True,False,True,False,True]) spot.set_synthesis_outputs(a, o1&o2) b = spot.minimize_mealy(a) -assert(list(spot.get_state_players(b)).count(False) == 2) -assert(spot.is_split_mealy_specialization(a, b)) +tc.assertEqual(list(spot.get_state_players(b)).count(False), 2) +tc.assertTrue(spot.is_split_mealy_specialization(a, b)) test_auts = [ ("""HOA: v1 @@ -371,21 +373,21 @@ for (mealy_str, nenv_min) in test_auts: elif aap.ap_name().startswith("i"): ins = ins & buddy.bdd_ithvar(mealy.register_ap(aap.ap_name())) else: - assert("""Aps must start with either "i" or "o".""") + raise AssertionError("""Aps must start with either "i" or "o".""") spot.set_synthesis_outputs(mealy, outs) mealy_min_ks = spot.minimize_mealy(mealy, -1) n_e = sum([s == 0 for s in spot.get_state_players(mealy_min_ks)]) - assert(n_e == nenv_min) - assert(spot.is_split_mealy_specialization(mealy, mealy_min_ks)) + tc.assertEqual(n_e, nenv_min) + tc.assertTrue(spot.is_split_mealy_specialization(mealy, mealy_min_ks)) # Test un- and resplit tmp = spot.unsplit_2step(mealy_min_ks) mealy_min_rs = spot.split_2step(tmp, spot.get_synthesis_outputs(tmp), False) - assert(spot.is_split_mealy_specialization(mealy, mealy_min_rs, True)) - assert(spot.are_equivalent(mealy_min_ks, mealy_min_rs)) + tc.assertTrue(spot.is_split_mealy_specialization(mealy, mealy_min_rs, True)) + tc.assertTrue(spot.are_equivalent(mealy_min_ks, mealy_min_rs)) # Testing bisimulation (with output assignment) @@ -515,15 +517,15 @@ spot.set_synthesis_outputs(aut, & buddy.bdd_ithvar( aut.register_ap("u02alarm29control0f1d2alarm29turn2off1b"))) min_equiv = spot.reduce_mealy(aut, False) -assert min_equiv.num_states() == 6 -assert spot.are_equivalent(min_equiv, aut) +tc.assertEqual(min_equiv.num_states(), 6) +tc.assertTrue(spot.are_equivalent(min_equiv, aut)) # Build an automaton that recognizes a subset of the language of the original # automaton min_sub = spot.reduce_mealy(aut, True) -assert min_sub.num_states() == 5 +tc.assertEqual(min_sub.num_states(), 5) prod = spot.product(spot.complement(aut), min_sub) -assert spot.generic_emptiness_check(prod) +tc.assertTrue(spot.generic_emptiness_check(prod)) aut = spot.automaton(""" HOA: v1 @@ -564,7 +566,7 @@ State: 0 # An example that shows that we should not build a tree when we use inclusion. res = spot.reduce_mealy(aut, True) -assert res.to_str() == exp +tc.assertEqual(res.to_str(), exp) aut = spot.automaton(""" HOA: v1 @@ -608,4 +610,4 @@ State: 1 --END--""" res = spot.reduce_mealy(aut, True) -assert res.to_str() == exp +tc.assertEqual(res.to_str(), exp) diff --git a/tests/python/merge.py b/tests/python/merge.py index c56d8f309..893916953 100644 --- a/tests/python/merge.py +++ b/tests/python/merge.py @@ -1,7 +1,7 @@ #!/usr/bin/python3 # -*- mode: python; coding: utf-8 -*- -# Copyright (C) 2017, 2020 Laboratoire de Recherche et Développement de -# l'EPITA. +# Copyright (C) 2017, 2020, 2022 Laboratoire de Recherche et +# Développement de l'EPITA. # # This file is part of Spot, a model checking library. # @@ -19,6 +19,8 @@ # along with this program. If not, see . import spot +from unittest import TestCase +tc = TestCase() aut = spot.automaton(""" HOA: v1 @@ -39,7 +41,7 @@ State: 2 out = spot.simplify_acceptance(aut) hoa = out.to_str('hoa') -assert hoa == """HOA: v1 +tc.assertEqual(hoa, """HOA: v1 States: 3 Start: 0 AP: 2 "a" "b" @@ -54,8 +56,8 @@ State: 1 [1] 2 {0} State: 2 [1] 0 ---END--""" -assert spot.are_equivalent(out, aut) +--END--""") +tc.assertTrue(spot.are_equivalent(out, aut)) aut = spot.automaton("""HOA: v1 States: 3 @@ -75,7 +77,7 @@ State: 2 spot.simplify_acceptance_here(aut) hoa = aut.to_str('hoa') -assert hoa == """HOA: v1 +tc.assertEqual(hoa, """HOA: v1 States: 3 Start: 0 AP: 2 "a" "b" @@ -90,7 +92,7 @@ State: 1 [1] 2 {0} State: 2 [1] 0 ---END--""" +--END--""") aut = spot.automaton(""" HOA: v1 @@ -111,7 +113,7 @@ State: 2 spot.simplify_acceptance_here(aut) hoa = aut.to_str('hoa') -assert hoa == """HOA: v1 +tc.assertEqual(hoa, """HOA: v1 States: 3 Start: 0 AP: 2 "a" "b" @@ -126,7 +128,7 @@ State: 1 [1] 2 State: 2 [1] 0 ---END--""" +--END--""") aut = spot.automaton("""HOA: v1 States: 3 @@ -146,7 +148,7 @@ State: 2 spot.simplify_acceptance_here(aut) hoa = aut.to_str('hoa') -assert hoa == """HOA: v1 +tc.assertEqual(hoa, """HOA: v1 States: 3 Start: 0 AP: 2 "a" "b" @@ -161,7 +163,7 @@ State: 1 [1] 2 {1} State: 2 [1] 0 {0} ---END--""" +--END--""") aut = spot.automaton(""" HOA: v1 @@ -182,7 +184,7 @@ State: 2 spot.simplify_acceptance_here(aut) hoa = aut.to_str('hoa') -assert hoa == """HOA: v1 +tc.assertEqual(hoa, """HOA: v1 States: 3 Start: 0 AP: 2 "a" "b" @@ -197,7 +199,7 @@ State: 1 [1] 2 State: 2 [1] 0 ---END--""" +--END--""") aut = spot.automaton("""HOA: v1 States: 3 @@ -217,7 +219,7 @@ State: 2 spot.simplify_acceptance_here(aut) hoa = aut.to_str('hoa') -assert hoa == """HOA: v1 +tc.assertEqual(hoa, """HOA: v1 States: 3 Start: 0 AP: 2 "a" "b" @@ -232,7 +234,7 @@ State: 1 {0} [1] 2 State: 2 {0} [1] 0 ---END--""" +--END--""") aut = spot.automaton("""HOA: v1 States: 3 @@ -252,7 +254,7 @@ State: 2 spot.simplify_acceptance_here(aut) hoa = aut.to_str('hoa') -assert hoa == """HOA: v1 +tc.assertEqual(hoa, """HOA: v1 States: 3 Start: 0 AP: 2 "a" "b" @@ -267,7 +269,7 @@ State: 1 {0} [1] 2 State: 2 {0} [1] 0 ---END--""" +--END--""") aut = spot.automaton("""HOA: v1 States: 3 @@ -287,7 +289,7 @@ State: 2 spot.simplify_acceptance_here(aut) hoa = aut.to_str('hoa') -assert hoa == """HOA: v1 +tc.assertEqual(hoa, """HOA: v1 States: 3 Start: 0 AP: 2 "a" "b" @@ -301,7 +303,7 @@ State: 1 {1} [1] 2 State: 2 [1] 0 ---END--""" +--END--""") aut = spot.automaton("""HOA: v1 States: 4 @@ -335,7 +337,7 @@ State: 3 {1 3} spot.simplify_acceptance_here(aut) hoa = aut.to_str('hoa') -assert hoa == """HOA: v1 +tc.assertEqual(hoa, """HOA: v1 States: 4 Start: 0 AP: 2 "a" "b" @@ -364,7 +366,7 @@ State: 3 {1} [0&!1] 0 [!0&1] 3 [0&1] 2 ---END--""" +--END--""") aut = spot.automaton("""HOA: v1 States: 3 @@ -388,7 +390,7 @@ State: 2 out = spot.simplify_acceptance(aut) hoa = out.to_str('hoa') -assert hoa == """HOA: v1 +tc.assertEqual(hoa, """HOA: v1 States: 3 Start: 0 AP: 2 "p0" "p1" @@ -406,8 +408,8 @@ State: 1 State: 2 [0] 2 {0} [!0] 1 {0} ---END--""" -assert spot.are_equivalent(out, aut) +--END--""") +tc.assertTrue(spot.are_equivalent(out, aut)) aut = spot.automaton("""HOA: v1 States: 4 @@ -435,7 +437,7 @@ State: 3 spot.simplify_acceptance_here(aut) hoa = aut.to_str('hoa') -assert hoa == """HOA: v1 +tc.assertEqual(hoa, """HOA: v1 States: 4 Start: 0 AP: 2 "p0" "p1" @@ -457,7 +459,7 @@ State: 3 [0&1] 0 {1} [0&!1] 3 {1 2} [!0] 1 {3} ---END--""" +--END--""") aut = spot.automaton("""HOA: v1 States: 1 @@ -475,7 +477,7 @@ State: 0 {1 2} spot.simplify_acceptance_here(aut) hoa = aut.to_str('hoa') -assert hoa == """HOA: v1 +tc.assertEqual(hoa, """HOA: v1 States: 1 Start: 0 AP: 2 "p0" "p1" @@ -486,7 +488,7 @@ properties: deterministic --BODY-- State: 0 [t] 0 ---END--""" +--END--""") aut = spot.automaton("""HOA: v1 States: 2 @@ -506,7 +508,7 @@ State: 1 spot.simplify_acceptance_here(aut) hoa = aut.to_str('hoa') -assert hoa == """HOA: v1 +tc.assertEqual(hoa, """HOA: v1 States: 2 Start: 0 AP: 2 "p0" "p1" @@ -519,7 +521,7 @@ State: 0 [!0] 1 {2} State: 1 [t] 1 {1 2} ---END--""" +--END--""") aut = spot.automaton("""HOA: v1 States: 1 @@ -536,7 +538,7 @@ State: 0 {0 1 3} spot.simplify_acceptance_here(aut) hoa = aut.to_str('hoa') -assert hoa == """HOA: v1 +tc.assertEqual(hoa, """HOA: v1 States: 1 Start: 0 AP: 2 "p0" "p1" @@ -547,7 +549,7 @@ properties: deterministic --BODY-- State: 0 [t] 0 ---END--""" +--END--""") aut = spot.automaton("""HOA: v1 States: 2 @@ -568,7 +570,7 @@ State: 1 spot.simplify_acceptance_here(aut) hoa = aut.to_str('hoa') -assert hoa == """HOA: v1 +tc.assertEqual(hoa, """HOA: v1 States: 2 Start: 0 AP: 2 "p0" "p1" @@ -583,7 +585,7 @@ State: 0 State: 1 [0] 1 [!0] 0 {1} ---END--""" +--END--""") aut = spot.automaton("""HOA: v1 States: 2 @@ -602,7 +604,7 @@ State: 1 {1} spot.simplify_acceptance_here(aut) hoa = aut.to_str('hoa') -assert hoa == """HOA: v1 +tc.assertEqual(hoa, """HOA: v1 States: 2 Start: 0 AP: 2 "p0" "p1" @@ -615,7 +617,7 @@ State: 0 [t] 1 State: 1 [t] 0 ---END--""" +--END--""") aut = spot.automaton("""HOA: v1 States: 3 @@ -636,7 +638,7 @@ State: 2 {2} spot.simplify_acceptance_here(aut) hoa = aut.to_str('hoa') -assert hoa == """HOA: v1 +tc.assertEqual(hoa, """HOA: v1 States: 3 Start: 0 AP: 2 "p0" "p1" @@ -650,7 +652,7 @@ State: 1 {0} [t] 1 State: 2 {2} [t] 1 ---END--""" +--END--""") aut = spot.automaton("""HOA: v1 States: 3 @@ -672,7 +674,7 @@ State: 2 {1 2 3} out = spot.simplify_acceptance(aut) hoa = out.to_str('hoa') -assert hoa == """HOA: v1 +tc.assertEqual(hoa, """HOA: v1 States: 3 Start: 0 AP: 2 "p0" "p1" @@ -687,8 +689,8 @@ State: 1 {1} [t] 2 State: 2 {0 1} [t] 1 ---END--""" -assert spot.are_equivalent(out, aut) +--END--""") +tc.assertTrue(spot.are_equivalent(out, aut)) aut = spot.automaton("""HOA: v1 States: 2 @@ -708,7 +710,7 @@ State: 1 spot.simplify_acceptance_here(aut) hoa = aut.to_str('hoa') -assert hoa == """HOA: v1 +tc.assertEqual(hoa, """HOA: v1 States: 2 Start: 0 AP: 2 "p0" "p1" @@ -722,7 +724,7 @@ State: 0 State: 1 [0] 1 [!0] 0 ---END--""" +--END--""") aut = spot.automaton("""HOA: v1 States: 3 @@ -740,7 +742,7 @@ State: 2 --END--""") spot.simplify_acceptance_here(aut) hoa = aut.to_str('hoa') -assert hoa == """HOA: v1 +tc.assertEqual(hoa, """HOA: v1 States: 3 Start: 0 AP: 2 "a" "b" @@ -755,7 +757,7 @@ State: 1 [1] 2 State: 2 [1] 0 ---END--""" +--END--""") aut = spot.automaton("""HOA: v1 States: 3 @@ -773,7 +775,7 @@ State: 2 --END--""") spot.simplify_acceptance_here(aut) hoa = aut.to_str('hoa') -assert hoa == """HOA: v1 +tc.assertEqual(hoa, """HOA: v1 States: 3 Start: 0 AP: 2 "a" "b" @@ -788,4 +790,4 @@ State: 1 [1] 2 State: 2 [1] 0 ---END--""" +--END--""") diff --git a/tests/python/mergedge.py b/tests/python/mergedge.py index e55bdabf2..4e97abe23 100644 --- a/tests/python/mergedge.py +++ b/tests/python/mergedge.py @@ -1,6 +1,6 @@ #!/usr/bin/python3 # -*- mode: python; coding: utf-8 -*- -# Copyright (C) 2020, 2021 Laboratoire de Recherche et Développement de +# Copyright (C) 2020-2022 Laboratoire de Recherche et Développement de # l'EPITA. # # This file is part of Spot, a model checking library. @@ -20,12 +20,14 @@ import spot +from unittest import TestCase +tc = TestCase() aut = spot.automaton("""HOA: v1 States: 1 Start: 0 AP: 1 "a" Acceptance: 1 Inf(0) --BODY-- State: 0 [0] 0 [0] 0 {0} --END--""") -assert aut.num_edges() == 2 +tc.assertEqual(aut.num_edges(), 2) aut.merge_edges() -assert aut.num_edges() == 1 +tc.assertEqual(aut.num_edges(), 1) aut = spot.automaton(""" HOA: v1 @@ -44,15 +46,15 @@ State: 1 [0 | 1] 1 [0&!1] 1 {0} --END--""") -assert aut.num_edges() == 5 +tc.assertEqual(aut.num_edges(), 5) aut.merge_edges() -assert aut.num_edges() == 5 -assert not spot.is_deterministic(aut) +tc.assertEqual(aut.num_edges(), 5) +tc.assertFalse(spot.is_deterministic(aut)) aut = spot.split_edges(aut) -assert aut.num_edges() == 9 +tc.assertEqual(aut.num_edges(), 9) aut.merge_edges() -assert aut.num_edges() == 5 -assert spot.is_deterministic(aut) +tc.assertEqual(aut.num_edges(), 5) +tc.assertTrue(spot.is_deterministic(aut)) aut = spot.automaton(""" HOA: v1 @@ -74,15 +76,15 @@ State: 2 [0] 1 --END--""") aut.merge_states() -assert aut.num_edges() == 4 -assert aut.num_states() == 2 -assert spot.is_deterministic(aut) -assert aut.prop_complete() +tc.assertEqual(aut.num_edges(), 4) +tc.assertEqual(aut.num_states(), 2) +tc.assertTrue(spot.is_deterministic(aut)) +tc.assertTrue(aut.prop_complete()) aut.merge_states() -assert aut.num_edges() == 4 -assert aut.num_states() == 2 -assert spot.is_deterministic(aut) -assert aut.prop_complete() +tc.assertEqual(aut.num_edges(), 4) +tc.assertEqual(aut.num_states(), 2) +tc.assertTrue(spot.is_deterministic(aut)) +tc.assertTrue(aut.prop_complete()) aa = spot.automaton(""" diff --git a/tests/python/misc-ec.py b/tests/python/misc-ec.py index d1234bd69..85d4aaa47 100644 --- a/tests/python/misc-ec.py +++ b/tests/python/misc-ec.py @@ -1,6 +1,6 @@ # -*- mode: python; coding: utf-8 -*- -# Copyright (C) 2017, 2020 Laboratoire de Recherche et Développement de l'Epita -# (LRDE). +# Copyright (C) 2017, 2020, 2022 Laboratoire de Recherche et +# Développement de l'Epita (LRDE). # # This file is part of Spot, a model checking library. # @@ -18,6 +18,9 @@ # along with this program. If not, see . import spot +from unittest import TestCase +tc = TestCase() + aut = spot.translate("G(p0 | (p0 R Xp0) | XF(!p0 & p1))", 'Buchi', 'SBAcc') ec = spot.make_emptiness_check_instantiator('SE05')[0].instantiate(aut) n = 0 @@ -27,7 +30,7 @@ while True: break print(res.accepting_run()) n += 1 -assert n == 2 +tc.assertEqual(n, 2) for name in ['SE05', 'CVWY90', 'GV04']: aut = spot.translate("GFa && GFb") @@ -35,13 +38,13 @@ for name in ['SE05', 'CVWY90', 'GV04']: ec = spot.make_emptiness_check_instantiator(name)[0].instantiate(aut) print(ec.check().accepting_run()) except RuntimeError as e: - assert "Büchi or weak" in str(e) + tc.assertIn("Büchi or weak", str(e)) aut = spot.translate("a", 'monitor') try: ec = spot.make_emptiness_check_instantiator('Tau03')[0].instantiate(aut) except RuntimeError as e: - assert "at least one" in str(e) + tc.assertIn("at least one", str(e)) aut = spot.translate("a", 'ba') ec = spot.make_emptiness_check_instantiator('Tau03')[0].instantiate(aut) diff --git a/tests/python/optionmap.py b/tests/python/optionmap.py index 667ef0b19..ad526f510 100755 --- a/tests/python/optionmap.py +++ b/tests/python/optionmap.py @@ -1,6 +1,6 @@ # -*- mode: python; coding: utf-8 -*- -# Copyright (C) 2010, 2012, 2018 Laboratoire de Recherche et Développement -# de l'EPITA. +# Copyright (C) 2010, 2012, 2018, 2022 Laboratoire de Recherche et +# Développement de l'EPITA. # Copyright (C) 2005 Laboratoire d'Informatique de Paris 6 (LIP6), # département Systèmes Répartis Coopératifs (SRC), Université Pierre # et Marie Curie. @@ -21,65 +21,67 @@ # along with this program. If not, see . import spot +from unittest import TestCase +tc = TestCase() o = spot.option_map() res = o.parse_options("optA, opta=2M, optb =4 ; optB = 7\ , optC= 10") -assert not res +tc.assertFalse(res) -assert o.get('optA') == 1 -assert o.get('opta') == 2*1024*1024 -assert o.get('optb') == 4 -assert o.get('optB') == 7 -assert o.get('optC') == 10 -assert o.get('none') == 0 -assert o.get('none', 16) == 16 +tc.assertEqual(o.get('optA'), 1) +tc.assertEqual(o.get('opta'), 2*1024*1024) +tc.assertEqual(o.get('optb'), 4) +tc.assertEqual(o.get('optB'), 7) +tc.assertEqual(o.get('optC'), 10) +tc.assertEqual(o.get('none'), 0) +tc.assertEqual(o.get('none', 16), 16) o.set('optb', 40) -assert o.get('optb') == 40 +tc.assertEqual(o.get('optb'), 40) res = o.parse_options("!optA !optb optC, !optB") -assert not res -assert o.get('optA') == 0 -assert o.get('opta') == 2*1024*1024 -assert o.get('optb') == 0 -assert o.get('optB') == 0 -assert o.get('optC') == 1 +tc.assertFalse(res) +tc.assertEqual(o.get('optA'), 0) +tc.assertEqual(o.get('opta'), 2*1024*1024) +tc.assertEqual(o.get('optb'), 0) +tc.assertEqual(o.get('optB'), 0) +tc.assertEqual(o.get('optC'), 1) res = o.parse_options("!") -assert res == "!" +tc.assertEqual(res, "!") res = o.parse_options("foo, !opt = 1") -assert res == "!opt = 1" +tc.assertEqual(res, "!opt = 1") res = o.parse_options("foo=3, opt == 1") -assert res == "opt == 1" +tc.assertEqual(res, "opt == 1") res = o.parse_options("foo=3opt == 1") -assert res == "3opt == 1" +tc.assertEqual(res, "3opt == 1") aut1 = spot.translate('GF(a <-> XXa)', 'det') -assert aut1.num_states() == 4 +tc.assertEqual(aut1.num_states(), 4) aut2 = spot.translate('GF(a <-> XXa)', 'det', xargs='gf-guarantee=0') -assert aut2.num_states() == 9 +tc.assertEqual(aut2.num_states(), 9) try: spot.translate('GF(a <-> XXa)', 'det', xargs='foobar=1') except RuntimeError as e: - assert "option 'foobar' was not used" in str(e) + tc.assertIn("option 'foobar' was not used", str(e)) else: raise RuntimeError("missing exception") try: spot.translate('GF(a <-> XXa)').postprocess('det', xargs='gf-guarantee=0') except RuntimeError as e: - assert "option 'gf-guarantee' was not used" in str(e) + tc.assertIn("option 'gf-guarantee' was not used", str(e)) else: raise RuntimeError("missing exception") try: spot.translate('GF(a <-> XXa)').postprocess('det', xargs='gf-guarantee=x') except RuntimeError as e: - assert "failed to parse option at: 'gf-guarantee=x'" in str(e) + tc.assertIn("failed to parse option at: 'gf-guarantee=x'", str(e)) else: raise RuntimeError("missing exception") diff --git a/tests/python/origstate.py b/tests/python/origstate.py index 0ca013889..15a7ab0ad 100644 --- a/tests/python/origstate.py +++ b/tests/python/origstate.py @@ -1,6 +1,6 @@ # -*- mode: python; coding: utf-8 -*- -# Copyright (C) 2015, 2017 Laboratoire de Recherche et Développement -# de l'Epita +# Copyright (C) 2015, 2017, 2022 Laboratoire de Recherche et +# Développement de l'Epita # # This file is part of Spot, a model checking library. # @@ -19,6 +19,8 @@ import spot from sys import exit +from unittest import TestCase +tc = TestCase() aut = spot.automaton(""" HOA: v1 @@ -38,7 +40,7 @@ State: 1 """) aut2 = spot.degeneralize(aut) -assert aut2.to_str() == """HOA: v1 +tc.assertEqual(aut2.to_str(), """HOA: v1 States: 3 Start: 0 AP: 2 "a" "b" @@ -56,10 +58,10 @@ State: 1 [1] 2 State: 2 {0} [1] 2 ---END--""" +--END--""") aut2.copy_state_names_from(aut) -assert aut2.to_str() == """HOA: v1 +tc.assertEqual(aut2.to_str(), """HOA: v1 States: 3 Start: 0 AP: 2 "a" "b" @@ -77,7 +79,7 @@ State: 1 "0#0" [1] 2 State: 2 "1#1" {0} [1] 2 ---END--""" +--END--""") aut2.set_init_state(2) aut2.purge_unreachable_states() @@ -93,16 +95,16 @@ properties: deterministic State: 0 "1#1" {0} [1] 0 --END--""" -assert aut2.to_str() == ref +tc.assertEqual(aut2.to_str(), ref) # This makes sure that the original-states vector has also been renamed. aut2.copy_state_names_from(aut) -assert aut2.to_str() == ref +tc.assertEqual(aut2.to_str(), ref) aut2 = spot.degeneralize(aut) aut2.release_named_properties() try: aut2.copy_state_names_from(aut) except RuntimeError as e: - assert "state does not exist in source automaton" in str(e) + tc.assertIn("state does not exist in source automaton", str(e)) else: exit(1) diff --git a/tests/python/otfcrash.py b/tests/python/otfcrash.py index 69acbcb1a..8e30cb501 100644 --- a/tests/python/otfcrash.py +++ b/tests/python/otfcrash.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Copyright (C) 2016, 2018 Laboratoire de Recherche et Développement +# Copyright (C) 2016, 2018, 2022 Laboratoire de Recherche et Développement # de l'Epita # # This file is part of Spot, a model checking library. @@ -23,6 +23,8 @@ import spot.aux import tempfile import shutil import sys +from unittest import TestCase +tc = TestCase() spot.ltsmin.require('divine') @@ -51,4 +53,4 @@ system async; p = spot.otf_product(k, a) return p.is_empty() - assert(modelcheck('X "R.found"', m) == True) + tc.assertTrue(modelcheck('X "R.found"', m)) diff --git a/tests/python/parity.py b/tests/python/parity.py index b0389c40e..6ced51c40 100644 --- a/tests/python/parity.py +++ b/tests/python/parity.py @@ -19,36 +19,38 @@ # along with this program. If not, see . import spot +from unittest import TestCase +tc = TestCase() max_even_5 = spot.acc_code.parity(True, False, 5) -assert max_even_5 == spot.acc_code.parity_max_even(5) -assert max_even_5 == spot.acc_code.parity_max(False, 5) +tc.assertEqual(max_even_5, spot.acc_code.parity_max_even(5)) +tc.assertEqual(max_even_5, spot.acc_code.parity_max(False, 5)) min_even_5 = spot.acc_code.parity(False, False, 5) -assert min_even_5 == spot.acc_code.parity_min_even(5) -assert min_even_5 == spot.acc_code.parity_min(False, 5) +tc.assertEqual(min_even_5, spot.acc_code.parity_min_even(5)) +tc.assertEqual(min_even_5, spot.acc_code.parity_min(False, 5)) max_odd_5 = spot.acc_code.parity(True, True, 5) -assert max_odd_5 == spot.acc_code.parity_max_odd(5) -assert max_odd_5 == spot.acc_code.parity_max(True, 5) +tc.assertEqual(max_odd_5, spot.acc_code.parity_max_odd(5)) +tc.assertEqual(max_odd_5, spot.acc_code.parity_max(True, 5)) min_odd_5 = spot.acc_code.parity(False, True, 5) -assert min_odd_5 == spot.acc_code.parity_min_odd(5) -assert min_odd_5 == spot.acc_code.parity_min(True, 5) +tc.assertEqual(min_odd_5, spot.acc_code.parity_min_odd(5)) +tc.assertEqual(min_odd_5, spot.acc_code.parity_min(True, 5)) for f in ('FGa', 'GFa & GFb & FGc', 'XXX(a U b)'): a1 = spot.translate(f, 'parity') - assert a1.acc().is_parity() + tc.assertTrue(a1.acc().is_parity()) a2 = spot.translate(f).postprocess('parity') - assert a2.acc().is_parity() + tc.assertTrue(a2.acc().is_parity()) a3 = spot.translate(f, 'det').postprocess('parity', 'colored') - assert a3.acc().is_parity() - assert spot.is_colored(a3) + tc.assertTrue(a3.acc().is_parity()) + tc.assertTrue(spot.is_colored(a3)) a = spot.translate('GFa & GFb') try: spot.change_parity_here(a, spot.parity_kind_same, spot.parity_style_even) except RuntimeError as e: - assert 'input should have parity acceptance' in str(e) + tc.assertIn('input should have parity acceptance', str(e)) else: exit(2) @@ -64,7 +66,7 @@ State: 0 --END-- """) spot.cleanup_parity_here(a) -assert a.to_str() == """HOA: v1 +tc.assertEqual(a.to_str(), """HOA: v1 States: 1 Start: 0 AP: 1 "a" @@ -75,7 +77,7 @@ properties: deterministic --BODY-- State: 0 [t] 0 ---END--""" +--END--""") a = spot.automaton(""" HOA: v1 @@ -89,7 +91,7 @@ State: 0 --END-- """) spot.cleanup_parity_here(a) -assert a.to_str() == """HOA: v1 +tc.assertEqual(a.to_str(), """HOA: v1 States: 1 Start: 0 AP: 1 "a" @@ -100,7 +102,7 @@ properties: deterministic --BODY-- State: 0 [t] 0 ---END--""" +--END--""") a = spot.automaton("""HOA: v1 States: 3 @@ -120,39 +122,39 @@ State: 2 try: spot.get_state_players(a) except RuntimeError as e: - assert "not a game" in str(e) + tc.assertIn("not a game", str(e)) else: report_missing_exception() try: spot.set_state_player(a, 1, True) except RuntimeError as e: - assert "Can only" in str(e) + tc.assertIn("Can only", str(e)) else: report_missing__exception() spot.set_state_players(a, (False, True, False)) -assert spot.get_state_player(a, 0) == False -assert spot.get_state_player(a, 1) == True -assert spot.get_state_player(a, 2) == False +tc.assertEqual(spot.get_state_player(a, 0), False) +tc.assertEqual(spot.get_state_player(a, 1), True) +tc.assertEqual(spot.get_state_player(a, 2), False) try: spot.set_state_players(a, [True, False, False, False]) except RuntimeError as e: - assert "many owners as states" in str(e) + tc.assertIn("many owners as states", str(e)) else: report_missing_exception() try: spot.get_state_player(a, 4) except RuntimeError as e: - assert "invalid state number" in str(e) + tc.assertIn("invalid state number", str(e)) else: report_missing_exception() try: spot.set_state_player(a, 4, True) except RuntimeError as e: - assert "invalid state number" in str(e) + tc.assertIn("invalid state number", str(e)) else: report_missing_exception() @@ -168,4 +170,4 @@ oi.erase() # postprocess used to call reduce_parity that did not # work correctly on automata with deleted edges. sm = a.postprocess("gen", "small") -assert sm.num_states() == 3 +tc.assertEqual(sm.num_states(), 3) diff --git a/tests/python/parsetgba.py b/tests/python/parsetgba.py index cbcacb183..038b33a19 100755 --- a/tests/python/parsetgba.py +++ b/tests/python/parsetgba.py @@ -1,6 +1,6 @@ # -*- mode: python; coding: utf-8 -*- -# Copyright (C) 2012, 2014, 2015 Laboratoire de Recherche et Développement -# de l'Epita (LRDE). +# Copyright (C) 2012, 2014, 2015, 2022 Laboratoire de Recherche et +# Développement de l'Epita (LRDE). # # This file is part of Spot, a model checking library. # @@ -19,6 +19,8 @@ import os import spot +from unittest import TestCase +tc = TestCase() contents = ''' HOA: v1 name: "a U b" States: 2 Start: 1 AP: 2 "a" "b" acc-name: Buchi @@ -34,7 +36,7 @@ out.close() a = spot.parse_aut(filename, spot.make_bdd_dict()) -assert not a.errors +tc.assertFalse(a.errors) spot.print_dot(spot.get_cout(), a.aut) diff --git a/tests/python/pdegen.py b/tests/python/pdegen.py index 02150d375..12bc9e39a 100644 --- a/tests/python/pdegen.py +++ b/tests/python/pdegen.py @@ -1,6 +1,6 @@ # -*- mode: python; coding: utf-8 -*- -# Copyright (C) 2019, 2020, 2021 Laboratoire de Recherche et Développement de -# l'Epita (LRDE). +# Copyright (C) 2019, 2020, 2021, 2022 Laboratoire de Recherche et +# Développement de l'Epita (LRDE). # # This file is part of Spot, a model checking library. # @@ -23,6 +23,8 @@ import spot +from unittest import TestCase +tc = TestCase() a, b, d, f = spot.automata(""" HOA: v1 @@ -73,19 +75,19 @@ State: 1 --END-- """) -assert spot.is_partially_degeneralizable(a) == [0, 1] +tc.assertEqual(spot.is_partially_degeneralizable(a), [0, 1]) da = spot.partial_degeneralize(a, [0, 1]) -assert da.equivalent_to(a) -assert da.num_states() == 2 +tc.assertTrue(da.equivalent_to(a)) +tc.assertEqual(da.num_states(), 2) -assert spot.is_partially_degeneralizable(b) == [0, 1] +tc.assertEqual(spot.is_partially_degeneralizable(b), [0, 1]) db = spot.partial_degeneralize(b, [0, 1]) -assert db.equivalent_to(b) -assert db.num_states() == 3 +tc.assertTrue(db.equivalent_to(b)) +tc.assertEqual(db.num_states(), 3) db.copy_state_names_from(b) dbhoa = db.to_str('hoa') -assert dbhoa == """HOA: v1 +tc.assertEqual(dbhoa, """HOA: v1 States: 3 Start: 0 AP: 1 "p0" @@ -99,28 +101,28 @@ State: 1 "0#0" {0 1} [0] 2 State: 2 "1#0" {1} [0] 1 ---END--""" +--END--""") c = spot.automaton("randaut -A'(Fin(0)&Inf(1)&Inf(2))|Fin(2)' 1 |") -assert spot.is_partially_degeneralizable(c) == [1, 2] +tc.assertEqual(spot.is_partially_degeneralizable(c), [1, 2]) dc = spot.partial_degeneralize(c, [1, 2]) -assert dc.equivalent_to(c) -assert str(dc.get_acceptance()) == '(Fin(0) & Inf(2)) | Fin(1)' +tc.assertTrue(dc.equivalent_to(c)) +tc.assertEqual(str(dc.get_acceptance()), '(Fin(0) & Inf(2)) | Fin(1)') -assert spot.is_partially_degeneralizable(d) == [] +tc.assertEqual(spot.is_partially_degeneralizable(d), []) dd = spot.partial_degeneralize(d, []) -assert dd.equivalent_to(d) -assert dd.num_states() == 1 -assert str(dd.get_acceptance()) == 'Inf(1) & Fin(0)' +tc.assertTrue(dd.equivalent_to(d)) +tc.assertEqual(dd.num_states(), 1) +tc.assertEqual(str(dd.get_acceptance()), 'Inf(1) & Fin(0)') e = spot.dualize(b) de = spot.partial_degeneralize(e, [0, 1]) -assert de.equivalent_to(e) -assert de.num_states() == 4 +tc.assertTrue(de.equivalent_to(e)) +tc.assertEqual(de.num_states(), 4) de.copy_state_names_from(e) dehoa = de.to_str('hoa') -assert dehoa == """HOA: v1 +tc.assertEqual(dehoa, """HOA: v1 States: 4 Start: 0 AP: 1 "p0" @@ -140,18 +142,18 @@ State: 2 "3#0" State: 3 "2#0" [0] 1 {0} [!0] 2 ---END--""" +--END--""") -assert spot.is_partially_degeneralizable(de) == [] +tc.assertEqual(spot.is_partially_degeneralizable(de), []) df = spot.partial_degeneralize(f, [0, 1]) df.equivalent_to(f) -assert str(df.acc()) == '(1, Fin(0))' +tc.assertEqual(str(df.acc()), '(1, Fin(0))') try: df = spot.partial_degeneralize(f, [0, 1, 2]) except RuntimeError as e: - assert 'partial_degeneralize(): {0,1,2} does not' in str(e) + tc.assertIn('partial_degeneralize(): {0,1,2} does not', str(e)) else: raise RuntimeError("missing exception") @@ -165,13 +167,13 @@ State: 2 [0&!1&2] 3 {1 4 9} State: 3 [0&!1&2] 4 {0 1 5 9} State: 4 [!0&!1&2] 1 State: 7 [0&!1&!2] 0 {4 7} --END--""") daut5 = spot.degeneralize_tba(aut5) -assert daut5.equivalent_to(aut5) +tc.assertTrue(daut5.equivalent_to(aut5)) sets = list(range(aut5.num_sets())) -assert spot.is_partially_degeneralizable(aut5) == sets +tc.assertEqual(spot.is_partially_degeneralizable(aut5), sets) pdaut5 = spot.partial_degeneralize(aut5, sets) -assert pdaut5.equivalent_to(aut5) -assert daut5.num_states() == 9 -assert pdaut5.num_states() == 8 +tc.assertTrue(pdaut5.equivalent_to(aut5)) +tc.assertEqual(daut5.num_states(), 9) +tc.assertEqual(pdaut5.num_states(), 8) aut6 = spot.automaton("""HOA: v1 States: 6 Start: 0 AP: 3 "p0" "p1" "p2" acc-name: generalized-Buchi 3 Acceptance: 3 Inf(0)&Inf(1)&Inf(2) properties: @@ -180,13 +182,13 @@ trans-labels explicit-labels trans-acc deterministic --BODY-- State: 0 [0&1&!2] 5 {1} State: 4 [!0&1&!2] 0 {1 2} [0&!1&!2] 3 {0} State: 5 [!0&1&2] 1 --END-- """) daut6 = spot.degeneralize_tba(aut6) -assert daut6.equivalent_to(aut6) +tc.assertTrue(daut6.equivalent_to(aut6)) sets = list(range(aut6.num_sets())) -assert spot.is_partially_degeneralizable(aut6) == sets +tc.assertEqual(spot.is_partially_degeneralizable(aut6), sets) pdaut6 = spot.partial_degeneralize(aut6, sets) -assert pdaut6.equivalent_to(aut6) -assert daut6.num_states() == 8 -assert pdaut6.num_states() == 8 +tc.assertTrue(pdaut6.equivalent_to(aut6)) +tc.assertEqual(daut6.num_states(), 8) +tc.assertEqual(pdaut6.num_states(), 8) aut7 = spot.automaton("""HOA: v1 States: 8 Start: 0 AP: 3 "p0" "p1" "p2" @@ -197,13 +199,13 @@ State: 0 [0&!1&2] 1 {2 3} State: 1 [0&!1&2] 0 {0 2} [0&!1&!2] 6 State: 2 [!0&!1&!2] 3 State: 5 [0&1&!2] 0 [!0&1&2] 7 State: 6 [0&1&2] 2 {1} State: 7 [!0&!1&2] 0 {0} [!0&1&!2] 4 --END--""") daut7 = spot.degeneralize_tba(aut7) -assert daut7.equivalent_to(aut7) +tc.assertTrue(daut7.equivalent_to(aut7)) sets = list(range(aut7.num_sets())) -assert spot.is_partially_degeneralizable(aut7) == sets +tc.assertEqual(spot.is_partially_degeneralizable(aut7), sets) pdaut7 = spot.partial_degeneralize(aut7, sets) -assert pdaut7.equivalent_to(aut7) -assert daut7.num_states() == 10 -assert pdaut7.num_states() == 10 +tc.assertTrue(pdaut7.equivalent_to(aut7)) +tc.assertEqual(daut7.num_states(), 10) +tc.assertEqual(pdaut7.num_states(), 10) aut8 = spot.automaton("""HOA: v1 States: 8 Start: 0 AP: 3 "p0" "p1" "p2" acc-name: generalized-Buchi 5 Acceptance: 5 Inf(0)&Inf(1)&Inf(2)&Inf(3)&Inf(4) @@ -213,19 +215,19 @@ State: 0 [!0&1&!2] 7 {0} State: 1 [!0&1&2] 1 {4} [0&!1&2] 6 {1 2} State: 2 5 [!0&1&!2] 0 {1 3} State: 6 [0&1&2] 4 [0&1&!2] 6 State: 7 [!0&!1&!2] 1 --END--""") daut8 = spot.degeneralize_tba(aut8) -assert daut8.equivalent_to(aut8) +tc.assertTrue(daut8.equivalent_to(aut8)) sets = list(range(aut8.num_sets())) -assert spot.is_partially_degeneralizable(aut8) == sets +tc.assertEqual(spot.is_partially_degeneralizable(aut8), sets) pdaut8 = spot.partial_degeneralize(aut8, sets) -assert pdaut8.equivalent_to(aut8) -assert daut8.num_states() == 22 -assert pdaut8.num_states() == 9 +tc.assertTrue(pdaut8.equivalent_to(aut8)) +tc.assertEqual(daut8.num_states(), 22) +tc.assertEqual(pdaut8.num_states(), 9) aut9 = spot.dualize(aut8) pdaut9 = spot.partial_degeneralize(aut9, sets) -assert pdaut9.equivalent_to(aut9) +tc.assertTrue(pdaut9.equivalent_to(aut9)) # one more state than aut9, because dualize completed the automaton. -assert pdaut9.num_states() == 10 +tc.assertEqual(pdaut9.num_states(), 10) aut10 = spot.automaton("""HOA: v1 States: 3 @@ -242,10 +244,10 @@ State: 2 [0] 0 {1} [!0] 1 --END--""") -assert spot.is_partially_degeneralizable(aut10) == [0, 1] +tc.assertEqual(spot.is_partially_degeneralizable(aut10), [0, 1]) pdaut10 = spot.partial_degeneralize(aut10, [0, 1]) -assert pdaut10.equivalent_to(aut10) -assert pdaut10.to_str() == """HOA: v1 +tc.assertTrue(pdaut10.equivalent_to(aut10)) +tc.assertEqual(pdaut10.to_str(), """HOA: v1 States: 3 Start: 0 AP: 1 "p0" @@ -260,7 +262,7 @@ State: 1 State: 2 [0] 0 {1} [!0] 1 ---END--""" +--END--""") aut11 = spot.automaton("""HOA: v1 States: 3 @@ -277,10 +279,10 @@ State: 2 [0] 0 {1} [!0] 1 --END--""") -assert spot.is_partially_degeneralizable(aut11) == [0, 1] +tc.assertEqual(spot.is_partially_degeneralizable(aut11), [0, 1]) pdaut11 = spot.partial_degeneralize(aut11, [0, 1]) -assert pdaut11.equivalent_to(aut11) -assert pdaut11.to_str() == """HOA: v1 +tc.assertTrue(pdaut11.equivalent_to(aut11)) +tc.assertEqual(pdaut11.to_str(), """HOA: v1 States: 3 Start: 0 AP: 1 "p0" @@ -295,7 +297,7 @@ State: 1 State: 2 [0] 0 {2} [!0] 1 ---END--""" +--END--""") aut12 = spot.automaton("""HOA: v1 States: 3 @@ -313,24 +315,24 @@ State: 2 [0] 0 [!0] 1 {3} --END--""") -assert spot.is_partially_degeneralizable(aut12) == [0,1] +tc.assertEqual(spot.is_partially_degeneralizable(aut12), [0,1]) aut12b = spot.partial_degeneralize(aut12, [0,1]) aut12c = spot.partial_degeneralize(aut12b, [1,2]) -assert aut12c.equivalent_to(aut12) -assert aut12c.num_states() == 9 +tc.assertTrue(aut12c.equivalent_to(aut12)) +tc.assertEqual(aut12c.num_states(), 9) aut12d = spot.partial_degeneralize(aut12, [0,1,3]) aut12e = spot.partial_degeneralize(aut12d, [0,1]) -assert aut12e.equivalent_to(aut12) -assert aut12e.num_states() == 9 +tc.assertTrue(aut12e.equivalent_to(aut12)) +tc.assertEqual(aut12e.num_states(), 9) aut12f = spot.partial_degeneralize(aut12) -assert aut12f.equivalent_to(aut12) -assert aut12f.num_states() == 9 +tc.assertTrue(aut12f.equivalent_to(aut12)) +tc.assertEqual(aut12f.num_states(), 9) # Check handling of original-states dot = aut12f.to_str('dot', 'd') -assert dot == """digraph "" { +tc.assertEqual(dot, """digraph "" { rankdir=LR label="Inf(2) | (Inf(1) & Fin(0))\\n[Rabin-like 2]" labelloc="t" @@ -367,10 +369,10 @@ assert dot == """digraph "" { 8 -> 4 [label="p0\\n{1,2}"] 8 -> 7 [label="p0"] } -""" +""") aut12g = spot.partial_degeneralize(aut12f) -assert aut12f == aut12g +tc.assertEqual(aut12f, aut12g) aut13 = spot.automaton("""HOA: v1 States: 2 @@ -390,8 +392,8 @@ State: 1 [!0&!1&2&3] 1 {0 2} --END--""") aut13g = spot.partial_degeneralize(aut13) -assert aut13g.equivalent_to(aut13) -assert aut13g.num_states() == 3 +tc.assertTrue(aut13g.equivalent_to(aut13)) +tc.assertEqual(aut13g.num_states(), 3) aut14 = spot.automaton("""HOA: v1 @@ -412,8 +414,8 @@ State: 1 --END-- """) aut14g = spot.partial_degeneralize(aut14) -assert aut14g.equivalent_to(aut14) -assert aut14g.num_states() == 3 +tc.assertTrue(aut14g.equivalent_to(aut14)) +tc.assertEqual(aut14g.num_states(), 3) # Extracting an SCC from this large automaton will produce an automaton A in # which original-states refers to states larger than those in A. Some version @@ -439,4 +441,4 @@ State: 10 [!0&1] 4 [0&1] 8 [!0&!1] 10 {0 1 2 3 5} [0&!1] 13 {1 2 3} State: 11 si = spot.scc_info(aut15) aut15b = si.split_on_sets(2, [])[0]; d aut15c = spot.partial_degeneralize(aut15b) -assert aut15c.equivalent_to(aut15b) +tc.assertTrue(aut15c.equivalent_to(aut15b)) diff --git a/tests/python/prodexpt.py b/tests/python/prodexpt.py index 098bafb26..4d00b4dae 100644 --- a/tests/python/prodexpt.py +++ b/tests/python/prodexpt.py @@ -1,6 +1,6 @@ # -*- mode: python; coding: utf-8 -*- -# Copyright (C) 2016-2017, 2019-2020 Laboratoire de Recherche et Développement -# de l'Epita (LRDE). +# Copyright (C) 2016-2017, 2019-2020, 2022 Laboratoire de Recherche et +# Développement de l'Epita (LRDE). # # This file is part of Spot, a model checking library. # @@ -18,6 +18,8 @@ # along with this program. If not, see . import spot +from unittest import TestCase +tc = TestCase() # make sure that we are not allowed to build the product of two automata with # different dictionaries. @@ -94,14 +96,14 @@ State: 60 40 38 60 68 State: 61 40 41 57 61 State: 62 40 59 44 62 State: State: 70 40 59 57 70 State: 71 40 63 57 71 State: 72 40 69 57 72 --END-- ''') res = spot.product(left, right) -assert res.num_states() == 977 -assert res.num_edges() == 8554 +tc.assertEqual(res.num_states(), 977) +tc.assertEqual(res.num_edges(), 8554) res = spot.product(left, right, spot.output_aborter(1000, 6000)) -assert res is None +tc.assertIsNone(res) res = spot.product(left, right, spot.output_aborter(900, 9000)) -assert res is None +tc.assertIsNone(res) res = spot.product(left, right, spot.output_aborter(1000, 9000)) -assert res is not None +tc.assertIsNotNone(res) a, b = spot.automata("""HOA: v1 States: 1 Start: 0 AP: 0 acc-name: all Acceptance: 0 t properties: trans-labels explicit-labels state-acc complete @@ -110,7 +112,7 @@ properties: deterministic stutter-invariant weak --BODY-- State: 0 [t] 0 properties: trans-labels explicit-labels state-acc complete properties: deterministic stutter-invariant weak --BODY-- State: 0 [t] 0 --END--""") out = spot.product(a, b).to_str() -assert out == """HOA: v1 +tc.assertEqual(out, """HOA: v1 States: 1 Start: 0 AP: 0 @@ -120,9 +122,9 @@ properties: trans-labels explicit-labels state-acc deterministic properties: stutter-invariant terminal --BODY-- State: 0 ---END--""" +--END--""") out = spot.product_susp(a, b).to_str() -assert out == """HOA: v1 +tc.assertEqual(out, """HOA: v1 States: 1 Start: 0 AP: 0 @@ -132,4 +134,4 @@ properties: trans-labels explicit-labels state-acc deterministic properties: stutter-invariant terminal --BODY-- State: 0 ---END--""" +--END--""") diff --git a/tests/python/randgen.py b/tests/python/randgen.py index 094ddcb3f..32762d02e 100755 --- a/tests/python/randgen.py +++ b/tests/python/randgen.py @@ -1,6 +1,6 @@ # -*- mode: python; coding: utf-8 -*- -# Copyright (C) 2015 Laboratoire de Recherche et Développement de -# l'Epita (LRDE). +# Copyright (C) 2015, 2022 Laboratoire de Recherche et Développement +# de l'Epita (LRDE). # # This file is part of Spot, a model checking library. # @@ -18,9 +18,11 @@ # along with this program. If not, see . import spot +from unittest import TestCase +tc = TestCase() o = spot.option_map() g = spot.randltlgenerator(0, o) -assert str(g.next()) == '1' -assert str(g.next()) == '0' -assert str(g.next()) == 'None' +tc.assertEqual(str(g.next()), '1') +tc.assertEqual(str(g.next()), '0') +tc.assertEqual(str(g.next()), 'None') diff --git a/tests/python/relabel.py b/tests/python/relabel.py index 5a4a370eb..0de668b12 100644 --- a/tests/python/relabel.py +++ b/tests/python/relabel.py @@ -1,6 +1,6 @@ # -*- mode: python; coding: utf-8 -*- -# Copyright (C) 2015, 2017-2019 Laboratoire de Recherche et Développement -# de l'Epita +# Copyright (C) 2015, 2017-2019, 2022 Laboratoire de Recherche et +# Développement de l'Epita # # This file is part of Spot, a model checking library. # @@ -18,6 +18,8 @@ # along with this program. If not, see . import spot +from unittest import TestCase +tc = TestCase() f = spot.formula('GF(a & b) -> (FG(a & b) & Gc)') m = spot.relabeling_map() @@ -26,19 +28,18 @@ res = "" for old, new in m.items(): res += "#define {} {}\n".format(old, new) res += str(g) -print(res) -assert(res == """#define p0 a & b +tc.assertEqual(res, """#define p0 a & b #define p1 c GFp0 -> (FGp0 & Gp1)""") h = spot.relabel_apply(g, m) -assert h == f +tc.assertEqual(h, f) autg = g.translate() spot.relabel_here(autg, m) -assert str(autg.ap()) == \ - '(spot.formula("a"), spot.formula("b"), spot.formula("c"))' -assert spot.isomorphism_checker.are_isomorphic(autg, f.translate()) +tc.assertEqual(str(autg.ap()), \ + '(spot.formula("a"), spot.formula("b"), spot.formula("c"))') +tc.assertTrue(spot.isomorphism_checker.are_isomorphic(autg, f.translate())) a = spot.formula('a') u = spot.formula('a U b') @@ -46,11 +47,11 @@ m[a] = u try: spot.relabel_here(autg, m) except RuntimeError as e: - assert "new labels" in str(e) + tc.assertIn("new labels", str(e)) m = spot.relabeling_map() m[u] = a try: spot.relabel_here(autg, m) except RuntimeError as e: - assert "old labels" in str(e) + tc.assertIn("old labels", str(e)) diff --git a/tests/python/remfin.py b/tests/python/remfin.py index 20115a14f..ffff3e22a 100644 --- a/tests/python/remfin.py +++ b/tests/python/remfin.py @@ -1,6 +1,6 @@ # -*- mode: python; coding: utf-8 -*- -# Copyright (C) 2015-2018, 2020 Laboratoire de Recherche et Développement de -# l'Epita +# Copyright (C) 2015-2018, 2020, 2022 Laboratoire de Recherche et +# Développement de l'Epita # # This file is part of Spot, a model checking library. # @@ -19,6 +19,8 @@ import spot +from unittest import TestCase +tc = TestCase() # This test used to trigger an assertion (or a segfault) # in scc_filter_states(). @@ -41,7 +43,7 @@ State: 2 aut.prop_inherently_weak(True) aut = spot.dualize(aut) aut1 = spot.scc_filter_states(aut) -assert(aut1.to_str('hoa') == """HOA: v1 +tc.assertEqual(aut1.to_str('hoa'), """HOA: v1 States: 2 Start: 0 AP: 1 "a" @@ -56,17 +58,17 @@ State: 1 [t] 1 --END--""") -assert(aut.scc_filter_states().to_str() == aut1.to_str()) -assert(aut1.get_name() == None) +tc.assertEqual(aut.scc_filter_states().to_str(), aut1.to_str()) +tc.assertIsNone(aut1.get_name()) aut1.set_name("test me") -assert(aut1.get_name() == "test me") +tc.assertEqual(aut1.get_name(), "test me") # The method is the same as the function a = spot.translate('true', 'low', 'any') -assert(a.prop_universal().is_maybe()) -assert(a.prop_unambiguous().is_maybe()) -assert(a.is_deterministic() == True) -assert(a.is_unambiguous() == True) +tc.assertTrue(a.prop_universal().is_maybe()) +tc.assertTrue(a.prop_unambiguous().is_maybe()) +tc.assertTrue(a.is_deterministic()) +tc.assertTrue(a.is_unambiguous()) a = spot.automaton(""" HOA: v1 @@ -92,4 +94,4 @@ State: 2 """) b = spot.remove_fin(a) size = (b.num_states(), b.num_edges()) -assert size == (5, 13); +tc.assertEqual(size, (5, 13)) diff --git a/tests/python/removeap.py b/tests/python/removeap.py index 7a9268c85..ba656ac89 100644 --- a/tests/python/removeap.py +++ b/tests/python/removeap.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Copyright (C) 2019 Laboratoire de Recherche et Développement +# Copyright (C) 2019, 2022 Laboratoire de Recherche et Développement # de l'Epita # # This file is part of Spot, a model checking library. @@ -18,16 +18,18 @@ # along with this program. If not, see . import spot +from unittest import TestCase +tc = TestCase() aut = spot.translate('a U (c & Gb)') -assert not spot.is_terminal_automaton(aut) -assert aut.prop_terminal().is_false() +tc.assertFalse(spot.is_terminal_automaton(aut)) +tc.assertTrue(aut.prop_terminal().is_false()) rem = spot.remove_ap() rem.add_ap("b") aut = rem.strip(aut) -assert not aut.prop_terminal().is_false() -assert spot.is_terminal_automaton(aut) -assert aut.prop_terminal().is_true() +tc.assertFalse(aut.prop_terminal().is_false()) +tc.assertTrue(spot.is_terminal_automaton(aut)) +tc.assertTrue(aut.prop_terminal().is_true()) aut = rem.strip(aut) -assert aut.prop_terminal().is_true() +tc.assertTrue(aut.prop_terminal().is_true()) diff --git a/tests/python/rs_like.py b/tests/python/rs_like.py index 7b4ee75cf..669af5885 100644 --- a/tests/python/rs_like.py +++ b/tests/python/rs_like.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Copyright (C) 2017 Laboratoire de Recherche et Développement +# Copyright (C) 2017, 2022 Laboratoire de Recherche et Développement # de l'Epita # # This file is part of Spot, a model checking library. @@ -18,6 +18,8 @@ # along with this program. If not, see . import spot +from unittest import TestCase +tc = TestCase() a = spot.vector_rs_pair() @@ -30,12 +32,13 @@ mall = spot.mark_t() def test_rs(acc, rs, expected_res, expected_pairs): res, p = getattr(acc, 'is_' + rs + '_like')() - assert res == expected_res + tc.assertEqual(res, expected_res) if expected_res: expected_pairs.sort() p = sorted(p) for a, b in zip(p, expected_pairs): - assert a.fin == b.fin and a.inf == b.inf + tc.assertEqual(a.fin, b.fin) + tc.assertEqual(a.inf, b.inf) def switch_pairs(pairs): diff --git a/tests/python/satmin.py b/tests/python/satmin.py index 2d28dd405..f9fa466f8 100644 --- a/tests/python/satmin.py +++ b/tests/python/satmin.py @@ -1,6 +1,6 @@ # -*- mode: python; coding: utf-8 -*- -# Copyright (C) 2015, 2020, 2021 Laboratoire de Recherche et Développement -# de l'Epita +# Copyright (C) 2015, 2020, 2021, 2022 Laboratoire de Recherche et +# Développement de l'Epita # # This file is part of Spot, a model checking library. # @@ -18,232 +18,234 @@ # along with this program. If not, see . import spot +from unittest import TestCase +tc = TestCase() aut = spot.translate('GFa & GFb', 'Buchi', 'SBAcc') -assert aut.num_sets() == 1 -assert aut.num_states() == 3 -assert aut.is_deterministic() +tc.assertEqual(aut.num_sets(), 1) +tc.assertEqual(aut.num_states(), 3) +tc.assertTrue(aut.is_deterministic()) min1 = spot.sat_minimize(aut, acc='Rabin 1') -assert min1.num_sets() == 2 -assert min1.num_states() == 2 +tc.assertEqual(min1.num_sets(), 2) +tc.assertEqual(min1.num_states(), 2) min1 = spot.sat_minimize(aut, acc='Rabin 1', sat_langmap=True) -assert min1.num_sets() == 2 -assert min1.num_states() == 2 +tc.assertEqual(min1.num_sets(), 2) +tc.assertEqual(min1.num_states(), 2) min1 = spot.sat_minimize(aut, acc='Rabin 1', sat_incr=1) -assert min1.num_sets() == 2 -assert min1.num_states() == 2 +tc.assertEqual(min1.num_sets(), 2) +tc.assertEqual(min1.num_states(), 2) min1 = spot.sat_minimize(aut, acc='Rabin 1', sat_incr=1, sat_incr_steps=0) -assert min1.num_sets() == 2 -assert min1.num_states() == 2 +tc.assertEqual(min1.num_sets(), 2) +tc.assertEqual(min1.num_states(), 2) min1 = spot.sat_minimize(aut, acc='Rabin 1', sat_incr=1, sat_incr_steps=1) -assert min1.num_sets() == 2 -assert min1.num_states() == 2 +tc.assertEqual(min1.num_sets(), 2) +tc.assertEqual(min1.num_states(), 2) min1 = spot.sat_minimize(aut, acc='Rabin 1', sat_incr=1, sat_incr_steps=2) -assert min1.num_sets() == 2 -assert min1.num_states() == 2 +tc.assertEqual(min1.num_sets(), 2) +tc.assertEqual(min1.num_states(), 2) min1 = spot.sat_minimize(aut, acc='Rabin 1', sat_incr=1, sat_incr_steps=50) -assert min1.num_sets() == 2 -assert min1.num_states() == 2 +tc.assertEqual(min1.num_sets(), 2) +tc.assertEqual(min1.num_states(), 2) min1 = spot.sat_minimize(aut, acc='Rabin 1', sat_incr=2) -assert min1.num_sets() == 2 -assert min1.num_states() == 2 +tc.assertEqual(min1.num_sets(), 2) +tc.assertEqual(min1.num_states(), 2) min1 = spot.sat_minimize(aut, acc='Rabin 1', sat_incr=2, sat_incr_steps=-1) -assert min1.num_sets() == 2 -assert min1.num_states() == 2 +tc.assertEqual(min1.num_sets(), 2) +tc.assertEqual(min1.num_states(), 2) min1 = spot.sat_minimize(aut, acc='Rabin 1', sat_incr=2, sat_incr_steps=0) -assert min1.num_sets() == 2 -assert min1.num_states() == 2 +tc.assertEqual(min1.num_sets(), 2) +tc.assertEqual(min1.num_states(), 2) min1 = spot.sat_minimize(aut, acc='Rabin 1', sat_incr=2, sat_incr_steps=1) -assert min1.num_sets() == 2 -assert min1.num_states() == 2 +tc.assertEqual(min1.num_sets(), 2) +tc.assertEqual(min1.num_states(), 2) min1 = spot.sat_minimize(aut, acc='Rabin 1', sat_incr=2, sat_incr_steps=2) -assert min1.num_sets() == 2 -assert min1.num_states() == 2 +tc.assertEqual(min1.num_sets(), 2) +tc.assertEqual(min1.num_states(), 2) min1 = spot.sat_minimize(aut, acc='Rabin 1', sat_incr=2, sat_incr_steps=50) -assert min1.num_sets() == 2 -assert min1.num_states() == 2 +tc.assertEqual(min1.num_sets(), 2) +tc.assertEqual(min1.num_states(), 2) min1 = spot.sat_minimize(aut, acc='Rabin 1', sat_naive=True) -assert min1.num_sets() == 2 -assert min1.num_states() == 2 +tc.assertEqual(min1.num_sets(), 2) +tc.assertEqual(min1.num_states(), 2) min2 = spot.sat_minimize(aut, acc='Streett 2') -assert min2.num_sets() == 4 -assert min2.num_states() == 1 +tc.assertEqual(min2.num_sets(), 4) +tc.assertEqual(min2.num_states(), 1) min2 = spot.sat_minimize(aut, acc='Streett 2', sat_langmap=True) -assert min2.num_sets() == 4 -assert min2.num_states() == 1 +tc.assertEqual(min2.num_sets(), 4) +tc.assertEqual(min2.num_states(), 1) min2 = spot.sat_minimize(aut, acc='Streett 2', sat_incr=1) -assert min2.num_sets() == 4 -assert min2.num_states() == 1 +tc.assertEqual(min2.num_sets(), 4) +tc.assertEqual(min2.num_states(), 1) min2 = spot.sat_minimize(aut, acc='Streett 2', sat_incr=1, sat_incr_steps=0) -assert min2.num_sets() == 4 -assert min2.num_states() == 1 +tc.assertEqual(min2.num_sets(), 4) +tc.assertEqual(min2.num_states(), 1) min2 = spot.sat_minimize(aut, acc='Streett 2', sat_incr=1, sat_incr_steps=1) -assert min2.num_sets() == 4 -assert min2.num_states() == 1 +tc.assertEqual(min2.num_sets(), 4) +tc.assertEqual(min2.num_states(), 1) min2 = spot.sat_minimize(aut, acc='Streett 2', sat_incr=1, sat_incr_steps=2) -assert min2.num_sets() == 4 -assert min2.num_states() == 1 +tc.assertEqual(min2.num_sets(), 4) +tc.assertEqual(min2.num_states(), 1) min2 = spot.sat_minimize(aut, acc='Streett 2', sat_incr=1, sat_incr_steps=50) -assert min2.num_sets() == 4 -assert min2.num_states() == 1 +tc.assertEqual(min2.num_sets(), 4) +tc.assertEqual(min2.num_states(), 1) min2 = spot.sat_minimize(aut, acc='Streett 2', sat_incr=2) -assert min2.num_sets() == 4 -assert min2.num_states() == 1 +tc.assertEqual(min2.num_sets(), 4) +tc.assertEqual(min2.num_states(), 1) min2 = spot.sat_minimize(aut, acc='Streett 2', sat_incr=2, sat_incr_steps=-1) -assert min2.num_sets() == 4 -assert min2.num_states() == 1 +tc.assertEqual(min2.num_sets(), 4) +tc.assertEqual(min2.num_states(), 1) min2 = spot.sat_minimize(aut, acc='Streett 2', sat_incr=2, sat_incr_steps=0) -assert min2.num_sets() == 4 -assert min2.num_states() == 1 +tc.assertEqual(min2.num_sets(), 4) +tc.assertEqual(min2.num_states(), 1) min2 = spot.sat_minimize(aut, acc='Streett 2', sat_incr=2, sat_incr_steps=1) -assert min2.num_sets() == 4 -assert min2.num_states() == 1 +tc.assertEqual(min2.num_sets(), 4) +tc.assertEqual(min2.num_states(), 1) min2 = spot.sat_minimize(aut, acc='Streett 2', sat_incr=2, sat_incr_steps=2) -assert min2.num_sets() == 4 -assert min2.num_states() == 1 +tc.assertEqual(min2.num_sets(), 4) +tc.assertEqual(min2.num_states(), 1) min2 = spot.sat_minimize(aut, acc='Streett 2', sat_incr=2, sat_incr_steps=50) -assert min2.num_sets() == 4 -assert min2.num_states() == 1 +tc.assertEqual(min2.num_sets(), 4) +tc.assertEqual(min2.num_states(), 1) min2 = spot.sat_minimize(aut, acc='Streett 2', sat_naive=True) -assert min2.num_sets() == 4 -assert min2.num_states() == 1 +tc.assertEqual(min2.num_sets(), 4) +tc.assertEqual(min2.num_states(), 1) min3 = spot.sat_minimize(aut, acc='Rabin 2', state_based=True, max_states=5) -assert min3.num_sets() == 4 -assert min3.num_states() == 3 +tc.assertEqual(min3.num_sets(), 4) +tc.assertEqual(min3.num_states(), 3) min3 = spot.sat_minimize(aut, acc='Rabin 2', state_based=True, max_states=5, sat_langmap=True) -assert min3.num_sets() == 4 -assert min3.num_states() == 3 +tc.assertEqual(min3.num_sets(), 4) +tc.assertEqual(min3.num_states(), 3) min3 = spot.sat_minimize(aut, acc='Rabin 2', state_based=True, max_states=5, sat_incr=1) -assert min3.num_sets() == 4 -assert min3.num_states() == 3 +tc.assertEqual(min3.num_sets(), 4) +tc.assertEqual(min3.num_states(), 3) min3 = spot.sat_minimize(aut, acc='Rabin 2', state_based=True, max_states=5, sat_incr=1, sat_incr_steps=0) -assert min3.num_sets() == 4 -assert min3.num_states() == 3 +tc.assertEqual(min3.num_sets(), 4) +tc.assertEqual(min3.num_states(), 3) min3 = spot.sat_minimize(aut, acc='Rabin 2', state_based=True, max_states=5, sat_incr=1, sat_incr_steps=1) -assert min3.num_sets() == 4 -assert min3.num_states() == 3 +tc.assertEqual(min3.num_sets(), 4) +tc.assertEqual(min3.num_states(), 3) min3 = spot.sat_minimize(aut, acc='Rabin 2', state_based=True, max_states=5, sat_incr=1, sat_incr_steps=2) -assert min3.num_sets() == 4 -assert min3.num_states() == 3 +tc.assertEqual(min3.num_sets(), 4) +tc.assertEqual(min3.num_states(), 3) min3 = spot.sat_minimize(aut, acc='Rabin 2', state_based=True, max_states=5, sat_incr=1, sat_incr_steps=50) -assert min3.num_sets() == 4 -assert min3.num_states() == 3 +tc.assertEqual(min3.num_sets(), 4) +tc.assertEqual(min3.num_states(), 3) min3 = spot.sat_minimize(aut, acc='Rabin 2', state_based=True, max_states=5, sat_incr=2) -assert min3.num_sets() == 4 -assert min3.num_states() == 3 +tc.assertEqual(min3.num_sets(), 4) +tc.assertEqual(min3.num_states(), 3) min3 = spot.sat_minimize(aut, acc='Rabin 2', state_based=True, max_states=5, sat_incr=2, sat_incr_steps=-1) -assert min3.num_sets() == 4 -assert min3.num_states() == 3 +tc.assertEqual(min3.num_sets(), 4) +tc.assertEqual(min3.num_states(), 3) min3 = spot.sat_minimize(aut, acc='Rabin 2', state_based=True, max_states=5, sat_incr=2, sat_incr_steps=0) -assert min3.num_sets() == 4 -assert min3.num_states() == 3 +tc.assertEqual(min3.num_sets(), 4) +tc.assertEqual(min3.num_states(), 3) min3 = spot.sat_minimize(aut, acc='Rabin 2', state_based=True, max_states=5, sat_incr=2, sat_incr_steps=1) -assert min3.num_sets() == 4 -assert min3.num_states() == 3 +tc.assertEqual(min3.num_sets(), 4) +tc.assertEqual(min3.num_states(), 3) min3 = spot.sat_minimize(aut, acc='Rabin 2', state_based=True, max_states=5, sat_incr=2, sat_incr_steps=2) -assert min3.num_sets() == 4 -assert min3.num_states() == 3 +tc.assertEqual(min3.num_sets(), 4) +tc.assertEqual(min3.num_states(), 3) min3 = spot.sat_minimize(aut, acc='Rabin 2', state_based=True, max_states=5, sat_incr=2, sat_incr_steps=50) -assert min3.num_sets() == 4 -assert min3.num_states() == 3 +tc.assertEqual(min3.num_sets(), 4) +tc.assertEqual(min3.num_states(), 3) min3 = spot.sat_minimize(aut, acc='Rabin 2', state_based=True, max_states=5, sat_naive=True) -assert min3.num_sets() == 4 -assert min3.num_states() == 3 +tc.assertEqual(min3.num_sets(), 4) +tc.assertEqual(min3.num_states(), 3) min4 = spot.sat_minimize(aut, acc='parity max odd 3', colored=True) -assert min4.num_sets() == 3 -assert min4.num_states() == 2 +tc.assertEqual(min4.num_sets(), 3) +tc.assertEqual(min4.num_states(), 2) min4 = spot.sat_minimize(aut, acc='parity max odd 3', colored=True, sat_langmap=True) -assert min4.num_sets() == 3 -assert min4.num_states() == 2 +tc.assertEqual(min4.num_sets(), 3) +tc.assertEqual(min4.num_states(), 2) min4 = spot.sat_minimize(aut, acc='parity max odd 3', colored=True, sat_incr=1) -assert min4.num_sets() == 3 -assert min4.num_states() == 2 +tc.assertEqual(min4.num_sets(), 3) +tc.assertEqual(min4.num_states(), 2) min4 = spot.sat_minimize(aut, acc='parity max odd 3', colored=True, sat_incr=1, sat_incr_steps=0) -assert min4.num_sets() == 3 -assert min4.num_states() == 2 +tc.assertEqual(min4.num_sets(), 3) +tc.assertEqual(min4.num_states(), 2) min4 = spot.sat_minimize(aut, acc='parity max odd 3', colored=True, sat_incr=1, sat_incr_steps=1) -assert min4.num_sets() == 3 -assert min4.num_states() == 2 +tc.assertEqual(min4.num_sets(), 3) +tc.assertEqual(min4.num_states(), 2) min4 = spot.sat_minimize(aut, acc='parity max odd 3', colored=True, sat_incr=1, sat_incr_steps=2) -assert min4.num_sets() == 3 -assert min4.num_states() == 2 +tc.assertEqual(min4.num_sets(), 3) +tc.assertEqual(min4.num_states(), 2) min4 = spot.sat_minimize(aut, acc='parity max odd 3', colored=True, sat_incr=1, sat_incr_steps=50) -assert min4.num_sets() == 3 -assert min4.num_states() == 2 +tc.assertEqual(min4.num_sets(), 3) +tc.assertEqual(min4.num_states(), 2) min4 = spot.sat_minimize(aut, acc='parity max odd 3', colored=True, sat_incr=2) -assert min4.num_sets() == 3 -assert min4.num_states() == 2 +tc.assertEqual(min4.num_sets(), 3) +tc.assertEqual(min4.num_states(), 2) min4 = spot.sat_minimize(aut, acc='parity max odd 3', colored=True, sat_incr=2, sat_incr_steps=-1) -assert min4.num_sets() == 3 -assert min4.num_states() == 2 +tc.assertEqual(min4.num_sets(), 3) +tc.assertEqual(min4.num_states(), 2) min4 = spot.sat_minimize(aut, acc='parity max odd 3', colored=True, sat_incr=2, sat_incr_steps=0) -assert min4.num_sets() == 3 -assert min4.num_states() == 2 +tc.assertEqual(min4.num_sets(), 3) +tc.assertEqual(min4.num_states(), 2) min4 = spot.sat_minimize(aut, acc='parity max odd 3', colored=True, sat_incr=2, sat_incr_steps=1) -assert min4.num_sets() == 3 -assert min4.num_states() == 2 +tc.assertEqual(min4.num_sets(), 3) +tc.assertEqual(min4.num_states(), 2) min4 = spot.sat_minimize(aut, acc='parity max odd 3', colored=True, sat_incr=2, sat_incr_steps=2) -assert min4.num_sets() == 3 -assert min4.num_states() == 2 +tc.assertEqual(min4.num_sets(), 3) +tc.assertEqual(min4.num_states(), 2) min4 = spot.sat_minimize(aut, acc='parity max odd 3', colored=True, sat_incr=2, sat_incr_steps=50) -assert min4.num_sets() == 3 -assert min4.num_states() == 2 +tc.assertEqual(min4.num_sets(), 3) +tc.assertEqual(min4.num_states(), 2) min4 = spot.sat_minimize(aut, acc='parity max odd 3', colored=True, sat_naive=True) -assert min4.num_sets() == 3 -assert min4.num_states() == 2 +tc.assertEqual(min4.num_sets(), 3) +tc.assertEqual(min4.num_states(), 2) aut = spot.translate('GFa') -assert aut.num_sets() == 1 -assert aut.num_states() == 1 -assert aut.is_deterministic() +tc.assertEqual(aut.num_sets(), 1) +tc.assertEqual(aut.num_states(), 1) +tc.assertTrue(aut.is_deterministic()) out = spot.sat_minimize(aut, state_based=True) -assert out.num_states() == 2 +tc.assertEqual(out.num_states(), 2) out = spot.sat_minimize(aut, state_based=True, max_states=1) -assert out is None +tc.assertTrue(out is None) diff --git a/tests/python/sbacc.py b/tests/python/sbacc.py index 445845dbc..22d937014 100644 --- a/tests/python/sbacc.py +++ b/tests/python/sbacc.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Copyright (C) 2017-2018, 2021 Laboratoire de Recherche et +# Copyright (C) 2017-2018, 2021, 2022 Laboratoire de Recherche et # Développement de l'Epita (LRDE). # # This file is part of Spot, a model checking library. @@ -18,13 +18,15 @@ # along with this program. If not, see . import spot -aut = spot.translate('GFa') -assert aut.num_states() == 1 -assert not aut.prop_state_acc().is_true() -aut = spot.sbacc(aut) -assert aut.num_states() == 2 -assert aut.prop_state_acc().is_true() +from unittest import TestCase +tc = TestCase() +aut = spot.translate('GFa') +tc.assertEqual(aut.num_states(), 1) +tc.assertFalse(aut.prop_state_acc().is_true()) +aut = spot.sbacc(aut) +tc.assertEqual(aut.num_states(), 2) +tc.assertTrue(aut.prop_state_acc().is_true()) aut = spot.automaton("""HOA: v1 States: 3 @@ -48,7 +50,7 @@ s = spot.sbacc(aut) s.copy_state_names_from(aut) h = s.to_str('hoa') -assert h == """HOA: v1 +tc.assertEqual(h, """HOA: v1 States: 2 Start: 0 AP: 2 "a" "b" @@ -59,7 +61,7 @@ State: 0 "0" [0] 1 State: 1 "2" {1} [t] 1 ---END--""" +--END--""") aut = spot.automaton("""HOA: v1 States: 3 @@ -83,7 +85,7 @@ d = spot.degeneralize(aut) d.copy_state_names_from(aut) h = d.to_str('hoa') -assert h == """HOA: v1 +tc.assertEqual(h, """HOA: v1 States: 2 Start: 0 AP: 2 "a" "b" @@ -95,4 +97,4 @@ State: 0 "0#0" [0] 1 State: 1 "2#0" {0} [t] 1 ---END--""" +--END--""") diff --git a/tests/python/sccfilter.py b/tests/python/sccfilter.py index 6edd33e9f..7728b70a6 100644 --- a/tests/python/sccfilter.py +++ b/tests/python/sccfilter.py @@ -1,6 +1,6 @@ #!/usr/bin/python3 # -*- mode: python; coding: utf-8 -*- -# Copyright (C) 2016 Laboratoire de Recherche et Développement de +# Copyright (C) 2016, 2022 Laboratoire de Recherche et Développement de # l'EPITA. # # This file is part of Spot, a model checking library. @@ -22,6 +22,8 @@ # Major) import spot +from unittest import TestCase +tc = TestCase() a = spot.automaton(""" HOA: v1.1 @@ -43,7 +45,7 @@ State: 1 "bar" --END-- """) -assert (spot.scc_filter(a, True).to_str('hoa', '1.1') == """HOA: v1.1 +tc.assertEqual(spot.scc_filter(a, True).to_str('hoa', '1.1'), """HOA: v1.1 States: 2 Start: 0 AP: 1 "a" diff --git a/tests/python/sccinfo.py b/tests/python/sccinfo.py index 0ac645726..f8ade7e4b 100644 --- a/tests/python/sccinfo.py +++ b/tests/python/sccinfo.py @@ -1,7 +1,7 @@ #!/usr/bin/python3 # -*- mode: python; coding: utf-8 -*- -# Copyright (C) 2017, 2021 Laboratoire de Recherche et Développement de -# l'EPITA. +# Copyright (C) 2017, 2021, 2022 Laboratoire de Recherche et +# Développement de l'EPITA. # # This file is part of Spot, a model checking library. # @@ -19,6 +19,8 @@ # along with this program. If not, see . import spot +from unittest import TestCase +tc = TestCase() a = spot.translate('(Ga -> Gb) W c') @@ -26,11 +28,11 @@ try: si = spot.scc_info(a, 10) exit(2) except RuntimeError as e: - assert "initial state does not exist" in str(e) + tc.assertIn("initial state does not exist", str(e)) si = spot.scc_info(a) n = si.scc_count() -assert n == 4 +tc.assertEqual(n, 4) acc = 0 rej = 0 @@ -39,24 +41,24 @@ for i in range(n): acc += si.is_accepting_scc(i) rej += si.is_rejecting_scc(i) triv += si.is_trivial(i) -assert acc == 3 -assert rej == 1 -assert triv == 0 +tc.assertEqual(acc, 3) +tc.assertEqual(rej, 1) +tc.assertEqual(triv, 0) for scc in si: acc -= scc.is_accepting() rej -= scc.is_rejecting() triv -= scc.is_trivial() -assert acc == 0 -assert rej == 0 -assert triv == 0 +tc.assertEqual(acc, 0) +tc.assertEqual(rej, 0) +tc.assertEqual(triv, 0) l0 = si.states_of(0) l1 = si.states_of(1) l2 = si.states_of(2) l3 = si.states_of(3) l = sorted(list(l0) + list(l1) + list(l2) + list(l3)) -assert l == [0, 1, 2, 3, 4] +tc.assertEqual(l, [0, 1, 2, 3, 4]) i = si.initial() todo = [i] @@ -73,14 +75,14 @@ while todo: if s not in seen: seen.add(s) todo.append(s) -assert seen == {0, 1, 2, 3} -assert trans == [(0, 0), (0, 1), (0, 2), (0, 3), - (2, 0), (2, 1), (2, 2), (2, 4), - (3, 3), (4, 1), (4, 4), (1, 1)] -assert transi == [(0, 0, 1), (0, 2, 3), (2, 0, 6), - (2, 2, 8), (3, 3, 10), (4, 4, 12), (1, 1, 5)] +tc.assertEqual(seen, {0, 1, 2, 3}) +tc.assertEqual(trans, [(0, 0), (0, 1), (0, 2), (0, 3), + (2, 0), (2, 1), (2, 2), (2, 4), + (3, 3), (4, 1), (4, 4), (1, 1)]) +tc.assertEqual(transi, [(0, 0, 1), (0, 2, 3), (2, 0, 6), + (2, 2, 8), (3, 3, 10), (4, 4, 12), (1, 1, 5)]) -assert not spot.is_weak_automaton(a, si) +tc.assertFalse(spot.is_weak_automaton(a, si)) a = spot.automaton(""" @@ -107,8 +109,8 @@ State: 3 """) si = spot.scc_info(a) si.determine_unknown_acceptance() -assert si.scc_count() == 2 -assert si.is_accepting_scc(0) -assert not si.is_rejecting_scc(0) -assert si.is_rejecting_scc(1) -assert not si.is_accepting_scc(1) +tc.assertEqual(si.scc_count(), 2) +tc.assertTrue(si.is_accepting_scc(0)) +tc.assertFalse(si.is_rejecting_scc(0)) +tc.assertTrue(si.is_rejecting_scc(1)) +tc.assertFalse(si.is_accepting_scc(1)) diff --git a/tests/python/sccsplit.py b/tests/python/sccsplit.py index 9095a1a29..4a1781475 100644 --- a/tests/python/sccsplit.py +++ b/tests/python/sccsplit.py @@ -1,7 +1,7 @@ #!/usr/bin/python3 # -*- mode: python; coding: utf-8 -*- -# Copyright (C) 2018 Laboratoire de Recherche et Développement de -# l'EPITA. +# Copyright (C) 2018, 2022 Laboratoire de Recherche et Développement +# de l'EPITA. # # This file is part of Spot, a model checking library. # @@ -19,6 +19,9 @@ # along with this program. If not, see . import spot +from unittest import TestCase +tc = TestCase() + aut = spot.translate('GF(a <-> Xa) & GF(b <-> XXb)') si = spot.scc_info(aut) @@ -27,4 +30,4 @@ for aut2 in si.split_on_sets(0, [0]): # This call to to_str() used to fail because split_on_sets had not # registered the atomic propositions of aut s += aut2.to_str() -assert spot.automaton(s).num_states() == 8 +tc.assertEqual(spot.automaton(s).num_states(), 8) diff --git a/tests/python/semidet.py b/tests/python/semidet.py index 856b3b7d2..9072f5917 100644 --- a/tests/python/semidet.py +++ b/tests/python/semidet.py @@ -1,6 +1,6 @@ # -*- mode: python; coding: utf-8 -*- -# Copyright (C) 2017 Laboratoire de Recherche et Développement de -# l'Epita (LRDE). +# Copyright (C) 2017, 2022 Laboratoire de Recherche et Développement +# de l'Epita (LRDE). # # This file is part of Spot, a model checking library. # @@ -18,6 +18,8 @@ # along with this program. If not, see . import spot +from unittest import TestCase +tc = TestCase() formulas = [('(Gp0 | Fp1) M 1', False, True), ('(!p1 U p1) U X(!p0 -> Fp1)', False, True), @@ -31,9 +33,9 @@ for f, isd, issd in formulas: aut = spot.translate(f) # The formula with isd=True, issd=True is the only one # for which both properties are already set. - assert (aut.prop_deterministic().is_maybe() or - aut.prop_semi_deterministic().is_maybe() or - isd == issd) + tc.assertTrue(aut.prop_deterministic().is_maybe() or + aut.prop_semi_deterministic().is_maybe() or + isd == issd) spot.check_determinism(aut) - assert aut.prop_deterministic() == isd - assert aut.prop_semi_deterministic() == issd + tc.assertEqual(aut.prop_deterministic(), isd) + tc.assertEqual(aut.prop_semi_deterministic(), issd) diff --git a/tests/python/setacc.py b/tests/python/setacc.py index 8d20b6a49..7246bf5cc 100644 --- a/tests/python/setacc.py +++ b/tests/python/setacc.py @@ -1,7 +1,7 @@ #!/usr/bin/python3 # -*- mode: python; coding: utf-8 -*- -# Copyright (C) 2016, 2018, 2021 Laboratoire de Recherche et Développement de -# l'EPITA. +# Copyright (C) 2016, 2018, 2021, 2022 Laboratoire de Recherche et +# Développement de l'EPITA. # # This file is part of Spot, a model checking library. # @@ -19,54 +19,56 @@ # along with this program. If not, see . import spot +from unittest import TestCase +tc = TestCase() # Test case reduced from a report from Juraj Major . a = spot.make_twa_graph(spot._bdd_dict) a.set_acceptance(0, spot.acc_code("t")) -assert(a.prop_state_acc() == True) +tc.assertTrue(a.prop_state_acc()) a.set_acceptance(1, spot.acc_code("Fin(0)")) -assert(a.prop_state_acc() == spot.trival.maybe()) +tc.assertEqual(a.prop_state_acc(), spot.trival.maybe()) # Some tests for used_inf_fin_sets(), which return a pair of mark_t. (inf, fin) = a.get_acceptance().used_inf_fin_sets() -assert inf == [] -assert fin == [0] +tc.assertEqual(inf, []) +tc.assertEqual(fin, [0]) (inf, fin) = spot.acc_code("(Fin(0)|Inf(1))&Fin(2)&Inf(0)").used_inf_fin_sets() -assert inf == [0, 1] -assert fin == [0, 2] +tc.assertEqual(inf, [0, 1]) +tc.assertEqual(fin, [0, 2]) # is_rabin_like() returns (bool, [(inf, fin), ...]) (b, v) = spot.acc_cond("(Fin(0)&Inf(1))|(Fin(2)&Inf(0))").is_rabin_like() -assert b == True -assert len(v) == 2 -assert v[0].fin == [0] -assert v[0].inf == [1] -assert v[1].fin == [2] -assert v[1].inf == [0] +tc.assertTrue(b) +tc.assertEqual(len(v), 2) +tc.assertEqual(v[0].fin, [0]) +tc.assertEqual(v[0].inf, [1]) +tc.assertEqual(v[1].fin, [2]) +tc.assertEqual(v[1].inf, [0]) (b, v) = spot.acc_cond("(Fin(0)|Inf(1))&(Fin(2)|Inf(0))").is_rabin_like() -assert b == False -assert len(v) == 0 +tc.assertFalse(b) +tc.assertEqual(len(v), 0) (b, v) = spot.acc_cond("(Fin(0)|Inf(1))&(Fin(2)|Inf(0))").is_streett_like() -assert b == True -assert repr(v) == \ - '(spot.rs_pair(fin=[0], inf=[1]), spot.rs_pair(fin=[2], inf=[0]))' +tc.assertTrue(b) +tc.assertEqual(repr(v), \ + '(spot.rs_pair(fin=[0], inf=[1]), spot.rs_pair(fin=[2], inf=[0]))') v2 = (spot.rs_pair(fin=[0], inf=[1]), spot.rs_pair(fin=[2], inf=[0])) -assert v == v2 +tc.assertEqual(v, v2) acc = spot.acc_cond("generalized-Rabin 1 2") (b, v) = acc.is_generalized_rabin() -assert b == True -assert v == (2,) +tc.assertTrue(b) +tc.assertEqual(v, (2,)) (b, v) = acc.is_generalized_streett() -assert b == False -assert v == () +tc.assertFalse(b) +tc.assertEqual(v, ()) (b, v) = acc.is_streett_like() -assert b == True +tc.assertTrue(b) ve = (spot.rs_pair([0], []), spot.rs_pair([], [1]), spot.rs_pair([], [2])) -assert v == ve -assert acc.name() == "generalized-Rabin 1 2" +tc.assertEqual(v, ve) +tc.assertEqual(acc.name(), "generalized-Rabin 1 2") # At the time of writting, acc_cond does not yet recognize # "generalized-Streett", as there is no definition for that in the HOA format, @@ -74,23 +76,23 @@ assert acc.name() == "generalized-Rabin 1 2" # being a generalized-Streett. See issue #249. acc = spot.acc_cond("Inf(0)|Fin(1)|Fin(2)") (b, v) = acc.is_generalized_streett() -assert b == True -assert v == (2,) +tc.assertTrue(b) +tc.assertEqual(v, (2,)) (b, v) = acc.is_generalized_rabin() -assert b == False -assert v == () +tc.assertFalse(b) +tc.assertEqual(v, ()) # FIXME: We should have a way to disable the following output, as it is not # part of HOA v1. -assert acc.name() == "generalized-Streett 1 2" +tc.assertEqual(acc.name(), "generalized-Streett 1 2") # issue #469. This test is meaningful only if Spot is compiled with # --enable-max-accsets=64 or more. try: m = spot.mark_t([33]) - assert m.lowest() == m + tc.assertEqual(m.lowest(), m) n = spot.mark_t([33,34]) - assert n.lowest() == m + tc.assertEqual(n.lowest(), m) except RuntimeError as e: if "Too many acceptance sets used." in str(e): pass @@ -102,24 +104,24 @@ except RuntimeError as e: from gc import collect acc = spot.translate('a').acc() collect() -assert acc == spot.acc_cond('Inf(0)') +tc.assertEqual(acc, spot.acc_cond('Inf(0)')) acc = spot.translate('b').get_acceptance() collect() -assert acc == spot.acc_code('Inf(0)') +tc.assertEqual(acc, spot.acc_code('Inf(0)')) c = spot.acc_cond('Fin(0)&Fin(1)&(Inf(2)|Fin(3))') m1 = c.fin_unit() m2 = c.inf_unit() -assert m1 == [0,1] -assert m2 == [] +tc.assertEqual(m1, [0,1]) +tc.assertEqual(m2, []) c = spot.acc_cond('Inf(0)&Inf(1)&(Inf(2)|Fin(3))') m1 = c.fin_unit() m2 = c.inf_unit() -assert m1 == [] -assert m2 == [0,1] +tc.assertEqual(m1, []) +tc.assertEqual(m2, [0,1]) c = spot.acc_cond('Inf(0)&Inf(1)|(Inf(2)|Fin(3))') m1 = c.fin_unit() m2 = c.inf_unit() -assert m1 == [] -assert m2 == [] +tc.assertEqual(m1, []) +tc.assertEqual(m2, []) diff --git a/tests/python/setxor.py b/tests/python/setxor.py index 7cd1e5da1..2fe69cd99 100755 --- a/tests/python/setxor.py +++ b/tests/python/setxor.py @@ -1,6 +1,6 @@ # -*- mode: python; coding: utf-8 -*- -# Copyright (C) 2010, 2011 Laboratoire de Recherche et Développement -# de l'EPITA. +# Copyright (C) 2010, 2011, 2022 Laboratoire de Recherche et +# Développement de l'EPITA. # # This file is part of Spot, a model checking library. # @@ -19,6 +19,8 @@ import sys from buddy import * +from unittest import TestCase +tc = TestCase() bdd_init(10000, 10000) bdd_setvarnum(5) @@ -29,18 +31,18 @@ a = V[0] & -V[1] & V[2] & -V[3] b = V[0] & V[1] & V[2] & -V[3] c = -V[0] & V[1] & -V[2] & -V[3] -assert(c == bdd_setxor(a, b)) -assert(c == bdd_setxor(b, a)) -assert(a == bdd_setxor(b, c)) -assert(a == bdd_setxor(c, b)) -assert(b == bdd_setxor(a, c)) -assert(b == bdd_setxor(c, a)) +tc.assertEqual(c, bdd_setxor(a, b)) +tc.assertEqual(c, bdd_setxor(b, a)) +tc.assertEqual(a, bdd_setxor(b, c)) +tc.assertEqual(a, bdd_setxor(c, b)) +tc.assertEqual(b, bdd_setxor(a, c)) +tc.assertEqual(b, bdd_setxor(c, a)) d = V[1] & V[2] & -V[3] & V[4] e = V[0] & V[1] & -V[2] & -V[3] & V[4] -assert(e == bdd_setxor(a, d)) -assert(e == bdd_setxor(d, a)) +tc.assertEqual(e, bdd_setxor(a, d)) +tc.assertEqual(e, bdd_setxor(d, a)) # Cleanup all BDD variables before calling bdd_done(), otherwise # bdd_delref will be called after bdd_done() and this is unsafe in diff --git a/tests/python/simplacc.py b/tests/python/simplacc.py index e742d69a4..50dc2d74a 100644 --- a/tests/python/simplacc.py +++ b/tests/python/simplacc.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Copyright (C) 2020 Laboratoire de Recherche et Développement de l'Epita +# Copyright (C) 2020, 2022 Laboratoire de Recherche et Développement de l'Epita # (LRDE). # # This file is part of Spot, a model checking library. @@ -18,6 +18,8 @@ # along with this program. If not, see . import spot +from unittest import TestCase +tc = TestCase() auts = list(spot.automata(""" @@ -70,19 +72,19 @@ explicit-labels trans-acc deterministic --BODY-- State: 0 [0&!1] 0 {2 3} res = [] for a in auts: b = spot.simplify_acceptance(a) - assert b.equivalent_to(a) + tc.assertTrue(b.equivalent_to(a)) res.append(str(b.get_acceptance())) c = spot.simplify_acceptance(b) - assert b.get_acceptance() == c.get_acceptance() + tc.assertEqual(b.get_acceptance(), c.get_acceptance()) a.set_acceptance(a.num_sets(), a.get_acceptance().complement()) b = spot.simplify_acceptance(a) - assert b.equivalent_to(a) + tc.assertTrue(b.equivalent_to(a)) res.append(str(b.get_acceptance())) c = spot.simplify_acceptance(b) - assert b.get_acceptance() == c.get_acceptance() + tc.assertEqual(b.get_acceptance(), c.get_acceptance()) -assert res == [ +tc.assertEqual(res, [ 'Inf(0)', 'Fin(0)', 'Inf(1) & Fin(0)', @@ -101,4 +103,4 @@ assert res == [ '(Inf(0) | Fin(2)) & Inf(1)', '(Fin(2) & (Inf(1) | Fin(0))) | (Inf(0)&Inf(2))', '(Inf(2) | (Fin(1) & Inf(0))) & (Fin(0)|Fin(2))', - ] + ]) diff --git a/tests/python/simstate.py b/tests/python/simstate.py index 6c2ca8bc3..b0b62267d 100644 --- a/tests/python/simstate.py +++ b/tests/python/simstate.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Copyright (C) 2015, 2017-2018, 2020-2021 Laboratoire de Recherche +# Copyright (C) 2015, 2017-2018, 2020-2022 Laboratoire de Recherche # et Développement de l'Epita # # This file is part of Spot, a model checking library. @@ -19,6 +19,8 @@ import spot from sys import exit +from unittest import TestCase +tc = TestCase() # CPython use reference counting, so that automata are destructed # when we expect them to be. However other implementations like @@ -48,7 +50,7 @@ State: 1 """) aut2 = spot.simulation(aut) -assert aut2.to_str() == """HOA: v1 +tc.assertEqual(aut2.to_str(), """HOA: v1 States: 1 Start: 0 AP: 2 "a" "b" @@ -59,10 +61,10 @@ properties: deterministic --BODY-- State: 0 {0} [t] 0 ---END--""" +--END--""") aut2.copy_state_names_from(aut) -assert aut2.to_str() == """HOA: v1 +tc.assertEqual(aut2.to_str(), """HOA: v1 States: 1 Start: 0 AP: 2 "a" "b" @@ -73,7 +75,7 @@ properties: deterministic --BODY-- State: 0 "[0,1]" {0} [t] 0 ---END--""" +--END--""") del aut del aut2 @@ -82,7 +84,7 @@ gcollect() aut = spot.translate('GF((p0 -> Gp0) R p1)') daut = spot.tgba_determinize(aut, True) -assert daut.to_str() == """HOA: v1 +tc.assertEqual(daut.to_str(), """HOA: v1 States: 3 Start: 0 AP: 2 "p1" "p0" @@ -106,7 +108,7 @@ State: 2 "{₀[0]₀}{₁[1]₁}" [!0&1] 2 [0&!1] 0 {0} [0&1] 1 {2} ---END--""" +--END--""") del aut del daut @@ -129,7 +131,7 @@ State: 1 """) daut = spot.tgba_determinize(aut, True) -assert daut.to_str() == """HOA: v1 +tc.assertEqual(daut.to_str(), """HOA: v1 States: 12 Start: 0 AP: 2 "a" "b" @@ -185,18 +187,18 @@ State: 11 "{₀[1#1]{₁[0#0,0#1]{₂[1#0]₂}₁}₀}" [!0&1] 2 {0} [0&!1] 6 {0} [0&1] 9 {0} ---END--""" +--END--""") a = spot.translate('!Gp0 xor FG((p0 W Gp1) M p1)') a = spot.degeneralize_tba(a) -assert a.num_states() == 8 +tc.assertEqual(a.num_states(), 8) b = spot.simulation(a) -assert b.num_states() == 3 +tc.assertEqual(b.num_states(), 3) b.set_init_state(1) b.purge_unreachable_states() b.copy_state_names_from(a) -assert b.to_str() == """HOA: v1 +tc.assertEqual(b.to_str(), """HOA: v1 States: 1 Start: 0 AP: 2 "p0" "p1" @@ -208,7 +210,7 @@ properties: deterministic stutter-invariant State: 0 "[1,7]" [1] 0 [!1] 0 {0} ---END--""" +--END--""") aut = spot.automaton('''HOA: v1 States: 12 @@ -267,7 +269,7 @@ State: 11 [0&!1] 6 {0} [0&1] 9 {0} --END--''') -assert spot.reduce_iterated(aut).to_str() == '''HOA: v1 +tc.assertEqual(spot.reduce_iterated(aut).to_str(), '''HOA: v1 States: 9 Start: 0 AP: 2 "a" "b" @@ -308,7 +310,7 @@ State: 8 [0&!1] 4 {0} [!0&1] 6 [0&1] 7 ---END--''' +--END--''') aut = spot.automaton('''HOA: v1 States: 6 @@ -332,7 +334,7 @@ State: 4 State: 5 [0] 5 --END--''') -assert spot.reduce_iterated(aut).to_str() == '''HOA: v1 +tc.assertEqual(spot.reduce_iterated(aut).to_str(), '''HOA: v1 States: 3 Start: 0 AP: 2 "a" "b" @@ -347,7 +349,7 @@ State: 1 [0] 2 State: 2 [1] 2 ---END--''' +--END--''') aut = spot.automaton('''HOA: v1 States: 5 @@ -374,7 +376,7 @@ State: 4 [0&1&!2&3] 4 {0} --END--''') -assert spot.reduce_direct_cosim(aut).to_str() == '''HOA: v1 +tc.assertEqual(spot.reduce_direct_cosim(aut).to_str(), '''HOA: v1 States: 5 Start: 0 AP: 4 "p0" "p2" "p3" "p1" @@ -395,7 +397,7 @@ State: 3 [0&!1&2&3] 3 {1} State: 4 [0&!1&2&3] 4 {0} ---END--''' +--END--''') aut = spot.automaton('''HOA: v1 States: 2 @@ -410,7 +412,7 @@ State: 0 State: 1 [0] 0 --END--''') -assert spot.reduce_direct_sim(aut).to_str() == '''HOA: v1 +tc.assertEqual(spot.reduce_direct_sim(aut).to_str(), '''HOA: v1 States: 1 Start: 0 AP: 2 "a" "b" @@ -418,7 +420,7 @@ Acceptance: 2 Fin(0) & Fin(1) properties: trans-labels explicit-labels state-acc deterministic --BODY-- State: 0 ---END--''' +--END--''') aut = spot.automaton('''HOA: v1 name: "(p1 U p2) U p3" @@ -445,7 +447,7 @@ State: 3 [1] 1 [0&!1] 3 --END--''') -assert spot.reduce_direct_cosim_sba(aut).to_str() == '''HOA: v1 +tc.assertEqual(spot.reduce_direct_cosim_sba(aut).to_str(), '''HOA: v1 States: 4 Start: 0 AP: 3 "p2" "p3" "p1" @@ -468,7 +470,7 @@ State: 2 State: 3 [0] 1 [!0&2] 3 ---END--''' +--END--''') aut = spot.automaton('''HOA: v1 States: 4 @@ -488,7 +490,7 @@ State: 2 State: 3 {0} [1] 3 --END--''') -assert spot.reduce_direct_cosim(aut).to_str() == '''HOA: v1 +tc.assertEqual(spot.reduce_direct_cosim(aut).to_str(), '''HOA: v1 States: 3 Start: 0 AP: 2 "a" "b" @@ -502,9 +504,9 @@ State: 1 [1] 2 State: 2 {0} [1] 2 ---END--''' +--END--''') -assert spot.reduce_direct_sim_sba(aut).to_str() == '''HOA: v1 +tc.assertEqual(spot.reduce_direct_sim_sba(aut).to_str(), '''HOA: v1 States: 2 Start: 0 AP: 2 "a" "b" @@ -516,7 +518,7 @@ State: 0 [0] 1 State: 1 {0} [1] 1 ---END--''' +--END--''') aut = spot.automaton('''HOA: v1 States: 3 @@ -532,7 +534,7 @@ State: 1 State: 2 {0} [0] 2 --END--''') -assert spot.reduce_iterated_sba(aut).to_str() == '''HOA: v1 +tc.assertEqual(spot.reduce_iterated_sba(aut).to_str(), '''HOA: v1 States: 1 Start: 0 AP: 1 "a" @@ -542,7 +544,7 @@ properties: deterministic --BODY-- State: 0 {0} [0] 0 ---END--''' +--END--''') aut = spot.automaton('''HOA: v1 States: 30 @@ -630,7 +632,7 @@ State: 28 State: 29 [0&!1&!2&!3] 29 --END--''') -assert spot.reduce_iterated(a).to_str() == '''HOA: v1 +tc.assertEqual(spot.reduce_iterated(a).to_str(), '''HOA: v1 States: 8 Start: 0 AP: 2 "p0" "p1" @@ -669,7 +671,7 @@ State: 7 [!1] 1 {0} [0&1] 5 [1] 7 ---END--''' +--END--''') # issue #452 @@ -707,4 +709,4 @@ State: 8 [@p] 3 {0 1} --END--""") aut = spot.simulation(aut) -assert aut.num_states() == 1 +tc.assertEqual(aut.num_states(), 1) diff --git a/tests/python/sonf.py b/tests/python/sonf.py index 558f90c63..40af758b0 100644 --- a/tests/python/sonf.py +++ b/tests/python/sonf.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Copyright (C) 2020 Laboratoire de Recherche et Développement de l'Epita +# Copyright (C) 2020, 2022 Laboratoire de Recherche et Développement de l'Epita # (LRDE). # # This file is part of Spot, a model checking library. @@ -18,6 +18,8 @@ # along with this program. If not, see . import spot +from unittest import TestCase +tc = TestCase() formulas = """\ {x[*]}[]-> F({y[*]}<>-> GFz) @@ -38,4 +40,4 @@ for f1 in formulas.splitlines(): rm.add_ap(ap) a2 = rm.strip(a2) - assert(spot.are_equivalent(a1, a2)) + tc.assertTrue(spot.are_equivalent(a1, a2)) diff --git a/tests/python/split.py b/tests/python/split.py index adab5a931..b916f494f 100644 --- a/tests/python/split.py +++ b/tests/python/split.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Copyright (C) 2018-2021 Laboratoire de Recherche et +# Copyright (C) 2018-2022 Laboratoire de Recherche et # Développement de l'Epita # # This file is part of Spot, a model checking library. @@ -19,6 +19,8 @@ import spot import buddy +from unittest import TestCase +tc = TestCase() # CPython use reference counting, so that automata are destructed # when we expect them to be. However other implementations like @@ -51,16 +53,17 @@ def do_split(f, out_list): return aut, s aut, s = do_split('(FG !a) <-> (GF b)', ['b']) -assert equiv(aut, spot.unsplit_2step(s)) +tc.assertTrue(equiv(aut, spot.unsplit_2step(s))) del aut del s gcollect() aut, s = do_split('GFa && GFb', ['b']) -assert equiv(aut, spot.unsplit_2step(s)) -# FIXME see below -# assert str_diff("""HOA: v1 +tc.assertTrue(equiv(aut, spot.unsplit_2step(s))) +# FIXME s.to_str() is NOT the same on Debian stable and on Debian unstable +# we should investigate this. See Issue #502. +# tc.assertEqual("""HOA: v1 # States: 3 # Start: 0 # AP: 2 "a" "b" @@ -86,10 +89,11 @@ del s gcollect() aut, s = do_split('! ((G (req -> (F ack))) && (G (go -> (F grant))))', ['ack']) -assert equiv(aut, spot.unsplit_2step(s)) +tc.assertTrue(equiv(aut, spot.unsplit_2step(s))) + # FIXME s.to_str() is NOT the same on Debian stable and on Debian unstable -# we should investigate this -# assert s.to_str() == """HOA: v1 +# we should investigate this. See Issue #502. +# tc.assertEqual(s.to_str(), """HOA: v1 # States: 9 # Start: 0 # AP: 4 "ack" "req" "go" "grant" @@ -122,7 +126,7 @@ assert equiv(aut, spot.unsplit_2step(s)) # [!0] 1 # State: 8 {0} # [!3] 2 -# --END--""" +# --END--""") del aut del s @@ -131,4 +135,4 @@ gcollect() aut, s = do_split('((G (((! g_0) || (! g_1)) && ((r_0 && (X r_1)) -> (F (g_0 \ && g_1))))) && (G (r_0 -> F g_0))) && (G (r_1 -> F g_1))', ['g_0', 'g_1']) -assert equiv(aut, spot.unsplit_2step(s)) +tc.assertTrue(equiv(aut, spot.unsplit_2step(s))) diff --git a/tests/python/streett_totgba.py b/tests/python/streett_totgba.py index 1c0bfc13e..8a18defbc 100644 --- a/tests/python/streett_totgba.py +++ b/tests/python/streett_totgba.py @@ -1,7 +1,7 @@ #!/usr/bin/python3 # -*- mode: python; coding: utf-8 -*- -# Copyright (C) 2017, 2018, 2021 Laboratoire de Recherche et Développement de -# l'EPITA. +# Copyright (C) 2017-2018, 2021-2022 Laboratoire de Recherche et +# Développement de l'EPITA. # # This file is part of Spot, a model checking library. # @@ -22,7 +22,8 @@ import spot import os import shutil import sys - +from unittest import TestCase +tc = TestCase() def tgba(a): if not a.is_existential(): @@ -33,11 +34,11 @@ def tgba(a): def test_aut(aut): stgba = tgba(aut) - assert stgba.equivalent_to(aut) + tc.assertTrue(stgba.equivalent_to(aut)) os.environ["SPOT_STREETT_CONV_MIN"] = '1' sftgba = tgba(aut) del os.environ["SPOT_STREETT_CONV_MIN"] - assert stgba.equivalent_to(sftgba) + tc.assertTrue(stgba.equivalent_to(sftgba)) slike = spot.simplify_acceptance(aut) @@ -45,8 +46,7 @@ def test_aut(aut): os.environ["SPOT_STREETT_CONV_MIN"] = "1" slftgba = tgba(slike) del os.environ["SPOT_STREETT_CONV_MIN"] - assert sltgba.equivalent_to(slftgba) - + tc.assertTrue(sltgba.equivalent_to(slftgba)) if shutil.which('ltl2dstar') is None: sys.exit(77) diff --git a/tests/python/streett_totgba2.py b/tests/python/streett_totgba2.py index 852eff0af..5ff97a369 100644 --- a/tests/python/streett_totgba2.py +++ b/tests/python/streett_totgba2.py @@ -1,7 +1,7 @@ #!/usr/bin/python3 # -*- mode: python; coding: utf-8 -*- -# Copyright (C) 2018 Laboratoire de Recherche et Développement de -# l'EPITA. +# Copyright (C) 2018, 2022 Laboratoire de Recherche et Développement +# de l'EPITA. # # This file is part of Spot, a model checking library. # @@ -19,6 +19,8 @@ # along with this program. If not, see . import spot +from unittest import TestCase +tc = TestCase() # Issue 316 a = spot.automaton(""" @@ -60,11 +62,11 @@ State: 7 {1 3 4} """) tgba = spot.streett_to_generalized_buchi(a) -assert tgba.acc().is_generalized_buchi() +tc.assertTrue(tgba.acc().is_generalized_buchi()) ba = spot.simplify_acceptance(a) -assert ba.acc().is_buchi() +tc.assertTrue(ba.acc().is_buchi()) nba = spot.dualize(ba.postprocess('generic', 'deterministic')) ntgba = spot.dualize(tgba.postprocess('generic', 'deterministic')) -assert not ba.intersects(ntgba) -assert not tgba.intersects(nba) +tc.assertFalse(ba.intersects(ntgba)) +tc.assertFalse(tgba.intersects(nba)) diff --git a/tests/python/stutter.py b/tests/python/stutter.py index dafb03b7e..05c28fda9 100644 --- a/tests/python/stutter.py +++ b/tests/python/stutter.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Copyright (C) 2019-2021 Laboratoire de Recherche et Développement de +# Copyright (C) 2019-2022 Laboratoire de Recherche et Développement de # l'Epita (LRDE). # # This file is part of Spot, a model checking library. @@ -23,6 +23,8 @@ import spot +from unittest import TestCase +tc = TestCase() def explain_stut(f): @@ -45,20 +47,20 @@ def explain_stut(f): # Test from issue #388 w1, w2 = explain_stut('{(a:b) | (a;b)}|->Gc') -assert str(w1) == 'a & !b & !c; cycle{!a & b & !c}' -assert str(w2) == 'a & !b & !c; a & !b & !c; cycle{!a & b & !c}' +tc.assertEqual(str(w1), 'a & !b & !c; cycle{!a & b & !c}') +tc.assertEqual(str(w2), 'a & !b & !c; a & !b & !c; cycle{!a & b & !c}') # Test from issue #401 w1, w2 = explain_stut('G({x} |-> ({x[+]} <>-> ({Y1[+]} <>=> Y2)))') -assert str(w1) == 'cycle{!Y1 & !Y2 & x; Y1 & Y2 & x}' -assert str(w2) == 'cycle{!Y1 & !Y2 & x; Y1 & Y2 & x; Y1 & Y2 & x}' +tc.assertEqual(str(w1), 'cycle{!Y1 & !Y2 & x; Y1 & Y2 & x}') +tc.assertEqual(str(w2), 'cycle{!Y1 & !Y2 & x; Y1 & Y2 & x; Y1 & Y2 & x}') # Related to issue #401 as well. sl() and sl2() should upgrade # the t acceptance condition into inf(0). pos = spot.translate('Xa & XXb') w = pos.accepting_word().as_automaton() -assert w.acc().is_t() +tc.assertTrue(w.acc().is_t()) a = spot.sl2(w) -assert a.acc().is_buchi() +tc.assertTrue(a.acc().is_buchi()) a = spot.sl(w) -assert a.acc().is_buchi() +tc.assertTrue(a.acc().is_buchi()) diff --git a/tests/python/sum.py b/tests/python/sum.py index 7e2e74220..1f7c6e0a1 100644 --- a/tests/python/sum.py +++ b/tests/python/sum.py @@ -1,6 +1,6 @@ # -*- mode: python; coding: utf-8 -*- -# Copyright (C) 2017-2019 Laboratoire de Recherche et Développement -# de l'Epita +# Copyright (C) 2017-2019, 2022 Laboratoire de Recherche et +# Développement de l'Epita # # This file is part of Spot, a model checking library. # @@ -20,6 +20,8 @@ import spot import sys import itertools +from unittest import TestCase +tc = TestCase() # make sure that we are not allowed to build the sum of two automata with # different dictionaries. @@ -65,8 +67,8 @@ for p in zip(phi1, phi2): p0orp1 = spot.formula.Or(p) a1ora2 = spot.remove_alternation(spot.sum(a1, a2), True) - assert p0orp1.equivalent_to(a1ora2) + tc.assertTrue(p0orp1.equivalent_to(a1ora2)) p0andp1 = spot.formula.And(p) a1anda2 = spot.remove_alternation(spot.sum_and(a1, a2), True) - assert p0andp1.equivalent_to(a1anda2) + tc.assertTrue(p0andp1.equivalent_to(a1anda2)) diff --git a/tests/python/synthesis.py b/tests/python/synthesis.py index 59022624c..e1a88650a 100644 --- a/tests/python/synthesis.py +++ b/tests/python/synthesis.py @@ -18,6 +18,8 @@ # along with this program. If not, see . import spot +from unittest import TestCase +tc = TestCase() # A shared variable caused the 2nd call to ltl_to_game to give an incorrect # result. @@ -25,11 +27,11 @@ for i in range(0, 2): gi = spot.synthesis_info() gi.s = spot.synthesis_info.algo_LAR game = spot.ltl_to_game("(Ga) <-> (Fb)", ["b"], gi) - assert not spot.solve_game(game) + tc.assertFalse(spot.solve_game(game)) # A game can have only inputs game = spot.ltl_to_game("GFa", []) -assert(game.to_str() == """HOA: v1 +tc.assertEqual(game.to_str(), """HOA: v1 States: 3 Start: 0 AP: 1 "a" diff --git a/tests/python/toparity.py b/tests/python/toparity.py index df226ebe4..37e111f9b 100644 --- a/tests/python/toparity.py +++ b/tests/python/toparity.py @@ -1,6 +1,6 @@ #!/usr/bin/python3 # -*- mode: python; coding: utf-8 -*- -# Copyright (C) 2018-2021 Laboratoire de Recherche et Développement de +# Copyright (C) 2018-2022 Laboratoire de Recherche et Développement de # l'EPITA. # # This file is part of Spot, a model checking library. @@ -21,6 +21,8 @@ import spot from itertools import zip_longest from buddy import bddfalse +from unittest import TestCase +tc = TestCase() # Tests for the new version of to_parity @@ -114,17 +116,16 @@ def test(aut, expected_num_states=[], full=True): if opt is not None and opt.parity_prefix is False: # Reduce the number of colors to help are_equivalent spot.reduce_parity_here(p1) - assert spot.are_equivalent(aut, p1) + tc.assertTrue(spot.are_equivalent(aut, p1)) if expected_num is not None: - # print(p1.num_states(), expected_num) - assert p1.num_states() == expected_num + tc.assertEqual(p1.num_states(), expected_num) if full and opt is not None: # Make sure passing opt is the same as setting # each argument individually p2 = spot.to_parity(aut, opt) - assert p2.num_states() == p1st - assert p2.num_edges() == p1ed - assert p2.num_sets() == p1se + tc.assertEqual(p2.num_states(), p1st) + tc.assertEqual(p2.num_edges(), p1ed) + tc.assertEqual(p2.num_sets(), p1se) test(spot.automaton("""HOA: v1 name: "(FGp0 & ((XFp0 & F!p1) | F(Gp1 & XG!p0))) | G(F!p0 & (XFp0 | F!p1) & @@ -351,7 +352,7 @@ State: 0 [!0&!1] 0 --END--""") p = spot.to_parity_old(a, True) -assert spot.are_equivalent(a, p) +tc.assertTrue(spot.are_equivalent(a, p)) test(a) a = spot.automaton(""" @@ -363,8 +364,8 @@ explicit-labels trans-acc --BODY-- State: 0 [0&1] 2 {4 5} [0&1] 4 {0 4} 4 [!0&!1] 1 {2 4} State: 5 [!0&1] 4 --END-- """) p = spot.to_parity_old(a, True) -assert p.num_states() == 22 -assert spot.are_equivalent(a, p) +tc.assertEqual(p.num_states(), 22) +tc.assertTrue(spot.are_equivalent(a, p)) test(a, [8, 6, 6, 6, 6, 6, 6, 6]) # Force a few edges to false, to make sure to_parity() is OK with that. @@ -377,22 +378,22 @@ for e in a.out(3): e.cond = bddfalse break p = spot.to_parity_old(a, True) -assert p.num_states() == 22 -assert spot.are_equivalent(a, p) +tc.assertEqual(p.num_states(), 22) +tc.assertTrue(spot.are_equivalent(a, p)) test(a, [7, 6, 6, 6, 6, 6, 6, 6]) for f in spot.randltl(4, 400): d = spot.translate(f, "det", "G") p = spot.to_parity_old(d, True) - assert spot.are_equivalent(p, d) + tc.assertTrue(spot.are_equivalent(p, d)) for f in spot.randltl(5, 2000): n = spot.translate(f) p = spot.to_parity_old(n, True) - assert spot.are_equivalent(n, p) + tc.assertTrue(spot.are_equivalent(n, p)) # Issue #390. a = spot.translate('!(GFa -> (GFb & GF(!b & !Xb)))', 'gen', 'det') b = spot.to_parity_old(a, True) -assert a.equivalent_to(b) +tc.assertTrue(a.equivalent_to(b)) test(a, [7, 7, 3, 7, 7, 7, 3, 3]) diff --git a/tests/python/toweak.py b/tests/python/toweak.py index b2d908037..23dcf66fa 100644 --- a/tests/python/toweak.py +++ b/tests/python/toweak.py @@ -1,6 +1,6 @@ # -*- mode: python; coding: utf-8 -*- -# Copyright (C) 2017, 2018, 2020 Laboratoire de Recherche et Développement -# de l'Epita +# Copyright (C) 2017, 2018, 2020, 2022 Laboratoire de Recherche et +# Développement de l'Epita # # This file is part of Spot, a model checking library. # @@ -18,6 +18,8 @@ # along with this program. If not, see . import spot +from unittest import TestCase +tc = TestCase() phi1 = """GFb X(!b | GF!a) @@ -33,7 +35,7 @@ b | (a & XF(b R a)) | (!a & XG(!b U !a))""" def test_phi(phi): a = spot.translate(phi, 'GeneralizedBuchi', 'SBAcc') res = spot.to_weak_alternating(spot.dualize(a)) - assert res.equivalent_to(spot.formula.Not(spot.formula(phi))) + tc.assertTrue(res.equivalent_to(spot.formula.Not(spot.formula(phi)))) for p in phi1.split('\n'): @@ -83,4 +85,4 @@ State: 6 --END-- """) a2 = spot.to_weak_alternating(a2) -assert a2.equivalent_to(phi2) +tc.assertTrue(a2.equivalent_to(phi2)) diff --git a/tests/python/tra2tba.py b/tests/python/tra2tba.py index b303c010b..354ced630 100644 --- a/tests/python/tra2tba.py +++ b/tests/python/tra2tba.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Copyright (C) 2016-2018, 2020-2021 Laboratoire de Recherche +# Copyright (C) 2016-2018, 2020-2022 Laboratoire de Recherche # et Développement de l'Epita # # This file is part of Spot, a model checking library. @@ -18,6 +18,8 @@ # along with this program. If not, see . import spot +from unittest import TestCase +tc = TestCase() # CPython use reference counting, so that automata are destructed # when we expect them to be. However other implementations like @@ -57,7 +59,7 @@ State: 1 --END--""" res = spot.remove_fin(aut) -assert(res.to_str('hoa') == exp) +tc.assertEqual(res.to_str('hoa'), exp) # Test 2. aut = spot.automaton(""" @@ -97,7 +99,7 @@ State: 2 --END--""" res = spot.remove_fin(aut) -assert(res.to_str('hoa') == exp) +tc.assertEqual(res.to_str('hoa'), exp) # Test 3. aut = spot.automaton(""" @@ -128,7 +130,7 @@ State: 0 --END--""" res = spot.remove_fin(aut) -assert(res.to_str('hoa') == exp) +tc.assertEqual(res.to_str('hoa'), exp) # Test 4. aut = spot.automaton(""" @@ -168,7 +170,7 @@ State: 2 {0} --END--""" res = spot.remove_fin(aut) -assert(res.to_str('hoa') == exp) +tc.assertEqual(res.to_str('hoa'), exp) # Test 5. aut = spot.automaton(""" @@ -214,7 +216,7 @@ State: 3 {0} --END--""" res = spot.remove_fin(aut) -assert(res.to_str('hoa') == exp) +tc.assertEqual(res.to_str('hoa'), exp) # Test 6. aut = spot.automaton(""" @@ -257,7 +259,7 @@ State: 2 {0} --END--""" res = spot.remove_fin(aut) -assert(res.to_str('hoa') == exp) +tc.assertEqual(res.to_str('hoa'), exp) # Test 7. aut = spot.automaton(""" @@ -292,7 +294,7 @@ State: 1 {0} --END--""" res = spot.remove_fin(aut) -assert(res.to_str('hoa') == exp) +tc.assertEqual(res.to_str('hoa'), exp) # Test 8. aut = spot.automaton(""" @@ -372,9 +374,9 @@ State: 7 res = spot.remove_fin(aut) if is_cpython: - assert(res.to_str('hoa') == exp) + tc.assertEqual(res.to_str('hoa'), exp) else: - assert(res.equivalent_to(spot.automaton(exp))) + tc.assertTrue(res.equivalent_to(spot.automaton(exp))) # Test 9. aut = spot.automaton(""" @@ -411,9 +413,9 @@ State: 1 res = spot.remove_fin(aut) if is_cpython: - assert(res.to_str('hoa') == exp) + tc.assertEqual(res.to_str('hoa'), exp) else: - assert(res.equivalent_to(spot.automaton(exp))) + tc.assertTrue(res.equivalent_to(spot.automaton(exp))) # Test 10. aut = spot.automaton(""" @@ -453,9 +455,9 @@ State: 2 {0} res = spot.remove_fin(aut) if is_cpython: - assert(res.to_str('hoa') == exp) + tc.assertEqual(res.to_str('hoa'), exp) else: - assert(res.equivalent_to(spot.automaton(exp))) + tc.assertTrue(res.equivalent_to(spot.automaton(exp))) # Test 11. aut = spot.automaton(""" @@ -493,9 +495,9 @@ State: 1 res = spot.remove_fin(aut) if is_cpython: - assert(res.to_str('hoa') == exp) + tc.assertEqual(res.to_str('hoa'), exp) else: - assert(res.equivalent_to(spot.automaton(exp))) + tc.assertTrue(res.equivalent_to(spot.automaton(exp))) # Different order for rabin_to_buchi_if_realizable() due to merge_edges() not # being called. This is on purpose: the edge order should match exactly the @@ -518,9 +520,9 @@ State: 1 --END--""" res = spot.rabin_to_buchi_if_realizable(aut) if is_cpython: - assert(res.to_str('hoa') == exp2) + tc.assertEqual(res.to_str('hoa'), exp2) else: - assert(res.equivalent_to(spot.automaton(exp2))) + tc.assertTrue(res.equivalent_to(spot.automaton(exp2))) # Test 12. aut = spot.automaton(""" @@ -565,9 +567,9 @@ State: 3 {0} res = spot.remove_fin(aut) if is_cpython: - assert(res.to_str('hoa') == exp) + tc.assertEqual(res.to_str('hoa'), exp) else: - assert(res.equivalent_to(spot.automaton(exp))) + tc.assertTrue(res.equivalent_to(spot.automaton(exp))) # Test 13. aut = spot.automaton(""" @@ -615,9 +617,9 @@ State: 1 res = spot.remove_fin(aut) if is_cpython: - assert(res.to_str('hoa') == exp) + tc.assertEqual(res.to_str('hoa'), exp) else: - assert(res.equivalent_to(spot.automaton(exp))) + tc.assertTrue(res.equivalent_to(spot.automaton(exp))) # rabin_to_buchi_if_realizable() does not call merge_edges() on purpose: the # edge order should match exactly the original automaton. @@ -644,9 +646,9 @@ State: 1 res = spot.rabin_to_buchi_if_realizable(aut) if is_cpython: - assert(res.to_str('hoa') == exp2) + tc.assertEqual(res.to_str('hoa'), exp2) else: - assert(res.equivalent_to(spot.automaton(exp2))) + tc.assertTrue(res.equivalent_to(spot.automaton(exp2))) # Test 14. aut = spot.automaton(""" @@ -681,7 +683,7 @@ State: 1 res = spot.remove_fin(aut) if is_cpython: - assert(res.to_str('hoa') == exp) + tc.assertEqual(res.to_str('hoa'), exp) else: - assert(res.equivalent_to(spot.automaton(exp))) -assert spot.rabin_to_buchi_if_realizable(aut) is None + tc.assertTrue(res.equivalent_to(spot.automaton(exp))) +tc.assertIsNone(spot.rabin_to_buchi_if_realizable(aut)) diff --git a/tests/python/trival.py b/tests/python/trival.py index 8fcf6a1fa..ea844e29c 100644 --- a/tests/python/trival.py +++ b/tests/python/trival.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Copyright (C) 2016, 2018 Laboratoire de Recherche et Développement +# Copyright (C) 2016, 2018, 2022 Laboratoire de Recherche et Développement # de l'Epita # # This file is part of Spot, a model checking library. @@ -18,30 +18,32 @@ # along with this program. If not, see . import spot +from unittest import TestCase +tc = TestCase() v1 = spot.trival() v2 = spot.trival(False) v3 = spot.trival(True) v4 = spot.trival_maybe() -assert v1 != v2 -assert v1 != v3 -assert v2 != v3 -assert v2 == spot.trival(spot.trival.no_value) -assert v2 != spot.trival(spot.trival.yes_value) -assert v4 != v2 -assert v4 != v3 -assert v2 == False -assert True == v3 -assert v2 != True -assert False != v3 -assert v4 == spot.trival_maybe() -assert v4 == spot.trival(spot.trival.maybe_value) -assert v3 -assert -v2 -assert not -v1 -assert not v1 -assert not -v3 +tc.assertNotEqual(v1, v2) +tc.assertNotEqual(v1, v3) +tc.assertNotEqual(v2, v3) +tc.assertEqual(v2, spot.trival(spot.trival.no_value)) +tc.assertNotEqual(v2, spot.trival(spot.trival.yes_value)) +tc.assertNotEqual(v4, v2) +tc.assertNotEqual(v4, v3) +tc.assertEqual(v2, False) +tc.assertEqual(True, v3) +tc.assertNotEqual(v2, True) +tc.assertNotEqual(False, v3) +tc.assertEqual(v4, spot.trival_maybe()) +tc.assertEqual(v4, spot.trival(spot.trival.maybe_value)) +tc.assertTrue(v3) +tc.assertTrue(-v2) +tc.assertFalse(-v1) +tc.assertFalse(v1) +tc.assertFalse(-v3) for u in (v1, v2, v3): for v in (v1, v2, v3): - assert (u & v) == -(-u | -v) + tc.assertEqual((u & v), -(-u | -v)) diff --git a/tests/python/twagraph.py b/tests/python/twagraph.py index b8834b211..1ebcb8ac5 100644 --- a/tests/python/twagraph.py +++ b/tests/python/twagraph.py @@ -1,6 +1,6 @@ # -*- mode: python; coding: utf-8 -*- -# Copyright (C) 2017, 2021 Laboratoire de Recherche et Développement de l'Epita -# (LRDE). +# Copyright (C) 2017, 2021-2022 Laboratoire de Recherche et +# Développement de l'Epita (LRDE). # # This file is part of Spot, a model checking library. # @@ -22,6 +22,8 @@ import spot from buddy import bddtrue, bddfalse +from unittest import TestCase +tc = TestCase() aut = spot.make_twa_graph(spot.make_bdd_dict()) @@ -29,98 +31,98 @@ try: print(aut.to_str()) exit(2) except RuntimeError as e: - assert "no state" in str(e) + tc.assertIn("no state", str(e)) try: aut.set_init_state(2) except ValueError as e: - assert "nonexisting" in str(e) + tc.assertIn("nonexisting", str(e)) try: aut.set_univ_init_state([2, 1]) except ValueError as e: - assert "nonexisting" in str(e) + tc.assertIn("nonexisting", str(e)) aut.new_states(3) aut.set_init_state(2) -assert aut.get_init_state_number() == 2 +tc.assertEqual(aut.get_init_state_number(), 2) aut.set_univ_init_state([2, 1]) -assert [2, 1] == list(aut.univ_dests(aut.get_init_state_number())) +tc.assertEqual([2, 1], list(aut.univ_dests(aut.get_init_state_number()))) try: aut.get_init_state() except RuntimeError as e: s = str(e) - assert "abstract interface" in s and "alternating automata" in s + tc.assertIn("abstract interface" in s and "alternating automata", s) cpy = spot.make_twa_graph(aut, spot.twa_prop_set.all()) -assert aut.to_str() == cpy.to_str() +tc.assertEqual(aut.to_str(), cpy.to_str()) all = aut.set_buchi() -assert aut.to_str() != cpy.to_str() +tc.assertNotEqual(aut.to_str(), cpy.to_str()) cpy = spot.make_twa_graph(aut, spot.twa_prop_set.all()) aut.new_acc_edge(0, 1, bddtrue, True) -assert aut.num_edges() == 1 + cpy.num_edges() +tc.assertEqual(aut.num_edges(), 1 + cpy.num_edges()) aut.prop_universal(True) aut.set_name("some name") cpy = spot.make_twa_graph(aut, spot.twa_prop_set(False, False, False, False, False, False)) -assert cpy.prop_universal() != aut.prop_universal() -assert cpy.prop_universal() == spot.trival.maybe() -assert cpy.get_name() == None +tc.assertNotEqual(cpy.prop_universal(), aut.prop_universal()) +tc.assertEqual(cpy.prop_universal(), spot.trival.maybe()) +tc.assertEqual(cpy.get_name(), None) cpy = spot.make_twa_graph(aut, spot.twa_prop_set(False, False, False, False, False, False), True) -assert cpy.get_name() == "some name" +tc.assertEqual(cpy.get_name(), "some name") from copy import copy cpy = copy(aut) -assert aut.to_str() == cpy.to_str() +tc.assertEqual(aut.to_str(), cpy.to_str()) cpy.set_init_state(1) -assert [2, 1] == list(aut.univ_dests(aut.get_init_state_number())) -assert cpy.get_init_state_number() == 1 -assert cpy.get_name() == "some name" +tc.assertEqual([2, 1], list(aut.univ_dests(aut.get_init_state_number()))) +tc.assertEqual(cpy.get_init_state_number(), 1) +tc.assertEqual(cpy.get_name(), "some name") try: s = aut.state_acc_sets(0) except RuntimeError as e: - assert "state-based acceptance" in str(e) + tc.assertIn("state-based acceptance", str(e)) try: s = aut.state_is_accepting(0) except RuntimeError as e: - assert "state-based acceptance" in str(e) + tc.assertIn("state-based acceptance", str(e)) aut.prop_state_acc(True) -assert aut.state_acc_sets(0) == all -assert aut.state_is_accepting(0) == True +tc.assertEqual(aut.state_acc_sets(0), all) +tc.assertEqual(aut.state_is_accepting(0), True) aut.set_init_state(0) aut.purge_unreachable_states() i = aut.get_init_state() -assert aut.state_is_accepting(i) == True +tc.assertEqual(aut.state_is_accepting(i), True) it = aut.succ_iter(i) it.first() -assert aut.edge_number(it) == 1 -assert aut.state_number(it.dst()) == 1 -assert aut.edge_storage(it).src == 0 -assert aut.edge_storage(1).src == 0 -assert aut.edge_data(it).cond == bddtrue -assert aut.edge_data(1).cond == bddtrue +tc.assertEqual(aut.edge_number(it), 1) +tc.assertEqual(aut.state_number(it.dst()), 1) +tc.assertEqual(aut.edge_storage(it).src, 0) +tc.assertEqual(aut.edge_storage(1).src, 0) +tc.assertEqual(aut.edge_data(it).cond, bddtrue) +tc.assertEqual(aut.edge_data(1).cond, bddtrue) aut.release_iter(it) aut.purge_dead_states() i = aut.get_init_state() -assert aut.state_is_accepting(i) == False +tc.assertEqual(aut.state_is_accepting(i), False) aut = spot.translate('FGa') # Kill the edge between state 0 and 1 -assert aut.edge_storage(2).src == 0 -assert aut.edge_storage(2).dst == 1 +tc.assertEqual(aut.edge_storage(2).src, 0) +tc.assertEqual(aut.edge_storage(2).dst, 1) aut.edge_data(2).cond = bddfalse -assert aut.num_edges() == 3 -assert aut.num_states() == 2 +tc.assertEqual(aut.num_edges(), 3) +tc.assertEqual(aut.num_states(), 2) aut.purge_dead_states() -assert aut.num_edges() == 1 -assert aut.num_states() == 1 +tc.assertEqual(aut.num_edges(), 1) +tc.assertEqual(aut.num_states(), 1) diff --git a/tests/python/zlktree.py b/tests/python/zlktree.py index df8fd86f0..e1b0c9e7b 100644 --- a/tests/python/zlktree.py +++ b/tests/python/zlktree.py @@ -1,6 +1,6 @@ # -*- mode: python; coding: utf-8 -*- -# Copyright (C) 2021 Laboratoire de Recherche et Développement de l'Epita -# (LRDE). +# Copyright (C) 2021, 2022 Laboratoire de Recherche et Développement +# de l'Epita (LRDE). # # This file is part of Spot, a model checking library. # @@ -18,6 +18,8 @@ # along with this program. If not, see . import spot +from unittest import TestCase +tc = TestCase() a = spot.automaton("""HOA: v1 States: 5 Start: 0 AP: 2 "p0" "p1" Acceptance: 4 Inf(3) | Fin(3) properties: trans-labels explicit-labels @@ -25,8 +27,8 @@ trans-acc --BODY-- State: 0 [!0&!1] 3 [!0&!1] 4 State: 1 [!0&!1] 4 {3} [0&!1] 0 {2} [!0&1] 1 {2} State: 2 [!0&1] 0 {0 2} [!0&!1] 1 State: 3 [!0&1] 2 State: 4 [0&!1] 3 --END--""") b = spot.zielonka_tree_transform(a) -assert spot.are_equivalent(a, b) -assert b.acc().is_buchi() +tc.assertTrue(spot.are_equivalent(a, b)) +tc.assertTrue(b.acc().is_buchi()) def report_missing_exception(): raise RuntimeError("missing exception") @@ -45,95 +47,96 @@ State: 2 [0&1] 8 {3} [0&1] 2 {1} [!0&1] 4 {3 4} [!0&!1] 3 {2 5} State: [!0&!1] 2 {5} [!0&!1] 0 {3} [!0&!1] 5 --END--""") aa = spot.acd(a) try: - assert aa.has_rabin_shape() + tc.assertTrue(aa.has_rabin_shape()) except RuntimeError as e: - assert 'CHECK_RABIN' in str(e) + tc.assertIn('CHECK_RABIN', str(e)) else: report_missing_exception() try: - assert not aa.has_streett_shape() + tc.assertFalse(aa.has_streett_shape()) except RuntimeError as e: - assert 'CHECK_STREETT' in str(e) + tc.assertIn('CHECK_STREETT', str(e)) else: report_missing_exception() try: - assert not aa.has_parity_shape() + tc.assertFalse(aa.has_parity_shape()) except RuntimeError as e: - assert 'CHECK_PARITY' in str(e) + tc.assertIn('CHECK_PARITY', str(e)) else: report_missing_exception() aa = spot.acd(a, spot.acd_options_CHECK_RABIN) -assert aa.has_rabin_shape() -assert aa.node_count() == 13 +tc.assertTrue(aa.has_rabin_shape()) +tc.assertEqual(aa.node_count(), 13) try: - assert not aa.has_streett_shape() + tc.assertFalse(aa.has_streett_shape()) except RuntimeError as e: - assert 'CHECK_STREETT' in str(e) + tc.assertIn('CHECK_STREETT', str(e)) else: report_missing_exception() try: - assert aa.has_parity_shape() + tc.assertTrue(aa.has_parity_shape()) except RuntimeError as e: - assert 'CHECK_PARITY' in str(e) + tc.assertIn('CHECK_PARITY', str(e)) else: report_missing_exception() aa = spot.acd(a, (spot.acd_options_CHECK_PARITY | spot.acd_options_ABORT_WRONG_SHAPE)) -assert aa.has_rabin_shape() -assert not aa.has_streett_shape() -assert not aa.has_parity_shape() -assert aa.node_count() == 0 +tc.assertTrue(aa.has_rabin_shape()) +tc.assertFalse(aa.has_streett_shape()) +tc.assertFalse(aa.has_parity_shape()) +tc.assertEqual(aa.node_count(), 0) + try: aa.first_branch(0) except RuntimeError as e: - assert 'ABORT_WRONG_SHAPE' in str(e) + tc.assertIn('ABORT_WRONG_SHAPE', str(e)) else: report_missing_exception() try: aa.step(0, 0) except RuntimeError as e: - assert 'incorrect branch number' in str(e) + tc.assertIn('incorrect branch number', str(e)) else: report_missing_exception() try: aa.node_acceptance(0) except RuntimeError as e: - assert 'unknown node' in str(e) + tc.assertIn('unknown node', str(e)) else: report_missing_exception() try: aa.edges_of_node(0) except RuntimeError as e: - assert 'unknown node' in str(e) + tc.assertIn('unknown node', str(e)) else: report_missing_exception() try: aa.node_level(0) except RuntimeError as e: - assert 'unknown node' in str(e) + tc.assertIn('unknown node', str(e)) else: report_missing_exception() a = spot.translate('true') a.set_acceptance(spot.acc_cond('f')) b = spot.acd_transform(a) -assert a.equivalent_to(b) +tc.assertTrue(a.equivalent_to(b)) a = spot.translate('true') a.set_acceptance(spot.acc_cond('f')) b = spot.zielonka_tree_transform(a) -assert a.equivalent_to(b) +tc.assertTrue(a.equivalent_to(b)) a = spot.automaton("""HOA: v1 name: "^ G F p0 G F p1" States: 5 Start: 2 AP: 2 "a" "b" acc-name: Rabin 2 Acceptance: 4 (Fin(0) & Inf(1)) | @@ -144,8 +147,8 @@ complete properties: deterministic --BODY-- State: 0 {0} [!0&!1] 0 2} [!0&!1] 1 [0&!1] 4 [!0&1] 3 [0&1] 2 State: 4 {0 3} [!0&!1] 0 [0&!1] 4 [!0&1] 3 [0&1] 2 --END--""") b = spot.acd_transform_sbacc(a, True) -assert str(b.acc()) == '(3, Fin(0) & (Inf(1) | Fin(2)))' -assert a.equivalent_to(b) +tc.assertEqual(str(b.acc()), '(3, Fin(0) & (Inf(1) | Fin(2)))') +tc.assertTrue(a.equivalent_to(b)) b = spot.acd_transform_sbacc(a, False) -assert str(b.acc()) == '(2, Fin(0) & Inf(1))' -assert a.equivalent_to(b) +tc.assertEqual(str(b.acc()), '(2, Fin(0) & Inf(1))') +tc.assertTrue(a.equivalent_to(b)) diff --git a/tests/sanity/style.test b/tests/sanity/style.test index 8f157014d..85ef359b0 100755 --- a/tests/sanity/style.test +++ b/tests/sanity/style.test @@ -1,6 +1,6 @@ #! /bin/sh # -*- coding: utf-8 -*- -# Copyright (C) 2009-2021 Laboratoire de Recherche et Développement de +# Copyright (C) 2009-2022 Laboratoire de Recherche et Développement de # l'Epita (LRDE). # Copyright (C) 2004, 2005 Laboratoire d'Informatique de Paris 6 # (LIP6), département Systèmes Répartis Coopératifs (SRC), Université @@ -392,6 +392,27 @@ for dir in "${INCDIR-..}" "${INCDIR-..}/../bin" "${INCDIR-..}/../tests"; do done || : # Make sure sh does not abort when read exits with false. done +# Rules for Python tests +for dir in "${INCDIR-..}/../tests"; do + + find "$dir" -name "*.py" -a -type f -a -print | + while read file; do + fail=false + + # Strip comments. + sed 's,[ ]*#.*,,' < $file > $tmp + + $GREP '[ ]$' $tmp && + diag 'Trailing whitespace.' + + $GREP -E '([ ]|^)assert[ (]' $tmp && + diag "replace assert keywords by unittest assertion tests" + + $fail && echo "$file" >>failures.style + done || : # Make sure sh does not abort when read exits with false. +done + + if test -f failures.style; then echo "The following files contain style errors:" cat failures.style From 187bacc25402c1673437c2b0ea83459c512dc263 Mon Sep 17 00:00:00 2001 From: Antoine Martin Date: Thu, 10 Mar 2022 15:49:46 +0100 Subject: [PATCH 005/337] tests: don't wipe python environment * tests/run.in: keep original PYTHONPATH contents * NEWS: mention the bug --- NEWS | 3 +++ tests/run.in | 9 ++++++--- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/NEWS b/NEWS index 89a1d52a2..345ec7fdd 100644 --- a/NEWS +++ b/NEWS @@ -77,6 +77,9 @@ New in spot 2.10.4.dev (net yet released) - Do not use the seq command in test cases, it is not available everywhere. + - Do not erase the previous contents of the PYTHONPATH environment + variable when running tests, prepend to it instead. + New in spot 2.10.4 (2022-02-01) Bug fixed: diff --git a/tests/run.in b/tests/run.in index d14bf52a9..7eaa7732c 100755 --- a/tests/run.in +++ b/tests/run.in @@ -104,18 +104,21 @@ export srcdir case $1 in *.ipynb) - PYTHONPATH=$pypath DYLD_LIBRARY_PATH=$modpath:$DYLD_LIBRARY_PATH \ + PYTHONPATH=$pypath:$PYTHONPATH \ + DYLD_LIBRARY_PATH=$modpath:$DYLD_LIBRARY_PATH \ PYTHONIOENCODING=utf-8:surrogateescape \ exec $PREFIXCMD @PYTHON@ @abs_srcdir@/python/ipnbdoctest.py "$@";; *.py) - PYTHONPATH=$pypath DYLD_LIBRARY_PATH=$modpath:$DYLD_LIBRARY_PATH \ + PYTHONPATH=$pypath:$PYTHONPATH \ + DYLD_LIBRARY_PATH=$modpath:$DYLD_LIBRARY_PATH \ exec $PREFIXCMD @PYTHON@ "$@";; *.test) exec sh -x "$@";; *.pl) exec $PERL "$@";; *python*|*jupyter*|*pypy*) - PYTHONPATH=$pypath DYLD_LIBRARY_PATH=$modpath:$DYLD_LIBRARY_PATH \ + PYTHONPATH=$pypath:$PYTHONPATH \ + DYLD_LIBRARY_PATH=$modpath:$DYLD_LIBRARY_PATH \ exec $PREFIXCMD "$@";; *) echo "Unknown extension" >&2 From 0745e735bb8bf90610bc297009cd07fa78a51205 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Thu, 10 Mar 2022 10:53:18 +0100 Subject: [PATCH 006/337] fix typos and make formula_from_bdd more usable in Python * python/spot/impl.i (formula_from_bdd): Instantiate for twa_graph. * spot/twa/twa.hh (register_aps_from_dict): Typo in exception. * tests/python/except.py: More tests for the above. * tests/python/bdddict.py: Typo in comment. --- python/spot/impl.i | 2 ++ spot/twa/twa.hh | 4 ++-- tests/python/bdddict.py | 4 ++-- tests/python/except.py | 36 ++++++++++++++++++++++++++++++++++++ 4 files changed, 42 insertions(+), 4 deletions(-) diff --git a/python/spot/impl.i b/python/spot/impl.i index 7132a5cc6..b7f116201 100644 --- a/python/spot/impl.i +++ b/python/spot/impl.i @@ -533,6 +533,8 @@ namespace std { %include %include %include +%template(formula_to_bdd) spot::formula_to_bdd; + %include /* These operators may raise exceptions, and we do not want Swig4 to convert those exceptions to NotImplemented. */ diff --git a/spot/twa/twa.hh b/spot/twa/twa.hh index cb1e208ec..819a90962 100644 --- a/spot/twa/twa.hh +++ b/spot/twa/twa.hh @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2009, 2011, 2013-2021 Laboratoire de Recherche et +// Copyright (C) 2009, 2011, 2013-2022 Laboratoire de Recherche et // Développement de l'Epita (LRDE). // Copyright (C) 2003-2005 Laboratoire d'Informatique de Paris 6 // (LIP6), département Systèmes Répartis Coopératifs (SRC), Université @@ -761,7 +761,7 @@ namespace spot void register_aps_from_dict() { if (!aps_.empty()) - throw std::runtime_error("register_ap_from_dict() may not be" + throw std::runtime_error("register_aps_from_dict() may not be" " called on an automaton that has already" " registered some AP"); auto& m = get_dict()->bdd_map; diff --git a/tests/python/bdddict.py b/tests/python/bdddict.py index 0172bd050..b7b442b1f 100644 --- a/tests/python/bdddict.py +++ b/tests/python/bdddict.py @@ -17,8 +17,8 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -# Make sure we can leep track of BDD association in Python using bdd_dict, as -# discussed in issue #372. +# Make sure we can keep track of BDD association in Python using bdd_dict, as +# discussed in (deleted???) issue #372. # CPython use reference counting, so that automata are destructed # when we expect them to be. However other implementations like diff --git a/tests/python/except.py b/tests/python/except.py index 76f17f76c..8674721c9 100644 --- a/tests/python/except.py +++ b/tests/python/except.py @@ -295,3 +295,39 @@ except RuntimeError as e: se = str(e) tc.assertIn("synthesis-outputs", se) tc.assertIn("unregistered proposition", se) +else: + report_missing_exception() + + +a = spot.make_twa_graph() +s = a.new_state() +b = spot.formula_to_bdd("a & b", a.get_dict(), a) +a.new_edge(s, s, b, []) +try: + print(a.to_str('hoa')) +except RuntimeError as e: + tc.assertIn("unregistered atomic propositions", str(e)) +else: + report_missing_exception() + +a.register_aps_from_dict() +tc.assertEqual(a.to_str('hoa'), """HOA: v1 +States: 1 +Start: 0 +AP: 2 "a" "b" +acc-name: all +Acceptance: 0 t +properties: trans-labels explicit-labels state-acc deterministic +--BODY-- +State: 0 +[0&1] 0 +--END--""") + +try: + a.register_aps_from_dict() +except RuntimeError as e: + se = str(e) + tc.assertIn("register_aps_from_dict", se) + tc.assertIn("already registered", se) +else: + report_missing_exception() From e248f4500d9ed1c0fca03a606a9a6442fa0ac560 Mon Sep 17 00:00:00 2001 From: Florian Renkin Date: Tue, 15 Mar 2022 14:01:25 +0100 Subject: [PATCH 007/337] ltlsynt: typo in help * bin/ltlsynt.cc: here --- bin/ltlsynt.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/ltlsynt.cc b/bin/ltlsynt.cc index 45fd3b159..73ec6b2b1 100644 --- a/bin/ltlsynt.cc +++ b/bin/ltlsynt.cc @@ -65,7 +65,7 @@ static const argp_option options[] = "comma-separated list of controllable (a.k.a. output) atomic" " propositions", 0}, { "ins", OPT_INPUT, "PROPS", 0, - "comma-separated list of controllable (a.k.a. output) atomic" + "comma-separated list of uncontrollable (a.k.a. input) atomic" " propositions", 0}, /**************************************************/ { nullptr, 0, nullptr, 0, "Fine tuning:", 10 }, From 4f69e99c453af747a16a852bb96c25f97bfaf3e0 Mon Sep 17 00:00:00 2001 From: Florian Renkin Date: Thu, 17 Mar 2022 11:38:23 +0100 Subject: [PATCH 008/337] synthesis.ipynb: correct typos * tests/python/synthesis.ipynb: here --- tests/python/synthesis.ipynb | 3402 +++++++++++++++++----------------- 1 file changed, 1685 insertions(+), 1717 deletions(-) diff --git a/tests/python/synthesis.ipynb b/tests/python/synthesis.ipynb index be001a9b3..e290f02b5 100644 --- a/tests/python/synthesis.ipynb +++ b/tests/python/synthesis.ipynb @@ -3,7 +3,6 @@ { "cell_type": "code", "execution_count": 1, - "id": "452c00ae", "metadata": {}, "outputs": [], "source": [ @@ -14,13 +13,12 @@ }, { "cell_type": "markdown", - "id": "7545ab74", "metadata": {}, "source": [ "This notebook presents functions that can be used to solve the Reactive Synthesis problem using games.\n", - "If you are not familiar with how Spot represent games, please read the `games` notebook first.\n", + "If you are not familiar with how Spot represents games, please read the `games` notebook first.\n", "\n", - "In Reactive Synthesis, the goal is to build an electronic circuit that reacts to some input signals by producing some output signals, under some LTL constraints that tie both input and output. Of course the input signals are not controlable, so only job is to decide what output signal to produce.\n", + "In Reactive Synthesis, the goal is to build an electronic circuit that reacts to some input signals by producing some output signals, under some LTL constraints that tie both input and output. Of course the input signals are not controllable, so only job is to decide what output signal to produce.\n", "\n", "# Reactive synthesis in four steps\n", "\n", @@ -33,13 +31,12 @@ "\n", "Each of these steps is parametrized by a structure called `synthesis_info`. This structure stores some additional data needed to pass fine-tuning options or to store statistics.\n", "\n", - "The `ltl_to_game` function takes the LTL specification, and the list of controlable atomic propositions (or output signals). It returns a two-player game, where player 0 plays the input variables (and wants to invalidate the acceptance condition), and player 1 plays the output variables (and wants to satisfy the output condition). The conversion from LTL to parity automata can use one of many algorithms, and can be specified in the `synthesis_info` structure (this works like the `--algo=` option of `ltlsynt`)." + "The `ltl_to_game` function takes the LTL specification, and the list of controllable atomic propositions (or output signals). It returns a two-player game, where player 0 plays the input variables (and wants to invalidate the acceptance condition), and player 1 plays the output variables (and wants to satisfy the output condition). The conversion from LTL to parity automata can use one of many algorithms, and can be specified in the `synthesis_info` structure (this works like the `--algo=` option of `ltlsynt`)." ] }, { "cell_type": "code", "execution_count": 2, - "id": "fb49e681", "metadata": {}, "outputs": [ { @@ -56,649 +53,649 @@ "\n", "\n", - "\n", "\n", - "\n", - "\n", - "\n", - "Inf(\n", - "\n", - ") | (Fin(\n", - "\n", - ") & (Inf(\n", - "\n", - ") | Fin(\n", - "\n", - ")))\n", - "[parity max odd 4]\n", + "\n", + "\n", + "\n", + "Inf(\n", + "\n", + ") | (Fin(\n", + "\n", + ") & (Inf(\n", + "\n", + ") | Fin(\n", + "\n", + ")))\n", + "[parity max odd 4]\n", "\n", "\n", "\n", "9\n", - "\n", - "9\n", + "\n", + "9\n", "\n", "\n", "\n", "I->9\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "25\n", - "\n", - "25\n", + "\n", + "25\n", "\n", "\n", "\n", "9->25\n", - "\n", - "\n", - "!i0 & !i1\n", - "\n", + "\n", + "\n", + "!i0 & !i1\n", + "\n", "\n", "\n", "\n", "26\n", - "\n", - "26\n", + "\n", + "26\n", "\n", "\n", "\n", "9->26\n", - "\n", - "\n", - "i0 & !i1\n", - "\n", + "\n", + "\n", + "i0 & !i1\n", + "\n", "\n", "\n", "\n", "27\n", - "\n", - "27\n", + "\n", + "27\n", "\n", "\n", "\n", "9->27\n", - "\n", - "\n", - "!i0 & i1\n", - "\n", + "\n", + "\n", + "!i0 & i1\n", + "\n", "\n", "\n", "\n", "28\n", - "\n", - "28\n", + "\n", + "28\n", "\n", "\n", "\n", "9->28\n", - "\n", - "\n", - "i0 & i1\n", - "\n", + "\n", + "\n", + "i0 & i1\n", + "\n", "\n", "\n", "\n", "0\n", - "\n", - "0\n", + "\n", + "0\n", "\n", "\n", "\n", "10\n", - "\n", - "10\n", + "\n", + "10\n", "\n", "\n", "\n", "0->10\n", - "\n", - "\n", - "!i1\n", - "\n", + "\n", + "\n", + "!i1\n", + "\n", "\n", "\n", "\n", "11\n", - "\n", - "11\n", + "\n", + "11\n", "\n", "\n", "\n", "0->11\n", - "\n", - "\n", - "i1\n", - "\n", + "\n", + "\n", + "i1\n", + "\n", "\n", "\n", "\n", "1\n", - "\n", - "1\n", + "\n", + "1\n", "\n", "\n", "\n", "10->1\n", - "\n", - "\n", - "o0\n", - "\n", + "\n", + "\n", + "o0\n", + "\n", "\n", "\n", "\n", "11->0\n", - "\n", - "\n", - "o0\n", - "\n", + "\n", + "\n", + "o0\n", + "\n", "\n", "\n", "\n", "12\n", - "\n", - "12\n", + "\n", + "12\n", "\n", "\n", "\n", "1->12\n", - "\n", - "\n", - "!i1\n", - "\n", + "\n", + "\n", + "!i1\n", + "\n", "\n", "\n", "\n", "13\n", - "\n", - "13\n", + "\n", + "13\n", "\n", "\n", "\n", "1->13\n", - "\n", - "\n", - "i1\n", - "\n", + "\n", + "\n", + "i1\n", + "\n", "\n", "\n", "\n", "12->1\n", - "\n", - "\n", - "!o0\n", - "\n", + "\n", + "\n", + "!o0\n", + "\n", "\n", "\n", "\n", "13->0\n", - "\n", - "\n", - "!o0\n", - "\n", + "\n", + "\n", + "!o0\n", + "\n", "\n", "\n", "\n", "2\n", - "\n", - "2\n", + "\n", + "2\n", "\n", "\n", "\n", "14\n", - "\n", - "14\n", + "\n", + "14\n", "\n", "\n", "\n", "2->14\n", - "\n", - "\n", - "i1\n", - "\n", + "\n", + "\n", + "i1\n", + "\n", "\n", "\n", "\n", "16\n", - "\n", - "16\n", + "\n", + "16\n", "\n", "\n", "\n", "2->16\n", - "\n", - "\n", - "!i1\n", - "\n", + "\n", + "\n", + "!i1\n", + "\n", "\n", "\n", "\n", "15\n", - "\n", - "15\n", + "\n", + "15\n", "\n", "\n", "\n", "14->15\n", - "\n", - "\n", - "1\n", - "\n", + "\n", + "\n", + "1\n", + "\n", "\n", "\n", "\n", "16->2\n", - "\n", - "\n", - "1\n", - "\n", + "\n", + "\n", + "1\n", + "\n", "\n", "\n", "\n", "3\n", - "\n", - "3\n", + "\n", + "3\n", "\n", "\n", "\n", "3->13\n", - "\n", - "\n", - "i1\n", - "\n", + "\n", + "\n", + "i1\n", + "\n", "\n", "\n", "\n", "17\n", - "\n", - "17\n", + "\n", + "17\n", "\n", "\n", "\n", "3->17\n", - "\n", - "\n", - "!i1\n", - "\n", + "\n", + "\n", + "!i1\n", + "\n", "\n", "\n", "\n", "17->2\n", - "\n", - "\n", - "o0\n", - "\n", + "\n", + "\n", + "o0\n", + "\n", "\n", "\n", "\n", "17->3\n", - "\n", - "\n", - "!o0\n", - "\n", + "\n", + "\n", + "!o0\n", + "\n", "\n", "\n", "\n", "4\n", - "\n", - "4\n", + "\n", + "4\n", "\n", "\n", "\n", "4->14\n", - "\n", - "\n", - "i0\n", - "\n", + "\n", + "\n", + "i0\n", + "\n", "\n", "\n", "\n", "18\n", - "\n", - "18\n", + "\n", + "18\n", "\n", "\n", "\n", "4->18\n", - "\n", - "\n", - "!i0\n", - "\n", + "\n", + "\n", + "!i0\n", + "\n", "\n", "\n", "\n", "18->4\n", - "\n", - "\n", - "1\n", - "\n", + "\n", + "\n", + "1\n", + "\n", "\n", "\n", "\n", "5\n", - "\n", - "5\n", + "\n", + "5\n", "\n", "\n", "\n", "5->14\n", - "\n", - "\n", - "i0 & i1\n", - "\n", + "\n", + "\n", + "i0 & i1\n", + "\n", "\n", "\n", "\n", "5->16\n", - "\n", - "\n", - "i0 & !i1\n", - "\n", + "\n", + "\n", + "i0 & !i1\n", + "\n", "\n", "\n", "\n", "5->18\n", - "\n", - "\n", - "!i0 & i1\n", - "\n", + "\n", + "\n", + "!i0 & i1\n", + "\n", "\n", "\n", "\n", "19\n", - "\n", - "19\n", + "\n", + "19\n", "\n", "\n", "\n", "5->19\n", - "\n", - "\n", - "!i0 & !i1\n", - "\n", + "\n", + "\n", + "!i0 & !i1\n", + "\n", "\n", "\n", "\n", "19->5\n", - "\n", - "\n", - "1\n", - "\n", + "\n", + "\n", + "1\n", + "\n", "\n", "\n", "\n", "6\n", - "\n", - "6\n", + "\n", + "6\n", "\n", "\n", "\n", "6->10\n", - "\n", - "\n", - "i0 & !i1\n", - "\n", + "\n", + "\n", + "i0 & !i1\n", + "\n", "\n", "\n", "\n", "6->11\n", - "\n", - "\n", - "i0 & i1\n", - "\n", + "\n", + "\n", + "i0 & i1\n", + "\n", "\n", "\n", "\n", "20\n", - "\n", - "20\n", + "\n", + "20\n", "\n", "\n", "\n", "6->20\n", - "\n", - "\n", - "!i0 & !i1\n", - "\n", + "\n", + "\n", + "!i0 & !i1\n", + "\n", "\n", "\n", "\n", "21\n", - "\n", - "21\n", + "\n", + "21\n", "\n", "\n", "\n", "6->21\n", - "\n", - "\n", - "!i0 & i1\n", - "\n", + "\n", + "\n", + "!i0 & i1\n", + "\n", "\n", "\n", "\n", "20->4\n", - "\n", - "\n", - "!o0\n", - "\n", + "\n", + "\n", + "!o0\n", + "\n", "\n", "\n", "\n", "7\n", - "\n", - "7\n", + "\n", + "7\n", "\n", "\n", "\n", "20->7\n", - "\n", - "\n", - "o0\n", - "\n", + "\n", + "\n", + "o0\n", + "\n", "\n", "\n", "\n", "21->4\n", - "\n", - "\n", - "!o0\n", - "\n", + "\n", + "\n", + "!o0\n", + "\n", "\n", "\n", "\n", "21->6\n", - "\n", - "\n", - "o0\n", - "\n", + "\n", + "\n", + "o0\n", + "\n", "\n", "\n", "\n", "7->12\n", - "\n", - "\n", - "i0 & !i1\n", - "\n", + "\n", + "\n", + "i0 & !i1\n", + "\n", "\n", "\n", "\n", "7->13\n", - "\n", - "\n", - "i0 & i1\n", - "\n", + "\n", + "\n", + "i0 & i1\n", + "\n", "\n", "\n", "\n", "22\n", - "\n", - "22\n", + "\n", + "22\n", "\n", "\n", "\n", "7->22\n", - "\n", - "\n", - "!i0 & !i1\n", - "\n", + "\n", + "\n", + "!i0 & !i1\n", + "\n", "\n", "\n", "\n", "23\n", - "\n", - "23\n", + "\n", + "23\n", "\n", "\n", "\n", "7->23\n", - "\n", - "\n", - "!i0 & i1\n", - "\n", + "\n", + "\n", + "!i0 & i1\n", + "\n", "\n", "\n", "\n", "22->4\n", - "\n", - "\n", - "o0\n", - "\n", + "\n", + "\n", + "o0\n", + "\n", "\n", "\n", "\n", "22->7\n", - "\n", - "\n", - "!o0\n", - "\n", + "\n", + "\n", + "!o0\n", + "\n", "\n", "\n", "\n", "23->4\n", - "\n", - "\n", - "o0\n", - "\n", + "\n", + "\n", + "o0\n", + "\n", "\n", "\n", "\n", "23->6\n", - "\n", - "\n", - "!o0\n", - "\n", + "\n", + "\n", + "!o0\n", + "\n", "\n", "\n", "\n", "8\n", - "\n", - "8\n", + "\n", + "8\n", "\n", "\n", "\n", "8->13\n", - "\n", - "\n", - "i0 & i1\n", - "\n", + "\n", + "\n", + "i0 & i1\n", + "\n", "\n", "\n", "\n", "8->17\n", - "\n", - "\n", - "i0 & !i1\n", - "\n", + "\n", + "\n", + "i0 & !i1\n", + "\n", "\n", "\n", "\n", "8->23\n", - "\n", - "\n", - "!i0 & i1\n", - "\n", + "\n", + "\n", + "!i0 & i1\n", + "\n", "\n", "\n", "\n", "24\n", - "\n", - "24\n", + "\n", + "24\n", "\n", "\n", "\n", "8->24\n", - "\n", - "\n", - "!i0 & !i1\n", - "\n", + "\n", + "\n", + "!i0 & !i1\n", + "\n", "\n", "\n", "\n", "24->5\n", - "\n", - "\n", - "o0\n", - "\n", + "\n", + "\n", + "o0\n", + "\n", "\n", "\n", "\n", "24->8\n", - "\n", - "\n", - "!o0\n", - "\n", + "\n", + "\n", + "!o0\n", + "\n", "\n", "\n", "\n", "25->8\n", - "\n", - "\n", - "1\n", - "\n", + "\n", + "\n", + "1\n", + "\n", "\n", "\n", "\n", "26->3\n", - "\n", - "\n", - "1\n", - "\n", + "\n", + "\n", + "1\n", + "\n", "\n", "\n", "\n", "27->6\n", - "\n", - "\n", - "1\n", - "\n", + "\n", + "\n", + "1\n", + "\n", "\n", "\n", "\n", "28->0\n", - "\n", - "\n", - "1\n", - "\n", + "\n", + "\n", + "1\n", + "\n", "\n", "\n", "\n", "15->14\n", - "\n", - "\n", - "1\n", - "\n", + "\n", + "\n", + "1\n", + "\n", "\n", "\n", "\n" ], "text/plain": [ - " *' at 0x7ff27d57dc90> >" + " *' at 0x7f44ac249ed0> >" ] }, "metadata": {}, @@ -717,7 +714,6 @@ }, { "cell_type": "markdown", - "id": "3797307f", "metadata": {}, "source": [ "Solving the game, is done with `solve_game()` as with any game. There is also a version that takes a `synthesis_info` as second argument in case the time it takes has to be recorded. Here passing `si` or not makes no difference." @@ -726,7 +722,6 @@ { "cell_type": "code", "execution_count": 3, - "id": "62fb169f", "metadata": {}, "outputs": [ { @@ -742,588 +737,588 @@ "\n", "\n", - "\n", "\n", "\n", - "\n", - "\n", - "Inf(\n", - "\n", - ") | (Fin(\n", - "\n", - ") & (Inf(\n", - "\n", - ") | Fin(\n", - "\n", - ")))\n", - "[parity max odd 4]\n", + " viewBox=\"0.00 0.00 650.40 360.00\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n", + "\n", + "\n", + "Inf(\n", + "\n", + ") | (Fin(\n", + "\n", + ") & (Inf(\n", + "\n", + ") | Fin(\n", + "\n", + ")))\n", + "[parity max odd 4]\n", "\n", "\n", "\n", "9\n", - "\n", - "9\n", + "\n", + "9\n", "\n", "\n", "\n", "I->9\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "25\n", - "\n", - "25\n", + "\n", + "25\n", "\n", "\n", "\n", "9->25\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "26\n", - "\n", - "26\n", + "\n", + "26\n", "\n", "\n", "\n", "9->26\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "27\n", - "\n", - "27\n", + "\n", + "27\n", "\n", "\n", "\n", "9->27\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "28\n", - "\n", - "28\n", + "\n", + "28\n", "\n", "\n", "\n", "9->28\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "0\n", - "\n", - "0\n", + "\n", + "0\n", "\n", "\n", "\n", "10\n", - "\n", - "10\n", + "\n", + "10\n", "\n", "\n", "\n", "0->10\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "11\n", - "\n", - "11\n", + "\n", + "11\n", "\n", "\n", "\n", "0->11\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "1\n", - "\n", - "1\n", + "\n", + "1\n", "\n", "\n", "\n", "10->1\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "11->0\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "12\n", - "\n", - "12\n", + "\n", + "12\n", "\n", "\n", "\n", "1->12\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "13\n", - "\n", - "13\n", + "\n", + "13\n", "\n", "\n", "\n", "1->13\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "12->1\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "13->0\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "2\n", - "\n", - "2\n", + "\n", + "2\n", "\n", "\n", "\n", "14\n", - "\n", - "14\n", + "\n", + "14\n", "\n", "\n", "\n", "2->14\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "16\n", - "\n", - "16\n", + "\n", + "16\n", "\n", "\n", "\n", "2->16\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "15\n", - "\n", - "15\n", + "\n", + "15\n", "\n", "\n", "\n", "14->15\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "16->2\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "3\n", - "\n", - "3\n", + "\n", + "3\n", "\n", "\n", "\n", "3->13\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "17\n", - "\n", - "17\n", + "\n", + "17\n", "\n", "\n", "\n", "3->17\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "17->2\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "17->3\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "4\n", - "\n", - "4\n", + "\n", + "4\n", "\n", "\n", "\n", "4->14\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "18\n", - "\n", - "18\n", + "\n", + "18\n", "\n", "\n", "\n", "4->18\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "18->4\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "5\n", - "\n", - "5\n", + "\n", + "5\n", "\n", "\n", "\n", "5->14\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "5->16\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "5->18\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "19\n", - "\n", - "19\n", + "\n", + "19\n", "\n", "\n", "\n", "5->19\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "19->5\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "6\n", - "\n", - "6\n", + "\n", + "6\n", "\n", "\n", "\n", "6->10\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "6->11\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "20\n", - "\n", - "20\n", + "\n", + "20\n", "\n", "\n", "\n", "6->20\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "21\n", - "\n", - "21\n", + "\n", + "21\n", "\n", "\n", "\n", "6->21\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "20->4\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "7\n", - "\n", - "7\n", + "\n", + "7\n", "\n", "\n", "\n", "20->7\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "21->4\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "21->6\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "7->12\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "7->13\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "22\n", - "\n", - "22\n", + "\n", + "22\n", "\n", "\n", "\n", "7->22\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "23\n", - "\n", - "23\n", + "\n", + "23\n", "\n", "\n", "\n", "7->23\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "22->4\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "22->7\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "23->4\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "23->6\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "8\n", - "\n", - "8\n", + "\n", + "8\n", "\n", "\n", "\n", "8->13\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "8->17\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "8->23\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "24\n", - "\n", - "24\n", + "\n", + "24\n", "\n", "\n", "\n", "8->24\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "24->5\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "24->8\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "25->8\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "26->3\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "27->6\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "28->0\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "15->14\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n" @@ -1345,16 +1340,14 @@ }, { "cell_type": "markdown", - "id": "d5a53d3f", "metadata": {}, "source": [ - "Once a strategy has been found, it can be extracted as an automaton and simplified using 6 different levels (the default is 2). The output should be interpreted as a mealy automaton, where transition have the form `(ins)&(outs)` where `ins` and `outs` are Boolean formulas representing possible possibles inputs and outputs (they could be more than just conjunctions of atomic proposition). Mealy machines with this type of labels are called \"separated\" in Spot." + "Once a strategy has been found, it can be extracted as an automaton and simplified using 6 different levels (the default is 2). The output should be interpreted as a Mealy automaton, where transition have the form `(ins)&(outs)` where `ins` and `outs` are Boolean formulas representing possible inputs and outputs (they could be more than just conjunctions of atomic proposition). Mealy machines with this type of labels are called \"separated\" in Spot." ] }, { "cell_type": "code", "execution_count": 4, - "id": "cdf8f5f1", "metadata": {}, "outputs": [ { @@ -1370,303 +1363,303 @@ "\n", "\n", - "\n", "\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "0\n", - "\n", - "0\n", + "\n", + "0\n", "\n", "\n", "\n", "I->0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "1\n", - "\n", - "1\n", + "\n", + "1\n", "\n", "\n", "\n", "0->1\n", - "\n", - "\n", - "\n", - "!i0 & !i1\n", - "/\n", - "\n", - "1\n", + "\n", + "\n", + "\n", + "!i0 & !i1\n", + "/\n", + "\n", + "1\n", "\n", "\n", "\n", "2\n", - "\n", - "2\n", + "\n", + "2\n", "\n", "\n", "\n", "0->2\n", - "\n", - "\n", - "\n", - "i0 & !i1\n", - "/\n", - "\n", - "1\n", + "\n", + "\n", + "\n", + "i0 & !i1\n", + "/\n", + "\n", + "1\n", "\n", "\n", "\n", "3\n", - "\n", - "3\n", + "\n", + "3\n", "\n", "\n", "\n", "0->3\n", - "\n", - "\n", - "\n", - "!i0 & i1\n", - "/\n", - "\n", - "1\n", + "\n", + "\n", + "\n", + "!i0 & i1\n", + "/\n", + "\n", + "1\n", "\n", "\n", "\n", "4\n", - "\n", - "4\n", + "\n", + "4\n", "\n", "\n", "\n", "0->4\n", - "\n", - "\n", - "\n", - "i0 & i1\n", - "/\n", - "\n", - "1\n", + "\n", + "\n", + "\n", + "i0 & i1\n", + "/\n", + "\n", + "1\n", "\n", "\n", "\n", "1->1\n", - "\n", - "\n", - "\n", - "!i0 & !i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "!i0 & !i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "1->2\n", - "\n", - "\n", - "\n", - "i0 & !i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "i0 & !i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "1->3\n", - "\n", - "\n", - "\n", - "!i0 & i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "!i0 & i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "1->4\n", - "\n", - "\n", - "\n", - "i0 & i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "i0 & i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "2->2\n", - "\n", - "\n", - "\n", - "!i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "!i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "2->4\n", - "\n", - "\n", - "\n", - "i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "3->3\n", - "\n", - "\n", - "\n", - "!i0 & i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "!i0 & i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "3->4\n", - "\n", - "\n", - "\n", - "i0 & i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "i0 & i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "5\n", - "\n", - "5\n", + "\n", + "5\n", "\n", "\n", "\n", "3->5\n", - "\n", - "\n", - "\n", - "i0 & !i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "i0 & !i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "6\n", - "\n", - "6\n", + "\n", + "6\n", "\n", "\n", "\n", "3->6\n", - "\n", - "\n", - "\n", - "!i0 & !i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "!i0 & !i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "4->4\n", - "\n", - "\n", - "\n", - "i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "4->5\n", - "\n", - "\n", - "\n", - "!i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "!i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "5->4\n", - "\n", - "\n", - "\n", - "i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "5->5\n", - "\n", - "\n", - "\n", - "!i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "!i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "6->3\n", - "\n", - "\n", - "\n", - "!i0 & i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "!i0 & i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "6->4\n", - "\n", - "\n", - "\n", - "i0 & i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "i0 & i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "6->5\n", - "\n", - "\n", - "\n", - "i0 & !i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "i0 & !i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "6->6\n", - "\n", - "\n", - "\n", - "!i0 & !i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "!i0 & !i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n" @@ -1691,169 +1684,169 @@ "\n", "\n", - "\n", "\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "0\n", - "\n", - "0\n", + "\n", + "0\n", "\n", "\n", "\n", "I->0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "1\n", - "\n", - "1\n", + "\n", + "1\n", "\n", "\n", "\n", "0->1\n", - "\n", - "\n", - "\n", - "!i0 & !i1\n", - "/\n", - "\n", - "1\n", + "\n", + "\n", + "\n", + "!i0 & !i1\n", + "/\n", + "\n", + "1\n", "\n", "\n", "\n", "0->1\n", - "\n", - "\n", - "\n", - "i0 & !i1\n", - "/\n", - "\n", - "1\n", + "\n", + "\n", + "\n", + "i0 & !i1\n", + "/\n", + "\n", + "1\n", "\n", "\n", "\n", "2\n", - "\n", - "2\n", + "\n", + "2\n", "\n", "\n", "\n", "0->2\n", - "\n", - "\n", - "\n", - "!i0 & i1\n", - "/\n", - "\n", - "1\n", + "\n", + "\n", + "\n", + "!i0 & i1\n", + "/\n", + "\n", + "1\n", "\n", "\n", "\n", "0->2\n", - "\n", - "\n", - "\n", - "i0 & i1\n", - "/\n", - "\n", - "1\n", + "\n", + "\n", + "\n", + "i0 & i1\n", + "/\n", + "\n", + "1\n", "\n", "\n", "\n", "1->1\n", - "\n", - "\n", - "\n", - "i0 & !i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "i0 & !i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "1->1\n", - "\n", - "\n", - "\n", - "!i0 & !i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "!i0 & !i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "1->2\n", - "\n", - "\n", - "\n", - "i0 & i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "i0 & i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "1->2\n", - "\n", - "\n", - "\n", - "!i0 & i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "!i0 & i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "2->1\n", - "\n", - "\n", - "\n", - "i0 & !i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "i0 & !i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "2->1\n", - "\n", - "\n", - "\n", - "!i0 & !i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "!i0 & !i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "2->2\n", - "\n", - "\n", - "\n", - "i0 & i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "i0 & i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "2->2\n", - "\n", - "\n", - "\n", - "!i0 & i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "!i0 & i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n" @@ -1869,7 +1862,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "simplification lvl 2 : bisimulation-based reduction with output output assignement\n" + "simplification lvl 2 : bisimulation-based reduction with output assignement\n" ] }, { @@ -1878,119 +1871,119 @@ "\n", "\n", - "\n", "\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "1\n", - "\n", - "1\n", + "\n", + "1\n", "\n", "\n", "\n", "I->1\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "1->1\n", - "\n", - "\n", - "\n", - "i0 & i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "i0 & i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "1->1\n", - "\n", - "\n", - "\n", - "!i0 & i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "!i0 & i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "0\n", - "\n", - "0\n", + "\n", + "0\n", "\n", "\n", "\n", "1->0\n", - "\n", - "\n", - "\n", - "i0 & !i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "i0 & !i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "1->0\n", - "\n", - "\n", - "\n", - "!i0 & !i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "!i0 & !i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "0->1\n", - "\n", - "\n", - "\n", - "i0 & i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "i0 & i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "0->1\n", - "\n", - "\n", - "\n", - "!i0 & i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "!i0 & i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "0->0\n", - "\n", - "\n", - "\n", - "i0 & !i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "i0 & !i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "0->0\n", - "\n", - "\n", - "\n", - "!i0 & !i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "!i0 & !i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n" @@ -2015,75 +2008,75 @@ "\n", "\n", - "\n", "\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "0\n", - "\n", - "0\n", + "\n", + "0\n", "\n", "\n", "\n", "I->0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "0->0\n", - "\n", - "\n", - "\n", - "i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "1\n", - "\n", - "1\n", + "\n", + "1\n", "\n", "\n", "\n", "0->1\n", - "\n", - "\n", - "\n", - "!i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "!i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "1->0\n", - "\n", - "\n", - "\n", - "i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "1->1\n", - "\n", - "\n", - "\n", - "!i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "!i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n" @@ -2108,75 +2101,75 @@ "\n", "\n", - "\n", "\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "0\n", - "\n", - "0\n", + "\n", + "0\n", "\n", "\n", "\n", "I->0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", - "\n", + "\n", "0->0\n", - "\n", - "\n", - "\n", - "!i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "!i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "1\n", - "\n", - "1\n", + "\n", + "1\n", "\n", "\n", - "\n", + "\n", "0->1\n", - "\n", - "\n", - "\n", - "i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", - "\n", + "\n", "1->0\n", - "\n", - "\n", - "\n", - "!i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "!i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", - "\n", + "\n", "1->1\n", - "\n", - "\n", - "\n", - "i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n" @@ -2201,119 +2194,119 @@ "\n", "\n", - "\n", "\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "0\n", - "\n", - "0\n", + "\n", + "0\n", "\n", "\n", "\n", "I->0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "0->0\n", - "\n", - "\n", - "\n", - "i0 & i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "i0 & i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "0->0\n", - "\n", - "\n", - "\n", - "!i0 & i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "!i0 & i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "1\n", - "\n", - "1\n", + "\n", + "1\n", "\n", "\n", "\n", "0->1\n", - "\n", - "\n", - "\n", - "i0 & !i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "i0 & !i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "0->1\n", - "\n", - "\n", - "\n", - "!i0 & !i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "!i0 & !i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "1->0\n", - "\n", - "\n", - "\n", - "i0 & i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "i0 & i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "1->0\n", - "\n", - "\n", - "\n", - "!i0 & i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "!i0 & i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "1->1\n", - "\n", - "\n", - "\n", - "i0 & !i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "i0 & !i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "1->1\n", - "\n", - "\n", - "\n", - "!i0 & !i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "!i0 & !i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n" @@ -2330,14 +2323,14 @@ "# We have different levels of simplification:\n", "# 0 : No simplification\n", "# 1 : bisimulation-based reduction\n", - "# 2 : bisimulation-based reduction with output output assignement\n", + "# 2 : bisimulation-based reduction with output assignement\n", "# 3 : SAT-based exact minimization\n", "# 4 : First 1 then 3 (exact)\n", "# 5 : First 2 then 3 (not exact)\n", "\n", "descr = [\"0 : No simplification\", \n", " \"1 : bisimulation-based reduction\", \n", - " \"2 : bisimulation-based reduction with output output assignement\",\n", + " \"2 : bisimulation-based reduction with output assignement\",\n", " \"3 : SAT-based exact minimization\",\n", " \"4 : First 1 then 3 (exact)\",\n", " \"5 : First 2 then 3 (not exact)\"]\n", @@ -2352,7 +2345,6 @@ }, { "cell_type": "markdown", - "id": "511093c3", "metadata": {}, "source": [ "If needed, a separated Mealy machine can be turned into game shape using `split_sepearated_mealy()`, which is more efficient than `split_2step()`." @@ -2361,7 +2353,6 @@ { "cell_type": "code", "execution_count": 5, - "id": "cc977286", "metadata": {}, "outputs": [ { @@ -2370,260 +2361,260 @@ "
\n", "\n", - "\n", "\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "0\n", - "\n", - "0\n", + "\n", + "0\n", "\n", "\n", "\n", "I->0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "0->0\n", - "\n", - "\n", - "\n", - "i0 & i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "i0 & i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "0->0\n", - "\n", - "\n", - "\n", - "!i0 & i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "!i0 & i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "1\n", - "\n", - "1\n", + "\n", + "1\n", "\n", "\n", "\n", "0->1\n", - "\n", - "\n", - "\n", - "i0 & !i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "i0 & !i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "0->1\n", - "\n", - "\n", - "\n", - "!i0 & !i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "!i0 & !i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "1->0\n", - "\n", - "\n", - "\n", - "i0 & i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "i0 & i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "1->0\n", - "\n", - "\n", - "\n", - "!i0 & i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "!i0 & i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "1->1\n", - "\n", - "\n", - "\n", - "i0 & !i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "i0 & !i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "1->1\n", - "\n", - "\n", - "\n", - "!i0 & !i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "!i0 & !i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "
\n", "\n", - "\n", "\n", - "\n", - "\n", - "\n", - "t\n", - "[all]\n", + "\n", + "\n", + "\n", + "t\n", + "[all]\n", "\n", "\n", "\n", "0\n", - "\n", - "0\n", + "\n", + "0\n", "\n", "\n", "\n", "I->0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "2\n", - "\n", - "2\n", + "\n", + "2\n", "\n", "\n", "\n", "0->2\n", - "\n", - "\n", - "i0 & !i1\n", + "\n", + "\n", + "i0 & !i1\n", "\n", "\n", "\n", "0->2\n", - "\n", - "\n", - "!i0 & !i1\n", + "\n", + "\n", + "!i0 & !i1\n", "\n", "\n", "\n", "3\n", - "\n", - "3\n", + "\n", + "3\n", "\n", "\n", "\n", "0->3\n", - "\n", - "\n", - "i0 & i1\n", + "\n", + "\n", + "i0 & i1\n", "\n", "\n", "\n", "0->3\n", - "\n", - "\n", - "!i0 & i1\n", + "\n", + "\n", + "!i0 & i1\n", "\n", "\n", "\n", "1\n", - "\n", - "1\n", + "\n", + "1\n", "\n", "\n", "\n", "2->1\n", - "\n", - "\n", - "o0\n", + "\n", + "\n", + "o0\n", "\n", "\n", "\n", "3->0\n", - "\n", - "\n", - "o0\n", + "\n", + "\n", + "o0\n", "\n", "\n", "\n", "4\n", - "\n", - "4\n", + "\n", + "4\n", "\n", "\n", "\n", "1->4\n", - "\n", - "\n", - "i0 & i1\n", + "\n", + "\n", + "i0 & i1\n", "\n", "\n", "\n", "1->4\n", - "\n", - "\n", - "!i0 & i1\n", + "\n", + "\n", + "!i0 & i1\n", "\n", "\n", "\n", "5\n", - "\n", - "5\n", + "\n", + "5\n", "\n", "\n", "\n", "1->5\n", - "\n", - "\n", - "i0 & !i1\n", + "\n", + "\n", + "i0 & !i1\n", "\n", "\n", "\n", "1->5\n", - "\n", - "\n", - "!i0 & !i1\n", + "\n", + "\n", + "!i0 & !i1\n", "\n", "\n", "\n", "4->0\n", - "\n", - "\n", - "!o0\n", + "\n", + "\n", + "!o0\n", "\n", "\n", "\n", "5->1\n", - "\n", - "\n", - "!o0\n", + "\n", + "\n", + "!o0\n", "\n", "\n", "\n", @@ -2643,12 +2634,11 @@ }, { "cell_type": "markdown", - "id": "aa6fe484", "metadata": {}, "source": [ - "# Converting the separated mealy machine to AIGER\n", + "# Converting the separated mealy machine to AIG\n", "\n", - "A separated mealy machine can be converted to a circuit in the [AIGER format](http://fmv.jku.at/aiger/FORMAT.aiger) using `mealy_machine_to_aig()`. This takes a second argument specifying what type of encoding to use (exactly like `ltlsynt`'s `--aiger=...` option). \n", + "A separated Mealy machine can be converted to a circuit in the [AIGER format](http://fmv.jku.at/aiger/FORMAT.aiger) using `mealy_machine_to_aig()`. This takes a second argument specifying what type of encoding to use (exactly like `ltlsynt`'s `--aiger=...` option). \n", "\n", "In this case, the circuit is quite simple: `o0` should be the negation of previous value of `i1`. This is done by storing the value of `i1` in a latch. And the value if `i0` can be ignored." ] @@ -2656,7 +2646,6 @@ { "cell_type": "code", "execution_count": 6, - "id": "78261ec4", "metadata": {}, "outputs": [ { @@ -2665,60 +2654,60 @@ "\n", "\n", - "\n", "\n", - "\n", + "\n", "\n", - "\n", + "\n", "\n", "\n", "6\n", - "\n", - "L0_out\n", + "\n", + "L0_out\n", "\n", "\n", "\n", "o0\n", - "\n", - "o0\n", + "\n", + "o0\n", "\n", "\n", "\n", "6->o0:s\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "L0\n", - "\n", - "L0_in\n", + "\n", + "L0_in\n", "\n", "\n", "\n", "2\n", - "\n", - "i1\n", + "\n", + "i1\n", "\n", "\n", "\n", "2->L0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "4\n", - "\n", - "i0\n", + "\n", + "i0\n", "\n", "\n", "\n" ], "text/plain": [ - " >" + " >" ] }, "metadata": {}, @@ -2732,7 +2721,6 @@ }, { "cell_type": "markdown", - "id": "f95dc6b7", "metadata": {}, "source": [ "While we are at it, let us mention that you can render those circuits horizontally as follows:" @@ -2741,7 +2729,6 @@ { "cell_type": "code", "execution_count": 7, - "id": "14410565", "metadata": {}, "outputs": [ { @@ -2750,54 +2737,54 @@ "\n", "\n", - "\n", "\n", - "\n", + "\n", "\n", - "\n", + "\n", "\n", "\n", "6\n", - "\n", - "L0_out\n", + "\n", + "L0_out\n", "\n", "\n", "\n", "o0\n", - "\n", - "o0\n", + "\n", + "o0\n", "\n", "\n", "\n", "6->o0:w\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "L0\n", - "\n", - "L0_in\n", + "\n", + "L0_in\n", "\n", "\n", "\n", "2\n", - "\n", - "i1\n", + "\n", + "i1\n", "\n", "\n", "\n", "2->L0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "4\n", - "\n", - "i0\n", + "\n", + "i0\n", "\n", "\n", "\n" @@ -2817,16 +2804,14 @@ }, { "cell_type": "markdown", - "id": "9ccbc2e2", "metadata": {}, "source": [ - "To encode the circuit in the aig format (ASCII version) use:" + "To encode the circuit in the AIGER format (ASCII version) use:" ] }, { "cell_type": "code", "execution_count": 8, - "id": "06e485d0", "metadata": {}, "outputs": [ { @@ -2850,7 +2835,6 @@ }, { "cell_type": "markdown", - "id": "5f006648", "metadata": {}, "source": [ "# Adding more inputs and outputs by force" @@ -2858,7 +2842,6 @@ }, { "cell_type": "markdown", - "id": "9905208f", "metadata": {}, "source": [ "It can happen that propositions declared as output are ommited in the aig circuit (either because they are not part of the specification, or because they do not appear in the winning strategy). In that case those \n", @@ -2870,7 +2853,6 @@ { "cell_type": "code", "execution_count": 9, - "id": "560a7e46", "metadata": {}, "outputs": [ { @@ -2879,167 +2861,167 @@ "\n", "\n", - "\n", "\n", - "\n", - "\n", - "\n", - "Inf(\n", - "\n", - ") | (Fin(\n", - "\n", - ") & (Inf(\n", - "\n", - ") | (Fin(\n", - "\n", - ") & (Inf(\n", - "\n", - ") | Fin(\n", - "\n", - ")))))\n", - "[parity max odd 6]\n", + "\n", + "\n", + "\n", + "Inf(\n", + "\n", + ") | (Fin(\n", + "\n", + ") & (Inf(\n", + "\n", + ") | (Fin(\n", + "\n", + ") & (Inf(\n", + "\n", + ") | Fin(\n", + "\n", + ")))))\n", + "[parity max odd 6]\n", "\n", "\n", "\n", "3\n", - "\n", - "3\n", + "\n", + "3\n", "\n", "\n", "\n", "I->3\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "6\n", - "\n", - "6\n", + "\n", + "6\n", "\n", "\n", "\n", "3->6\n", - "\n", - "\n", - "i0\n", - "\n", + "\n", + "\n", + "i0\n", + "\n", "\n", "\n", "\n", "7\n", - "\n", - "7\n", + "\n", + "7\n", "\n", "\n", "\n", "3->7\n", - "\n", - "\n", - "!i0\n", - "\n", + "\n", + "\n", + "!i0\n", + "\n", "\n", "\n", "\n", "0\n", - "\n", - "0\n", + "\n", + "0\n", "\n", "\n", "\n", "4\n", - "\n", - "4\n", + "\n", + "4\n", "\n", "\n", "\n", "0->4\n", - "\n", - "\n", - "1\n", - "\n", + "\n", + "\n", + "1\n", + "\n", "\n", "\n", "\n", "4->0\n", - "\n", - "\n", - "1\n", - "\n", + "\n", + "\n", + "1\n", + "\n", "\n", "\n", "\n", "1\n", - "\n", - "1\n", + "\n", + "1\n", "\n", "\n", "\n", "5\n", - "\n", - "5\n", + "\n", + "5\n", "\n", "\n", "\n", "1->5\n", - "\n", - "\n", - "1\n", - "\n", + "\n", + "\n", + "1\n", + "\n", "\n", "\n", "\n", "5->1\n", - "\n", - "\n", - "!o0\n", - "\n", + "\n", + "\n", + "!o0\n", + "\n", "\n", "\n", "\n", "2\n", - "\n", - "2\n", + "\n", + "2\n", "\n", "\n", "\n", "2->6\n", - "\n", - "\n", - "1\n", - "\n", + "\n", + "\n", + "1\n", + "\n", "\n", "\n", "\n", "6->0\n", - "\n", - "\n", - "o0\n", - "\n", + "\n", + "\n", + "o0\n", + "\n", "\n", "\n", "\n", "6->2\n", - "\n", - "\n", - "!o0\n", - "\n", + "\n", + "\n", + "!o0\n", + "\n", "\n", "\n", "\n", "7->1\n", - "\n", - "\n", - "!o0\n", - "\n", + "\n", + "\n", + "!o0\n", + "\n", "\n", "\n", "\n" ], "text/plain": [ - " *' at 0x7ff27ca90390> >" + " *' at 0x7f44ac1d6750> >" ] }, "metadata": {}, @@ -3051,70 +3033,70 @@ "\n", "\n", - "\n", "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "0\n", - "\n", - "0\n", + "\n", + "0\n", "\n", "\n", "\n", "I->0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "0->0\n", - "\n", - "\n", - "\n", - "i0\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "i0\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "1\n", - "\n", - "1\n", + "\n", + "1\n", "\n", "\n", "\n", "0->1\n", - "\n", - "\n", + "\n", + "\n", "\n", - "!i0\n", - "/\n", + "!i0\n", + "/\n", "\n", - "!o0\n", + "!o0\n", "\n", "\n", "\n", "1->1\n", - "\n", - "\n", + "\n", + "\n", "\n", - "1\n", - "/\n", + "1\n", + "/\n", "\n", - "!o0\n", + "!o0\n", "\n", "\n", "\n" ], "text/plain": [ - " *' at 0x7ff27d57dd20> >" + " *' at 0x7f44ac249ed0> >" ] }, "metadata": {}, @@ -3126,72 +3108,72 @@ "\n", "\n", - "\n", "\n", "\n", + " viewBox=\"0.00 0.00 143.20 352.00\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n", "\n", - "\n", + "\n", "\n", "\n", "4\n", - "\n", - "L0_out\n", + "\n", + "L0_out\n", "\n", "\n", "\n", "6\n", - "\n", - "6\n", + "\n", + "6\n", "\n", "\n", "\n", "4->6\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "L0\n", - "\n", - "L0_in\n", + "\n", + "L0_in\n", "\n", "\n", "\n", "6->L0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "o0\n", - "\n", - "o0\n", + "\n", + "o0\n", "\n", "\n", "\n", "6->o0:s\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "2\n", - "\n", - "i0\n", + "\n", + "i0\n", "\n", "\n", "\n", "2->6\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n" ], "text/plain": [ - " >" + " >" ] }, "metadata": {}, @@ -3211,7 +3193,6 @@ }, { "cell_type": "markdown", - "id": "06d42ec3", "metadata": {}, "source": [ "To force the presence of extra variables in the circuit, they can be passed to `mealy_machine_to_aig()`." @@ -3220,7 +3201,6 @@ { "cell_type": "code", "execution_count": 10, - "id": "6ea759ea", "metadata": {}, "outputs": [ { @@ -3229,96 +3209,96 @@ "\n", "\n", - "\n", "\n", - "\n", + "\n", "\n", - "\n", + "\n", "\n", "\n", "6\n", - "\n", - "L0_out\n", + "\n", + "L0_out\n", "\n", "\n", "\n", "8\n", - "\n", - "8\n", + "\n", + "8\n", "\n", "\n", "\n", "6->8\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "L0\n", - "\n", - "L0_in\n", + "\n", + "L0_in\n", "\n", "\n", "\n", "8->L0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "o0\n", - "\n", - "o0\n", + "\n", + "o0\n", "\n", "\n", "\n", "8->o0:s\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "o1\n", - "\n", - "o1\n", + "\n", + "o1\n", "\n", "\n", "\n", "2\n", - "\n", - "i0\n", + "\n", + "i0\n", "\n", "\n", "\n", "2->8\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "4\n", - "\n", - "i1\n", + "\n", + "i1\n", "\n", "\n", "\n", "0\n", - "\n", - "False\n", + "\n", + "False\n", "\n", "\n", "\n", "0->o1:s\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n" ], "text/plain": [ - " >" + " >" ] }, "metadata": {}, @@ -3331,22 +3311,20 @@ }, { "cell_type": "markdown", - "id": "4135f43e", "metadata": {}, "source": [ - "# Combining mealy machines\n", + "# Combining Mealy machines\n", "\n", - "It can happen that the complet specification of the controller can be separated into sub-specifications with DISJOINT output propositions, see Finkbeiner et al. Specification Decomposition for Reactive Synthesis.\n", - "This results in multiple mealy machines which have to be converted into one single aiger circuit.\n", + "It can happen that the complete specification of the controller can be separated into sub-specifications with DISJOINT output propositions, see Finkbeiner et al. Specification Decomposition for Reactive Synthesis.\n", + "This results in multiple Mealy machines which have to be converted into one single AIG circuit.\n", "\n", - "This can be done using the function `mealy_machines_to_aig()`, which takes a vector of separated mealy machines as argument.\n", - "In order for this to work, all mealy machines need to share the same `bdd_dict`. This can be ensured by passing a common options strucuture." + "This can be done using the function `mealy_machines_to_aig()`, which takes a vector of separated Mealy machines as argument.\n", + "In order for this to work, all Mealy machines need to share the same `bdd_dict`. This can be ensured by passing a common options strucuture." ] }, { "cell_type": "code", "execution_count": 11, - "id": "4f9be142", "metadata": {}, "outputs": [ { @@ -3362,158 +3340,158 @@ "
\n", "\n", - "\n", "\n", - "\n", - "\n", - "\n", - "Inf(\n", - "\n", - ") | (Fin(\n", - "\n", - ") & (Inf(\n", - "\n", - ") | Fin(\n", - "\n", - ")))\n", - "[parity max odd 4]\n", + "\n", + "\n", + "\n", + "Inf(\n", + "\n", + ") | (Fin(\n", + "\n", + ") & (Inf(\n", + "\n", + ") | Fin(\n", + "\n", + ")))\n", + "[parity max odd 4]\n", "\n", "\n", "\n", "0\n", - "\n", - "0\n", + "\n", + "0\n", "\n", "\n", "\n", "I->0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "1\n", - "\n", - "1\n", + "\n", + "1\n", "\n", "\n", "\n", "0->1\n", - "\n", - "\n", - "(!i0 & !i1) | (i0 & i1)\n", - "\n", + "\n", + "\n", + "(!i0 & !i1) | (i0 & i1)\n", + "\n", "\n", "\n", "\n", "2\n", - "\n", - "2\n", + "\n", + "2\n", "\n", "\n", "\n", "0->2\n", - "\n", - "\n", - "(!i0 & i1) | (i0 & !i1)\n", - "\n", + "\n", + "\n", + "(!i0 & i1) | (i0 & !i1)\n", + "\n", "\n", "\n", "\n", "1->0\n", - "\n", - "\n", - "!o0\n", - "\n", + "\n", + "\n", + "!o0\n", + "\n", "\n", "\n", "\n", "2->0\n", - "\n", - "\n", - "o0\n", - "\n", + "\n", + "\n", + "o0\n", + "\n", "\n", "\n", "\n", "
\n", "\n", - "\n", "\n", - "\n", - "\n", - "\n", - "Inf(\n", - "\n", - ") | (Fin(\n", - "\n", - ") & (Inf(\n", - "\n", - ") | Fin(\n", - "\n", - ")))\n", - "[parity max odd 4]\n", + "\n", + "\n", + "\n", + "Inf(\n", + "\n", + ") | (Fin(\n", + "\n", + ") & (Inf(\n", + "\n", + ") | Fin(\n", + "\n", + ")))\n", + "[parity max odd 4]\n", "\n", "\n", "\n", "0\n", - "\n", - "0\n", + "\n", + "0\n", "\n", "\n", "\n", "I->0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "1\n", - "\n", - "1\n", + "\n", + "1\n", "\n", "\n", "\n", "0->1\n", - "\n", - "\n", - "(!i0 & !i1) | (i0 & i1)\n", - "\n", + "\n", + "\n", + "(!i0 & !i1) | (i0 & i1)\n", + "\n", "\n", "\n", "\n", "2\n", - "\n", - "2\n", + "\n", + "2\n", "\n", "\n", "\n", "0->2\n", - "\n", - "\n", - "(!i0 & i1) | (i0 & !i1)\n", - "\n", + "\n", + "\n", + "(!i0 & i1) | (i0 & !i1)\n", + "\n", "\n", "\n", "\n", "1->0\n", - "\n", - "\n", - "o1\n", - "\n", + "\n", + "\n", + "o1\n", + "\n", "\n", "\n", "\n", "2->0\n", - "\n", - "\n", - "!o1\n", - "\n", + "\n", + "\n", + "!o1\n", + "\n", "\n", "\n", "\n", @@ -3539,94 +3517,94 @@ "
\n", "\n", - "\n", "\n", - "\n", + "\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "0\n", - "\n", - "0\n", + "\n", + "0\n", "\n", "\n", "\n", "I->0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "0->0\n", - "\n", - "\n", - "\n", - "(!i0 & !i1) | (i0 & i1)\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "(!i0 & !i1) | (i0 & i1)\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "0->0\n", - "\n", - "\n", - "\n", - "(!i0 & i1) | (i0 & !i1)\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "(!i0 & i1) | (i0 & !i1)\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "
\n", "\n", - "\n", "\n", - "\n", + "\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "0\n", - "\n", - "0\n", + "\n", + "0\n", "\n", "\n", "\n", "I->0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "0->0\n", - "\n", - "\n", - "\n", - "(!i0 & !i1) | (i0 & i1)\n", - "/\n", - "\n", - "o1\n", + "\n", + "\n", + "\n", + "(!i0 & !i1) | (i0 & i1)\n", + "/\n", + "\n", + "o1\n", "\n", "\n", "\n", "0->0\n", - "\n", - "\n", - "\n", - "(!i0 & i1) | (i0 & !i1)\n", - "/\n", - "\n", - "!o1\n", + "\n", + "\n", + "\n", + "(!i0 & i1) | (i0 & !i1)\n", + "/\n", + "\n", + "!o1\n", "\n", "\n", "\n", @@ -3652,108 +3630,108 @@ "\n", "\n", - "\n", "\n", "\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "6\n", - "\n", - "6\n", + "\n", + "6\n", "\n", "\n", "\n", "10\n", - "\n", - "10\n", + "\n", + "10\n", "\n", "\n", "\n", "6->10\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "8\n", - "\n", - "8\n", + "\n", + "8\n", "\n", "\n", "\n", "8->10\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "o0\n", - "\n", - "o0\n", + "\n", + "o0\n", "\n", "\n", "\n", "10->o0:s\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "o1\n", - "\n", - "o1\n", + "\n", + "o1\n", "\n", "\n", "\n", "10->o1:s\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "2\n", - "\n", - "i0\n", + "\n", + "i0\n", "\n", "\n", "\n", "2->6\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "2->8\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "4\n", - "\n", - "i1\n", + "\n", + "i1\n", "\n", "\n", "\n", "4->6\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "4->8\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n" ], "text/plain": [ - " >" + " >" ] }, "metadata": {}, @@ -3780,7 +3758,6 @@ }, { "cell_type": "markdown", - "id": "b3985f04", "metadata": {}, "source": [ "# Reading an AIGER-file\n", @@ -3795,7 +3772,6 @@ { "cell_type": "code", "execution_count": 12, - "id": "3bc0b1f2", "metadata": {}, "outputs": [], "source": [ @@ -3816,7 +3792,6 @@ { "cell_type": "code", "execution_count": 13, - "id": "1455e6ab", "metadata": {}, "outputs": [ { @@ -3825,108 +3800,108 @@ "\n", "\n", - "\n", "\n", "\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "6\n", - "\n", - "6\n", + "\n", + "6\n", "\n", "\n", "\n", "10\n", - "\n", - "10\n", + "\n", + "10\n", "\n", "\n", "\n", "6->10\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "o1\n", - "\n", - "d\n", + "\n", + "d\n", "\n", "\n", "\n", "6->o1:s\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "8\n", - "\n", - "8\n", + "\n", + "8\n", "\n", "\n", "\n", "8->10\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "o0\n", - "\n", - "c\n", + "\n", + "c\n", "\n", "\n", "\n", "10->o0:s\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "2\n", - "\n", - "a\n", + "\n", + "a\n", "\n", "\n", "\n", "2->6\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "2->8\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "4\n", - "\n", - "b\n", + "\n", + "b\n", "\n", "\n", "\n", "4->6\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "4->8\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n" ], "text/plain": [ - " >" + " >" ] }, "metadata": {}, @@ -3941,7 +3916,6 @@ { "cell_type": "code", "execution_count": 14, - "id": "0c418256", "metadata": {}, "outputs": [ { @@ -3970,7 +3944,6 @@ { "cell_type": "code", "execution_count": 15, - "id": "bd4b6aa2", "metadata": {}, "outputs": [ { @@ -3987,16 +3960,14 @@ }, { "cell_type": "markdown", - "id": "94fd22a1", "metadata": {}, "source": [ - "An aiger circuit can be transformed into a monitor/mealy machine. This can be used for instance to check that it does not intersect the negation of the specification." + "An AIG circuit can be transformed into a monitor/Mealy machine. This can be used for instance to check that it does not intersect the negation of the specification." ] }, { "cell_type": "code", "execution_count": 16, - "id": "b157ec16", "metadata": {}, "outputs": [ { @@ -4005,52 +3976,52 @@ "\n", "\n", - "\n", "\n", - "\n", + "\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "0\n", - "\n", - "0\n", + "\n", + "0\n", "\n", "\n", "\n", "I->0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "0->0\n", - "\n", - "\n", - "\n", - "!a & !b\n", - "/\n", - "\n", - "!c & !d\n", - "\n", - "a & b\n", - "/\n", - "\n", - "!c & d\n", - "\n", - "(!a & b) | (a & !b)\n", - "/\n", - "\n", - "c & !d\n", + "\n", + "\n", + "\n", + "!a & !b\n", + "/\n", + "\n", + "!c & !d\n", + "\n", + "a & b\n", + "/\n", + "\n", + "!c & d\n", + "\n", + "(!a & b) | (a & !b)\n", + "/\n", + "\n", + "c & !d\n", "\n", "\n", "\n" ], "text/plain": [ - " *' at 0x7ff27ca90cf0> >" + " *' at 0x7f44ac2649c0> >" ] }, "execution_count": 16, @@ -4064,17 +4035,15 @@ }, { "cell_type": "markdown", - "id": "671b849d", "metadata": {}, "source": [ - "Note that the generation of aiger circuits from mealy machines is flexible and accepts separated mealy machines\n", - "as well as split mealy machines." + "Note that the generation of aiger circuits from Mealy machines is flexible and accepts separated Mealy machines\n", + "as well as split Mealy machines." ] }, { "cell_type": "code", "execution_count": 17, - "id": "fcf3b73e", "metadata": {}, "outputs": [ { @@ -4083,114 +4052,114 @@ "
\n", "\n", - "\n", "\n", - "\n", + "\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "0\n", - "\n", - "0\n", + "\n", + "0\n", "\n", "\n", "\n", "I->0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "0->0\n", - "\n", - "\n", - "\n", - "(!i0 & !i1) | (i0 & i1)\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "(!i0 & !i1) | (i0 & i1)\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "0->0\n", - "\n", - "\n", - "\n", - "(!i0 & i1) | (i0 & !i1)\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "(!i0 & i1) | (i0 & !i1)\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "
\n", "\n", - "\n", "\n", - "\n", - "\n", - "\n", - "t\n", - "[all]\n", + "\n", + "\n", + "\n", + "t\n", + "[all]\n", "\n", "\n", "\n", "0\n", - "\n", - "0\n", + "\n", + "0\n", "\n", "\n", "\n", "I->0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "1\n", - "\n", - "1\n", + "\n", + "1\n", "\n", "\n", "\n", "0->1\n", - "\n", - "\n", - "(!i0 & !i1) | (i0 & i1)\n", + "\n", + "\n", + "(!i0 & !i1) | (i0 & i1)\n", "\n", "\n", "\n", "2\n", - "\n", - "2\n", + "\n", + "2\n", "\n", "\n", "\n", "0->2\n", - "\n", - "\n", - "(!i0 & i1) | (i0 & !i1)\n", + "\n", + "\n", + "(!i0 & i1) | (i0 & !i1)\n", "\n", "\n", "\n", "1->0\n", - "\n", - "\n", - "!o0\n", + "\n", + "\n", + "!o0\n", "\n", "\n", "\n", "2->0\n", - "\n", - "\n", - "o0\n", + "\n", + "\n", + "o0\n", "\n", "\n", "\n", @@ -4222,7 +4191,6 @@ { "cell_type": "code", "execution_count": 18, - "id": "cd06f9ab", "metadata": {}, "outputs": [ { @@ -4231,180 +4199,180 @@ "
\n", "\n", - "\n", "\n", "\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "6\n", - "\n", - "6\n", + "\n", + "6\n", "\n", "\n", "\n", "10\n", - "\n", - "10\n", + "\n", + "10\n", "\n", "\n", "\n", "6->10\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "8\n", - "\n", - "8\n", + "\n", + "8\n", "\n", "\n", "\n", "8->10\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "o0\n", - "\n", - "o0\n", + "\n", + "o0\n", "\n", "\n", "\n", "10->o0:s\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "2\n", - "\n", - "i0\n", + "\n", + "i0\n", "\n", "\n", "\n", "2->6\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "2->8\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "4\n", - "\n", - "i1\n", + "\n", + "i1\n", "\n", "\n", "\n", "4->6\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "4->8\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "
\n", "\n", - "\n", "\n", "\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "6\n", - "\n", - "6\n", + "\n", + "6\n", "\n", "\n", "\n", "10\n", - "\n", - "10\n", + "\n", + "10\n", "\n", "\n", "\n", "6->10\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "8\n", - "\n", - "8\n", + "\n", + "8\n", "\n", "\n", "\n", "8->10\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "o0\n", - "\n", - "o0\n", + "\n", + "o0\n", "\n", "\n", "\n", "10->o0:s\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "2\n", - "\n", - "i0\n", + "\n", + "i0\n", "\n", "\n", "\n", "2->6\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "2->8\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "4\n", - "\n", - "i1\n", + "\n", + "i1\n", "\n", "\n", "\n", "4->6\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "4->8\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", @@ -4425,9 +4393,9 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 3 (ipykernel)", + "display_name": "'Python Interactive'", "language": "python", - "name": "python3" + "name": "748aac80-c5a9-4430-8d88-15820461ebdf" }, "language_info": { "codemirror_mode": { @@ -4439,7 +4407,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.10" + "version": "3.7.3" } }, "nbformat": 4, From c1e6340228303ba5ca0766597f815119f0fefa29 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Thu, 17 Mar 2022 10:55:30 +0100 Subject: [PATCH 009/337] optionmap: set_if_unset and simplifications * spot/misc/optionmap.hh (set_if_unset): New method. * spot/misc/optionmap.cc (set_if_unset, set, set_str): Implement set_if_unset, and simplify set and set_str to not perform two lookups. * spot/twaalgos/synthesis.cc (create_translator): Use set_if_unset to simplify the code. --- spot/misc/optionmap.cc | 36 +++++++++++++++++++++++++++++------- spot/misc/optionmap.hh | 7 +++++-- spot/twaalgos/synthesis.cc | 12 ++++-------- 3 files changed, 38 insertions(+), 17 deletions(-) diff --git a/spot/misc/optionmap.cc b/spot/misc/optionmap.cc index 3349f0f0d..4db5235eb 100644 --- a/spot/misc/optionmap.cc +++ b/spot/misc/optionmap.cc @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2008, 2013-2016, 2018 Laboratoire de Recherche +// Copyright (C) 2008, 2013-2016, 2018, 2022 Laboratoire de Recherche // et Développement de l'Epita (LRDE). // Copyright (C) 2005 Laboratoire d'Informatique de Paris 6 (LIP6), // département Systèmes Répartis Coopératifs (SRC), Université Pierre @@ -158,17 +158,39 @@ namespace spot int option_map::set(const char* option, int val, int def) { - int old = get(option, def); - set_(option, val); - return old; + if (auto [p, b] = options_.emplace(option, val); b) + { + unused_.insert(option); + return def; + } + else + { + int old = p->second; + p->second = val; + return old; + } + } + + void + option_map::set_if_unset(const char* option, int val) + { + if (options_.emplace(option, val).second) + unused_.insert(option); } std::string option_map::set_str(const char* option, std::string val, std::string def) { - std::string old = get_str(option, def); - set_str_(option, val); - return old; + if (auto [p, b] = options_str_.emplace(option, val); b) + { + unused_.insert(option); + return def; + } + else + { + std::swap(val, p->second); + return val; + } } void diff --git a/spot/misc/optionmap.hh b/spot/misc/optionmap.hh index ea06c62f9..229733a18 100644 --- a/spot/misc/optionmap.hh +++ b/spot/misc/optionmap.hh @@ -1,6 +1,6 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2013, 2015, 2016-2017 Laboratoire de Recherche et -// Developpement de l'Epita (LRDE) +// Copyright (C) 2013, 2015, 2016-2017, 2022 Laboratoire de Recherche +// et Developpement de l'Epita (LRDE) // Copyright (C) 2005 Laboratoire d'Informatique de Paris 6 (LIP6), // département Systèmes Répartis Coopératifs (SRC), Université Pierre // et Marie Curie. @@ -80,6 +80,9 @@ namespace spot /// or \a def otherwise. int set(const char* option, int val, int def = 0); + /// \brief Set the value of \a option to \a val if it is unset. + void set_if_unset(const char* option, int val); + /// \brief Set the value of a string \a option to \a val. /// /// \return The previous value associated to \a option if declared, diff --git a/spot/twaalgos/synthesis.cc b/spot/twaalgos/synthesis.cc index 9025bb303..ec2defb4f 100644 --- a/spot/twaalgos/synthesis.cc +++ b/spot/twaalgos/synthesis.cc @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2020, 2021 Laboratoire de Recherche et +// Copyright (C) 2020-2022 Laboratoire de Recherche et // Développement de l'Epita (LRDE). // // This file is part of Spot, a model checking library. @@ -768,13 +768,9 @@ namespace spot auto sol = gi.s; const bdd_dict_ptr& dict = gi.dict; - for (auto&& p : std::vector> - {{"simul", 0}, - {"ba-simul", 0}, - {"det-simul", 0}, - {"tls-impl", 1}, - {"wdba-minimize", 2}}) - extra_options.set(p.first, extra_options.get(p.first, p.second)); + extra_options.set_if_unset("simul", 0); + extra_options.set_if_unset("tls-impl", 1); + extra_options.set_if_unset("wdba-minimize", 2); translator trans(dict, &extra_options); switch (sol) From 75818fde1361203c2b63c717f914fc589306cc10 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Thu, 17 Mar 2022 14:30:05 +0100 Subject: [PATCH 010/337] synthesis: fix suboptimal colorization after LAR * spot/twaalgos/synthesis.cc (ltl_to_game): In LAR and LAR_OLD mode, for max odd and colorize the game after the split, not before. The previous code used to colorize twice, and could waste up to 4 colors in the process. * tests/core/ltlsynt.test, tests/python/_mealy.ipynb, tests/python/games.ipynb, tests/python/synthesis.ipynb, tests/python/synthesis.py: Adjust all test cases to reflect the fact that the game uses fewer colors. --- spot/twaalgos/synthesis.cc | 6 +- tests/core/ltlsynt.test | 64 +- tests/python/_mealy.ipynb | 89 +- tests/python/games.ipynb | 119 +- tests/python/synthesis.ipynb | 3338 +++++++++++++++++----------------- tests/python/synthesis.py | 10 +- 6 files changed, 1820 insertions(+), 1806 deletions(-) diff --git a/spot/twaalgos/synthesis.cc b/spot/twaalgos/synthesis.cc index ec2defb4f..6d4537206 100644 --- a/spot/twaalgos/synthesis.cc +++ b/spot/twaalgos/synthesis.cc @@ -974,16 +974,13 @@ namespace spot if (gi.s == algo::LAR) { dpa = to_parity(aut); - // reduce_parity is called by to_parity(), - // but with colorization turned off. - colorize_parity_here(dpa, true); + // reduce_parity is called by to_parity() } else { dpa = to_parity_old(aut); dpa = reduce_parity_here(dpa, true); } - change_parity_here(dpa, parity_kind_max, parity_style_odd); if (bv) bv->paritize_time += sw.stop(); if (vs) @@ -995,6 +992,7 @@ namespace spot if (bv) sw.start(); dpa = split_2step(dpa, outs, true); + change_parity_here(dpa, parity_kind_max, parity_style_odd); colorize_parity_here(dpa, true); if (bv) bv->split_time += sw.stop(); diff --git a/tests/core/ltlsynt.test b/tests/core/ltlsynt.test index 24a53556a..215143df2 100644 --- a/tests/core/ltlsynt.test +++ b/tests/core/ltlsynt.test @@ -59,11 +59,11 @@ parity 13; 1 1 1 6,3; parity 5; 1 1 0 4,5 "INIT"; -5 4 1 1,1; -4 5 1 0,1; +5 2 1 1,1; +4 3 1 0,1; 0 1 0 2,3; -3 5 1 1; -2 3 1 0,0; +3 3 1 1; +2 1 1 0,0; EOF : > out @@ -230,10 +230,10 @@ direct strategy might exist but was not found. translating formula done in X seconds automaton has 2 states and 3 colors LAR construction done in X seconds -DPA has 4 states, 3 colors +DPA has 4 states, 1 colors split inputs and outputs done in X seconds automaton has 12 states -solving game with acceptance: parity max odd 5 +solving game with acceptance: parity max odd 3 game solved in X seconds EOF ltlsynt -f "G(Fi0 && Fi1 && Fi2) -> G(i1 <-> o0)" --outs="o0" --algo=lar \ @@ -569,10 +569,10 @@ direct strategy might exist but was not found. translating formula done in X seconds automaton has 1 states and 1 colors LAR construction done in X seconds -DPA has 1 states, 2 colors +DPA has 1 states, 0 colors split inputs and outputs done in X seconds automaton has 2 states -solving game with acceptance: parity max odd 4 +solving game with acceptance: Streett 1 game solved in X seconds EOF ltlsynt -f '(GFa <-> GFb) && (Gc)' --outs=b,c --verbose 2> out @@ -646,10 +646,10 @@ direct strategy might exist but was not found. translating formula done in X seconds automaton has 4 states and 1 colors LAR construction done in X seconds -DPA has 4 states, 4 colors +DPA has 4 states, 1 colors split inputs and outputs done in X seconds automaton has 9 states -solving game with acceptance: parity max odd 6 +solving game with acceptance: Streett 1 game solved in X seconds AIG circuit was created in X seconds and has 0 latches and 0 gates EOF @@ -663,20 +663,20 @@ direct strategy might exist but was not found. translating formula done in X seconds automaton has 2 states and 1 colors LAR construction done in X seconds -DPA has 2 states, 2 colors +DPA has 2 states, 0 colors split inputs and outputs done in X seconds automaton has 4 states -solving game with acceptance: parity max odd 4 +solving game with acceptance: Streett 1 game solved in X seconds trying to create strategy directly for (a | x) -> x direct strategy might exist but was not found. translating formula done in X seconds automaton has 2 states and 1 colors LAR construction done in X seconds -DPA has 2 states, 2 colors +DPA has 2 states, 0 colors split inputs and outputs done in X seconds automaton has 4 states -solving game with acceptance: parity max odd 4 +solving game with acceptance: Streett 1 game solved in X seconds AIG circuit was created in X seconds and has 0 latches and 0 gates EOF @@ -692,20 +692,20 @@ direct strategy might exist but was not found. translating formula done in X seconds automaton has 1 states and 1 colors LAR construction done in X seconds -DPA has 1 states, 2 colors +DPA has 1 states, 0 colors split inputs and outputs done in X seconds automaton has 2 states -solving game with acceptance: parity max odd 4 +solving game with acceptance: Streett 1 game solved in X seconds trying to create strategy directly for Gy direct strategy might exist but was not found. translating formula done in X seconds automaton has 1 states and 1 colors LAR construction done in X seconds -DPA has 1 states, 2 colors +DPA has 1 states, 0 colors split inputs and outputs done in X seconds automaton has 2 states -solving game with acceptance: parity max odd 4 +solving game with acceptance: Streett 1 game solved in X seconds AIG circuit was created in X seconds and has 0 latches and 0 gates EOF @@ -720,10 +720,10 @@ direct strategy might exist but was not found. translating formula done in X seconds automaton has 1 states and 1 colors LAR construction done in X seconds -DPA has 1 states, 2 colors +DPA has 1 states, 0 colors split inputs and outputs done in X seconds automaton has 4 states -solving game with acceptance: parity max odd 4 +solving game with acceptance: parity max odd 3 game solved in X seconds EOF ltlsynt -f '!F(a|b)' --outs=b --decompose=yes --aiger --verbose 2> out || true @@ -737,10 +737,10 @@ direct strategy might exist but was not found. translating formula done in X seconds automaton has 1 states and 1 colors LAR construction done in X seconds -DPA has 1 states, 2 colors +DPA has 1 states, 0 colors split inputs and outputs done in X seconds automaton has 4 states -solving game with acceptance: parity max odd 4 +solving game with acceptance: parity max odd 3 game solved in X seconds EOF ltlsynt -f 'G!(a -> b)' --outs=b --decompose=yes --aiger\ @@ -755,10 +755,10 @@ direct strategy might exist but was not found. translating formula done in X seconds automaton has 2 states and 1 colors LAR construction done in X seconds -DPA has 2 states, 4 colors +DPA has 2 states, 1 colors split inputs and outputs done in X seconds automaton has 5 states -solving game with acceptance: parity max odd 6 +solving game with acceptance: Streett 1 game solved in X seconds AIG circuit was created in X seconds and has 0 latches and 0 gates EOF @@ -775,30 +775,30 @@ direct strategy might exist but was not found. translating formula done in X seconds automaton has 2 states and 1 colors LAR construction done in X seconds -DPA has 2 states, 2 colors +DPA has 2 states, 0 colors split inputs and outputs done in X seconds automaton has 4 states -solving game with acceptance: parity max odd 4 +solving game with acceptance: Streett 1 game solved in X seconds trying to create strategy directly for a -> c direct strategy might exist but was not found. translating formula done in X seconds automaton has 2 states and 1 colors LAR construction done in X seconds -DPA has 2 states, 2 colors +DPA has 2 states, 0 colors split inputs and outputs done in X seconds automaton has 4 states -solving game with acceptance: parity max odd 4 +solving game with acceptance: Streett 1 game solved in X seconds trying to create strategy directly for a -> d direct strategy might exist but was not found. translating formula done in X seconds automaton has 2 states and 1 colors LAR construction done in X seconds -DPA has 2 states, 2 colors +DPA has 2 states, 0 colors split inputs and outputs done in X seconds automaton has 4 states -solving game with acceptance: parity max odd 4 +solving game with acceptance: Streett 1 game solved in X seconds AIG circuit was created in X seconds and has 0 latches and 0 gates EOF @@ -814,10 +814,10 @@ direct strategy might exist but was not found. translating formula done in X seconds automaton has 1 states and 1 colors LAR construction done in X seconds -DPA has 1 states, 2 colors +DPA has 1 states, 0 colors split inputs and outputs done in X seconds automaton has 4 states -solving game with acceptance: parity max odd 4 +solving game with acceptance: parity max odd 3 game solved in X seconds EOF ltlsynt -f '!(F(a | b))' --outs=b, --decompose=yes \ diff --git a/tests/python/_mealy.ipynb b/tests/python/_mealy.ipynb index 0fbad3d08..4e7374852 100644 --- a/tests/python/_mealy.ipynb +++ b/tests/python/_mealy.ipynb @@ -3,6 +3,7 @@ { "cell_type": "code", "execution_count": 1, + "id": "8bca10b8", "metadata": {}, "outputs": [], "source": [ @@ -12,6 +13,7 @@ }, { "cell_type": "markdown", + "id": "c73e997a", "metadata": {}, "source": [ "Test the Mealy printer." @@ -20,6 +22,7 @@ { "cell_type": "code", "execution_count": 2, + "id": "f8eff7ed", "metadata": {}, "outputs": [], "source": [ @@ -29,6 +32,7 @@ { "cell_type": "code", "execution_count": 3, + "id": "ad3c80bc", "metadata": {}, "outputs": [ { @@ -49,6 +53,7 @@ { "cell_type": "code", "execution_count": 4, + "id": "50130d85", "metadata": {}, "outputs": [ { @@ -60,82 +65,78 @@ "\n", "\n", - "\n", + "\n", "\n", - "\n", - "Inf(\n", - "\n", - ") | (Fin(\n", - "\n", - ") & (Inf(\n", - "\n", - ") | Fin(\n", - "\n", - ")))\n", - "[parity max odd 4]\n", + "\n", + "Inf(\n", + "\n", + ") | Fin(\n", + "\n", + ")\n", + "[Streett 1]\n", "\n", "\n", "\n", "0\n", - "\n", - "0\n", + "\n", + "0\n", "\n", "\n", "\n", "I->0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "1\n", - "\n", - "1\n", + "\n", + "1\n", "\n", "\n", "\n", "0->1\n", - "\n", - "\n", - "!a & !c\n", - "\n", + "\n", + "\n", + "!a & !c\n", + "\n", "\n", "\n", "\n", "2\n", - "\n", - "2\n", + "\n", + "2\n", "\n", "\n", "\n", "0->2\n", - "\n", - "\n", - "a | c\n", - "\n", + "\n", + "\n", + "a | c\n", + "\n", "\n", "\n", "\n", "1->0\n", - "\n", - "\n", - "!b & !d\n", - "\n", + "\n", + "\n", + "!b & !d\n", + "\n", "\n", "\n", "\n", "2->0\n", - "\n", - "\n", - "b | d\n", - "\n", + "\n", + "\n", + "b | d\n", + "\n", "\n", "\n", "\n" ], "text/plain": [ - " *' at 0x7f300caabba0> >" + " *' at 0x7f32ec50ce40> >" ] }, "execution_count": 4, @@ -150,6 +151,7 @@ { "cell_type": "code", "execution_count": 5, + "id": "3d56cda6", "metadata": {}, "outputs": [], "source": [ @@ -159,6 +161,7 @@ { "cell_type": "code", "execution_count": 6, + "id": "c24548a1", "metadata": {}, "outputs": [ { @@ -213,7 +216,7 @@ "\n" ], "text/plain": [ - " *' at 0x7f300c179300> >" + " *' at 0x7f32ec571c30> >" ] }, "execution_count": 6, @@ -228,6 +231,7 @@ { "cell_type": "code", "execution_count": 7, + "id": "88f2c0e0", "metadata": {}, "outputs": [], "source": [ @@ -237,6 +241,7 @@ { "cell_type": "code", "execution_count": 8, + "id": "e626997e", "metadata": {}, "outputs": [ { @@ -285,7 +290,7 @@ "\n" ], "text/plain": [ - " *' at 0x7f300c179300> >" + " *' at 0x7f32ec571c30> >" ] }, "execution_count": 8, @@ -300,7 +305,7 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 3", + "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, @@ -314,7 +319,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.10" + "version": "3.9.2" } }, "nbformat": 4, diff --git a/tests/python/games.ipynb b/tests/python/games.ipynb index 324aab546..028ed0372 100644 --- a/tests/python/games.ipynb +++ b/tests/python/games.ipynb @@ -693,18 +693,14 @@ " viewBox=\"0.00 0.00 566.58 353.20\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n", "\n", "\n", - "Fin(\n", - "\n", - ") & (Inf(\n", - "\n", - ") | (Fin(\n", - "\n", - ") & (Inf(\n", - "\n", - ") | Fin(\n", - "\n", - "))))\n", - "[parity max odd 5]\n", + "Fin(\n", + "\n", + ") & (Inf(\n", + "\n", + ") | Fin(\n", + "\n", + "))\n", + "[parity max odd 3]\n", "\n", "\n", "\n", @@ -786,7 +782,7 @@ "\n", "\n", "1\n", - "\n", + "\n", "\n", "\n", "\n", @@ -800,7 +796,7 @@ "\n", "\n", "1\n", - "\n", + "\n", "\n", "\n", "\n", @@ -830,7 +826,7 @@ "\n", "\n", "1\n", - "\n", + "\n", "\n", "\n", "\n", @@ -858,7 +854,7 @@ "\n", "\n", "1\n", - "\n", + "\n", "\n", "\n", "\n", @@ -886,7 +882,7 @@ "\n", "\n", "!b\n", - "\n", + "\n", "\n", "\n", "\n", @@ -894,7 +890,7 @@ "\n", "\n", "b\n", - "\n", + "\n", "\n", "\n", "\n", @@ -902,7 +898,7 @@ "\n", "\n", "!b\n", - "\n", + "\n", "\n", "\n", "\n", @@ -910,7 +906,7 @@ "\n", "\n", "b\n", - "\n", + "\n", "\n", "\n", "\n", @@ -918,7 +914,7 @@ "\n", "\n", "!b\n", - "\n", + "\n", "\n", "\n", "\n", @@ -926,13 +922,13 @@ "\n", "\n", "b\n", - "\n", + "\n", "\n", "\n", "\n" ], "text/plain": [ - " *' at 0x7f5c143ec630> >" + " *' at 0x7f80642eee70> >" ] }, "execution_count": 8, @@ -967,8 +963,8 @@ "States: 12\n", "Start: 4\n", "AP: 2 \"b\" \"a\"\n", - "acc-name: parity max odd 5\n", - "Acceptance: 5 Fin(4) & (Inf(3) | (Fin(2) & (Inf(1) | Fin(0))))\n", + "acc-name: parity max odd 3\n", + "Acceptance: 3 Fin(2) & (Inf(1) | Fin(0))\n", "properties: trans-labels explicit-labels trans-acc colored complete\n", "properties: deterministic\n", "spot-state-player: 0 0 0 0 0 1 1 1 1 1 1 1\n", @@ -988,22 +984,22 @@ "[!1] 10 {1}\n", "[1] 11 {1}\n", "State: 5\n", - "[t] 0 {3}\n", + "[t] 0 {1}\n", "State: 6\n", - "[t] 1 {4}\n", + "[t] 1 {2}\n", "State: 7\n", - "[t] 0 {4}\n", + "[t] 0 {2}\n", "State: 8\n", - "[t] 2 {3}\n", + "[t] 2 {1}\n", "State: 9\n", - "[!0] 2 {3}\n", - "[0] 3 {4}\n", + "[!0] 2 {1}\n", + "[0] 3 {2}\n", "State: 10\n", - "[!0] 0 {3}\n", - "[0] 3 {3}\n", + "[!0] 0 {1}\n", + "[0] 3 {1}\n", "State: 11\n", - "[!0] 1 {3}\n", - "[0] 3 {3}\n", + "[!0] 1 {1}\n", + "[0] 3 {1}\n", "--END--\n" ] } @@ -1057,18 +1053,14 @@ " viewBox=\"0.00 0.00 566.58 353.20\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n", "\n", "\n", - "Fin(\n", - "\n", - ") & (Inf(\n", - "\n", - ") | (Fin(\n", - "\n", - ") & (Inf(\n", - "\n", - ") | Fin(\n", - "\n", - "))))\n", - "[parity max odd 5]\n", + "Fin(\n", + "\n", + ") & (Inf(\n", + "\n", + ") | Fin(\n", + "\n", + "))\n", + "[parity max odd 3]\n", "\n", "\n", "\n", @@ -1150,7 +1142,7 @@ "\n", "\n", "1\n", - "\n", + "\n", "\n", "\n", "\n", @@ -1164,7 +1156,7 @@ "\n", "\n", "1\n", - "\n", + "\n", "\n", "\n", "\n", @@ -1194,7 +1186,7 @@ "\n", "\n", "1\n", - "\n", + "\n", "\n", "\n", "\n", @@ -1222,7 +1214,7 @@ "\n", "\n", "1\n", - "\n", + "\n", "\n", "\n", "\n", @@ -1250,7 +1242,7 @@ "\n", "\n", "!b\n", - "\n", + "\n", "\n", "\n", "\n", @@ -1258,7 +1250,7 @@ "\n", "\n", "b\n", - "\n", + "\n", "\n", "\n", "\n", @@ -1266,7 +1258,7 @@ "\n", "\n", "!b\n", - "\n", + "\n", "\n", "\n", "\n", @@ -1274,7 +1266,7 @@ "\n", "\n", "b\n", - "\n", + "\n", "\n", "\n", "\n", @@ -1282,7 +1274,7 @@ "\n", "\n", "!b\n", - "\n", + "\n", "\n", "\n", "\n", @@ -1290,13 +1282,13 @@ "\n", "\n", "b\n", - "\n", + "\n", "\n", "\n", "\n" ], "text/plain": [ - " *' at 0x7f5c143fdf30> >" + " *' at 0x7f806443b1b0> >" ] }, "execution_count": 11, @@ -1307,11 +1299,18 @@ "source": [ "spot.highlight_strategy(game)" ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] } ], "metadata": { "kernelspec": { - "display_name": "Python 3", + "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, @@ -1325,7 +1324,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.7.3" + "version": "3.9.2" } }, "nbformat": 4, diff --git a/tests/python/synthesis.ipynb b/tests/python/synthesis.ipynb index e290f02b5..8c706c481 100644 --- a/tests/python/synthesis.ipynb +++ b/tests/python/synthesis.ipynb @@ -3,6 +3,7 @@ { "cell_type": "code", "execution_count": 1, + "id": "1b9b5964", "metadata": {}, "outputs": [], "source": [ @@ -13,6 +14,7 @@ }, { "cell_type": "markdown", + "id": "b465c6ba", "metadata": {}, "source": [ "This notebook presents functions that can be used to solve the Reactive Synthesis problem using games.\n", @@ -37,6 +39,7 @@ { "cell_type": "code", "execution_count": 2, + "id": "8576bdad", "metadata": {}, "outputs": [ { @@ -53,649 +56,647 @@ "\n", "\n", - "\n", "\n", - "\n", - "\n", - "\n", - "Inf(\n", - "\n", - ") | (Fin(\n", - "\n", - ") & (Inf(\n", - "\n", - ") | Fin(\n", - "\n", - ")))\n", - "[parity max odd 4]\n", + "\n", + "\n", + "\n", + "Fin(\n", + "\n", + ") & (Inf(\n", + "\n", + ") | Fin(\n", + "\n", + "))\n", + "[parity max odd 3]\n", "\n", "\n", "\n", "9\n", - "\n", - "9\n", + "\n", + "9\n", "\n", "\n", "\n", "I->9\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "25\n", - "\n", - "25\n", + "\n", + "25\n", "\n", "\n", "\n", "9->25\n", - "\n", - "\n", - "!i0 & !i1\n", - "\n", + "\n", + "\n", + "!i0 & !i1\n", + "\n", "\n", "\n", "\n", "26\n", - "\n", - "26\n", + "\n", + "26\n", "\n", "\n", "\n", "9->26\n", - "\n", - "\n", - "i0 & !i1\n", - "\n", + "\n", + "\n", + "i0 & !i1\n", + "\n", "\n", "\n", "\n", "27\n", - "\n", - "27\n", + "\n", + "27\n", "\n", "\n", "\n", "9->27\n", - "\n", - "\n", - "!i0 & i1\n", - "\n", + "\n", + "\n", + "!i0 & i1\n", + "\n", "\n", "\n", "\n", "28\n", - "\n", - "28\n", + "\n", + "28\n", "\n", "\n", "\n", "9->28\n", - "\n", - "\n", - "i0 & i1\n", - "\n", + "\n", + "\n", + "i0 & i1\n", + "\n", "\n", "\n", "\n", "0\n", - "\n", - "0\n", + "\n", + "0\n", "\n", "\n", "\n", "10\n", - "\n", - "10\n", + "\n", + "10\n", "\n", "\n", "\n", "0->10\n", - "\n", - "\n", - "!i1\n", - "\n", + "\n", + "\n", + "!i1\n", + "\n", "\n", "\n", "\n", "11\n", - "\n", - "11\n", + "\n", + "11\n", "\n", "\n", "\n", "0->11\n", - "\n", - "\n", - "i1\n", - "\n", + "\n", + "\n", + "i1\n", + "\n", "\n", "\n", "\n", "1\n", - "\n", - "1\n", + "\n", + "1\n", "\n", "\n", "\n", "10->1\n", - "\n", - "\n", - "o0\n", - "\n", + "\n", + "\n", + "o0\n", + "\n", "\n", "\n", "\n", "11->0\n", - "\n", - "\n", - "o0\n", - "\n", + "\n", + "\n", + "o0\n", + "\n", "\n", "\n", "\n", "12\n", - "\n", - "12\n", + "\n", + "12\n", "\n", "\n", "\n", "1->12\n", - "\n", - "\n", - "!i1\n", - "\n", + "\n", + "\n", + "!i1\n", + "\n", "\n", "\n", "\n", "13\n", - "\n", - "13\n", + "\n", + "13\n", "\n", "\n", "\n", "1->13\n", - "\n", - "\n", - "i1\n", - "\n", + "\n", + "\n", + "i1\n", + "\n", "\n", "\n", "\n", "12->1\n", - "\n", - "\n", - "!o0\n", - "\n", + "\n", + "\n", + "!o0\n", + "\n", "\n", "\n", "\n", "13->0\n", - "\n", - "\n", - "!o0\n", - "\n", + "\n", + "\n", + "!o0\n", + "\n", "\n", "\n", "\n", "2\n", - "\n", - "2\n", + "\n", + "2\n", "\n", "\n", "\n", "14\n", - "\n", - "14\n", + "\n", + "14\n", "\n", "\n", "\n", "2->14\n", - "\n", - "\n", - "i1\n", - "\n", + "\n", + "\n", + "i1\n", + "\n", "\n", "\n", "\n", "16\n", - "\n", - "16\n", + "\n", + "16\n", "\n", "\n", "\n", "2->16\n", - "\n", - "\n", - "!i1\n", - "\n", + "\n", + "\n", + "!i1\n", + "\n", "\n", "\n", "\n", "15\n", - "\n", - "15\n", + "\n", + "15\n", "\n", "\n", "\n", "14->15\n", - "\n", - "\n", - "1\n", - "\n", + "\n", + "\n", + "1\n", + "\n", "\n", "\n", "\n", "16->2\n", - "\n", - "\n", - "1\n", - "\n", + "\n", + "\n", + "1\n", + "\n", "\n", "\n", "\n", "3\n", - "\n", - "3\n", + "\n", + "3\n", "\n", "\n", "\n", "3->13\n", - "\n", - "\n", - "i1\n", - "\n", + "\n", + "\n", + "i1\n", + "\n", "\n", "\n", "\n", "17\n", - "\n", - "17\n", + "\n", + "17\n", "\n", "\n", "\n", "3->17\n", - "\n", - "\n", - "!i1\n", - "\n", + "\n", + "\n", + "!i1\n", + "\n", "\n", "\n", "\n", "17->2\n", - "\n", - "\n", - "o0\n", - "\n", + "\n", + "\n", + "o0\n", + "\n", "\n", "\n", "\n", "17->3\n", - "\n", - "\n", - "!o0\n", - "\n", + "\n", + "\n", + "!o0\n", + "\n", "\n", "\n", "\n", "4\n", - "\n", - "4\n", + "\n", + "4\n", "\n", "\n", "\n", "4->14\n", - "\n", - "\n", - "i0\n", - "\n", + "\n", + "\n", + "i0\n", + "\n", "\n", "\n", "\n", "18\n", - "\n", - "18\n", + "\n", + "18\n", "\n", "\n", "\n", "4->18\n", - "\n", - "\n", - "!i0\n", - "\n", + "\n", + "\n", + "!i0\n", + "\n", "\n", "\n", "\n", "18->4\n", - "\n", - "\n", - "1\n", - "\n", + "\n", + "\n", + "1\n", + "\n", "\n", "\n", "\n", "5\n", - "\n", - "5\n", + "\n", + "5\n", "\n", "\n", "\n", "5->14\n", - "\n", - "\n", - "i0 & i1\n", - "\n", + "\n", + "\n", + "i0 & i1\n", + "\n", "\n", "\n", "\n", "5->16\n", - "\n", - "\n", - "i0 & !i1\n", - "\n", + "\n", + "\n", + "i0 & !i1\n", + "\n", "\n", "\n", "\n", "5->18\n", - "\n", - "\n", - "!i0 & i1\n", - "\n", + "\n", + "\n", + "!i0 & i1\n", + "\n", "\n", "\n", "\n", "19\n", - "\n", - "19\n", + "\n", + "19\n", "\n", "\n", "\n", "5->19\n", - "\n", - "\n", - "!i0 & !i1\n", - "\n", + "\n", + "\n", + "!i0 & !i1\n", + "\n", "\n", "\n", "\n", "19->5\n", - "\n", - "\n", - "1\n", - "\n", + "\n", + "\n", + "1\n", + "\n", "\n", "\n", "\n", "6\n", - "\n", - "6\n", + "\n", + "6\n", "\n", "\n", "\n", "6->10\n", - "\n", - "\n", - "i0 & !i1\n", - "\n", + "\n", + "\n", + "i0 & !i1\n", + "\n", "\n", "\n", "\n", "6->11\n", - "\n", - "\n", - "i0 & i1\n", - "\n", + "\n", + "\n", + "i0 & i1\n", + "\n", "\n", "\n", "\n", "20\n", - "\n", - "20\n", + "\n", + "20\n", "\n", "\n", "\n", "6->20\n", - "\n", - "\n", - "!i0 & !i1\n", - "\n", + "\n", + "\n", + "!i0 & !i1\n", + "\n", "\n", "\n", "\n", "21\n", - "\n", - "21\n", + "\n", + "21\n", "\n", "\n", "\n", "6->21\n", - "\n", - "\n", - "!i0 & i1\n", - "\n", + "\n", + "\n", + "!i0 & i1\n", + "\n", "\n", "\n", "\n", "20->4\n", - "\n", - "\n", - "!o0\n", - "\n", + "\n", + "\n", + "!o0\n", + "\n", "\n", "\n", "\n", "7\n", - "\n", - "7\n", + "\n", + "7\n", "\n", "\n", "\n", "20->7\n", - "\n", - "\n", - "o0\n", - "\n", + "\n", + "\n", + "o0\n", + "\n", "\n", "\n", "\n", "21->4\n", - "\n", - "\n", - "!o0\n", - "\n", + "\n", + "\n", + "!o0\n", + "\n", "\n", "\n", "\n", "21->6\n", - "\n", - "\n", - "o0\n", - "\n", + "\n", + "\n", + "o0\n", + "\n", "\n", "\n", "\n", "7->12\n", - "\n", - "\n", - "i0 & !i1\n", - "\n", + "\n", + "\n", + "i0 & !i1\n", + "\n", "\n", "\n", "\n", "7->13\n", - "\n", - "\n", - "i0 & i1\n", - "\n", + "\n", + "\n", + "i0 & i1\n", + "\n", "\n", "\n", "\n", "22\n", - "\n", - "22\n", + "\n", + "22\n", "\n", "\n", "\n", "7->22\n", - "\n", - "\n", - "!i0 & !i1\n", - "\n", + "\n", + "\n", + "!i0 & !i1\n", + "\n", "\n", "\n", "\n", "23\n", - "\n", - "23\n", + "\n", + "23\n", "\n", "\n", "\n", "7->23\n", - "\n", - "\n", - "!i0 & i1\n", - "\n", + "\n", + "\n", + "!i0 & i1\n", + "\n", "\n", "\n", "\n", "22->4\n", - "\n", - "\n", - "o0\n", - "\n", + "\n", + "\n", + "o0\n", + "\n", "\n", "\n", "\n", "22->7\n", - "\n", - "\n", - "!o0\n", - "\n", + "\n", + "\n", + "!o0\n", + "\n", "\n", "\n", "\n", "23->4\n", - "\n", - "\n", - "o0\n", - "\n", + "\n", + "\n", + "o0\n", + "\n", "\n", "\n", "\n", "23->6\n", - "\n", - "\n", - "!o0\n", - "\n", + "\n", + "\n", + "!o0\n", + "\n", "\n", "\n", "\n", "8\n", - "\n", - "8\n", + "\n", + "8\n", "\n", "\n", "\n", "8->13\n", - "\n", - "\n", - "i0 & i1\n", - "\n", + "\n", + "\n", + "i0 & i1\n", + "\n", "\n", "\n", "\n", "8->17\n", - "\n", - "\n", - "i0 & !i1\n", - "\n", + "\n", + "\n", + "i0 & !i1\n", + "\n", "\n", "\n", "\n", "8->23\n", - "\n", - "\n", - "!i0 & i1\n", - "\n", + "\n", + "\n", + "!i0 & i1\n", + "\n", "\n", "\n", "\n", "24\n", - "\n", - "24\n", + "\n", + "24\n", "\n", "\n", "\n", "8->24\n", - "\n", - "\n", - "!i0 & !i1\n", - "\n", + "\n", + "\n", + "!i0 & !i1\n", + "\n", "\n", "\n", "\n", "24->5\n", - "\n", - "\n", - "o0\n", - "\n", + "\n", + "\n", + "o0\n", + "\n", "\n", "\n", "\n", "24->8\n", - "\n", - "\n", - "!o0\n", - "\n", + "\n", + "\n", + "!o0\n", + "\n", "\n", "\n", "\n", "25->8\n", - "\n", - "\n", - "1\n", - "\n", + "\n", + "\n", + "1\n", + "\n", "\n", "\n", "\n", "26->3\n", - "\n", - "\n", - "1\n", - "\n", + "\n", + "\n", + "1\n", + "\n", "\n", "\n", "\n", "27->6\n", - "\n", - "\n", - "1\n", - "\n", + "\n", + "\n", + "1\n", + "\n", "\n", "\n", "\n", "28->0\n", - "\n", - "\n", - "1\n", - "\n", + "\n", + "\n", + "1\n", + "\n", "\n", "\n", "\n", "15->14\n", - "\n", - "\n", - "1\n", - "\n", + "\n", + "\n", + "1\n", + "\n", "\n", "\n", "\n" ], "text/plain": [ - " *' at 0x7f44ac249ed0> >" + " *' at 0x7fc5ec2bf930> >" ] }, "metadata": {}, @@ -714,6 +715,7 @@ }, { "cell_type": "markdown", + "id": "fbd7095b", "metadata": {}, "source": [ "Solving the game, is done with `solve_game()` as with any game. There is also a version that takes a `synthesis_info` as second argument in case the time it takes has to be recorded. Here passing `si` or not makes no difference." @@ -722,6 +724,7 @@ { "cell_type": "code", "execution_count": 3, + "id": "02d7525e", "metadata": {}, "outputs": [ { @@ -737,588 +740,586 @@ "\n", "\n", - "\n", "\n", "\n", - "\n", - "\n", - "Inf(\n", - "\n", - ") | (Fin(\n", - "\n", - ") & (Inf(\n", - "\n", - ") | Fin(\n", - "\n", - ")))\n", - "[parity max odd 4]\n", + " viewBox=\"0.00 0.00 650.45 360.00\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n", + "\n", + "\n", + "Fin(\n", + "\n", + ") & (Inf(\n", + "\n", + ") | Fin(\n", + "\n", + "))\n", + "[parity max odd 3]\n", "\n", "\n", "\n", "9\n", - "\n", - "9\n", + "\n", + "9\n", "\n", "\n", "\n", "I->9\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "25\n", - "\n", - "25\n", + "\n", + "25\n", "\n", "\n", "\n", "9->25\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "26\n", - "\n", - "26\n", + "\n", + "26\n", "\n", "\n", "\n", "9->26\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "27\n", - "\n", - "27\n", + "\n", + "27\n", "\n", "\n", "\n", "9->27\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "28\n", - "\n", - "28\n", + "\n", + "28\n", "\n", "\n", "\n", "9->28\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "0\n", - "\n", - "0\n", + "\n", + "0\n", "\n", "\n", "\n", "10\n", - "\n", - "10\n", + "\n", + "10\n", "\n", "\n", "\n", "0->10\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "11\n", - "\n", - "11\n", + "\n", + "11\n", "\n", "\n", "\n", "0->11\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "1\n", - "\n", - "1\n", + "\n", + "1\n", "\n", "\n", "\n", "10->1\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "11->0\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "12\n", - "\n", - "12\n", + "\n", + "12\n", "\n", "\n", "\n", "1->12\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "13\n", - "\n", - "13\n", + "\n", + "13\n", "\n", "\n", "\n", "1->13\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "12->1\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "13->0\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "2\n", - "\n", - "2\n", + "\n", + "2\n", "\n", "\n", "\n", "14\n", - "\n", - "14\n", + "\n", + "14\n", "\n", "\n", "\n", "2->14\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "16\n", - "\n", - "16\n", + "\n", + "16\n", "\n", "\n", "\n", "2->16\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "15\n", - "\n", - "15\n", + "\n", + "15\n", "\n", "\n", "\n", "14->15\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "16->2\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "3\n", - "\n", - "3\n", + "\n", + "3\n", "\n", "\n", "\n", "3->13\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "17\n", - "\n", - "17\n", + "\n", + "17\n", "\n", "\n", "\n", "3->17\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "17->2\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "17->3\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "4\n", - "\n", - "4\n", + "\n", + "4\n", "\n", "\n", "\n", "4->14\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "18\n", - "\n", - "18\n", + "\n", + "18\n", "\n", "\n", "\n", "4->18\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "18->4\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "5\n", - "\n", - "5\n", + "\n", + "5\n", "\n", "\n", "\n", "5->14\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "5->16\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "5->18\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "19\n", - "\n", - "19\n", + "\n", + "19\n", "\n", "\n", "\n", "5->19\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "19->5\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "6\n", - "\n", - "6\n", + "\n", + "6\n", "\n", "\n", "\n", "6->10\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "6->11\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "20\n", - "\n", - "20\n", + "\n", + "20\n", "\n", "\n", "\n", "6->20\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "21\n", - "\n", - "21\n", + "\n", + "21\n", "\n", "\n", "\n", "6->21\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "20->4\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "7\n", - "\n", - "7\n", + "\n", + "7\n", "\n", "\n", "\n", "20->7\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "21->4\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "21->6\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "7->12\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "7->13\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "22\n", - "\n", - "22\n", + "\n", + "22\n", "\n", "\n", "\n", "7->22\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "23\n", - "\n", - "23\n", + "\n", + "23\n", "\n", "\n", "\n", "7->23\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "22->4\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "22->7\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "23->4\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "23->6\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "8\n", - "\n", - "8\n", + "\n", + "8\n", "\n", "\n", "\n", "8->13\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "8->17\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "8->23\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "24\n", - "\n", - "24\n", + "\n", + "24\n", "\n", "\n", "\n", "8->24\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "24->5\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "24->8\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "25->8\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "26->3\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "27->6\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "28->0\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "15->14\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n" @@ -1340,6 +1341,7 @@ }, { "cell_type": "markdown", + "id": "d66f3da1", "metadata": {}, "source": [ "Once a strategy has been found, it can be extracted as an automaton and simplified using 6 different levels (the default is 2). The output should be interpreted as a Mealy automaton, where transition have the form `(ins)&(outs)` where `ins` and `outs` are Boolean formulas representing possible inputs and outputs (they could be more than just conjunctions of atomic proposition). Mealy machines with this type of labels are called \"separated\" in Spot." @@ -1348,6 +1350,7 @@ { "cell_type": "code", "execution_count": 4, + "id": "89342e18", "metadata": {}, "outputs": [ { @@ -1363,303 +1366,303 @@ "\n", "\n", - "\n", "\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "0\n", - "\n", - "0\n", + "\n", + "0\n", "\n", "\n", "\n", "I->0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "1\n", - "\n", - "1\n", + "\n", + "1\n", "\n", "\n", "\n", "0->1\n", - "\n", - "\n", - "\n", - "!i0 & !i1\n", - "/\n", - "\n", - "1\n", + "\n", + "\n", + "\n", + "!i0 & !i1\n", + "/\n", + "\n", + "1\n", "\n", "\n", "\n", "2\n", - "\n", - "2\n", + "\n", + "2\n", "\n", "\n", "\n", "0->2\n", - "\n", - "\n", - "\n", - "i0 & !i1\n", - "/\n", - "\n", - "1\n", + "\n", + "\n", + "\n", + "i0 & !i1\n", + "/\n", + "\n", + "1\n", "\n", "\n", "\n", "3\n", - "\n", - "3\n", + "\n", + "3\n", "\n", "\n", "\n", "0->3\n", - "\n", - "\n", - "\n", - "!i0 & i1\n", - "/\n", - "\n", - "1\n", + "\n", + "\n", + "\n", + "!i0 & i1\n", + "/\n", + "\n", + "1\n", "\n", "\n", "\n", "4\n", - "\n", - "4\n", + "\n", + "4\n", "\n", "\n", "\n", "0->4\n", - "\n", - "\n", - "\n", - "i0 & i1\n", - "/\n", - "\n", - "1\n", + "\n", + "\n", + "\n", + "i0 & i1\n", + "/\n", + "\n", + "1\n", "\n", "\n", "\n", "1->1\n", - "\n", - "\n", - "\n", - "!i0 & !i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "!i0 & !i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "1->2\n", - "\n", - "\n", - "\n", - "i0 & !i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "i0 & !i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "1->3\n", - "\n", - "\n", - "\n", - "!i0 & i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "!i0 & i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "1->4\n", - "\n", - "\n", - "\n", - "i0 & i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "i0 & i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "2->2\n", - "\n", - "\n", - "\n", - "!i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "!i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "2->4\n", - "\n", - "\n", - "\n", - "i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "3->3\n", - "\n", - "\n", - "\n", - "!i0 & i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "!i0 & i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "3->4\n", - "\n", - "\n", - "\n", - "i0 & i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "i0 & i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "5\n", - "\n", - "5\n", + "\n", + "5\n", "\n", "\n", "\n", "3->5\n", - "\n", - "\n", - "\n", - "i0 & !i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "i0 & !i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "6\n", - "\n", - "6\n", + "\n", + "6\n", "\n", "\n", "\n", "3->6\n", - "\n", - "\n", - "\n", - "!i0 & !i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "!i0 & !i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "4->4\n", - "\n", - "\n", - "\n", - "i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "4->5\n", - "\n", - "\n", - "\n", - "!i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "!i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "5->4\n", - "\n", - "\n", - "\n", - "i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "5->5\n", - "\n", - "\n", - "\n", - "!i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "!i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "6->3\n", - "\n", - "\n", - "\n", - "!i0 & i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "!i0 & i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "6->4\n", - "\n", - "\n", - "\n", - "i0 & i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "i0 & i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "6->5\n", - "\n", - "\n", - "\n", - "i0 & !i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "i0 & !i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "6->6\n", - "\n", - "\n", - "\n", - "!i0 & !i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "!i0 & !i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n" @@ -1684,169 +1687,169 @@ "\n", "\n", - "\n", "\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "0\n", - "\n", - "0\n", + "\n", + "0\n", "\n", "\n", "\n", "I->0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "1\n", - "\n", - "1\n", + "\n", + "1\n", "\n", "\n", "\n", "0->1\n", - "\n", - "\n", - "\n", - "!i0 & !i1\n", - "/\n", - "\n", - "1\n", + "\n", + "\n", + "\n", + "!i0 & !i1\n", + "/\n", + "\n", + "1\n", "\n", "\n", "\n", "0->1\n", - "\n", - "\n", - "\n", - "i0 & !i1\n", - "/\n", - "\n", - "1\n", + "\n", + "\n", + "\n", + "i0 & !i1\n", + "/\n", + "\n", + "1\n", "\n", "\n", "\n", "2\n", - "\n", - "2\n", + "\n", + "2\n", "\n", "\n", "\n", "0->2\n", - "\n", - "\n", - "\n", - "!i0 & i1\n", - "/\n", - "\n", - "1\n", + "\n", + "\n", + "\n", + "!i0 & i1\n", + "/\n", + "\n", + "1\n", "\n", "\n", "\n", "0->2\n", - "\n", - "\n", - "\n", - "i0 & i1\n", - "/\n", - "\n", - "1\n", + "\n", + "\n", + "\n", + "i0 & i1\n", + "/\n", + "\n", + "1\n", "\n", "\n", "\n", "1->1\n", - "\n", - "\n", - "\n", - "i0 & !i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "i0 & !i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "1->1\n", - "\n", - "\n", - "\n", - "!i0 & !i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "!i0 & !i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "1->2\n", - "\n", - "\n", - "\n", - "i0 & i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "i0 & i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "1->2\n", - "\n", - "\n", - "\n", - "!i0 & i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "!i0 & i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "2->1\n", - "\n", - "\n", - "\n", - "i0 & !i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "i0 & !i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "2->1\n", - "\n", - "\n", - "\n", - "!i0 & !i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "!i0 & !i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "2->2\n", - "\n", - "\n", - "\n", - "i0 & i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "i0 & i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "2->2\n", - "\n", - "\n", - "\n", - "!i0 & i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "!i0 & i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n" @@ -1871,119 +1874,119 @@ "\n", "\n", - "\n", "\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "1\n", - "\n", - "1\n", + "\n", + "1\n", "\n", "\n", "\n", "I->1\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "1->1\n", - "\n", - "\n", - "\n", - "i0 & i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "i0 & i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "1->1\n", - "\n", - "\n", - "\n", - "!i0 & i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "!i0 & i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "0\n", - "\n", - "0\n", + "\n", + "0\n", "\n", "\n", "\n", "1->0\n", - "\n", - "\n", - "\n", - "i0 & !i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "i0 & !i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "1->0\n", - "\n", - "\n", - "\n", - "!i0 & !i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "!i0 & !i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "0->1\n", - "\n", - "\n", - "\n", - "i0 & i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "i0 & i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "0->1\n", - "\n", - "\n", - "\n", - "!i0 & i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "!i0 & i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "0->0\n", - "\n", - "\n", - "\n", - "i0 & !i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "i0 & !i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "0->0\n", - "\n", - "\n", - "\n", - "!i0 & !i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "!i0 & !i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n" @@ -2008,75 +2011,75 @@ "\n", "\n", - "\n", "\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "0\n", - "\n", - "0\n", + "\n", + "0\n", "\n", "\n", "\n", "I->0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "0->0\n", - "\n", - "\n", - "\n", - "i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "1\n", - "\n", - "1\n", + "\n", + "1\n", "\n", "\n", "\n", "0->1\n", - "\n", - "\n", - "\n", - "!i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "!i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "1->0\n", - "\n", - "\n", - "\n", - "i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "1->1\n", - "\n", - "\n", - "\n", - "!i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "!i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n" @@ -2101,75 +2104,75 @@ "\n", "\n", - "\n", "\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "0\n", - "\n", - "0\n", + "\n", + "0\n", "\n", "\n", "\n", "I->0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "0->0\n", - "\n", - "\n", - "\n", - "!i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "!i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "1\n", - "\n", - "1\n", + "\n", + "1\n", "\n", "\n", "\n", "0->1\n", - "\n", - "\n", - "\n", - "i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "1->0\n", - "\n", - "\n", - "\n", - "!i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "!i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "1->1\n", - "\n", - "\n", - "\n", - "i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n" @@ -2194,119 +2197,119 @@ "\n", "\n", - "\n", "\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "0\n", - "\n", - "0\n", + "\n", + "0\n", "\n", "\n", "\n", "I->0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "0->0\n", - "\n", - "\n", - "\n", - "i0 & i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "i0 & i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "0->0\n", - "\n", - "\n", - "\n", - "!i0 & i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "!i0 & i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "1\n", - "\n", - "1\n", + "\n", + "1\n", "\n", "\n", "\n", "0->1\n", - "\n", - "\n", - "\n", - "i0 & !i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "i0 & !i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "0->1\n", - "\n", - "\n", - "\n", - "!i0 & !i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "!i0 & !i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "1->0\n", - "\n", - "\n", - "\n", - "i0 & i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "i0 & i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "1->0\n", - "\n", - "\n", - "\n", - "!i0 & i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "!i0 & i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "1->1\n", - "\n", - "\n", - "\n", - "i0 & !i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "i0 & !i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "1->1\n", - "\n", - "\n", - "\n", - "!i0 & !i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "!i0 & !i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n" @@ -2345,6 +2348,7 @@ }, { "cell_type": "markdown", + "id": "435c9bae", "metadata": {}, "source": [ "If needed, a separated Mealy machine can be turned into game shape using `split_sepearated_mealy()`, which is more efficient than `split_2step()`." @@ -2353,6 +2357,7 @@ { "cell_type": "code", "execution_count": 5, + "id": "688a1ced", "metadata": {}, "outputs": [ { @@ -2361,260 +2366,260 @@ "
\n", "\n", - "\n", "\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "0\n", - "\n", - "0\n", + "\n", + "0\n", "\n", "\n", "\n", "I->0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "0->0\n", - "\n", - "\n", - "\n", - "i0 & i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "i0 & i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "0->0\n", - "\n", - "\n", - "\n", - "!i0 & i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "!i0 & i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "1\n", - "\n", - "1\n", + "\n", + "1\n", "\n", "\n", "\n", "0->1\n", - "\n", - "\n", - "\n", - "i0 & !i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "i0 & !i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "0->1\n", - "\n", - "\n", - "\n", - "!i0 & !i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "!i0 & !i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "1->0\n", - "\n", - "\n", - "\n", - "i0 & i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "i0 & i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "1->0\n", - "\n", - "\n", - "\n", - "!i0 & i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "!i0 & i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "1->1\n", - "\n", - "\n", - "\n", - "i0 & !i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "i0 & !i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "1->1\n", - "\n", - "\n", - "\n", - "!i0 & !i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "!i0 & !i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "
\n", "\n", - "\n", "\n", - "\n", - "\n", - "\n", - "t\n", - "[all]\n", + "\n", + "\n", + "\n", + "t\n", + "[all]\n", "\n", "\n", "\n", "0\n", - "\n", - "0\n", + "\n", + "0\n", "\n", "\n", "\n", "I->0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "2\n", - "\n", - "2\n", + "\n", + "2\n", "\n", "\n", "\n", "0->2\n", - "\n", - "\n", - "i0 & !i1\n", + "\n", + "\n", + "i0 & !i1\n", "\n", "\n", "\n", "0->2\n", - "\n", - "\n", - "!i0 & !i1\n", + "\n", + "\n", + "!i0 & !i1\n", "\n", "\n", "\n", "3\n", - "\n", - "3\n", + "\n", + "3\n", "\n", "\n", "\n", "0->3\n", - "\n", - "\n", - "i0 & i1\n", + "\n", + "\n", + "i0 & i1\n", "\n", "\n", "\n", "0->3\n", - "\n", - "\n", - "!i0 & i1\n", + "\n", + "\n", + "!i0 & i1\n", "\n", "\n", "\n", "1\n", - "\n", - "1\n", + "\n", + "1\n", "\n", "\n", "\n", "2->1\n", - "\n", - "\n", - "o0\n", + "\n", + "\n", + "o0\n", "\n", "\n", "\n", "3->0\n", - "\n", - "\n", - "o0\n", + "\n", + "\n", + "o0\n", "\n", "\n", "\n", "4\n", - "\n", - "4\n", + "\n", + "4\n", "\n", "\n", "\n", "1->4\n", - "\n", - "\n", - "i0 & i1\n", + "\n", + "\n", + "i0 & i1\n", "\n", "\n", "\n", "1->4\n", - "\n", - "\n", - "!i0 & i1\n", + "\n", + "\n", + "!i0 & i1\n", "\n", "\n", "\n", "5\n", - "\n", - "5\n", + "\n", + "5\n", "\n", "\n", "\n", "1->5\n", - "\n", - "\n", - "i0 & !i1\n", + "\n", + "\n", + "i0 & !i1\n", "\n", "\n", "\n", "1->5\n", - "\n", - "\n", - "!i0 & !i1\n", + "\n", + "\n", + "!i0 & !i1\n", "\n", "\n", "\n", "4->0\n", - "\n", - "\n", - "!o0\n", + "\n", + "\n", + "!o0\n", "\n", "\n", "\n", "5->1\n", - "\n", - "\n", - "!o0\n", + "\n", + "\n", + "!o0\n", "\n", "\n", "\n", @@ -2634,6 +2639,7 @@ }, { "cell_type": "markdown", + "id": "e3bb2d7d", "metadata": {}, "source": [ "# Converting the separated mealy machine to AIG\n", @@ -2646,6 +2652,7 @@ { "cell_type": "code", "execution_count": 6, + "id": "b5fea2d1", "metadata": {}, "outputs": [ { @@ -2654,60 +2661,60 @@ "\n", "\n", - "\n", "\n", - "\n", + "\n", "\n", - "\n", + "\n", "\n", "\n", "6\n", - "\n", - "L0_out\n", + "\n", + "L0_out\n", "\n", "\n", "\n", "o0\n", - "\n", - "o0\n", + "\n", + "o0\n", "\n", "\n", "\n", "6->o0:s\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "L0\n", - "\n", - "L0_in\n", + "\n", + "L0_in\n", "\n", "\n", "\n", "2\n", - "\n", - "i1\n", + "\n", + "i1\n", "\n", "\n", "\n", "2->L0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "4\n", - "\n", - "i0\n", + "\n", + "i0\n", "\n", "\n", "\n" ], "text/plain": [ - " >" + " >" ] }, "metadata": {}, @@ -2721,6 +2728,7 @@ }, { "cell_type": "markdown", + "id": "2a1a6fc9", "metadata": {}, "source": [ "While we are at it, let us mention that you can render those circuits horizontally as follows:" @@ -2729,6 +2737,7 @@ { "cell_type": "code", "execution_count": 7, + "id": "f909d578", "metadata": {}, "outputs": [ { @@ -2737,54 +2746,54 @@ "\n", "\n", - "\n", "\n", - "\n", + "\n", "\n", - "\n", + "\n", "\n", "\n", "6\n", - "\n", - "L0_out\n", + "\n", + "L0_out\n", "\n", "\n", "\n", "o0\n", - "\n", - "o0\n", + "\n", + "o0\n", "\n", "\n", "\n", "6->o0:w\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "L0\n", - "\n", - "L0_in\n", + "\n", + "L0_in\n", "\n", "\n", "\n", "2\n", - "\n", - "i1\n", + "\n", + "i1\n", "\n", "\n", "\n", "2->L0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "4\n", - "\n", - "i0\n", + "\n", + "i0\n", "\n", "\n", "\n" @@ -2804,6 +2813,7 @@ }, { "cell_type": "markdown", + "id": "2c87313f", "metadata": {}, "source": [ "To encode the circuit in the AIGER format (ASCII version) use:" @@ -2812,6 +2822,7 @@ { "cell_type": "code", "execution_count": 8, + "id": "1b787f50", "metadata": {}, "outputs": [ { @@ -2835,6 +2846,7 @@ }, { "cell_type": "markdown", + "id": "72038258", "metadata": {}, "source": [ "# Adding more inputs and outputs by force" @@ -2842,6 +2854,7 @@ }, { "cell_type": "markdown", + "id": "3fbb3c2f", "metadata": {}, "source": [ "It can happen that propositions declared as output are ommited in the aig circuit (either because they are not part of the specification, or because they do not appear in the winning strategy). In that case those \n", @@ -2853,6 +2866,7 @@ { "cell_type": "code", "execution_count": 9, + "id": "8ed4e382", "metadata": {}, "outputs": [ { @@ -2861,167 +2875,159 @@ "\n", "\n", - "\n", "\n", - "\n", - "\n", - "\n", - "Inf(\n", - "\n", - ") | (Fin(\n", - "\n", - ") & (Inf(\n", - "\n", - ") | (Fin(\n", - "\n", - ") & (Inf(\n", - "\n", - ") | Fin(\n", - "\n", - ")))))\n", - "[parity max odd 6]\n", + "\n", + "\n", + "\n", + "Inf(\n", + "\n", + ") | Fin(\n", + "\n", + ")\n", + "[Streett 1]\n", "\n", "\n", "\n", "3\n", - "\n", - "3\n", + "\n", + "3\n", "\n", "\n", "\n", "I->3\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "6\n", - "\n", - "6\n", + "\n", + "6\n", "\n", "\n", "\n", "3->6\n", - "\n", - "\n", - "i0\n", - "\n", + "\n", + "\n", + "i0\n", + "\n", "\n", "\n", "\n", "7\n", - "\n", - "7\n", + "\n", + "7\n", "\n", "\n", "\n", "3->7\n", - "\n", - "\n", - "!i0\n", - "\n", + "\n", + "\n", + "!i0\n", + "\n", "\n", "\n", "\n", "0\n", - "\n", - "0\n", + "\n", + "0\n", "\n", "\n", "\n", "4\n", - "\n", - "4\n", + "\n", + "4\n", "\n", "\n", "\n", "0->4\n", - "\n", - "\n", - "1\n", - "\n", + "\n", + "\n", + "1\n", + "\n", "\n", "\n", "\n", "4->0\n", - "\n", - "\n", - "1\n", - "\n", + "\n", + "\n", + "1\n", + "\n", "\n", "\n", "\n", "1\n", - "\n", - "1\n", + "\n", + "1\n", "\n", "\n", "\n", "5\n", - "\n", - "5\n", + "\n", + "5\n", "\n", "\n", "\n", "1->5\n", - "\n", - "\n", - "1\n", - "\n", + "\n", + "\n", + "1\n", + "\n", "\n", "\n", "\n", "5->1\n", - "\n", - "\n", - "!o0\n", - "\n", + "\n", + "\n", + "!o0\n", + "\n", "\n", "\n", "\n", "2\n", - "\n", - "2\n", + "\n", + "2\n", "\n", "\n", "\n", "2->6\n", - "\n", - "\n", - "1\n", - "\n", + "\n", + "\n", + "1\n", + "\n", "\n", "\n", "\n", "6->0\n", - "\n", - "\n", - "o0\n", - "\n", + "\n", + "\n", + "o0\n", + "\n", "\n", "\n", "\n", "6->2\n", - "\n", - "\n", - "!o0\n", - "\n", + "\n", + "\n", + "!o0\n", + "\n", "\n", "\n", "\n", "7->1\n", - "\n", - "\n", - "!o0\n", - "\n", + "\n", + "\n", + "!o0\n", + "\n", "\n", "\n", "\n" ], "text/plain": [ - " *' at 0x7f44ac1d6750> >" + " *' at 0x7fc5dc8463f0> >" ] }, "metadata": {}, @@ -3033,70 +3039,70 @@ "\n", "\n", - "\n", "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "0\n", - "\n", - "0\n", + "\n", + "0\n", "\n", "\n", "\n", "I->0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "0->0\n", - "\n", - "\n", - "\n", - "i0\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "i0\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "1\n", - "\n", - "1\n", + "\n", + "1\n", "\n", "\n", "\n", "0->1\n", - "\n", - "\n", + "\n", + "\n", "\n", - "!i0\n", - "/\n", + "!i0\n", + "/\n", "\n", - "!o0\n", + "!o0\n", "\n", "\n", "\n", "1->1\n", - "\n", - "\n", + "\n", + "\n", "\n", - "1\n", - "/\n", + "1\n", + "/\n", "\n", - "!o0\n", + "!o0\n", "\n", "\n", "\n" ], "text/plain": [ - " *' at 0x7f44ac249ed0> >" + " *' at 0x7fc5ec2bf990> >" ] }, "metadata": {}, @@ -3108,72 +3114,72 @@ "\n", "\n", - "\n", "\n", "\n", + " viewBox=\"0.00 0.00 142.70 352.00\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n", "\n", - "\n", + "\n", "\n", "\n", "4\n", - "\n", - "L0_out\n", + "\n", + "L0_out\n", "\n", "\n", "\n", "6\n", - "\n", - "6\n", + "\n", + "6\n", "\n", "\n", "\n", "4->6\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "L0\n", - "\n", - "L0_in\n", + "\n", + "L0_in\n", "\n", "\n", "\n", "6->L0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "o0\n", - "\n", - "o0\n", + "\n", + "o0\n", "\n", "\n", "\n", "6->o0:s\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "2\n", - "\n", - "i0\n", + "\n", + "i0\n", "\n", "\n", "\n", "2->6\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n" ], "text/plain": [ - " >" + " >" ] }, "metadata": {}, @@ -3193,6 +3199,7 @@ }, { "cell_type": "markdown", + "id": "a82ca6c6", "metadata": {}, "source": [ "To force the presence of extra variables in the circuit, they can be passed to `mealy_machine_to_aig()`." @@ -3201,6 +3208,7 @@ { "cell_type": "code", "execution_count": 10, + "id": "a86436a7", "metadata": {}, "outputs": [ { @@ -3209,96 +3217,96 @@ "\n", "\n", - "\n", "\n", - "\n", + "\n", "\n", - "\n", + "\n", "\n", "\n", "6\n", - "\n", - "L0_out\n", + "\n", + "L0_out\n", "\n", "\n", "\n", "8\n", - "\n", - "8\n", + "\n", + "8\n", "\n", "\n", "\n", "6->8\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "L0\n", - "\n", - "L0_in\n", + "\n", + "L0_in\n", "\n", "\n", "\n", "8->L0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "o0\n", - "\n", - "o0\n", + "\n", + "o0\n", "\n", "\n", "\n", "8->o0:s\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "o1\n", - "\n", - "o1\n", + "\n", + "o1\n", "\n", "\n", "\n", "2\n", - "\n", - "i0\n", + "\n", + "i0\n", "\n", "\n", "\n", "2->8\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "4\n", - "\n", - "i1\n", + "\n", + "i1\n", "\n", "\n", "\n", "0\n", - "\n", - "False\n", + "\n", + "False\n", "\n", "\n", "\n", "0->o1:s\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n" ], "text/plain": [ - " >" + " >" ] }, "metadata": {}, @@ -3311,6 +3319,7 @@ }, { "cell_type": "markdown", + "id": "9af5c9b9", "metadata": {}, "source": [ "# Combining Mealy machines\n", @@ -3325,6 +3334,7 @@ { "cell_type": "code", "execution_count": 11, + "id": "750e55f5", "metadata": {}, "outputs": [ { @@ -3340,158 +3350,150 @@ "
\n", "\n", - "\n", "\n", - "\n", - "\n", - "\n", - "Inf(\n", - "\n", - ") | (Fin(\n", - "\n", - ") & (Inf(\n", - "\n", - ") | Fin(\n", - "\n", - ")))\n", - "[parity max odd 4]\n", + "\n", + "\n", + "\n", + "Inf(\n", + "\n", + ") | Fin(\n", + "\n", + ")\n", + "[Streett 1]\n", "\n", "\n", "\n", "0\n", - "\n", - "0\n", + "\n", + "0\n", "\n", "\n", "\n", "I->0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "1\n", - "\n", - "1\n", + "\n", + "1\n", "\n", "\n", "\n", "0->1\n", - "\n", - "\n", - "(!i0 & !i1) | (i0 & i1)\n", - "\n", + "\n", + "\n", + "(!i0 & !i1) | (i0 & i1)\n", + "\n", "\n", "\n", "\n", "2\n", - "\n", - "2\n", + "\n", + "2\n", "\n", "\n", "\n", "0->2\n", - "\n", - "\n", - "(!i0 & i1) | (i0 & !i1)\n", - "\n", + "\n", + "\n", + "(!i0 & i1) | (i0 & !i1)\n", + "\n", "\n", "\n", "\n", "1->0\n", - "\n", - "\n", - "!o0\n", - "\n", + "\n", + "\n", + "!o0\n", + "\n", "\n", "\n", "\n", "2->0\n", - "\n", - "\n", - "o0\n", - "\n", + "\n", + "\n", + "o0\n", + "\n", "\n", "\n", "\n", "
\n", "\n", - "\n", "\n", - "\n", - "\n", - "\n", - "Inf(\n", - "\n", - ") | (Fin(\n", - "\n", - ") & (Inf(\n", - "\n", - ") | Fin(\n", - "\n", - ")))\n", - "[parity max odd 4]\n", + "\n", + "\n", + "\n", + "Inf(\n", + "\n", + ") | Fin(\n", + "\n", + ")\n", + "[Streett 1]\n", "\n", "\n", "\n", "0\n", - "\n", - "0\n", + "\n", + "0\n", "\n", "\n", "\n", "I->0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "1\n", - "\n", - "1\n", + "\n", + "1\n", "\n", "\n", "\n", "0->1\n", - "\n", - "\n", - "(!i0 & !i1) | (i0 & i1)\n", - "\n", + "\n", + "\n", + "(!i0 & !i1) | (i0 & i1)\n", + "\n", "\n", "\n", "\n", "2\n", - "\n", - "2\n", + "\n", + "2\n", "\n", "\n", "\n", "0->2\n", - "\n", - "\n", - "(!i0 & i1) | (i0 & !i1)\n", - "\n", + "\n", + "\n", + "(!i0 & i1) | (i0 & !i1)\n", + "\n", "\n", "\n", "\n", "1->0\n", - "\n", - "\n", - "o1\n", - "\n", + "\n", + "\n", + "o1\n", + "\n", "\n", "\n", "\n", "2->0\n", - "\n", - "\n", - "!o1\n", - "\n", + "\n", + "\n", + "!o1\n", + "\n", "\n", "\n", "\n", @@ -3517,94 +3519,94 @@ "
\n", "\n", - "\n", "\n", - "\n", + "\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "0\n", - "\n", - "0\n", + "\n", + "0\n", "\n", "\n", "\n", "I->0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "0->0\n", - "\n", - "\n", - "\n", - "(!i0 & !i1) | (i0 & i1)\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "(!i0 & !i1) | (i0 & i1)\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "0->0\n", - "\n", - "\n", - "\n", - "(!i0 & i1) | (i0 & !i1)\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "(!i0 & i1) | (i0 & !i1)\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "
\n", "\n", - "\n", "\n", - "\n", + "\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "0\n", - "\n", - "0\n", + "\n", + "0\n", "\n", "\n", "\n", "I->0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "0->0\n", - "\n", - "\n", - "\n", - "(!i0 & !i1) | (i0 & i1)\n", - "/\n", - "\n", - "o1\n", + "\n", + "\n", + "\n", + "(!i0 & !i1) | (i0 & i1)\n", + "/\n", + "\n", + "o1\n", "\n", "\n", "\n", "0->0\n", - "\n", - "\n", - "\n", - "(!i0 & i1) | (i0 & !i1)\n", - "/\n", - "\n", - "!o1\n", + "\n", + "\n", + "\n", + "(!i0 & i1) | (i0 & !i1)\n", + "/\n", + "\n", + "!o1\n", "\n", "\n", "\n", @@ -3630,108 +3632,108 @@ "\n", "\n", - "\n", "\n", "\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "6\n", - "\n", - "6\n", + "\n", + "6\n", "\n", "\n", "\n", "10\n", - "\n", - "10\n", + "\n", + "10\n", "\n", "\n", "\n", "6->10\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "8\n", - "\n", - "8\n", + "\n", + "8\n", "\n", "\n", "\n", "8->10\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "o0\n", - "\n", - "o0\n", + "\n", + "o0\n", "\n", "\n", "\n", "10->o0:s\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "o1\n", - "\n", - "o1\n", + "\n", + "o1\n", "\n", "\n", "\n", "10->o1:s\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "2\n", - "\n", - "i0\n", + "\n", + "i0\n", "\n", "\n", "\n", "2->6\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "2->8\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "4\n", - "\n", - "i1\n", + "\n", + "i1\n", "\n", "\n", "\n", "4->6\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "4->8\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n" ], "text/plain": [ - " >" + " >" ] }, "metadata": {}, @@ -3758,6 +3760,7 @@ }, { "cell_type": "markdown", + "id": "5102e762", "metadata": {}, "source": [ "# Reading an AIGER-file\n", @@ -3772,6 +3775,7 @@ { "cell_type": "code", "execution_count": 12, + "id": "29d37752", "metadata": {}, "outputs": [], "source": [ @@ -3792,6 +3796,7 @@ { "cell_type": "code", "execution_count": 13, + "id": "8989722d", "metadata": {}, "outputs": [ { @@ -3800,108 +3805,108 @@ "\n", "\n", - "\n", "\n", "\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "6\n", - "\n", - "6\n", + "\n", + "6\n", "\n", "\n", "\n", "10\n", - "\n", - "10\n", + "\n", + "10\n", "\n", "\n", "\n", "6->10\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "o1\n", - "\n", - "d\n", + "\n", + "d\n", "\n", "\n", "\n", "6->o1:s\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "8\n", - "\n", - "8\n", + "\n", + "8\n", "\n", "\n", "\n", "8->10\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "o0\n", - "\n", - "c\n", + "\n", + "c\n", "\n", "\n", "\n", "10->o0:s\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "2\n", - "\n", - "a\n", + "\n", + "a\n", "\n", "\n", "\n", "2->6\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "2->8\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "4\n", - "\n", - "b\n", + "\n", + "b\n", "\n", "\n", "\n", "4->6\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "4->8\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n" ], "text/plain": [ - " >" + " >" ] }, "metadata": {}, @@ -3916,6 +3921,7 @@ { "cell_type": "code", "execution_count": 14, + "id": "9560f368", "metadata": {}, "outputs": [ { @@ -3944,6 +3950,7 @@ { "cell_type": "code", "execution_count": 15, + "id": "9dadee6a", "metadata": {}, "outputs": [ { @@ -3960,6 +3967,7 @@ }, { "cell_type": "markdown", + "id": "734f10f1", "metadata": {}, "source": [ "An AIG circuit can be transformed into a monitor/Mealy machine. This can be used for instance to check that it does not intersect the negation of the specification." @@ -3968,6 +3976,7 @@ { "cell_type": "code", "execution_count": 16, + "id": "b29c95b4", "metadata": {}, "outputs": [ { @@ -3976,52 +3985,52 @@ "\n", "\n", - "\n", "\n", - "\n", + "\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "0\n", - "\n", - "0\n", + "\n", + "0\n", "\n", "\n", "\n", "I->0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "0->0\n", - "\n", - "\n", - "\n", - "!a & !b\n", - "/\n", - "\n", - "!c & !d\n", - "\n", - "a & b\n", - "/\n", - "\n", - "!c & d\n", - "\n", - "(!a & b) | (a & !b)\n", - "/\n", - "\n", - "c & !d\n", + "\n", + "\n", + "\n", + "!a & !b\n", + "/\n", + "\n", + "!c & !d\n", + "\n", + "a & b\n", + "/\n", + "\n", + "!c & d\n", + "\n", + "(!a & b) | (a & !b)\n", + "/\n", + "\n", + "c & !d\n", "\n", "\n", "\n" ], "text/plain": [ - " *' at 0x7f44ac2649c0> >" + " *' at 0x7fc5dc846690> >" ] }, "execution_count": 16, @@ -4035,6 +4044,7 @@ }, { "cell_type": "markdown", + "id": "09cad9f5", "metadata": {}, "source": [ "Note that the generation of aiger circuits from Mealy machines is flexible and accepts separated Mealy machines\n", @@ -4044,6 +4054,7 @@ { "cell_type": "code", "execution_count": 17, + "id": "62ebedae", "metadata": {}, "outputs": [ { @@ -4052,114 +4063,114 @@ "
\n", "\n", - "\n", "\n", - "\n", + "\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "0\n", - "\n", - "0\n", + "\n", + "0\n", "\n", "\n", "\n", "I->0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "0->0\n", - "\n", - "\n", - "\n", - "(!i0 & !i1) | (i0 & i1)\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "(!i0 & !i1) | (i0 & i1)\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "0->0\n", - "\n", - "\n", - "\n", - "(!i0 & i1) | (i0 & !i1)\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "(!i0 & i1) | (i0 & !i1)\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "
\n", "\n", - "\n", "\n", - "\n", - "\n", - "\n", - "t\n", - "[all]\n", + "\n", + "\n", + "\n", + "t\n", + "[all]\n", "\n", "\n", "\n", "0\n", - "\n", - "0\n", + "\n", + "0\n", "\n", "\n", "\n", "I->0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "1\n", - "\n", - "1\n", + "\n", + "1\n", "\n", "\n", "\n", "0->1\n", - "\n", - "\n", - "(!i0 & !i1) | (i0 & i1)\n", + "\n", + "\n", + "(!i0 & !i1) | (i0 & i1)\n", "\n", "\n", "\n", "2\n", - "\n", - "2\n", + "\n", + "2\n", "\n", "\n", "\n", "0->2\n", - "\n", - "\n", - "(!i0 & i1) | (i0 & !i1)\n", + "\n", + "\n", + "(!i0 & i1) | (i0 & !i1)\n", "\n", "\n", "\n", "1->0\n", - "\n", - "\n", - "!o0\n", + "\n", + "\n", + "!o0\n", "\n", "\n", "\n", "2->0\n", - "\n", - "\n", - "o0\n", + "\n", + "\n", + "o0\n", "\n", "\n", "\n", @@ -4191,6 +4202,7 @@ { "cell_type": "code", "execution_count": 18, + "id": "4a0bb1a7", "metadata": {}, "outputs": [ { @@ -4199,180 +4211,180 @@ "
\n", "\n", - "\n", "\n", "\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "6\n", - "\n", - "6\n", + "\n", + "6\n", "\n", "\n", "\n", "10\n", - "\n", - "10\n", + "\n", + "10\n", "\n", "\n", "\n", "6->10\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "8\n", - "\n", - "8\n", + "\n", + "8\n", "\n", "\n", "\n", "8->10\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "o0\n", - "\n", - "o0\n", + "\n", + "o0\n", "\n", "\n", "\n", "10->o0:s\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "2\n", - "\n", - "i0\n", + "\n", + "i0\n", "\n", "\n", "\n", "2->6\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "2->8\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "4\n", - "\n", - "i1\n", + "\n", + "i1\n", "\n", "\n", "\n", "4->6\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "4->8\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "
\n", "\n", - "\n", "\n", "\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "6\n", - "\n", - "6\n", + "\n", + "6\n", "\n", "\n", "\n", "10\n", - "\n", - "10\n", + "\n", + "10\n", "\n", "\n", "\n", "6->10\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "8\n", - "\n", - "8\n", + "\n", + "8\n", "\n", "\n", "\n", "8->10\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "o0\n", - "\n", - "o0\n", + "\n", + "o0\n", "\n", "\n", "\n", "10->o0:s\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "2\n", - "\n", - "i0\n", + "\n", + "i0\n", "\n", "\n", "\n", "2->6\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "2->8\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "4\n", - "\n", - "i1\n", + "\n", + "i1\n", "\n", "\n", "\n", "4->6\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "4->8\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", @@ -4393,9 +4405,9 @@ ], "metadata": { "kernelspec": { - "display_name": "'Python Interactive'", + "display_name": "Python 3 (ipykernel)", "language": "python", - "name": "748aac80-c5a9-4430-8d88-15820461ebdf" + "name": "python3" }, "language_info": { "codemirror_mode": { @@ -4407,7 +4419,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.7.3" + "version": "3.9.2" } }, "nbformat": 4, diff --git a/tests/python/synthesis.py b/tests/python/synthesis.py index e1a88650a..1b1cf4fbb 100644 --- a/tests/python/synthesis.py +++ b/tests/python/synthesis.py @@ -35,18 +35,18 @@ tc.assertEqual(game.to_str(), """HOA: v1 States: 3 Start: 0 AP: 1 "a" -acc-name: parity max odd 6 -Acceptance: 6 Inf(5) | (Fin(4) & (Inf(3) | (Fin(2) & (Inf(1) | Fin(0))))) +acc-name: Streett 1 +Acceptance: 2 Fin(0) | Inf(1) properties: trans-labels explicit-labels state-acc colored complete properties: deterministic spot-state-player: 0 1 1 controllable-AP: --BODY-- -State: 0 {1} +State: 0 {0} [!0] 1 [0] 2 -State: 1 {4} +State: 1 {0} [t] 0 -State: 2 {5} +State: 2 {1} [t] 0 --END--""") From 5cd0ce14b0de9c822c4dd32a2fe793778a4e106c Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Thu, 17 Mar 2022 14:37:57 +0100 Subject: [PATCH 011/337] fix mempool test to use __has_include This follows 6b88d6f35b2e. * tests/core/mempool.cc: Use __has_include too. --- tests/core/mempool.cc | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/tests/core/mempool.cc b/tests/core/mempool.cc index 0dae6ce0e..9d3610df7 100644 --- a/tests/core/mempool.cc +++ b/tests/core/mempool.cc @@ -1,6 +1,6 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2016, 2018 Laboratoire de Recherche et Développement -// de l'Epita. +// Copyright (C) 2016, 2018, 2022 Laboratoire de Recherche et +// Développement de l'Epita. // // This file is part of Spot, a model checking library. // @@ -103,7 +103,7 @@ namespace int main() { -#ifndef HAVE_VALGRIND_MEMCHECK_H +#if !__has_include() return 77; #endif @@ -186,4 +186,3 @@ int main() return 0; } - From 86de4d40529b61cd93bfcbe53ade5fa774c46450 Mon Sep 17 00:00:00 2001 From: Philipp Schlehuber-Caissier Date: Fri, 18 Mar 2022 01:02:45 +0100 Subject: [PATCH 012/337] Introduce mealy_prod Product between mealy machines with propagation of synthesis outputs and additional assertions. Currently it only supports input complete machines * spot/twaalgos/mealy_machine.cc, spot/twaalgos/mealy_machine.hh: Here * bin/ltlsynt.cc: Use * tests/python/except.py, tests/python/synthesis.ipynb: Test --- bin/ltlsynt.cc | 3 +- spot/twaalgos/mealy_machine.cc | 54 ++++++++ spot/twaalgos/mealy_machine.hh | 10 ++ tests/python/except.py | 17 +++ tests/python/synthesis.ipynb | 220 ++++++++++++++++++++++++++++++--- 5 files changed, 288 insertions(+), 16 deletions(-) diff --git a/bin/ltlsynt.cc b/bin/ltlsynt.cc index 73ec6b2b1..4d118dd46 100644 --- a/bin/ltlsynt.cc +++ b/bin/ltlsynt.cc @@ -545,7 +545,8 @@ namespace && "ltlsynt: Cannot handle TGBA as strategy."); tot_strat = mealy_machines.front().mealy_like; for (size_t i = 1; i < mealy_machines.size(); ++i) - tot_strat = spot::product(tot_strat, mealy_machines[i].mealy_like); + tot_strat = spot::mealy_product(tot_strat, + mealy_machines[i].mealy_like); printer.print(tot_strat, timer_printer_dummy); } diff --git a/spot/twaalgos/mealy_machine.cc b/spot/twaalgos/mealy_machine.cc index 99b762f16..36c162402 100644 --- a/spot/twaalgos/mealy_machine.cc +++ b/spot/twaalgos/mealy_machine.cc @@ -34,6 +34,7 @@ #include #include #include +#include #include #include @@ -55,6 +56,7 @@ namespace { + using namespace spot; bool is_deterministic_(const std::vector& ins) { const unsigned n_ins = ins.size(); @@ -64,6 +66,24 @@ namespace return false; return true; } + + bool is_complete_(const const_twa_graph_ptr& m, + const bdd& outs) + { + auto* sp = m->get_named_prop("state-player"); + const auto N = m->num_states(); + for (auto s = 0u; s < N; ++s) + { + if (sp && sp->at(s)) + continue; // No need tpo check player states + bdd all_cond = bddfalse; + for (const auto& e : m->out(s)) + all_cond |= bdd_exist(e.cond, outs); + if (all_cond != bddtrue) + return false; + } + return true; + } } @@ -3843,4 +3863,38 @@ namespace spot return true; } + twa_graph_ptr + mealy_product(const const_twa_graph_ptr& left, + const const_twa_graph_ptr& right) + { + bdd outs[] = {get_synthesis_outputs(left), + get_synthesis_outputs(right)}; + +#ifndef NDEBUG + for (const auto& [m, n, o] : {std::tuple{left, "left", outs[0]}, + {right, "right", outs[1]}}) + { + if (!is_mealy(m)) + throw std::runtime_error(std::string("mealy_prod(): ") + n + + " is not a mealy machine"); + if (!is_complete_(m, o)) + throw std::runtime_error(std::string("mealy_prod(): ") + n + + " is not input complete"); + } +#endif + + auto p = product(left, right); + bdd pouts = outs[0] & outs[1]; + set_synthesis_outputs(p, pouts); + +#ifndef NDEBUG + if (!is_mealy(p)) + throw std::runtime_error("mealy_prod(): Prooduct is not mealy"); + if (!is_complete_(p, pouts)) + throw std::runtime_error("mealy_prod(): Prooduct is not input complete. " + "Incompatible machines?"); +#endif + + return p; + } } diff --git a/spot/twaalgos/mealy_machine.hh b/spot/twaalgos/mealy_machine.hh index 139f7cce2..77c3968ab 100644 --- a/spot/twaalgos/mealy_machine.hh +++ b/spot/twaalgos/mealy_machine.hh @@ -128,4 +128,14 @@ namespace spot is_split_mealy_specialization(const_twa_graph_ptr left, const_twa_graph_ptr right, bool verbose = false); + + /// \brief Product between two mealy machines \a left and \a right. + /// \pre The machines have to be both either split or unsplit, + /// input complete and compatible. All of this is check by assertion + /// \result The mealy machine representing the shared behaviour. + /// The resulting machine has the same class (mealy/separated/split) + /// as the input machines + SPOT_API twa_graph_ptr + mealy_product(const const_twa_graph_ptr& left, + const const_twa_graph_ptr& right); } \ No newline at end of file diff --git a/tests/python/except.py b/tests/python/except.py index 8674721c9..3aeee7a3e 100644 --- a/tests/python/except.py +++ b/tests/python/except.py @@ -331,3 +331,20 @@ except RuntimeError as e: tc.assertIn("already registered", se) else: report_missing_exception() + + +si = spot.synthesis_info() +si.s = spot.synthesis_info.algo_LAR +g1 = spot.ltl_to_game("G((i0 xor i1) <-> o0)", ["o0"], si) +g2 = spot.ltl_to_game("G((i0 xor i1) <-> (!o0 & !o1))", ["o0", "o1"], si) +spot.solve_game(g1) +spot.solve_game(g2) +strat1 = spot.solved_game_to_separated_mealy(g1) +strat2 = spot.solved_game_to_separated_mealy(g2) +try: + stratcomp = spot.mealy_product(strat1, strat2) +except RuntimeError as e: + se = str(e) + tc.assertIn("Incompatible", se) +else: + report_missing_exception() \ No newline at end of file diff --git a/tests/python/synthesis.ipynb b/tests/python/synthesis.ipynb index 8c706c481..7ec181ebe 100644 --- a/tests/python/synthesis.ipynb +++ b/tests/python/synthesis.ipynb @@ -696,7 +696,7 @@ "\n" ], "text/plain": [ - " *' at 0x7fc5ec2bf930> >" + " *' at 0x7f05083f22a0> >" ] }, "metadata": {}, @@ -2714,7 +2714,7 @@ "\n" ], "text/plain": [ - " >" + " >" ] }, "metadata": {}, @@ -3027,7 +3027,7 @@ "\n" ], "text/plain": [ - " *' at 0x7fc5dc8463f0> >" + " *' at 0x7f05082c7a20> >" ] }, "metadata": {}, @@ -3102,7 +3102,7 @@ "\n" ], "text/plain": [ - " *' at 0x7fc5ec2bf990> >" + " *' at 0x7f05083f2300> >" ] }, "metadata": {}, @@ -3179,7 +3179,7 @@ "\n" ], "text/plain": [ - " >" + " >" ] }, "metadata": {}, @@ -3306,7 +3306,7 @@ "\n" ], "text/plain": [ - " >" + " >" ] }, "metadata": {}, @@ -3327,8 +3327,13 @@ "It can happen that the complete specification of the controller can be separated into sub-specifications with DISJOINT output propositions, see Finkbeiner et al. Specification Decomposition for Reactive Synthesis.\n", "This results in multiple Mealy machines which have to be converted into one single AIG circuit.\n", "\n", - "This can be done using the function `mealy_machines_to_aig()`, which takes a vector of separated Mealy machines as argument.\n", - "In order for this to work, all Mealy machines need to share the same `bdd_dict`. This can be ensured by passing a common options strucuture." + "This can be done in two ways:\n", + "\n", + "1. Using the function `mealy_machines_to_aig()`, which takes a vector of separated mealy machines as argument.\n", + "2. Combine the mealy machines into one before passing it to `mealy_machine_to aig(). This currently only supports input complete machines of the same type (mealy/separated mealy/split mealy)\n", + "\n", + "Note that the method version is usually preferable as it is faster.\n", + "Also note that in order for this to work, all mealy machines need to share the same `bdd_dict`. This can be ensured by passing a common options strucuture." ] }, { @@ -3623,7 +3628,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "Circuit implementing both machines:\n" + "Circuit implementing both machines from a vector of machines:\n" ] }, { @@ -3733,7 +3738,185 @@ "\n" ], "text/plain": [ - " >" + " >" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Combining the two machines into one.\n" + ] + }, + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0,0\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "\n", + "(!i0 & !i1) | (i0 & i1)\n", + "/\n", + "\n", + "!o0 & o1\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "\n", + "(!i0 & i1) | (i0 & !i1)\n", + "/\n", + "\n", + "o0 & !o1\n", + "\n", + "\n", + "\n" + ], + "text/plain": [ + " *' at 0x7f05082c7ba0> >" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "6\n", + "\n", + "6\n", + "\n", + "\n", + "\n", + "10\n", + "\n", + "10\n", + "\n", + "\n", + "\n", + "6->10\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "8\n", + "\n", + "8\n", + "\n", + "\n", + "\n", + "8->10\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "o0\n", + "\n", + "o0\n", + "\n", + "\n", + "\n", + "10->o0:s\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "o1\n", + "\n", + "o1\n", + "\n", + "\n", + "\n", + "10->o1:s\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "2\n", + "\n", + "i0\n", + "\n", + "\n", + "\n", + "2->6\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "2->8\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "4\n", + "\n", + "i1\n", + "\n", + "\n", + "\n", + "4->6\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "4->8\n", + "\n", + "\n", + "\n", + "\n", + "\n" + ], + "text/plain": [ + " >" ] }, "metadata": {}, @@ -3753,9 +3936,16 @@ "strat2 = spot.solved_game_to_separated_mealy(g2)\n", "print(\"Reduced strategies:\")\n", "display_inline(strat1, strat2)\n", - "print(\"Circuit implementing both machines:\")\n", + "#Method 1\n", + "print(\"Circuit implementing both machines from a vector of machines:\")\n", "aig = spot.mealy_machines_to_aig([strat1, strat2], \"isop\")\n", - "display(aig)" + "display(aig)\n", + "#Method 2\n", + "strat_comb = spot.mealy_product(strat1, strat2)\n", + "print(\"Combining the two machines into one.\")\n", + "display(strat_comb)\n", + "aig_comb = spot.mealy_machine_to_aig(strat_comb, \"isop\")\n", + "display(aig_comb)" ] }, { @@ -3906,7 +4096,7 @@ "\n" ], "text/plain": [ - " >" + " >" ] }, "metadata": {}, @@ -4030,7 +4220,7 @@ "\n" ], "text/plain": [ - " *' at 0x7fc5dc846690> >" + " *' at 0x7f05082c7690> >" ] }, "execution_count": 16, @@ -4419,7 +4609,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.2" + "version": "3.8.10" } }, "nbformat": 4, From 97fc3f6c0bd9732e9ec16fdb3fd34c7973155ced Mon Sep 17 00:00:00 2001 From: Philipp Schlehuber-Caissier Date: Fri, 18 Mar 2022 15:27:46 +0100 Subject: [PATCH 013/337] Introduce simplify_mealy Convenience function dispatching to minimize_mealy and reduce_mealy. Change tests accordingly * spot/twaalgos/mealy_machine.cc, spot/twaalgos/mealy_machine.hh: Here * bin/ltlsynt.cc: Use simplify * spot/twaalgos/synthesis.cc, spot/twaalgos/synthesis.hh: Remove minimization, Update options * tests/core/ltlsynt.test, tests/python/synthesis.ipynb, tests/python/_synthesis.ipynb: Adapt --- bin/ltlsynt.cc | 64 +--- spot/twaalgos/mealy_machine.cc | 167 +++++++-- spot/twaalgos/mealy_machine.hh | 23 +- spot/twaalgos/synthesis.cc | 26 +- spot/twaalgos/synthesis.hh | 3 + tests/core/ltlsynt.test | 39 +- tests/python/_synthesis.ipynb | 660 +++++++++++++++++++++++---------- tests/python/synthesis.ipynb | 246 ++++++++++-- 8 files changed, 901 insertions(+), 327 deletions(-) diff --git a/bin/ltlsynt.cc b/bin/ltlsynt.cc index 4d118dd46..305c7a2f4 100644 --- a/bin/ltlsynt.cc +++ b/bin/ltlsynt.cc @@ -408,14 +408,13 @@ namespace spot::mealy_like ml; ml.success = spot::mealy_like::realizability_code::REALIZABLE_REGULAR; - if (opt_print_aiger) - // we do not care about the type, - // machine to aiger can handle it - ml.mealy_like = - spot::solved_game_to_mealy(arena, *gi); - else - ml.mealy_like = - spot::solved_game_to_separated_mealy(arena, *gi); + // By default this produces a split machine + ml.mealy_like = + spot::solved_game_to_mealy(arena, *gi); + // Keep the machine split for aiger + // else -> separated + spot::simplify_mealy_here(ml.mealy_like, *gi, + opt_print_aiger); ml.glob_cond = bddfalse; mealy_machines.push_back(ml); } @@ -429,51 +428,10 @@ namespace assert(m_like.mealy_like && "Expected success but found no mealy!"); if (!opt_real) { - spot::stopwatch sw_direct; - sw_direct.start(); - - if ((0 < gi->minimize_lvl) && (gi->minimize_lvl < 3)) - // Uses reduction or not, - // both work with mealy machines (non-separated) - reduce_mealy_here(m_like.mealy_like, gi->minimize_lvl == 2); - - auto delta = sw_direct.stop(); - - sw_direct.start(); - // todo better algo here? - m_like.mealy_like = - split_2step(m_like.mealy_like, - spot::get_synthesis_outputs(m_like.mealy_like), - false); - if (gi->bv) - gi->bv->split_time += sw_direct.stop(); - - sw_direct.start(); - if (gi->minimize_lvl >= 3) - { - sw_direct.start(); - // actual minimization, works on split mealy - m_like.mealy_like = minimize_mealy(m_like.mealy_like, - gi->minimize_lvl - 4); - delta = sw_direct.stop(); - } - - // If our goal is to have an aiger, - // we can use split or separated machines - if (!opt_print_aiger) - // Unsplit to have separated mealy - m_like.mealy_like = unsplit_mealy(m_like.mealy_like); - - if (gi->bv) - gi->bv->strat2aut_time += delta; - if (gi->verbose_stream) - *gi->verbose_stream << "final strategy has " - << m_like.mealy_like->num_states() - << " states and " - << m_like.mealy_like->num_edges() - << " edges\n" - << "minimization took " << delta - << " seconds\n"; + // Keep the machine split for aiger + // else -> separated + spot::simplify_mealy_here(m_like.mealy_like, *gi, + opt_print_aiger); } SPOT_FALLTHROUGH; } diff --git a/spot/twaalgos/mealy_machine.cc b/spot/twaalgos/mealy_machine.cc index 36c162402..f985da506 100644 --- a/spot/twaalgos/mealy_machine.cc +++ b/spot/twaalgos/mealy_machine.cc @@ -135,11 +135,12 @@ namespace spot if (!is_mealy(m)) return false; - if (m->get_named_prop("state-player") == nullptr) + if (!m->get_named_prop("state-player")) { trace << "is_split_mealy(): Split mealy machine must define the named " "property \"state-player\"!\n"; } + auto sp = get_state_players(m); if (sp.size() != m->num_states()) @@ -1027,6 +1028,28 @@ namespace std::pair reorganize_mm(const_twa_graph_ptr mm, const std::vector& sp) { + // Check if the twa_graph already has the correct form + { + auto sp = get_state_players(mm); + // All player states mus be at the end + bool is_ok = true; + bool seen_player = false; + for (const auto& p : sp) + { + if (seen_player & !p) + { + is_ok = false; + break; + } + seen_player |= p; + } + if (is_ok) + return {mm, + mm->num_states() + - std::accumulate(sp.begin(), sp.end(), 0)}; + } + // We actually need to generate a new graph with the correct + // form // Purge unreachable and reorganize the graph std::vector renamed(mm->num_states(), -1u); const unsigned n_old = mm->num_states(); @@ -3607,7 +3630,7 @@ namespace spot twa_graph_ptr minimize_mealy(const const_twa_graph_ptr& mm, int premin) { - assert(is_split_mealy(mm)); + assert(is_mealy(mm)); stopwatch sw; sw.start(); @@ -3615,38 +3638,33 @@ namespace spot if ((premin < -1) || (premin > 1)) throw std::runtime_error("premin has to be -1, 0 or 1"); - auto orig_spref = get_state_players(mm); - - // Check if finite traces exist - // If so, deactivate fast minimization - // todo : this is overly conservative - // If unreachable states have no outgoing edges we do not care - // but testing this as well starts to be expensive... - if (premin != -1 - && [&]() - { - for (unsigned s = 0; s < mm->num_states(); ++s) - { - auto eit = mm->out(s); - if (eit.begin() == eit.end()) - return true; - } - return false; - }()) - premin = -1; - auto do_premin = [&]()->const_twa_graph_ptr { if (premin == -1) - return mm; + { + if (!mm->get_named_prop("state-player")) + return split_2step(mm, false); + else + return mm; + } else { + bool is_split = mm->get_named_prop("state-player"); // We have a split machine -> unsplit then resplit, // as reduce mealy works on separated - auto mms = unsplit_mealy(mm); - reduce_mealy_here(mms, premin == 1); - split_separated_mealy_here(mms); - return mms; + twa_graph_ptr mms; + if (is_split) + { + auto mmi = unsplit_2step(mm); + reduce_mealy_here(mmi, premin == 1); + split_separated_mealy_here(mmi); + return mmi; + } + else + { + auto mms = reduce_mealy(mm, premin == 1); + return split_2step(mms, false); + } } }; @@ -3689,9 +3707,13 @@ namespace spot auto early_exit = [&]() { // Always keep machines split - assert(is_split_mealy_specialization(mm, mmw)); + if (mm->get_named_prop("state-player")) + assert(is_split_mealy_specialization(mm, mmw)); + else + assert(is_split_mealy_specialization(split_2step(mm, false), + mmw)); return std::const_pointer_cast(mmw); - }; + }; // If the partial solution has the same number of // states as the original automaton -> we are done @@ -3897,4 +3919,91 @@ namespace spot return p; } + + + void + simplify_mealy_here(twa_graph_ptr& m, int minimize_lvl, + bool split_out) + { + auto si = synthesis_info(); + si.minimize_lvl = minimize_lvl; + return simplify_mealy_here(m, si, split_out); + } + + void + simplify_mealy_here(twa_graph_ptr& m, synthesis_info& si, + bool split_out) + { + const auto minimize_lvl = si.minimize_lvl; + assert(is_mealy(m) + && "simplify_mealy_here(): m is not a mealy machine!"); + if (minimize_lvl < 0 || 5 < minimize_lvl) + throw std::runtime_error("simplify_mealy_here(): minimize_lvl " + "must be between 0 and 5."); + + stopwatch sw; + if (si.bv) + sw.start(); + + bool is_separated = false; + if (0 < minimize_lvl && minimize_lvl < 3) + { + // unsplit if necessary + if (m->get_named_prop("state-player")) + { + m = unsplit_mealy(m); + is_separated = true; + } + reduce_mealy_here(m, minimize_lvl == 2); + } + else if (3 <= minimize_lvl) + m = minimize_mealy(m, minimize_lvl - 4); + + // Convert to demanded output format + bool is_split = m->get_named_prop("state-player"); + if (minimize_lvl == 0) + { + if (is_split && !split_out) + m = unsplit_mealy(m); + else if (!is_split && split_out) + m = split_2step(m, false); + } + else if (0 < minimize_lvl && minimize_lvl < 3 && split_out) + { + if (is_separated) + split_separated_mealy_here(m); + else + m = split_2step(m, false); + } + else if (3 <= minimize_lvl && !split_out) + m = unsplit_mealy(m); + + if (si.bv) + { + if (si.verbose_stream) + *si.verbose_stream << "simplification took " << sw.stop() + << " seconds\n"; + si.bv->simplify_strat_time += sw.stop(); + auto n_s_env = 0u; + auto n_e_env = 0u; + if (auto sp = m->get_named_prop("state-player")) + { + n_s_env = sp->size() - std::accumulate(sp->begin(), + sp->end(), + 0u); + std::for_each(m->edges().begin(), m->edges().end(), + [&n_e_env, &sp](const auto& e) + { + n_e_env += (*sp)[e.src]; + }); + } + else + { + n_s_env = m->num_states(); + n_e_env = m->num_edges(); + } + si.bv->nb_simpl_strat_states += n_s_env; + si.bv->nb_simpl_strat_edges += n_e_env; + } + } } diff --git a/spot/twaalgos/mealy_machine.hh b/spot/twaalgos/mealy_machine.hh index 77c3968ab..7406cb61d 100644 --- a/spot/twaalgos/mealy_machine.hh +++ b/spot/twaalgos/mealy_machine.hh @@ -23,6 +23,9 @@ namespace spot { + // Forward decl + struct synthesis_info; + /// todo /// Comment je faire au mieux pour expliquer mealy dans les doc @@ -104,7 +107,7 @@ namespace spot bool output_assignment = false); /// @} - /// \brief Minimizes a split (in)completely specified mealy machine + /// \brief Minimizes an (in)completely specified mealy machine /// The approach is described in \todo TACAS /// \param premin Use reduce_mealy before applying the /// main algorithm if demanded AND @@ -138,4 +141,22 @@ namespace spot SPOT_API twa_graph_ptr mealy_product(const const_twa_graph_ptr& left, const const_twa_graph_ptr& right); + + /// \brief Convenience function to call minimize_mealy or reduce_mealy. + /// Uses the same convention as ltlsynt for \a minimize_lvl: + /// 0: no reduction + /// 1: bisimulation based reduction + /// 2: bisimulation with output assignment + /// 3: SAT minimization + /// 4: 1 then 3 + /// 5: 2 then 3 + /// Minimizes the given machine \a m inplace, the parameter + /// \a split_out defines whether it is split or not + SPOT_API void + simplify_mealy_here(twa_graph_ptr& m, int minimize_lvl, + bool split_out); + + SPOT_API void + simplify_mealy_here(twa_graph_ptr& m, synthesis_info& si, + bool split_out); } \ No newline at end of file diff --git a/spot/twaalgos/synthesis.cc b/spot/twaalgos/synthesis.cc index 6d4537206..7620a1098 100644 --- a/spot/twaalgos/synthesis.cc +++ b/spot/twaalgos/synthesis.cc @@ -1051,22 +1051,25 @@ namespace spot if (!get_state_winner(arena, arena->get_init_state_number())) return nullptr; - // If we use minimizations 0,1 or 2 -> unsplit - const bool do_unsplit = gi.minimize_lvl < 3; - auto m = apply_strategy(arena, do_unsplit, false); + auto m = apply_strategy(arena, false, false); m->prop_universal(true); - if ((0 < gi.minimize_lvl) && (gi.minimize_lvl < 3)) - reduce_mealy_here(m, gi.minimize_lvl == 2); - else if (gi.minimize_lvl >= 3) - m = minimize_mealy(m, gi.minimize_lvl - 4); - if (gi.bv) { + auto sp = get_state_players(m); + auto n_s_env = sp.size() - std::accumulate(sp.begin(), + sp.end(), + 0u); + auto n_e_env = 0u; + std::for_each(m->edges().begin(), m->edges().end(), + [&n_e_env, &sp](const auto& e) + { + n_e_env += sp[e.src]; + }); gi.bv->strat2aut_time += sw.stop(); - gi.bv->nb_strat_states += m->num_states(); - gi.bv->nb_strat_edges += m->num_edges(); + gi.bv->nb_strat_states += n_s_env; + gi.bv->nb_strat_edges += n_e_env; } assert(is_mealy(m)); @@ -1200,7 +1203,8 @@ namespace spot { *vs << "direct strategy was found.\n" << "direct strat has " << strat->num_states() - << " states and " << strat->num_sets() << " colors\n"; + << " states, " << strat->num_edges() + << " edges and " << strat->num_sets() << " colors\n"; } return mealy_like{ mealy_like::realizability_code::REALIZABLE_REGULAR, diff --git a/spot/twaalgos/synthesis.hh b/spot/twaalgos/synthesis.hh index 95590504c..46c0bc2bd 100644 --- a/spot/twaalgos/synthesis.hh +++ b/spot/twaalgos/synthesis.hh @@ -96,11 +96,14 @@ namespace spot double paritize_time = 0.0; double solve_time = 0.0; double strat2aut_time = 0.0; + double simplify_strat_time = 0.0; double aig_time = 0.0; unsigned nb_states_arena = 0; unsigned nb_states_arena_env = 0; unsigned nb_strat_states = 0; unsigned nb_strat_edges = 0; + unsigned nb_simpl_strat_states = 0; + unsigned nb_simpl_strat_edges = 0; unsigned nb_latches = 0; unsigned nb_gates = 0; bool realizable = false; diff --git a/tests/core/ltlsynt.test b/tests/core/ltlsynt.test index 215143df2..335c1b01e 100644 --- a/tests/core/ltlsynt.test +++ b/tests/core/ltlsynt.test @@ -195,7 +195,7 @@ cat >exp < GFb tanslating formula done in X seconds direct strategy was found. -direct strat has 1 states and 0 colors +direct strat has 1 states, 2 edges and 0 colors EOF ltlsynt --ins='a' --outs='b' -f 'GFa <-> GFb' --verbose --realizability 2> out sed 's/ [0-9.e-]* seconds/ X seconds/g' out > outx @@ -205,9 +205,8 @@ cat >exp < GFb tanslating formula done in X seconds direct strategy was found. -direct strat has 1 states and 0 colors -final strategy has 1 states and 2 edges -minimization took X seconds +direct strat has 1 states, 2 edges and 0 colors +simplification took X seconds EOF ltlsynt --ins=a --outs=b -f 'GFa <-> GFb' --verbose --algo=ps 2> out sed 's/ [0-9.e-]* seconds/ X seconds/g' out > outx @@ -217,7 +216,7 @@ cat >exp < GFe tanslating formula done in X seconds direct strategy was found. -direct strat has 16 states and 0 colors +direct strat has 16 states, 81 edges and 0 colors EOF ltlsynt --ins='a,b,c,d' --outs='e' -f '(Fa & Fb & Fc & Fd) <-> GFe' \ --verbose --realizability --algo=lar 2> out @@ -561,9 +560,8 @@ cat >exp < GFb tanslating formula done in X seconds direct strategy was found. -direct strat has 1 states and 0 colors -final strategy has 1 states and 2 edges -minimization took X seconds +direct strat has 1 states, 2 edges and 0 colors +simplification took X seconds trying to create strategy directly for Gc direct strategy might exist but was not found. translating formula done in X seconds @@ -574,6 +572,7 @@ split inputs and outputs done in X seconds automaton has 2 states solving game with acceptance: Streett 1 game solved in X seconds +simplification took X seconds EOF ltlsynt -f '(GFa <-> GFb) && (Gc)' --outs=b,c --verbose 2> out sed 's/ [0-9.e-]* seconds/ X seconds/g' out > outx @@ -588,9 +587,8 @@ cat >exp < out sed 's/ [0-9.e-]* seconds/ X seconds/g' out > outx @@ -615,9 +613,8 @@ cat >exp < GFa) & G((a & c) | (!a & !c)) tanslating formula done in X seconds direct strategy was found. -direct strat has 1 states and 0 colors -final strategy has 1 states and 2 edges -minimization took X seconds +direct strat has 1 states, 2 edges and 0 colors +simplification took X seconds EOF ltlsynt -f '(GFb <-> GFa) && (G((a&c)|(!a&!c)))' --outs=b,c --verbose\ --verify --decompose=0 2> out @@ -630,9 +627,8 @@ cat >exp < FGb tanslating formula done in X seconds direct strategy was found. -direct strat has 2 states and 0 colors -final strategy has 2 states and 3 edges -minimization took X seconds +direct strat has 2 states, 3 edges and 0 colors +simplification took X seconds EOF ltlsynt -f "Fa <-> FGb" --outs=b,c --verbose --decompose=0 --verify 2> out sed 's/ [0-9.e-]* seconds/ X seconds/g' out > outx @@ -651,6 +647,7 @@ split inputs and outputs done in X seconds automaton has 9 states solving game with acceptance: Streett 1 game solved in X seconds +simplification took X seconds AIG circuit was created in X seconds and has 0 latches and 0 gates EOF ltlsynt -f "Ga <-> Gb" --outs=b --verbose --decompose=0 --verify --aiger 2> out @@ -668,6 +665,7 @@ split inputs and outputs done in X seconds automaton has 4 states solving game with acceptance: Streett 1 game solved in X seconds +simplification took X seconds trying to create strategy directly for (a | x) -> x direct strategy might exist but was not found. translating formula done in X seconds @@ -678,6 +676,7 @@ split inputs and outputs done in X seconds automaton has 4 states solving game with acceptance: Streett 1 game solved in X seconds +simplification took X seconds AIG circuit was created in X seconds and has 0 latches and 0 gates EOF ltlsynt -f '((a|x) & (b | y) & b) => (x & y)' --outs="x,y" --aiger=ite\ @@ -697,6 +696,7 @@ split inputs and outputs done in X seconds automaton has 2 states solving game with acceptance: Streett 1 game solved in X seconds +simplification took X seconds trying to create strategy directly for Gy direct strategy might exist but was not found. translating formula done in X seconds @@ -707,6 +707,7 @@ split inputs and outputs done in X seconds automaton has 2 states solving game with acceptance: Streett 1 game solved in X seconds +simplification took X seconds AIG circuit was created in X seconds and has 0 latches and 0 gates EOF ltlsynt -f 'G!(!x | !y)' --outs="x, y" --aiger=ite --verify --verbose 2> out @@ -760,6 +761,7 @@ split inputs and outputs done in X seconds automaton has 5 states solving game with acceptance: Streett 1 game solved in X seconds +simplification took X seconds AIG circuit was created in X seconds and has 0 latches and 0 gates EOF ltlsynt -f '(a & b) U (b & c)' --outs=b,c --decompose=yes --aiger --verbose\ @@ -780,6 +782,7 @@ split inputs and outputs done in X seconds automaton has 4 states solving game with acceptance: Streett 1 game solved in X seconds +simplification took X seconds trying to create strategy directly for a -> c direct strategy might exist but was not found. translating formula done in X seconds @@ -790,6 +793,7 @@ split inputs and outputs done in X seconds automaton has 4 states solving game with acceptance: Streett 1 game solved in X seconds +simplification took X seconds trying to create strategy directly for a -> d direct strategy might exist but was not found. translating formula done in X seconds @@ -800,6 +804,7 @@ split inputs and outputs done in X seconds automaton has 4 states solving game with acceptance: Streett 1 game solved in X seconds +simplification took X seconds AIG circuit was created in X seconds and has 0 latches and 0 gates EOF ltlsynt -f 'a => (b & c & d)' --outs=b,c,d, --decompose=yes\ diff --git a/tests/python/_synthesis.ipynb b/tests/python/_synthesis.ipynb index 3a91415e9..5866057a1 100644 --- a/tests/python/_synthesis.ipynb +++ b/tests/python/_synthesis.ipynb @@ -3,6 +3,7 @@ { "cell_type": "code", "execution_count": 1, + "id": "c54c43ba", "metadata": {}, "outputs": [], "source": [ @@ -12,6 +13,7 @@ }, { "cell_type": "markdown", + "id": "0576f64a", "metadata": {}, "source": [ "Additional testing for synthesis" @@ -19,6 +21,7 @@ }, { "cell_type": "markdown", + "id": "e25b7989", "metadata": {}, "source": [ "Testing the different methods to solve" @@ -27,6 +30,7 @@ { "cell_type": "code", "execution_count": 2, + "id": "007107a6", "metadata": {}, "outputs": [ { @@ -50,6 +54,7 @@ { "cell_type": "code", "execution_count": 3, + "id": "a7859f19", "metadata": {}, "outputs": [ { @@ -57,43 +62,72 @@ "output_type": "stream", "text": [ "HOA: v1\n", - "States: 7\n", + "States: 21\n", "Start: 0\n", "AP: 3 \"i1\" \"i0\" \"o0\"\n", "acc-name: all\n", "Acceptance: 0 t\n", "properties: trans-labels explicit-labels state-acc deterministic\n", + "spot-state-player: 0 0 1 0 1 0 1 0 1 0 1 1 1 1 0 1 1 1 1 1 1\n", "controllable-AP: 2\n", "--BODY--\n", "State: 0\n", - "[!0&!1] 1\n", - "[!0&1] 2\n", - "[0&!1] 3\n", - "[0&1] 4\n", + "[!0&!1] 2\n", + "[!0&1] 4\n", + "[0&!1] 6\n", + "[0&1] 8\n", "State: 1\n", - "[0&1&!2] 4\n", - "[0&!1&!2] 3\n", - "[!0&1&!2] 2\n", - "[!0&!1&!2] 1\n", + "[0&1] 13\n", + "[0&!1] 18\n", + "[!0&1] 19\n", + "[!0&!1] 20\n", "State: 2\n", - "[0&!2] 4\n", - "[!0&!2] 2\n", + "[t] 1\n", "State: 3\n", - "[!0&1&2] 5\n", - "[0&1&2] 4\n", - "[!0&!1&2] 6\n", - "[0&!1&2] 3\n", + "[0] 13\n", + "[!0] 19\n", "State: 4\n", - "[!0&2] 5\n", - "[0&2] 4\n", + "[t] 3\n", "State: 5\n", - "[!0&!2] 5\n", - "[0&!2] 4\n", + "[!0&1] 10\n", + "[0&1] 11\n", + "[!0&!1] 15\n", + "[0&!1] 16\n", "State: 6\n", - "[!0&1&!2] 5\n", - "[0&1&!2] 4\n", - "[!0&!1&!2] 6\n", - "[0&!1&!2] 3\n", + "[t] 5\n", + "State: 7\n", + "[!0] 10\n", + "[0] 11\n", + "State: 8\n", + "[t] 7\n", + "State: 9\n", + "[!0] 12\n", + "[0] 13\n", + "State: 10\n", + "[2] 9\n", + "State: 11\n", + "[2] 7\n", + "State: 12\n", + "[!2] 9\n", + "State: 13\n", + "[!2] 7\n", + "State: 14\n", + "[!0&1] 12\n", + "[0&1] 13\n", + "[!0&!1] 17\n", + "[0&!1] 18\n", + "State: 15\n", + "[2] 14\n", + "State: 16\n", + "[2] 5\n", + "State: 17\n", + "[!2] 14\n", + "State: 18\n", + "[!2] 5\n", + "State: 19\n", + "[!2] 3\n", + "State: 20\n", + "[!2] 1\n", "--END--\n", "HOA: v1\n", "States: 7\n", @@ -141,163 +175,137 @@ "acc-name: all\n", "Acceptance: 0 t\n", "properties: trans-labels explicit-labels state-acc deterministic\n", - "spot-state-player: 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1\n", + "spot-state-player: 0 0 1 0 1 0 1 0 1 0 1 1 1 1 0 1 1 1 1 1 1\n", "controllable-AP: 2\n", "--BODY--\n", "State: 0\n", - "[!0&!1] 7\n", - "[!0&1] 8\n", - "[0&!1] 9\n", - "[0&1] 10\n", + "[!0&!1] 2\n", + "[!0&1] 4\n", + "[0&!1] 6\n", + "[0&1] 8\n", "State: 1\n", - "[0&1] 11\n", - "[0&!1] 12\n", - "[!0&1] 13\n", - "[!0&!1] 14\n", + "[0&1] 13\n", + "[0&!1] 18\n", + "[!0&1] 19\n", + "[!0&!1] 20\n", "State: 2\n", - "[0] 11\n", - "[!0] 13\n", + "[t] 1\n", "State: 3\n", - "[!0&1] 15\n", - "[0&1] 16\n", + "[0] 13\n", + "[!0] 19\n", + "State: 4\n", + "[t] 3\n", + "State: 5\n", + "[!0&1] 10\n", + "[0&1] 11\n", + "[!0&!1] 15\n", + "[0&!1] 16\n", + "State: 6\n", + "[t] 5\n", + "State: 7\n", + "[!0] 10\n", + "[0] 11\n", + "State: 8\n", + "[t] 7\n", + "State: 9\n", + "[!0] 12\n", + "[0] 13\n", + "State: 10\n", + "[2] 9\n", + "State: 11\n", + "[2] 7\n", + "State: 12\n", + "[!2] 9\n", + "State: 13\n", + "[!2] 7\n", + "State: 14\n", + "[!0&1] 12\n", + "[0&1] 13\n", "[!0&!1] 17\n", "[0&!1] 18\n", - "State: 4\n", - "[!0] 15\n", - "[0] 16\n", - "State: 5\n", - "[!0] 19\n", - "[0] 11\n", - "State: 6\n", - "[!0&1] 19\n", - "[0&1] 11\n", - "[!0&!1] 20\n", - "[0&!1] 12\n", - "State: 7\n", - "[t] 1\n", - "State: 8\n", - "[t] 2\n", - "State: 9\n", - "[t] 3\n", - "State: 10\n", - "[t] 4\n", - "State: 11\n", - "[!2] 4\n", - "State: 12\n", - "[!2] 3\n", - "State: 13\n", - "[!2] 2\n", - "State: 14\n", - "[!2] 1\n", "State: 15\n", - "[2] 5\n", + "[2] 14\n", "State: 16\n", - "[2] 4\n", + "[2] 5\n", "State: 17\n", - "[2] 6\n", + "[!2] 14\n", "State: 18\n", - "[2] 3\n", - "State: 19\n", "[!2] 5\n", + "State: 19\n", + "[!2] 3\n", "State: 20\n", - "[!2] 6\n", + "[!2] 1\n", "--END--\n", "HOA: v1\n", - "States: 2\n", - "Start: 1\n", + "States: 21\n", + "Start: 0\n", "AP: 3 \"i1\" \"i0\" \"o0\"\n", "acc-name: all\n", "Acceptance: 0 t\n", "properties: trans-labels explicit-labels state-acc deterministic\n", + "spot-state-player: 0 0 1 0 1 0 1 0 1 0 1 1 1 1 0 1 1 1 1 1 1\n", "controllable-AP: 2\n", "--BODY--\n", "State: 0\n", - "[0&1&!2] 1\n", - "[0&!1&!2] 1\n", - "[!0&1&!2] 0\n", - "[!0&!1&!2] 0\n", - "State: 1\n", - "[!0&1&2] 0\n", - "[0&1&2] 1\n", - "[!0&!1&2] 0\n", - "[0&!1&2] 1\n", - "--END--\n", - "HOA: v1\n", - "States: 2\n", - "Start: 1\n", - "AP: 3 \"i1\" \"i0\" \"o0\"\n", - "acc-name: all\n", - "Acceptance: 0 t\n", - "properties: trans-labels explicit-labels state-acc deterministic\n", - "controllable-AP: 2\n", - "--BODY--\n", - "State: 0\n", - "[0&1&!2] 1\n", - "[0&!1&!2] 1\n", - "[!0&1&!2] 0\n", - "[!0&!1&!2] 0\n", - "State: 1\n", - "[!0&1&2] 0\n", - "[0&1&2] 1\n", - "[!0&!1&2] 0\n", - "[0&!1&2] 1\n", - "--END--\n", - "HOA: v1\n", - "States: 6\n", - "Start: 1\n", - "AP: 3 \"i1\" \"i0\" \"o0\"\n", - "acc-name: all\n", - "Acceptance: 0 t\n", - "properties: trans-labels explicit-labels state-acc deterministic\n", - "spot-state-player: 0 0 1 1 1 1\n", - "controllable-AP: 2\n", - "--BODY--\n", - "State: 0\n", - "[0&1] 2\n", - "[0&!1] 2\n", - "[!0&1] 3\n", - "[!0&!1] 3\n", - "State: 1\n", + "[!0&!1] 2\n", "[!0&1] 4\n", - "[0&1] 5\n", - "[!0&!1] 4\n", - "[0&!1] 5\n", - "State: 2\n", - "[!2] 1\n", - "State: 3\n", - "[!2] 0\n", - "State: 4\n", - "[2] 0\n", - "State: 5\n", - "[2] 1\n", - "--END--\n", - "HOA: v1\n", - "States: 6\n", - "Start: 1\n", - "AP: 3 \"i1\" \"i0\" \"o0\"\n", - "acc-name: all\n", - "Acceptance: 0 t\n", - "properties: trans-labels explicit-labels state-acc deterministic\n", - "spot-state-player: 0 0 1 1 1 1\n", - "controllable-AP: 2\n", - "--BODY--\n", - "State: 0\n", - "[0] 2\n", - "[!0] 3\n", + "[0&!1] 6\n", + "[0&1] 8\n", "State: 1\n", - "[0] 4\n", - "[!0] 5\n", + "[0&1] 13\n", + "[0&!1] 18\n", + "[!0&1] 19\n", + "[!0&!1] 20\n", "State: 2\n", - "[!2] 1\n", + "[t] 1\n", "State: 3\n", - "[!2] 0\n", + "[0] 13\n", + "[!0] 19\n", "State: 4\n", - "[2] 1\n", + "[t] 3\n", "State: 5\n", - "[2] 0\n", + "[!0&1] 10\n", + "[0&1] 11\n", + "[!0&!1] 15\n", + "[0&!1] 16\n", + "State: 6\n", + "[t] 5\n", + "State: 7\n", + "[!0] 10\n", + "[0] 11\n", + "State: 8\n", + "[t] 7\n", + "State: 9\n", + "[!0] 12\n", + "[0] 13\n", + "State: 10\n", + "[2] 9\n", + "State: 11\n", + "[2] 7\n", + "State: 12\n", + "[!2] 9\n", + "State: 13\n", + "[!2] 7\n", + "State: 14\n", + "[!0&1] 12\n", + "[0&1] 13\n", + "[!0&!1] 17\n", + "[0&!1] 18\n", + "State: 15\n", + "[2] 14\n", + "State: 16\n", + "[2] 5\n", + "State: 17\n", + "[!2] 14\n", + "State: 18\n", + "[!2] 5\n", + "State: 19\n", + "[!2] 3\n", + "State: 20\n", + "[!2] 1\n", "--END--\n", "HOA: v1\n", - "States: 2\n", + "States: 7\n", "Start: 0\n", "AP: 3 \"i1\" \"i0\" \"o0\"\n", "acc-name: all\n", @@ -306,36 +314,277 @@ "controllable-AP: 2\n", "--BODY--\n", "State: 0\n", - "[0&2] 0\n", - "[!0&2] 1\n", + "[!0&!1] 1\n", + "[!0&1] 2\n", + "[0&!1] 3\n", + "[0&1] 4\n", "State: 1\n", - "[0&!2] 0\n", - "[!0&!2] 1\n", + "[0&1&!2] 4\n", + "[0&!1&!2] 3\n", + "[!0&1&!2] 2\n", + "[!0&!1&!2] 1\n", + "State: 2\n", + "[0&!2] 4\n", + "[!0&!2] 2\n", + "State: 3\n", + "[!0&1&2] 5\n", + "[0&1&2] 4\n", + "[!0&!1&2] 6\n", + "[0&!1&2] 3\n", + "State: 4\n", + "[!0&2] 5\n", + "[0&2] 4\n", + "State: 5\n", + "[!0&!2] 5\n", + "[0&!2] 4\n", + "State: 6\n", + "[!0&1&!2] 5\n", + "[0&1&!2] 4\n", + "[!0&!1&!2] 6\n", + "[0&!1&!2] 3\n", "--END--\n", "HOA: v1\n", - "States: 6\n", - "Start: 1\n", + "States: 21\n", + "Start: 0\n", "AP: 3 \"i1\" \"i0\" \"o0\"\n", "acc-name: all\n", "Acceptance: 0 t\n", "properties: trans-labels explicit-labels state-acc deterministic\n", - "spot-state-player: 0 0 1 1 1 1\n", + "spot-state-player: 0 0 1 0 1 0 1 0 1 0 1 1 1 1 0 1 1 1 1 1 1\n", "controllable-AP: 2\n", "--BODY--\n", "State: 0\n", - "[0] 2\n", - "[!0] 3\n", + "[!0&!1] 2\n", + "[!0&1] 4\n", + "[0&!1] 6\n", + "[0&1] 8\n", "State: 1\n", - "[0] 4\n", - "[!0] 5\n", + "[0&1] 13\n", + "[0&!1] 18\n", + "[!0&1] 19\n", + "[!0&!1] 20\n", "State: 2\n", - "[!2] 1\n", + "[t] 1\n", "State: 3\n", - "[!2] 0\n", + "[0] 13\n", + "[!0] 19\n", "State: 4\n", - "[2] 1\n", + "[t] 3\n", "State: 5\n", - "[2] 0\n", + "[!0&1] 10\n", + "[0&1] 11\n", + "[!0&!1] 15\n", + "[0&!1] 16\n", + "State: 6\n", + "[t] 5\n", + "State: 7\n", + "[!0] 10\n", + "[0] 11\n", + "State: 8\n", + "[t] 7\n", + "State: 9\n", + "[!0] 12\n", + "[0] 13\n", + "State: 10\n", + "[2] 9\n", + "State: 11\n", + "[2] 7\n", + "State: 12\n", + "[!2] 9\n", + "State: 13\n", + "[!2] 7\n", + "State: 14\n", + "[!0&1] 12\n", + "[0&1] 13\n", + "[!0&!1] 17\n", + "[0&!1] 18\n", + "State: 15\n", + "[2] 14\n", + "State: 16\n", + "[2] 5\n", + "State: 17\n", + "[!2] 14\n", + "State: 18\n", + "[!2] 5\n", + "State: 19\n", + "[!2] 3\n", + "State: 20\n", + "[!2] 1\n", + "--END--\n", + "HOA: v1\n", + "States: 21\n", + "Start: 0\n", + "AP: 3 \"i1\" \"i0\" \"o0\"\n", + "acc-name: all\n", + "Acceptance: 0 t\n", + "properties: trans-labels explicit-labels state-acc deterministic\n", + "spot-state-player: 0 0 1 0 1 0 1 0 1 0 1 1 1 1 0 1 1 1 1 1 1\n", + "controllable-AP: 2\n", + "--BODY--\n", + "State: 0\n", + "[!0&!1] 2\n", + "[!0&1] 4\n", + "[0&!1] 6\n", + "[0&1] 8\n", + "State: 1\n", + "[0&1] 13\n", + "[0&!1] 18\n", + "[!0&1] 19\n", + "[!0&!1] 20\n", + "State: 2\n", + "[t] 1\n", + "State: 3\n", + "[0] 13\n", + "[!0] 19\n", + "State: 4\n", + "[t] 3\n", + "State: 5\n", + "[!0&1] 10\n", + "[0&1] 11\n", + "[!0&!1] 15\n", + "[0&!1] 16\n", + "State: 6\n", + "[t] 5\n", + "State: 7\n", + "[!0] 10\n", + "[0] 11\n", + "State: 8\n", + "[t] 7\n", + "State: 9\n", + "[!0] 12\n", + "[0] 13\n", + "State: 10\n", + "[2] 9\n", + "State: 11\n", + "[2] 7\n", + "State: 12\n", + "[!2] 9\n", + "State: 13\n", + "[!2] 7\n", + "State: 14\n", + "[!0&1] 12\n", + "[0&1] 13\n", + "[!0&!1] 17\n", + "[0&!1] 18\n", + "State: 15\n", + "[2] 14\n", + "State: 16\n", + "[2] 5\n", + "State: 17\n", + "[!2] 14\n", + "State: 18\n", + "[!2] 5\n", + "State: 19\n", + "[!2] 3\n", + "State: 20\n", + "[!2] 1\n", + "--END--\n", + "HOA: v1\n", + "States: 7\n", + "Start: 0\n", + "AP: 3 \"i1\" \"i0\" \"o0\"\n", + "acc-name: all\n", + "Acceptance: 0 t\n", + "properties: trans-labels explicit-labels state-acc deterministic\n", + "controllable-AP: 2\n", + "--BODY--\n", + "State: 0\n", + "[!0&!1] 1\n", + "[!0&1] 2\n", + "[0&!1] 3\n", + "[0&1] 4\n", + "State: 1\n", + "[0&1&!2] 4\n", + "[0&!1&!2] 3\n", + "[!0&1&!2] 2\n", + "[!0&!1&!2] 1\n", + "State: 2\n", + "[0&!2] 4\n", + "[!0&!2] 2\n", + "State: 3\n", + "[!0&1&2] 5\n", + "[0&1&2] 4\n", + "[!0&!1&2] 6\n", + "[0&!1&2] 3\n", + "State: 4\n", + "[!0&2] 5\n", + "[0&2] 4\n", + "State: 5\n", + "[!0&!2] 5\n", + "[0&!2] 4\n", + "State: 6\n", + "[!0&1&!2] 5\n", + "[0&1&!2] 4\n", + "[!0&!1&!2] 6\n", + "[0&!1&!2] 3\n", + "--END--\n", + "HOA: v1\n", + "States: 21\n", + "Start: 0\n", + "AP: 3 \"i1\" \"i0\" \"o0\"\n", + "acc-name: all\n", + "Acceptance: 0 t\n", + "properties: trans-labels explicit-labels state-acc deterministic\n", + "spot-state-player: 0 0 1 0 1 0 1 0 1 0 1 1 1 1 0 1 1 1 1 1 1\n", + "controllable-AP: 2\n", + "--BODY--\n", + "State: 0\n", + "[!0&!1] 2\n", + "[!0&1] 4\n", + "[0&!1] 6\n", + "[0&1] 8\n", + "State: 1\n", + "[0&1] 13\n", + "[0&!1] 18\n", + "[!0&1] 19\n", + "[!0&!1] 20\n", + "State: 2\n", + "[t] 1\n", + "State: 3\n", + "[0] 13\n", + "[!0] 19\n", + "State: 4\n", + "[t] 3\n", + "State: 5\n", + "[!0&1] 10\n", + "[0&1] 11\n", + "[!0&!1] 15\n", + "[0&!1] 16\n", + "State: 6\n", + "[t] 5\n", + "State: 7\n", + "[!0] 10\n", + "[0] 11\n", + "State: 8\n", + "[t] 7\n", + "State: 9\n", + "[!0] 12\n", + "[0] 13\n", + "State: 10\n", + "[2] 9\n", + "State: 11\n", + "[2] 7\n", + "State: 12\n", + "[!2] 9\n", + "State: 13\n", + "[!2] 7\n", + "State: 14\n", + "[!0&1] 12\n", + "[0&1] 13\n", + "[!0&!1] 17\n", + "[0&!1] 18\n", + "State: 15\n", + "[2] 14\n", + "State: 16\n", + "[2] 5\n", + "State: 17\n", + "[!2] 14\n", + "State: 18\n", + "[!2] 5\n", + "State: 19\n", + "[!2] 3\n", + "State: 20\n", + "[!2] 1\n", "--END--\n" ] } @@ -345,7 +594,7 @@ "mm0 = spot.solved_game_to_mealy(game, si)\n", "msep0 = spot.solved_game_to_separated_mealy(game, si)\n", "msplit0 = spot.solved_game_to_split_mealy(game, si)\n", - "assert(spot.is_separated_mealy(mm0)) #Not imposed by the functions pre or post, but results of current impl, change if necessary\n", + "assert(spot.is_mealy(mm0))\n", "assert(spot.is_separated_mealy(msep0))\n", "assert(spot.is_split_mealy(msplit0))\n", "print(mm0.to_str(\"hoa\"))\n", @@ -355,7 +604,7 @@ "mm2 = spot.solved_game_to_mealy(game, si)\n", "msep2 = spot.solved_game_to_separated_mealy(game, si)\n", "msplit2 = spot.solved_game_to_split_mealy(game, si)\n", - "assert(spot.is_separated_mealy(mm2)) #Not imposed by the functions pre or post, but results of current impl, change if necessary\n", + "assert(spot.is_mealy(mm2))\n", "assert(spot.is_separated_mealy(msep2))\n", "assert(spot.is_split_mealy(msplit2))\n", "print(mm2.to_str(\"hoa\"))\n", @@ -365,7 +614,7 @@ "mm3 = spot.solved_game_to_mealy(game, si)\n", "msep3 = spot.solved_game_to_separated_mealy(game, si)\n", "msplit3 = spot.solved_game_to_split_mealy(game, si)\n", - "assert(spot.is_split_mealy(mm3)) #Not imposed by the functions pre or post, but results of current impl, change if necessary\n", + "assert(spot.is_mealy(mm3))\n", "assert(spot.is_separated_mealy(msep3))\n", "assert(spot.is_split_mealy(msplit3))\n", "print(mm3.to_str(\"hoa\"))\n", @@ -376,31 +625,48 @@ { "cell_type": "code", "execution_count": 4, + "id": "fb57ac53", "metadata": {}, "outputs": [], "source": [ "mus0 = spot.unsplit_mealy(msplit0)\n", "mus2 = spot.unsplit_mealy(msplit2)\n", - "mus3 = spot.unsplit_mealy(msplit3)\n", - "mmus3 = spot.unsplit_mealy(mm3)" + "mus3 = spot.unsplit_mealy(msplit3)" ] }, { "cell_type": "code", "execution_count": 5, + "id": "40fc65b5", "metadata": {}, "outputs": [], "source": [ - "assert(mm0.equivalent_to(msep0))\n", - "assert(mm0.equivalent_to(mus0))\n", - "assert(mm2.equivalent_to(msep2))\n", - "assert(mm2.equivalent_to(mus2))\n", - "assert(mmus3.equivalent_to(msep3))\n", - "assert(mmus3.equivalent_to(mus3))" + "assert(mus0.equivalent_to(msep0))" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "f6d8b29c", + "metadata": {}, + "outputs": [], + "source": [ + "assert(mus2.equivalent_to(msep2))" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "db8d47f2", + "metadata": {}, + "outputs": [], + "source": [ + "assert(mus3.equivalent_to(msep3))" ] }, { "cell_type": "markdown", + "id": "c19beeb0", "metadata": {}, "source": [ "Testing related to #495" @@ -408,7 +674,8 @@ }, { "cell_type": "code", - "execution_count": 6, + "execution_count": 8, + "id": "3736cd1b", "metadata": {}, "outputs": [ { @@ -470,10 +737,10 @@ "\n" ], "text/plain": [ - " *' at 0x7fee3716f7b0> >" + " *' at 0x7f7458055570> >" ] }, - "execution_count": 6, + "execution_count": 8, "metadata": {}, "output_type": "execute_result" } @@ -486,7 +753,8 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 9, + "id": "da6a7802", "metadata": {}, "outputs": [ { @@ -552,10 +820,10 @@ "\n" ], "text/plain": [ - " *' at 0x7fee3716f7b0> >" + " *' at 0x7f7458055570> >" ] }, - "execution_count": 7, + "execution_count": 9, "metadata": {}, "output_type": "execute_result" } @@ -567,7 +835,8 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 10, + "id": "987219a4", "metadata": {}, "outputs": [ { @@ -675,10 +944,10 @@ "\n" ], "text/plain": [ - " *' at 0x7fee3716f630> >" + " *' at 0x7f74580553c0> >" ] }, - "execution_count": 8, + "execution_count": 10, "metadata": {}, "output_type": "execute_result" } @@ -691,7 +960,8 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 11, + "id": "958d81f2", "metadata": {}, "outputs": [ { @@ -772,10 +1042,10 @@ "\n" ], "text/plain": [ - " *' at 0x7fee36703f60> >" + " *' at 0x7f743a5ca6c0> >" ] }, - "execution_count": 9, + "execution_count": 11, "metadata": {}, "output_type": "execute_result" } @@ -798,7 +1068,8 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 12, + "id": "078bb43e", "metadata": {}, "outputs": [ { @@ -939,10 +1210,10 @@ "\n" ], "text/plain": [ - " *' at 0x7fee3716f930> >" + " *' at 0x7f7458059f90> >" ] }, - "execution_count": 10, + "execution_count": 12, "metadata": {}, "output_type": "execute_result" } @@ -955,7 +1226,8 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 13, + "id": "05b4a138", "metadata": {}, "outputs": [ { @@ -1147,10 +1419,10 @@ "\n" ], "text/plain": [ - " *' at 0x7fee3716fa20> >" + " *' at 0x7f7458055870> >" ] }, - "execution_count": 11, + "execution_count": 13, "metadata": {}, "output_type": "execute_result" } @@ -1164,7 +1436,7 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 3", + "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, diff --git a/tests/python/synthesis.ipynb b/tests/python/synthesis.ipynb index 7ec181ebe..47cafbf41 100644 --- a/tests/python/synthesis.ipynb +++ b/tests/python/synthesis.ipynb @@ -9,7 +9,8 @@ "source": [ "import spot\n", "spot.setup()\n", - "from spot.jupyter import display_inline" + "from spot.jupyter import display_inline\n", + "import time" ] }, { @@ -696,7 +697,7 @@ "\n" ], "text/plain": [ - " *' at 0x7f05083f22a0> >" + " *' at 0x7f7a0c2640c0> >" ] }, "metadata": {}, @@ -1668,7 +1669,7 @@ "\n" ], "text/plain": [ - "" + " *' at 0x7f7a0c264e40> >" ] }, "metadata": {}, @@ -1855,7 +1856,7 @@ "\n" ], "text/plain": [ - "" + " *' at 0x7f7a0c264d50> >" ] }, "metadata": {}, @@ -1992,7 +1993,7 @@ "\n" ], "text/plain": [ - "" + " *' at 0x7f7a0c2643c0> >" ] }, "metadata": {}, @@ -2085,7 +2086,7 @@ "\n" ], "text/plain": [ - "" + " *' at 0x7f7a0c264a20> >" ] }, "metadata": {}, @@ -2178,7 +2179,7 @@ "\n" ], "text/plain": [ - "" + " *' at 0x7f7a0c264ea0> >" ] }, "metadata": {}, @@ -2315,7 +2316,7 @@ "\n" ], "text/plain": [ - "" + " *' at 0x7f7a0c2646f0> >" ] }, "metadata": {}, @@ -2342,8 +2343,9 @@ "for i in range(6):\n", " print(\"simplification lvl \", descr[i])\n", " si.minimize_lvl = i\n", - " mealy = spot.solved_game_to_separated_mealy(game, si)\n", - " display(mealy.show())" + " mealy = spot.solved_game_to_mealy(game, si)\n", + " spot.simplify_mealy_here(mealy, si.minimize_lvl, False)\n", + " display(mealy)" ] }, { @@ -2714,7 +2716,7 @@ "\n" ], "text/plain": [ - " >" + " >" ] }, "metadata": {}, @@ -3027,7 +3029,7 @@ "\n" ], "text/plain": [ - " *' at 0x7f05082c7a20> >" + " *' at 0x7f7a0c009f00> >" ] }, "metadata": {}, @@ -3042,6 +3044,203 @@ "\n", "\n", + "\n", + "\n", + "\n", + "t\n", + "[all]\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "2\n", + "\n", + "2\n", + "\n", + "\n", + "\n", + "0->2\n", + "\n", + "\n", + "i0\n", + "\n", + "\n", + "\n", + "4\n", + "\n", + "4\n", + "\n", + "\n", + "\n", + "0->4\n", + "\n", + "\n", + "!i0\n", + "\n", + "\n", + "\n", + "1\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "2->1\n", + "\n", + "\n", + "o0\n", + "\n", + "\n", + "\n", + "3\n", + "\n", + "3\n", + "\n", + "\n", + "\n", + "4->3\n", + "\n", + "\n", + "!o0\n", + "\n", + "\n", + "\n", + "5\n", + "\n", + "5\n", + "\n", + "\n", + "\n", + "1->5\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "5->1\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "3->4\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n" + ], + "text/plain": [ + " *' at 0x7f7a0c009180> >" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "t\n", + "[all]\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "2\n", + "\n", + "2\n", + "\n", + "\n", + "\n", + "0->2\n", + "\n", + "\n", + "i0\n", + "\n", + "\n", + "\n", + "3\n", + "\n", + "3\n", + "\n", + "\n", + "\n", + "0->3\n", + "\n", + "\n", + "!i0\n", + "\n", + "\n", + "\n", + "2->0\n", + "\n", + "\n", + "o0\n", + "\n", + "\n", + "\n", + "1\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "3->1\n", + "\n", + "\n", + "!o0\n", + "\n", + "\n", + "\n", + "1->3\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "
\n", + "\n", + "\n", + "\n", "\n", "\n", @@ -3099,10 +3298,11 @@ "!o0\n", "\n", "\n", - "\n" + "\n", + "
" ], "text/plain": [ - " *' at 0x7f05083f2300> >" + "" ] }, "metadata": {}, @@ -3179,7 +3379,7 @@ "\n" ], "text/plain": [ - " >" + " >" ] }, "metadata": {}, @@ -3191,8 +3391,10 @@ "spot.solve_game(game)\n", "spot.highlight_strategy(game)\n", "display(game)\n", - "mealy = spot.solved_game_to_separated_mealy(game)\n", + "mealy = spot.solved_game_to_mealy(game)\n", "display(mealy)\n", + "spot.simplify_mealy_here(mealy, 2, True)\n", + "display_inline(mealy, spot.unsplit_mealy(mealy))\n", "aig = spot.mealy_machine_to_aig(mealy, \"isop\")\n", "display(aig)" ] @@ -3306,7 +3508,7 @@ "\n" ], "text/plain": [ - " >" + " >" ] }, "metadata": {}, @@ -3738,7 +3940,7 @@ "\n" ], "text/plain": [ - " >" + " >" ] }, "metadata": {}, @@ -3803,7 +4005,7 @@ "\n" ], "text/plain": [ - " *' at 0x7f05082c7ba0> >" + " *' at 0x7f7a0c009de0> >" ] }, "metadata": {}, @@ -3916,7 +4118,7 @@ "\n" ], "text/plain": [ - " >" + " >" ] }, "metadata": {}, @@ -4096,7 +4298,7 @@ "\n" ], "text/plain": [ - " >" + " >" ] }, "metadata": {}, @@ -4220,7 +4422,7 @@ "\n" ], "text/plain": [ - " *' at 0x7f05082c7690> >" + " *' at 0x7f7a0c2640c0> >" ] }, "execution_count": 16, From bb7072402a4eb9865baaa0a424a6f8613caa214b Mon Sep 17 00:00:00 2001 From: Philipp Schlehuber-Caissier Date: Mon, 21 Mar 2022 10:51:35 +0100 Subject: [PATCH 014/337] Removing eeroneaus test * tests/python/except.py: Here --- tests/python/except.py | 17 ----------------- 1 file changed, 17 deletions(-) diff --git a/tests/python/except.py b/tests/python/except.py index 3aeee7a3e..8674721c9 100644 --- a/tests/python/except.py +++ b/tests/python/except.py @@ -331,20 +331,3 @@ except RuntimeError as e: tc.assertIn("already registered", se) else: report_missing_exception() - - -si = spot.synthesis_info() -si.s = spot.synthesis_info.algo_LAR -g1 = spot.ltl_to_game("G((i0 xor i1) <-> o0)", ["o0"], si) -g2 = spot.ltl_to_game("G((i0 xor i1) <-> (!o0 & !o1))", ["o0", "o1"], si) -spot.solve_game(g1) -spot.solve_game(g2) -strat1 = spot.solved_game_to_separated_mealy(g1) -strat2 = spot.solved_game_to_separated_mealy(g2) -try: - stratcomp = spot.mealy_product(strat1, strat2) -except RuntimeError as e: - se = str(e) - tc.assertIn("Incompatible", se) -else: - report_missing_exception() \ No newline at end of file From 3ed337ec4642603b1ab7fad7566a7f0d3f274fe8 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Tue, 22 Mar 2022 12:18:25 +0100 Subject: [PATCH 015/337] graph: fix invalid read Reported by Florian Renkin. * spot/graph/graph.hh (sort_edges_of): Fix invalid read when sorting a state without successor. Seen on core/tgbagraph.test. --- spot/graph/graph.hh | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/spot/graph/graph.hh b/spot/graph/graph.hh index 75e0977b7..fa276131d 100644 --- a/spot/graph/graph.hh +++ b/spot/graph/graph.hh @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2014-2018, 2020, 2021 Laboratoire de Recherche et +// Copyright (C) 2014-2018, 2020-2022 Laboratoire de Recherche et // Développement de l'Epita. // // This file is part of Spot, a model checking library. @@ -1243,14 +1243,19 @@ namespace spot //dump_storage(std::cerr); auto pi = [&](unsigned t1, unsigned t2) {return p(edges_[t1], edges_[t2]); }; + + // Sort the outgoing edges of each selected state according + // to predicate p. Do that in place. std::vector sort_idx_; - for (unsigned i = 0; i < num_states(); ++i) + unsigned ns = num_states(); + for (unsigned i = 0; i < ns; ++i) { if (to_sort_ptr && !(*to_sort_ptr)[i]) continue; - - sort_idx_.clear(); unsigned t = states_[i].succ; + if (t == 0) + continue; + sort_idx_.clear(); do { sort_idx_.push_back(t); From e9c1aeaa54b7ab0c872f4de0f77f18abc034b6c3 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Tue, 22 Mar 2022 12:22:48 +0100 Subject: [PATCH 016/337] * spot/twaalgos/gfguarantee.hh: Typos in comments. --- spot/twaalgos/gfguarantee.hh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spot/twaalgos/gfguarantee.hh b/spot/twaalgos/gfguarantee.hh index 5124667f4..40cb16f97 100644 --- a/spot/twaalgos/gfguarantee.hh +++ b/spot/twaalgos/gfguarantee.hh @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2018 Laboratoire de Recherche et Développement +// Copyright (C) 2018, 2022 Laboratoire de Recherche et Développement // de l'Epita (LRDE). // // This file is part of Spot, a model checking library. @@ -48,7 +48,7 @@ namespace spot /// \brief Convert GF(φ) into a (D)BA if φ is a guarantee property. /// /// If the formula \a gf has the form GΦ where Φ matches either F(φ) - /// or F(φ₁)|F(φ₂)|...|F(φₙ), we translate Φ into A_Φ and attempt to + /// or F(φ₁)&F(φ₂)&...&F(φₙ), we translate Φ into A_Φ and attempt to /// minimize it to a WDBA W_Φ. If the resulting automaton is /// terminal, we then call g_f_terminal_inplace(W_Φ). If \a /// deterministic is not set, we keep the minimized automaton only From 0dd36e9a53a4c94ece27a95b6884cde6d8c18911 Mon Sep 17 00:00:00 2001 From: Florian Renkin Date: Mon, 21 Mar 2022 10:46:42 +0100 Subject: [PATCH 017/337] ltlsynt: don't fail if --outs or --ins is set to empty * bin/ltlsynt.cc: here * tests/core/ltlsynt.test: add tests --- bin/ltlsynt.cc | 58 +++++++++++++++++++++++------------------ tests/core/ltlsynt.test | 8 ++++++ 2 files changed, 40 insertions(+), 26 deletions(-) diff --git a/bin/ltlsynt.cc b/bin/ltlsynt.cc index 305c7a2f4..25b8bb04a 100644 --- a/bin/ltlsynt.cc +++ b/bin/ltlsynt.cc @@ -135,8 +135,8 @@ Exit status:\n\ 1 if the input problem is not realizable\n\ 2 if any error has been reported"; -static std::vector all_output_aps; -static std::vector all_input_aps; +static std::optional> all_output_aps; +static std::optional> all_input_aps; static const char* opt_csv = nullptr; static bool opt_print_pg = false; @@ -545,12 +545,12 @@ namespace class ltl_processor final : public job_processor { private: - std::vector input_aps_; - std::vector output_aps_; + std::optional> input_aps_; + std::optional> output_aps_; public: - ltl_processor(std::vector input_aps_, - std::vector output_aps_) + ltl_processor(std::optional> input_aps_, + std::optional> output_aps_) : input_aps_(std::move(input_aps_)), output_aps_(std::move(output_aps_)) { @@ -560,11 +560,13 @@ namespace const char* filename, int linenum) override { auto unknown_aps = [](spot::formula f, - const std::vector& known, - const std::vector* known2 = nullptr) + const std::optional>& known, + const std::optional>& known2 = {}) { std::vector unknown; std::set seen; + // If we don't have --ins and --outs, we must not find an AP. + bool can_have_ap = known.has_value(); f.traverse([&](const spot::formula& s) { if (s.is(spot::op::ap)) @@ -572,10 +574,11 @@ namespace if (!seen.insert(s).second) return false; const std::string& a = s.ap_name(); - if (std::find(known.begin(), known.end(), a) == known.end() - && (!known2 + if (!can_have_ap + || (std::find(known->begin(), known->end(), a) == known->end() + && (!known2.has_value() || std::find(known2->begin(), - known2->end(), a) == known2->end())) + known2->end(), a) == known2->end()))) unknown.push_back(a); } return false; @@ -585,30 +588,30 @@ namespace // Decide which atomic propositions are input or output. int res; - if (input_aps_.empty() && !output_aps_.empty()) + if (!input_aps_.has_value() && output_aps_.has_value()) { - res = solve_formula(f, unknown_aps(f, output_aps_), output_aps_); + res = solve_formula(f, unknown_aps(f, output_aps_), *output_aps_); } - else if (output_aps_.empty() && !input_aps_.empty()) + else if (!output_aps_.has_value() && input_aps_.has_value()) { - res = solve_formula(f, input_aps_, unknown_aps(f, input_aps_)); + res = solve_formula(f, *input_aps_, unknown_aps(f, input_aps_)); } - else if (output_aps_.empty() && input_aps_.empty()) + else if (!output_aps_.has_value() && !input_aps_.has_value()) { - for (const std::string& ap: unknown_aps(f, input_aps_, &output_aps_)) + for (const std::string& ap: unknown_aps(f, input_aps_, output_aps_)) error_at_line(2, 0, filename, linenum, "one of --ins or --outs should list '%s'", ap.c_str()); - res = solve_formula(f, input_aps_, output_aps_); + res = solve_formula(f, *input_aps_, *output_aps_); } else { - for (const std::string& ap: unknown_aps(f, input_aps_, &output_aps_)) + for (const std::string& ap: unknown_aps(f, input_aps_, output_aps_)) error_at_line(2, 0, filename, linenum, "both --ins and --outs are specified, " "but '%s' is unlisted", ap.c_str()); - res = solve_formula(f, input_aps_, output_aps_); + res = solve_formula(f, *input_aps_, *output_aps_); } if (opt_csv) @@ -639,23 +642,25 @@ parse_opt(int key, char *arg, struct argp_state *) break; case OPT_INPUT: { + all_input_aps.emplace(std::vector{}); std::istringstream aps(arg); std::string ap; while (std::getline(aps, ap, ',')) { ap.erase(remove_if(ap.begin(), ap.end(), isspace), ap.end()); - all_input_aps.push_back(str_tolower(ap)); + all_input_aps->push_back(str_tolower(ap)); } break; } case OPT_OUTPUT: { + all_output_aps.emplace(std::vector{}); std::istringstream aps(arg); std::string ap; while (std::getline(aps, ap, ',')) { ap.erase(remove_if(ap.begin(), ap.end(), isspace), ap.end()); - all_output_aps.push_back(str_tolower(ap)); + all_output_aps->push_back(str_tolower(ap)); } break; } @@ -716,10 +721,11 @@ main(int argc, char **argv) check_no_formula(); // Check if inputs and outputs are distinct - for (const std::string& ai : all_input_aps) - if (std::find(all_output_aps.begin(), all_output_aps.end(), ai) - != all_output_aps.end()) - error(2, 0, "'%s' appears both in --ins and --outs", ai.c_str()); + if (all_input_aps.has_value() && all_output_aps.has_value()) + for (const std::string& ai : *all_input_aps) + if (std::find(all_output_aps->begin(), all_output_aps->end(), ai) + != all_output_aps->end()) + error(2, 0, "'%s' appears both in --ins and --outs", ai.c_str()); ltl_processor processor(all_input_aps, all_output_aps); if (int res = processor.run(); res == 0 || res == 1) diff --git a/tests/core/ltlsynt.test b/tests/core/ltlsynt.test index 335c1b01e..c22cd4e6b 100644 --- a/tests/core/ltlsynt.test +++ b/tests/core/ltlsynt.test @@ -829,3 +829,11 @@ ltlsynt -f '!(F(a | b))' --outs=b, --decompose=yes \ --verbose --aiger 2> out || true sed 's/ [0-9.e-]* seconds/ X seconds/g' out > outx diff outx exp + +ltlsynt --ins="" -f "GFa" +ltlsynt --outs="" -f "GFb" | grep "UNREALIZABLE" + +ltlsynt --outs="" -f "1" + +ltlsynt --outs="" --ins="" -f "GFa" 2>&1 | \ + grep "both --ins and --outs are specified" \ No newline at end of file From dd587476593a8b179d1fafaf24d7dfccf517e355 Mon Sep 17 00:00:00 2001 From: Florian Renkin Date: Mon, 21 Mar 2022 13:56:50 +0100 Subject: [PATCH 018/337] synthesis.ipynb: remove useless import * tests/python/synthesis.ipynb: here. --- tests/python/synthesis.ipynb | 3589 +++++++++++++++++----------------- 1 file changed, 1778 insertions(+), 1811 deletions(-) diff --git a/tests/python/synthesis.ipynb b/tests/python/synthesis.ipynb index 47cafbf41..3738e6f72 100644 --- a/tests/python/synthesis.ipynb +++ b/tests/python/synthesis.ipynb @@ -3,19 +3,16 @@ { "cell_type": "code", "execution_count": 1, - "id": "1b9b5964", "metadata": {}, "outputs": [], "source": [ "import spot\n", "spot.setup()\n", - "from spot.jupyter import display_inline\n", - "import time" + "from spot.jupyter import display_inline" ] }, { "cell_type": "markdown", - "id": "b465c6ba", "metadata": {}, "source": [ "This notebook presents functions that can be used to solve the Reactive Synthesis problem using games.\n", @@ -40,7 +37,6 @@ { "cell_type": "code", "execution_count": 2, - "id": "8576bdad", "metadata": {}, "outputs": [ { @@ -57,647 +53,647 @@ "\n", "\n", - "\n", "\n", - "\n", - "\n", - "\n", - "Fin(\n", - "\n", - ") & (Inf(\n", - "\n", - ") | Fin(\n", - "\n", - "))\n", - "[parity max odd 3]\n", + "\n", + "\n", + "\n", + "Fin(\n", + "\n", + ") & (Inf(\n", + "\n", + ") | Fin(\n", + "\n", + "))\n", + "[parity max odd 3]\n", "\n", "\n", "\n", "9\n", - "\n", - "9\n", + "\n", + "9\n", "\n", "\n", "\n", "I->9\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "25\n", - "\n", - "25\n", + "\n", + "25\n", "\n", "\n", "\n", "9->25\n", - "\n", - "\n", - "!i0 & !i1\n", - "\n", + "\n", + "\n", + "!i0 & !i1\n", + "\n", "\n", "\n", "\n", "26\n", - "\n", - "26\n", + "\n", + "26\n", "\n", "\n", "\n", "9->26\n", - "\n", - "\n", - "i0 & !i1\n", - "\n", + "\n", + "\n", + "i0 & !i1\n", + "\n", "\n", "\n", "\n", "27\n", - "\n", - "27\n", + "\n", + "27\n", "\n", "\n", "\n", "9->27\n", - "\n", - "\n", - "!i0 & i1\n", - "\n", + "\n", + "\n", + "!i0 & i1\n", + "\n", "\n", "\n", "\n", "28\n", - "\n", - "28\n", + "\n", + "28\n", "\n", "\n", "\n", "9->28\n", - "\n", - "\n", - "i0 & i1\n", - "\n", + "\n", + "\n", + "i0 & i1\n", + "\n", "\n", "\n", "\n", "0\n", - "\n", - "0\n", + "\n", + "0\n", "\n", "\n", "\n", "10\n", - "\n", - "10\n", + "\n", + "10\n", "\n", "\n", "\n", "0->10\n", - "\n", - "\n", - "!i1\n", - "\n", + "\n", + "\n", + "!i1\n", + "\n", "\n", "\n", "\n", "11\n", - "\n", - "11\n", + "\n", + "11\n", "\n", "\n", "\n", "0->11\n", - "\n", - "\n", - "i1\n", - "\n", + "\n", + "\n", + "i1\n", + "\n", "\n", "\n", "\n", "1\n", - "\n", - "1\n", + "\n", + "1\n", "\n", "\n", "\n", "10->1\n", - "\n", - "\n", - "o0\n", - "\n", + "\n", + "\n", + "o0\n", + "\n", "\n", "\n", "\n", "11->0\n", - "\n", - "\n", - "o0\n", - "\n", + "\n", + "\n", + "o0\n", + "\n", "\n", "\n", "\n", "12\n", - "\n", - "12\n", + "\n", + "12\n", "\n", "\n", "\n", "1->12\n", - "\n", - "\n", - "!i1\n", - "\n", + "\n", + "\n", + "!i1\n", + "\n", "\n", "\n", "\n", "13\n", - "\n", - "13\n", + "\n", + "13\n", "\n", "\n", "\n", "1->13\n", - "\n", - "\n", - "i1\n", - "\n", + "\n", + "\n", + "i1\n", + "\n", "\n", "\n", "\n", "12->1\n", - "\n", - "\n", - "!o0\n", - "\n", + "\n", + "\n", + "!o0\n", + "\n", "\n", "\n", "\n", "13->0\n", - "\n", - "\n", - "!o0\n", - "\n", + "\n", + "\n", + "!o0\n", + "\n", "\n", "\n", "\n", "2\n", - "\n", - "2\n", + "\n", + "2\n", "\n", "\n", "\n", "14\n", - "\n", - "14\n", + "\n", + "14\n", "\n", "\n", "\n", "2->14\n", - "\n", - "\n", - "i1\n", - "\n", + "\n", + "\n", + "i1\n", + "\n", "\n", "\n", "\n", "16\n", - "\n", - "16\n", + "\n", + "16\n", "\n", "\n", "\n", "2->16\n", - "\n", - "\n", - "!i1\n", - "\n", + "\n", + "\n", + "!i1\n", + "\n", "\n", "\n", "\n", "15\n", - "\n", - "15\n", + "\n", + "15\n", "\n", "\n", "\n", "14->15\n", - "\n", - "\n", - "1\n", - "\n", + "\n", + "\n", + "1\n", + "\n", "\n", "\n", "\n", "16->2\n", - "\n", - "\n", - "1\n", - "\n", + "\n", + "\n", + "1\n", + "\n", "\n", "\n", "\n", "3\n", - "\n", - "3\n", + "\n", + "3\n", "\n", "\n", "\n", "3->13\n", - "\n", - "\n", - "i1\n", - "\n", + "\n", + "\n", + "i1\n", + "\n", "\n", "\n", "\n", "17\n", - "\n", - "17\n", + "\n", + "17\n", "\n", "\n", "\n", "3->17\n", - "\n", - "\n", - "!i1\n", - "\n", + "\n", + "\n", + "!i1\n", + "\n", "\n", "\n", "\n", "17->2\n", - "\n", - "\n", - "o0\n", - "\n", + "\n", + "\n", + "o0\n", + "\n", "\n", "\n", "\n", "17->3\n", - "\n", - "\n", - "!o0\n", - "\n", + "\n", + "\n", + "!o0\n", + "\n", "\n", "\n", "\n", "4\n", - "\n", - "4\n", + "\n", + "4\n", "\n", "\n", "\n", "4->14\n", - "\n", - "\n", - "i0\n", - "\n", + "\n", + "\n", + "i0\n", + "\n", "\n", "\n", "\n", "18\n", - "\n", - "18\n", + "\n", + "18\n", "\n", "\n", "\n", "4->18\n", - "\n", - "\n", - "!i0\n", - "\n", + "\n", + "\n", + "!i0\n", + "\n", "\n", "\n", "\n", "18->4\n", - "\n", - "\n", - "1\n", - "\n", + "\n", + "\n", + "1\n", + "\n", "\n", "\n", "\n", "5\n", - "\n", - "5\n", + "\n", + "5\n", "\n", "\n", "\n", "5->14\n", - "\n", - "\n", - "i0 & i1\n", - "\n", + "\n", + "\n", + "i0 & i1\n", + "\n", "\n", "\n", "\n", "5->16\n", - "\n", - "\n", - "i0 & !i1\n", - "\n", + "\n", + "\n", + "i0 & !i1\n", + "\n", "\n", "\n", "\n", "5->18\n", - "\n", - "\n", - "!i0 & i1\n", - "\n", + "\n", + "\n", + "!i0 & i1\n", + "\n", "\n", "\n", "\n", "19\n", - "\n", - "19\n", + "\n", + "19\n", "\n", "\n", "\n", "5->19\n", - "\n", - "\n", - "!i0 & !i1\n", - "\n", + "\n", + "\n", + "!i0 & !i1\n", + "\n", "\n", "\n", "\n", "19->5\n", - "\n", - "\n", - "1\n", - "\n", + "\n", + "\n", + "1\n", + "\n", "\n", "\n", "\n", "6\n", - "\n", - "6\n", + "\n", + "6\n", "\n", "\n", "\n", "6->10\n", - "\n", - "\n", - "i0 & !i1\n", - "\n", + "\n", + "\n", + "i0 & !i1\n", + "\n", "\n", "\n", "\n", "6->11\n", - "\n", - "\n", - "i0 & i1\n", - "\n", + "\n", + "\n", + "i0 & i1\n", + "\n", "\n", "\n", "\n", "20\n", - "\n", - "20\n", + "\n", + "20\n", "\n", "\n", "\n", "6->20\n", - "\n", - "\n", - "!i0 & !i1\n", - "\n", + "\n", + "\n", + "!i0 & !i1\n", + "\n", "\n", "\n", "\n", "21\n", - "\n", - "21\n", + "\n", + "21\n", "\n", "\n", "\n", "6->21\n", - "\n", - "\n", - "!i0 & i1\n", - "\n", + "\n", + "\n", + "!i0 & i1\n", + "\n", "\n", "\n", "\n", "20->4\n", - "\n", - "\n", - "!o0\n", - "\n", + "\n", + "\n", + "!o0\n", + "\n", "\n", "\n", "\n", "7\n", - "\n", - "7\n", + "\n", + "7\n", "\n", "\n", "\n", "20->7\n", - "\n", - "\n", - "o0\n", - "\n", + "\n", + "\n", + "o0\n", + "\n", "\n", "\n", "\n", "21->4\n", - "\n", - "\n", - "!o0\n", - "\n", + "\n", + "\n", + "!o0\n", + "\n", "\n", "\n", "\n", "21->6\n", - "\n", - "\n", - "o0\n", - "\n", + "\n", + "\n", + "o0\n", + "\n", "\n", "\n", "\n", "7->12\n", - "\n", - "\n", - "i0 & !i1\n", - "\n", + "\n", + "\n", + "i0 & !i1\n", + "\n", "\n", "\n", "\n", "7->13\n", - "\n", - "\n", - "i0 & i1\n", - "\n", + "\n", + "\n", + "i0 & i1\n", + "\n", "\n", "\n", "\n", "22\n", - "\n", - "22\n", + "\n", + "22\n", "\n", "\n", "\n", "7->22\n", - "\n", - "\n", - "!i0 & !i1\n", - "\n", + "\n", + "\n", + "!i0 & !i1\n", + "\n", "\n", "\n", "\n", "23\n", - "\n", - "23\n", + "\n", + "23\n", "\n", "\n", "\n", "7->23\n", - "\n", - "\n", - "!i0 & i1\n", - "\n", + "\n", + "\n", + "!i0 & i1\n", + "\n", "\n", "\n", "\n", "22->4\n", - "\n", - "\n", - "o0\n", - "\n", + "\n", + "\n", + "o0\n", + "\n", "\n", "\n", "\n", "22->7\n", - "\n", - "\n", - "!o0\n", - "\n", + "\n", + "\n", + "!o0\n", + "\n", "\n", "\n", "\n", "23->4\n", - "\n", - "\n", - "o0\n", - "\n", + "\n", + "\n", + "o0\n", + "\n", "\n", "\n", "\n", "23->6\n", - "\n", - "\n", - "!o0\n", - "\n", + "\n", + "\n", + "!o0\n", + "\n", "\n", "\n", "\n", "8\n", - "\n", - "8\n", + "\n", + "8\n", "\n", "\n", "\n", "8->13\n", - "\n", - "\n", - "i0 & i1\n", - "\n", + "\n", + "\n", + "i0 & i1\n", + "\n", "\n", "\n", "\n", "8->17\n", - "\n", - "\n", - "i0 & !i1\n", - "\n", + "\n", + "\n", + "i0 & !i1\n", + "\n", "\n", "\n", "\n", "8->23\n", - "\n", - "\n", - "!i0 & i1\n", - "\n", + "\n", + "\n", + "!i0 & i1\n", + "\n", "\n", "\n", "\n", "24\n", - "\n", - "24\n", + "\n", + "24\n", "\n", "\n", "\n", "8->24\n", - "\n", - "\n", - "!i0 & !i1\n", - "\n", + "\n", + "\n", + "!i0 & !i1\n", + "\n", "\n", "\n", "\n", "24->5\n", - "\n", - "\n", - "o0\n", - "\n", + "\n", + "\n", + "o0\n", + "\n", "\n", "\n", "\n", "24->8\n", - "\n", - "\n", - "!o0\n", - "\n", + "\n", + "\n", + "!o0\n", + "\n", "\n", "\n", "\n", "25->8\n", - "\n", - "\n", - "1\n", - "\n", + "\n", + "\n", + "1\n", + "\n", "\n", "\n", "\n", "26->3\n", - "\n", - "\n", - "1\n", - "\n", + "\n", + "\n", + "1\n", + "\n", "\n", "\n", "\n", "27->6\n", - "\n", - "\n", - "1\n", - "\n", + "\n", + "\n", + "1\n", + "\n", "\n", "\n", "\n", "28->0\n", - "\n", - "\n", - "1\n", - "\n", + "\n", + "\n", + "1\n", + "\n", "\n", "\n", "\n", "15->14\n", - "\n", - "\n", - "1\n", - "\n", + "\n", + "\n", + "1\n", + "\n", "\n", "\n", "\n" ], "text/plain": [ - " *' at 0x7f7a0c2640c0> >" + " *' at 0x7fb964737d50> >" ] }, "metadata": {}, @@ -716,7 +712,6 @@ }, { "cell_type": "markdown", - "id": "fbd7095b", "metadata": {}, "source": [ "Solving the game, is done with `solve_game()` as with any game. There is also a version that takes a `synthesis_info` as second argument in case the time it takes has to be recorded. Here passing `si` or not makes no difference." @@ -725,7 +720,6 @@ { "cell_type": "code", "execution_count": 3, - "id": "02d7525e", "metadata": {}, "outputs": [ { @@ -741,586 +735,586 @@ "\n", "\n", - "\n", "\n", "\n", - "\n", - "\n", - "Fin(\n", - "\n", - ") & (Inf(\n", - "\n", - ") | Fin(\n", - "\n", - "))\n", - "[parity max odd 3]\n", + " viewBox=\"0.00 0.00 650.40 360.00\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n", + "\n", + "\n", + "Fin(\n", + "\n", + ") & (Inf(\n", + "\n", + ") | Fin(\n", + "\n", + "))\n", + "[parity max odd 3]\n", "\n", "\n", "\n", "9\n", - "\n", - "9\n", + "\n", + "9\n", "\n", "\n", "\n", "I->9\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "25\n", - "\n", - "25\n", + "\n", + "25\n", "\n", "\n", "\n", "9->25\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "26\n", - "\n", - "26\n", + "\n", + "26\n", "\n", "\n", "\n", "9->26\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "27\n", - "\n", - "27\n", + "\n", + "27\n", "\n", "\n", "\n", "9->27\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "28\n", - "\n", - "28\n", + "\n", + "28\n", "\n", "\n", "\n", "9->28\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "0\n", - "\n", - "0\n", + "\n", + "0\n", "\n", "\n", "\n", "10\n", - "\n", - "10\n", + "\n", + "10\n", "\n", "\n", "\n", "0->10\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "11\n", - "\n", - "11\n", + "\n", + "11\n", "\n", "\n", "\n", "0->11\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "1\n", - "\n", - "1\n", + "\n", + "1\n", "\n", "\n", "\n", "10->1\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "11->0\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "12\n", - "\n", - "12\n", + "\n", + "12\n", "\n", "\n", "\n", "1->12\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "13\n", - "\n", - "13\n", + "\n", + "13\n", "\n", "\n", "\n", "1->13\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "12->1\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "13->0\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "2\n", - "\n", - "2\n", + "\n", + "2\n", "\n", "\n", "\n", "14\n", - "\n", - "14\n", + "\n", + "14\n", "\n", "\n", "\n", "2->14\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "16\n", - "\n", - "16\n", + "\n", + "16\n", "\n", "\n", "\n", "2->16\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "15\n", - "\n", - "15\n", + "\n", + "15\n", "\n", "\n", "\n", "14->15\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "16->2\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "3\n", - "\n", - "3\n", + "\n", + "3\n", "\n", "\n", "\n", "3->13\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "17\n", - "\n", - "17\n", + "\n", + "17\n", "\n", "\n", "\n", "3->17\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "17->2\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "17->3\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "4\n", - "\n", - "4\n", + "\n", + "4\n", "\n", "\n", "\n", "4->14\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "18\n", - "\n", - "18\n", + "\n", + "18\n", "\n", "\n", "\n", "4->18\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "18->4\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "5\n", - "\n", - "5\n", + "\n", + "5\n", "\n", "\n", "\n", "5->14\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "5->16\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "5->18\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "19\n", - "\n", - "19\n", + "\n", + "19\n", "\n", "\n", "\n", "5->19\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "19->5\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "6\n", - "\n", - "6\n", + "\n", + "6\n", "\n", "\n", "\n", "6->10\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "6->11\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "20\n", - "\n", - "20\n", + "\n", + "20\n", "\n", "\n", "\n", "6->20\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "21\n", - "\n", - "21\n", + "\n", + "21\n", "\n", "\n", "\n", "6->21\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "20->4\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "7\n", - "\n", - "7\n", + "\n", + "7\n", "\n", "\n", "\n", "20->7\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "21->4\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "21->6\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "7->12\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "7->13\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "22\n", - "\n", - "22\n", + "\n", + "22\n", "\n", "\n", "\n", "7->22\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "23\n", - "\n", - "23\n", + "\n", + "23\n", "\n", "\n", "\n", "7->23\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "22->4\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "22->7\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "23->4\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "23->6\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "8\n", - "\n", - "8\n", + "\n", + "8\n", "\n", "\n", "\n", "8->13\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "8->17\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "8->23\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "24\n", - "\n", - "24\n", + "\n", + "24\n", "\n", "\n", "\n", "8->24\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "24->5\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "24->8\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "25->8\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "26->3\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "27->6\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "28->0\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "15->14\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n" @@ -1342,7 +1336,6 @@ }, { "cell_type": "markdown", - "id": "d66f3da1", "metadata": {}, "source": [ "Once a strategy has been found, it can be extracted as an automaton and simplified using 6 different levels (the default is 2). The output should be interpreted as a Mealy automaton, where transition have the form `(ins)&(outs)` where `ins` and `outs` are Boolean formulas representing possible inputs and outputs (they could be more than just conjunctions of atomic proposition). Mealy machines with this type of labels are called \"separated\" in Spot." @@ -1351,7 +1344,6 @@ { "cell_type": "code", "execution_count": 4, - "id": "89342e18", "metadata": {}, "outputs": [ { @@ -1367,309 +1359,309 @@ "\n", "\n", - "\n", "\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "0\n", - "\n", - "0\n", + "\n", + "0\n", "\n", "\n", "\n", "I->0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "1\n", - "\n", - "1\n", + "\n", + "1\n", "\n", "\n", "\n", "0->1\n", - "\n", - "\n", - "\n", - "!i0 & !i1\n", - "/\n", - "\n", - "1\n", + "\n", + "\n", + "\n", + "!i0 & !i1\n", + "/\n", + "\n", + "1\n", "\n", "\n", "\n", "2\n", - "\n", - "2\n", + "\n", + "2\n", "\n", "\n", "\n", "0->2\n", - "\n", - "\n", - "\n", - "i0 & !i1\n", - "/\n", - "\n", - "1\n", + "\n", + "\n", + "\n", + "i0 & !i1\n", + "/\n", + "\n", + "1\n", "\n", "\n", "\n", "3\n", - "\n", - "3\n", + "\n", + "3\n", "\n", "\n", "\n", "0->3\n", - "\n", - "\n", - "\n", - "!i0 & i1\n", - "/\n", - "\n", - "1\n", + "\n", + "\n", + "\n", + "!i0 & i1\n", + "/\n", + "\n", + "1\n", "\n", "\n", "\n", "4\n", - "\n", - "4\n", + "\n", + "4\n", "\n", "\n", "\n", "0->4\n", - "\n", - "\n", - "\n", - "i0 & i1\n", - "/\n", - "\n", - "1\n", + "\n", + "\n", + "\n", + "i0 & i1\n", + "/\n", + "\n", + "1\n", "\n", "\n", "\n", "1->1\n", - "\n", - "\n", - "\n", - "!i0 & !i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "!i0 & !i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "1->2\n", - "\n", - "\n", - "\n", - "i0 & !i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "i0 & !i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "1->3\n", - "\n", - "\n", - "\n", - "!i0 & i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "!i0 & i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "1->4\n", - "\n", - "\n", - "\n", - "i0 & i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "i0 & i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "2->2\n", - "\n", - "\n", - "\n", - "!i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "!i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "2->4\n", - "\n", - "\n", - "\n", - "i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "3->3\n", - "\n", - "\n", - "\n", - "!i0 & i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "!i0 & i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "3->4\n", - "\n", - "\n", - "\n", - "i0 & i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "i0 & i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "5\n", - "\n", - "5\n", + "\n", + "5\n", "\n", "\n", "\n", "3->5\n", - "\n", - "\n", - "\n", - "i0 & !i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "i0 & !i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "6\n", - "\n", - "6\n", + "\n", + "6\n", "\n", "\n", "\n", "3->6\n", - "\n", - "\n", - "\n", - "!i0 & !i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "!i0 & !i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "4->4\n", - "\n", - "\n", - "\n", - "i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "4->5\n", - "\n", - "\n", - "\n", - "!i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "!i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "5->4\n", - "\n", - "\n", - "\n", - "i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "5->5\n", - "\n", - "\n", - "\n", - "!i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "!i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "6->3\n", - "\n", - "\n", - "\n", - "!i0 & i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "!i0 & i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "6->4\n", - "\n", - "\n", - "\n", - "i0 & i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "i0 & i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "6->5\n", - "\n", - "\n", - "\n", - "i0 & !i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "i0 & !i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "6->6\n", - "\n", - "\n", - "\n", - "!i0 & !i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "!i0 & !i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n" ], "text/plain": [ - " *' at 0x7f7a0c264e40> >" + " *' at 0x7fb9646b7870> >" ] }, "metadata": {}, @@ -1688,175 +1680,175 @@ "\n", "\n", - "\n", "\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "0\n", - "\n", - "0\n", + "\n", + "0\n", "\n", "\n", "\n", "I->0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "1\n", - "\n", - "1\n", + "\n", + "1\n", "\n", "\n", "\n", "0->1\n", - "\n", - "\n", - "\n", - "!i0 & !i1\n", - "/\n", - "\n", - "1\n", + "\n", + "\n", + "\n", + "!i0 & !i1\n", + "/\n", + "\n", + "1\n", "\n", "\n", "\n", "0->1\n", - "\n", - "\n", - "\n", - "i0 & !i1\n", - "/\n", - "\n", - "1\n", + "\n", + "\n", + "\n", + "i0 & !i1\n", + "/\n", + "\n", + "1\n", "\n", "\n", "\n", "2\n", - "\n", - "2\n", + "\n", + "2\n", "\n", "\n", "\n", "0->2\n", - "\n", - "\n", - "\n", - "!i0 & i1\n", - "/\n", - "\n", - "1\n", + "\n", + "\n", + "\n", + "!i0 & i1\n", + "/\n", + "\n", + "1\n", "\n", "\n", "\n", "0->2\n", - "\n", - "\n", - "\n", - "i0 & i1\n", - "/\n", - "\n", - "1\n", + "\n", + "\n", + "\n", + "i0 & i1\n", + "/\n", + "\n", + "1\n", "\n", "\n", "\n", "1->1\n", - "\n", - "\n", - "\n", - "i0 & !i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "i0 & !i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "1->1\n", - "\n", - "\n", - "\n", - "!i0 & !i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "!i0 & !i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "1->2\n", - "\n", - "\n", - "\n", - "i0 & i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "i0 & i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "1->2\n", - "\n", - "\n", - "\n", - "!i0 & i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "!i0 & i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "2->1\n", - "\n", - "\n", - "\n", - "i0 & !i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "i0 & !i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "2->1\n", - "\n", - "\n", - "\n", - "!i0 & !i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "!i0 & !i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "2->2\n", - "\n", - "\n", - "\n", - "i0 & i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "i0 & i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "2->2\n", - "\n", - "\n", - "\n", - "!i0 & i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "!i0 & i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n" ], "text/plain": [ - " *' at 0x7f7a0c264d50> >" + " *' at 0x7fb9646b7de0> >" ] }, "metadata": {}, @@ -1875,125 +1867,125 @@ "\n", "\n", - "\n", "\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "1\n", - "\n", - "1\n", + "\n", + "1\n", "\n", "\n", "\n", "I->1\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "1->1\n", - "\n", - "\n", - "\n", - "i0 & i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "i0 & i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "1->1\n", - "\n", - "\n", - "\n", - "!i0 & i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "!i0 & i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "0\n", - "\n", - "0\n", + "\n", + "0\n", "\n", "\n", "\n", "1->0\n", - "\n", - "\n", - "\n", - "i0 & !i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "i0 & !i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "1->0\n", - "\n", - "\n", - "\n", - "!i0 & !i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "!i0 & !i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "0->1\n", - "\n", - "\n", - "\n", - "i0 & i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "i0 & i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "0->1\n", - "\n", - "\n", - "\n", - "!i0 & i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "!i0 & i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "0->0\n", - "\n", - "\n", - "\n", - "i0 & !i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "i0 & !i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "0->0\n", - "\n", - "\n", - "\n", - "!i0 & !i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "!i0 & !i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n" ], "text/plain": [ - " *' at 0x7f7a0c2643c0> >" + " *' at 0x7fb9646b7630> >" ] }, "metadata": {}, @@ -2012,81 +2004,81 @@ "\n", "\n", - "\n", "\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "0\n", - "\n", - "0\n", + "\n", + "0\n", "\n", "\n", "\n", "I->0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "0->0\n", - "\n", - "\n", - "\n", - "i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "1\n", - "\n", - "1\n", + "\n", + "1\n", "\n", "\n", "\n", "0->1\n", - "\n", - "\n", - "\n", - "!i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "!i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "1->0\n", - "\n", - "\n", - "\n", - "i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "1->1\n", - "\n", - "\n", - "\n", - "!i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "!i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n" ], "text/plain": [ - " *' at 0x7f7a0c264a20> >" + " *' at 0x7fb9646b77b0> >" ] }, "metadata": {}, @@ -2105,81 +2097,81 @@ "\n", "\n", - "\n", "\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "0\n", - "\n", - "0\n", + "\n", + "0\n", "\n", "\n", "\n", "I->0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "0->0\n", - "\n", - "\n", - "\n", - "!i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "!i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "1\n", - "\n", - "1\n", + "\n", + "1\n", "\n", "\n", "\n", "0->1\n", - "\n", - "\n", - "\n", - "i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "1->0\n", - "\n", - "\n", - "\n", - "!i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "!i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "1->1\n", - "\n", - "\n", - "\n", - "i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n" ], "text/plain": [ - " *' at 0x7f7a0c264ea0> >" + " *' at 0x7fb9646b7f00> >" ] }, "metadata": {}, @@ -2198,125 +2190,125 @@ "\n", "\n", - "\n", "\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "0\n", - "\n", - "0\n", + "\n", + "0\n", "\n", "\n", "\n", "I->0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "0->0\n", - "\n", - "\n", - "\n", - "i0 & i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "i0 & i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "0->0\n", - "\n", - "\n", - "\n", - "!i0 & i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "!i0 & i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "1\n", - "\n", - "1\n", + "\n", + "1\n", "\n", "\n", "\n", "0->1\n", - "\n", - "\n", - "\n", - "i0 & !i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "i0 & !i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "0->1\n", - "\n", - "\n", - "\n", - "!i0 & !i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "!i0 & !i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "1->0\n", - "\n", - "\n", - "\n", - "i0 & i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "i0 & i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "1->0\n", - "\n", - "\n", - "\n", - "!i0 & i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "!i0 & i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "1->1\n", - "\n", - "\n", - "\n", - "i0 & !i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "i0 & !i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "1->1\n", - "\n", - "\n", - "\n", - "!i0 & !i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "!i0 & !i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n" ], "text/plain": [ - " *' at 0x7f7a0c2646f0> >" + " *' at 0x7fb9646b74b0> >" ] }, "metadata": {}, @@ -2350,7 +2342,6 @@ }, { "cell_type": "markdown", - "id": "435c9bae", "metadata": {}, "source": [ "If needed, a separated Mealy machine can be turned into game shape using `split_sepearated_mealy()`, which is more efficient than `split_2step()`." @@ -2359,7 +2350,6 @@ { "cell_type": "code", "execution_count": 5, - "id": "688a1ced", "metadata": {}, "outputs": [ { @@ -2368,260 +2358,260 @@ "
\n", "\n", - "\n", "\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "0\n", - "\n", - "0\n", + "\n", + "0\n", "\n", "\n", "\n", "I->0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "0->0\n", - "\n", - "\n", - "\n", - "i0 & i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "i0 & i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "0->0\n", - "\n", - "\n", - "\n", - "!i0 & i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "!i0 & i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "1\n", - "\n", - "1\n", + "\n", + "1\n", "\n", "\n", "\n", "0->1\n", - "\n", - "\n", - "\n", - "i0 & !i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "i0 & !i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "0->1\n", - "\n", - "\n", - "\n", - "!i0 & !i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "!i0 & !i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "1->0\n", - "\n", - "\n", - "\n", - "i0 & i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "i0 & i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "1->0\n", - "\n", - "\n", - "\n", - "!i0 & i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "!i0 & i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "1->1\n", - "\n", - "\n", - "\n", - "i0 & !i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "i0 & !i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "1->1\n", - "\n", - "\n", - "\n", - "!i0 & !i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "!i0 & !i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "
\n", "\n", - "\n", "\n", - "\n", - "\n", - "\n", - "t\n", - "[all]\n", + "\n", + "\n", + "\n", + "t\n", + "[all]\n", "\n", "\n", "\n", "0\n", - "\n", - "0\n", + "\n", + "0\n", "\n", "\n", "\n", "I->0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "2\n", - "\n", - "2\n", + "\n", + "2\n", "\n", "\n", "\n", "0->2\n", - "\n", - "\n", - "i0 & !i1\n", + "\n", + "\n", + "i0 & !i1\n", "\n", "\n", "\n", "0->2\n", - "\n", - "\n", - "!i0 & !i1\n", + "\n", + "\n", + "!i0 & !i1\n", "\n", "\n", "\n", "3\n", - "\n", - "3\n", + "\n", + "3\n", "\n", "\n", "\n", "0->3\n", - "\n", - "\n", - "i0 & i1\n", + "\n", + "\n", + "i0 & i1\n", "\n", "\n", "\n", "0->3\n", - "\n", - "\n", - "!i0 & i1\n", + "\n", + "\n", + "!i0 & i1\n", "\n", "\n", "\n", "1\n", - "\n", - "1\n", + "\n", + "1\n", "\n", "\n", "\n", "2->1\n", - "\n", - "\n", - "o0\n", + "\n", + "\n", + "o0\n", "\n", "\n", "\n", "3->0\n", - "\n", - "\n", - "o0\n", + "\n", + "\n", + "o0\n", "\n", "\n", "\n", "4\n", - "\n", - "4\n", + "\n", + "4\n", "\n", "\n", "\n", "1->4\n", - "\n", - "\n", - "i0 & i1\n", + "\n", + "\n", + "i0 & i1\n", "\n", "\n", "\n", "1->4\n", - "\n", - "\n", - "!i0 & i1\n", + "\n", + "\n", + "!i0 & i1\n", "\n", "\n", "\n", "5\n", - "\n", - "5\n", + "\n", + "5\n", "\n", "\n", "\n", "1->5\n", - "\n", - "\n", - "i0 & !i1\n", + "\n", + "\n", + "i0 & !i1\n", "\n", "\n", "\n", "1->5\n", - "\n", - "\n", - "!i0 & !i1\n", + "\n", + "\n", + "!i0 & !i1\n", "\n", "\n", "\n", "4->0\n", - "\n", - "\n", - "!o0\n", + "\n", + "\n", + "!o0\n", "\n", "\n", "\n", "5->1\n", - "\n", - "\n", - "!o0\n", + "\n", + "\n", + "!o0\n", "\n", "\n", "\n", @@ -2641,10 +2631,9 @@ }, { "cell_type": "markdown", - "id": "e3bb2d7d", "metadata": {}, "source": [ - "# Converting the separated mealy machine to AIG\n", + "# Converting the separated Mealy machine to AIG\n", "\n", "A separated Mealy machine can be converted to a circuit in the [AIGER format](http://fmv.jku.at/aiger/FORMAT.aiger) using `mealy_machine_to_aig()`. This takes a second argument specifying what type of encoding to use (exactly like `ltlsynt`'s `--aiger=...` option). \n", "\n", @@ -2654,7 +2643,6 @@ { "cell_type": "code", "execution_count": 6, - "id": "b5fea2d1", "metadata": {}, "outputs": [ { @@ -2663,60 +2651,60 @@ "\n", "\n", - "\n", "\n", - "\n", + "\n", "\n", - "\n", + "\n", "\n", "\n", "6\n", - "\n", - "L0_out\n", + "\n", + "L0_out\n", "\n", "\n", "\n", "o0\n", - "\n", - "o0\n", + "\n", + "o0\n", "\n", "\n", "\n", "6->o0:s\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "L0\n", - "\n", - "L0_in\n", + "\n", + "L0_in\n", "\n", "\n", "\n", "2\n", - "\n", - "i1\n", + "\n", + "i1\n", "\n", "\n", "\n", "2->L0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "4\n", - "\n", - "i0\n", + "\n", + "i0\n", "\n", "\n", "\n" ], "text/plain": [ - " >" + " >" ] }, "metadata": {}, @@ -2730,7 +2718,6 @@ }, { "cell_type": "markdown", - "id": "2a1a6fc9", "metadata": {}, "source": [ "While we are at it, let us mention that you can render those circuits horizontally as follows:" @@ -2739,7 +2726,6 @@ { "cell_type": "code", "execution_count": 7, - "id": "f909d578", "metadata": {}, "outputs": [ { @@ -2748,54 +2734,54 @@ "\n", "\n", - "\n", "\n", - "\n", + "\n", "\n", - "\n", + "\n", "\n", "\n", "6\n", - "\n", - "L0_out\n", + "\n", + "L0_out\n", "\n", "\n", "\n", "o0\n", - "\n", - "o0\n", + "\n", + "o0\n", "\n", "\n", "\n", "6->o0:w\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "L0\n", - "\n", - "L0_in\n", + "\n", + "L0_in\n", "\n", "\n", "\n", "2\n", - "\n", - "i1\n", + "\n", + "i1\n", "\n", "\n", "\n", "2->L0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "4\n", - "\n", - "i0\n", + "\n", + "i0\n", "\n", "\n", "\n" @@ -2815,7 +2801,6 @@ }, { "cell_type": "markdown", - "id": "2c87313f", "metadata": {}, "source": [ "To encode the circuit in the AIGER format (ASCII version) use:" @@ -2824,7 +2809,6 @@ { "cell_type": "code", "execution_count": 8, - "id": "1b787f50", "metadata": {}, "outputs": [ { @@ -2848,7 +2832,6 @@ }, { "cell_type": "markdown", - "id": "72038258", "metadata": {}, "source": [ "# Adding more inputs and outputs by force" @@ -2856,7 +2839,6 @@ }, { "cell_type": "markdown", - "id": "3fbb3c2f", "metadata": {}, "source": [ "It can happen that propositions declared as output are ommited in the aig circuit (either because they are not part of the specification, or because they do not appear in the winning strategy). In that case those \n", @@ -2868,7 +2850,6 @@ { "cell_type": "code", "execution_count": 9, - "id": "8ed4e382", "metadata": {}, "outputs": [ { @@ -2877,159 +2858,159 @@ "\n", "\n", - "\n", "\n", - "\n", - "\n", - "\n", - "Inf(\n", - "\n", - ") | Fin(\n", - "\n", - ")\n", - "[Streett 1]\n", + "\n", + "\n", + "\n", + "Inf(\n", + "\n", + ") | Fin(\n", + "\n", + ")\n", + "[Streett 1]\n", "\n", "\n", "\n", "3\n", - "\n", - "3\n", + "\n", + "3\n", "\n", "\n", "\n", "I->3\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "6\n", - "\n", - "6\n", + "\n", + "6\n", "\n", "\n", "\n", "3->6\n", - "\n", - "\n", - "i0\n", - "\n", + "\n", + "\n", + "i0\n", + "\n", "\n", "\n", "\n", "7\n", - "\n", - "7\n", + "\n", + "7\n", "\n", "\n", "\n", "3->7\n", - "\n", - "\n", - "!i0\n", - "\n", + "\n", + "\n", + "!i0\n", + "\n", "\n", "\n", "\n", "0\n", - "\n", - "0\n", + "\n", + "0\n", "\n", "\n", "\n", "4\n", - "\n", - "4\n", + "\n", + "4\n", "\n", "\n", "\n", "0->4\n", - "\n", - "\n", - "1\n", - "\n", + "\n", + "\n", + "1\n", + "\n", "\n", "\n", "\n", "4->0\n", - "\n", - "\n", - "1\n", - "\n", + "\n", + "\n", + "1\n", + "\n", "\n", "\n", "\n", "1\n", - "\n", - "1\n", + "\n", + "1\n", "\n", "\n", "\n", "5\n", - "\n", - "5\n", + "\n", + "5\n", "\n", "\n", "\n", "1->5\n", - "\n", - "\n", - "1\n", - "\n", + "\n", + "\n", + "1\n", + "\n", "\n", "\n", "\n", "5->1\n", - "\n", - "\n", - "!o0\n", - "\n", + "\n", + "\n", + "!o0\n", + "\n", "\n", "\n", "\n", "2\n", - "\n", - "2\n", + "\n", + "2\n", "\n", "\n", "\n", "2->6\n", - "\n", - "\n", - "1\n", - "\n", + "\n", + "\n", + "1\n", + "\n", "\n", "\n", "\n", "6->0\n", - "\n", - "\n", - "o0\n", - "\n", + "\n", + "\n", + "o0\n", + "\n", "\n", "\n", "\n", "6->2\n", - "\n", - "\n", - "!o0\n", - "\n", + "\n", + "\n", + "!o0\n", + "\n", "\n", "\n", "\n", "7->1\n", - "\n", - "\n", - "!o0\n", - "\n", + "\n", + "\n", + "!o0\n", + "\n", "\n", "\n", "\n" ], "text/plain": [ - " *' at 0x7f7a0c009f00> >" + " *' at 0x7fb9646b7570> >" ] }, "metadata": {}, @@ -3041,112 +3022,112 @@ "\n", "\n", - "\n", "\n", "\n", "\n", - "\n", - "t\n", - "[all]\n", + "\n", + "t\n", + "[all]\n", "\n", "\n", "\n", "0\n", - "\n", - "0\n", + "\n", + "0\n", "\n", "\n", "\n", "I->0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "2\n", - "\n", - "2\n", + "\n", + "2\n", "\n", "\n", "\n", "0->2\n", - "\n", - "\n", - "i0\n", + "\n", + "\n", + "i0\n", "\n", "\n", "\n", "4\n", - "\n", - "4\n", + "\n", + "4\n", "\n", "\n", "\n", "0->4\n", - "\n", - "\n", - "!i0\n", + "\n", + "\n", + "!i0\n", "\n", "\n", "\n", "1\n", - "\n", - "1\n", + "\n", + "1\n", "\n", "\n", "\n", "2->1\n", - "\n", - "\n", - "o0\n", + "\n", + "\n", + "o0\n", "\n", "\n", "\n", "3\n", - "\n", - "3\n", + "\n", + "3\n", "\n", "\n", "\n", "4->3\n", - "\n", - "\n", - "!o0\n", + "\n", + "\n", + "!o0\n", "\n", "\n", "\n", "5\n", - "\n", - "5\n", + "\n", + "5\n", "\n", "\n", "\n", "1->5\n", - "\n", - "\n", - "1\n", + "\n", + "\n", + "1\n", "\n", "\n", "\n", "5->1\n", - "\n", - "\n", - "1\n", + "\n", + "\n", + "1\n", "\n", "\n", "\n", "3->4\n", - "\n", - "\n", - "1\n", + "\n", + "\n", + "1\n", "\n", "\n", "\n" ], "text/plain": [ - " *' at 0x7f7a0c009180> >" + " *' at 0x7fb9646b7630> >" ] }, "metadata": {}, @@ -3158,144 +3139,144 @@ "
\n", "\n", - "\n", "\n", "\n", - "\n", - "\n", - "t\n", - "[all]\n", + " viewBox=\"0.00 0.00 282.00 148.79\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n", + "\n", + "\n", + "t\n", + "[all]\n", "\n", "\n", "\n", "0\n", - "\n", - "0\n", + "\n", + "0\n", "\n", "\n", "\n", "I->0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "2\n", - "\n", - "2\n", + "\n", + "2\n", "\n", "\n", "\n", "0->2\n", - "\n", - "\n", - "i0\n", + "\n", + "\n", + "i0\n", "\n", "\n", "\n", "3\n", - "\n", - "3\n", + "\n", + "3\n", "\n", "\n", "\n", "0->3\n", - "\n", - "\n", - "!i0\n", + "\n", + "\n", + "!i0\n", "\n", "\n", "\n", "2->0\n", - "\n", - "\n", - "o0\n", + "\n", + "\n", + "o0\n", "\n", "\n", "\n", "1\n", - "\n", - "1\n", + "\n", + "1\n", "\n", "\n", "\n", "3->1\n", - "\n", - "\n", - "!o0\n", + "\n", + "\n", + "!o0\n", "\n", "\n", "\n", "1->3\n", - "\n", - "\n", - "1\n", + "\n", + "\n", + "1\n", "\n", "\n", "\n", "
\n", "\n", - "\n", "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "0\n", - "\n", - "0\n", + "\n", + "0\n", "\n", "\n", "\n", "I->0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "0->0\n", - "\n", - "\n", - "\n", - "i0\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "i0\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "1\n", - "\n", - "1\n", + "\n", + "1\n", "\n", "\n", "\n", "0->1\n", - "\n", - "\n", + "\n", + "\n", "\n", - "!i0\n", - "/\n", + "!i0\n", + "/\n", "\n", - "!o0\n", + "!o0\n", "\n", "\n", "\n", "1->1\n", - "\n", - "\n", + "\n", + "\n", "\n", - "1\n", - "/\n", + "1\n", + "/\n", "\n", - "!o0\n", + "!o0\n", "\n", "\n", "\n", @@ -3314,72 +3295,72 @@ "\n", "\n", - "\n", "\n", "\n", + " viewBox=\"0.00 0.00 143.20 352.00\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n", "\n", - "\n", + "\n", "\n", "\n", "4\n", - "\n", - "L0_out\n", + "\n", + "L0_out\n", "\n", "\n", "\n", "6\n", - "\n", - "6\n", + "\n", + "6\n", "\n", "\n", "\n", "4->6\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "L0\n", - "\n", - "L0_in\n", + "\n", + "L0_in\n", "\n", "\n", "\n", "6->L0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "o0\n", - "\n", - "o0\n", + "\n", + "o0\n", "\n", "\n", "\n", "6->o0:s\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "2\n", - "\n", - "i0\n", + "\n", + "i0\n", "\n", "\n", "\n", "2->6\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n" ], "text/plain": [ - " >" + " >" ] }, "metadata": {}, @@ -3401,7 +3382,6 @@ }, { "cell_type": "markdown", - "id": "a82ca6c6", "metadata": {}, "source": [ "To force the presence of extra variables in the circuit, they can be passed to `mealy_machine_to_aig()`." @@ -3410,7 +3390,6 @@ { "cell_type": "code", "execution_count": 10, - "id": "a86436a7", "metadata": {}, "outputs": [ { @@ -3419,96 +3398,96 @@ "\n", "\n", - "\n", "\n", - "\n", + "\n", "\n", - "\n", + "\n", "\n", "\n", "6\n", - "\n", - "L0_out\n", + "\n", + "L0_out\n", "\n", "\n", "\n", "8\n", - "\n", - "8\n", + "\n", + "8\n", "\n", "\n", "\n", "6->8\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "L0\n", - "\n", - "L0_in\n", + "\n", + "L0_in\n", "\n", "\n", "\n", "8->L0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "o0\n", - "\n", - "o0\n", + "\n", + "o0\n", "\n", "\n", "\n", "8->o0:s\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "o1\n", - "\n", - "o1\n", + "\n", + "o1\n", "\n", "\n", "\n", "2\n", - "\n", - "i0\n", + "\n", + "i0\n", "\n", "\n", "\n", "2->8\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "4\n", - "\n", - "i1\n", + "\n", + "i1\n", "\n", "\n", "\n", "0\n", - "\n", - "False\n", + "\n", + "False\n", "\n", "\n", "\n", "0->o1:s\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n" ], "text/plain": [ - " >" + " >" ] }, "metadata": {}, @@ -3521,7 +3500,6 @@ }, { "cell_type": "markdown", - "id": "9af5c9b9", "metadata": {}, "source": [ "# Combining Mealy machines\n", @@ -3531,7 +3509,7 @@ "\n", "This can be done in two ways:\n", "\n", - "1. Using the function `mealy_machines_to_aig()`, which takes a vector of separated mealy machines as argument.\n", + "1. Using the function `mealy_machines_to_aig()`, which takes a vector of separated Mealy machines as argument.\n", "2. Combine the mealy machines into one before passing it to `mealy_machine_to aig(). This currently only supports input complete machines of the same type (mealy/separated mealy/split mealy)\n", "\n", "Note that the method version is usually preferable as it is faster.\n", @@ -3541,7 +3519,6 @@ { "cell_type": "code", "execution_count": 11, - "id": "750e55f5", "metadata": {}, "outputs": [ { @@ -3557,150 +3534,150 @@ "
\n", "\n", - "\n", "\n", - "\n", - "\n", - "\n", - "Inf(\n", - "\n", - ") | Fin(\n", - "\n", - ")\n", - "[Streett 1]\n", + "\n", + "\n", + "\n", + "Inf(\n", + "\n", + ") | Fin(\n", + "\n", + ")\n", + "[Streett 1]\n", "\n", "\n", "\n", "0\n", - "\n", - "0\n", + "\n", + "0\n", "\n", "\n", "\n", "I->0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "1\n", - "\n", - "1\n", + "\n", + "1\n", "\n", "\n", "\n", "0->1\n", - "\n", - "\n", - "(!i0 & !i1) | (i0 & i1)\n", - "\n", + "\n", + "\n", + "(!i0 & !i1) | (i0 & i1)\n", + "\n", "\n", "\n", "\n", "2\n", - "\n", - "2\n", + "\n", + "2\n", "\n", "\n", "\n", "0->2\n", - "\n", - "\n", - "(!i0 & i1) | (i0 & !i1)\n", - "\n", + "\n", + "\n", + "(!i0 & i1) | (i0 & !i1)\n", + "\n", "\n", "\n", "\n", "1->0\n", - "\n", - "\n", - "!o0\n", - "\n", + "\n", + "\n", + "!o0\n", + "\n", "\n", "\n", "\n", "2->0\n", - "\n", - "\n", - "o0\n", - "\n", + "\n", + "\n", + "o0\n", + "\n", "\n", "\n", "\n", "
\n", "\n", - "\n", "\n", - "\n", - "\n", - "\n", - "Inf(\n", - "\n", - ") | Fin(\n", - "\n", - ")\n", - "[Streett 1]\n", + "\n", + "\n", + "\n", + "Inf(\n", + "\n", + ") | Fin(\n", + "\n", + ")\n", + "[Streett 1]\n", "\n", "\n", "\n", "0\n", - "\n", - "0\n", + "\n", + "0\n", "\n", "\n", "\n", "I->0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "1\n", - "\n", - "1\n", + "\n", + "1\n", "\n", "\n", "\n", "0->1\n", - "\n", - "\n", - "(!i0 & !i1) | (i0 & i1)\n", - "\n", + "\n", + "\n", + "(!i0 & !i1) | (i0 & i1)\n", + "\n", "\n", "\n", "\n", "2\n", - "\n", - "2\n", + "\n", + "2\n", "\n", "\n", "\n", "0->2\n", - "\n", - "\n", - "(!i0 & i1) | (i0 & !i1)\n", - "\n", + "\n", + "\n", + "(!i0 & i1) | (i0 & !i1)\n", + "\n", "\n", "\n", "\n", "1->0\n", - "\n", - "\n", - "o1\n", - "\n", + "\n", + "\n", + "o1\n", + "\n", "\n", "\n", "\n", "2->0\n", - "\n", - "\n", - "!o1\n", - "\n", + "\n", + "\n", + "!o1\n", + "\n", "\n", "\n", "\n", @@ -3726,94 +3703,94 @@ "
\n", "\n", - "\n", "\n", - "\n", + "\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "0\n", - "\n", - "0\n", + "\n", + "0\n", "\n", "\n", "\n", "I->0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "0->0\n", - "\n", - "\n", - "\n", - "(!i0 & !i1) | (i0 & i1)\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "(!i0 & !i1) | (i0 & i1)\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "0->0\n", - "\n", - "\n", - "\n", - "(!i0 & i1) | (i0 & !i1)\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "(!i0 & i1) | (i0 & !i1)\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "
\n", "\n", - "\n", "\n", - "\n", + "\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "0\n", - "\n", - "0\n", + "\n", + "0\n", "\n", "\n", "\n", "I->0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "0->0\n", - "\n", - "\n", - "\n", - "(!i0 & !i1) | (i0 & i1)\n", - "/\n", - "\n", - "o1\n", + "\n", + "\n", + "\n", + "(!i0 & !i1) | (i0 & i1)\n", + "/\n", + "\n", + "o1\n", "\n", "\n", "\n", "0->0\n", - "\n", - "\n", - "\n", - "(!i0 & i1) | (i0 & !i1)\n", - "/\n", - "\n", - "!o1\n", + "\n", + "\n", + "\n", + "(!i0 & i1) | (i0 & !i1)\n", + "/\n", + "\n", + "!o1\n", "\n", "\n", "\n", @@ -3839,108 +3816,108 @@ "\n", "\n", - "\n", "\n", "\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "6\n", - "\n", - "6\n", + "\n", + "6\n", "\n", "\n", "\n", "10\n", - "\n", - "10\n", + "\n", + "10\n", "\n", "\n", "\n", "6->10\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "8\n", - "\n", - "8\n", + "\n", + "8\n", "\n", "\n", "\n", "8->10\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "o0\n", - "\n", - "o0\n", + "\n", + "o0\n", "\n", "\n", "\n", "10->o0:s\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "o1\n", - "\n", - "o1\n", + "\n", + "o1\n", "\n", "\n", "\n", "10->o1:s\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "2\n", - "\n", - "i0\n", + "\n", + "i0\n", "\n", "\n", "\n", "2->6\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "2->8\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "4\n", - "\n", - "i1\n", + "\n", + "i1\n", "\n", "\n", "\n", "4->6\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "4->8\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n" ], "text/plain": [ - " >" + " >" ] }, "metadata": {}, @@ -3959,53 +3936,53 @@ "\n", "\n", - "\n", "\n", - "\n", + "\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "0\n", - "\n", - "0,0\n", + "\n", + "0,0\n", "\n", "\n", "\n", "I->0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "0->0\n", - "\n", - "\n", - "\n", - "(!i0 & !i1) | (i0 & i1)\n", - "/\n", - "\n", - "!o0 & o1\n", + "\n", + "\n", + "\n", + "(!i0 & !i1) | (i0 & i1)\n", + "/\n", + "\n", + "!o0 & o1\n", "\n", "\n", "\n", "0->0\n", - "\n", - "\n", - "\n", - "(!i0 & i1) | (i0 & !i1)\n", - "/\n", - "\n", - "o0 & !o1\n", + "\n", + "\n", + "\n", + "(!i0 & i1) | (i0 & !i1)\n", + "/\n", + "\n", + "o0 & !o1\n", "\n", "\n", "\n" ], "text/plain": [ - " *' at 0x7f7a0c009de0> >" + " *' at 0x7fb9646b7c60> >" ] }, "metadata": {}, @@ -4017,108 +3994,108 @@ "\n", "\n", - "\n", "\n", "\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "6\n", - "\n", - "6\n", + "\n", + "6\n", "\n", "\n", "\n", "10\n", - "\n", - "10\n", + "\n", + "10\n", "\n", "\n", "\n", "6->10\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "8\n", - "\n", - "8\n", + "\n", + "8\n", "\n", "\n", "\n", "8->10\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "o0\n", - "\n", - "o0\n", + "\n", + "o0\n", "\n", "\n", "\n", "10->o0:s\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "o1\n", - "\n", - "o1\n", + "\n", + "o1\n", "\n", "\n", "\n", "10->o1:s\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "2\n", - "\n", - "i0\n", + "\n", + "i0\n", "\n", "\n", "\n", "2->6\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "2->8\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "4\n", - "\n", - "i1\n", + "\n", + "i1\n", "\n", "\n", "\n", "4->6\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "4->8\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n" ], "text/plain": [ - " >" + " >" ] }, "metadata": {}, @@ -4152,7 +4129,6 @@ }, { "cell_type": "markdown", - "id": "5102e762", "metadata": {}, "source": [ "# Reading an AIGER-file\n", @@ -4167,7 +4143,6 @@ { "cell_type": "code", "execution_count": 12, - "id": "29d37752", "metadata": {}, "outputs": [], "source": [ @@ -4188,7 +4163,6 @@ { "cell_type": "code", "execution_count": 13, - "id": "8989722d", "metadata": {}, "outputs": [ { @@ -4197,108 +4171,108 @@ "\n", "\n", - "\n", "\n", "\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "6\n", - "\n", - "6\n", + "\n", + "6\n", "\n", "\n", "\n", "10\n", - "\n", - "10\n", + "\n", + "10\n", "\n", "\n", "\n", "6->10\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "o1\n", - "\n", - "d\n", + "\n", + "d\n", "\n", "\n", "\n", "6->o1:s\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "8\n", - "\n", - "8\n", + "\n", + "8\n", "\n", "\n", "\n", "8->10\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "o0\n", - "\n", - "c\n", + "\n", + "c\n", "\n", "\n", "\n", "10->o0:s\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "2\n", - "\n", - "a\n", + "\n", + "a\n", "\n", "\n", "\n", "2->6\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "2->8\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "4\n", - "\n", - "b\n", + "\n", + "b\n", "\n", "\n", "\n", "4->6\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "4->8\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n" ], "text/plain": [ - " >" + " >" ] }, "metadata": {}, @@ -4313,7 +4287,6 @@ { "cell_type": "code", "execution_count": 14, - "id": "9560f368", "metadata": {}, "outputs": [ { @@ -4342,7 +4315,6 @@ { "cell_type": "code", "execution_count": 15, - "id": "9dadee6a", "metadata": {}, "outputs": [ { @@ -4359,7 +4331,6 @@ }, { "cell_type": "markdown", - "id": "734f10f1", "metadata": {}, "source": [ "An AIG circuit can be transformed into a monitor/Mealy machine. This can be used for instance to check that it does not intersect the negation of the specification." @@ -4368,7 +4339,6 @@ { "cell_type": "code", "execution_count": 16, - "id": "b29c95b4", "metadata": {}, "outputs": [ { @@ -4377,52 +4347,52 @@ "\n", "\n", - "\n", "\n", - "\n", + "\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "0\n", - "\n", - "0\n", + "\n", + "0\n", "\n", "\n", "\n", "I->0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "0->0\n", - "\n", - "\n", - "\n", - "!a & !b\n", - "/\n", - "\n", - "!c & !d\n", - "\n", - "a & b\n", - "/\n", - "\n", - "!c & d\n", - "\n", - "(!a & b) | (a & !b)\n", - "/\n", - "\n", - "c & !d\n", + "\n", + "\n", + "\n", + "!a & !b\n", + "/\n", + "\n", + "!c & !d\n", + "\n", + "a & b\n", + "/\n", + "\n", + "!c & d\n", + "\n", + "(!a & b) | (a & !b)\n", + "/\n", + "\n", + "c & !d\n", "\n", "\n", "\n" ], "text/plain": [ - " *' at 0x7f7a0c2640c0> >" + " *' at 0x7fb9646c8360> >" ] }, "execution_count": 16, @@ -4436,7 +4406,6 @@ }, { "cell_type": "markdown", - "id": "09cad9f5", "metadata": {}, "source": [ "Note that the generation of aiger circuits from Mealy machines is flexible and accepts separated Mealy machines\n", @@ -4446,7 +4415,6 @@ { "cell_type": "code", "execution_count": 17, - "id": "62ebedae", "metadata": {}, "outputs": [ { @@ -4455,114 +4423,114 @@ "
\n", "\n", - "\n", "\n", - "\n", + "\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "0\n", - "\n", - "0\n", + "\n", + "0\n", "\n", "\n", "\n", "I->0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "0->0\n", - "\n", - "\n", - "\n", - "(!i0 & !i1) | (i0 & i1)\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "(!i0 & !i1) | (i0 & i1)\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "0->0\n", - "\n", - "\n", - "\n", - "(!i0 & i1) | (i0 & !i1)\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "(!i0 & i1) | (i0 & !i1)\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "
\n", "\n", - "\n", "\n", - "\n", - "\n", - "\n", - "t\n", - "[all]\n", + "\n", + "\n", + "\n", + "t\n", + "[all]\n", "\n", "\n", "\n", "0\n", - "\n", - "0\n", + "\n", + "0\n", "\n", "\n", "\n", "I->0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "1\n", - "\n", - "1\n", + "\n", + "1\n", "\n", "\n", "\n", "0->1\n", - "\n", - "\n", - "(!i0 & !i1) | (i0 & i1)\n", + "\n", + "\n", + "(!i0 & !i1) | (i0 & i1)\n", "\n", "\n", "\n", "2\n", - "\n", - "2\n", + "\n", + "2\n", "\n", "\n", "\n", "0->2\n", - "\n", - "\n", - "(!i0 & i1) | (i0 & !i1)\n", + "\n", + "\n", + "(!i0 & i1) | (i0 & !i1)\n", "\n", "\n", "\n", "1->0\n", - "\n", - "\n", - "!o0\n", + "\n", + "\n", + "!o0\n", "\n", "\n", "\n", "2->0\n", - "\n", - "\n", - "o0\n", + "\n", + "\n", + "o0\n", "\n", "\n", "\n", @@ -4594,7 +4562,6 @@ { "cell_type": "code", "execution_count": 18, - "id": "4a0bb1a7", "metadata": {}, "outputs": [ { @@ -4603,180 +4570,180 @@ "
\n", "\n", - "\n", "\n", "\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "6\n", - "\n", - "6\n", + "\n", + "6\n", "\n", "\n", "\n", "10\n", - "\n", - "10\n", + "\n", + "10\n", "\n", "\n", "\n", "6->10\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "8\n", - "\n", - "8\n", + "\n", + "8\n", "\n", "\n", "\n", "8->10\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "o0\n", - "\n", - "o0\n", + "\n", + "o0\n", "\n", "\n", "\n", "10->o0:s\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "2\n", - "\n", - "i0\n", + "\n", + "i0\n", "\n", "\n", "\n", "2->6\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "2->8\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "4\n", - "\n", - "i1\n", + "\n", + "i1\n", "\n", "\n", "\n", "4->6\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "4->8\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "
\n", "\n", - "\n", "\n", "\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "6\n", - "\n", - "6\n", + "\n", + "6\n", "\n", "\n", "\n", "10\n", - "\n", - "10\n", + "\n", + "10\n", "\n", "\n", "\n", "6->10\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "8\n", - "\n", - "8\n", + "\n", + "8\n", "\n", "\n", "\n", "8->10\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "o0\n", - "\n", - "o0\n", + "\n", + "o0\n", "\n", "\n", "\n", "10->o0:s\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "2\n", - "\n", - "i0\n", + "\n", + "i0\n", "\n", "\n", "\n", "2->6\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "2->8\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "4\n", - "\n", - "i1\n", + "\n", + "i1\n", "\n", "\n", "\n", "4->6\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "4->8\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", @@ -4797,7 +4764,7 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 3 (ipykernel)", + "display_name": "Python 3", "language": "python", "name": "python3" }, @@ -4811,7 +4778,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.10" + "version": "3.7.3" } }, "nbformat": 4, From 8d9597d80dcaa1ef98f65985a7c8a0964e2631df Mon Sep 17 00:00:00 2001 From: Florian Renkin Date: Mon, 21 Mar 2022 15:58:58 +0100 Subject: [PATCH 019/337] ltlsynt: add --algo=acd * bin/ltlsynt.cc: Add "acd" to the list of possible paritization algorithms used by ltlsynt * spot/twaalgos/synthesis.cc, spot/twaalgos/synthesis.hh: Add ACD as paritisation algorithm * tests/core/ltlsynt.test: add tests --- bin/ltlsynt.cc | 12 +++++++++--- spot/twaalgos/synthesis.cc | 14 ++++++++++++-- spot/twaalgos/synthesis.hh | 1 + tests/core/ltlsynt.test | 12 +++++++++++- 4 files changed, 33 insertions(+), 6 deletions(-) diff --git a/bin/ltlsynt.cc b/bin/ltlsynt.cc index 25b8bb04a..8dcd9511a 100644 --- a/bin/ltlsynt.cc +++ b/bin/ltlsynt.cc @@ -69,7 +69,7 @@ static const argp_option options[] = " propositions", 0}, /**************************************************/ { nullptr, 0, nullptr, 0, "Fine tuning:", 10 }, - { "algo", OPT_ALGO, "sd|ds|ps|lar|lar.old", 0, + { "algo", OPT_ALGO, "sd|ds|ps|lar|lar.old|acd", 0, "choose the algorithm for synthesis:" " \"sd\": translate to tgba, split, then determinize;" " \"ds\": translate to tgba, determinize, then split;" @@ -77,7 +77,10 @@ static const argp_option options[] = " \"lar\": translate to a deterministic automaton with arbitrary" " acceptance condition, then use LAR to turn to parity," " then split (default);" - " \"lar.old\": old version of LAR, for benchmarking.\n", 0 }, + " \"lar.old\": old version of LAR, for benchmarking;" + " \"acd\": translate to a deterministic automaton with arbitrary" + " acceptance condition, then use ACD to turn to parity," + " then split.\n", 0 }, { "decompose", OPT_DECOMPOSE, "yes|no", 0, "whether to decompose the specification as multiple output-disjoint " "problems to solve independently (enabled by default)", 0 }, @@ -154,7 +157,8 @@ static char const *const algo_names[] = "sd", "ps", "lar", - "lar.old" + "lar.old", + "acd", }; static char const *const algo_args[] = @@ -164,6 +168,7 @@ static char const *const algo_args[] = "dpasplit", "ps", "lar", "lar.old", + "acd", nullptr }; static spot::synthesis_info::algo const algo_types[] = @@ -173,6 +178,7 @@ static spot::synthesis_info::algo const algo_types[] = spot::synthesis_info::algo::DPA_SPLIT, spot::synthesis_info::algo::DPA_SPLIT, spot::synthesis_info::algo::LAR, spot::synthesis_info::algo::LAR_OLD, + spot::synthesis_info::algo::ACD, }; ARGMATCH_VERIFY(algo_args, algo_types); diff --git a/spot/twaalgos/synthesis.cc b/spot/twaalgos/synthesis.cc index 7620a1098..95e0725d9 100644 --- a/spot/twaalgos/synthesis.cc +++ b/spot/twaalgos/synthesis.cc @@ -29,6 +29,7 @@ #include #include #include +#include #include #include #include @@ -741,6 +742,9 @@ namespace spot case (algo::LAR_OLD): name = "lar.old"; break; + case (algo::ACD): + name = "acd"; + break; } return os << name; } @@ -775,6 +779,8 @@ namespace spot translator trans(dict, &extra_options); switch (sol) { + case algo::ACD: + SPOT_FALLTHROUGH; case algo::LAR: SPOT_FALLTHROUGH; case algo::LAR_OLD: @@ -965,6 +971,8 @@ namespace spot alternate_players(dpa); break; } + case algo::ACD: + SPOT_FALLTHROUGH; case algo::LAR: SPOT_FALLTHROUGH; case algo::LAR_OLD: @@ -976,11 +984,13 @@ namespace spot dpa = to_parity(aut); // reduce_parity is called by to_parity() } - else + else if (gi.s == algo::LAR_OLD) { dpa = to_parity_old(aut); - dpa = reduce_parity_here(dpa, true); + reduce_parity_here(dpa, true); } + else + dpa = acd_transform(aut); if (bv) bv->paritize_time += sw.stop(); if (vs) diff --git a/spot/twaalgos/synthesis.hh b/spot/twaalgos/synthesis.hh index 46c0bc2bd..a5fced429 100644 --- a/spot/twaalgos/synthesis.hh +++ b/spot/twaalgos/synthesis.hh @@ -86,6 +86,7 @@ namespace spot DPA_SPLIT, LAR, LAR_OLD, + ACD, }; struct bench_var diff --git a/tests/core/ltlsynt.test b/tests/core/ltlsynt.test index c22cd4e6b..742f05a5e 100644 --- a/tests/core/ltlsynt.test +++ b/tests/core/ltlsynt.test @@ -836,4 +836,14 @@ ltlsynt --outs="" -f "GFb" | grep "UNREALIZABLE" ltlsynt --outs="" -f "1" ltlsynt --outs="" --ins="" -f "GFa" 2>&1 | \ - grep "both --ins and --outs are specified" \ No newline at end of file + grep "both --ins and --outs are specified" + +LTL='(((((G (((((((g_0) && (G (! (r_0)))) -> (F (! (g_0)))) && (((g_0) && +(X ((! (r_0)) && (! (g_0))))) -> (X ((r_0) R (! (g_0)))))) && (((g_1) && +(G (! (r_1)))) -> (F (! (g_1))))) && (((g_1) && (X ((! (r_1)) && (! (g_1))))) -> +(X ((r_1) R (! (g_1)))))) && (((! (g_0)) && (true)) || ((true) && (! (g_1)))))) +&& ((r_0) R (! (g_0)))) && (G ((r_0) -> (F (g_0))))) && ((r_1) R (! (g_1)))) && +(G ((r_1) -> (F (g_1)))))' +OUT='g_0, g_1' +ltlsynt --outs="$OUT" -f "$LTL" --aiger=both --algo=acd | grep "aag 8 2 2 2 4" +ltlsynt --outs="$OUT" -f "$LTL" --aiger=both --algo=lar | grep "aag 34 2 3 2 29" From 0a6b627914eee3c8c2465a2c5b427455008239c0 Mon Sep 17 00:00:00 2001 From: Florian Renkin Date: Tue, 22 Mar 2022 14:46:31 +0100 Subject: [PATCH 020/337] option_map: Don't report unused options if option_map is not used * spot/misc/optionmap.cc, spot/misc/optionmap.hh: here. --- spot/misc/optionmap.cc | 38 ++++++++++++++++++++++---------------- spot/misc/optionmap.hh | 1 + 2 files changed, 23 insertions(+), 16 deletions(-) diff --git a/spot/misc/optionmap.cc b/spot/misc/optionmap.cc index 4db5235eb..8be8d1adc 100644 --- a/spot/misc/optionmap.cc +++ b/spot/misc/optionmap.cc @@ -130,6 +130,7 @@ namespace spot int option_map::get(const char* option, int def) const { + is_used_ = true; unused_.erase(option); auto it = options_.find(option); return (it == options_.end()) ? def : it->second; @@ -138,6 +139,7 @@ namespace spot std::string option_map::get_str(const char* option, std::string def) const { + is_used_ = true; unused_.erase(option); auto it = options_str_.find(option); return (it == options_str_.end()) ? def : it->second; @@ -226,21 +228,25 @@ namespace spot void option_map::report_unused_options() const { - auto s = unused_.size(); - if (s == 0U) - return; - std::ostringstream os; - if (s == 1U) - { - os << "option '" << *unused_.begin() - << "' was not used (possible typo?)"; - } - else - { - os << "the following options where not used (possible typos?):"; - for (auto opt: unused_) - os << "\n\t- '" << opt << '\''; - } - throw std::runtime_error(os.str()); + // We don't consider that an unused map has unused options. + if (is_used_) + { + auto s = unused_.size(); + if (s == 0U) + return; + std::ostringstream os; + if (s == 1U) + { + os << "option '" << *unused_.begin() + << "' was not used (possible typo?)"; + } + else + { + os << "the following options where not used (possible typos?):"; + for (auto opt: unused_) + os << "\n\t- '" << opt << '\''; + } + throw std::runtime_error(os.str()); + } } } diff --git a/spot/misc/optionmap.hh b/spot/misc/optionmap.hh index 229733a18..11ec8c456 100644 --- a/spot/misc/optionmap.hh +++ b/spot/misc/optionmap.hh @@ -110,6 +110,7 @@ namespace spot // will be erased as they are used. The resulting set can be used // for diagnosing errors. mutable std::set unused_; + mutable bool is_used_ = false; void set_(const std::string&, int val); void set_str_(const std::string&, const std::string& val); From 328cf9581629f29a168b376be0e4309c86a7f258 Mon Sep 17 00:00:00 2001 From: Florian Renkin Date: Tue, 22 Mar 2022 14:50:49 +0100 Subject: [PATCH 021/337] ltlsynt: generalization of the bypass * spot/twaalgos/synthesis.cc, spot/twaalgos/synthesis.hh: generalize the bypass and avoid to construct a strategy when we want realizability. * bin/ltlsynt.cc: adapt for realizability * tests/core/ltlsynt.test: update tests --- bin/ltlsynt.cc | 5 +- spot/twaalgos/synthesis.cc | 325 ++++++++++++++++++++++--------------- spot/twaalgos/synthesis.hh | 4 +- tests/core/ltlsynt.test | 75 ++------- 4 files changed, 212 insertions(+), 197 deletions(-) diff --git a/bin/ltlsynt.cc b/bin/ltlsynt.cc index 8dcd9511a..3d9f46900 100644 --- a/bin/ltlsynt.cc +++ b/bin/ltlsynt.cc @@ -376,7 +376,7 @@ namespace // we never use the direct approach if (!want_game) m_like = - spot::try_create_direct_strategy(*sub_f, *sub_o, *gi); + spot::try_create_direct_strategy(*sub_f, *sub_o, *gi, !opt_real); switch (m_like.success) { @@ -431,7 +431,8 @@ namespace // the direct approach yielded a strategy // which can now be minimized // We minimize only if we need it - assert(m_like.mealy_like && "Expected success but found no mealy!"); + assert(opt_real || + (m_like.mealy_like && "Expected success but found no mealy!")); if (!opt_real) { // Keep the machine split for aiger diff --git a/spot/twaalgos/synthesis.cc b/spot/twaalgos/synthesis.cc index 95e0725d9..dc79f8cce 100644 --- a/spot/twaalgos/synthesis.cc +++ b/spot/twaalgos/synthesis.cc @@ -1180,16 +1180,19 @@ namespace spot mealy_like try_create_direct_strategy(formula f, const std::vector& output_aps, - synthesis_info &gi) + synthesis_info &gi, bool want_strategy) { auto vs = gi.verbose_stream; auto& bv = gi.bv; + bdd_dict_ptr& dict = gi.dict; + int tmp; if (vs) *vs << "trying to create strategy directly for " << f << '\n'; - auto ret_sol_maybe = [&vs]() + auto ret_sol_maybe = [&vs, &tmp, &dict]() { + dict->unregister_all_my_variables(&tmp); if (vs) *vs << "direct strategy might exist but was not found.\n"; return mealy_like{ @@ -1197,8 +1200,9 @@ namespace spot nullptr, bddfalse}; }; - auto ret_sol_none = [&vs]() + auto ret_sol_none = [&vs, &tmp, &dict]() { + dict->unregister_all_my_variables(&tmp); if (vs) *vs << "no strategy exists.\n"; return mealy_like{ @@ -1207,15 +1211,23 @@ namespace spot bddfalse}; }; - auto ret_sol_exists = [&vs](auto strat) + auto ret_sol_exists = + [&vs, &want_strategy, &tmp, &dict](twa_graph_ptr strat) { + dict->unregister_all_my_variables(&tmp); if (vs) { - *vs << "direct strategy was found.\n" - << "direct strat has " << strat->num_states() + *vs << "direct strategy was found.\n"; + if (want_strategy) + { + *vs << "direct strat has " << strat->num_states() << " states, " << strat->num_edges() << " edges and " << strat->num_sets() << " colors\n"; + + } } + if (strat) + strat->merge_edges(); return mealy_like{ mealy_like::realizability_code::REALIZABLE_REGULAR, strat, @@ -1223,88 +1235,142 @@ namespace spot }; formula_2_inout_props form2props(output_aps); - auto output_aps_set = std::set(output_aps.begin(), - output_aps.end()); - - formula f_g = formula::tt(), f_left, f_right; - - // If we have a formula like G(b₁) ∧ (φ ↔ GFb₂), we extract b₁ and - // continue the construction for (φ ↔ GFb₂). + formula f_g, f_other; + // If it is G(α) ∧ G(β) ∧ … if (f.is(op::And)) { - if (f.size() != 2) - return ret_sol_maybe(); - if (f[0].is(op::G) && f[0][0].is_boolean()) - { - f_g = f[0]; - f = f[1]; - } - else if (f[1].is(op::G) && f[1][0].is_boolean()) - { - f_g = f[1]; - f = f[0]; - } - else - return ret_sol_maybe(); - } - if (f.is(op::Equiv)) - { - auto [left_ins, left_outs] = form2props.aps_of(f[0]); - auto [right_ins, right_outs] = form2props.aps_of(f[1]); + std::vector gs; + std::vector others; + for (auto child : f) + if (child.is(op::G) && child[0].is_boolean()) + gs.push_back(child[0]); + else + others.push_back(child); - auto properties_vector = [](const formula& f, - const std::set& ins, - const std::set& outs) + f_g = formula::And(gs); + f_other = formula::And(others); + } + else if (f.is(op::G) && f[0].is_boolean()) + { + f_g = f[0]; + f_other = formula::tt(); + } + else + { + f_g = formula::tt(); + f_other = f; + } + + // We have to check if the content of G is realizable (input-complete) + bdd output_bdd_tmp = bddtrue; + for (auto& out : output_aps) + output_bdd_tmp &= bdd_ithvar( + dict->register_proposition(formula::ap(out), &tmp)); + + if (!f_g.is_tt()) + { + auto g_bdd = formula_to_bdd(f_g, dict, &tmp); + if (bdd_exist(g_bdd, output_bdd_tmp) != bddtrue) + return ret_sol_none(); + } + + if (f_other.is(op::Equiv)) + { + // Check if FG or GF + auto is_general = [&tmp, &output_bdd_tmp, &dict](const formula &f, + op first, op second) { - return std::vector - { - f.is({op::G, op::F}) && f[0][0].is_boolean() && ins.empty(), - f.is_syntactic_recurrence() && outs.empty(), - // f is FG(bool) - f.is({op::F, op::G}) && f[0][0].is_boolean() && ins.empty(), - f.is_syntactic_persistence() && outs.empty() - }; + if (!f.is({first, second}) || !f[0][0].is_boolean()) + return false; + auto f_bdd = formula_to_bdd(f[0][0], dict, &tmp); + if (bdd_exist(f_bdd, output_bdd_tmp) != bddtrue) + return false; + f_bdd = formula_to_bdd(formula::Not(f[0][0]), dict, &tmp); + bool res = (bdd_exist(f_bdd, output_bdd_tmp) == bddtrue); + return res; }; - // We need to detect - // GF(outs) ↔ recurrence(ins), - // recurrence(ins) ↔ GF(outs), - // FG(outs) ↔ persistence(ins), - // persistence(ins) ↔ FG(outs) - const auto left_properties = properties_vector(f[0], left_ins, left_outs), - right_properties = properties_vector(f[1], right_ins, right_outs); + + auto is_gf = [is_general](const formula& f) + { + return is_general(f, op::G, op::F); + }; + + auto is_fg = [is_general](const formula& f) + { + return is_general(f, op::F, op::G); + }; + + auto is_co_bu = [](const formula &f, const std::set& outs) + { + return outs.empty() && f.is_syntactic_obligation(); + }; + + auto is_buchi = [](const formula &f, const std::set& outs) + { + return outs.empty() && f.is_syntactic_recurrence(); + }; + + auto properties_vector = [&](const formula &f, + const std::set &outs) + { + auto is_lgf = is_gf(f); + auto is_lfg = is_fg(f); + return std::vector{ + // f is GF(ins + outs) <-> buchi(ins) + is_lgf, + is_buchi(f, outs), + // f is FG(ins + outs) <-> co-buchi(ins) + is_lfg, + is_co_bu(f, outs)}; + }; + + + auto [left_ins, left_outs] = form2props.aps_of(f_other[0]); + auto [right_ins, right_outs] = form2props.aps_of(f_other[1]); + + auto left_properties = properties_vector(f_other[0], left_outs); + auto right_properties = properties_vector(f_other[1], right_outs); + unsigned combin = -1U; for (unsigned i = 0; i < 4; ++i) - { - if (left_properties[i] && right_properties[(i%2) ? (i-1) : (i+1)]) + if (left_properties[i] && right_properties[(i % 2) ? (i - 1) : (i + 1)]) { combin = i; break; } - } + + // If we don't match, we don't know if (combin == -1U) return ret_sol_maybe(); - // left is the recurrence (resp. persistence) - // right is GF(outs) (resp. GF(outs)) - // If f[0] is GF or FG - f_left = f[(combin+1)%2]; - f_right = f[combin%2]; - if (!(combin%2)) + // We know that a strategy exists and we don't want to construct it. + if (!want_strategy) + return ret_sol_exists(nullptr); + + formula f_left = f_other[(combin + 1) % 2]; + formula f_right = f_other[combin % 2]; + if (!(combin % 2)) { std::swap(left_ins, right_ins); std::swap(left_outs, right_outs); } auto trans = create_translator(gi); - trans.set_type(combin < 2 ? postprocessor::Buchi - : postprocessor::CoBuchi); + trans.set_pref(postprocessor::Deterministic | postprocessor::Complete); + if (combin < 2) + trans.set_type(postprocessor::Buchi); + else + trans.set_type(postprocessor::CoBuchi); stopwatch sw; if (bv) sw.start(); auto res = trans.run(f_left); + if (!is_deterministic(res)) + return ret_sol_maybe(); + if (bv) { auto delta = sw.stop(); @@ -1312,79 +1378,76 @@ namespace spot if (vs) *vs << "tanslating formula done in " << delta << " seconds\n"; } - - if (!is_deterministic(res)) - return ret_sol_maybe(); - for (auto& out : right_outs) - res->register_ap(out.ap_name()); - - // The BDD that describes the content of the G in a conjunction - bdd g_bdd = bddtrue; - - // Convert the set of outputs to a BDD - bdd output_bdd = bddtrue; - for (auto &out : output_aps_set) - output_bdd &= bdd_ithvar(res->register_ap(out)); - - if (!f_g.is_tt()) - { - g_bdd = formula_to_bdd(f_g[0], res->get_dict(), res); - // If the content of G is not input-complete, a simple strategy for - // env is to play this missing value. - if (bdd_exist(g_bdd, output_bdd) != bddtrue) - { - return ret_sol_none(); - } - } - - // For the GF(outs) (resp. GF(outs)), the content and its negation can be - // converted to a BDD. - bdd right_bdd, neg_right_bdd; - if (combin < 2) - { - right_bdd = formula_to_bdd(f_right[0][0], res->get_dict(), res); - neg_right_bdd = bdd_not(right_bdd); - } - else - { - neg_right_bdd = formula_to_bdd(f_right[0][0], res->get_dict(), res); - right_bdd = bdd_not(neg_right_bdd); - } - // Monitor is a special case. As we color accepting transitions, if the - // acceptance is true, we cannot say that a transition is accepting if - // a color is seen. - const bool is_true = res->acc().is_t(); - scc_info si(res, scc_info_options::NONE); - for (auto& e : res->edges()) - { - // Here the part describing the outputs is based on the fact that - // they must be seen infinitely often. As these edges are seen - // finitely often, we can let the minimization choose the value. - if (si.scc_of(e.src) == si.scc_of(e.dst)) - { - if (e.acc || is_true) - e.cond &= right_bdd; - else - e.cond &= neg_right_bdd; - } - // g_bdd has to be true all the time. So we cannot only do it - // between SCCs. - e.cond &= g_bdd; - if (e.cond == bddfalse) - return ret_sol_maybe(); - // The recurrence is Büchi but the strategy is a monitor. We need - // to remove the color. - e.acc = {}; - } - - set_synthesis_outputs(res, output_bdd); - res->set_acceptance(acc_cond::acc_code::t()); - res->prop_complete(trival::maybe()); + + bdd output_bdd = bddtrue; + auto [is, os] = form2props.aps_of(f); + for (auto i : is) + res->register_ap(i); + for (auto o : os) + output_bdd &= bdd_ithvar(res->register_ap(o)); + + bdd right_bdd = formula_to_bdd(f_right[0][0], dict, res); + bdd neg_right_bdd = bdd_not(right_bdd); + bdd g_bdd = formula_to_bdd(f_g, dict, res); + + if (combin > 1) + std::swap(right_bdd, neg_right_bdd); + + right_bdd = bdd_and(right_bdd, g_bdd); + neg_right_bdd = bdd_and(neg_right_bdd, g_bdd); + + scc_info si(res, scc_info_options::NONE); + + bool is_true_acc = ((combin < 2) && res->acc().is_t()) + || ((combin > 1) && res->acc().is_f()); + auto prop_vector = propagate_marks_vector(res); + auto& ev = res->edge_vector(); + for (unsigned i = 1; i < ev.size(); ++i) + { + auto &edge = ev[i]; + if (si.scc_of(edge.src) == si.scc_of(edge.dst)) + { + if (edge.acc || is_true_acc) + edge.cond &= right_bdd; + // If we have a GF and an edge is not colored but prop_vector says + // that this edge could be colored, it means that we can do what we + // want + else if (!prop_vector[i]) + edge.cond &= neg_right_bdd; + else + edge.cond &= g_bdd; + } + else + edge.cond &= g_bdd; + edge.acc = {}; + } + res->set_acceptance(acc_cond::acc_code::t()); + res->set_named_prop("synthesis-outputs", new bdd(output_bdd)); + return ret_sol_exists(res); } - else - return ret_sol_maybe(); + else if (f_other.is_tt()) + { + if (!want_strategy) + return ret_sol_exists(nullptr); + auto res = make_twa_graph(dict); + + bdd output_bdd = bddtrue; + auto [ins_f, _] = form2props.aps_of(f_g); + for (auto &out : output_aps) + output_bdd &= bdd_ithvar(res->register_ap(out)); + + for (auto &in : ins_f) + res->register_ap(in); + + res->set_named_prop("synthesis-outputs", new bdd(output_bdd)); + bdd g_bdd = formula_to_bdd(f_g, dict, res); + res->new_state(); + res->new_edge(0, 0, g_bdd); + return ret_sol_exists(res); + } + return ret_sol_maybe(); } } // spot diff --git a/spot/twaalgos/synthesis.hh b/spot/twaalgos/synthesis.hh index a5fced429..115b8097c 100644 --- a/spot/twaalgos/synthesis.hh +++ b/spot/twaalgos/synthesis.hh @@ -241,10 +241,12 @@ namespace spot /// \param f The formula to synthesize a strategy for /// \param output_aps A vector with the name of all output properties. /// All APs not named in this vector are treated as inputs + /// \param want_strategy Set to false if we don't want to construct the + /// strategy but only test realizability. SPOT_API mealy_like try_create_direct_strategy(formula f, const std::vector& output_aps, - synthesis_info& gi); + synthesis_info& gi, bool want_strategy = false); /// \ingroup synthesis /// \brief Solve a game, and update synthesis_info diff --git a/tests/core/ltlsynt.test b/tests/core/ltlsynt.test index 742f05a5e..f0cda98af 100644 --- a/tests/core/ltlsynt.test +++ b/tests/core/ltlsynt.test @@ -193,9 +193,7 @@ diff out exp cat >exp < GFb -tanslating formula done in X seconds direct strategy was found. -direct strat has 1 states, 2 edges and 0 colors EOF ltlsynt --ins='a' --outs='b' -f 'GFa <-> GFb' --verbose --realizability 2> out sed 's/ [0-9.e-]* seconds/ X seconds/g' out > outx @@ -214,9 +212,7 @@ diff outx exp cat >exp < GFe -tanslating formula done in X seconds direct strategy was found. -direct strat has 16 states, 81 edges and 0 colors EOF ltlsynt --ins='a,b,c,d' --outs='e' -f '(Fa & Fb & Fc & Fd) <-> GFe' \ --verbose --realizability --algo=lar 2> out @@ -526,15 +522,14 @@ REALIZABLE HOA: v1 States: 1 Start: 0 -AP: 3 "a" "b" "c" +AP: 3 "c" "a" "b" acc-name: all Acceptance: 0 t properties: trans-labels explicit-labels state-acc deterministic -controllable-AP: 2 +controllable-AP: 0 --BODY-- State: 0 -[!0&!2 | !1&!2] 0 -[0&1&2] 0 +[!0&!1 | !0&!2 | 0&1&2] 0 --END-- EOF ltlsynt --ins=a,b -f 'G (a & b <=> c)' >stdout @@ -563,15 +558,8 @@ direct strategy was found. direct strat has 1 states, 2 edges and 0 colors simplification took X seconds trying to create strategy directly for Gc -direct strategy might exist but was not found. -translating formula done in X seconds -automaton has 1 states and 1 colors -LAR construction done in X seconds -DPA has 1 states, 0 colors -split inputs and outputs done in X seconds -automaton has 2 states -solving game with acceptance: Streett 1 -game solved in X seconds +direct strategy was found. +direct strat has 1 states, 1 edges and 0 colors simplification took X seconds EOF ltlsynt -f '(GFa <-> GFb) && (Gc)' --outs=b,c --verbose 2> out @@ -599,7 +587,6 @@ done # # impossible to find a strategy. cat >exp < GFa) & G(a & c) -tanslating formula done in X seconds no strategy exists. EOF ltlsynt -f '(GFb <-> GFa) && G(a&c)' --outs=b,c --verbose\ @@ -687,26 +674,12 @@ diff outx exp # Here, G!(!x | !y) should be Gx & Gy cat >exp <exp < out || true sed 's/ [0-9.e-]* seconds/ X seconds/g' out > outx @@ -734,15 +699,7 @@ diff outx exp # Here, G!(a -> b) should be G(a) & G(!b) cat >exp < b)' --outs=b --decompose=yes --aiger\ --verbose 2> out || true @@ -815,15 +772,7 @@ diff outx exp # Here, !(F(a | b)) should be G!a & G!b cat >exp < out || true From 7abcf4e38bfb97c884adf2e6e3977432261314c9 Mon Sep 17 00:00:00 2001 From: Florian Renkin Date: Tue, 22 Mar 2022 15:08:40 +0100 Subject: [PATCH 022/337] ltlsynt: create a "bypass" option * bin/ltlsynt.cc: here. * tests/core/ltlsynt.test: add tests --- bin/ltlsynt.cc | 23 ++++++++++++++++++++++- tests/core/ltlsynt.test | 38 ++++++++++++++++++++++++++++++++++++-- 2 files changed, 58 insertions(+), 3 deletions(-) diff --git a/bin/ltlsynt.cc b/bin/ltlsynt.cc index 3d9f46900..0e5d765a1 100644 --- a/bin/ltlsynt.cc +++ b/bin/ltlsynt.cc @@ -44,6 +44,7 @@ enum { OPT_ALGO = 256, + OPT_BYPASS, OPT_CSV, OPT_DECOMPOSE, OPT_INPUT, @@ -81,6 +82,9 @@ static const argp_option options[] = " \"acd\": translate to a deterministic automaton with arbitrary" " acceptance condition, then use ACD to turn to parity," " then split.\n", 0 }, + { "bypass", OPT_BYPASS, "yes|no", 0, + "whether to try to avoid to construct a parity game " + "(enabled by default)", 0}, { "decompose", OPT_DECOMPOSE, "yes|no", 0, "whether to decompose the specification as multiple output-disjoint " "problems to solve independently (enabled by default)", 0 }, @@ -182,6 +186,20 @@ static spot::synthesis_info::algo const algo_types[] = }; ARGMATCH_VERIFY(algo_args, algo_types); +static const char* const bypass_args[] = + { + "yes", "true", "enabled", "1", + "no", "false", "disabled", "0", + nullptr + }; +static bool bypass_values[] = + { + true, true, true, true, + false, false, false, false, + }; +ARGMATCH_VERIFY(bypass_args, bypass_values); +bool opt_bypass = true; + static const char* const decompose_args[] = { "yes", "true", "enabled", "1", @@ -374,7 +392,7 @@ namespace }; // If we want to print a game, // we never use the direct approach - if (!want_game) + if (!want_game && opt_bypass) m_like = spot::try_create_direct_strategy(*sub_f, *sub_o, *gi, !opt_real); @@ -638,6 +656,9 @@ parse_opt(int key, char *arg, struct argp_state *) case OPT_ALGO: gi->s = XARGMATCH("--algo", arg, algo_args, algo_types); break; + case OPT_BYPASS: + opt_bypass = XARGMATCH("--bypass", arg, bypass_args, bypass_values); + break; case OPT_CSV: opt_csv = arg ? arg : "-"; if (not gi->bv) diff --git a/tests/core/ltlsynt.test b/tests/core/ltlsynt.test index f0cda98af..60b96bb46 100644 --- a/tests/core/ltlsynt.test +++ b/tests/core/ltlsynt.test @@ -566,7 +566,7 @@ ltlsynt -f '(GFa <-> GFb) && (Gc)' --outs=b,c --verbose 2> out sed 's/ [0-9.e-]* seconds/ X seconds/g' out > outx diff outx exp -# Try to find a direct strategy for (GFa <-> GFb) & Gc. THe order should not +# Try to find a direct strategy for (GFa <-> GFb) & Gc. The order should not # impact the result for f in "(GFa <-> GFb) & Gc" "(GFb <-> GFa) & Gc" \ "Gc & (GFa <-> GFb)" "Gc & (GFb <-> GFa)" @@ -594,7 +594,7 @@ ltlsynt -f '(GFb <-> GFa) && G(a&c)' --outs=b,c --verbose\ sed 's/ [0-9.e-]* seconds/ X seconds/g' out > outx diff outx exp -# # Ltlsynt should be able to create a strategy when the last G +# # ltlsynt should be able to create a strategy when the last G # is input-complete. cat >exp < GFa) & G((a & c) | (!a & !c)) @@ -796,3 +796,37 @@ LTL='(((((G (((((((g_0) && (G (! (r_0)))) -> (F (! (g_0)))) && (((g_0) && OUT='g_0, g_1' ltlsynt --outs="$OUT" -f "$LTL" --aiger=both --algo=acd | grep "aag 8 2 2 2 4" ltlsynt --outs="$OUT" -f "$LTL" --aiger=both --algo=lar | grep "aag 34 2 3 2 29" + +ltlsynt -f 'G(c) & (G(a) <-> GFb)' --outs=b,c --decompose=yes\ + --verbose --realizability 2> out +cat >exp < GFb +direct strategy was found. +EOF +diff out exp + +ltlsynt -f 'G(c) & (G(a) <-> GFb)' --outs=b,c --decompose=yes\ + --verbose --realizability --bypass=no 2> out +cat >exp < outx +diff outx exp From 46f3f5aaf42d96cc68826758837bc3e4a6eef5b2 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Fri, 25 Mar 2022 09:25:04 +0100 Subject: [PATCH 023/337] * doc/org/tut40.org: Clarify, as suggested by a CAV'22 reviewer. --- doc/org/tut40.org | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/doc/org/tut40.org b/doc/org/tut40.org index b68efe558..8d9b004da 100644 --- a/doc/org/tut40.org +++ b/doc/org/tut40.org @@ -144,9 +144,11 @@ states. We now look at how to create such a game in Python. -Essentially, a game in Spot is just an automaton equiped with a -special property to indicate the owner of each states. So it can be -created using the usual interface: +Essentially, a game in Spot is just an automaton equiped with a [[file:concepts.org::#named-properties][named +property "state-player"]] that hold a Boolean vector indicating the +owner of each state. The game can be created using the usual +automaton interface, and the owners are set by calling +=game.set_state_players()= with a vector of Boolean at the very end. #+NAME: build_game #+BEGIN_SRC python :exports code @@ -173,7 +175,7 @@ created using the usual interface: todo = [] # Create the state (i, j) for a player if it does not exist yet and - # returns the state's number in the game. + # return the state's number in the game. def get_game_state(player, i, j): orig_state = s_orig_states if player else d_orig_states if (i, j) in orig_state: From 9c6a09890ea2820771bc08df96d4d1fca8d9dc75 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Sat, 26 Mar 2022 15:57:56 +0100 Subject: [PATCH 024/337] parsetl: speedup parsing of n-ary operators with many operands Issue #500, reported by Yann Thierry-Mieg. * spot/parsetl/parsetl.yy, spot/parsetl/scantl.ll: Use variant to store a new pnode objects that delays the construction of n-ary operators. * spot/parsetl/Makefile.am: Do not distribute stack.hh anymore. * spot/tl/formula.cc: Fix detection of overflow in Star and FStar. * HACKING: Update Bison requirements to 3.3. * tests/core/500.test: New test case. * tests/Makefile.am: Add it. * tests/core/ltl2tgba2.test, tests/core/ltlsynt.test, tests/core/tostring.test: Adjust to new expected order. * NEWS: Mention the change. --- HACKING | 2 +- NEWS | 9 + spot/parsetl/Makefile.am | 3 +- spot/parsetl/parsetl.yy | 416 +++++++++++++++++++++++++------------- spot/parsetl/scantl.ll | 55 ++--- spot/tl/formula.cc | 12 +- tests/Makefile.am | 1 + tests/core/500.test | 43 ++++ tests/core/ltl2tgba2.test | 6 +- tests/core/ltlsynt.test | 2 +- tests/core/tostring.test | 6 +- 11 files changed, 374 insertions(+), 181 deletions(-) create mode 100755 tests/core/500.test diff --git a/HACKING b/HACKING index c6e127a70..de461376b 100644 --- a/HACKING +++ b/HACKING @@ -25,7 +25,7 @@ since the generated files they produce are distributed.) GNU Automake >= 1.11 GNU Libtool >= 2.4 GNU Flex >= 2.6 - GNU Bison >= 3.0 + GNU Bison >= 3.3 GNU Emacs (preferably >= 24 but it may work with older versions) org-mode >= 9.1 (the version that comes bundled with your emacs version is likely out-of-date; but distribution often have diff --git a/NEWS b/NEWS index 345ec7fdd..81aaf9a22 100644 --- a/NEWS +++ b/NEWS @@ -66,6 +66,15 @@ New in spot 2.10.4.dev (net yet released) - purge_dead_states() will now also remove edges labeled by false (except self-loops). + - When parsing formulas with a huge number of operands for an n-ary + operator (for instance 'p1 | p2 | ... | p1000') the LTL parser + would construct that formula two operand at a time, and the + formula constructor for that operator would be responsible for + inlining, sorting, deduplicating, ... all operands at each step. + This resulted in a worst-than-quadratic slowdown. This is now + averted in the parser by delaying the construction of such n-ary + nodes until all children are known. + Bugs fixed: - reduce_parity() produced incorrect results when applied to diff --git a/spot/parsetl/Makefile.am b/spot/parsetl/Makefile.am index d98c9ebab..f218ca067 100644 --- a/spot/parsetl/Makefile.am +++ b/spot/parsetl/Makefile.am @@ -1,5 +1,5 @@ ## -*- coding: utf-8 -*- -## Copyright (C) 2008-2015, 2018 Laboratoire de Recherche et +## Copyright (C) 2008-2015, 2018, 2022 Laboratoire de Recherche et ## Développement de l'Epita (LRDE). ## Copyright (C) 2003, 2004 Laboratoire d'Informatique de Paris ## 6 (LIP6), département Systèmes Répartis Coopératifs (SRC), @@ -30,7 +30,6 @@ noinst_LTLIBRARIES = libparsetl.la PARSETL_YY = parsetl.yy FROM_PARSETL_YY_MAIN = parsetl.cc FROM_PARSETL_YY_OTHERS = \ - stack.hh \ parsetl.hh FROM_PARSETL_YY = $(FROM_PARSETL_YY_MAIN) $(FROM_PARSETL_YY_OTHERS) diff --git a/spot/parsetl/parsetl.yy b/spot/parsetl/parsetl.yy index bbcdedcb5..e6defffb3 100644 --- a/spot/parsetl/parsetl.yy +++ b/spot/parsetl/parsetl.yy @@ -1,7 +1,6 @@ /* -*- coding: utf-8 -*- - -** Copyright (C) 2009-2019, 2021 Laboratoire de Recherche et Développement -** de l'Epita (LRDE). +** Copyright (C) 2009-2019, 2021, 2022 Laboratoire de Recherche et +** Développement de l'Epita (LRDE). ** Copyright (C) 2003-2006 Laboratoire d'Informatique de Paris 6 ** (LIP6), département Systèmes Répartis Coopératifs (SRC), Université ** Pierre et Marie Curie. @@ -21,11 +20,13 @@ ** You should have received a copy of the GNU General Public License ** along with this program. If not, see . */ -%require "3.0" +%require "3.3" %language "C++" %locations %defines %define api.prefix {tlyy} +%define api.value.type variant +%define api.value.automove true %debug %define parse.error verbose %expect 0 @@ -37,25 +38,164 @@ #include "config.h" #include #include +#include #include #include #include struct minmax_t { unsigned min, max; }; + + // pnode (parsing node) is simular to fnode (formula node) except + // that n-ary operators will delay their construction until all + // children are known; this is a hack to speedup the parsing, + // because n-ary operator usually do a lot of work on construction + // (sorting all children if the operator is commutative, removing + // duplicates if applicable, etc.). Building n-ary nodes by + // repeatedly calling the binary constructor as we did in the past + // has a prohibitive cost. See issue #500. + + struct nary + { + std::vector children; + spot::op kind; + }; + + struct pnode + { + // Hold either a constructed formula, or an n-ary operator that we + // will construct only when it is combined with a different + // operator. + std::variant data; + // Record whether this pnode has been transformed into a fnode( or + // moved to another pnode). If that occurred, the ownership of + // any fnode we store has been transfered to the constructed fnode + // (or to the other pnode), and our destructor has nothing to do. + // This is the usual case while parsing a formula without error. + // However during error recovering, the parser may have to discard + // unused pnode, in which case we have to remember to free fnode + // during destruction. + // + // We have to track this used status because pnode are destructed + // whenever the parser pops a token, and as of Bison 3.7.6, the + // handling of "%destructor" is broken when + // "%define api.value.type variant" is used. See + // https://lists.gnu.org/archive/html/bug-bison/2022-03/msg00000.html + bool used = false; + + pnode() + : data(nullptr) + { + } + + pnode(const spot::fnode* ltl) + : data(ltl) + { + } + + // We only support move construction. + pnode(const pnode& other) = delete; + pnode& operator=(const pnode& other) = delete; + + pnode(pnode&& other) + : data(std::move(other.data)) + { + other.used = true; + } + + pnode& operator=(pnode&& other) + { + data = std::move(other.data); + other.used = true; + return *this; + } + + ~pnode() + { + if (used) + return; + if (auto* n = std::get_if(&data)) + { + for (auto f: n->children) + f->destroy(); + } + else + { + auto* f = std::get(data); + // The only case where we expect f to be nullptr, is if + // parse_ap() return nullptr: then $$ is unset when YYERROR + // is called. + if (f) + f->destroy(); + } + } + + // Create a new n-ary node from left and right. + // This will empty left and right so that their + // destructor do nothing. + pnode(spot::op o, pnode&& left, pnode&& right) + : data(nary{}) + { + nary& n = std::get(data); + n.kind = o; + if (auto* nleft = std::get_if(&left.data); + nleft && nleft->kind == o) + std::swap(n.children, nleft->children); + else + n.children.push_back(left); + if (auto* nright = std::get_if(&right.data); + nright && nright->kind == o) + { + auto& rch = nright->children; + n.children.insert(n.children.end(), rch.begin(), rch.end()); + rch.clear(); + } + else + { + n.children.push_back(right); + } + } + + operator const spot::fnode*() + { + used = true; + if (auto* n = std::get_if(&data)) + { + return spot::fnode::multop(n->kind, n->children); + } + else + { + return std::get(data); + } + } + + // Convert to a temporary formula, for printing, do not mark as + // used. + const spot::formula tmp() const + { + const spot::fnode* f; + if (auto* n = std::get_if(&data)) + { + for (auto c: n->children) + c->clone(); + f = spot::fnode::multop(n->kind, n->children); + } + else + { + f = std::get(data); + assert(f != nullptr); + f->clone(); + } + return spot::formula(f); + } + }; + + } %parse-param {spot::parse_error_list &error_list} %parse-param {spot::environment &parse_environment} %parse-param {spot::formula &result} -%union -{ - std::string* str; - const spot::fnode* ltl; - unsigned num; - minmax_t minmax; -} - %code { /* parsetl.hh and parsedecl.hh include each other recursively. We mut ensure that YYSTYPE is declared (by the above %union) @@ -84,28 +224,20 @@ using namespace spot; } \ while (0); -// right is missing, so complain and use false. -#define missing_right_binop_hard(res, left, op, str) \ - do \ - { \ - left->destroy(); \ - missing_right_op(res, op, str); \ - } \ - while (0); - - static bool + static const fnode* sere_ensure_bool(const fnode* f, const spot::location& loc, const char* oper, spot::parse_error_list& error_list) { if (f->is_boolean()) - return true; + return f; + f->destroy(); std::string s; s.reserve(80); s = "not a Boolean expression: in a SERE "; s += oper; s += " can only be applied to a Boolean expression"; error_list.emplace_back(loc, s); - return false; + return nullptr; } static const fnode* @@ -196,9 +328,9 @@ using namespace spot; %token START_SERE "SERE start marker" %token START_BOOL "BOOLEAN start marker" %token PAR_OPEN "opening parenthesis" PAR_CLOSE "closing parenthesis" -%token PAR_BLOCK "(...) block" -%token BRA_BLOCK "{...} block" -%token BRA_BANG_BLOCK "{...}! block" +%token PAR_BLOCK "(...) block" +%token BRA_BLOCK "{...} block" +%token BRA_BANG_BLOCK "{...}! block" %token BRACE_OPEN "opening brace" BRACE_CLOSE "closing brace" %token BRACE_BANG_CLOSE "closing brace-bang" %token OP_OR "or operator" OP_XOR "xor operator" @@ -221,7 +353,7 @@ using namespace spot; %token OP_GOTO_OPEN "opening bracket for goto operator" %token OP_SQBKT_CLOSE "closing bracket" %token OP_SQBKT_STRONG_CLOSE "closing !]" -%token OP_SQBKT_NUM "number for square bracket operator" +%token OP_SQBKT_NUM "number for square bracket operator" %token OP_UNBOUNDED "unbounded mark" %token OP_SQBKT_SEP "separator for square bracket operator" %token OP_UCONCAT "universal concat operator" @@ -229,12 +361,12 @@ using namespace spot; %token OP_UCONCAT_NONO "universal non-overlapping concat operator" %token OP_ECONCAT_NONO "existential non-overlapping concat operator" %token OP_FIRST_MATCH "first_match" -%token ATOMIC_PROP "atomic proposition" +%token ATOMIC_PROP "atomic proposition" %token OP_CONCAT "concat operator" OP_FUSION "fusion operator" %token CONST_TRUE "constant true" CONST_FALSE "constant false" %token END_OF_INPUT "end of formula" %token OP_POST_NEG "negative suffix" OP_POST_POS "positive suffix" -%token OP_DELAY_N "SVA delay operator" +%token OP_DELAY_N "SVA delay operator" %token OP_DELAY_OPEN "opening bracket for SVA delay operator" %token OP_DELAY_PLUS "##[+] operator" %token OP_DELAY_STAR "##[*] operator" @@ -276,19 +408,16 @@ using namespace spot; need any precedence). */ %precedence OP_NOT -%type subformula atomprop booleanatom sere lbtformula boolformula -%type bracedsere parenthesedsubformula -%type starargs fstarargs equalargs sqbracketargs gotoargs delayargs -%type sqbkt_num +%type subformula atomprop booleanatom sere lbtformula +%type boolformula bracedsere parenthesedsubformula +%type starargs fstarargs equalargs sqbracketargs gotoargs delayargs +%type sqbkt_num -%destructor { delete $$; } -%destructor { $$->destroy(); } - -%printer { debug_stream() << *$$; } -%printer { print_psl(debug_stream(), formula($$->clone())); } -%printer { print_sere(debug_stream(), formula($$->clone())); } sere bracedsere -%printer { debug_stream() << $$; } -%printer { debug_stream() << $$.min << ".." << $$.max; } +%printer { debug_stream() << $$; } +%printer { print_psl(debug_stream(), $$.tmp()); } +%printer { print_sere(debug_stream(), $$.tmp()); } sere bracedsere +%printer { debug_stream() << $$; } +%printer { debug_stream() << $$.min << ".." << $$.max; } %% result: START_LTL subformula END_OF_INPUT @@ -380,18 +509,19 @@ error_opt: %empty sqbkt_num: OP_SQBKT_NUM { - if ($1 >= fnode::unbounded()) + auto n = $1; + if (n >= fnode::unbounded()) { auto max = fnode::unbounded() - 1; std::ostringstream s; - s << $1 << " exceeds maximum supported repetition (" + s << n << " exceeds maximum supported repetition (" << max << ")"; error_list.emplace_back(@1, s.str()); $$ = max; } else { - $$ = $1; + $$ = n; } } @@ -484,10 +614,10 @@ delayargs: OP_DELAY_OPEN sqbracketargs atomprop: ATOMIC_PROP { - $$ = parse_ap(*$1, @1, parse_environment, error_list); - delete $1; - if (!$$) + auto* f = parse_ap($1, @1, parse_environment, error_list); + if (!f) YYERROR; + $$ = f; } booleanatom: atomprop @@ -504,13 +634,12 @@ booleanatom: atomprop sere: booleanatom | OP_NOT sere { - if (sere_ensure_bool($2, @2, "`!'", error_list)) + if (auto f = sere_ensure_bool($2, @2, "`!'", error_list)) { - $$ = fnode::unop(op::Not, $2); + $$ = fnode::unop(op::Not, f); } else { - $2->destroy(); $$ = error_false_block(@$, error_list); } } @@ -518,9 +647,8 @@ sere: booleanatom | PAR_BLOCK { $$ = - try_recursive_parse(*$1, @1, parse_environment, + try_recursive_parse($1, @1, parse_environment, debug_level(), parser_sere, error_list); - delete $1; if (!$$) YYERROR; } @@ -543,134 +671,142 @@ sere: booleanatom $$ = fnode::ff(); } | sere OP_AND sere - { $$ = fnode::multop(op::AndRat, {$1, $3}); } + { $$ = pnode(op::AndRat, $1, $3); } | sere OP_AND error { missing_right_binop($$, $1, @2, "length-matching and operator"); } | sere OP_SHORT_AND sere - { $$ = fnode::multop(op::AndNLM, {$1, $3}); } + { $$ = pnode(op::AndNLM, $1, $3); } | sere OP_SHORT_AND error { missing_right_binop($$, $1, @2, "non-length-matching and operator"); } | sere OP_OR sere - { $$ = fnode::multop(op::OrRat, {$1, $3}); } + { $$ = pnode(op::OrRat, $1, $3); } | sere OP_OR error { missing_right_binop($$, $1, @2, "or operator"); } | sere OP_CONCAT sere - { $$ = fnode::multop(op::Concat, {$1, $3}); } + { $$ = pnode(op::Concat, $1, $3); } | sere OP_CONCAT error { missing_right_binop($$, $1, @2, "concat operator"); } | sere OP_FUSION sere - { $$ = fnode::multop(op::Fusion, {$1, $3}); } + { $$ = pnode(op::Fusion, $1, $3); } | sere OP_FUSION error { missing_right_binop($$, $1, @2, "fusion operator"); } | OP_DELAY_N sere - { $$ = formula::sugar_delay(formula($2), $1, $1).to_node_(); } + { unsigned n = $1; $$ = formula::sugar_delay(formula($2), n, n).to_node_(); } | OP_DELAY_N error { missing_right_binop($$, fnode::tt(), @1, "SVA delay operator"); } | sere OP_DELAY_N sere - { $$ = formula::sugar_delay(formula($1), formula($3), - $2, $2).to_node_(); } + { unsigned n = $2; + $$ = formula::sugar_delay(formula($1), formula($3), + n, n).to_node_(); } | sere OP_DELAY_N error { missing_right_binop($$, $1, @2, "SVA delay operator"); } | delayargs sere %prec OP_DELAY_OPEN { - if ($1.max < $1.min) + auto [min, max] = $1; + if (max < min) { error_list.emplace_back(@1, "reversed range"); - std::swap($1.max, $1.min); + std::swap(max, min); } $$ = formula::sugar_delay(formula($2), - $1.min, $1.max).to_node_(); + min, max).to_node_(); } | delayargs error { missing_right_binop($$, fnode::tt(), @1, "SVA delay operator"); } | sere delayargs sere %prec OP_DELAY_OPEN { - if ($2.max < $2.min) + auto [min, max] = $2; + if (max < min) { error_list.emplace_back(@1, "reversed range"); - std::swap($2.max, $2.min); + std::swap(max, min); } $$ = formula::sugar_delay(formula($1), formula($3), - $2.min, $2.max).to_node_(); + min, max).to_node_(); } | sere delayargs error { missing_right_binop($$, $1, @2, "SVA delay operator"); } | starargs { - if ($1.max < $1.min) + auto [min, max] = $1; + if (max < min) { error_list.emplace_back(@1, "reversed range"); - std::swap($1.max, $1.min); + std::swap(max, min); } - $$ = fnode::bunop(op::Star, fnode::tt(), $1.min, $1.max); + $$ = fnode::bunop(op::Star, fnode::tt(), min, max); } | sere starargs { - if ($2.max < $2.min) + auto [min, max] = $2; + if (max < min) { error_list.emplace_back(@2, "reversed range"); - std::swap($2.max, $2.min); + std::swap(max, min); } - $$ = fnode::bunop(op::Star, $1, $2.min, $2.max); + $$ = fnode::bunop(op::Star, $1, min, max); } | sere fstarargs { - if ($2.max < $2.min) + auto [min, max] = $2; + if (max < min) { error_list.emplace_back(@2, "reversed range"); - std::swap($2.max, $2.min); + std::swap(max, min); } - $$ = fnode::bunop(op::FStar, $1, $2.min, $2.max); + $$ = fnode::bunop(op::FStar, $1, min, max); } | sere equalargs { - if ($2.max < $2.min) + auto [min, max] = $2; + if (max < min) { error_list.emplace_back(@2, "reversed range"); - std::swap($2.max, $2.min); + std::swap(max, min); } - if (sere_ensure_bool($1, @1, "[=...]", error_list)) + if (auto f = sere_ensure_bool($1, @1, "[=...]", error_list)) { - $$ = formula::sugar_equal(formula($1), - $2.min, $2.max).to_node_(); + $$ = formula::sugar_equal(formula(f), + min, max).to_node_(); } else { - $1->destroy(); $$ = error_false_block(@$, error_list); } } | sere gotoargs { - if ($2.max < $2.min) + auto [min, max] = $2; + if (max < min) { error_list.emplace_back(@2, "reversed range"); - std::swap($2.max, $2.min); + std::swap(max, min); } - if (sere_ensure_bool($1, @1, "[->...]", error_list)) + if (auto f = sere_ensure_bool($1, @1, "[->...]", error_list)) { - $$ = formula::sugar_goto(formula($1), - $2.min, $2.max).to_node_(); + $$ = formula::sugar_goto(formula(f), min, max).to_node_(); } else { - $1->destroy(); $$ = error_false_block(@$, error_list); } } | sere OP_XOR sere { - if (sere_ensure_bool($1, @1, "`^'", error_list) - && sere_ensure_bool($3, @3, "`^'", error_list)) + auto left = sere_ensure_bool($1, @1, "`^'", error_list); + auto right = sere_ensure_bool($3, @3, "`^'", error_list); + if (left && right) { - $$ = fnode::binop(op::Xor, $1, $3); + $$ = fnode::binop(op::Xor, left, right); } else { - $1->destroy(); - $3->destroy(); + if (left) + left->destroy(); + else if (right) + right->destroy(); $$ = error_false_block(@$, error_list); } } @@ -678,14 +814,13 @@ sere: booleanatom { missing_right_binop($$, $1, @2, "xor operator"); } | sere OP_IMPLIES sere { - if (sere_ensure_bool($1, @1, "`->'", error_list)) + auto left = sere_ensure_bool($1, @1, "`->'", error_list); + if (left) { - $$ = fnode::binop(op::Implies, $1, $3); + $$ = fnode::binop(op::Implies, left, $3); } else { - $1->destroy(); - $3->destroy(); $$ = error_false_block(@$, error_list); } } @@ -693,15 +828,18 @@ sere: booleanatom { missing_right_binop($$, $1, @2, "implication operator"); } | sere OP_EQUIV sere { - if (sere_ensure_bool($1, @1, "`<->'", error_list) - && sere_ensure_bool($3, @3, "`<->'", error_list)) + auto left = sere_ensure_bool($1, @1, "`<->'", error_list); + auto right = sere_ensure_bool($3, @3, "`<->'", error_list); + if (left && right) { - $$ = fnode::binop(op::Equiv, $1, $3); + $$ = fnode::binop(op::Equiv, left, right); } else { - $1->destroy(); - $3->destroy(); + if (left) + left->destroy(); + else if (right) + right->destroy(); $$ = error_false_block(@$, error_list); } } @@ -739,19 +877,17 @@ bracedsere: BRACE_OPEN sere BRACE_CLOSE } | BRA_BLOCK { - $$ = try_recursive_parse(*$1, @1, parse_environment, + $$ = try_recursive_parse($1, @1, parse_environment, debug_level(), parser_sere, error_list); - delete $1; if (!$$) YYERROR; } parenthesedsubformula: PAR_BLOCK { - $$ = try_recursive_parse(*$1, @1, parse_environment, + $$ = try_recursive_parse($1, @1, parse_environment, debug_level(), parser_ltl, error_list); - delete $1; if (!$$) YYERROR; } @@ -786,10 +922,9 @@ parenthesedsubformula: PAR_BLOCK boolformula: booleanatom | PAR_BLOCK { - $$ = try_recursive_parse(*$1, @1, parse_environment, + $$ = try_recursive_parse($1, @1, parse_environment, debug_level(), parser_bool, error_list); - delete $1; if (!$$) YYERROR; } @@ -821,19 +956,19 @@ boolformula: booleanatom $$ = fnode::ff(); } | boolformula OP_AND boolformula - { $$ = fnode::multop(op::And, {$1, $3}); } + { $$ = pnode(op::And, $1, $3); } | boolformula OP_AND error { missing_right_binop($$, $1, @2, "and operator"); } | boolformula OP_SHORT_AND boolformula - { $$ = fnode::multop(op::And, {$1, $3}); } + { $$ = pnode(op::And, $1, $3); } | boolformula OP_SHORT_AND error { missing_right_binop($$, $1, @2, "and operator"); } | boolformula OP_STAR boolformula - { $$ = fnode::multop(op::And, {$1, $3}); } + { $$ = pnode(op::And, $1, $3); } | boolformula OP_STAR error { missing_right_binop($$, $1, @2, "and operator"); } | boolformula OP_OR boolformula - { $$ = fnode::multop(op::Or, {$1, $3}); } + { $$ = pnode(op::Or, $1, $3); } | boolformula OP_OR error { missing_right_binop($$, $1, @2, "or operator"); } | boolformula OP_XOR boolformula @@ -856,19 +991,19 @@ boolformula: booleanatom subformula: booleanatom | parenthesedsubformula | subformula OP_AND subformula - { $$ = fnode::multop(op::And, {$1, $3}); } + { $$ = pnode(op::And, $1, $3); } | subformula OP_AND error { missing_right_binop($$, $1, @2, "and operator"); } | subformula OP_SHORT_AND subformula - { $$ = fnode::multop(op::And, {$1, $3}); } + { $$ = pnode(op::And, $1, $3); } | subformula OP_SHORT_AND error { missing_right_binop($$, $1, @2, "and operator"); } | subformula OP_STAR subformula - { $$ = fnode::multop(op::And, {$1, $3}); } + { $$ = pnode(op::And, $1, $3); } | subformula OP_STAR error { missing_right_binop($$, $1, @2, "and operator"); } | subformula OP_OR subformula - { $$ = fnode::multop(op::Or, {$1, $3}); } + { $$ = pnode(op::Or, $1, $3); } | subformula OP_OR error { missing_right_binop($$, $1, @2, "or operator"); } | subformula OP_XOR subformula @@ -904,13 +1039,15 @@ subformula: booleanatom | OP_F error { missing_right_op($$, @1, "sometimes operator"); } | OP_FREP sqbkt_num OP_SQBKT_CLOSE subformula %prec OP_FREP - { $$ = fnode::nested_unop_range(op::X, op::Or, $2, $2, $4); + { unsigned n = $2; + $$ = fnode::nested_unop_range(op::X, op::Or, n, n, $4); error_list.emplace_back(@1 + @3, "F[n:m] expects two parameters"); } | OP_FREP sqbkt_num OP_SQBKT_STRONG_CLOSE subformula %prec OP_FREP - { $$ = fnode::nested_unop_range(op::strong_X, op::Or, $2, $2, $4); + { unsigned n = $2; + $$ = fnode::nested_unop_range(op::strong_X, op::Or, n, n, $4); error_list.emplace_back(@1 + @3, "F[n:m!] expects two parameters"); } @@ -966,14 +1103,16 @@ subformula: booleanatom { $$ = fnode::nested_unop_range(op::strong_X, op::And, $2, fnode::unbounded(), $5); } | OP_GREP sqbkt_num OP_SQBKT_CLOSE subformula %prec OP_GREP - { $$ = fnode::nested_unop_range(op::X, op::And, $2, $2, $4); + { unsigned n = $2; + $$ = fnode::nested_unop_range(op::X, op::And, n, n, $4); error_list.emplace_back(@1 + @3, "G[n:m] expects two parameters"); } | OP_GREP sqbkt_num OP_SQBKT_STRONG_CLOSE subformula %prec OP_GREP - { $$ = fnode::nested_unop_range(op::strong_X, op::And, - $2, $2, $4); + { unsigned n = $2; + $$ = fnode::nested_unop_range(op::strong_X, op::And, + n, n, $4); error_list.emplace_back(@1 + @3, "G[n:m!] expects two parameters"); } @@ -1003,7 +1142,8 @@ subformula: booleanatom | OP_STRONG_X error { missing_right_op($$, @1, "strong next operator"); } | OP_XREP sqbkt_num OP_SQBKT_CLOSE subformula %prec OP_XREP - { $$ = fnode::nested_unop_range(op::X, op::Or, $2, $2, $4); } + { unsigned n = $2; + $$ = fnode::nested_unop_range(op::X, op::Or, n, n, $4); } | OP_XREP sqbkt_num OP_SQBKT_CLOSE error { missing_right_op($$, @1 + @3, "X[.] operator"); } | OP_XREP error OP_SQBKT_CLOSE subformula %prec OP_XREP @@ -1013,8 +1153,9 @@ subformula: booleanatom { $$ = fnode::unop(op::strong_X, $3); } | OP_XREP sqbkt_num OP_SQBKT_STRONG_CLOSE subformula %prec OP_XREP - { $$ = fnode::nested_unop_range(op::strong_X, - op::Or, $2, $2, $4); } + { unsigned n = $2; + $$ = fnode::nested_unop_range(op::strong_X, + op::Or, n, n, $4); } | OP_XREP error OP_SQBKT_STRONG_CLOSE subformula %prec OP_XREP { error_list.emplace_back(@$, "treating this X[.!] as a simple X[!]"); $$ = fnode::unop(op::strong_X, $4); } @@ -1032,41 +1173,40 @@ subformula: booleanatom | bracedsere parenthesedsubformula { $$ = fnode::binop(op::UConcat, $1, $2); } | bracedsere OP_UCONCAT error - { missing_right_binop_hard($$, $1, @2, - "universal overlapping concat operator"); } + { missing_right_op($$, @2, + "universal overlapping concat operator"); } | bracedsere OP_ECONCAT subformula { $$ = fnode::binop(op::EConcat, $1, $3); } | bracedsere OP_ECONCAT error - { missing_right_binop_hard($$, $1, @2, - "existential overlapping concat operator"); + { missing_right_op($$, @2, + "existential overlapping concat operator"); } | bracedsere OP_UCONCAT_NONO subformula /* {SERE}[]=>EXP = {SERE;1}[]->EXP */ { $$ = fnode::binop(op::UConcat, - fnode::multop(op::Concat, {$1, fnode::tt()}), + pnode(op::Concat, $1, fnode::tt()), $3); } | bracedsere OP_UCONCAT_NONO error - { missing_right_binop_hard($$, $1, @2, - "universal non-overlapping concat operator"); + { missing_right_op($$, @2, + "universal non-overlapping concat operator"); } | bracedsere OP_ECONCAT_NONO subformula /* {SERE}<>=>EXP = {SERE;1}<>->EXP */ { $$ = fnode::binop(op::EConcat, - fnode::multop(op::Concat, {$1, fnode::tt()}), + pnode(op::Concat, $1, fnode::tt()), $3); } | bracedsere OP_ECONCAT_NONO error - { missing_right_binop_hard($$, $1, @2, - "existential non-overlapping concat operator"); + { missing_right_op($$, @2, + "existential non-overlapping concat operator"); } | BRACE_OPEN sere BRACE_BANG_CLOSE /* {SERE}! = {SERE} <>-> 1 */ { $$ = fnode::binop(op::EConcat, $2, fnode::tt()); } | BRA_BANG_BLOCK { - $$ = try_recursive_parse(*$1, @1, parse_environment, + $$ = try_recursive_parse($1, @1, parse_environment, debug_level(), parser_sere, error_list); - delete $1; if (!$$) YYERROR; $$ = fnode::binop(op::EConcat, $$, fnode::tt()); @@ -1076,9 +1216,9 @@ lbtformula: atomprop | '!' lbtformula { $$ = fnode::unop(op::Not, $2); } | '&' lbtformula lbtformula - { $$ = fnode::multop(op::And, {$2, $3}); } + { $$ = pnode(op::And, $2, $3); } | '|' lbtformula lbtformula - { $$ = fnode::multop(op::Or, {$2, $3}); } + { $$ = pnode(op::Or, $2, $3); } | '^' lbtformula lbtformula { $$ = fnode::binop(op::Xor, $2, $3); } | 'i' lbtformula lbtformula diff --git a/spot/parsetl/scantl.ll b/spot/parsetl/scantl.ll index 34fbfef32..871f1300d 100644 --- a/spot/parsetl/scantl.ll +++ b/spot/parsetl/scantl.ll @@ -130,26 +130,26 @@ eol2 (\n\r)+|(\r\n)+ recursively. */ BEGIN(in_par); parent_level = 1; - yylval->str = new std::string(); + yylval->emplace(); } { "(" { ++parent_level; - yylval->str->append(yytext, yyleng); + yylval->as().append(yytext, yyleng); } ")" { if (--parent_level) { - yylval->str->append(yytext, yyleng); + yylval->as().append(yytext, yyleng); } else { BEGIN(not_prop); - spot::trim(*yylval->str); + spot::trim(yylval->as()); return token::PAR_BLOCK; } } - [^()]+ yylval->str->append(yytext, yyleng); + [^()]+ yylval->as().append(yytext, yyleng); <> { unput(')'); if (!missing_parent) @@ -172,38 +172,38 @@ eol2 (\n\r)+|(\r\n)+ recursively. */ BEGIN(in_bra); parent_level = 1; - yylval->str = new std::string(); + yylval->emplace(); } { "{" { ++parent_level; - yylval->str->append(yytext, yyleng); + yylval->as().append(yytext, yyleng); } "}"[ \t]*"!" { if (--parent_level) { - yylval->str->append(yytext, yyleng); + yylval->as().append(yytext, yyleng); } else { BEGIN(not_prop); - spot::trim(*yylval->str); + spot::trim(yylval->as()); return token::BRA_BANG_BLOCK; } } "}" { if (--parent_level) { - yylval->str->append(yytext, yyleng); + yylval->as().append(yytext, yyleng); } else { BEGIN(not_prop); - spot::trim(*yylval->str); + spot::trim(yylval->as()); return token::BRA_BLOCK; } } - [^{}]+ yylval->str->append(yytext, yyleng); + [^{}]+ yylval->as().append(yytext, yyleng); <> { unput('}'); if (!missing_parent) @@ -231,35 +231,36 @@ eol2 (\n\r)+|(\r\n)+ /* SVA operators */ "##"[0-9] { - yylval->num = yytext[2] - '0'; + yylval->emplace(yytext[2] - '0'); return token::OP_DELAY_N; } "##"[0-9][0-9] { - yylval->num = - yytext[2] * 10 + yytext[3] - '0' * 11; + yylval->emplace(yytext[2] * 10 + + yytext[3] + - '0' * 11); return token::OP_DELAY_N; } "##"[0-9]{3,} { errno = 0; unsigned long n = strtoul(yytext + 2, 0, 10); - yylval->num = n; - if (errno || yylval->num != n) + yylval->emplace(n); + if (errno || yylval->as() != n) { error_list.push_back( spot::one_parse_error(*yylloc, "value too large ignored")); - yylval->num = 1; + yylval->emplace(1); } - if (yylval->num >= spot::fnode::unbounded()) + if (yylval->as() >= spot::fnode::unbounded()) { auto max = spot::fnode::unbounded() - 1; std::ostringstream s; - s << yylval->num + s << yylval->as() << (" exceeds maximum supported " "repetition (") << max << ")"; error_list.emplace_back(*yylloc, s.str()); - yylval->num = max; + yylval->emplace(max); } return token::OP_DELAY_N; } @@ -288,8 +289,8 @@ eol2 (\n\r)+|(\r\n)+ [0-9]+ { errno = 0; unsigned long n = strtoul(yytext, 0, 10); - yylval->num = n; - if (errno || yylval->num != n) + yylval->emplace(n); + if (errno || yylval->as() != n) { error_list.push_back( spot::one_parse_error(*yylloc, @@ -380,7 +381,7 @@ eol2 (\n\r)+|(\r\n)+ */ [a-zA-EH-LN-QSTYZ_.][a-zA-EH-WYZ0-9_.]* | [a-zA-EH-LN-QSTYZ_.][a-zA-EH-WYZ0-9_.][a-zA-Z0-9_.]* { - yylval->str = new std::string(yytext, yyleng); + yylval->emplace(yytext, yyleng); BEGIN(not_prop); return token::ATOMIC_PROP; } @@ -401,7 +402,7 @@ eol2 (\n\r)+|(\r\n)+ { \" { BEGIN(orig_cond); - yylval->str = new std::string(s); + yylval->emplace(s); return token::ATOMIC_PROP; } {eol} { @@ -419,7 +420,7 @@ eol2 (\n\r)+|(\r\n)+ spot::one_parse_error(*yylloc, "unclosed string")); BEGIN(orig_cond); - yylval->str = new std::string(s); + yylval->emplace(s); return token::ATOMIC_PROP; } } @@ -430,7 +431,7 @@ eol2 (\n\r)+|(\r\n)+ for compatibility with ltl2dstar we also accept any alphanumeric string that is not an operator. */ [a-zA-Z._][a-zA-Z0-9._]* { - yylval->str = new std::string(yytext, yyleng); + yylval->emplace(yytext, yyleng); return token::ATOMIC_PROP; } diff --git a/spot/tl/formula.cc b/spot/tl/formula.cc index 8fe91bf70..fb4ab0d49 100644 --- a/spot/tl/formula.cc +++ b/spot/tl/formula.cc @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2015-2019, 2021 Laboratoire de Recherche et +// Copyright (C) 2015-2019, 2021, 2022 Laboratoire de Recherche et // Développement de l'Epita (LRDE). // // This file is part of Spot, a model checking library. @@ -136,7 +136,7 @@ namespace spot // - AndRat(Exps1...,Bool1,Exps2...,Bool2,Exps3...) = // AndRat(And(Bool1,Bool2),Exps1...,Exps2...,Exps3...) // - OrRat(Exps1...,Bool1,Exps2...,Bool2,Exps3...) = - // AndRat(Or(Bool1,Bool2),Exps1...,Exps2...,Exps3...) + // OrRat(Or(Bool1,Bool2),Exps1...,Exps2...,Exps3...) if (!b.empty()) v.insert(v.begin(), fnode::multop(o, std::move(b))); } @@ -588,9 +588,9 @@ namespace spot } else if (min != unbounded()) { - min += min2; - if (SPOT_UNLIKELY(min >= unbounded())) + if (SPOT_UNLIKELY(min + min2 >= unbounded())) break; + min += min2; } if (max2 == unbounded()) { @@ -598,9 +598,9 @@ namespace spot } else if (max != unbounded()) { - max += max2; - if (SPOT_UNLIKELY(max >= unbounded())) + if (SPOT_UNLIKELY(max + max2 >= unbounded())) break; + max += max2; } (*i)->destroy(); i = v.erase(i); diff --git a/tests/Makefile.am b/tests/Makefile.am index afcd0c8d2..c8a722f5c 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -165,6 +165,7 @@ TESTS_tl = \ core/parse.test \ core/parseerr.test \ core/utf8.test \ + core/500.test \ core/length.test \ core/equals.test \ core/tostring.test \ diff --git a/tests/core/500.test b/tests/core/500.test new file mode 100755 index 000000000..60d5c6365 --- /dev/null +++ b/tests/core/500.test @@ -0,0 +1,43 @@ +#!/bin/sh +# -*- coding: utf-8 -*- +# Copyright (C) 2022 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 . + +. ./defs + +set -e + +# The LTL parser used to exhibit a worse-than-quadratic behavior on +# n-ary operators with many children. See issue #500. Before the +# fix, this test would run for ages. + +awk 'BEGIN{x="s0"; for(i = 1; i < 40000; ++i) x=x " | s" i; print x;}' | + ltlfilt --stats=%x > out +test 40000 = `cat out` + +awk 'BEGIN{x="s0"; for(i = 1; i < 40000; ++i) x=x " & s" i; print x;}' | + ltlfilt --stats=%x > out +test 40000 = `cat out` + +awk 'BEGIN{x="s0"; for(i = 1; i < 40000; ++i) x=x ";s" i; print "{" x "}";}' | + ltlfilt --stats=%x > out +test 40000 = `cat out` + +awk 'BEGIN{x="s0"; for(i = 1; i < 40000; ++i) x=x ":s" i; print "{" x "}";}' | + ltlfilt --stats=%x > out +test 40000 = `cat out` diff --git a/tests/core/ltl2tgba2.test b/tests/core/ltl2tgba2.test index 79a07a17a..8397bbc85 100755 --- a/tests/core/ltl2tgba2.test +++ b/tests/core/ltl2tgba2.test @@ -1,6 +1,6 @@ #!/bin/sh # -*- coding: utf-8 -*- -# Copyright (C) 2009-2021 Laboratoire de Recherche et Développement de +# Copyright (C) 2009-2022 Laboratoire de Recherche et Développement de # l'Epita (LRDE). # Copyright (C) 2003, 2004 Laboratoire d'Informatique de Paris 6 (LIP6), # département Systèmes Répartis Coopératifs (SRC), Université Pierre @@ -375,8 +375,8 @@ diff output expected cat >formulas < outx diff outx exp cat >exp < GFe +trying to create strategy directly for GFe <-> (Fa & Fb & Fc & Fd) direct strategy was found. EOF ltlsynt --ins='a,b,c,d' --outs='e' -f '(Fa & Fb & Fc & Fd) <-> GFe' \ diff --git a/tests/core/tostring.test b/tests/core/tostring.test index e559ea198..7067a8b2c 100755 --- a/tests/core/tostring.test +++ b/tests/core/tostring.test @@ -1,7 +1,7 @@ #! /bin/sh # -*- coding: utf-8 -*- -# Copyright (C) 2009, 2010, 2011, 2013, 2016 Laboratoire de Recherche et -# Développement de l'Epita (LRDE). +# Copyright (C) 2009-2011, 2013, 2016, 2022 Laboratoire de Recherche +# et Développement de l'Epita (LRDE). # Copyright (C) 2003, 2004 Laboratoire d'Informatique de Paris 6 (LIP6), # département Systèmes Répartis Coopératifs (SRC), Université Pierre # et Marie Curie. @@ -63,7 +63,7 @@ X"R" {a;b;{c && d[*]};[+]}[]-> G{a[*]:b[*]} GF!(b & (a | c)) GF!({b && {a | c[*]}}<>-> {{!a}[*]}) -GF({{a | c[*]} & b[*]}[]-> d) +GF({b[*] & {a | c[*]}}[]-> d) {a[*2..3]} {a[*0..1]} {a[*]} From 27d455389efc3272213193c6e493bb2f0f5ae3ac Mon Sep 17 00:00:00 2001 From: Philipp Schlehuber-Caissier Date: Tue, 29 Mar 2022 15:51:31 +0200 Subject: [PATCH 025/337] Correct bug in zielonka Optimization in Zielonka failed under certain circumstances todo: Devise a specialized test for direct attr computation * spot/twaalgos/game.cc: Correction * tests/python/game.py: Test --- spot/twaalgos/game.cc | 46 ++++++--- tests/python/game.py | 211 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 246 insertions(+), 11 deletions(-) diff --git a/spot/twaalgos/game.cc b/spot/twaalgos/game.cc index 9b8fdcee9..6bb62500d 100644 --- a/spot/twaalgos/game.cc +++ b/spot/twaalgos/game.cc @@ -309,16 +309,21 @@ namespace spot { auto scc_acc = info_->acc_sets_of(c_scc_idx_); // We will override all parities of edges leaving the scc + // Currently game is colored max odd + // So there is at least one color bool added[] = {false, false}; unsigned par_pair[2]; unsigned scc_new_par = std::max(scc_acc.max_set(), 1u); + bool player_color_larger; if (scc_new_par&1) { + player_color_larger = false; par_pair[1] = scc_new_par; par_pair[0] = scc_new_par+1; } else { + player_color_larger = true; par_pair[1] = scc_new_par+1; par_pair[0] = scc_new_par; } @@ -331,6 +336,7 @@ namespace spot for (unsigned v : c_states()) { assert(subgame_[v] == unseen_mark); + bool owner = (*owner_ptr_)[v]; for (auto &e : arena_->out(v)) { // The outgoing edges are taken finitely often @@ -342,14 +348,20 @@ namespace spot e.dst, e.acc); if (w_.winner(e.dst)) { - // Winning region of player -> odd - e.acc = odd_mark; + // Winning region off player -> + // odd mark if player + // else 1 (smallest loosing for env) + e.acc = owner ? odd_mark + : acc_cond::mark_t({1}); added[1] = true; } else { - // Winning region of env -> even - e.acc = even_mark; + // Winning region of env -> + // even mark for env, + // else 0 (smallest loosing for player) + e.acc = !owner ? even_mark + : acc_cond::mark_t({0}); added[0] = true; } // Replace with self-loop @@ -360,13 +372,22 @@ namespace spot // Compute the attractors of the self-loops/transitions leaving scc // These can be directly added to the winning states - // Note: attractors can not intersect therefore the order in which - // they are computed does not matter + // To avoid disregarding edges in attr computation we + // need to start with the larger color + // Todo come up with a test for this unsigned dummy_rd; - for (bool p : {false, true}) - if (added[p]) - attr(dummy_rd, p, par_pair[p], true, par_pair[p]); + for (bool p : {player_color_larger, + !player_color_larger}) + { + if (added[p]) + { + // Always take the larger, + // Otherwise states with an transition to a winning AND + // a loosing scc are treated incorrectly + attr(dummy_rd, p, par_pair[p], true, par_pair[p]); + } + } if (added[0] || added[1]) // Fix "negative" strategy @@ -379,8 +400,11 @@ namespace spot inline bool attr(unsigned &rd, bool p, unsigned max_par, - bool acc_par, unsigned min_win_par) + bool acc_par, unsigned min_win_par, + bool no_check=false) { + // In fix_scc, the attr computation is + // abused so we can not check ertain things // Computes the attractor of the winning set of player p within a // subgame given as rd. // If acc_par is true, max_par transitions are also accepting and @@ -394,7 +418,7 @@ namespace spot // As proposed in Oink! / PGSolver // Needs the transposed graph however - assert((!acc_par) || (acc_par && (max_par&1) == p)); + assert((no_check || !acc_par) || (acc_par && (max_par&1) == p)); assert(!acc_par || (0 < min_win_par)); assert((min_win_par <= max_par) && (max_par <= max_abs_par_)); diff --git a/tests/python/game.py b/tests/python/game.py index d7aec2f38..cea09f295 100644 --- a/tests/python/game.py +++ b/tests/python/game.py @@ -63,3 +63,214 @@ State: 7 State: 8 {1} [0] 2 --END--""") + +# Testing case where parity_game optimization +# lead to wrong results +si = spot.synthesis_info() + +game = spot.automaton("""HOA: v1 +States: 27 +Start: 7 +AP: 11 "a" "b" "c" "d" "e" "f" "g" "h" "i" "j" "k" +acc-name: parity max odd 3 +Acceptance: 3 Fin(2) & (Inf(1) | Fin(0)) +properties: trans-labels explicit-labels trans-acc colored +properties: deterministic +spot-state-player: 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 +controllable-AP: 0 1 2 3 4 5 6 7 +--BODY-- +State: 0 +[t] 8 {0} +State: 1 +[8&9] 8 {0} +[!8&!10 | !9&!10] 9 {0} +[!8&10 | !9&10] 10 {0} +State: 2 +[8&9] 8 {0} +[!8&!10 | !9&!10] 11 {0} +[!8&10 | !9&10] 12 {0} +State: 3 +[8&9] 8 {0} +[!9&!10] 13 {0} +[!8&10 | !9&10] 14 {0} +[!8&9&!10] 15 {0} +State: 4 +[8&9] 8 {0} +[!8&!10 | !9&!10] 16 {0} +[!8&!9&10] 17 {0} +[!8&9&10] 18 {0} +[8&!9&10] 19 {0} +State: 5 +[8&9] 8 {0} +[!9&!10] 20 {0} +[!8&10 | !9&10] 21 {0} +[!8&9&!10] 22 {0} +State: 6 +[8&9] 8 {0} +[!8&!10 | !9&!10] 23 {0} +[!8&!9&10] 24 {0} +[!8&9&10] 25 {0} +[8&!9&10] 26 {0} +State: 7 +[8&9] 8 {0} +[!9&!10] 13 {0} +[!8&9&!10] 15 {0} +[!8&!9&10] 17 {0} +[!8&9&10] 18 {0} +[8&!9&10] 19 {0} +State: 8 +[!0&!1&2&!3&!4&5&!6&7 | !0&!1&2&!3&!4&5&6&!7 | !0&!1&2&!3&4&!5&!6&7 | +!0&!1&2&!3&4&!5&6&!7 | !0&!1&2&3&!4&!5&!6&7 | !0&!1&2&3&!4&!5&6&!7 | +!0&1&!2&!3&!4&5&!6&7 | !0&1&!2&!3&!4&5&6&!7 | !0&1&!2&!3&4&!5&!6&7 | +!0&1&!2&!3&4&!5&6&!7 | !0&1&!2&3&!4&!5&!6&7 | !0&1&!2&3&!4&!5&6&!7 | +0&!1&!2&!3&!4&5&!6&7 | 0&!1&!2&!3&!4&5&6&!7 | 0&!1&!2&!3&4&!5&!6&7 | + 0&!1&!2&!3&4&!5&6&!7 | 0&!1&!2&3&!4&!5&!6&7 | 0&!1&!2&3&!4&!5&6&!7] 0 {1} +State: 9 +[!0&!1&2&!3&!4&5&!6&7 | !0&!1&2&!3&!4&5&6&!7 | !0&!1&2&!3&4&!5&!6&7 | +!0&!1&2&!3&4&!5&6&!7 | !0&1&!2&!3&!4&5&!6&7 | !0&1&!2&!3&!4&5&6&!7 | +!0&1&!2&!3&4&!5&!6&7 | !0&1&!2&!3&4&!5&6&!7 | 0&!1&!2&!3&!4&5&!6&7 | +0&!1&!2&!3&!4&5&6&!7 | 0&!1&!2&!3&4&!5&!6&7 | 0&!1&!2&!3&4&!5&6&!7] 1 {2} +[!0&!1&2&3&!4&!5&!6&7 | !0&!1&2&3&!4&!5&6&!7 | !0&1&!2&3&!4&!5&!6&7 | +!0&1&!2&3&!4&!5&6&!7 | 0&!1&!2&3&!4&!5&!6&7 | 0&!1&!2&3&!4&!5&6&!7] 2 {2} +State: 10 +[!0&!1&2&!3&!4&5&!6&7 | !0&!1&2&!3&!4&5&6&!7 | !0&!1&2&!3&4&!5&!6&7 | +!0&!1&2&!3&4&!5&6&!7 | !0&1&!2&!3&!4&5&!6&7 | !0&1&!2&!3&!4&5&6&!7 | +!0&1&!2&!3&4&!5&!6&7 | !0&1&!2&!3&4&!5&6&!7 | 0&!1&!2&!3&!4&5&!6&7 | +0&!1&!2&!3&!4&5&6&!7 | 0&!1&!2&!3&4&!5&!6&7 | 0&!1&!2&!3&4&!5&6&!7] 0 {1} +[!0&!1&2&3&!4&!5&!6&7 | !0&!1&2&3&!4&!5&6&!7 | !0&1&!2&3&!4&!5&!6&7 | +!0&1&!2&3&!4&!5&6&!7 | 0&!1&!2&3&!4&!5&!6&7 | 0&!1&!2&3&!4&!5&6&!7] 2 {2} +State: 11 +[!0&!1&2&!3&4&!5&!6&7 | !0&!1&2&!3&4&!5&6&!7 | !0&!1&2&3&!4&!5&!6&7 | +!0&!1&2&3&!4&!5&6&!7 | !0&1&!2&!3&4&!5&!6&7 | !0&1&!2&!3&4&!5&6&!7 | +!0&1&!2&3&!4&!5&!6&7 | !0&1&!2&3&!4&!5&6&!7 | 0&!1&!2&!3&4&!5&!6&7 | +0&!1&!2&!3&4&!5&6&!7 | 0&!1&!2&3&!4&!5&!6&7 | 0&!1&!2&3&!4&!5&6&!7] 0 {1} +[!0&!1&2&!3&!4&5&!6&7 | !0&!1&2&!3&!4&5&6&!7 | !0&1&!2&!3&!4&5&!6&7 | +!0&1&!2&!3&!4&5&6&!7 | 0&!1&!2&!3&!4&5&!6&7 | 0&!1&!2&!3&!4&5&6&!7] 1 {2} +State: 12 +[!0&!1&2&!3&!4&5&!6&7 | !0&!1&2&!3&!4&5&6&!7 | !0&1&!2&!3&!4&5&!6&7 | +!0&1&!2&!3&!4&5&6&!7 | 0&!1&!2&!3&!4&5&!6&7 | 0&!1&!2&!3&!4&5&6&!7] 1 {2} +[!0&!1&2&!3&4&!5&!6&7 | !0&!1&2&!3&4&!5&6&!7 | !0&!1&2&3&!4&!5&!6&7 | +!0&!1&2&3&!4&!5&6&!7 | !0&1&!2&!3&4&!5&!6&7 | !0&1&!2&!3&4&!5&6&!7 | +!0&1&!2&3&!4&!5&!6&7 | !0&1&!2&3&!4&!5&6&!7 | 0&!1&!2&!3&4&!5&!6&7 | +0&!1&!2&!3&4&!5&6&!7 | 0&!1&!2&3&!4&!5&!6&7 | 0&!1&!2&3&!4&!5&6&!7] 2 {2} +State: 13 +[!0&!1&2&!3&!4&5&6&!7 | !0&!1&2&!3&4&!5&6&!7 | !0&1&!2&!3&!4&5&!6&7 | +!0&1&!2&!3&!4&5&6&!7 | !0&1&!2&!3&4&!5&!6&7 | !0&1&!2&!3&4&!5&6&!7 | +0&!1&!2&!3&!4&5&!6&7 | 0&!1&!2&!3&!4&5&6&!7 | 0&!1&!2&!3&4&!5&!6&7 | +0&!1&!2&!3&4&!5&6&!7] 1 {1} +[!0&!1&2&3&!4&!5&6&!7 | !0&1&!2&3&!4&!5&!6&7 | !0&1&!2&3&!4&!5&6&!7 | +0&!1&!2&3&!4&!5&!6&7 | 0&!1&!2&3&!4&!5&6&!7] 2 {1} +[!0&!1&2&!3&!4&5&!6&7 | !0&!1&2&!3&4&!5&!6&7] 3 {1} +[!0&!1&2&3&!4&!5&!6&7] 5 {1} +State: 14 +[!0&!1&2&!3&!4&5&!6&7 | !0&!1&2&!3&!4&5&6&!7 | !0&!1&2&!3&4&!5&!6&7 | +!0&!1&2&!3&4&!5&6&!7 | !0&1&!2&!3&!4&5&!6&7 | !0&1&!2&!3&!4&5&6&!7 | +!0&1&!2&!3&4&!5&!6&7 | !0&1&!2&!3&4&!5&6&!7 | 0&!1&!2&!3&!4&5&!6&7 | +0&!1&!2&!3&!4&5&6&!7 | 0&!1&!2&!3&4&!5&!6&7 | 0&!1&!2&!3&4&!5&6&!7] 0 {1} +[!0&!1&2&3&!4&!5&!6&7 | !0&!1&2&3&!4&!5&6&!7 | !0&1&!2&3&!4&!5&!6&7 | +!0&1&!2&3&!4&!5&6&!7 | 0&!1&!2&3&!4&!5&!6&7 | 0&!1&!2&3&!4&!5&6&!7] 2 {1} +State: 15 +[!0&!1&2&!3&!4&5&6&!7 | !0&!1&2&!3&4&!5&6&!7 | !0&1&!2&!3&!4&5&!6&7 | +!0&1&!2&!3&!4&5&6&!7 | !0&1&!2&!3&4&!5&!6&7 | !0&1&!2&!3&4&!5&6&!7 | +0&!1&!2&!3&!4&5&!6&7 | 0&!1&!2&!3&!4&5&6&!7 | 0&!1&!2&!3&4&!5&!6&7 | +0&!1&!2&!3&4&!5&6&!7] 1 {1} +[!0&!1&2&3&!4&!5&6&!7 | !0&1&!2&3&!4&!5&!6&7 | !0&1&!2&3&!4&!5&6&!7 | +0&!1&!2&3&!4&!5&!6&7 | 0&!1&!2&3&!4&!5&6&!7] 2 {1} +[!0&!1&2&!3&!4&5&!6&7 | !0&!1&2&!3&4&!5&!6&7] 4 {1} +[!0&!1&2&3&!4&!5&!6&7] 6 {1} +State: 16 +[!0&!1&2&!3&!4&5&!6&7 | !0&!1&2&!3&!4&5&6&!7 | !0&!1&2&!3&4&!5&!6&7 | +!0&!1&2&!3&4&!5&6&!7 | !0&1&!2&!3&!4&5&!6&7 | !0&1&!2&!3&!4&5&6&!7 | +!0&1&!2&!3&4&!5&!6&7 | !0&1&!2&!3&4&!5&6&!7 | 0&!1&!2&!3&!4&5&!6&7 | +0&!1&!2&!3&!4&5&6&!7 | 0&!1&!2&!3&4&!5&!6&7 | 0&!1&!2&!3&4&!5&6&!7] 1 {1} +[!0&!1&2&3&!4&!5&!6&7 | !0&!1&2&3&!4&!5&6&!7 | !0&1&!2&3&!4&!5&!6&7 | +!0&1&!2&3&!4&!5&6&!7 | 0&!1&!2&3&!4&!5&!6&7 | 0&!1&!2&3&!4&!5&6&!7] 2 {1} +State: 17 +[!0&!1&2&!3&!4&5&!6&7 | !0&!1&2&!3&!4&5&6&!7 | !0&!1&2&!3&4&!5&!6&7 | +!0&!1&2&!3&4&!5&6&!7 | !0&1&!2&!3&!4&5&!6&7 | !0&1&!2&!3&!4&5&6&!7 | +!0&1&!2&!3&4&!5&!6&7 | !0&1&!2&!3&4&!5&6&!7 | 0&!1&!2&!3&!4&5&!6&7 | +0&!1&!2&!3&!4&5&6&!7 | 0&!1&!2&!3&4&!5&!6&7 | 0&!1&!2&!3&4&!5&6&!7] 0 {1} +[!0&!1&2&3&!4&!5&!6&7 | !0&!1&2&3&!4&!5&6&!7 | !0&1&!2&3&!4&!5&6&!7 | +0&!1&!2&3&!4&!5&!6&7 | 0&!1&!2&3&!4&!5&6&!7] 2 {1} +[!0&1&!2&3&!4&!5&!6&7] 6 {1} +State: 18 +[!0&!1&2&!3&!4&5&!6&7 | !0&!1&2&!3&!4&5&6&!7 | !0&!1&2&!3&4&!5&!6&7 | +!0&!1&2&!3&4&!5&6&!7 | !0&1&!2&!3&!4&5&!6&7 | !0&1&!2&!3&!4&5&6&!7 | +!0&1&!2&!3&4&!5&!6&7 | !0&1&!2&!3&4&!5&6&!7 | 0&!1&!2&!3&!4&5&!6&7 | +0&!1&!2&!3&!4&5&6&!7 | 0&!1&!2&!3&4&!5&!6&7 | 0&!1&!2&!3&4&!5&6&!7] 0 {1} +[!0&!1&2&3&!4&!5&!6&7 | !0&!1&2&3&!4&!5&6&!7 | !0&1&!2&3&!4&!5&6&!7 | +0&!1&!2&3&!4&!5&!6&7 | 0&!1&!2&3&!4&!5&6&!7] 2 {1} +[!0&1&!2&3&!4&!5&!6&7] 5 {1} +State: 19 +[!0&!1&2&!3&!4&5&!6&7 | !0&!1&2&!3&!4&5&6&!7 | !0&!1&2&!3&4&!5&!6&7 | +!0&!1&2&!3&4&!5&6&!7 | !0&1&!2&!3&!4&5&!6&7 | !0&1&!2&!3&!4&5&6&!7 | +!0&1&!2&!3&4&!5&!6&7 | !0&1&!2&!3&4&!5&6&!7 | 0&!1&!2&!3&!4&5&!6&7 | +0&!1&!2&!3&!4&5&6&!7 | 0&!1&!2&!3&4&!5&!6&7 | 0&!1&!2&!3&4&!5&6&!7] 0 {1} +[!0&!1&2&3&!4&!5&!6&7 | !0&!1&2&3&!4&!5&6&!7 | !0&1&!2&3&!4&!5&!6&7 | +0&!1&!2&3&!4&!5&!6&7 | 0&!1&!2&3&!4&!5&6&!7] 2 {1} +[!0&1&!2&3&!4&!5&6&!7] 6 {1} +State: 20 +[!0&!1&2&!3&4&!5&!6&7 | !0&!1&2&!3&4&!5&6&!7 | !0&!1&2&3&!4&!5&!6&7 | +!0&!1&2&3&!4&!5&6&!7 | !0&1&!2&!3&4&!5&!6&7 | !0&1&!2&!3&4&!5&6&!7 | +!0&1&!2&3&!4&!5&!6&7 | !0&1&!2&3&!4&!5&6&!7 | 0&!1&!2&!3&4&!5&!6&7 | +0&!1&!2&!3&4&!5&6&!7 | 0&!1&!2&3&!4&!5&!6&7 | 0&!1&!2&3&!4&!5&6&!7] 0 {1} +[!0&!1&2&!3&!4&5&6&!7 | !0&1&!2&!3&!4&5&!6&7 | !0&1&!2&!3&!4&5&6&!7 | +0&!1&!2&!3&!4&5&!6&7 | 0&!1&!2&!3&!4&5&6&!7] 1 {1} +[!0&!1&2&!3&!4&5&!6&7] 3 {1} +State: 21 +[!0&!1&2&!3&!4&5&!6&7 | !0&!1&2&!3&!4&5&6&!7 | !0&1&!2&!3&!4&5&!6&7 | +!0&1&!2&!3&!4&5&6&!7 | 0&!1&!2&!3&!4&5&!6&7 | 0&!1&!2&!3&!4&5&6&!7] 1 {1} +[!0&!1&2&!3&4&!5&!6&7 | !0&!1&2&!3&4&!5&6&!7 | !0&!1&2&3&!4&!5&!6&7 | +!0&!1&2&3&!4&!5&6&!7 | !0&1&!2&!3&4&!5&!6&7 | !0&1&!2&!3&4&!5&6&!7 | +!0&1&!2&3&!4&!5&!6&7 | !0&1&!2&3&!4&!5&6&!7 | 0&!1&!2&!3&4&!5&!6&7 | +0&!1&!2&!3&4&!5&6&!7 | 0&!1&!2&3&!4&!5&!6&7 | 0&!1&!2&3&!4&!5&6&!7] 2 {1} +State: 22 +[!0&!1&2&!3&4&!5&!6&7 | !0&!1&2&!3&4&!5&6&!7 | !0&!1&2&3&!4&!5&!6&7 | +!0&!1&2&3&!4&!5&6&!7 | !0&1&!2&!3&4&!5&!6&7 | !0&1&!2&!3&4&!5&6&!7 | +!0&1&!2&3&!4&!5&!6&7 | !0&1&!2&3&!4&!5&6&!7 | 0&!1&!2&!3&4&!5&!6&7 | +0&!1&!2&!3&4&!5&6&!7 | 0&!1&!2&3&!4&!5&!6&7 | 0&!1&!2&3&!4&!5&6&!7] 0 {1} +[!0&!1&2&!3&!4&5&6&!7 | !0&1&!2&!3&!4&5&!6&7 | !0&1&!2&!3&!4&5&6&!7 | +0&!1&!2&!3&!4&5&!6&7 | 0&!1&!2&!3&!4&5&6&!7] 1 {1} +[!0&!1&2&!3&!4&5&!6&7] 4 {1} +State: 23 +[!0&!1&2&!3&4&!5&!6&7 | !0&!1&2&!3&4&!5&6&!7 | !0&!1&2&3&!4&!5&!6&7 | +!0&!1&2&3&!4&!5&6&!7 | !0&1&!2&!3&4&!5&!6&7 | !0&1&!2&!3&4&!5&6&!7 | +!0&1&!2&3&!4&!5&!6&7 | !0&1&!2&3&!4&!5&6&!7 | 0&!1&!2&!3&4&!5&!6&7 | +0&!1&!2&!3&4&!5&6&!7 | 0&!1&!2&3&!4&!5&!6&7 | 0&!1&!2&3&!4&!5&6&!7] 0 {1} +[!0&!1&2&!3&!4&5&!6&7 | !0&!1&2&!3&!4&5&6&!7 | !0&1&!2&!3&!4&5&!6&7 | +!0&1&!2&!3&!4&5&6&!7 | 0&!1&!2&!3&!4&5&!6&7 | 0&!1&!2&!3&!4&5&6&!7] 1 {1} +State: 24 +[!0&!1&2&!3&!4&5&!6&7 | !0&!1&2&!3&!4&5&6&!7 | !0&1&!2&!3&!4&5&6&!7 | +0&!1&!2&!3&!4&5&!6&7 | 0&!1&!2&!3&!4&5&6&!7] 1 {1} +[!0&!1&2&!3&4&!5&!6&7 | !0&!1&2&!3&4&!5&6&!7 | !0&!1&2&3&!4&!5&!6&7 | +!0&!1&2&3&!4&!5&6&!7 | !0&1&!2&!3&4&!5&6&!7 | !0&1&!2&3&!4&!5&6&!7 | +0&!1&!2&!3&4&!5&!6&7 | 0&!1&!2&!3&4&!5&6&!7 | 0&!1&!2&3&!4&!5&!6&7 | +0&!1&!2&3&!4&!5&6&!7] 2 {1} +[!0&1&!2&!3&!4&5&!6&7] 4 {1} +[!0&1&!2&!3&4&!5&!6&7 | !0&1&!2&3&!4&!5&!6&7] 6 {1} +State: 25 +[!0&!1&2&!3&!4&5&!6&7 | !0&!1&2&!3&!4&5&6&!7 | !0&1&!2&!3&!4&5&6&!7 | +0&!1&!2&!3&!4&5&!6&7 | 0&!1&!2&!3&!4&5&6&!7] 1 {1} +[!0&!1&2&!3&4&!5&!6&7 | !0&!1&2&!3&4&!5&6&!7 | !0&!1&2&3&!4&!5&!6&7 | +!0&!1&2&3&!4&!5&6&!7 | !0&1&!2&!3&4&!5&6&!7 | !0&1&!2&3&!4&!5&6&!7 | +0&!1&!2&!3&4&!5&!6&7 | 0&!1&!2&!3&4&!5&6&!7 | 0&!1&!2&3&!4&!5&!6&7 | +0&!1&!2&3&!4&!5&6&!7] 2 {1} +[!0&1&!2&!3&!4&5&!6&7] 3 {1} +[!0&1&!2&!3&4&!5&!6&7 | !0&1&!2&3&!4&!5&!6&7] 5 {1} +State: 26 +[!0&!1&2&!3&!4&5&!6&7 | !0&!1&2&!3&!4&5&6&!7 | !0&1&!2&!3&!4&5&!6&7 | +0&!1&!2&!3&!4&5&!6&7 | 0&!1&!2&!3&!4&5&6&!7] 1 {1} +[!0&!1&2&!3&4&!5&!6&7 | !0&!1&2&!3&4&!5&6&!7 | !0&!1&2&3&!4&!5&!6&7 | +!0&!1&2&3&!4&!5&6&!7 | !0&1&!2&!3&4&!5&!6&7 | !0&1&!2&3&!4&!5&!6&7 | +0&!1&!2&!3&4&!5&!6&7 | 0&!1&!2&!3&4&!5&6&!7 | 0&!1&!2&3&!4&!5&!6&7 | +0&!1&!2&3&!4&!5&6&!7] 2 {1} +[!0&1&!2&!3&!4&5&6&!7] 4 {1} +[!0&1&!2&!3&4&!5&6&!7 | !0&1&!2&3&!4&!5&6&!7] 6 {1} +--END--""") + +tc.assertTrue(spot.solve_game(game, si)) + +games = spot.split_edges(game) +spot.set_state_players(games, spot.get_state_players(game)) +tc.assertTrue(spot.solve_game(games, si)) + From 5e1b75197122cdf3d3a461354f42dc577c3e017c Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Tue, 29 Mar 2022 11:13:19 +0200 Subject: [PATCH 026/337] debian: simplify LTO configuration to work around newer libtool Libtool 2.4.7 breaks if AR_FLAGS contains a space. See https://lists.gnu.org/archive/html/bug-libtool/2022-03/msg00009.html * debian/rules: Use gcc-{nm,ar,ranlib} so we do not have to pass the plugin explicitly. --- debian/rules | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/debian/rules b/debian/rules index 0193e9a62..51daf21ed 100755 --- a/debian/rules +++ b/debian/rules @@ -20,20 +20,16 @@ include /usr/share/dpkg/default.mk %: dh $@ --with=python3 -# Find the LTO plugin, which we need to pass to ar, nm, and ranlib. -LTOPLUG := $(shell gcc -v 2>&1 | \ - sed -n 's:COLLECT_LTO_WRAPPER=\(/.*/\)[^/]*:\1:p')liblto_plugin.so - # ARFLAGS is for Automake -# AR_FLAGS is for Libtool -# These activate the LTO pluggin, but also remove the 'u' option -# from ar, since its now ignored with Debian's default to 'D'. -LTOSETUP = \ - LDFLAGS='-fuse-linker-plugin' \ - NM='nm --plugin $(LTOPLUG)' \ - ARFLAGS='cr --plugin $(LTOPLUG)' \ - AR_FLAGS='cr --plugin $(LTOPLUG)' \ - RANLIB='ranlib --plugin $(LTOPLUG)' \ +# AR_FLAGS is for Libtool, (but libtool 2.4.7 will now use ARFLAGS as well) +# The gcc-tools activate the LTO plugin. +LTOSETUP = \ + LDFLAGS='-fuse-linker-plugin' \ + NM='gcc-nm' \ + AR='gcc-ar' \ + ARFLAGS='cr' \ + AR_FLAGS='cr' \ + RANLIB='gcc-ranlib' \ VALGRIND=false GCDADIR := $(shell pwd)/gcda From a211bace688577fe62160ab22b49fabcbdef922b Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Wed, 6 Apr 2022 15:25:44 +0200 Subject: [PATCH 027/337] autcross: implement --language-complemented MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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. --- NEWS | 3 +++ THANKS | 1 + bin/autcross.cc | 45 +++++++++++++++++++++++++++++++--------- doc/org/autcross.org | 9 +++++++- tests/core/autcross.test | 16 +++++++++++++- 5 files changed, 62 insertions(+), 12 deletions(-) diff --git a/NEWS b/NEWS index 81aaf9a22..78a14561b 100644 --- a/NEWS +++ b/NEWS @@ -14,6 +14,9 @@ New in spot 2.10.4.dev (net yet released) associated option --sonf-aps allows listing the newly introduced atomic propositions. + - autcross learned a --language-complemented option to assist in the + case one is testing tools that complement automata. (issue #504). + Library: - The new function suffix_operator_normal_form() implements diff --git a/THANKS b/THANKS index 9eb566483..b49b3eb95 100644 --- a/THANKS +++ b/THANKS @@ -41,6 +41,7 @@ Michael Weber Mikuláš Klokočka Ming-Hsien Tsai Nikos Gorogiannis +Ondřej Lengál Paul Guénézan Reuben Rowe Roei Nahum diff --git a/bin/autcross.cc b/bin/autcross.cc index 81b6bcef5..2aade5e49 100644 --- a/bin/autcross.cc +++ b/bin/autcross.cc @@ -1,5 +1,5 @@ // -*- 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). // // This file is part of Spot, a model checking library. @@ -64,6 +64,7 @@ Exit status:\n\ enum { OPT_BOGUS = 256, + OPT_COMPLEMENTED, OPT_CSV, OPT_HIGH, OPT_FAIL_ON_TIMEOUT, @@ -94,6 +95,8 @@ static const argp_option options[] = "consider timeouts as errors", 0 }, { "language-preserved", OPT_LANG, nullptr, 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, "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 no_checks = false; static bool opt_language_preserved = false; +static bool opt_language_complemented = false; static bool opt_omit = false; static const char* csv_output = nullptr; static unsigned round_num = 0; @@ -170,6 +174,9 @@ parse_opt(int key, char* arg, struct argp_state*) bogus_output_filename = arg; break; } + case OPT_COMPLEMENTED: + opt_language_complemented = true; + break; case OPT_CSV: csv_output = arg ? arg : "-"; break; @@ -533,25 +540,32 @@ namespace const spot::const_twa_graph_ptr& aut_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() > spot::acc_cond::mark_t::max_accsets()) { if (!quiet) - std::cerr << "info: building " << autname(i) - << '*' << autname(j, true) + std::cerr << "info: building " << autname(i, is_really_comp(i)) + << '*' << autname(j, true ^ is_really_comp(j)) << " requires more acceptance sets than supported\n"; return false; } if (verbose) 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); if (w) { 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" " "); example() << *w << '\n'; @@ -621,12 +635,15 @@ namespace int problems = 0; size_t m = tools.size(); - size_t mi = m + opt_language_preserved; + size_t mi = m + opt_language_preserved + opt_language_complemented; std::vector pos(mi); std::vector neg(mi); 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; 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 // A0 <= A1, A1 <= A2, ..., AN <= A0 unsigned ok = 0; @@ -824,10 +844,15 @@ main(int argc, char** argv) check_no_automaton(); - 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."); + if (s == 1 && !no_checks + && !opt_language_preserved + && !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_sig_handler(); diff --git a/doc/org/autcross.org b/doc/org/autcross.org index 90a268b44..9e4972cf6 100644 --- a/doc/org/autcross.org +++ b/doc/org/autcross.org @@ -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 | 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 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 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 :PROPERTIES: :CUSTOM_ID: checks diff --git a/tests/core/autcross.test b/tests/core/autcross.test index 2ac14eb34..b3d27ec0a 100755 --- a/tests/core/autcross.test +++ b/tests/core/autcross.test @@ -1,6 +1,6 @@ #!/bin/sh # -*- 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). # # 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 done 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 From dfb75632ba50f2243abb12147986fa73079a9db6 Mon Sep 17 00:00:00 2001 From: Philipp Schlehuber-Caissier Date: Thu, 24 Mar 2022 09:45:33 +0100 Subject: [PATCH 028/337] Update merge_states Current implementation of merge_states fails on certain self-loops. Updated implementation to take them into account and use a hashbased implementation to speed up calculations. Moreover, merge_states() is now aware of "state-player", just like defrag_states_ * spot/twa/twagraph.cc: Here * spot/twaalgos/game.cc: Fix odd cycle for sink * spot/twaalgos/synthesis.cc: Adapt split_det pipeline * tests/python/_synthesis.ipynb: Tests --- spot/twa/twagraph.cc | 413 ++- spot/twaalgos/game.cc | 11 +- spot/twaalgos/synthesis.cc | 7 +- tests/python/_synthesis.ipynb | 4541 ++++++++++++++++++++++++++++++++- 4 files changed, 4919 insertions(+), 53 deletions(-) diff --git a/spot/twa/twagraph.cc b/spot/twa/twagraph.cc index 051514550..b11ca12c5 100644 --- a/spot/twa/twagraph.cc +++ b/spot/twa/twagraph.cc @@ -21,13 +21,89 @@ #include #include #include +#include #include #include +#include #include #include using namespace std::string_literals; +namespace +{ + using namespace spot; + // If LAST is false, + // it is guaranteed that there will be another src state + template + void treat(std::vector>& e_idx, + const twa_graph::graph_t::edge_vector_t& e_vec, + std::vector& e_chain, + std::vector& use_for_hash, + unsigned& idx, + unsigned s, + unsigned n_e) + { + assert(s < e_idx.size()); + assert(idx < e_vec.size()); + assert(e_chain.size() == e_vec.size()); + + //std::cout << s << "; " << idx << std::endl; + + // Check if this state has outgoing transitions + if (s != e_vec[idx].src) + // Nothing to do + { + assert(!LAST); + return; + } + + auto& s_idx = e_idx[s]; + s_idx[0] = idx; + + // helper + unsigned sub_idx[] = {-1u, -1u}; + + // All transitions of this state + while (true) + { + assert(idx < e_vec.size() + LAST); + if constexpr (!LAST) + { + if (e_vec[idx].src != s) + break; + } + else + { + if (idx == n_e) + break; + } + + // Argh so many ifs + unsigned which = e_vec[idx].src == e_vec[idx].dst; + if (sub_idx[which] == -1u) + { + // First non-selflooping + sub_idx[which] = idx; + s_idx[1u+which] = idx; + } + else + { + // Continue the chained list + e_chain[sub_idx[which]] = idx; + sub_idx[which] = idx; + } + ++idx; + } + s_idx[3] = idx; + + // Check if self-loops appeared + // If so -> do not use for hash + if constexpr (!SPE) + use_for_hash[s] = s_idx[2] == -1u; + } +} + namespace spot { @@ -306,68 +382,287 @@ namespace spot return true; if (lhs.acc > rhs.acc) return false; + // compare with id? if (bdd_less_than_stable lt; lt(lhs.cond, rhs.cond)) return true; if (rhs.cond != lhs.cond) return false; - // The destination must be sorted last - // for our self-loop optimization to work. return lhs.dst < rhs.dst; }); g_.chain_edges_(); + const auto n_states = num_states(); + + // Edges are nicely chained and there are no erased edges + // -> We can work with the edge_vector + + // Check if it is a game <-> "state-player" is defined + // if so, the graph alternates between env and player vertices, + // so there are, by definition, no self-loops + auto sp = get_named_prop>("state-player"); + const auto spe = (bool) sp; + + // The hashing is a bit delicat: We may only use the dst + // if it has no self-loop + auto use_for_hash = spe ? std::vector() + : std::vector(n_states); + + const auto& e_vec = edge_vector(); + const auto n_edges = e_vec.size(); + + // For each state we need 4 indices of the edge vector + // [first, first_non_sfirst_selflooplfloop, first_selfloop, end] + // The init value makes sure nothing is done for dead end states + auto e_idx = + std::vector>(n_states, {-1u, -1u, + -1u, -1u}); + // Like a linked list holding the non-selfloop and selfloop transitions + auto e_chain = std::vector(e_vec.size(), -1u); + + unsigned idx = 1; + + // Edges are sorted with repected to src first + const unsigned n_high = e_vec.back().src; + if (spe) + for (auto s = 0u; s < n_high; ++s) + treat(e_idx, e_vec, e_chain, + use_for_hash, idx, s, n_edges); + else + for (auto s = 0u; s < n_high; ++s) + treat(e_idx, e_vec, e_chain, + use_for_hash, idx, s, n_edges); + // Last one + if (spe) + treat(e_idx, e_vec, e_chain, + use_for_hash, idx, n_high, n_edges); + else + treat(e_idx, e_vec, e_chain, + use_for_hash, idx, n_high, n_edges); + + assert(idx == e_vec.size() && "Something went wrong during indexing"); + + auto n_players = 0u; + if (sp) + n_players = std::accumulate(sp->begin(), sp->end(), 0u); + + // Represents which states share a hash + // Head is in the unordered_map, + // hash_linked_list is like a linked list structure + // of false pointers + + auto hash_linked_list = std::vector(n_states, -1u); + auto s_to_hash = std::vector(n_states, 0); + auto env_map = + robin_hood::unordered_flat_map>(); + auto player_map = + robin_hood::unordered_flat_map>(); + env_map.reserve(n_states - n_players); + player_map.reserve(n_players); + + // Sadly we need to loop the edges twice since we have + // to check for self-loops before hashing + + auto emplace = [&hash_linked_list](auto& m, auto h, auto s) + { + auto it = m.find(h); + if (it == m.end()) + m.emplace(h, std::make_pair(s, s)); + else + { + // "tail" + auto idx = it->second.second; + assert(idx < s && "Must be monotone"); + hash_linked_list[idx] = s; + it->second.second = s; + } + }; + + // Hash all states + constexpr auto SHIFT = sizeof(size_t)/2 * CHAR_BIT; + for (auto s = 0u; s != n_states; ++s) + { + auto h = fnv::init; + const auto e = e_idx[s][3]; + for (auto i = e_idx[s][0]; i != e; ++i) + { + // If size_t has 8byte and unsigned has 4byte + // then this works fine, otherwise there might be more collisions + size_t hh = spe || use_for_hash[e_vec[i].dst] + ? e_vec[i].dst + : fnv::init; + hh <<= SHIFT; + hh += e_vec[i].cond.id(); + h ^= hh; + h *= fnv::prime; + h ^= e_vec[i].acc.hash(); + h *= fnv::prime; + } + s_to_hash[s] = h; + if (spe && (*sp)[s]) + emplace(player_map, h, s); + else + emplace(env_map, h, s); + } + // All states that might possible be merged share the same hash + // Info hash coll + //std::cout << "Hash collission rate pre merge: " + // << ((env_map.size()+player_map.size())/((float)n_states)) + // << '\n'; + + // Check whether we can merge two states + // and takes into account the self-loops + auto state_equal = [&](unsigned s1, unsigned s2) + { + auto edge_data_comp = [](const auto& lhs, + const auto& rhs) + { + if (lhs.acc < rhs.acc) + return true; + if (lhs.acc > rhs.acc) + return false; + // todo compare with id + if (bdd_less_than_stable lt; lt(lhs.cond, rhs.cond)) + return true; + return false; + }; + + + static auto checked1 = std::vector(); + static auto checked2 = std::vector(); + + auto [i1, nsl1, sl1, e1] = e_idx[s1]; + auto [i2, nsl2, sl2, e2] = e_idx[s2]; + + if ((e2-i2) != (e1-i1)) + return false; // Different number of outgoing trans + + // checked1/2 is one element larger than necessary + // the last element is always false + // and acts like a nulltermination + checked1.resize(e1-i1+1); + std::fill(checked1.begin(), checked1.end(), false); + checked2.resize(e2-i2+1); + std::fill(checked2.begin(), checked2.end(), false); + + // Try to match self-loops + // Not entirely sure when this helps exactly + while ((sl1 < e1) & (sl2 < e2)) + { + // Like a search in ordered array + if (e_vec[sl1].data() == e_vec[sl2].data()) + { + // Matched + checked1[sl1 - i1] = true; //never touches last element + checked2[sl2 - i2] = true; + // Advance both + sl1 = e_chain[sl1]; + sl2 = e_chain[sl2]; + } + else if (edge_data_comp(e_vec[sl1].data(), + e_vec[sl2].data())) + // sl1 needs to advance + sl1 = e_chain[sl1]; + else + // sl2 needs to advance + sl2 = e_chain[sl2]; + } + + // If there are no non-self-loops, in s1 + // Check if all have been correctly treated + if ((nsl1 > e1) + && std::all_of(checked1.begin(), checked1.end(), + [](const auto& e){return e; })) + return true; + + // The remaining edges need to match exactly + auto idx1 = i1; + auto idx2 = i2; + while (((idx1 < e1) & (idx2 < e2))) + { + // More efficient version? + // Skip checked edges + // Last element serves as break + for (; checked1[idx1 - i1]; ++idx1) + { + } + for (; checked2[idx2 - i2]; ++idx2) + { + } + // If one is out of bounds, so is the other + if (idx1 == e1) + { + assert(idx2 == e2); + break; + } + + + if ((e_vec[idx1].dst != e_vec[idx2].dst) + || !(e_vec[idx1].data() == e_vec[idx2].data())) + return false; + + // Advance + ++idx1; + ++idx2; + } + // All edges have bee paired + return true; + }; + const unsigned nb_states = num_states(); std::vector remap(nb_states, -1U); + for (unsigned i = 0; i != nb_states; ++i) { - auto out1 = out(i); - for (unsigned j = 0; j != i; ++j) + auto j = spe && (*sp)[i] ? player_map.at(s_to_hash[i]).first + : env_map.at(s_to_hash[i]).first; + for (; jat(e.dst) == sp->at(remap[e.dst]))) + && "States do not have the same owner"); + e.dst = remap[e.dst]; + } + if (remap[get_init_state_number()] != -1U) set_init_state(remap[get_init_state_number()]); @@ -382,6 +677,10 @@ namespace spot unsigned merged = num_states() - st; if (merged) defrag_states(remap, st); + // Info hash coll 2 + //std::cout << "Hash collission rate post merge: " + // << ((env_map.size()+player_map.size())/((float)num_states())) + // << '\n'; return merged; } @@ -942,8 +1241,36 @@ namespace spot s = newst[s]; } } + // Reassign the state-players + if (auto sp = get_named_prop>("state-player")) + { + const auto ns = (unsigned) used_states; + const auto sps = (unsigned) sp->size(); + assert(ns <= sps); + assert(sps == newst.size()); + + for (unsigned i = 0; i < sps; ++i) + { + if (newst[i] == -1u) + continue; + (*sp)[newst[i]] = (*sp)[i]; + } + sp->resize(ns); + } init_number_ = newst[init_number_]; g_.defrag_states(newst, used_states); + // Make sure we did not mess up the structure + assert([&]() + { + if (auto sp = get_named_prop>("state-player")) + { + for (const auto& e : edges()) + if (sp->at(e.src) == sp->at(e.dst)) + return false; + return true; + } + return true; + }() && "Game not alternating!"); } void twa_graph::remove_unused_ap() diff --git a/spot/twaalgos/game.cc b/spot/twaalgos/game.cc index 6bb62500d..6d319eea8 100644 --- a/spot/twaalgos/game.cc +++ b/spot/twaalgos/game.cc @@ -896,10 +896,19 @@ namespace spot arena->new_edge(sink_con, sink_env, bddtrue, um.second); arena->new_edge(sink_env, sink_con, bddtrue, um.second); } - arena->new_edge(src, sink_con, missing, um.second); + arena->new_edge(src, sink_env, missing, um.second); + assert(owner->at(src) != owner->at(sink_env)); } } + assert([&]() + { + for (const auto& e : arena->edges()) + if (owner->at(e.src) == owner->at(e.dst)) + return false; + return true; + }() && "Not alternating"); + arena->set_named_prop("state-player", owner); } diff --git a/spot/twaalgos/synthesis.cc b/spot/twaalgos/synthesis.cc index dc79f8cce..1708923c6 100644 --- a/spot/twaalgos/synthesis.cc +++ b/spot/twaalgos/synthesis.cc @@ -958,6 +958,10 @@ namespace spot *vs << "determinization done\nDPA has " << dpa->num_states() << " states, " << dpa->num_sets() << " colors\n"; + // The named property "state-player" is set in split_2step + // but not propagated by ntgba2dpa + alternate_players(dpa); + // Merge states knows about players dpa->merge_states(); if (bv) bv->paritize_time += sw.stop(); @@ -966,9 +970,6 @@ namespace spot << dpa->num_states() << " states\n" << "determinization and simplification took " << bv->paritize_time << " seconds\n"; - // The named property "state-player" is set in split_2step - // but not propagated by ntgba2dpa - alternate_players(dpa); break; } case algo::ACD: diff --git a/tests/python/_synthesis.ipynb b/tests/python/_synthesis.ipynb index 5866057a1..b9e065c18 100644 --- a/tests/python/_synthesis.ipynb +++ b/tests/python/_synthesis.ipynb @@ -737,7 +737,7 @@ "\n" ], "text/plain": [ - " *' at 0x7f7458055570> >" + " *' at 0x7fcc883a7720> >" ] }, "execution_count": 8, @@ -820,7 +820,7 @@ "\n" ], "text/plain": [ - " *' at 0x7f7458055570> >" + " *' at 0x7fcc883a7720> >" ] }, "execution_count": 9, @@ -944,7 +944,7 @@ "\n" ], "text/plain": [ - " *' at 0x7f74580553c0> >" + " *' at 0x7fcc8833aa80> >" ] }, "execution_count": 10, @@ -1042,7 +1042,7 @@ "\n" ], "text/plain": [ - " *' at 0x7f743a5ca6c0> >" + " *' at 0x7fcc880c2ab0> >" ] }, "execution_count": 11, @@ -1210,7 +1210,7 @@ "\n" ], "text/plain": [ - " *' at 0x7f7458059f90> >" + " *' at 0x7fcc8833ae70> >" ] }, "execution_count": 12, @@ -1419,7 +1419,7 @@ "\n" ], "text/plain": [ - " *' at 0x7f7458055870> >" + " *' at 0x7fcc880c2240> >" ] }, "execution_count": 13, @@ -1432,6 +1432,4535 @@ "print(a_s.acc())\n", "a_s" ] + }, + { + "cell_type": "markdown", + "id": "0ee90b2a", + "metadata": {}, + "source": [ + "## A problematic case for merge\n", + "\n", + "This is an example graph for which the self-loop optimisation in merge_states does not work" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "06b20a8c", + "metadata": {}, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "Inf(\n", + "\n", + ")\n", + "[Büchi]\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "1\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "0->1\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "4\n", + "\n", + "4\n", + "\n", + "\n", + "\n", + "0->4\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "1->1\n", + "\n", + "\n", + "a\n", + "\n", + "\n", + "\n", + "2\n", + "\n", + "2\n", + "\n", + "\n", + "\n", + "1->2\n", + "\n", + "\n", + "b\n", + "\n", + "\n", + "\n", + "3\n", + "\n", + "3\n", + "\n", + "\n", + "\n", + "1->3\n", + "\n", + "\n", + "c\n", + "\n", + "\n", + "\n", + "4->4\n", + "\n", + "\n", + "a\n", + "\n", + "\n", + "\n", + "4->2\n", + "\n", + "\n", + "b\n", + "\n", + "\n", + "\n", + "4->3\n", + "\n", + "\n", + "c\n", + "\n", + "\n", + "\n", + "2->2\n", + "\n", + "\n", + "x\n", + "\n", + "\n", + "\n", + "3->3\n", + "\n", + "\n", + "!x\n", + "\n", + "\n", + "\n" + ], + "text/plain": [ + " *' at 0x7fcc880bdc30> >" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "aut = spot.make_twa_graph()\n", + "aut.set_buchi()\n", + "aut.new_states(5)\n", + "\n", + "a = buddy.bdd_ithvar(aut.register_ap(\"a\"))\n", + "b = buddy.bdd_ithvar(aut.register_ap(\"b\"))\n", + "c = buddy.bdd_ithvar(aut.register_ap(\"c\"))\n", + "x = buddy.bdd_ithvar(aut.register_ap(\"x\"))\n", + "\n", + "\n", + "aut.new_edge(0, 1, buddy.bddtrue)\n", + "aut.new_edge(0, 4, buddy.bddtrue)\n", + "\n", + "# OK, edge conditions ensure \"correct\" ordering\n", + "aut.new_edge(1, 1, a)\n", + "aut.new_edge(1, 2, b)\n", + "aut.new_edge(1, 3, c)\n", + "\n", + "aut.new_edge(4, 4, a)\n", + "aut.new_edge(4, 2, b)\n", + "aut.new_edge(4, 3, c)\n", + "\n", + "aut.new_edge(2, 2, x)\n", + "aut.new_edge(3, 3, buddy.bdd_not(x))\n", + "\n", + "aut" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "8a2f2e4d", + "metadata": {}, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "Inf(\n", + "\n", + ")\n", + "[Büchi]\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "1\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "0->1\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "0->1\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "1->1\n", + "\n", + "\n", + "a\n", + "\n", + "\n", + "\n", + "3\n", + "\n", + "3\n", + "\n", + "\n", + "\n", + "1->3\n", + "\n", + "\n", + "c\n", + "\n", + "\n", + "\n", + "2\n", + "\n", + "2\n", + "\n", + "\n", + "\n", + "1->2\n", + "\n", + "\n", + "b\n", + "\n", + "\n", + "\n", + "3->3\n", + "\n", + "\n", + "!x\n", + "\n", + "\n", + "\n", + "2->2\n", + "\n", + "\n", + "x\n", + "\n", + "\n", + "\n" + ], + "text/plain": [ + " *' at 0x7fcc880bdc30> >" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "aut.merge_states()\n", + "aut" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "b40f8ce7", + "metadata": {}, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "Inf(\n", + "\n", + ")\n", + "[Büchi]\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "1\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "0->1\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "4\n", + "\n", + "4\n", + "\n", + "\n", + "\n", + "0->4\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "1->1\n", + "\n", + "\n", + "a\n", + "\n", + "\n", + "\n", + "2\n", + "\n", + "2\n", + "\n", + "\n", + "\n", + "1->2\n", + "\n", + "\n", + "a\n", + "\n", + "\n", + "\n", + "3\n", + "\n", + "3\n", + "\n", + "\n", + "\n", + "1->3\n", + "\n", + "\n", + "a\n", + "\n", + "\n", + "\n", + "4->4\n", + "\n", + "\n", + "a\n", + "\n", + "\n", + "\n", + "4->2\n", + "\n", + "\n", + "a\n", + "\n", + "\n", + "\n", + "4->3\n", + "\n", + "\n", + "a\n", + "\n", + "\n", + "\n", + "2->2\n", + "\n", + "\n", + "x\n", + "\n", + "\n", + "\n", + "3->3\n", + "\n", + "\n", + "!x\n", + "\n", + "\n", + "\n" + ], + "text/plain": [ + " *' at 0x7fcc880c5210> >" + ] + }, + "execution_count": 16, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "aut = spot.make_twa_graph()\n", + "aut.set_buchi()\n", + "aut.new_states(5)\n", + "\n", + "a = buddy.bdd_ithvar(aut.register_ap(\"a\"))\n", + "b = buddy.bdd_ithvar(aut.register_ap(\"b\"))\n", + "c = buddy.bdd_ithvar(aut.register_ap(\"c\"))\n", + "x = buddy.bdd_ithvar(aut.register_ap(\"x\"))\n", + "\n", + "\n", + "aut.new_edge(0, 1, buddy.bddtrue)\n", + "aut.new_edge(0, 4, buddy.bddtrue)\n", + "\n", + "# Not OK, all edge equal -> sorted by destination\n", + "# Fails to merge\n", + "aut.new_edge(1, 1, a)\n", + "aut.new_edge(1, 2, a)\n", + "aut.new_edge(1, 3, a)\n", + "\n", + "aut.new_edge(4, 4, a)\n", + "aut.new_edge(4, 2, a)\n", + "aut.new_edge(4, 3, a)\n", + "\n", + "aut.new_edge(2, 2, x)\n", + "aut.new_edge(3, 3, buddy.bdd_not(x))\n", + "\n", + "aut" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "1f596284", + "metadata": {}, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "Inf(\n", + "\n", + ")\n", + "[Büchi]\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "1\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "0->1\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "0->1\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "1->1\n", + "\n", + "\n", + "a\n", + "\n", + "\n", + "\n", + "2\n", + "\n", + "2\n", + "\n", + "\n", + "\n", + "1->2\n", + "\n", + "\n", + "a\n", + "\n", + "\n", + "\n", + "3\n", + "\n", + "3\n", + "\n", + "\n", + "\n", + "1->3\n", + "\n", + "\n", + "a\n", + "\n", + "\n", + "\n", + "2->2\n", + "\n", + "\n", + "x\n", + "\n", + "\n", + "\n", + "3->3\n", + "\n", + "\n", + "!x\n", + "\n", + "\n", + "\n" + ], + "text/plain": [ + " *' at 0x7fcc880c5210> >" + ] + }, + "execution_count": 17, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "aut.merge_states()\n", + "aut" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "761b4c96", + "metadata": {}, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "g\n", + "\n", + "\n", + "\n", + "states\n", + "\n", + "\n", + "states\n", + "\n", + "\n", + "0\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "2\n", + "\n", + "\n", + "3\n", + "\n", + "\n", + "4\n", + "\n", + "\n", + "5\n", + "\n", + "\n", + "6\n", + "\n", + "\n", + "7\n", + "\n", + "succ\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "3\n", + "\n", + "\n", + "9\n", + "\n", + "\n", + "10\n", + "\n", + "\n", + "6\n", + "\n", + "\n", + "11\n", + "\n", + "0\n", + "\n", + "0\n", + "\n", + "succ_tail\n", + "\n", + "\n", + "2\n", + "\n", + "\n", + "5\n", + "\n", + "\n", + "9\n", + "\n", + "\n", + "10\n", + "\n", + "\n", + "8\n", + "\n", + "\n", + "13\n", + "\n", + "0\n", + "\n", + "0\n", + "\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "2\n", + "\n", + "\n", + "3\n", + "\n", + "\n", + "4\n", + "\n", + "\n", + "5\n", + "\n", + "\n", + "6\n", + "\n", + "\n", + "7\n", + "\n", + "\n", + "8\n", + "\n", + "\n", + "9\n", + "\n", + "\n", + "10\n", + "\n", + "\n", + "11\n", + "\n", + "\n", + "12\n", + "\n", + "\n", + "13\n", + "\n", + "cond\n", + "\n", + "1\n", + "\n", + "1\n", + "\n", + "a\n", + "\n", + "b\n", + "\n", + "c\n", + "\n", + "a\n", + "\n", + "b\n", + "\n", + "c\n", + "\n", + "x\n", + "\n", + "!x\n", + "\n", + "a\n", + "\n", + "b\n", + "\n", + "c\n", + "\n", + "acc\n", + "\n", + "{}\n", + "\n", + "{}\n", + "\n", + "{}\n", + "\n", + "{}\n", + "\n", + "{}\n", + "\n", + "{}\n", + "\n", + "{}\n", + "\n", + "{}\n", + "\n", + "{}\n", + "\n", + "{}\n", + "\n", + "{}\n", + "\n", + "{}\n", + "\n", + "{}\n", + "\n", + "dst\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "4\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "2\n", + "\n", + "\n", + "3\n", + "\n", + "\n", + "4\n", + "\n", + "\n", + "2\n", + "\n", + "\n", + "3\n", + "\n", + "\n", + "2\n", + "\n", + "\n", + "3\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "2\n", + "\n", + "\n", + "3\n", + "\n", + "next_succ\n", + "\n", + "\n", + "2\n", + "\n", + "0\n", + "\n", + "\n", + "4\n", + "\n", + "\n", + "5\n", + "\n", + "0\n", + "\n", + "\n", + "7\n", + "\n", + "\n", + "8\n", + "\n", + "0\n", + "\n", + "0\n", + "\n", + "0\n", + "\n", + "\n", + "12\n", + "\n", + "\n", + "13\n", + "\n", + "0\n", + "\n", + "src\n", + "\n", + "\n", + "0\n", + "\n", + "\n", + "0\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "4\n", + "\n", + "\n", + "4\n", + "\n", + "\n", + "4\n", + "\n", + "\n", + "2\n", + "\n", + "\n", + "3\n", + "\n", + "\n", + "5\n", + "\n", + "\n", + "5\n", + "\n", + "\n", + "5\n", + "\n", + "\n", + "\n", + "meta\n", + "init_state:\n", + "\n", + "0\n", + "num_sets:\n", + "1\n", + "acceptance:\n", + "Inf(0)\n", + "ap_vars:\n", + "a b c x\n", + "\n", + "\n", + "\n", + "\n", + "props\n", + "prop_state_acc:\n", + "maybe\n", + "prop_inherently_weak:\n", + "maybe\n", + "prop_terminal:\n", + "maybe\n", + "prop_weak:\n", + "maybe\n", + "prop_very_weak:\n", + "maybe\n", + "prop_complete:\n", + "maybe\n", + "prop_universal:\n", + "maybe\n", + "prop_unambiguous:\n", + "maybe\n", + "prop_semi_deterministic:\n", + "maybe\n", + "prop_stutter_invariant:\n", + "maybe\n", + "\n", + "\n", + "\n", + "\n" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "Inf(\n", + "\n", + ")\n", + "[Büchi]\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "1\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "0->1\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "4\n", + "\n", + "4\n", + "\n", + "\n", + "\n", + "0->4\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "1->1\n", + "\n", + "\n", + "a\n", + "\n", + "\n", + "\n", + "2\n", + "\n", + "2\n", + "\n", + "\n", + "\n", + "1->2\n", + "\n", + "\n", + "b\n", + "\n", + "\n", + "\n", + "3\n", + "\n", + "3\n", + "\n", + "\n", + "\n", + "1->3\n", + "\n", + "\n", + "c\n", + "\n", + "\n", + "\n", + "4->4\n", + "\n", + "\n", + "a\n", + "\n", + "\n", + "\n", + "4->2\n", + "\n", + "\n", + "b\n", + "\n", + "\n", + "\n", + "4->3\n", + "\n", + "\n", + "c\n", + "\n", + "\n", + "\n", + "2->2\n", + "\n", + "\n", + "x\n", + "\n", + "\n", + "\n", + "3->3\n", + "\n", + "\n", + "!x\n", + "\n", + "\n", + "\n", + "5\n", + "\n", + "5\n", + "\n", + "\n", + "\n", + "5->1\n", + "\n", + "\n", + "a\n", + "\n", + "\n", + "\n", + "5->2\n", + "\n", + "\n", + "b\n", + "\n", + "\n", + "\n", + "5->3\n", + "\n", + "\n", + "c\n", + "\n", + "\n", + "\n", + "6\n", + "\n", + "6\n", + "\n", + "\n", + "\n", + "7\n", + "\n", + "7\n", + "\n", + "\n", + "\n" + ], + "text/plain": [ + " *' at 0x7fcc880c5d50> >" + ] + }, + "execution_count": 18, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "aut = spot.make_twa_graph()\n", + "aut.set_buchi()\n", + "aut.new_states(8)\n", + "\n", + "a = buddy.bdd_ithvar(aut.register_ap(\"a\"))\n", + "b = buddy.bdd_ithvar(aut.register_ap(\"b\"))\n", + "c = buddy.bdd_ithvar(aut.register_ap(\"c\"))\n", + "x = buddy.bdd_ithvar(aut.register_ap(\"x\"))\n", + "\n", + "\n", + "aut.new_edge(0, 1, buddy.bddtrue)\n", + "aut.new_edge(0, 4, buddy.bddtrue)\n", + "\n", + "aut.new_edge(1, 1, a)\n", + "aut.new_edge(1, 2, b)\n", + "aut.new_edge(1, 3, c)\n", + "\n", + "aut.new_edge(4, 4, a)\n", + "aut.new_edge(4, 2, b)\n", + "aut.new_edge(4, 3, c)\n", + "\n", + "aut.new_edge(2, 2, x)\n", + "aut.new_edge(3, 3, buddy.bdd_not(x))\n", + "\n", + "aut.new_edge(5, 1, a)\n", + "aut.new_edge(5, 2, b)\n", + "aut.new_edge(5, 3, c)\n", + "\n", + "display(aut.show_storage())\n", + "aut" + ] + }, + { + "cell_type": "code", + "execution_count": 19, + "id": "d4e09261", + "metadata": {}, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "Inf(\n", + "\n", + ")\n", + "[Büchi]\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "1\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "0->1\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "0->1\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "1->1\n", + "\n", + "\n", + "a\n", + "\n", + "\n", + "\n", + "3\n", + "\n", + "3\n", + "\n", + "\n", + "\n", + "1->3\n", + "\n", + "\n", + "c\n", + "\n", + "\n", + "\n", + "2\n", + "\n", + "2\n", + "\n", + "\n", + "\n", + "1->2\n", + "\n", + "\n", + "b\n", + "\n", + "\n", + "\n", + "3->3\n", + "\n", + "\n", + "!x\n", + "\n", + "\n", + "\n", + "2->2\n", + "\n", + "\n", + "x\n", + "\n", + "\n", + "\n", + "4\n", + "\n", + "4\n", + "\n", + "\n", + "\n" + ], + "text/plain": [ + " *' at 0x7fcc880c5d50> >" + ] + }, + "execution_count": 19, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "aut.merge_states()\n", + "aut" + ] + }, + { + "cell_type": "markdown", + "id": "4a8ace82", + "metadata": {}, + "source": [ + "## Splitting can inhibit merging\n", + "\n", + "In split automata, no self-loops exist.\n", + "Therefore states that can be merged pre-split can not be merged in a split automaton" + ] + }, + { + "cell_type": "code", + "execution_count": 20, + "id": "c9e38db9", + "metadata": {}, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "Inf(\n", + "\n", + ")\n", + "[Büchi]\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "1\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "0->1\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "4\n", + "\n", + "4\n", + "\n", + "\n", + "\n", + "0->4\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "1->1\n", + "\n", + "\n", + "a\n", + "\n", + "\n", + "\n", + "2\n", + "\n", + "2\n", + "\n", + "\n", + "\n", + "1->2\n", + "\n", + "\n", + "b\n", + "\n", + "\n", + "\n", + "3\n", + "\n", + "3\n", + "\n", + "\n", + "\n", + "1->3\n", + "\n", + "\n", + "c\n", + "\n", + "\n", + "\n", + "4->4\n", + "\n", + "\n", + "a\n", + "\n", + "\n", + "\n", + "4->2\n", + "\n", + "\n", + "b\n", + "\n", + "\n", + "\n", + "4->3\n", + "\n", + "\n", + "c\n", + "\n", + "\n", + "\n", + "2->2\n", + "\n", + "\n", + "x\n", + "\n", + "\n", + "\n", + "3->3\n", + "\n", + "\n", + "!x\n", + "\n", + "\n", + "\n" + ], + "text/plain": [ + " *' at 0x7fcc880cd090> >" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "g\n", + "\n", + "\n", + "\n", + "states\n", + "\n", + "\n", + "states\n", + "\n", + "\n", + "0\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "2\n", + "\n", + "\n", + "3\n", + "\n", + "\n", + "4\n", + "\n", + "\n", + "5\n", + "\n", + "\n", + "6\n", + "\n", + "\n", + "7\n", + "\n", + "\n", + "8\n", + "\n", + "\n", + "9\n", + "\n", + "\n", + "10\n", + "\n", + "\n", + "11\n", + "\n", + "\n", + "12\n", + "\n", + "\n", + "13\n", + "\n", + "\n", + "14\n", + "\n", + "\n", + "15\n", + "\n", + "\n", + "16\n", + "\n", + "\n", + "17\n", + "\n", + "\n", + "18\n", + "\n", + "succ\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "2\n", + "\n", + "\n", + "9\n", + "\n", + "\n", + "10\n", + "\n", + "\n", + "11\n", + "\n", + "\n", + "18\n", + "\n", + "\n", + "20\n", + "\n", + "\n", + "21\n", + "\n", + "\n", + "22\n", + "\n", + "\n", + "24\n", + "\n", + "\n", + "25\n", + "\n", + "\n", + "27\n", + "\n", + "\n", + "29\n", + "\n", + "\n", + "32\n", + "\n", + "\n", + "33\n", + "\n", + "\n", + "34\n", + "\n", + "\n", + "35\n", + "\n", + "\n", + "37\n", + "\n", + "\n", + "39\n", + "\n", + "succ_tail\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "8\n", + "\n", + "\n", + "9\n", + "\n", + "\n", + "10\n", + "\n", + "\n", + "17\n", + "\n", + "\n", + "19\n", + "\n", + "\n", + "20\n", + "\n", + "\n", + "21\n", + "\n", + "\n", + "23\n", + "\n", + "\n", + "24\n", + "\n", + "\n", + "26\n", + "\n", + "\n", + "28\n", + "\n", + "\n", + "31\n", + "\n", + "\n", + "32\n", + "\n", + "\n", + "33\n", + "\n", + "\n", + "34\n", + "\n", + "\n", + "36\n", + "\n", + "\n", + "38\n", + "\n", + "\n", + "41\n", + "\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "2\n", + "\n", + "\n", + "3\n", + "\n", + "\n", + "4\n", + "\n", + "\n", + "5\n", + "\n", + "\n", + "6\n", + "\n", + "\n", + "7\n", + "\n", + "\n", + "8\n", + "\n", + "\n", + "9\n", + "\n", + "\n", + "10\n", + "\n", + "\n", + "11\n", + "\n", + "\n", + "12\n", + "\n", + "\n", + "13\n", + "\n", + "\n", + "14\n", + "\n", + "\n", + "15\n", + "\n", + "\n", + "16\n", + "\n", + "\n", + "17\n", + "\n", + "\n", + "18\n", + "\n", + "\n", + "19\n", + "\n", + "\n", + "20\n", + "\n", + "\n", + "21\n", + "\n", + "\n", + "22\n", + "\n", + "\n", + "23\n", + "\n", + "\n", + "24\n", + "\n", + "\n", + "25\n", + "\n", + "\n", + "26\n", + "\n", + "\n", + "27\n", + "\n", + "\n", + "28\n", + "\n", + "\n", + "29\n", + "\n", + "\n", + "30\n", + "\n", + "\n", + "31\n", + "\n", + "\n", + "32\n", + "\n", + "\n", + "33\n", + "\n", + "\n", + "34\n", + "\n", + "\n", + "35\n", + "\n", + "\n", + "36\n", + "\n", + "\n", + "37\n", + "\n", + "\n", + "38\n", + "\n", + "\n", + "39\n", + "\n", + "\n", + "40\n", + "\n", + "\n", + "41\n", + "\n", + "cond\n", + "\n", + "1\n", + "\n", + "!a & !b & c\n", + "\n", + "!a & b & !c\n", + "\n", + "!a & b & c\n", + "\n", + "a & !b & !c\n", + "\n", + "a & !b & c\n", + "\n", + "a & b & !c\n", + "\n", + "a & b & c\n", + "\n", + "1\n", + "\n", + "1\n", + "\n", + "!a & !b & c\n", + "\n", + "!a & b & !c\n", + "\n", + "!a & b & c\n", + "\n", + "a & !b & !c\n", + "\n", + "a & !b & c\n", + "\n", + "a & b & !c\n", + "\n", + "a & b & c\n", + "\n", + "1\n", + "\n", + "1\n", + "\n", + "1\n", + "\n", + "1\n", + "\n", + "1\n", + "\n", + "1\n", + "\n", + "1\n", + "\n", + "1\n", + "\n", + "1\n", + "\n", + "1\n", + "\n", + "1\n", + "\n", + "1\n", + "\n", + "1\n", + "\n", + "1\n", + "\n", + "x\n", + "\n", + "!x\n", + "\n", + "1\n", + "\n", + "1\n", + "\n", + "1\n", + "\n", + "1\n", + "\n", + "1\n", + "\n", + "1\n", + "\n", + "1\n", + "\n", + "1\n", + "\n", + "acc\n", + "\n", + "{}\n", + "\n", + "{}\n", + "\n", + "{}\n", + "\n", + "{}\n", + "\n", + "{}\n", + "\n", + "{}\n", + "\n", + "{}\n", + "\n", + "{}\n", + "\n", + "{}\n", + "\n", + "{}\n", + "\n", + "{}\n", + "\n", + "{}\n", + "\n", + "{}\n", + "\n", + "{}\n", + "\n", + "{}\n", + "\n", + "{}\n", + "\n", + "{}\n", + "\n", + "{}\n", + "\n", + "{}\n", + "\n", + "{}\n", + "\n", + "{}\n", + "\n", + "{}\n", + "\n", + "{}\n", + "\n", + "{}\n", + "\n", + "{}\n", + "\n", + "{}\n", + "\n", + "{}\n", + "\n", + "{}\n", + "\n", + "{}\n", + "\n", + "{}\n", + "\n", + "{}\n", + "\n", + "{}\n", + "\n", + "{}\n", + "\n", + "{}\n", + "\n", + "{}\n", + "\n", + "{}\n", + "\n", + "{}\n", + "\n", + "{}\n", + "\n", + "{}\n", + "\n", + "{}\n", + "\n", + "{}\n", + "\n", + "dst\n", + "\n", + "\n", + "5\n", + "\n", + "\n", + "6\n", + "\n", + "\n", + "7\n", + "\n", + "\n", + "8\n", + "\n", + "\n", + "9\n", + "\n", + "\n", + "10\n", + "\n", + "\n", + "11\n", + "\n", + "\n", + "12\n", + "\n", + "\n", + "13\n", + "\n", + "\n", + "14\n", + "\n", + "\n", + "6\n", + "\n", + "\n", + "7\n", + "\n", + "\n", + "8\n", + "\n", + "\n", + "15\n", + "\n", + "\n", + "16\n", + "\n", + "\n", + "17\n", + "\n", + "\n", + "18\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "4\n", + "\n", + "\n", + "3\n", + "\n", + "\n", + "2\n", + "\n", + "\n", + "2\n", + "\n", + "\n", + "3\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "3\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "2\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "2\n", + "\n", + "\n", + "3\n", + "\n", + "\n", + "2\n", + "\n", + "\n", + "3\n", + "\n", + "\n", + "4\n", + "\n", + "\n", + "3\n", + "\n", + "\n", + "4\n", + "\n", + "\n", + "2\n", + "\n", + "\n", + "4\n", + "\n", + "\n", + "2\n", + "\n", + "\n", + "3\n", + "\n", + "\n", + "4\n", + "\n", + "next_succ\n", + "\n", + "0\n", + "\n", + "\n", + "3\n", + "\n", + "\n", + "4\n", + "\n", + "\n", + "5\n", + "\n", + "\n", + "6\n", + "\n", + "\n", + "7\n", + "\n", + "\n", + "8\n", + "\n", + "0\n", + "\n", + "0\n", + "\n", + "0\n", + "\n", + "\n", + "12\n", + "\n", + "\n", + "13\n", + "\n", + "\n", + "14\n", + "\n", + "\n", + "15\n", + "\n", + "\n", + "16\n", + "\n", + "\n", + "17\n", + "\n", + "0\n", + "\n", + "\n", + "19\n", + "\n", + "0\n", + "\n", + "0\n", + "\n", + "0\n", + "\n", + "\n", + "23\n", + "\n", + "0\n", + "\n", + "0\n", + "\n", + "\n", + "26\n", + "\n", + "0\n", + "\n", + "\n", + "28\n", + "\n", + "0\n", + "\n", + "\n", + "30\n", + "\n", + "\n", + "31\n", + "\n", + "0\n", + "\n", + "0\n", + "\n", + "0\n", + "\n", + "0\n", + "\n", + "\n", + "36\n", + "\n", + "0\n", + "\n", + "\n", + "38\n", + "\n", + "0\n", + "\n", + "\n", + "40\n", + "\n", + "\n", + "41\n", + "\n", + "0\n", + "\n", + "src\n", + "\n", + "\n", + "0\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "2\n", + "\n", + "\n", + "3\n", + "\n", + "\n", + "4\n", + "\n", + "\n", + "4\n", + "\n", + "\n", + "4\n", + "\n", + "\n", + "4\n", + "\n", + "\n", + "4\n", + "\n", + "\n", + "4\n", + "\n", + "\n", + "4\n", + "\n", + "\n", + "5\n", + "\n", + "\n", + "5\n", + "\n", + "\n", + "6\n", + "\n", + "\n", + "7\n", + "\n", + "\n", + "8\n", + "\n", + "\n", + "8\n", + "\n", + "\n", + "9\n", + "\n", + "\n", + "10\n", + "\n", + "\n", + "10\n", + "\n", + "\n", + "11\n", + "\n", + "\n", + "11\n", + "\n", + "\n", + "12\n", + "\n", + "\n", + "12\n", + "\n", + "\n", + "12\n", + "\n", + "\n", + "13\n", + "\n", + "\n", + "14\n", + "\n", + "\n", + "15\n", + "\n", + "\n", + "16\n", + "\n", + "\n", + "16\n", + "\n", + "\n", + "17\n", + "\n", + "\n", + "17\n", + "\n", + "\n", + "18\n", + "\n", + "\n", + "18\n", + "\n", + "\n", + "18\n", + "\n", + "\n", + "\n", + "meta\n", + "init_state:\n", + "\n", + "0\n", + "num_sets:\n", + "1\n", + "acceptance:\n", + "Inf(0)\n", + "ap_vars:\n", + "a b c x\n", + "\n", + "\n", + "\n", + "\n", + "props\n", + "prop_state_acc:\n", + "maybe\n", + "prop_inherently_weak:\n", + "maybe\n", + "prop_terminal:\n", + "maybe\n", + "prop_weak:\n", + "maybe\n", + "prop_very_weak:\n", + "maybe\n", + "prop_complete:\n", + "maybe\n", + "prop_universal:\n", + "maybe\n", + "prop_unambiguous:\n", + "maybe\n", + "prop_semi_deterministic:\n", + "maybe\n", + "prop_stutter_invariant:\n", + "maybe\n", + "\n", + "\n", + "\n", + "\n", + "namedprops\n", + "named properties:\n", + "state-player\n", + "synthesis-outputs\n", + "\n", + "\n", + "\n", + "\n" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "Inf(\n", + "\n", + ")\n", + "[Büchi]\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "5\n", + "\n", + "5\n", + "\n", + "\n", + "\n", + "0->5\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "1\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "5->1\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "4\n", + "\n", + "4\n", + "\n", + "\n", + "\n", + "5->4\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "6\n", + "\n", + "6\n", + "\n", + "\n", + "\n", + "1->6\n", + "\n", + "\n", + "!a & !b & c\n", + "\n", + "\n", + "\n", + "7\n", + "\n", + "7\n", + "\n", + "\n", + "\n", + "1->7\n", + "\n", + "\n", + "!a & b & !c\n", + "\n", + "\n", + "\n", + "8\n", + "\n", + "8\n", + "\n", + "\n", + "\n", + "1->8\n", + "\n", + "\n", + "!a & b & c\n", + "\n", + "\n", + "\n", + "9\n", + "\n", + "9\n", + "\n", + "\n", + "\n", + "1->9\n", + "\n", + "\n", + "a & !b & !c\n", + "\n", + "\n", + "\n", + "10\n", + "\n", + "10\n", + "\n", + "\n", + "\n", + "1->10\n", + "\n", + "\n", + "a & !b & c\n", + "\n", + "\n", + "\n", + "11\n", + "\n", + "11\n", + "\n", + "\n", + "\n", + "1->11\n", + "\n", + "\n", + "a & b & !c\n", + "\n", + "\n", + "\n", + "12\n", + "\n", + "12\n", + "\n", + "\n", + "\n", + "1->12\n", + "\n", + "\n", + "a & b & c\n", + "\n", + "\n", + "\n", + "3\n", + "\n", + "3\n", + "\n", + "\n", + "\n", + "6->3\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "2\n", + "\n", + "2\n", + "\n", + "\n", + "\n", + "7->2\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "8->2\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "8->3\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "9->1\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "10->1\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "10->3\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "11->1\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "11->2\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "12->1\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "12->2\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "12->3\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "13\n", + "\n", + "13\n", + "\n", + "\n", + "\n", + "2->13\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "13->2\n", + "\n", + "\n", + "x\n", + "\n", + "\n", + "\n", + "14\n", + "\n", + "14\n", + "\n", + "\n", + "\n", + "3->14\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "14->3\n", + "\n", + "\n", + "!x\n", + "\n", + "\n", + "\n", + "4->6\n", + "\n", + "\n", + "!a & !b & c\n", + "\n", + "\n", + "\n", + "4->7\n", + "\n", + "\n", + "!a & b & !c\n", + "\n", + "\n", + "\n", + "4->8\n", + "\n", + "\n", + "!a & b & c\n", + "\n", + "\n", + "\n", + "15\n", + "\n", + "15\n", + "\n", + "\n", + "\n", + "4->15\n", + "\n", + "\n", + "a & !b & !c\n", + "\n", + "\n", + "\n", + "16\n", + "\n", + "16\n", + "\n", + "\n", + "\n", + "4->16\n", + "\n", + "\n", + "a & !b & c\n", + "\n", + "\n", + "\n", + "17\n", + "\n", + "17\n", + "\n", + "\n", + "\n", + "4->17\n", + "\n", + "\n", + "a & b & !c\n", + "\n", + "\n", + "\n", + "18\n", + "\n", + "18\n", + "\n", + "\n", + "\n", + "4->18\n", + "\n", + "\n", + "a & b & c\n", + "\n", + "\n", + "\n", + "15->4\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "16->3\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "16->4\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "17->2\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "17->4\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "18->2\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "18->3\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "18->4\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n" + ], + "text/plain": [ + " *' at 0x7fcc880bdc00> >" + ] + }, + "execution_count": 20, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "aut = spot.make_twa_graph()\n", + "aut.set_buchi()\n", + "aut.new_states(5)\n", + "\n", + "a = buddy.bdd_ithvar(aut.register_ap(\"a\"))\n", + "b = buddy.bdd_ithvar(aut.register_ap(\"b\"))\n", + "c = buddy.bdd_ithvar(aut.register_ap(\"c\"))\n", + "x = buddy.bdd_ithvar(aut.register_ap(\"x\"))\n", + "\n", + "\n", + "aut.new_edge(0, 1, buddy.bddtrue)\n", + "aut.new_edge(0, 4, buddy.bddtrue)\n", + "\n", + "aut.new_edge(1, 1, a)\n", + "aut.new_edge(1, 2, b)\n", + "aut.new_edge(1, 3, c)\n", + "\n", + "aut.new_edge(4, 4, a)\n", + "aut.new_edge(4, 2, b)\n", + "aut.new_edge(4, 3, c)\n", + "\n", + "aut.new_edge(2, 2, x)\n", + "aut.new_edge(3, 3, buddy.bdd_not(x))\n", + "\n", + "display(aut)\n", + "\n", + "aut = spot.split_2step(aut, x, False)\n", + "\n", + "display(aut.show_storage())\n", + "aut" + ] + }, + { + "cell_type": "code", + "execution_count": 21, + "id": "2009f279", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0\n" + ] + }, + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "Inf(\n", + "\n", + ")\n", + "[Büchi]\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "5\n", + "\n", + "5\n", + "\n", + "\n", + "\n", + "0->5\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "1\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "5->1\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "4\n", + "\n", + "4\n", + "\n", + "\n", + "\n", + "5->4\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "6\n", + "\n", + "6\n", + "\n", + "\n", + "\n", + "1->6\n", + "\n", + "\n", + "!a & !b & c\n", + "\n", + "\n", + "\n", + "7\n", + "\n", + "7\n", + "\n", + "\n", + "\n", + "1->7\n", + "\n", + "\n", + "!a & b & !c\n", + "\n", + "\n", + "\n", + "8\n", + "\n", + "8\n", + "\n", + "\n", + "\n", + "1->8\n", + "\n", + "\n", + "!a & b & c\n", + "\n", + "\n", + "\n", + "9\n", + "\n", + "9\n", + "\n", + "\n", + "\n", + "1->9\n", + "\n", + "\n", + "a & !b & !c\n", + "\n", + "\n", + "\n", + "10\n", + "\n", + "10\n", + "\n", + "\n", + "\n", + "1->10\n", + "\n", + "\n", + "a & !b & c\n", + "\n", + "\n", + "\n", + "11\n", + "\n", + "11\n", + "\n", + "\n", + "\n", + "1->11\n", + "\n", + "\n", + "a & b & !c\n", + "\n", + "\n", + "\n", + "12\n", + "\n", + "12\n", + "\n", + "\n", + "\n", + "1->12\n", + "\n", + "\n", + "a & b & c\n", + "\n", + "\n", + "\n", + "3\n", + "\n", + "3\n", + "\n", + "\n", + "\n", + "6->3\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "2\n", + "\n", + "2\n", + "\n", + "\n", + "\n", + "7->2\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "8->2\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "8->3\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "9->1\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "10->1\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "10->3\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "11->1\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "11->2\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "12->1\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "12->2\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "12->3\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "13\n", + "\n", + "13\n", + "\n", + "\n", + "\n", + "2->13\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "13->2\n", + "\n", + "\n", + "x\n", + "\n", + "\n", + "\n", + "14\n", + "\n", + "14\n", + "\n", + "\n", + "\n", + "3->14\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "14->3\n", + "\n", + "\n", + "!x\n", + "\n", + "\n", + "\n", + "4->6\n", + "\n", + "\n", + "!a & !b & c\n", + "\n", + "\n", + "\n", + "4->7\n", + "\n", + "\n", + "!a & b & !c\n", + "\n", + "\n", + "\n", + "4->8\n", + "\n", + "\n", + "!a & b & c\n", + "\n", + "\n", + "\n", + "15\n", + "\n", + "15\n", + "\n", + "\n", + "\n", + "4->15\n", + "\n", + "\n", + "a & !b & !c\n", + "\n", + "\n", + "\n", + "16\n", + "\n", + "16\n", + "\n", + "\n", + "\n", + "4->16\n", + "\n", + "\n", + "a & !b & c\n", + "\n", + "\n", + "\n", + "17\n", + "\n", + "17\n", + "\n", + "\n", + "\n", + "4->17\n", + "\n", + "\n", + "a & b & !c\n", + "\n", + "\n", + "\n", + "18\n", + "\n", + "18\n", + "\n", + "\n", + "\n", + "4->18\n", + "\n", + "\n", + "a & b & c\n", + "\n", + "\n", + "\n", + "15->4\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "16->3\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "16->4\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "17->2\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "17->4\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "18->2\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "18->3\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "18->4\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n" + ], + "text/plain": [ + " *' at 0x7fcc880bdc00> >" + ] + }, + "execution_count": 21, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "print(aut.merge_states())\n", + "aut" + ] + }, + { + "cell_type": "code", + "execution_count": 22, + "id": "17c8d6bc", + "metadata": {}, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "Inf(\n", + "\n", + ")\n", + "[Büchi]\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "1\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "0->1\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "4\n", + "\n", + "4\n", + "\n", + "\n", + "\n", + "0->4\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "2\n", + "\n", + "2\n", + "\n", + "\n", + "\n", + "1->2\n", + "\n", + "\n", + "b\n", + "\n", + "\n", + "\n", + "3\n", + "\n", + "3\n", + "\n", + "\n", + "\n", + "1->3\n", + "\n", + "\n", + "c\n", + "\n", + "\n", + "\n", + "4->2\n", + "\n", + "\n", + "b\n", + "\n", + "\n", + "\n", + "4->3\n", + "\n", + "\n", + "c\n", + "\n", + "\n", + "\n", + "2->2\n", + "\n", + "\n", + "x\n", + "\n", + "\n", + "\n", + "3->3\n", + "\n", + "\n", + "!x\n", + "\n", + "\n", + "\n" + ], + "text/plain": [ + " *' at 0x7fcc880cd2a0> >" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "g\n", + "\n", + "\n", + "\n", + "states\n", + "\n", + "\n", + "states\n", + "\n", + "\n", + "0\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "2\n", + "\n", + "\n", + "3\n", + "\n", + "\n", + "4\n", + "\n", + "\n", + "5\n", + "\n", + "\n", + "6\n", + "\n", + "\n", + "7\n", + "\n", + "\n", + "8\n", + "\n", + "\n", + "9\n", + "\n", + "\n", + "10\n", + "\n", + "succ\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "2\n", + "\n", + "\n", + "5\n", + "\n", + "\n", + "6\n", + "\n", + "\n", + "7\n", + "\n", + "\n", + "10\n", + "\n", + "\n", + "12\n", + "\n", + "\n", + "13\n", + "\n", + "\n", + "14\n", + "\n", + "\n", + "16\n", + "\n", + "\n", + "17\n", + "\n", + "succ_tail\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "4\n", + "\n", + "\n", + "5\n", + "\n", + "\n", + "6\n", + "\n", + "\n", + "9\n", + "\n", + "\n", + "11\n", + "\n", + "\n", + "12\n", + "\n", + "\n", + "13\n", + "\n", + "\n", + "15\n", + "\n", + "\n", + "16\n", + "\n", + "\n", + "17\n", + "\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "2\n", + "\n", + "\n", + "3\n", + "\n", + "\n", + "4\n", + "\n", + "\n", + "5\n", + "\n", + "\n", + "6\n", + "\n", + "\n", + "7\n", + "\n", + "\n", + "8\n", + "\n", + "\n", + "9\n", + "\n", + "\n", + "10\n", + "\n", + "\n", + "11\n", + "\n", + "\n", + "12\n", + "\n", + "\n", + "13\n", + "\n", + "\n", + "14\n", + "\n", + "\n", + "15\n", + "\n", + "\n", + "16\n", + "\n", + "\n", + "17\n", + "\n", + "cond\n", + "\n", + "1\n", + "\n", + "!b & c\n", + "\n", + "b & !c\n", + "\n", + "b & c\n", + "\n", + "1\n", + "\n", + "1\n", + "\n", + "!b & c\n", + "\n", + "b & !c\n", + "\n", + "b & c\n", + "\n", + "1\n", + "\n", + "1\n", + "\n", + "1\n", + "\n", + "1\n", + "\n", + "1\n", + "\n", + "1\n", + "\n", + "x\n", + "\n", + "!x\n", + "\n", + "acc\n", + "\n", + "{}\n", + "\n", + "{}\n", + "\n", + "{}\n", + "\n", + "{}\n", + "\n", + "{}\n", + "\n", + "{}\n", + "\n", + "{}\n", + "\n", + "{}\n", + "\n", + "{}\n", + "\n", + "{}\n", + "\n", + "{}\n", + "\n", + "{}\n", + "\n", + "{}\n", + "\n", + "{}\n", + "\n", + "{}\n", + "\n", + "{}\n", + "\n", + "{}\n", + "\n", + "dst\n", + "\n", + "\n", + "5\n", + "\n", + "\n", + "6\n", + "\n", + "\n", + "7\n", + "\n", + "\n", + "8\n", + "\n", + "\n", + "9\n", + "\n", + "\n", + "10\n", + "\n", + "\n", + "6\n", + "\n", + "\n", + "7\n", + "\n", + "\n", + "8\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "4\n", + "\n", + "\n", + "3\n", + "\n", + "\n", + "2\n", + "\n", + "\n", + "2\n", + "\n", + "\n", + "3\n", + "\n", + "\n", + "2\n", + "\n", + "\n", + "3\n", + "\n", + "next_succ\n", + "\n", + "0\n", + "\n", + "\n", + "3\n", + "\n", + "\n", + "4\n", + "\n", + "0\n", + "\n", + "0\n", + "\n", + "0\n", + "\n", + "\n", + "8\n", + "\n", + "\n", + "9\n", + "\n", + "0\n", + "\n", + "\n", + "11\n", + "\n", + "0\n", + "\n", + "0\n", + "\n", + "0\n", + "\n", + "\n", + "15\n", + "\n", + "0\n", + "\n", + "0\n", + "\n", + "0\n", + "\n", + "src\n", + "\n", + "\n", + "0\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "2\n", + "\n", + "\n", + "3\n", + "\n", + "\n", + "4\n", + "\n", + "\n", + "4\n", + "\n", + "\n", + "4\n", + "\n", + "\n", + "5\n", + "\n", + "\n", + "5\n", + "\n", + "\n", + "6\n", + "\n", + "\n", + "7\n", + "\n", + "\n", + "8\n", + "\n", + "\n", + "8\n", + "\n", + "\n", + "9\n", + "\n", + "\n", + "10\n", + "\n", + "\n", + "\n", + "meta\n", + "init_state:\n", + "\n", + "0\n", + "num_sets:\n", + "1\n", + "acceptance:\n", + "Inf(0)\n", + "ap_vars:\n", + "a b c x\n", + "\n", + "\n", + "\n", + "\n", + "props\n", + "prop_state_acc:\n", + "maybe\n", + "prop_inherently_weak:\n", + "maybe\n", + "prop_terminal:\n", + "maybe\n", + "prop_weak:\n", + "maybe\n", + "prop_very_weak:\n", + "maybe\n", + "prop_complete:\n", + "maybe\n", + "prop_universal:\n", + "maybe\n", + "prop_unambiguous:\n", + "maybe\n", + "prop_semi_deterministic:\n", + "maybe\n", + "prop_stutter_invariant:\n", + "maybe\n", + "\n", + "\n", + "\n", + "\n", + "namedprops\n", + "named properties:\n", + "state-player\n", + "synthesis-outputs\n", + "\n", + "\n", + "\n", + "\n" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "Inf(\n", + "\n", + ")\n", + "[Büchi]\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "5\n", + "\n", + "5\n", + "\n", + "\n", + "\n", + "0->5\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "1\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "5->1\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "4\n", + "\n", + "4\n", + "\n", + "\n", + "\n", + "5->4\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "6\n", + "\n", + "6\n", + "\n", + "\n", + "\n", + "1->6\n", + "\n", + "\n", + "!b & c\n", + "\n", + "\n", + "\n", + "7\n", + "\n", + "7\n", + "\n", + "\n", + "\n", + "1->7\n", + "\n", + "\n", + "b & !c\n", + "\n", + "\n", + "\n", + "8\n", + "\n", + "8\n", + "\n", + "\n", + "\n", + "1->8\n", + "\n", + "\n", + "b & c\n", + "\n", + "\n", + "\n", + "3\n", + "\n", + "3\n", + "\n", + "\n", + "\n", + "6->3\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "2\n", + "\n", + "2\n", + "\n", + "\n", + "\n", + "7->2\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "8->2\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "8->3\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "9\n", + "\n", + "9\n", + "\n", + "\n", + "\n", + "2->9\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "9->2\n", + "\n", + "\n", + "x\n", + "\n", + "\n", + "\n", + "10\n", + "\n", + "10\n", + "\n", + "\n", + "\n", + "3->10\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "10->3\n", + "\n", + "\n", + "!x\n", + "\n", + "\n", + "\n", + "4->6\n", + "\n", + "\n", + "!b & c\n", + "\n", + "\n", + "\n", + "4->7\n", + "\n", + "\n", + "b & !c\n", + "\n", + "\n", + "\n", + "4->8\n", + "\n", + "\n", + "b & c\n", + "\n", + "\n", + "\n" + ], + "text/plain": [ + " *' at 0x7fcc880c55a0> >" + ] + }, + "execution_count": 22, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Merging possible even in split case\n", + "aut = spot.make_twa_graph()\n", + "aut.set_buchi()\n", + "aut.new_states(5)\n", + "\n", + "a = buddy.bdd_ithvar(aut.register_ap(\"a\"))\n", + "b = buddy.bdd_ithvar(aut.register_ap(\"b\"))\n", + "c = buddy.bdd_ithvar(aut.register_ap(\"c\"))\n", + "x = buddy.bdd_ithvar(aut.register_ap(\"x\"))\n", + "\n", + "\n", + "aut.new_edge(0, 1, buddy.bddtrue)\n", + "aut.new_edge(0, 4, buddy.bddtrue)\n", + "\n", + "aut.new_edge(1, 2, b)\n", + "aut.new_edge(1, 3, c)\n", + "\n", + "aut.new_edge(4, 2, b)\n", + "aut.new_edge(4, 3, c)\n", + "\n", + "aut.new_edge(2, 2, x)\n", + "aut.new_edge(3, 3, buddy.bdd_not(x))\n", + "\n", + "display(aut)\n", + "\n", + "aut = spot.split_2step(aut, x, False)\n", + "\n", + "display(aut.show_storage())\n", + "aut" + ] + }, + { + "cell_type": "code", + "execution_count": 23, + "id": "b3e90235", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "1\n" + ] + }, + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "Inf(\n", + "\n", + ")\n", + "[Büchi]\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "4\n", + "\n", + "4\n", + "\n", + "\n", + "\n", + "0->4\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "1\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "4->1\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "4->1\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "5\n", + "\n", + "5\n", + "\n", + "\n", + "\n", + "1->5\n", + "\n", + "\n", + "!b & c\n", + "\n", + "\n", + "\n", + "6\n", + "\n", + "6\n", + "\n", + "\n", + "\n", + "1->6\n", + "\n", + "\n", + "b & !c\n", + "\n", + "\n", + "\n", + "7\n", + "\n", + "7\n", + "\n", + "\n", + "\n", + "1->7\n", + "\n", + "\n", + "b & c\n", + "\n", + "\n", + "\n", + "3\n", + "\n", + "3\n", + "\n", + "\n", + "\n", + "5->3\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "2\n", + "\n", + "2\n", + "\n", + "\n", + "\n", + "6->2\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "7->2\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "7->3\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "8\n", + "\n", + "8\n", + "\n", + "\n", + "\n", + "2->8\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "8->2\n", + "\n", + "\n", + "x\n", + "\n", + "\n", + "\n", + "9\n", + "\n", + "9\n", + "\n", + "\n", + "\n", + "3->9\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "9->3\n", + "\n", + "\n", + "!x\n", + "\n", + "\n", + "\n" + ], + "text/plain": [ + " *' at 0x7fcc880c55a0> >" + ] + }, + "execution_count": 23, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "print(aut.merge_states())\n", + "aut" + ] + }, + { + "cell_type": "markdown", + "id": "05785bb1", + "metadata": {}, + "source": [ + "Fail case for alternate_players" + ] + }, + { + "cell_type": "code", + "execution_count": 24, + "id": "df4aa681", + "metadata": {}, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "Inf(\n", + "\n", + ")\n", + "[Büchi]\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "1\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "0->1\n", + "\n", + "\n", + "\n", + "i\n", + "/\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "1->0\n", + "\n", + "\n", + "\n", + "1\n", + "/\n", + "\n", + "o\n", + "\n", + "\n", + "\n", + "\n" + ], + "text/plain": [ + " *' at 0x7fcc880c5a50> >" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "Inf(\n", + "\n", + ")\n", + "[Büchi]\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "1\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "0->1\n", + "\n", + "\n", + "i\n", + "\n", + "\n", + "\n", + "2\n", + "\n", + "2\n", + "\n", + "\n", + "\n", + "0->2\n", + "\n", + "\n", + "!i\n", + "\n", + "\n", + "\n", + "1->0\n", + "\n", + "\n", + "o\n", + "\n", + "\n", + "\n", + "\n", + "3\n", + "\n", + "3\n", + "\n", + "\n", + "\n", + "2->3\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "3->2\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n" + ], + "text/plain": [ + " *' at 0x7fcc880c5a50> >" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "aut = spot.make_twa_graph()\n", + "aut.set_buchi()\n", + "i = buddy.bdd_ithvar(aut.register_ap(\"i\"))\n", + "o = buddy.bdd_ithvar(aut.register_ap(\"o\"))\n", + "\n", + "spot.set_synthesis_outputs(aut, o)\n", + "\n", + "aut.new_states(2)\n", + "aut.new_edge(0,1,i)\n", + "aut.new_edge(1,0,o,spot.mark_t([0]))\n", + "display(aut)\n", + "spot.alternate_players(aut)\n", + "display(aut)" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "f3b2d981", + "metadata": {}, + "outputs": [], + "source": [] } ], "metadata": { From 524edea8da75486402481ec33c51763374852a3c Mon Sep 17 00:00:00 2001 From: Philipp Schlehuber-Caissier Date: Mon, 4 Apr 2022 08:47:11 +0200 Subject: [PATCH 029/337] Propagate colors in split_2step Reduce the amount of uncolored transitions after split_2step by trying to color the env transitions. This is currently only supported for parity like acceptance conditions. * spot/twaalgos/game.cc: Determinizatio of "colored" game can created trivial self-loops. Fix them * spot/twaalgos/synthesis.cc: Here * tests/core/ltlsynt.test, tests/python/_synthesis.ipynb, tests/python/games.ipynb, tests/python/synthesis.ipynb, tests/python/synthesis.py: New and adjusted tests --- spot/twaalgos/game.cc | 17 +- spot/twaalgos/synthesis.cc | 47 +- tests/core/ltlsynt.test | 28 +- tests/python/_synthesis.ipynb | 3 +- tests/python/games.ipynb | 24 +- tests/python/synthesis.ipynb | 3582 +++++++++++++++++---------------- tests/python/synthesis.py | 16 +- 7 files changed, 1896 insertions(+), 1821 deletions(-) diff --git a/spot/twaalgos/game.cc b/spot/twaalgos/game.cc index 6d319eea8..e1d23e381 100644 --- a/spot/twaalgos/game.cc +++ b/spot/twaalgos/game.cc @@ -867,7 +867,7 @@ namespace spot todo.pop_back(); seen[src] = true; bdd missing = bddtrue; - for (const auto& e: arena->out(src)) + for (auto& e: arena->out(src)) { bool osrc = (*owner)[src]; if (complete0 && !osrc) @@ -878,6 +878,21 @@ namespace spot (*owner)[e.dst] = !osrc; todo.push_back(e.dst); } + else if (e.src == e.dst) + { + if (e.cond == bddtrue) + { + // Fix trivial self-loop + // No need to add it to seen + auto inter = arena->new_state(); + owner->push_back(!osrc); + e.dst = inter; + arena->new_edge(inter, src, bddtrue, e.acc); + } + else + throw std::runtime_error("alternate_players(): " + "Nontrivial selfloop"); + } else if ((*owner)[e.dst] == osrc) { delete owner; diff --git a/spot/twaalgos/synthesis.cc b/spot/twaalgos/synthesis.cc index 1708923c6..ed4915c4b 100644 --- a/spot/twaalgos/synthesis.cc +++ b/spot/twaalgos/synthesis.cc @@ -447,9 +447,13 @@ namespace spot split_2step(const const_twa_graph_ptr& aut, const bdd& output_bdd, bool complete_env) { + assert(!aut->get_named_prop("state-player") + && "aut is already split!"); auto split = make_twa_graph(aut->get_dict()); auto [has_unsat, unsat_mark] = aut->acc().unsat_mark(); + bool max_par, odd_par, color_env; + color_env = aut->acc().is_parity(max_par, odd_par, true); split->copy_ap_of(aut); split->new_states(aut->num_states()); @@ -457,6 +461,7 @@ namespace spot set_synthesis_outputs(split, output_bdd); const auto use_color = has_unsat; + color_env &= use_color; if (has_unsat) split->copy_acceptance_of(aut); else @@ -490,8 +495,10 @@ namespace spot // So we can first loop over the aut // and then deduce the owner - // a sort of hash-map for all new intermediate states - std::unordered_multimap env_hash; + // a sort of hash-map for all new intermediate stat + // second is the color of the incoming env trans + std::unordered_multimap> env_hash; env_hash.reserve((int) (1.5 * aut->num_states())); // a local map for edges leaving the current src // this avoids creating and then combining edges for each minterm @@ -590,7 +597,7 @@ namespace spot auto range_h = env_hash.equal_range(h); for (auto it_h = range_h.first; it_h != range_h.second; ++it_h) { - unsigned i = it_h->second; + const auto& [i, this_color] = it_h->second; auto out = split->out(i); if (std::equal(out.begin(), out.end(), dests.begin(), dests.end(), @@ -612,9 +619,10 @@ namespace spot if (it != env_edge_hash.end()) it->second.second |= one_letter; else - // Uncolored env_edge_hash.emplace(i, - eeh_t(split->new_edge(src, i, bddtrue), one_letter)); + eeh_t(split->new_edge(src, i, bddtrue, + this_color), + one_letter)); break; } } @@ -622,12 +630,31 @@ namespace spot if (to_add) { unsigned d = split->new_state(); - unsigned n_e = split->new_edge(src, d, bddtrue); - env_hash.emplace(h, d); + auto this_color = acc_cond::mark_t({}); + bool has_uncolored = false; + for (const auto &t: dests) + { + split->new_edge(d, t->dst, t->econdout, + use_color ? t->acc + : acc_cond::mark_t({})); + this_color |= t->acc; + has_uncolored |= !t->acc; + } + + if (!color_env | has_uncolored) + this_color = acc_cond::mark_t({}); + else if (max_par) + this_color = + acc_cond::mark_t({this_color.min_set()-1}); + else // min_par + this_color = + acc_cond::mark_t({this_color.max_set()-1}); + + unsigned n_e = split->new_edge(src, d, bddtrue, this_color); + env_hash.emplace(std::piecewise_construct, + std::forward_as_tuple(h), + std::forward_as_tuple(d, this_color)); env_edge_hash.emplace(d, eeh_t(n_e, one_letter)); - for (const auto &t: dests) - split->new_edge(d, t->dst, t->econdout, - use_color ? t->acc : acc_cond::mark_t({})); } } // letters // save locally stored condition diff --git a/tests/core/ltlsynt.test b/tests/core/ltlsynt.test index 8e63344b2..03f7598c9 100644 --- a/tests/core/ltlsynt.test +++ b/tests/core/ltlsynt.test @@ -25,23 +25,23 @@ set -e cat >exp <\n", "\n", "a\n", - "\n", + "\n", "\n", "\n", "\n", @@ -804,7 +804,7 @@ "\n", "\n", "a\n", - "\n", + "\n", "\n", "\n", "\n", @@ -818,7 +818,7 @@ "\n", "\n", "!a\n", - "\n", + "\n", "\n", "\n", "\n", @@ -928,7 +928,7 @@ "\n" ], "text/plain": [ - " *' at 0x7f80642eee70> >" + " *' at 0x7f202420db10> >" ] }, "execution_count": 8, @@ -972,10 +972,10 @@ "--BODY--\n", "State: 0\n", "[!1] 5 {1}\n", - "[1] 6 {1}\n", + "[1] 6 {2}\n", "State: 1\n", - "[1] 6 {1}\n", - "[!1] 7 {1}\n", + "[1] 6 {2}\n", + "[!1] 7 {2}\n", "State: 2\n", "[t] 8 {1}\n", "State: 3\n", @@ -1134,7 +1134,7 @@ "\n", "\n", "a\n", - "\n", + "\n", "\n", "\n", "\n", @@ -1164,7 +1164,7 @@ "\n", "\n", "a\n", - "\n", + "\n", "\n", "\n", "\n", @@ -1178,7 +1178,7 @@ "\n", "\n", "!a\n", - "\n", + "\n", "\n", "\n", "\n", @@ -1288,7 +1288,7 @@ "\n" ], "text/plain": [ - " *' at 0x7f806443b1b0> >" + " *' at 0x7f202420df90> >" ] }, "execution_count": 11, @@ -1324,7 +1324,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.2" + "version": "3.8.10" } }, "nbformat": 4, diff --git a/tests/python/synthesis.ipynb b/tests/python/synthesis.ipynb index 3738e6f72..08af437e2 100644 --- a/tests/python/synthesis.ipynb +++ b/tests/python/synthesis.ipynb @@ -3,6 +3,7 @@ { "cell_type": "code", "execution_count": 1, + "id": "7a864ea1", "metadata": {}, "outputs": [], "source": [ @@ -13,6 +14,7 @@ }, { "cell_type": "markdown", + "id": "9a294cae", "metadata": {}, "source": [ "This notebook presents functions that can be used to solve the Reactive Synthesis problem using games.\n", @@ -37,6 +39,7 @@ { "cell_type": "code", "execution_count": 2, + "id": "70429a41", "metadata": {}, "outputs": [ { @@ -53,647 +56,647 @@ "\n", "\n", - "\n", "\n", - "\n", - "\n", - "\n", - "Fin(\n", - "\n", - ") & (Inf(\n", - "\n", - ") | Fin(\n", - "\n", - "))\n", - "[parity max odd 3]\n", + "\n", + "\n", + "\n", + "Fin(\n", + "\n", + ") & (Inf(\n", + "\n", + ") | Fin(\n", + "\n", + "))\n", + "[parity max odd 3]\n", "\n", "\n", "\n", "9\n", - "\n", - "9\n", + "\n", + "9\n", "\n", "\n", "\n", "I->9\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "25\n", - "\n", - "25\n", + "\n", + "25\n", "\n", "\n", "\n", "9->25\n", - "\n", - "\n", - "!i0 & !i1\n", - "\n", + "\n", + "\n", + "!i0 & !i1\n", + "\n", "\n", "\n", "\n", "26\n", - "\n", - "26\n", + "\n", + "26\n", "\n", "\n", "\n", "9->26\n", - "\n", - "\n", - "i0 & !i1\n", - "\n", + "\n", + "\n", + "i0 & !i1\n", + "\n", "\n", "\n", "\n", "27\n", - "\n", - "27\n", + "\n", + "27\n", "\n", "\n", "\n", "9->27\n", - "\n", - "\n", - "!i0 & i1\n", - "\n", + "\n", + "\n", + "!i0 & i1\n", + "\n", "\n", "\n", "\n", "28\n", - "\n", - "28\n", + "\n", + "28\n", "\n", "\n", "\n", "9->28\n", - "\n", - "\n", - "i0 & i1\n", - "\n", + "\n", + "\n", + "i0 & i1\n", + "\n", "\n", "\n", "\n", "0\n", - "\n", - "0\n", + "\n", + "0\n", "\n", "\n", "\n", "10\n", - "\n", - "10\n", + "\n", + "10\n", "\n", "\n", "\n", "0->10\n", - "\n", - "\n", - "!i1\n", - "\n", + "\n", + "\n", + "!i1\n", + "\n", "\n", "\n", "\n", "11\n", - "\n", - "11\n", + "\n", + "11\n", "\n", "\n", "\n", "0->11\n", - "\n", - "\n", - "i1\n", - "\n", + "\n", + "\n", + "i1\n", + "\n", "\n", "\n", "\n", "1\n", - "\n", - "1\n", + "\n", + "1\n", "\n", "\n", "\n", "10->1\n", - "\n", - "\n", - "o0\n", - "\n", + "\n", + "\n", + "o0\n", + "\n", "\n", "\n", "\n", "11->0\n", - "\n", - "\n", - "o0\n", - "\n", + "\n", + "\n", + "o0\n", + "\n", "\n", "\n", "\n", "12\n", - "\n", - "12\n", + "\n", + "12\n", "\n", "\n", "\n", "1->12\n", - "\n", - "\n", - "!i1\n", - "\n", + "\n", + "\n", + "!i1\n", + "\n", "\n", "\n", "\n", "13\n", - "\n", - "13\n", + "\n", + "13\n", "\n", "\n", "\n", "1->13\n", - "\n", - "\n", - "i1\n", - "\n", + "\n", + "\n", + "i1\n", + "\n", "\n", "\n", "\n", "12->1\n", - "\n", - "\n", - "!o0\n", - "\n", + "\n", + "\n", + "!o0\n", + "\n", "\n", "\n", "\n", "13->0\n", - "\n", - "\n", - "!o0\n", - "\n", + "\n", + "\n", + "!o0\n", + "\n", "\n", "\n", "\n", "2\n", - "\n", - "2\n", + "\n", + "2\n", "\n", "\n", "\n", "14\n", - "\n", - "14\n", + "\n", + "14\n", "\n", "\n", "\n", "2->14\n", - "\n", - "\n", - "i1\n", - "\n", + "\n", + "\n", + "i1\n", + "\n", "\n", "\n", "\n", "16\n", - "\n", - "16\n", + "\n", + "16\n", "\n", "\n", "\n", "2->16\n", - "\n", - "\n", - "!i1\n", - "\n", + "\n", + "\n", + "!i1\n", + "\n", "\n", "\n", "\n", "15\n", - "\n", - "15\n", + "\n", + "15\n", "\n", "\n", "\n", "14->15\n", - "\n", - "\n", - "1\n", - "\n", + "\n", + "\n", + "1\n", + "\n", "\n", "\n", "\n", "16->2\n", - "\n", - "\n", - "1\n", - "\n", + "\n", + "\n", + "1\n", + "\n", "\n", "\n", "\n", "3\n", - "\n", - "3\n", + "\n", + "3\n", "\n", "\n", "\n", "3->13\n", - "\n", - "\n", - "i1\n", - "\n", + "\n", + "\n", + "i1\n", + "\n", "\n", "\n", "\n", "17\n", - "\n", - "17\n", + "\n", + "17\n", "\n", "\n", "\n", "3->17\n", - "\n", - "\n", - "!i1\n", - "\n", + "\n", + "\n", + "!i1\n", + "\n", "\n", "\n", "\n", "17->2\n", - "\n", - "\n", - "o0\n", - "\n", + "\n", + "\n", + "o0\n", + "\n", "\n", "\n", "\n", "17->3\n", - "\n", - "\n", - "!o0\n", - "\n", + "\n", + "\n", + "!o0\n", + "\n", "\n", "\n", "\n", "4\n", - "\n", - "4\n", + "\n", + "4\n", "\n", "\n", "\n", "4->14\n", - "\n", - "\n", - "i0\n", - "\n", + "\n", + "\n", + "i0\n", + "\n", "\n", "\n", "\n", "18\n", - "\n", - "18\n", + "\n", + "18\n", "\n", "\n", "\n", "4->18\n", - "\n", - "\n", - "!i0\n", - "\n", + "\n", + "\n", + "!i0\n", + "\n", "\n", "\n", "\n", "18->4\n", - "\n", - "\n", - "1\n", - "\n", + "\n", + "\n", + "1\n", + "\n", "\n", "\n", "\n", "5\n", - "\n", - "5\n", + "\n", + "5\n", "\n", "\n", "\n", "5->14\n", - "\n", - "\n", - "i0 & i1\n", - "\n", + "\n", + "\n", + "i0 & i1\n", + "\n", "\n", "\n", "\n", "5->16\n", - "\n", - "\n", - "i0 & !i1\n", - "\n", + "\n", + "\n", + "i0 & !i1\n", + "\n", "\n", "\n", "\n", "5->18\n", - "\n", - "\n", - "!i0 & i1\n", - "\n", + "\n", + "\n", + "!i0 & i1\n", + "\n", "\n", "\n", "\n", "19\n", - "\n", - "19\n", + "\n", + "19\n", "\n", "\n", "\n", "5->19\n", - "\n", - "\n", - "!i0 & !i1\n", - "\n", + "\n", + "\n", + "!i0 & !i1\n", + "\n", "\n", "\n", "\n", "19->5\n", - "\n", - "\n", - "1\n", - "\n", + "\n", + "\n", + "1\n", + "\n", "\n", "\n", "\n", "6\n", - "\n", - "6\n", + "\n", + "6\n", "\n", "\n", "\n", "6->10\n", - "\n", - "\n", - "i0 & !i1\n", - "\n", + "\n", + "\n", + "i0 & !i1\n", + "\n", "\n", "\n", "\n", "6->11\n", - "\n", - "\n", - "i0 & i1\n", - "\n", + "\n", + "\n", + "i0 & i1\n", + "\n", "\n", "\n", "\n", "20\n", - "\n", - "20\n", + "\n", + "20\n", "\n", "\n", "\n", "6->20\n", - "\n", - "\n", - "!i0 & !i1\n", - "\n", + "\n", + "\n", + "!i0 & !i1\n", + "\n", "\n", "\n", "\n", "21\n", - "\n", - "21\n", + "\n", + "21\n", "\n", "\n", "\n", "6->21\n", - "\n", - "\n", - "!i0 & i1\n", - "\n", + "\n", + "\n", + "!i0 & i1\n", + "\n", "\n", "\n", "\n", "20->4\n", - "\n", - "\n", - "!o0\n", - "\n", + "\n", + "\n", + "!o0\n", + "\n", "\n", "\n", "\n", "7\n", - "\n", - "7\n", + "\n", + "7\n", "\n", "\n", "\n", "20->7\n", - "\n", - "\n", - "o0\n", - "\n", + "\n", + "\n", + "o0\n", + "\n", "\n", "\n", "\n", "21->4\n", - "\n", - "\n", - "!o0\n", - "\n", + "\n", + "\n", + "!o0\n", + "\n", "\n", "\n", "\n", "21->6\n", - "\n", - "\n", - "o0\n", - "\n", + "\n", + "\n", + "o0\n", + "\n", "\n", "\n", "\n", "7->12\n", - "\n", - "\n", - "i0 & !i1\n", - "\n", + "\n", + "\n", + "i0 & !i1\n", + "\n", "\n", "\n", "\n", "7->13\n", - "\n", - "\n", - "i0 & i1\n", - "\n", + "\n", + "\n", + "i0 & i1\n", + "\n", "\n", "\n", "\n", "22\n", - "\n", - "22\n", + "\n", + "22\n", "\n", "\n", "\n", "7->22\n", - "\n", - "\n", - "!i0 & !i1\n", - "\n", + "\n", + "\n", + "!i0 & !i1\n", + "\n", "\n", "\n", "\n", "23\n", - "\n", - "23\n", + "\n", + "23\n", "\n", "\n", "\n", "7->23\n", - "\n", - "\n", - "!i0 & i1\n", - "\n", + "\n", + "\n", + "!i0 & i1\n", + "\n", "\n", "\n", "\n", "22->4\n", - "\n", - "\n", - "o0\n", - "\n", + "\n", + "\n", + "o0\n", + "\n", "\n", "\n", "\n", "22->7\n", - "\n", - "\n", - "!o0\n", - "\n", + "\n", + "\n", + "!o0\n", + "\n", "\n", "\n", "\n", "23->4\n", - "\n", - "\n", - "o0\n", - "\n", + "\n", + "\n", + "o0\n", + "\n", "\n", "\n", "\n", "23->6\n", - "\n", - "\n", - "!o0\n", - "\n", + "\n", + "\n", + "!o0\n", + "\n", "\n", "\n", "\n", "8\n", - "\n", - "8\n", + "\n", + "8\n", "\n", "\n", "\n", "8->13\n", - "\n", - "\n", - "i0 & i1\n", - "\n", + "\n", + "\n", + "i0 & i1\n", + "\n", "\n", "\n", "\n", "8->17\n", - "\n", - "\n", - "i0 & !i1\n", - "\n", + "\n", + "\n", + "i0 & !i1\n", + "\n", "\n", "\n", "\n", "8->23\n", - "\n", - "\n", - "!i0 & i1\n", - "\n", + "\n", + "\n", + "!i0 & i1\n", + "\n", "\n", "\n", "\n", "24\n", - "\n", - "24\n", + "\n", + "24\n", "\n", "\n", "\n", "8->24\n", - "\n", - "\n", - "!i0 & !i1\n", - "\n", + "\n", + "\n", + "!i0 & !i1\n", + "\n", "\n", "\n", "\n", "24->5\n", - "\n", - "\n", - "o0\n", - "\n", + "\n", + "\n", + "o0\n", + "\n", "\n", "\n", "\n", "24->8\n", - "\n", - "\n", - "!o0\n", - "\n", + "\n", + "\n", + "!o0\n", + "\n", "\n", "\n", "\n", "25->8\n", - "\n", - "\n", - "1\n", - "\n", + "\n", + "\n", + "1\n", + "\n", "\n", "\n", "\n", "26->3\n", - "\n", - "\n", - "1\n", - "\n", + "\n", + "\n", + "1\n", + "\n", "\n", "\n", "\n", "27->6\n", - "\n", - "\n", - "1\n", - "\n", + "\n", + "\n", + "1\n", + "\n", "\n", "\n", "\n", "28->0\n", - "\n", - "\n", - "1\n", - "\n", + "\n", + "\n", + "1\n", + "\n", "\n", "\n", "\n", "15->14\n", - "\n", - "\n", - "1\n", - "\n", + "\n", + "\n", + "1\n", + "\n", "\n", "\n", "\n" ], "text/plain": [ - " *' at 0x7fb964737d50> >" + " *' at 0x7f01fc12f030> >" ] }, "metadata": {}, @@ -712,6 +715,7 @@ }, { "cell_type": "markdown", + "id": "c02b2d8f", "metadata": {}, "source": [ "Solving the game, is done with `solve_game()` as with any game. There is also a version that takes a `synthesis_info` as second argument in case the time it takes has to be recorded. Here passing `si` or not makes no difference." @@ -720,6 +724,7 @@ { "cell_type": "code", "execution_count": 3, + "id": "d08e7b9f", "metadata": {}, "outputs": [ { @@ -735,586 +740,586 @@ "\n", "\n", - "\n", "\n", "\n", - "\n", - "\n", - "Fin(\n", - "\n", - ") & (Inf(\n", - "\n", - ") | Fin(\n", - "\n", - "))\n", - "[parity max odd 3]\n", + " viewBox=\"0.00 0.00 650.45 360.00\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n", + "\n", + "\n", + "Fin(\n", + "\n", + ") & (Inf(\n", + "\n", + ") | Fin(\n", + "\n", + "))\n", + "[parity max odd 3]\n", "\n", "\n", "\n", "9\n", - "\n", - "9\n", + "\n", + "9\n", "\n", "\n", "\n", "I->9\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "25\n", - "\n", - "25\n", + "\n", + "25\n", "\n", "\n", "\n", "9->25\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "26\n", - "\n", - "26\n", + "\n", + "26\n", "\n", "\n", "\n", "9->26\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "27\n", - "\n", - "27\n", + "\n", + "27\n", "\n", "\n", "\n", "9->27\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "28\n", - "\n", - "28\n", + "\n", + "28\n", "\n", "\n", "\n", "9->28\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "0\n", - "\n", - "0\n", + "\n", + "0\n", "\n", "\n", "\n", "10\n", - "\n", - "10\n", + "\n", + "10\n", "\n", "\n", "\n", "0->10\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "11\n", - "\n", - "11\n", + "\n", + "11\n", "\n", "\n", "\n", "0->11\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "1\n", - "\n", - "1\n", + "\n", + "1\n", "\n", "\n", "\n", "10->1\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "11->0\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "12\n", - "\n", - "12\n", + "\n", + "12\n", "\n", "\n", "\n", "1->12\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "13\n", - "\n", - "13\n", + "\n", + "13\n", "\n", "\n", "\n", "1->13\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "12->1\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "13->0\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "2\n", - "\n", - "2\n", + "\n", + "2\n", "\n", "\n", "\n", "14\n", - "\n", - "14\n", + "\n", + "14\n", "\n", "\n", "\n", "2->14\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "16\n", - "\n", - "16\n", + "\n", + "16\n", "\n", "\n", "\n", "2->16\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "15\n", - "\n", - "15\n", + "\n", + "15\n", "\n", "\n", "\n", "14->15\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "16->2\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "3\n", - "\n", - "3\n", + "\n", + "3\n", "\n", "\n", "\n", "3->13\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "17\n", - "\n", - "17\n", + "\n", + "17\n", "\n", "\n", "\n", "3->17\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "17->2\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "17->3\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "4\n", - "\n", - "4\n", + "\n", + "4\n", "\n", "\n", "\n", "4->14\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "18\n", - "\n", - "18\n", + "\n", + "18\n", "\n", "\n", "\n", "4->18\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "18->4\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "5\n", - "\n", - "5\n", + "\n", + "5\n", "\n", "\n", "\n", "5->14\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "5->16\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "5->18\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "19\n", - "\n", - "19\n", + "\n", + "19\n", "\n", "\n", "\n", "5->19\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "19->5\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "6\n", - "\n", - "6\n", + "\n", + "6\n", "\n", "\n", "\n", "6->10\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "6->11\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "20\n", - "\n", - "20\n", + "\n", + "20\n", "\n", "\n", "\n", "6->20\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "21\n", - "\n", - "21\n", + "\n", + "21\n", "\n", "\n", "\n", "6->21\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "20->4\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "7\n", - "\n", - "7\n", + "\n", + "7\n", "\n", "\n", "\n", "20->7\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "21->4\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "21->6\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "7->12\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "7->13\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "22\n", - "\n", - "22\n", + "\n", + "22\n", "\n", "\n", "\n", "7->22\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "23\n", - "\n", - "23\n", + "\n", + "23\n", "\n", "\n", "\n", "7->23\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "22->4\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "22->7\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "23->4\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "23->6\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "8\n", - "\n", - "8\n", + "\n", + "8\n", "\n", "\n", "\n", "8->13\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "8->17\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "8->23\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "24\n", - "\n", - "24\n", + "\n", + "24\n", "\n", "\n", "\n", "8->24\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "24->5\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "24->8\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "25->8\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "26->3\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "27->6\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "28->0\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "15->14\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n" @@ -1336,6 +1341,7 @@ }, { "cell_type": "markdown", + "id": "9590cf55", "metadata": {}, "source": [ "Once a strategy has been found, it can be extracted as an automaton and simplified using 6 different levels (the default is 2). The output should be interpreted as a Mealy automaton, where transition have the form `(ins)&(outs)` where `ins` and `outs` are Boolean formulas representing possible inputs and outputs (they could be more than just conjunctions of atomic proposition). Mealy machines with this type of labels are called \"separated\" in Spot." @@ -1344,6 +1350,7 @@ { "cell_type": "code", "execution_count": 4, + "id": "d6cb467d", "metadata": {}, "outputs": [ { @@ -1359,309 +1366,309 @@ "\n", "\n", - "\n", "\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "0\n", - "\n", - "0\n", + "\n", + "0\n", "\n", "\n", "\n", "I->0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "1\n", - "\n", - "1\n", + "\n", + "1\n", "\n", "\n", "\n", "0->1\n", - "\n", - "\n", - "\n", - "!i0 & !i1\n", - "/\n", - "\n", - "1\n", + "\n", + "\n", + "\n", + "!i0 & !i1\n", + "/\n", + "\n", + "1\n", "\n", "\n", "\n", "2\n", - "\n", - "2\n", + "\n", + "2\n", "\n", "\n", "\n", "0->2\n", - "\n", - "\n", - "\n", - "i0 & !i1\n", - "/\n", - "\n", - "1\n", + "\n", + "\n", + "\n", + "i0 & !i1\n", + "/\n", + "\n", + "1\n", "\n", "\n", "\n", "3\n", - "\n", - "3\n", + "\n", + "3\n", "\n", "\n", "\n", "0->3\n", - "\n", - "\n", - "\n", - "!i0 & i1\n", - "/\n", - "\n", - "1\n", + "\n", + "\n", + "\n", + "!i0 & i1\n", + "/\n", + "\n", + "1\n", "\n", "\n", "\n", "4\n", - "\n", - "4\n", + "\n", + "4\n", "\n", "\n", "\n", "0->4\n", - "\n", - "\n", - "\n", - "i0 & i1\n", - "/\n", - "\n", - "1\n", + "\n", + "\n", + "\n", + "i0 & i1\n", + "/\n", + "\n", + "1\n", "\n", "\n", "\n", "1->1\n", - "\n", - "\n", - "\n", - "!i0 & !i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "!i0 & !i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "1->2\n", - "\n", - "\n", - "\n", - "i0 & !i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "i0 & !i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "1->3\n", - "\n", - "\n", - "\n", - "!i0 & i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "!i0 & i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "1->4\n", - "\n", - "\n", - "\n", - "i0 & i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "i0 & i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "2->2\n", - "\n", - "\n", - "\n", - "!i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "!i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "2->4\n", - "\n", - "\n", - "\n", - "i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "3->3\n", - "\n", - "\n", - "\n", - "!i0 & i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "!i0 & i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "3->4\n", - "\n", - "\n", - "\n", - "i0 & i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "i0 & i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "5\n", - "\n", - "5\n", + "\n", + "5\n", "\n", "\n", "\n", "3->5\n", - "\n", - "\n", - "\n", - "i0 & !i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "i0 & !i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "6\n", - "\n", - "6\n", + "\n", + "6\n", "\n", "\n", "\n", "3->6\n", - "\n", - "\n", - "\n", - "!i0 & !i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "!i0 & !i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "4->4\n", - "\n", - "\n", - "\n", - "i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "4->5\n", - "\n", - "\n", - "\n", - "!i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "!i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "5->4\n", - "\n", - "\n", - "\n", - "i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "5->5\n", - "\n", - "\n", - "\n", - "!i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "!i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "6->3\n", - "\n", - "\n", - "\n", - "!i0 & i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "!i0 & i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "6->4\n", - "\n", - "\n", - "\n", - "i0 & i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "i0 & i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "6->5\n", - "\n", - "\n", - "\n", - "i0 & !i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "i0 & !i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "6->6\n", - "\n", - "\n", - "\n", - "!i0 & !i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "!i0 & !i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n" ], "text/plain": [ - " *' at 0x7fb9646b7870> >" + " *' at 0x7f01fc1b5f00> >" ] }, "metadata": {}, @@ -1680,175 +1687,175 @@ "\n", "\n", - "\n", "\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "0\n", - "\n", - "0\n", + "\n", + "0\n", "\n", "\n", "\n", "I->0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "1\n", - "\n", - "1\n", + "\n", + "1\n", "\n", "\n", "\n", "0->1\n", - "\n", - "\n", - "\n", - "!i0 & !i1\n", - "/\n", - "\n", - "1\n", + "\n", + "\n", + "\n", + "!i0 & !i1\n", + "/\n", + "\n", + "1\n", "\n", "\n", "\n", "0->1\n", - "\n", - "\n", - "\n", - "i0 & !i1\n", - "/\n", - "\n", - "1\n", + "\n", + "\n", + "\n", + "i0 & !i1\n", + "/\n", + "\n", + "1\n", "\n", "\n", "\n", "2\n", - "\n", - "2\n", + "\n", + "2\n", "\n", "\n", "\n", "0->2\n", - "\n", - "\n", - "\n", - "!i0 & i1\n", - "/\n", - "\n", - "1\n", + "\n", + "\n", + "\n", + "!i0 & i1\n", + "/\n", + "\n", + "1\n", "\n", "\n", "\n", "0->2\n", - "\n", - "\n", - "\n", - "i0 & i1\n", - "/\n", - "\n", - "1\n", + "\n", + "\n", + "\n", + "i0 & i1\n", + "/\n", + "\n", + "1\n", "\n", "\n", "\n", "1->1\n", - "\n", - "\n", - "\n", - "i0 & !i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "i0 & !i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "1->1\n", - "\n", - "\n", - "\n", - "!i0 & !i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "!i0 & !i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "1->2\n", - "\n", - "\n", - "\n", - "i0 & i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "i0 & i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "1->2\n", - "\n", - "\n", - "\n", - "!i0 & i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "!i0 & i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "2->1\n", - "\n", - "\n", - "\n", - "i0 & !i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "i0 & !i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "2->1\n", - "\n", - "\n", - "\n", - "!i0 & !i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "!i0 & !i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "2->2\n", - "\n", - "\n", - "\n", - "i0 & i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "i0 & i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "2->2\n", - "\n", - "\n", - "\n", - "!i0 & i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "!i0 & i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n" ], "text/plain": [ - " *' at 0x7fb9646b7de0> >" + " *' at 0x7f01fc12fd80> >" ] }, "metadata": {}, @@ -1867,125 +1874,125 @@ "\n", "\n", - "\n", "\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "1\n", - "\n", - "1\n", + "\n", + "1\n", "\n", "\n", "\n", "I->1\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "1->1\n", - "\n", - "\n", - "\n", - "i0 & i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "i0 & i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "1->1\n", - "\n", - "\n", - "\n", - "!i0 & i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "!i0 & i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "0\n", - "\n", - "0\n", + "\n", + "0\n", "\n", "\n", "\n", "1->0\n", - "\n", - "\n", - "\n", - "i0 & !i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "i0 & !i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "1->0\n", - "\n", - "\n", - "\n", - "!i0 & !i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "!i0 & !i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "0->1\n", - "\n", - "\n", - "\n", - "i0 & i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "i0 & i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "0->1\n", - "\n", - "\n", - "\n", - "!i0 & i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "!i0 & i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "0->0\n", - "\n", - "\n", - "\n", - "i0 & !i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "i0 & !i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "0->0\n", - "\n", - "\n", - "\n", - "!i0 & !i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "!i0 & !i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n" ], "text/plain": [ - " *' at 0x7fb9646b7630> >" + " *' at 0x7f01fc1b5bd0> >" ] }, "metadata": {}, @@ -2004,81 +2011,81 @@ "\n", "\n", - "\n", "\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "0\n", - "\n", - "0\n", + "\n", + "0\n", "\n", "\n", "\n", "I->0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "0->0\n", - "\n", - "\n", - "\n", - "i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "1\n", - "\n", - "1\n", + "\n", + "1\n", "\n", "\n", "\n", "0->1\n", - "\n", - "\n", - "\n", - "!i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "!i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "1->0\n", - "\n", - "\n", - "\n", - "i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "1->1\n", - "\n", - "\n", - "\n", - "!i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "!i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n" ], "text/plain": [ - " *' at 0x7fb9646b77b0> >" + " *' at 0x7f01fc1b5cc0> >" ] }, "metadata": {}, @@ -2097,81 +2104,81 @@ "\n", "\n", - "\n", "\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "0\n", - "\n", - "0\n", + "\n", + "0\n", "\n", "\n", "\n", "I->0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "0->0\n", - "\n", - "\n", - "\n", - "!i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "!i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "1\n", - "\n", - "1\n", + "\n", + "1\n", "\n", "\n", "\n", "0->1\n", - "\n", - "\n", - "\n", - "i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "1->0\n", - "\n", - "\n", - "\n", - "!i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "!i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "1->1\n", - "\n", - "\n", - "\n", - "i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n" ], "text/plain": [ - " *' at 0x7fb9646b7f00> >" + " *' at 0x7f01fc2a8b70> >" ] }, "metadata": {}, @@ -2190,125 +2197,125 @@ "\n", "\n", - "\n", "\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "0\n", - "\n", - "0\n", + "\n", + "0\n", "\n", "\n", "\n", "I->0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "0->0\n", - "\n", - "\n", - "\n", - "i0 & i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "i0 & i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "0->0\n", - "\n", - "\n", - "\n", - "!i0 & i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "!i0 & i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "1\n", - "\n", - "1\n", + "\n", + "1\n", "\n", "\n", "\n", "0->1\n", - "\n", - "\n", - "\n", - "i0 & !i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "i0 & !i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "0->1\n", - "\n", - "\n", - "\n", - "!i0 & !i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "!i0 & !i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "1->0\n", - "\n", - "\n", - "\n", - "i0 & i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "i0 & i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "1->0\n", - "\n", - "\n", - "\n", - "!i0 & i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "!i0 & i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "1->1\n", - "\n", - "\n", - "\n", - "i0 & !i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "i0 & !i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "1->1\n", - "\n", - "\n", - "\n", - "!i0 & !i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "!i0 & !i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n" ], "text/plain": [ - " *' at 0x7fb9646b74b0> >" + " *' at 0x7f01fc1b5de0> >" ] }, "metadata": {}, @@ -2342,6 +2349,7 @@ }, { "cell_type": "markdown", + "id": "7ee86443", "metadata": {}, "source": [ "If needed, a separated Mealy machine can be turned into game shape using `split_sepearated_mealy()`, which is more efficient than `split_2step()`." @@ -2350,6 +2358,7 @@ { "cell_type": "code", "execution_count": 5, + "id": "80510b01", "metadata": {}, "outputs": [ { @@ -2358,260 +2367,260 @@ "
\n", "\n", - "\n", "\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "0\n", - "\n", - "0\n", + "\n", + "0\n", "\n", "\n", "\n", "I->0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "0->0\n", - "\n", - "\n", - "\n", - "i0 & i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "i0 & i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "0->0\n", - "\n", - "\n", - "\n", - "!i0 & i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "!i0 & i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "1\n", - "\n", - "1\n", + "\n", + "1\n", "\n", "\n", "\n", "0->1\n", - "\n", - "\n", - "\n", - "i0 & !i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "i0 & !i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "0->1\n", - "\n", - "\n", - "\n", - "!i0 & !i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "!i0 & !i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "1->0\n", - "\n", - "\n", - "\n", - "i0 & i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "i0 & i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "1->0\n", - "\n", - "\n", - "\n", - "!i0 & i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "!i0 & i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "1->1\n", - "\n", - "\n", - "\n", - "i0 & !i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "i0 & !i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "1->1\n", - "\n", - "\n", - "\n", - "!i0 & !i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "!i0 & !i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "
\n", "\n", - "\n", "\n", - "\n", - "\n", - "\n", - "t\n", - "[all]\n", + "\n", + "\n", + "\n", + "t\n", + "[all]\n", "\n", "\n", "\n", "0\n", - "\n", - "0\n", + "\n", + "0\n", "\n", "\n", "\n", "I->0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "2\n", - "\n", - "2\n", + "\n", + "2\n", "\n", "\n", "\n", "0->2\n", - "\n", - "\n", - "i0 & !i1\n", + "\n", + "\n", + "i0 & !i1\n", "\n", "\n", "\n", "0->2\n", - "\n", - "\n", - "!i0 & !i1\n", + "\n", + "\n", + "!i0 & !i1\n", "\n", "\n", "\n", "3\n", - "\n", - "3\n", + "\n", + "3\n", "\n", "\n", "\n", "0->3\n", - "\n", - "\n", - "i0 & i1\n", + "\n", + "\n", + "i0 & i1\n", "\n", "\n", "\n", "0->3\n", - "\n", - "\n", - "!i0 & i1\n", + "\n", + "\n", + "!i0 & i1\n", "\n", "\n", "\n", "1\n", - "\n", - "1\n", + "\n", + "1\n", "\n", "\n", "\n", "2->1\n", - "\n", - "\n", - "o0\n", + "\n", + "\n", + "o0\n", "\n", "\n", "\n", "3->0\n", - "\n", - "\n", - "o0\n", + "\n", + "\n", + "o0\n", "\n", "\n", "\n", "4\n", - "\n", - "4\n", + "\n", + "4\n", "\n", "\n", "\n", "1->4\n", - "\n", - "\n", - "i0 & i1\n", + "\n", + "\n", + "i0 & i1\n", "\n", "\n", "\n", "1->4\n", - "\n", - "\n", - "!i0 & i1\n", + "\n", + "\n", + "!i0 & i1\n", "\n", "\n", "\n", "5\n", - "\n", - "5\n", + "\n", + "5\n", "\n", "\n", "\n", "1->5\n", - "\n", - "\n", - "i0 & !i1\n", + "\n", + "\n", + "i0 & !i1\n", "\n", "\n", "\n", "1->5\n", - "\n", - "\n", - "!i0 & !i1\n", + "\n", + "\n", + "!i0 & !i1\n", "\n", "\n", "\n", "4->0\n", - "\n", - "\n", - "!o0\n", + "\n", + "\n", + "!o0\n", "\n", "\n", "\n", "5->1\n", - "\n", - "\n", - "!o0\n", + "\n", + "\n", + "!o0\n", "\n", "\n", "\n", @@ -2631,6 +2640,7 @@ }, { "cell_type": "markdown", + "id": "8f97aa04", "metadata": {}, "source": [ "# Converting the separated Mealy machine to AIG\n", @@ -2643,6 +2653,7 @@ { "cell_type": "code", "execution_count": 6, + "id": "9c6d9e8b", "metadata": {}, "outputs": [ { @@ -2651,60 +2662,60 @@ "\n", "\n", - "\n", "\n", - "\n", + "\n", "\n", - "\n", + "\n", "\n", "\n", "6\n", - "\n", - "L0_out\n", + "\n", + "L0_out\n", "\n", "\n", "\n", "o0\n", - "\n", - "o0\n", + "\n", + "o0\n", "\n", "\n", "\n", "6->o0:s\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "L0\n", - "\n", - "L0_in\n", + "\n", + "L0_in\n", "\n", "\n", "\n", "2\n", - "\n", - "i1\n", + "\n", + "i1\n", "\n", "\n", "\n", "2->L0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "4\n", - "\n", - "i0\n", + "\n", + "i0\n", "\n", "\n", "\n" ], "text/plain": [ - " >" + " >" ] }, "metadata": {}, @@ -2718,6 +2729,7 @@ }, { "cell_type": "markdown", + "id": "d67f8bce", "metadata": {}, "source": [ "While we are at it, let us mention that you can render those circuits horizontally as follows:" @@ -2726,6 +2738,7 @@ { "cell_type": "code", "execution_count": 7, + "id": "3a363374", "metadata": {}, "outputs": [ { @@ -2734,54 +2747,54 @@ "\n", "\n", - "\n", "\n", - "\n", + "\n", "\n", - "\n", + "\n", "\n", "\n", "6\n", - "\n", - "L0_out\n", + "\n", + "L0_out\n", "\n", "\n", "\n", "o0\n", - "\n", - "o0\n", + "\n", + "o0\n", "\n", "\n", "\n", "6->o0:w\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "L0\n", - "\n", - "L0_in\n", + "\n", + "L0_in\n", "\n", "\n", "\n", "2\n", - "\n", - "i1\n", + "\n", + "i1\n", "\n", "\n", "\n", "2->L0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "4\n", - "\n", - "i0\n", + "\n", + "i0\n", "\n", "\n", "\n" @@ -2801,6 +2814,7 @@ }, { "cell_type": "markdown", + "id": "e4f607c3", "metadata": {}, "source": [ "To encode the circuit in the AIGER format (ASCII version) use:" @@ -2809,6 +2823,7 @@ { "cell_type": "code", "execution_count": 8, + "id": "564f7d0b", "metadata": {}, "outputs": [ { @@ -2832,6 +2847,7 @@ }, { "cell_type": "markdown", + "id": "cf2d4831", "metadata": {}, "source": [ "# Adding more inputs and outputs by force" @@ -2839,6 +2855,7 @@ }, { "cell_type": "markdown", + "id": "874a108e", "metadata": {}, "source": [ "It can happen that propositions declared as output are ommited in the aig circuit (either because they are not part of the specification, or because they do not appear in the winning strategy). In that case those \n", @@ -2850,6 +2867,7 @@ { "cell_type": "code", "execution_count": 9, + "id": "1fc4c566", "metadata": {}, "outputs": [ { @@ -2858,159 +2876,159 @@ "\n", "\n", - "\n", "\n", - "\n", - "\n", - "\n", - "Inf(\n", - "\n", - ") | Fin(\n", - "\n", - ")\n", - "[Streett 1]\n", + "\n", + "\n", + "\n", + "Inf(\n", + "\n", + ") | Fin(\n", + "\n", + ")\n", + "[Streett 1]\n", "\n", "\n", "\n", "3\n", - "\n", - "3\n", + "\n", + "3\n", "\n", "\n", "\n", "I->3\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "6\n", - "\n", - "6\n", + "\n", + "6\n", "\n", "\n", "\n", "3->6\n", - "\n", - "\n", - "i0\n", - "\n", + "\n", + "\n", + "i0\n", + "\n", "\n", "\n", "\n", "7\n", - "\n", - "7\n", + "\n", + "7\n", "\n", "\n", "\n", "3->7\n", - "\n", - "\n", - "!i0\n", - "\n", + "\n", + "\n", + "!i0\n", + "\n", "\n", "\n", "\n", "0\n", - "\n", - "0\n", + "\n", + "0\n", "\n", "\n", "\n", "4\n", - "\n", - "4\n", + "\n", + "4\n", "\n", "\n", "\n", "0->4\n", - "\n", - "\n", - "1\n", - "\n", + "\n", + "\n", + "1\n", + "\n", "\n", "\n", "\n", "4->0\n", - "\n", - "\n", - "1\n", - "\n", + "\n", + "\n", + "1\n", + "\n", "\n", "\n", "\n", "1\n", - "\n", - "1\n", + "\n", + "1\n", "\n", "\n", "\n", "5\n", - "\n", - "5\n", + "\n", + "5\n", "\n", "\n", "\n", "1->5\n", - "\n", - "\n", - "1\n", - "\n", + "\n", + "\n", + "1\n", + "\n", "\n", "\n", "\n", "5->1\n", - "\n", - "\n", - "!o0\n", - "\n", + "\n", + "\n", + "!o0\n", + "\n", "\n", "\n", "\n", "2\n", - "\n", - "2\n", + "\n", + "2\n", "\n", "\n", "\n", "2->6\n", - "\n", - "\n", - "1\n", - "\n", + "\n", + "\n", + "1\n", + "\n", "\n", "\n", "\n", "6->0\n", - "\n", - "\n", - "o0\n", - "\n", + "\n", + "\n", + "o0\n", + "\n", "\n", "\n", "\n", "6->2\n", - "\n", - "\n", - "!o0\n", - "\n", + "\n", + "\n", + "!o0\n", + "\n", "\n", "\n", "\n", "7->1\n", - "\n", - "\n", - "!o0\n", - "\n", + "\n", + "\n", + "!o0\n", + "\n", "\n", "\n", "\n" ], "text/plain": [ - " *' at 0x7fb9646b7570> >" + " *' at 0x7f01fc14bb10> >" ] }, "metadata": {}, @@ -3022,112 +3040,112 @@ "\n", "\n", - "\n", "\n", "\n", "\n", - "\n", - "t\n", - "[all]\n", + "\n", + "t\n", + "[all]\n", "\n", "\n", "\n", "0\n", - "\n", - "0\n", + "\n", + "0\n", "\n", "\n", "\n", "I->0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "2\n", - "\n", - "2\n", + "\n", + "2\n", "\n", "\n", "\n", "0->2\n", - "\n", - "\n", - "i0\n", + "\n", + "\n", + "i0\n", "\n", "\n", "\n", "4\n", - "\n", - "4\n", + "\n", + "4\n", "\n", "\n", "\n", "0->4\n", - "\n", - "\n", - "!i0\n", + "\n", + "\n", + "!i0\n", "\n", "\n", "\n", "1\n", - "\n", - "1\n", + "\n", + "1\n", "\n", "\n", "\n", "2->1\n", - "\n", - "\n", - "o0\n", + "\n", + "\n", + "o0\n", "\n", "\n", "\n", "3\n", - "\n", - "3\n", + "\n", + "3\n", "\n", "\n", "\n", "4->3\n", - "\n", - "\n", - "!o0\n", + "\n", + "\n", + "!o0\n", "\n", "\n", "\n", "5\n", - "\n", - "5\n", + "\n", + "5\n", "\n", "\n", "\n", "1->5\n", - "\n", - "\n", - "1\n", + "\n", + "\n", + "1\n", "\n", "\n", "\n", "5->1\n", - "\n", - "\n", - "1\n", + "\n", + "\n", + "1\n", "\n", "\n", "\n", "3->4\n", - "\n", - "\n", - "1\n", + "\n", + "\n", + "1\n", "\n", "\n", "\n" ], "text/plain": [ - " *' at 0x7fb9646b7630> >" + " *' at 0x7f01fc12f090> >" ] }, "metadata": {}, @@ -3139,144 +3157,144 @@ "
\n", "\n", - "\n", "\n", "\n", - "\n", - "\n", - "t\n", - "[all]\n", + " viewBox=\"0.00 0.00 282.00 148.80\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n", + "\n", + "\n", + "t\n", + "[all]\n", "\n", "\n", "\n", "0\n", - "\n", - "0\n", + "\n", + "0\n", "\n", "\n", "\n", "I->0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "2\n", - "\n", - "2\n", + "\n", + "2\n", "\n", "\n", "\n", "0->2\n", - "\n", - "\n", - "i0\n", + "\n", + "\n", + "i0\n", "\n", "\n", "\n", "3\n", - "\n", - "3\n", + "\n", + "3\n", "\n", "\n", "\n", "0->3\n", - "\n", - "\n", - "!i0\n", + "\n", + "\n", + "!i0\n", "\n", "\n", "\n", "2->0\n", - "\n", - "\n", - "o0\n", + "\n", + "\n", + "o0\n", "\n", "\n", "\n", "1\n", - "\n", - "1\n", + "\n", + "1\n", "\n", "\n", "\n", "3->1\n", - "\n", - "\n", - "!o0\n", + "\n", + "\n", + "!o0\n", "\n", "\n", "\n", "1->3\n", - "\n", - "\n", - "1\n", + "\n", + "\n", + "1\n", "\n", "\n", "\n", "
\n", "\n", - "\n", "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "0\n", - "\n", - "0\n", + "\n", + "0\n", "\n", "\n", "\n", "I->0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "0->0\n", - "\n", - "\n", - "\n", - "i0\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "i0\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "1\n", - "\n", - "1\n", + "\n", + "1\n", "\n", "\n", "\n", "0->1\n", - "\n", - "\n", + "\n", + "\n", "\n", - "!i0\n", - "/\n", + "!i0\n", + "/\n", "\n", - "!o0\n", + "!o0\n", "\n", "\n", "\n", "1->1\n", - "\n", - "\n", + "\n", + "\n", "\n", - "1\n", - "/\n", + "1\n", + "/\n", "\n", - "!o0\n", + "!o0\n", "\n", "\n", "\n", @@ -3295,72 +3313,72 @@ "\n", "\n", - "\n", "\n", "\n", + " viewBox=\"0.00 0.00 142.70 352.00\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n", "\n", - "\n", + "\n", "\n", "\n", "4\n", - "\n", - "L0_out\n", + "\n", + "L0_out\n", "\n", "\n", "\n", "6\n", - "\n", - "6\n", + "\n", + "6\n", "\n", "\n", "\n", "4->6\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "L0\n", - "\n", - "L0_in\n", + "\n", + "L0_in\n", "\n", "\n", "\n", "6->L0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "o0\n", - "\n", - "o0\n", + "\n", + "o0\n", "\n", "\n", "\n", "6->o0:s\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "2\n", - "\n", - "i0\n", + "\n", + "i0\n", "\n", "\n", "\n", "2->6\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n" ], "text/plain": [ - " >" + " >" ] }, "metadata": {}, @@ -3382,6 +3400,7 @@ }, { "cell_type": "markdown", + "id": "f8dab019", "metadata": {}, "source": [ "To force the presence of extra variables in the circuit, they can be passed to `mealy_machine_to_aig()`." @@ -3390,6 +3409,7 @@ { "cell_type": "code", "execution_count": 10, + "id": "091d7c97", "metadata": {}, "outputs": [ { @@ -3398,96 +3418,96 @@ "\n", "\n", - "\n", "\n", - "\n", + "\n", "\n", - "\n", + "\n", "\n", "\n", "6\n", - "\n", - "L0_out\n", + "\n", + "L0_out\n", "\n", "\n", "\n", "8\n", - "\n", - "8\n", + "\n", + "8\n", "\n", "\n", "\n", "6->8\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "L0\n", - "\n", - "L0_in\n", + "\n", + "L0_in\n", "\n", "\n", "\n", "8->L0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "o0\n", - "\n", - "o0\n", + "\n", + "o0\n", "\n", "\n", "\n", "8->o0:s\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "o1\n", - "\n", - "o1\n", + "\n", + "o1\n", "\n", "\n", "\n", "2\n", - "\n", - "i0\n", + "\n", + "i0\n", "\n", "\n", "\n", "2->8\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "4\n", - "\n", - "i1\n", + "\n", + "i1\n", "\n", "\n", "\n", "0\n", - "\n", - "False\n", + "\n", + "False\n", "\n", "\n", "\n", "0->o1:s\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n" ], "text/plain": [ - " >" + " >" ] }, "metadata": {}, @@ -3500,6 +3520,7 @@ }, { "cell_type": "markdown", + "id": "364c8d76", "metadata": {}, "source": [ "# Combining Mealy machines\n", @@ -3519,6 +3540,7 @@ { "cell_type": "code", "execution_count": 11, + "id": "57b3b51d", "metadata": {}, "outputs": [ { @@ -3534,150 +3556,150 @@ "
\n", "\n", - "\n", "\n", - "\n", - "\n", - "\n", - "Inf(\n", - "\n", - ") | Fin(\n", - "\n", - ")\n", - "[Streett 1]\n", + "\n", + "\n", + "\n", + "Inf(\n", + "\n", + ") | Fin(\n", + "\n", + ")\n", + "[Streett 1]\n", "\n", "\n", "\n", "0\n", - "\n", - "0\n", + "\n", + "0\n", "\n", "\n", "\n", "I->0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "1\n", - "\n", - "1\n", + "\n", + "1\n", "\n", "\n", "\n", "0->1\n", - "\n", - "\n", - "(!i0 & !i1) | (i0 & i1)\n", - "\n", + "\n", + "\n", + "(!i0 & !i1) | (i0 & i1)\n", + "\n", "\n", "\n", "\n", "2\n", - "\n", - "2\n", + "\n", + "2\n", "\n", "\n", "\n", "0->2\n", - "\n", - "\n", - "(!i0 & i1) | (i0 & !i1)\n", - "\n", + "\n", + "\n", + "(!i0 & i1) | (i0 & !i1)\n", + "\n", "\n", "\n", "\n", "1->0\n", - "\n", - "\n", - "!o0\n", - "\n", + "\n", + "\n", + "!o0\n", + "\n", "\n", "\n", "\n", "2->0\n", - "\n", - "\n", - "o0\n", - "\n", + "\n", + "\n", + "o0\n", + "\n", "\n", "\n", "\n", "
\n", "\n", - "\n", "\n", - "\n", - "\n", - "\n", - "Inf(\n", - "\n", - ") | Fin(\n", - "\n", - ")\n", - "[Streett 1]\n", + "\n", + "\n", + "\n", + "Inf(\n", + "\n", + ") | Fin(\n", + "\n", + ")\n", + "[Streett 1]\n", "\n", "\n", "\n", "0\n", - "\n", - "0\n", + "\n", + "0\n", "\n", "\n", "\n", "I->0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "1\n", - "\n", - "1\n", + "\n", + "1\n", "\n", "\n", "\n", "0->1\n", - "\n", - "\n", - "(!i0 & !i1) | (i0 & i1)\n", - "\n", + "\n", + "\n", + "(!i0 & !i1) | (i0 & i1)\n", + "\n", "\n", "\n", "\n", "2\n", - "\n", - "2\n", + "\n", + "2\n", "\n", "\n", "\n", "0->2\n", - "\n", - "\n", - "(!i0 & i1) | (i0 & !i1)\n", - "\n", + "\n", + "\n", + "(!i0 & i1) | (i0 & !i1)\n", + "\n", "\n", "\n", "\n", "1->0\n", - "\n", - "\n", - "o1\n", - "\n", + "\n", + "\n", + "o1\n", + "\n", "\n", "\n", "\n", "2->0\n", - "\n", - "\n", - "!o1\n", - "\n", + "\n", + "\n", + "!o1\n", + "\n", "\n", "\n", "\n", @@ -3703,94 +3725,94 @@ "
\n", "\n", - "\n", "\n", - "\n", + "\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "0\n", - "\n", - "0\n", + "\n", + "0\n", "\n", "\n", "\n", "I->0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "0->0\n", - "\n", - "\n", - "\n", - "(!i0 & !i1) | (i0 & i1)\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "(!i0 & !i1) | (i0 & i1)\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "0->0\n", - "\n", - "\n", - "\n", - "(!i0 & i1) | (i0 & !i1)\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "(!i0 & i1) | (i0 & !i1)\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "
\n", "\n", - "\n", "\n", - "\n", + "\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "0\n", - "\n", - "0\n", + "\n", + "0\n", "\n", "\n", "\n", "I->0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "0->0\n", - "\n", - "\n", - "\n", - "(!i0 & !i1) | (i0 & i1)\n", - "/\n", - "\n", - "o1\n", + "\n", + "\n", + "\n", + "(!i0 & !i1) | (i0 & i1)\n", + "/\n", + "\n", + "o1\n", "\n", "\n", "\n", "0->0\n", - "\n", - "\n", - "\n", - "(!i0 & i1) | (i0 & !i1)\n", - "/\n", - "\n", - "!o1\n", + "\n", + "\n", + "\n", + "(!i0 & i1) | (i0 & !i1)\n", + "/\n", + "\n", + "!o1\n", "\n", "\n", "\n", @@ -3816,108 +3838,108 @@ "\n", "\n", - "\n", "\n", "\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "6\n", - "\n", - "6\n", + "\n", + "6\n", "\n", "\n", "\n", "10\n", - "\n", - "10\n", + "\n", + "10\n", "\n", "\n", "\n", "6->10\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "8\n", - "\n", - "8\n", + "\n", + "8\n", "\n", "\n", "\n", "8->10\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "o0\n", - "\n", - "o0\n", + "\n", + "o0\n", "\n", "\n", "\n", "10->o0:s\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "o1\n", - "\n", - "o1\n", + "\n", + "o1\n", "\n", "\n", "\n", "10->o1:s\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "2\n", - "\n", - "i0\n", + "\n", + "i0\n", "\n", "\n", "\n", "2->6\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "2->8\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "4\n", - "\n", - "i1\n", + "\n", + "i1\n", "\n", "\n", "\n", "4->6\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "4->8\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n" ], "text/plain": [ - " >" + " >" ] }, "metadata": {}, @@ -3936,53 +3958,53 @@ "\n", "\n", - "\n", "\n", - "\n", + "\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "0\n", - "\n", - "0,0\n", + "\n", + "0,0\n", "\n", "\n", "\n", "I->0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "0->0\n", - "\n", - "\n", - "\n", - "(!i0 & !i1) | (i0 & i1)\n", - "/\n", - "\n", - "!o0 & o1\n", + "\n", + "\n", + "\n", + "(!i0 & !i1) | (i0 & i1)\n", + "/\n", + "\n", + "!o0 & o1\n", "\n", "\n", "\n", "0->0\n", - "\n", - "\n", - "\n", - "(!i0 & i1) | (i0 & !i1)\n", - "/\n", - "\n", - "o0 & !o1\n", + "\n", + "\n", + "\n", + "(!i0 & i1) | (i0 & !i1)\n", + "/\n", + "\n", + "o0 & !o1\n", "\n", "\n", "\n" ], "text/plain": [ - " *' at 0x7fb9646b7c60> >" + " *' at 0x7f01fc14bae0> >" ] }, "metadata": {}, @@ -3994,108 +4016,108 @@ "\n", "\n", - "\n", "\n", "\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "6\n", - "\n", - "6\n", + "\n", + "6\n", "\n", "\n", "\n", "10\n", - "\n", - "10\n", + "\n", + "10\n", "\n", "\n", "\n", "6->10\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "8\n", - "\n", - "8\n", + "\n", + "8\n", "\n", "\n", "\n", "8->10\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "o0\n", - "\n", - "o0\n", + "\n", + "o0\n", "\n", "\n", "\n", "10->o0:s\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "o1\n", - "\n", - "o1\n", + "\n", + "o1\n", "\n", "\n", "\n", "10->o1:s\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "2\n", - "\n", - "i0\n", + "\n", + "i0\n", "\n", "\n", "\n", "2->6\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "2->8\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "4\n", - "\n", - "i1\n", + "\n", + "i1\n", "\n", "\n", "\n", "4->6\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "4->8\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n" ], "text/plain": [ - " >" + " >" ] }, "metadata": {}, @@ -4129,6 +4151,7 @@ }, { "cell_type": "markdown", + "id": "7d5a8a32", "metadata": {}, "source": [ "# Reading an AIGER-file\n", @@ -4143,6 +4166,7 @@ { "cell_type": "code", "execution_count": 12, + "id": "9da1f39e", "metadata": {}, "outputs": [], "source": [ @@ -4163,6 +4187,7 @@ { "cell_type": "code", "execution_count": 13, + "id": "7295f20a", "metadata": {}, "outputs": [ { @@ -4171,108 +4196,108 @@ "\n", "\n", - "\n", "\n", "\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "6\n", - "\n", - "6\n", + "\n", + "6\n", "\n", "\n", "\n", "10\n", - "\n", - "10\n", + "\n", + "10\n", "\n", "\n", "\n", "6->10\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "o1\n", - "\n", - "d\n", + "\n", + "d\n", "\n", "\n", "\n", "6->o1:s\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "8\n", - "\n", - "8\n", + "\n", + "8\n", "\n", "\n", "\n", "8->10\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "o0\n", - "\n", - "c\n", + "\n", + "c\n", "\n", "\n", "\n", "10->o0:s\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "2\n", - "\n", - "a\n", + "\n", + "a\n", "\n", "\n", "\n", "2->6\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "2->8\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "4\n", - "\n", - "b\n", + "\n", + "b\n", "\n", "\n", "\n", "4->6\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "4->8\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n" ], "text/plain": [ - " >" + " >" ] }, "metadata": {}, @@ -4287,6 +4312,7 @@ { "cell_type": "code", "execution_count": 14, + "id": "730952f7", "metadata": {}, "outputs": [ { @@ -4315,6 +4341,7 @@ { "cell_type": "code", "execution_count": 15, + "id": "38b5b8a1", "metadata": {}, "outputs": [ { @@ -4331,6 +4358,7 @@ }, { "cell_type": "markdown", + "id": "6bde5eac", "metadata": {}, "source": [ "An AIG circuit can be transformed into a monitor/Mealy machine. This can be used for instance to check that it does not intersect the negation of the specification." @@ -4339,6 +4367,7 @@ { "cell_type": "code", "execution_count": 16, + "id": "14f89c9b", "metadata": {}, "outputs": [ { @@ -4347,52 +4376,52 @@ "\n", "\n", - "\n", "\n", - "\n", + "\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "0\n", - "\n", - "0\n", + "\n", + "0\n", "\n", "\n", "\n", "I->0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "0->0\n", - "\n", - "\n", - "\n", - "!a & !b\n", - "/\n", - "\n", - "!c & !d\n", - "\n", - "a & b\n", - "/\n", - "\n", - "!c & d\n", - "\n", - "(!a & b) | (a & !b)\n", - "/\n", - "\n", - "c & !d\n", + "\n", + "\n", + "\n", + "!a & !b\n", + "/\n", + "\n", + "!c & !d\n", + "\n", + "a & b\n", + "/\n", + "\n", + "!c & d\n", + "\n", + "(!a & b) | (a & !b)\n", + "/\n", + "\n", + "c & !d\n", "\n", "\n", "\n" ], "text/plain": [ - " *' at 0x7fb9646c8360> >" + " *' at 0x7f01fc1b5f90> >" ] }, "execution_count": 16, @@ -4406,6 +4435,7 @@ }, { "cell_type": "markdown", + "id": "e1f01aa0", "metadata": {}, "source": [ "Note that the generation of aiger circuits from Mealy machines is flexible and accepts separated Mealy machines\n", @@ -4415,6 +4445,7 @@ { "cell_type": "code", "execution_count": 17, + "id": "93e1fc70", "metadata": {}, "outputs": [ { @@ -4423,114 +4454,114 @@ "
\n", "\n", - "\n", "\n", - "\n", + "\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "0\n", - "\n", - "0\n", + "\n", + "0\n", "\n", "\n", "\n", "I->0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "0->0\n", - "\n", - "\n", - "\n", - "(!i0 & !i1) | (i0 & i1)\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "(!i0 & !i1) | (i0 & i1)\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "0->0\n", - "\n", - "\n", - "\n", - "(!i0 & i1) | (i0 & !i1)\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "(!i0 & i1) | (i0 & !i1)\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "
\n", "\n", - "\n", "\n", - "\n", - "\n", - "\n", - "t\n", - "[all]\n", + "\n", + "\n", + "\n", + "t\n", + "[all]\n", "\n", "\n", "\n", "0\n", - "\n", - "0\n", + "\n", + "0\n", "\n", "\n", "\n", "I->0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "1\n", - "\n", - "1\n", + "\n", + "1\n", "\n", "\n", "\n", "0->1\n", - "\n", - "\n", - "(!i0 & !i1) | (i0 & i1)\n", + "\n", + "\n", + "(!i0 & !i1) | (i0 & i1)\n", "\n", "\n", "\n", "2\n", - "\n", - "2\n", + "\n", + "2\n", "\n", "\n", "\n", "0->2\n", - "\n", - "\n", - "(!i0 & i1) | (i0 & !i1)\n", + "\n", + "\n", + "(!i0 & i1) | (i0 & !i1)\n", "\n", "\n", "\n", "1->0\n", - "\n", - "\n", - "!o0\n", + "\n", + "\n", + "!o0\n", "\n", "\n", "\n", "2->0\n", - "\n", - "\n", - "o0\n", + "\n", + "\n", + "o0\n", "\n", "\n", "\n", @@ -4562,6 +4593,7 @@ { "cell_type": "code", "execution_count": 18, + "id": "6cb96c81", "metadata": {}, "outputs": [ { @@ -4570,180 +4602,180 @@ "
\n", "\n", - "\n", "\n", "\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "6\n", - "\n", - "6\n", + "\n", + "6\n", "\n", "\n", "\n", "10\n", - "\n", - "10\n", + "\n", + "10\n", "\n", "\n", "\n", "6->10\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "8\n", - "\n", - "8\n", + "\n", + "8\n", "\n", "\n", "\n", "8->10\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "o0\n", - "\n", - "o0\n", + "\n", + "o0\n", "\n", "\n", "\n", "10->o0:s\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "2\n", - "\n", - "i0\n", + "\n", + "i0\n", "\n", "\n", "\n", "2->6\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "2->8\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "4\n", - "\n", - "i1\n", + "\n", + "i1\n", "\n", "\n", "\n", "4->6\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "4->8\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "
\n", "\n", - "\n", "\n", "\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "6\n", - "\n", - "6\n", + "\n", + "6\n", "\n", "\n", "\n", "10\n", - "\n", - "10\n", + "\n", + "10\n", "\n", "\n", "\n", "6->10\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "8\n", - "\n", - "8\n", + "\n", + "8\n", "\n", "\n", "\n", "8->10\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "o0\n", - "\n", - "o0\n", + "\n", + "o0\n", "\n", "\n", "\n", "10->o0:s\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "2\n", - "\n", - "i0\n", + "\n", + "i0\n", "\n", "\n", "\n", "2->6\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "2->8\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "4\n", - "\n", - "i1\n", + "\n", + "i1\n", "\n", "\n", "\n", "4->6\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "4->8\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", @@ -4764,7 +4796,7 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 3", + "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, @@ -4778,7 +4810,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.7.3" + "version": "3.8.10" } }, "nbformat": 4, diff --git a/tests/python/synthesis.py b/tests/python/synthesis.py index 1b1cf4fbb..559dc2d24 100644 --- a/tests/python/synthesis.py +++ b/tests/python/synthesis.py @@ -37,16 +37,16 @@ Start: 0 AP: 1 "a" acc-name: Streett 1 Acceptance: 2 Fin(0) | Inf(1) -properties: trans-labels explicit-labels state-acc colored complete +properties: trans-labels explicit-labels trans-acc colored complete properties: deterministic spot-state-player: 0 1 1 controllable-AP: --BODY-- -State: 0 {0} -[!0] 1 -[0] 2 -State: 1 {0} -[t] 0 -State: 2 {1} -[t] 0 +State: 0 +[!0] 1 {0} +[0] 2 {1} +State: 1 +[t] 0 {0} +State: 2 +[t] 0 {1} --END--""") From 06b73c39faedcb02016bea2e12d99569b6fb5c98 Mon Sep 17 00:00:00 2001 From: Philipp Schlehuber-Caissier Date: Wed, 6 Apr 2022 21:16:35 +0200 Subject: [PATCH 030/337] +ud option of mealy_machine_to_aig received wrong value Also aiger received a tracing option for debugging * spot/twaalgos/aiger.cc: Here * tests/core/ltlsynt.test: Test --- spot/twaalgos/aiger.cc | 127 +++++++++++++++++++++++++++------------- tests/core/ltlsynt.test | 81 +++++++++++++++++++++++-- 2 files changed, 164 insertions(+), 44 deletions(-) diff --git a/spot/twaalgos/aiger.cc b/spot/twaalgos/aiger.cc index d846e678c..660d5b46a 100644 --- a/spot/twaalgos/aiger.cc +++ b/spot/twaalgos/aiger.cc @@ -30,6 +30,7 @@ #include #include #include +#include #include #include @@ -41,6 +42,13 @@ #define STR_(x) STR(x) #define STR_LINE STR_(__LINE__) +//#define TRACE +#ifdef TRACE +# define trace std::cerr +#else +# define trace while (0) std::cerr +#endif + namespace { using namespace spot; @@ -459,6 +467,7 @@ namespace spot aig::roll_back_(safe_point sf, bool do_stash) { // todo specialise for safe_all? + trace << "Roll back to sf: " << sf.first << "; " << sf.second << '\n'; safe_stash ss; auto& [gates, vardict, negs] = ss; if (do_stash) @@ -480,6 +489,7 @@ namespace spot // Copy the gates std::copy(and_gates_.begin()+sf.second, and_gates_.end(), gates.begin()); + trace << "Safed " << gates.size() << '\n'; } // 1. Delete all literals // max_var_old was used before @@ -489,6 +499,8 @@ namespace spot // 2. Set back the gates and_gates_.erase(and_gates_.begin() + sf.second, and_gates_.end()); max_var_ = sf.first; + trace << "After rollback: \n" << and_gates_.size() << " gates and\n" + << max_var_ << " variables\n\n"; return ss; } @@ -497,6 +509,8 @@ namespace spot { // Do some check_ups auto& [gates, vardict, _] = ss; + trace << "Reapplying sf: " << sf.first << "; " << sf.second + << "\nwith " << gates.size() << " additional gates.\n\n"; assert(gates.size() == vardict.size()); assert(sf.first == max_var_); assert(sf.second == and_gates_.size()); @@ -511,6 +525,7 @@ namespace spot and_gates_.insert(and_gates_.end(), gates.begin(), gates.end()); max_var_ = new_max_var_; + trace << "New Ngates: " << num_gates() << '\n'; } void aig::set_output(unsigned i, unsigned v) @@ -698,7 +713,6 @@ namespace spot while ((prod = cond.next()) != bddfalse) plus_vars_.push_back(cube2var_(prod, use_split_off == 2 ? 0 : use_split_off)); - // Done building -> sum them return aig_or(plus_vars_); } @@ -709,11 +723,20 @@ namespace spot { // Before doing anything else, let us check if one the variables // already exists in which case we are done +#ifdef TRACE + trace << "encoding one of \n"; + for (const auto& c: c_alt) + trace << c << '\n'; +#endif + for (const bdd& cond : c_alt) { auto it = bdd2var_.find(cond.id()); if (it != bdd2var_.end()) - return it->second; + { + trace << "Condition already encoded -> Direct return\n\n"; + return it->second; + } } safe_point sf = get_safe_point_(); @@ -732,9 +755,6 @@ namespace spot && "Cannot convert the given method. " "Only 0,1 and 2 are currently supported"); - const auto negate = use_dual ? std::vector{false} - : std::vector{false, true}; - auto enc_1 = [&](const bdd& b, const char m) { @@ -751,41 +771,60 @@ namespace spot std::vector cond_parts; std::vector cond_vars; - for (bool do_negate : negate) - for (const bdd& b : c_alt) - { - bdd b_used = do_negate ? bdd_not(b) : b; - cond_parts.clear(); - split_cond_(b_used, - use_split_off != 1 ? use_split_off : 0, cond_parts); + //for (bool do_negate : (use_dual ? std::initializer_list{false, true} + // : std::initializer_list{false})) + for (unsigned neg_counter = 0; neg_counter <= 0 + use_dual; ++neg_counter) + { + bool do_negate = neg_counter; + for (const bdd& b : c_alt) + { + bdd b_used = do_negate ? bdd_not(b) : b; + cond_parts.clear(); + split_cond_(b_used, + use_split_off != 1 ? use_split_off : 0, cond_parts); - for (auto m : used_m) - { - cond_vars.clear(); - for (const bdd& cpart : cond_parts) - { - cond_vars.push_back(enc_1(cpart, m)); - if (num_gates() >= ngates_min) - break; // Cannot be optimal - } - // Compute the and if there is still hope - unsigned this_res = -1u; - if (num_gates() < ngates_min) - this_res = aig_and(cond_vars); - - if (num_gates() < ngates_min) - { - // This is the new best - res_var = do_negate ? aig_not(this_res) : this_res; - ngates_min = num_gates(); - ss_min = roll_back_(sf, true); - } - else - // Reset the computations - roll_back_(sf, false); - } // Encoding styles - } // alternatives - // end do_negate + for (auto m : used_m) + { + cond_vars.clear(); + for (const bdd& cpart : cond_parts) + { + cond_vars.push_back(enc_1(cpart, m)); + if (num_gates() >= ngates_min) + break; // Cannot be optimal + } + // Compute the and if there is still hope + auto this_res = -1u; + if (num_gates() < ngates_min) + this_res = aig_and(cond_vars); + // Check if after adding these gates + // the circuit is still smaller + if (num_gates() < ngates_min) + { + // This is the new best + assert(this_res != -1u); + res_var = do_negate ? aig_not(this_res) : this_res; + ngates_min = num_gates(); + trace << "Found new best encoding with\nneg: " + << do_negate << "\nmethod: " << (m == 0 ? "INF" + : "ISOP") + << "\nalt: " << b + << "\nNgates: " << num_gates() << "\n\n"; + ss_min = roll_back_(sf, true); + } + else + // Reset the computations + { + trace << "Method \nneg: " + << do_negate << "\nmethod: " << (m == 0 ? "INF" + : "ISOP") + << "\nalt: " << b + << "\nNgates: " << num_gates() + << " discarded.\n\n"; + roll_back_(sf, false); + } + } // Encoding styles + } // alternatives + } // end do_negate // Reapply the best result reapply_(sf, ss_min); @@ -1753,6 +1792,7 @@ namespace bool use_dual = false; bool use_dontcare = false; int use_split_off = 0; + std::string s; }; auto to_treat = [&mode]() @@ -1766,6 +1806,8 @@ namespace while (std::getline(s, buffer, ',')) { tr_opt this_opt; + // Store raw info + this_opt.s = buffer; std::stringstream s2; s2 << buffer; std::getline(s2, buffer2, '+'); @@ -1865,15 +1907,16 @@ namespace }; // Create the vars - std::vector alt_conds(amodedescr.use_dontcare ? 1 : 2); for (unsigned i = 0; i < n_outs; ++i) { + trace << "Assign out " << i << '\n'; if (circuit.num_gates() > min_gates) break; circuit.set_output(i, bdd2var(out[i], out_dc[i])); } for (unsigned i = 0; i < n_latches; ++i) { + trace << "Assign latch " << i << '\n'; if (circuit.num_gates() > min_gates) break; circuit.set_next_latch(i, bdd2var(latch[i], bddfalse)); @@ -1883,6 +1926,8 @@ namespace // Overwrite the stash if we generated less gates if (circuit.num_gates() < min_gates) { + trace << "New best mode: " << amodedescr.s + << " with Ngates: " << circuit.num_gates() << '\n'; min_gates = circuit.num_gates(); ss = circuit.roll_back_(sf, true); bdd2var_min = bdd2var; @@ -1892,6 +1937,8 @@ namespace } //Use the best sol circuit.reapply_(sf, ss); + trace << "Finished encoding, reasssigning\n" + << "Final gate count is " << circuit.num_gates() << '\n'; // Reset them for (unsigned i = 0; i < n_outs; ++i) circuit.set_output(i, bdd2var_min(out[i], out_dc[i])); diff --git a/tests/core/ltlsynt.test b/tests/core/ltlsynt.test index 03f7598c9..07e2690e7 100644 --- a/tests/core/ltlsynt.test +++ b/tests/core/ltlsynt.test @@ -470,10 +470,81 @@ i3 i3 o0 o0 o1 o1 EOF +ltlsynt -f "G((i0 && i1)<->X(o0)) && G((i2|i3)<->X(o1))" --outs="o0,o1"\ + --aiger=isop+ud --algo=lar --decompose=no --simpl=no >out +diff out exp + +cat >exp <X(o0)) && G((i2|i3)<->X(o1))" --outs="o0,o1"\ --aiger=isop --algo=lar --decompose=no --simpl=no >out diff out exp + cat >exp <X(o0)) && G((i2|i3)<->X(o1))" --outs="o0,o1"\ - --aiger=isop --algo=lar --decompose=yes --simpl=no >out + --aiger=isop+ud --algo=lar --decompose=yes --simpl=no >out diff out exp ltlsynt -f "G((i0 && i1)<->X(o0)) && G((i2|i3)<->X(o1))" --outs="o0,o1"\ - --aiger=isop --algo=lar --simpl=no >out + --aiger=isop+ud --algo=lar --simpl=no >out diff out exp # Issue #477 @@ -794,8 +865,10 @@ LTL='(((((G (((((((g_0) && (G (! (r_0)))) -> (F (! (g_0)))) && (((g_0) && && ((r_0) R (! (g_0)))) && (G ((r_0) -> (F (g_0))))) && ((r_1) R (! (g_1)))) && (G ((r_1) -> (F (g_1)))))' OUT='g_0, g_1' -ltlsynt --outs="$OUT" -f "$LTL" --aiger=both --algo=acd | grep "aag 8 2 2 2 4" -ltlsynt --outs="$OUT" -f "$LTL" --aiger=both --algo=lar | grep "aag 34 2 3 2 29" +ltlsynt --outs="$OUT" -f "$LTL" --aiger=both+ud\ + --algo=acd | grep "aag 8 2 2 2 4" +ltlsynt --outs="$OUT" -f "$LTL" --aiger=both+ud\ + --algo=lar | grep "aag 34 2 3 2 29" ltlsynt -f 'G(c) & (G(a) <-> GFb)' --outs=b,c --decompose=yes\ --verbose --realizability 2> out From 5f43c9bfcecabde80fb195e5ab47946b2345893e Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Fri, 8 Apr 2022 18:50:13 +0200 Subject: [PATCH 031/337] ltlsynt: implement --tlsf to call syfco automatically Fixes #473. * NEWS, doc/org/ltlsynt.org: Mention it. * bin/common_trans.cc, bin/common_trans.hh (read_stdout_of_command): New function. * bin/ltlsynt.cc: Implement the --tlsf option. * tests/core/syfco.test: New file. * tests/Makefile.am: Add it. --- NEWS | 4 ++ bin/common_trans.cc | 89 +++++++++++++++++++++++++++++++++++++++++++ bin/common_trans.hh | 8 +++- bin/ltlsynt.cc | 73 ++++++++++++++++++++++++++--------- doc/org/ltlsynt.org | 16 +++++--- tests/Makefile.am | 1 + tests/core/syfco.test | 48 +++++++++++++++++++++++ 7 files changed, 214 insertions(+), 25 deletions(-) create mode 100755 tests/core/syfco.test diff --git a/NEWS b/NEWS index 78a14561b..0d165bd95 100644 --- a/NEWS +++ b/NEWS @@ -17,6 +17,10 @@ New in spot 2.10.4.dev (net yet released) - autcross learned a --language-complemented option to assist in the case one is testing tools that complement automata. (issue #504). + - ltlsynt as a new option --tlsf that takes the filename of a TLSF + specification and calls syfco (which must be installed) to convert + it into an LTL formula. + Library: - The new function suffix_operator_normal_form() implements diff --git a/bin/common_trans.cc b/bin/common_trans.cc index a9b823ff4..9ab719a5b 100644 --- a/bin/common_trans.cc +++ b/bin/common_trans.cc @@ -27,6 +27,7 @@ #include #include #include +#include #if __has_include() #define HAVE_SPAWN_H 1 #include @@ -461,6 +462,93 @@ autproc_runner::round_automaton(spot::const_twa_graph_ptr aut, unsigned serial) filename_automaton.new_round(aut, serial); } + +std::string +read_stdout_of_command(char* const* args) +{ +#if HAVE_SPAWN_H + int cout_pipe[2]; + if (int err = pipe(cout_pipe)) + error(2, err, "pipe() failed"); + + posix_spawn_file_actions_t actions; + if (int err = posix_spawn_file_actions_init(&actions)) + error(2, err, "posix_spawn_file_actions_init() failed"); + + posix_spawn_file_actions_addclose(&actions, STDIN_FILENO); + posix_spawn_file_actions_addclose(&actions, cout_pipe[0]); + posix_spawn_file_actions_adddup2(&actions, cout_pipe[1], STDOUT_FILENO); + posix_spawn_file_actions_addclose(&actions, cout_pipe[1]); + + pid_t pid; + if (int err = posix_spawnp(&pid, args[0], &actions, nullptr, args, environ)) + error(2, err, "failed to run '%s'", args[0]); + + if (int err = posix_spawn_file_actions_destroy(&actions)) + error(2, err, "posix_spawn_file_actions_destroy() failed"); + + if (close(cout_pipe[1]) < 0) + error(2, errno, "closing write-side of pipe failed"); + + std::string buffer(32, 0); + std::string results; + int bytes_read; + for (;;) + { + static char buffer[512]; + bytes_read = read(cout_pipe[0], buffer, sizeof(buffer)); + if (bytes_read > 0) + results.insert(results.end(), buffer, buffer + bytes_read); + else + break; + } + if (bytes_read < 0) + error(2, bytes_read, "failed to read from pipe"); + + if (cout_pipe[0] < 0) + error(2, errno, "closing read-side of pipe failed"); + + int exit_code = 0; + if (waitpid(pid, &exit_code, 0) == -1) + error(2, errno, "waitpid() failed"); + + if (exit_code) + error(2, 0, "'%s' exited with status %d", args[0], exit_code); + + return results; +#else + // We could provide a pipe+fork+exec alternative implementation, but + // systems without posix_spawn() might also not have fork and exec. + // For instance MinGW does not. So let's fallback to system+tmpfile + // instead for maximum portability. + char prefix[30]; + snprintf(prefix, sizeof prefix, "spot-tmp"); + spot::temporary_file* tmpfile = spot::create_tmpfile(prefix); + std::string tmpname = tmpfile->name(); + std::ostringstream cmd; + for (auto t = args; *t != nullptr; ++t) + spot::quote_shell_string(cmd, *t) << ' '; + cmd << '>'; + spot::quote_shell_string(cmd, tmpfile->name()); + std::string cmdstr = cmd.str(); + int exit_code = system(cmdstr.c_str()); + if (exit_code < 0) + error(2, errno, "failed to execute %s", cmdstr.c_str()); + if (exit_code > 0) + error(2, 0, "'%s' exited with status %d", args[0], exit_code); + + std::ifstream ifs(tmpname, std::ifstream::in); + if (!ifs) + error(2, 0, "failed to open %s (output of %s)", tmpname.c_str(), args[0]); + ifs.exceptions(std::ifstream::failbit | std::ifstream::badbit); + std::stringstream buffer; + buffer << ifs.rdbuf(); + delete tmpfile; + return buffer.str(); +#endif +} + + std::atomic timed_out{false}; unsigned timeout_count = 0; @@ -706,6 +794,7 @@ parse_simple_command(const char* cmd) return res; } + #ifndef HAVE_SPAWN_H static void exec_command(const char* cmd) diff --git a/bin/common_trans.hh b/bin/common_trans.hh index e01131350..31c88c80c 100644 --- a/bin/common_trans.hh +++ b/bin/common_trans.hh @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2015-2018, 2020 Laboratoire de Recherche et +// Copyright (C) 2015-2018, 2020, 2022 Laboratoire de Recherche et // Développement de l'Epita (LRDE). // // This file is part of Spot, a model checking library. @@ -175,3 +175,9 @@ int exec_with_timeout(const char* cmd); #define exec_with_timeout(cmd) system(cmd) #define setup_sig_handler() while (0); #endif // !ENABLE_TIMEOUT + +// Run a command (whose args[0], args[1], etc. are given by args), and +// return its captured stdout. Stderr is not captured. Will abort +// with an error message if the command is not found, or if it exit +// with a non-zero status code. +std::string read_stdout_of_command(char* const* args); diff --git a/bin/ltlsynt.cc b/bin/ltlsynt.cc index 0e5d765a1..a6abb7c81 100644 --- a/bin/ltlsynt.cc +++ b/bin/ltlsynt.cc @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2017-2021 Laboratoire de Recherche et Développement +// Copyright (C) 2017-2022 Laboratoire de Recherche et Développement // de l'Epita (LRDE). // // This file is part of Spot, a model checking library. @@ -25,6 +25,7 @@ #include "common_finput.hh" #include "common_setup.hh" #include "common_sys.hh" +#include "common_trans.hh" #include #include @@ -54,6 +55,7 @@ enum OPT_PRINT_HOA, OPT_REAL, OPT_SIMPLIFY, + OPT_TLSF, OPT_VERBOSE, OPT_VERIFY }; @@ -64,10 +66,13 @@ static const argp_option options[] = { nullptr, 0, nullptr, 0, "Input options:", 1 }, { "outs", OPT_OUTPUT, "PROPS", 0, "comma-separated list of controllable (a.k.a. output) atomic" - " propositions", 0}, + " propositions", 0 }, { "ins", OPT_INPUT, "PROPS", 0, "comma-separated list of uncontrollable (a.k.a. input) atomic" - " propositions", 0}, + " propositions", 0 }, + { "tlsf", OPT_TLSF, "FILENAME", 0, + "Read a TLSF specification from FILENAME, and call syfco to " + "convert it into LTL", 0 }, /**************************************************/ { nullptr, 0, nullptr, 0, "Fine tuning:", 10 }, { "algo", OPT_ALGO, "sd|ds|ps|lar|lar.old|acd", 0, @@ -152,6 +157,8 @@ static const char* opt_print_hoa_args = nullptr; static bool opt_real = false; static bool opt_do_verify = false; static const char* opt_print_aiger = nullptr; +static char* opt_tlsf = nullptr; +static std::string opt_tlsf_string; static spot::synthesis_info* gi; @@ -486,8 +493,8 @@ namespace // If we reach this line // a strategy was found for each subformula assert(mealy_machines.size() == sub_form.size() - && "There are subformula for which no mealy like object" - "has been created."); + && ("There are subformula for which no mealy like object" + " has been created.")); spot::aig_ptr saig = nullptr; spot::twa_graph_ptr tot_strat = nullptr; @@ -646,6 +653,18 @@ namespace }; } +static void +split_aps(std::string arg, std::vector& where) +{ + std::istringstream aps(arg); + std::string ap; + while (std::getline(aps, ap, ',')) + { + ap.erase(remove_if(ap.begin(), ap.end(), isspace), ap.end()); + where.push_back(str_tolower(ap)); + } +} + static int parse_opt(int key, char *arg, struct argp_state *) { @@ -671,25 +690,13 @@ parse_opt(int key, char *arg, struct argp_state *) case OPT_INPUT: { all_input_aps.emplace(std::vector{}); - std::istringstream aps(arg); - std::string ap; - while (std::getline(aps, ap, ',')) - { - ap.erase(remove_if(ap.begin(), ap.end(), isspace), ap.end()); - all_input_aps->push_back(str_tolower(ap)); - } + split_aps(arg, *all_input_aps); break; } case OPT_OUTPUT: { all_output_aps.emplace(std::vector{}); - std::istringstream aps(arg); - std::string ap; - while (std::getline(aps, ap, ',')) - { - ap.erase(remove_if(ap.begin(), ap.end(), isspace), ap.end()); - all_output_aps->push_back(str_tolower(ap)); - } + split_aps(arg, *all_output_aps); break; } case OPT_PRINT: @@ -710,6 +717,11 @@ parse_opt(int key, char *arg, struct argp_state *) gi->minimize_lvl = XARGMATCH("--simplify", arg, simplify_args, simplify_values); break; + case OPT_TLSF: + if (opt_tlsf) + error(2, 0, "option --tlsf may only be used once"); + opt_tlsf = arg; + break; case OPT_VERBOSE: gi->verbose_stream = &std::cerr; if (not gi->bv) @@ -746,6 +758,29 @@ main(int argc, char **argv) argp_program_doc, children, nullptr, nullptr }; if (int err = argp_parse(&ap, argc, argv, ARGP_NO_HELP, nullptr, nullptr)) exit(err); + + if (opt_tlsf) + { + static char arg0[] = "syfco"; + static char arg1[] = "-f"; + static char arg2[] = "ltlxba"; + static char arg3[] = "-m"; + static char arg4[] = "fully"; + char* command[] = { arg0, arg1, arg2, arg3, arg4, opt_tlsf, nullptr }; + opt_tlsf_string = read_stdout_of_command(command); + jobs.emplace_back(opt_tlsf_string.c_str(), false); + + if (!all_input_aps.has_value() && !all_output_aps.has_value()) + { + static char arg5[] = "--print-output-signals"; + char* command[] = { arg0, arg5, opt_tlsf, nullptr }; + std::string res = read_stdout_of_command(command); + + all_output_aps.emplace(std::vector{}); + split_aps(res, *all_output_aps); + } + } + check_no_formula(); // Check if inputs and outputs are distinct diff --git a/doc/org/ltlsynt.org b/doc/org/ltlsynt.org index 45b4b2b1c..f05d58309 100644 --- a/doc/org/ltlsynt.org +++ b/doc/org/ltlsynt.org @@ -104,14 +104,20 @@ specification language created for the purpose of this competition. Fortunately, the SYNTCOMP organizers also provide a tool called [[https://github.com/reactive-systems/syfco][=syfco=]] which can translate a TLSF specification to an LTL formula. -The following four steps show you how a TLSF specification called =FILE= can +The following line shows how a TLSF specification called =FILE= can be synthesized using =syfco= and =ltlsynt=: #+BEGIN_SRC sh :export code -LTL=$(syfco FILE -f ltlxba -m fully) -IN=$(syfco FILE --print-input-signals) -OUT=$(syfco FILE --print-output-signals) -ltlsynt --formula="$LTL" --ins="$IN" --outs="$OUT" +ltlsynt --tlsf FILE +#+END_SRC + +The above =--tlsf= option will call =syfco= to perform the conversion +and extract output signals, as if you had used: + +#+BEGIN_SRC sh :export code +LTL=$(syfco -f ltlxba -m fully FILE) +OUT=$(syfco --print-output-signals FILE) +ltlsynt --formula="$LTL" --outs="$OUT" #+END_SRC * Internal details diff --git a/tests/Makefile.am b/tests/Makefile.am index c8a722f5c..3582a8493 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -342,6 +342,7 @@ TESTS_twa = \ core/parity.test \ core/parity2.test \ core/ltlsynt.test \ + core/syfco.test \ core/rabin2parity.test \ core/twacube.test diff --git a/tests/core/syfco.test b/tests/core/syfco.test new file mode 100755 index 000000000..b63f729a8 --- /dev/null +++ b/tests/core/syfco.test @@ -0,0 +1,48 @@ +#! /bin/sh +# -*- coding: utf-8 -*- +# Copyright (C) 2022 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 . + +. ./defs || exit 1 +set -e + +# Test that we can pass a tlsf specification to ltlsynt. This +# only work if syfco is installed. + +(syfco --version) || exit 77 + +cat >test.tlsf < X(out)); } +} +EOF + +test REALIZABLE = `ltlsynt --tlsf test.tlsf --realizability` +test UNREALIZABLE = `ltlsynt --tlsf test.tlsf --outs=foo --realizability` +test UNREALIZABLE = `ltlsynt --outs=foo --tlsf test.tlsf --realizability` + +ltlsynt --tlsf test.tlsf --tlsf test.tlsf 2>stderr && exit 0 +grep 'option --tlsf may only be used once' stderr From 55aac8e107a9ebe9c9b416b91d1c1f5e180feffc Mon Sep 17 00:00:00 2001 From: Florian Renkin Date: Tue, 12 Apr 2022 11:19:49 +0200 Subject: [PATCH 032/337] ltlsynt: display ACD instead of LAR when needed * spot/twaalgos/synthesis.cc: here * tests/core/ltlsynt.test: add test --- spot/twaalgos/synthesis.cc | 3 ++- tests/core/ltlsynt.test | 26 ++++++++++++++++++++++++++ 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/spot/twaalgos/synthesis.cc b/spot/twaalgos/synthesis.cc index ed4915c4b..b249acce9 100644 --- a/spot/twaalgos/synthesis.cc +++ b/spot/twaalgos/synthesis.cc @@ -1022,7 +1022,8 @@ namespace spot if (bv) bv->paritize_time += sw.stop(); if (vs) - *vs << "LAR construction done in " << bv->paritize_time + *vs << (gi.s == algo::ACD ? "ACD" : "LAR") + << " construction done in " << bv->paritize_time << " seconds\nDPA has " << dpa->num_states() << " states, " << dpa->num_sets() << " colors\n"; diff --git a/tests/core/ltlsynt.test b/tests/core/ltlsynt.test index 07e2690e7..7a7084099 100644 --- a/tests/core/ltlsynt.test +++ b/tests/core/ltlsynt.test @@ -903,3 +903,29 @@ EOF sed 's/ [0-9.e-]* seconds/ X seconds/g' out > outx diff outx exp + +# ACD verbose +cat >exp < GFb) && (Gc)' --outs=b,c --verbose --bypass=no\ + --algo=acd 2> out +sed 's/ [0-9.e-]* seconds/ X seconds/g' out > outx +diff outx exp From 62725fb507eb77edab751410c5cba81f273f4092 Mon Sep 17 00:00:00 2001 From: Florian Renkin Date: Tue, 12 Apr 2022 11:21:14 +0200 Subject: [PATCH 033/337] ltlsynt: don't solve games when we want to display them * bin/ltlsynt.cc: here --- bin/ltlsynt.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/bin/ltlsynt.cc b/bin/ltlsynt.cc index a6abb7c81..2c5141557 100644 --- a/bin/ltlsynt.cc +++ b/bin/ltlsynt.cc @@ -426,6 +426,8 @@ namespace && "Env needs first turn"); } print_game(arena); + if (want_game) + continue; if (!spot::solve_game(arena, *gi)) { std::cout << "UNREALIZABLE" << std::endl; From 355c5ffeb109a2758f10c578369cd3382bcef5ef Mon Sep 17 00:00:00 2001 From: Florian Renkin Date: Wed, 13 Apr 2022 10:51:52 +0200 Subject: [PATCH 034/337] ltlsynt: display the number of subformulas * bin/ltlsynt.cc: here * tests/core/ltlsynt.test: ajust tests --- bin/ltlsynt.cc | 6 ++++++ tests/core/ltlsynt.test | 15 +++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/bin/ltlsynt.cc b/bin/ltlsynt.cc index 2c5141557..50bae5f9e 100644 --- a/bin/ltlsynt.cc +++ b/bin/ltlsynt.cc @@ -336,6 +336,12 @@ namespace if (opt_decompose_ltl) { auto subs = split_independant_formulas(f, output_aps); + if (gi->verbose_stream) + { + *gi->verbose_stream << "there are " + << subs.first.size() + << " subformulas\n"; + } if (subs.first.size() > 1) { sub_form = subs.first; diff --git a/tests/core/ltlsynt.test b/tests/core/ltlsynt.test index 7a7084099..1badb9b4b 100644 --- a/tests/core/ltlsynt.test +++ b/tests/core/ltlsynt.test @@ -192,6 +192,7 @@ ltlsynt --ins=a --outs=b,c -f 'GFa <-> (GFb & GFc)' \ diff out exp cat >exp < GFb direct strategy was found. EOF @@ -200,6 +201,7 @@ sed 's/ [0-9.e-]* seconds/ X seconds/g' out > outx diff outx exp cat >exp < GFb tanslating formula done in X seconds direct strategy was found. @@ -211,6 +213,7 @@ sed 's/ [0-9.e-]* seconds/ X seconds/g' out > outx diff outx exp cat >exp < (Fa & Fb & Fc & Fd) direct strategy was found. EOF @@ -220,6 +223,7 @@ sed 's/ [0-9.e-]* seconds/ X seconds/g' out > outx diff outx exp cat >exp < G(i1 <-> o0) direct strategy might exist but was not found. translating formula done in X seconds @@ -623,6 +627,7 @@ grep "one of --ins or --outs" stderr # Try to find a direct strategy for GFa <-> GFb and a direct strategy for # Gc cat >exp < GFb tanslating formula done in X seconds direct strategy was found. @@ -713,6 +718,7 @@ sed 's/ [0-9.e-]* seconds/ X seconds/g' out > outx diff outx exp cat >exp < y direct strategy might exist but was not found. translating formula done in X seconds @@ -744,6 +750,7 @@ diff outx exp # Here, G!(!x | !y) should be Gx & Gy cat >exp <exp < b) should be G(a) & G(!b) cat >exp <exp < (b & c & d) should be # (a => b) & (a => c) & (a => d) cat >exp < b direct strategy might exist but was not found. translating formula done in X seconds @@ -842,6 +853,7 @@ diff outx exp # Here, !(F(a | b)) should be G!a & G!b cat >exp < GFb)' --outs=b,c --decompose=yes\ --verbose --realizability 2> out cat >exp < GFb @@ -883,6 +896,7 @@ diff out exp ltlsynt -f 'G(c) & (G(a) <-> GFb)' --outs=b,c --decompose=yes\ --verbose --realizability --bypass=no 2> out cat >exp <exp < Date: Mon, 7 Feb 2022 16:10:40 +0100 Subject: [PATCH 035/337] remove uses of unary_function and binary_function These were deprecated in C++11, and are supposed to be removed from C++17, however gcc-snapshot just started warning about those. * spot/misc/bddlt.hh, spot/misc/hash.hh, spot/misc/ltstr.hh, spot/twa/taatgba.hh, spot/twaalgos/ltl2tgba_fm.cc: Here. --- spot/misc/bddlt.hh | 11 ++++------- spot/misc/hash.hh | 8 +++----- spot/misc/ltstr.hh | 5 ++--- spot/twa/taatgba.hh | 8 +++----- spot/twaalgos/ltl2tgba_fm.cc | 7 +++---- 5 files changed, 15 insertions(+), 24 deletions(-) diff --git a/spot/misc/bddlt.hh b/spot/misc/bddlt.hh index 327fcd00a..46e24ed33 100644 --- a/spot/misc/bddlt.hh +++ b/spot/misc/bddlt.hh @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2011, 2014, 2017, 2021 Laboratoire de Recherche et +// Copyright (C) 2011, 2014, 2017, 2021, 2022 Laboratoire de Recherche et // Developpement de l'Epita (LRDE). // Copyright (C) 2003, 2004 Laboratoire d'Informatique de Paris 6 (LIP6), // département Systèmes Répartis Coopératifs (SRC), Université Pierre @@ -33,8 +33,7 @@ namespace spot /// This comparison function use BDD ids for efficiency. An /// algorithm depending on this order may return different results /// depending on how the BDD library has been used before. - struct bdd_less_than : - public std::binary_function + struct bdd_less_than { bool operator()(const bdd& left, const bdd& right) const @@ -50,8 +49,7 @@ namespace spot /// long as the variable order is the same, the output of this /// comparison will be stable and independent on previous BDD /// operations. - struct bdd_less_than_stable : - public std::binary_function + struct bdd_less_than_stable { bool operator()(const bdd& left, const bdd& right) const @@ -62,8 +60,7 @@ namespace spot /// \ingroup misc_tools /// \brief Hash functor for BDDs. - struct bdd_hash : - public std::unary_function + struct bdd_hash { size_t operator()(const bdd& b) const noexcept diff --git a/spot/misc/hash.hh b/spot/misc/hash.hh index b99c4ab53..cad845a68 100644 --- a/spot/misc/hash.hh +++ b/spot/misc/hash.hh @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2008, 2011, 2014, 2015-2018, 2021 Laboratoire de +// Copyright (C) 2008, 2011, 2014, 2015-2018, 2021, 2022 Laboratoire de // Recherche et Développement de l'Epita (LRDE). // Copyright (C) 2003, 2004, 2005 Laboratoire d'Informatique de // Paris 6 (LIP6), département Systèmes Répartis Coopératifs (SRC), @@ -36,8 +36,7 @@ namespace spot /// \ingroup hash_funcs /// \brief A hash function for pointers. template - struct ptr_hash : - public std::unary_function + struct ptr_hash { // A default constructor is needed if the ptr_hash object is // stored in a const member. This occur with the clang version @@ -59,8 +58,7 @@ namespace spot /// \ingroup hash_funcs /// \brief A hash function that returns identity template - struct identity_hash: - public std::unary_function + struct identity_hash { // A default constructor is needed if the identity_hash object is // stored in a const member. diff --git a/spot/misc/ltstr.hh b/spot/misc/ltstr.hh index 25b561b3b..15f7d11ef 100644 --- a/spot/misc/ltstr.hh +++ b/spot/misc/ltstr.hh @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2015 Laboratoire de Recherche et Développement +// Copyright (C) 2015, 2022 Laboratoire de Recherche et Développement // de l'Epita (LRDE) // Copyright (C) 2005 Laboratoire d'Informatique de Paris 6 (LIP6), // département Systèmes Répartis Coopératifs (SRC), Université Pierre @@ -39,8 +39,7 @@ namespace spot /// \code /// std::map seen; /// \endcode - struct char_ptr_less_than: - public std::binary_function + struct char_ptr_less_than { bool operator()(const char* left, const char* right) const diff --git a/spot/twa/taatgba.hh b/spot/twa/taatgba.hh index 9daa4975d..6a5b1c470 100644 --- a/spot/twa/taatgba.hh +++ b/spot/twa/taatgba.hh @@ -1,6 +1,6 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2009, 2011-2019 Laboratoire de Recherche et Développement de -// l'Epita (LRDE). +// Copyright (C) 2009, 2011-2019, 2022 Laboratoire de Recherche et +// Développement de l'Epita (LRDE). // // This file is part of Spot, a model checking library. // @@ -121,9 +121,7 @@ namespace spot std::vector, state_ptr_hash, state_ptr_equal> seen_map; - struct distance_sort : - public std::binary_function + struct distance_sort { bool operator()(const iterator_pair& lhs, const iterator_pair& rhs) const diff --git a/spot/twaalgos/ltl2tgba_fm.cc b/spot/twaalgos/ltl2tgba_fm.cc index 9ad149c1c..3566abc97 100644 --- a/spot/twaalgos/ltl2tgba_fm.cc +++ b/spot/twaalgos/ltl2tgba_fm.cc @@ -1,6 +1,6 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2008-2019, 2021 Laboratoire de Recherche et Développement -// de l'Epita (LRDE). +// Copyright (C) 2008-2019, 2021-2022 Laboratoire de Recherche et +// Développement de l'Epita (LRDE). // Copyright (C) 2003-2006 Laboratoire d'Informatique de Paris 6 // (LIP6), département Systèmes Répartis Coopératifs (SRC), Université // Pierre et Marie Curie. @@ -199,8 +199,7 @@ namespace spot } }; - struct flagged_formula_hash: - public std::unary_function + struct flagged_formula_hash { size_t operator()(const flagged_formula& that) const From 64020279cb9393d7783d72c2c8ae9bcb19923301 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Sun, 13 Feb 2022 18:37:34 +0100 Subject: [PATCH 036/337] reduce_parity: fix to work on automata with deleted edges * spot/twaalgos/parity.cc (reduce_parity): Use the size of the edge vector to initialize piprime1 and piprime2, not the number of edges. * tests/python/parity.py: Add test case, based on a report by Yann Thierry-Mieg. --- NEWS | 5 +++++ spot/twaalgos/parity.cc | 9 +++++---- tests/python/parity.py | 16 +++++++++++++++- 3 files changed, 25 insertions(+), 5 deletions(-) diff --git a/NEWS b/NEWS index 6d5fd9fc3..99a239a81 100644 --- a/NEWS +++ b/NEWS @@ -2,6 +2,11 @@ New in spot 2.10.4.dev (net yet released) Nothing yet. + Bugs fixed: + + - reduce_parity() produced incorrect results when applied to + automata with deleted edges. + New in spot 2.10.4 (2022-02-01) Bug fixed: diff --git a/spot/twaalgos/parity.cc b/spot/twaalgos/parity.cc index 35ec10a90..94c7bd922 100644 --- a/spot/twaalgos/parity.cc +++ b/spot/twaalgos/parity.cc @@ -1,6 +1,6 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2016, 2018, 2019 Laboratoire de Recherche et Développement -// de l'Epita (LRDE). +// Copyright (C) 2016, 2018, 2019, 2022 Laboratoire de Recherche et +// Développement de l'Epita (LRDE). // // This file is part of Spot, a model checking library. // @@ -465,8 +465,9 @@ namespace spot // using k=0 or k=1. // // -2 means the edge was never assigned a color. - std::vector piprime1(aut->num_edges() + 1, -2); // k=1 - std::vector piprime2(aut->num_edges() + 1, -2); // k=0 + unsigned evs = aut->edge_vector().size(); + std::vector piprime1(evs, -2); // k=1 + std::vector piprime2(evs, -2); // k=0 bool sba = aut->prop_state_acc().is_true(); auto rec = diff --git a/tests/python/parity.py b/tests/python/parity.py index dfeb24d3d..b0389c40e 100644 --- a/tests/python/parity.py +++ b/tests/python/parity.py @@ -1,6 +1,6 @@ #!/usr/bin/python3 # -*- mode: python; coding: utf-8 -*- -# Copyright (C) 2018, 2019 Laboratoire de Recherche et Développement de +# Copyright (C) 2018, 2019, 2022 Laboratoire de Recherche et Développement de # l'EPITA. # # This file is part of Spot, a model checking library. @@ -155,3 +155,17 @@ except RuntimeError as e: assert "invalid state number" in str(e) else: report_missing_exception() + + +a = spot.automaton("""HOA: v1 name: "F(!p0 | X!p1)" States: 3 +Start: 1 AP: 2 "p0" "p1" acc-name: Buchi Acceptance: 1 Inf(0) +properties: trans-labels explicit-labels trans-acc complete +properties: deterministic terminal --BODY-- State: 0 [t] 0 {0} State: +1 [!0] 0 [0] 2 State: 2 [!0 | !1] 0 [0&1] 2 --END--""") +# Erase the first edge of state 1 +oi = a.out_iteraser(1) +oi.erase() +# postprocess used to call reduce_parity that did not +# work correctly on automata with deleted edges. +sm = a.postprocess("gen", "small") +assert sm.num_states() == 3 From 7d9fddadceb51b512e294eed7f088aff9e78a4ad Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Wed, 16 Feb 2022 11:42:06 +0100 Subject: [PATCH 037/337] work around an issue in Flex 2.6.4 The fallback definition of SIZE_MAX supplied by flex was not preprocessor friendly and prevented robin_hood.hh from doing "#if SIZE_MAX == UINT64_MAX", as observed by Marc Espie on OpenBSD. * spot/parseaut/scanaut.ll, spot/parsetl/scantl.ll: Define __STDC_VERSION__ just so that the code generated by Flex include . * NEWS: Mention the bug. * THANKS: Add Marc. --- NEWS | 3 +++ THANKS | 1 + spot/parseaut/scanaut.ll | 8 ++++++++ spot/parsetl/scantl.ll | 12 ++++++++++-- 4 files changed, 22 insertions(+), 2 deletions(-) diff --git a/NEWS b/NEWS index 99a239a81..f2b95e7ba 100644 --- a/NEWS +++ b/NEWS @@ -7,6 +7,9 @@ New in spot 2.10.4.dev (net yet released) - reduce_parity() produced incorrect results when applied to automata with deleted edges. + - work around a portability issue in Flex 2.6.4 preventing + compilation on OpenBSD. + New in spot 2.10.4 (2022-02-01) Bug fixed: diff --git a/THANKS b/THANKS index 193dccaf7..9eb566483 100644 --- a/THANKS +++ b/THANKS @@ -31,6 +31,7 @@ Joachim Klein Juan Tzintzun Juraj Major Kristin Y. Rozier +Marc Espie Martin Dieguez Lodeiro Matthias Heizmann Maxime Bouton diff --git a/spot/parseaut/scanaut.ll b/spot/parseaut/scanaut.ll index c1c4fb44f..bf35810ed 100644 --- a/spot/parseaut/scanaut.ll +++ b/spot/parseaut/scanaut.ll @@ -26,6 +26,14 @@ /* %option debug */ %top{ #include "config.h" +/* Flex 2.6.4's test for relies on __STDC_VERSION__ + which is undefined in C++. So without that, it will define + its own integer types, including a broken SIZE_MAX definition. + So let's define __STDC_VERSION__ to make sure gets + included. */ +#if HAVE_INTTYPES_H && !(defined __STDC_VERSION__) +# define __STDC_VERSION__ 199901L +#endif } %{ #include diff --git a/spot/parsetl/scantl.ll b/spot/parsetl/scantl.ll index ff15685f8..554c28298 100644 --- a/spot/parsetl/scantl.ll +++ b/spot/parsetl/scantl.ll @@ -1,6 +1,6 @@ /* -*- coding: utf-8 -*- -** Copyright (C) 2010-2015, 2017-2019, 2021, Laboratoire de Recherche -** et Développement de l'Epita (LRDE). +** Copyright (C) 2010-2015, 2017-2019, 2021-2022, Laboratoire de +** Recherche et Développement de l'Epita (LRDE). ** Copyright (C) 2003, 2004 Laboratoire d'Informatique de Paris 6 ** (LIP6), département Systèmes Répartis Coopératifs (SRC), Université ** Pierre et Marie Curie. @@ -28,6 +28,14 @@ %top{ #include "config.h" +/* Flex 2.6.4's test for relies on __STDC_VERSION__ + which is undefined in C++. So without that, it will define + its own integer types, including a broken SIZE_MAX definition. + So let's define __STDC_VERSION__ to make sure gets + included. */ +#if HAVE_INTTYPES_H && !(defined __STDC_VERSION__) +# define __STDC_VERSION__ 199901L +#endif } %{ #include From 7149e5a34d4dc5374f81833e2f49f77c3a214e1e Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Thu, 3 Mar 2022 09:10:53 +0100 Subject: [PATCH 038/337] * .gitlab-ci.yml (alpine-gcc): Fix path for logs. --- .gitlab-ci.yml | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 583774da0..3c66af0b7 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -115,8 +115,9 @@ alpine-gcc: artifacts: when: always paths: - - tests/*/*.log + - ./spot-*/_build/sub/tests/*/*.log - ./*.log + - ./*.tar.gz arch-clang: stage: build From d61d6e5e2fa91046a20acad8820c88d2448a408e Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Thu, 3 Mar 2022 18:01:11 +0100 Subject: [PATCH 039/337] tests: avoid seq Partial fix for #501. * tests/core/prodchain.test: Hardcode the seq output. * tests/core/bricks.test: Use $AWK instead of seq. * tests/core/defs.in: Define $AWK. * NEWS: Mention the bug. --- NEWS | 3 +++ tests/core/bricks.test | 11 ++++++----- tests/core/defs.in | 3 ++- tests/core/prodchain.test | 5 +++-- 4 files changed, 14 insertions(+), 8 deletions(-) diff --git a/NEWS b/NEWS index f2b95e7ba..7f3429991 100644 --- a/NEWS +++ b/NEWS @@ -10,6 +10,9 @@ New in spot 2.10.4.dev (net yet released) - work around a portability issue in Flex 2.6.4 preventing compilation on OpenBSD. + - Do not use the seq command in test cases, it is not available + everywhere. + New in spot 2.10.4 (2022-02-01) Bug fixed: diff --git a/tests/core/bricks.test b/tests/core/bricks.test index b98c7e856..37ff57cb0 100644 --- a/tests/core/bricks.test +++ b/tests/core/bricks.test @@ -1,7 +1,7 @@ #!/bin/sh # -*- coding: utf-8 -*- -# Copyright (C) 2020 Laboratoire de Recherche et Développement de -# l'Epita (LRDE). +# Copyright (C) 2020, 2022 Laboratoire de Recherche et Développement +# de l'Epita (LRDE). # # This file is part of Spot, a model checking library. # @@ -21,12 +21,13 @@ . ./defs set -e -seq 0 1999 > expected +# The seq command is not always available, but we assume awk is. +$AWK 'BEGIN{for(x=0;x<2000;++x) print x;}' >expected ../bricks > stdout -cat stdout | head -n 2000 | awk '{print $2}' | sed 's/{//g' | \ - awk -F',' '{print $1}' | sort -n > map +cat stdout | head -n 2000 | $AWK '{print $2}' | sed 's/{//g' | \ + $AWK -F',' '{print $1}' | sort -n > map diff expected map diff --git a/tests/core/defs.in b/tests/core/defs.in index 7df6fdf77..d06a3b67d 100644 --- a/tests/core/defs.in +++ b/tests/core/defs.in @@ -1,5 +1,5 @@ # -*- mode: shell-script; coding: utf-8 -*- -# Copyright (C) 2009, 2010, 2012, 2013, 2015 Laboratoire de Recherche +# Copyright (C) 2009, 2010, 2012, 2013, 2015, 2022 Laboratoire de Recherche # et Développement de l'Epita (LRDE). # Copyright (C) 2003, 2004, 2006 Laboratoire d'Informatique de Paris 6 (LIP6), # département Systèmes Répartis Coopératifs (SRC), Université Pierre @@ -57,6 +57,7 @@ case $srcdir in *) srcdir=../$srcdir esac +AWK='@AWK@' DOT='@DOT@' LBTT="@LBTT@" LBTT_TRANSLATE="@LBTT_TRANSLATE@" diff --git a/tests/core/prodchain.test b/tests/core/prodchain.test index b5037782f..0a7f1a1d9 100755 --- a/tests/core/prodchain.test +++ b/tests/core/prodchain.test @@ -1,6 +1,6 @@ #!/bin/sh # -*- coding: utf-8 -*- -# Copyright (C) 2018 Laboratoire de Recherche et Développement +# Copyright (C) 2018, 2022 Laboratoire de Recherche et Développement # de l'Epita (LRDE). # # This file is part of Spot, a model checking library. @@ -23,7 +23,8 @@ set -e set x shift -for i in `seq 1 42`; do +for i in 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 \ + 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42; do ltl2tgba "{a[*$i]}[]->GFb" > $i.hoa done for i in *.hoa; do From 734de00bfd54de20ac142aabb90f1fca0cde0d7d Mon Sep 17 00:00:00 2001 From: Antoine Martin Date: Thu, 10 Mar 2022 15:49:46 +0100 Subject: [PATCH 040/337] tests: don't wipe python environment * tests/run.in: keep original PYTHONPATH contents * NEWS: mention the bug --- NEWS | 3 +++ tests/run.in | 9 ++++++--- 2 files changed, 9 insertions(+), 3 deletions(-) diff --git a/NEWS b/NEWS index 7f3429991..301711588 100644 --- a/NEWS +++ b/NEWS @@ -13,6 +13,9 @@ New in spot 2.10.4.dev (net yet released) - Do not use the seq command in test cases, it is not available everywhere. + - Do not erase the previous contents of the PYTHONPATH environment + variable when running tests, prepend to it instead. + New in spot 2.10.4 (2022-02-01) Bug fixed: diff --git a/tests/run.in b/tests/run.in index d14bf52a9..7eaa7732c 100755 --- a/tests/run.in +++ b/tests/run.in @@ -104,18 +104,21 @@ export srcdir case $1 in *.ipynb) - PYTHONPATH=$pypath DYLD_LIBRARY_PATH=$modpath:$DYLD_LIBRARY_PATH \ + PYTHONPATH=$pypath:$PYTHONPATH \ + DYLD_LIBRARY_PATH=$modpath:$DYLD_LIBRARY_PATH \ PYTHONIOENCODING=utf-8:surrogateescape \ exec $PREFIXCMD @PYTHON@ @abs_srcdir@/python/ipnbdoctest.py "$@";; *.py) - PYTHONPATH=$pypath DYLD_LIBRARY_PATH=$modpath:$DYLD_LIBRARY_PATH \ + PYTHONPATH=$pypath:$PYTHONPATH \ + DYLD_LIBRARY_PATH=$modpath:$DYLD_LIBRARY_PATH \ exec $PREFIXCMD @PYTHON@ "$@";; *.test) exec sh -x "$@";; *.pl) exec $PERL "$@";; *python*|*jupyter*|*pypy*) - PYTHONPATH=$pypath DYLD_LIBRARY_PATH=$modpath:$DYLD_LIBRARY_PATH \ + PYTHONPATH=$pypath:$PYTHONPATH \ + DYLD_LIBRARY_PATH=$modpath:$DYLD_LIBRARY_PATH \ exec $PREFIXCMD "$@";; *) echo "Unknown extension" >&2 From 2aecf9a79e64c55e8cac803591f73d93552a46e4 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Thu, 10 Mar 2022 10:53:18 +0100 Subject: [PATCH 041/337] fix typos and make formula_from_bdd more usable in Python * python/spot/impl.i (formula_from_bdd): Instantiate for twa_graph. * spot/twa/twa.hh (register_aps_from_dict): Typo in exception. * tests/python/except.py: More tests for the above. * tests/python/bdddict.py: Typo in comment. --- python/spot/impl.i | 2 ++ spot/twa/twa.hh | 4 ++-- tests/python/bdddict.py | 4 ++-- tests/python/except.py | 38 +++++++++++++++++++++++++++++++++++++- 4 files changed, 43 insertions(+), 5 deletions(-) diff --git a/python/spot/impl.i b/python/spot/impl.i index 12cae6311..21a9a68b8 100644 --- a/python/spot/impl.i +++ b/python/spot/impl.i @@ -530,6 +530,8 @@ namespace std { %include %include %include +%template(formula_to_bdd) spot::formula_to_bdd; + %include /* These operators may raise exceptions, and we do not want Swig4 to convert those exceptions to NotImplemented. */ diff --git a/spot/twa/twa.hh b/spot/twa/twa.hh index cb1e208ec..819a90962 100644 --- a/spot/twa/twa.hh +++ b/spot/twa/twa.hh @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2009, 2011, 2013-2021 Laboratoire de Recherche et +// Copyright (C) 2009, 2011, 2013-2022 Laboratoire de Recherche et // Développement de l'Epita (LRDE). // Copyright (C) 2003-2005 Laboratoire d'Informatique de Paris 6 // (LIP6), département Systèmes Répartis Coopératifs (SRC), Université @@ -761,7 +761,7 @@ namespace spot void register_aps_from_dict() { if (!aps_.empty()) - throw std::runtime_error("register_ap_from_dict() may not be" + throw std::runtime_error("register_aps_from_dict() may not be" " called on an automaton that has already" " registered some AP"); auto& m = get_dict()->bdd_map; diff --git a/tests/python/bdddict.py b/tests/python/bdddict.py index d6222b58f..45c505385 100644 --- a/tests/python/bdddict.py +++ b/tests/python/bdddict.py @@ -17,8 +17,8 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -# Make sure we can leep track of BDD association in Python using bdd_dict, as -# discussed in issue #372. +# Make sure we can keep track of BDD association in Python using bdd_dict, as +# discussed in (deleted???) issue #372. # CPython use reference counting, so that automata are destructed # when we expect them to be. However other implementations like diff --git a/tests/python/except.py b/tests/python/except.py index ef6ec3cc5..76ae88b0a 100644 --- a/tests/python/except.py +++ b/tests/python/except.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Copyright (C) 2018-2021 Laboratoire de Recherche et Développement de +# Copyright (C) 2018-2022 Laboratoire de Recherche et Développement de # l'Epita (LRDE). # # This file is part of Spot, a model checking library. @@ -24,6 +24,8 @@ import spot import buddy +from unittest import TestCase +tc = TestCase() def report_missing_exception(): @@ -278,3 +280,37 @@ except OverflowError as e: assert "reversed" in str(e) else: report_missing_exception() + + +a = spot.make_twa_graph() +s = a.new_state() +b = spot.formula_to_bdd("a & b", a.get_dict(), a) +a.new_edge(s, s, b, []) +try: + print(a.to_str('hoa')) +except RuntimeError as e: + tc.assertIn("unregistered atomic propositions", str(e)) +else: + report_missing_exception() + +a.register_aps_from_dict() +tc.assertEqual(a.to_str('hoa'), """HOA: v1 +States: 1 +Start: 0 +AP: 2 "a" "b" +acc-name: all +Acceptance: 0 t +properties: trans-labels explicit-labels state-acc deterministic +--BODY-- +State: 0 +[0&1] 0 +--END--""") + +try: + a.register_aps_from_dict() +except RuntimeError as e: + se = str(e) + tc.assertIn("register_aps_from_dict", se) + tc.assertIn("already registered", se) +else: + report_missing_exception() From 968ef0f7b89431f44f6c90c4ed5b2c7b92bb345a Mon Sep 17 00:00:00 2001 From: Florian Renkin Date: Tue, 15 Mar 2022 14:01:25 +0100 Subject: [PATCH 042/337] ltlsynt: typo in help * bin/ltlsynt.cc: here --- bin/ltlsynt.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/ltlsynt.cc b/bin/ltlsynt.cc index 57b67bcc0..c6e53258c 100644 --- a/bin/ltlsynt.cc +++ b/bin/ltlsynt.cc @@ -65,7 +65,7 @@ static const argp_option options[] = "comma-separated list of controllable (a.k.a. output) atomic" " propositions", 0}, { "ins", OPT_INPUT, "PROPS", 0, - "comma-separated list of controllable (a.k.a. output) atomic" + "comma-separated list of uncontrollable (a.k.a. input) atomic" " propositions", 0}, /**************************************************/ { nullptr, 0, nullptr, 0, "Fine tuning:", 10 }, From 96e051d2bb971ebecb27b21db3ea1d9c65a9f3bd Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Tue, 22 Mar 2022 12:18:25 +0100 Subject: [PATCH 043/337] graph: fix invalid read Reported by Florian Renkin. * spot/graph/graph.hh (sort_edges_of): Fix invalid read when sorting a state without successor. Seen on core/tgbagraph.test. --- spot/graph/graph.hh | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/spot/graph/graph.hh b/spot/graph/graph.hh index 75e0977b7..fa276131d 100644 --- a/spot/graph/graph.hh +++ b/spot/graph/graph.hh @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2014-2018, 2020, 2021 Laboratoire de Recherche et +// Copyright (C) 2014-2018, 2020-2022 Laboratoire de Recherche et // Développement de l'Epita. // // This file is part of Spot, a model checking library. @@ -1243,14 +1243,19 @@ namespace spot //dump_storage(std::cerr); auto pi = [&](unsigned t1, unsigned t2) {return p(edges_[t1], edges_[t2]); }; + + // Sort the outgoing edges of each selected state according + // to predicate p. Do that in place. std::vector sort_idx_; - for (unsigned i = 0; i < num_states(); ++i) + unsigned ns = num_states(); + for (unsigned i = 0; i < ns; ++i) { if (to_sort_ptr && !(*to_sort_ptr)[i]) continue; - - sort_idx_.clear(); unsigned t = states_[i].succ; + if (t == 0) + continue; + sort_idx_.clear(); do { sort_idx_.push_back(t); From 53118d9314783ce97a0069e14d390fba9f7f7eed Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Tue, 22 Mar 2022 12:22:48 +0100 Subject: [PATCH 044/337] * spot/twaalgos/gfguarantee.hh: Typos in comments. --- spot/twaalgos/gfguarantee.hh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/spot/twaalgos/gfguarantee.hh b/spot/twaalgos/gfguarantee.hh index 5124667f4..40cb16f97 100644 --- a/spot/twaalgos/gfguarantee.hh +++ b/spot/twaalgos/gfguarantee.hh @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2018 Laboratoire de Recherche et Développement +// Copyright (C) 2018, 2022 Laboratoire de Recherche et Développement // de l'Epita (LRDE). // // This file is part of Spot, a model checking library. @@ -48,7 +48,7 @@ namespace spot /// \brief Convert GF(φ) into a (D)BA if φ is a guarantee property. /// /// If the formula \a gf has the form GΦ where Φ matches either F(φ) - /// or F(φ₁)|F(φ₂)|...|F(φₙ), we translate Φ into A_Φ and attempt to + /// or F(φ₁)&F(φ₂)&...&F(φₙ), we translate Φ into A_Φ and attempt to /// minimize it to a WDBA W_Φ. If the resulting automaton is /// terminal, we then call g_f_terminal_inplace(W_Φ). If \a /// deterministic is not set, we keep the minimized automaton only From d1f49c721a45c58f24c085dfb14e3f70f1c7089d Mon Sep 17 00:00:00 2001 From: Florian Renkin Date: Mon, 21 Mar 2022 10:46:42 +0100 Subject: [PATCH 045/337] ltlsynt: don't fail if --outs or --ins is set to empty * bin/ltlsynt.cc: here * tests/core/ltlsynt.test: add tests --- bin/ltlsynt.cc | 58 +++++++++++++++++++++++------------------ tests/core/ltlsynt.test | 8 ++++++ 2 files changed, 40 insertions(+), 26 deletions(-) diff --git a/bin/ltlsynt.cc b/bin/ltlsynt.cc index c6e53258c..f5b2ade51 100644 --- a/bin/ltlsynt.cc +++ b/bin/ltlsynt.cc @@ -135,8 +135,8 @@ Exit status:\n\ 1 if the input problem is not realizable\n\ 2 if any error has been reported"; -static std::vector all_output_aps; -static std::vector all_input_aps; +static std::optional> all_output_aps; +static std::optional> all_input_aps; static const char* opt_csv = nullptr; static bool opt_print_pg = false; @@ -577,12 +577,12 @@ namespace class ltl_processor final : public job_processor { private: - std::vector input_aps_; - std::vector output_aps_; + std::optional> input_aps_; + std::optional> output_aps_; public: - ltl_processor(std::vector input_aps_, - std::vector output_aps_) + ltl_processor(std::optional> input_aps_, + std::optional> output_aps_) : input_aps_(std::move(input_aps_)), output_aps_(std::move(output_aps_)) { @@ -592,11 +592,13 @@ namespace const char* filename, int linenum) override { auto unknown_aps = [](spot::formula f, - const std::vector& known, - const std::vector* known2 = nullptr) + const std::optional>& known, + const std::optional>& known2 = {}) { std::vector unknown; std::set seen; + // If we don't have --ins and --outs, we must not find an AP. + bool can_have_ap = known.has_value(); f.traverse([&](const spot::formula& s) { if (s.is(spot::op::ap)) @@ -604,10 +606,11 @@ namespace if (!seen.insert(s).second) return false; const std::string& a = s.ap_name(); - if (std::find(known.begin(), known.end(), a) == known.end() - && (!known2 + if (!can_have_ap + || (std::find(known->begin(), known->end(), a) == known->end() + && (!known2.has_value() || std::find(known2->begin(), - known2->end(), a) == known2->end())) + known2->end(), a) == known2->end()))) unknown.push_back(a); } return false; @@ -617,30 +620,30 @@ namespace // Decide which atomic propositions are input or output. int res; - if (input_aps_.empty() && !output_aps_.empty()) + if (!input_aps_.has_value() && output_aps_.has_value()) { - res = solve_formula(f, unknown_aps(f, output_aps_), output_aps_); + res = solve_formula(f, unknown_aps(f, output_aps_), *output_aps_); } - else if (output_aps_.empty() && !input_aps_.empty()) + else if (!output_aps_.has_value() && input_aps_.has_value()) { - res = solve_formula(f, input_aps_, unknown_aps(f, input_aps_)); + res = solve_formula(f, *input_aps_, unknown_aps(f, input_aps_)); } - else if (output_aps_.empty() && input_aps_.empty()) + else if (!output_aps_.has_value() && !input_aps_.has_value()) { - for (const std::string& ap: unknown_aps(f, input_aps_, &output_aps_)) + for (const std::string& ap: unknown_aps(f, input_aps_, output_aps_)) error_at_line(2, 0, filename, linenum, "one of --ins or --outs should list '%s'", ap.c_str()); - res = solve_formula(f, input_aps_, output_aps_); + res = solve_formula(f, *input_aps_, *output_aps_); } else { - for (const std::string& ap: unknown_aps(f, input_aps_, &output_aps_)) + for (const std::string& ap: unknown_aps(f, input_aps_, output_aps_)) error_at_line(2, 0, filename, linenum, "both --ins and --outs are specified, " "but '%s' is unlisted", ap.c_str()); - res = solve_formula(f, input_aps_, output_aps_); + res = solve_formula(f, *input_aps_, *output_aps_); } if (opt_csv) @@ -671,23 +674,25 @@ parse_opt(int key, char *arg, struct argp_state *) break; case OPT_INPUT: { + all_input_aps.emplace(std::vector{}); std::istringstream aps(arg); std::string ap; while (std::getline(aps, ap, ',')) { ap.erase(remove_if(ap.begin(), ap.end(), isspace), ap.end()); - all_input_aps.push_back(str_tolower(ap)); + all_input_aps->push_back(str_tolower(ap)); } break; } case OPT_OUTPUT: { + all_output_aps.emplace(std::vector{}); std::istringstream aps(arg); std::string ap; while (std::getline(aps, ap, ',')) { ap.erase(remove_if(ap.begin(), ap.end(), isspace), ap.end()); - all_output_aps.push_back(str_tolower(ap)); + all_output_aps->push_back(str_tolower(ap)); } break; } @@ -748,10 +753,11 @@ main(int argc, char **argv) check_no_formula(); // Check if inputs and outputs are distinct - for (const std::string& ai : all_input_aps) - if (std::find(all_output_aps.begin(), all_output_aps.end(), ai) - != all_output_aps.end()) - error(2, 0, "'%s' appears both in --ins and --outs", ai.c_str()); + if (all_input_aps.has_value() && all_output_aps.has_value()) + for (const std::string& ai : *all_input_aps) + if (std::find(all_output_aps->begin(), all_output_aps->end(), ai) + != all_output_aps->end()) + error(2, 0, "'%s' appears both in --ins and --outs", ai.c_str()); ltl_processor processor(all_input_aps, all_output_aps); if (int res = processor.run(); res == 0 || res == 1) diff --git a/tests/core/ltlsynt.test b/tests/core/ltlsynt.test index 4d318dbae..99d1e92da 100644 --- a/tests/core/ltlsynt.test +++ b/tests/core/ltlsynt.test @@ -821,3 +821,11 @@ ltlsynt -f '!(F(a | b))' --outs=b, --decompose=yes \ --verbose --aiger 2> out || true sed 's/ [0-9.e-]* seconds/ X seconds/g' out > outx diff outx exp + +ltlsynt --ins="" -f "GFa" +ltlsynt --outs="" -f "GFb" | grep "UNREALIZABLE" + +ltlsynt --outs="" -f "1" + +ltlsynt --outs="" --ins="" -f "GFa" 2>&1 | \ + grep "both --ins and --outs are specified" \ No newline at end of file From 58f39ec2874c34a7f7e71c5fb683e89ab41ca095 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Fri, 25 Mar 2022 09:25:04 +0100 Subject: [PATCH 046/337] * doc/org/tut40.org: Clarify, as suggested by a CAV'22 reviewer. --- doc/org/tut40.org | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/doc/org/tut40.org b/doc/org/tut40.org index b68efe558..8d9b004da 100644 --- a/doc/org/tut40.org +++ b/doc/org/tut40.org @@ -144,9 +144,11 @@ states. We now look at how to create such a game in Python. -Essentially, a game in Spot is just an automaton equiped with a -special property to indicate the owner of each states. So it can be -created using the usual interface: +Essentially, a game in Spot is just an automaton equiped with a [[file:concepts.org::#named-properties][named +property "state-player"]] that hold a Boolean vector indicating the +owner of each state. The game can be created using the usual +automaton interface, and the owners are set by calling +=game.set_state_players()= with a vector of Boolean at the very end. #+NAME: build_game #+BEGIN_SRC python :exports code @@ -173,7 +175,7 @@ created using the usual interface: todo = [] # Create the state (i, j) for a player if it does not exist yet and - # returns the state's number in the game. + # return the state's number in the game. def get_game_state(player, i, j): orig_state = s_orig_states if player else d_orig_states if (i, j) in orig_state: From add2fced4430e01304a2cfae1b81675c7fe8c51f Mon Sep 17 00:00:00 2001 From: Philipp Schlehuber-Caissier Date: Tue, 29 Mar 2022 15:51:31 +0200 Subject: [PATCH 047/337] Correct bug in zielonka Optimization in Zielonka failed under certain circumstances todo: Devise a specialized test for direct attr computation * spot/twaalgos/game.cc: Correction * tests/python/game.py: Test --- spot/twaalgos/game.cc | 46 ++++++--- tests/python/game.py | 212 +++++++++++++++++++++++++++++++++++++++++- 2 files changed, 246 insertions(+), 12 deletions(-) diff --git a/spot/twaalgos/game.cc b/spot/twaalgos/game.cc index 9b8fdcee9..6bb62500d 100644 --- a/spot/twaalgos/game.cc +++ b/spot/twaalgos/game.cc @@ -309,16 +309,21 @@ namespace spot { auto scc_acc = info_->acc_sets_of(c_scc_idx_); // We will override all parities of edges leaving the scc + // Currently game is colored max odd + // So there is at least one color bool added[] = {false, false}; unsigned par_pair[2]; unsigned scc_new_par = std::max(scc_acc.max_set(), 1u); + bool player_color_larger; if (scc_new_par&1) { + player_color_larger = false; par_pair[1] = scc_new_par; par_pair[0] = scc_new_par+1; } else { + player_color_larger = true; par_pair[1] = scc_new_par+1; par_pair[0] = scc_new_par; } @@ -331,6 +336,7 @@ namespace spot for (unsigned v : c_states()) { assert(subgame_[v] == unseen_mark); + bool owner = (*owner_ptr_)[v]; for (auto &e : arena_->out(v)) { // The outgoing edges are taken finitely often @@ -342,14 +348,20 @@ namespace spot e.dst, e.acc); if (w_.winner(e.dst)) { - // Winning region of player -> odd - e.acc = odd_mark; + // Winning region off player -> + // odd mark if player + // else 1 (smallest loosing for env) + e.acc = owner ? odd_mark + : acc_cond::mark_t({1}); added[1] = true; } else { - // Winning region of env -> even - e.acc = even_mark; + // Winning region of env -> + // even mark for env, + // else 0 (smallest loosing for player) + e.acc = !owner ? even_mark + : acc_cond::mark_t({0}); added[0] = true; } // Replace with self-loop @@ -360,13 +372,22 @@ namespace spot // Compute the attractors of the self-loops/transitions leaving scc // These can be directly added to the winning states - // Note: attractors can not intersect therefore the order in which - // they are computed does not matter + // To avoid disregarding edges in attr computation we + // need to start with the larger color + // Todo come up with a test for this unsigned dummy_rd; - for (bool p : {false, true}) - if (added[p]) - attr(dummy_rd, p, par_pair[p], true, par_pair[p]); + for (bool p : {player_color_larger, + !player_color_larger}) + { + if (added[p]) + { + // Always take the larger, + // Otherwise states with an transition to a winning AND + // a loosing scc are treated incorrectly + attr(dummy_rd, p, par_pair[p], true, par_pair[p]); + } + } if (added[0] || added[1]) // Fix "negative" strategy @@ -379,8 +400,11 @@ namespace spot inline bool attr(unsigned &rd, bool p, unsigned max_par, - bool acc_par, unsigned min_win_par) + bool acc_par, unsigned min_win_par, + bool no_check=false) { + // In fix_scc, the attr computation is + // abused so we can not check ertain things // Computes the attractor of the winning set of player p within a // subgame given as rd. // If acc_par is true, max_par transitions are also accepting and @@ -394,7 +418,7 @@ namespace spot // As proposed in Oink! / PGSolver // Needs the transposed graph however - assert((!acc_par) || (acc_par && (max_par&1) == p)); + assert((no_check || !acc_par) || (acc_par && (max_par&1) == p)); assert(!acc_par || (0 < min_win_par)); assert((min_win_par <= max_par) && (max_par <= max_abs_par_)); diff --git a/tests/python/game.py b/tests/python/game.py index 9d77c153d..f45bed532 100644 --- a/tests/python/game.py +++ b/tests/python/game.py @@ -1,6 +1,6 @@ #!/usr/bin/python3 # -*- mode: python; coding: utf-8 -*- -# Copyright (C) 2020 Laboratoire de Recherche et Développement de +# Copyright (C) 2020, 2022 Laboratoire de Recherche et Développement de # l'EPITA. # # This file is part of Spot, a model checking library. @@ -61,3 +61,213 @@ State: 7 State: 8 {1} [0] 2 --END--""" + +# Testing case where parity_game optimization +# lead to wrong results +si = spot.synthesis_info() + +game = spot.automaton("""HOA: v1 +States: 27 +Start: 7 +AP: 11 "a" "b" "c" "d" "e" "f" "g" "h" "i" "j" "k" +acc-name: parity max odd 3 +Acceptance: 3 Fin(2) & (Inf(1) | Fin(0)) +properties: trans-labels explicit-labels trans-acc colored +properties: deterministic +spot-state-player: 0 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 1 +controllable-AP: 0 1 2 3 4 5 6 7 +--BODY-- +State: 0 +[t] 8 {0} +State: 1 +[8&9] 8 {0} +[!8&!10 | !9&!10] 9 {0} +[!8&10 | !9&10] 10 {0} +State: 2 +[8&9] 8 {0} +[!8&!10 | !9&!10] 11 {0} +[!8&10 | !9&10] 12 {0} +State: 3 +[8&9] 8 {0} +[!9&!10] 13 {0} +[!8&10 | !9&10] 14 {0} +[!8&9&!10] 15 {0} +State: 4 +[8&9] 8 {0} +[!8&!10 | !9&!10] 16 {0} +[!8&!9&10] 17 {0} +[!8&9&10] 18 {0} +[8&!9&10] 19 {0} +State: 5 +[8&9] 8 {0} +[!9&!10] 20 {0} +[!8&10 | !9&10] 21 {0} +[!8&9&!10] 22 {0} +State: 6 +[8&9] 8 {0} +[!8&!10 | !9&!10] 23 {0} +[!8&!9&10] 24 {0} +[!8&9&10] 25 {0} +[8&!9&10] 26 {0} +State: 7 +[8&9] 8 {0} +[!9&!10] 13 {0} +[!8&9&!10] 15 {0} +[!8&!9&10] 17 {0} +[!8&9&10] 18 {0} +[8&!9&10] 19 {0} +State: 8 +[!0&!1&2&!3&!4&5&!6&7 | !0&!1&2&!3&!4&5&6&!7 | !0&!1&2&!3&4&!5&!6&7 | +!0&!1&2&!3&4&!5&6&!7 | !0&!1&2&3&!4&!5&!6&7 | !0&!1&2&3&!4&!5&6&!7 | +!0&1&!2&!3&!4&5&!6&7 | !0&1&!2&!3&!4&5&6&!7 | !0&1&!2&!3&4&!5&!6&7 | +!0&1&!2&!3&4&!5&6&!7 | !0&1&!2&3&!4&!5&!6&7 | !0&1&!2&3&!4&!5&6&!7 | +0&!1&!2&!3&!4&5&!6&7 | 0&!1&!2&!3&!4&5&6&!7 | 0&!1&!2&!3&4&!5&!6&7 | + 0&!1&!2&!3&4&!5&6&!7 | 0&!1&!2&3&!4&!5&!6&7 | 0&!1&!2&3&!4&!5&6&!7] 0 {1} +State: 9 +[!0&!1&2&!3&!4&5&!6&7 | !0&!1&2&!3&!4&5&6&!7 | !0&!1&2&!3&4&!5&!6&7 | +!0&!1&2&!3&4&!5&6&!7 | !0&1&!2&!3&!4&5&!6&7 | !0&1&!2&!3&!4&5&6&!7 | +!0&1&!2&!3&4&!5&!6&7 | !0&1&!2&!3&4&!5&6&!7 | 0&!1&!2&!3&!4&5&!6&7 | +0&!1&!2&!3&!4&5&6&!7 | 0&!1&!2&!3&4&!5&!6&7 | 0&!1&!2&!3&4&!5&6&!7] 1 {2} +[!0&!1&2&3&!4&!5&!6&7 | !0&!1&2&3&!4&!5&6&!7 | !0&1&!2&3&!4&!5&!6&7 | +!0&1&!2&3&!4&!5&6&!7 | 0&!1&!2&3&!4&!5&!6&7 | 0&!1&!2&3&!4&!5&6&!7] 2 {2} +State: 10 +[!0&!1&2&!3&!4&5&!6&7 | !0&!1&2&!3&!4&5&6&!7 | !0&!1&2&!3&4&!5&!6&7 | +!0&!1&2&!3&4&!5&6&!7 | !0&1&!2&!3&!4&5&!6&7 | !0&1&!2&!3&!4&5&6&!7 | +!0&1&!2&!3&4&!5&!6&7 | !0&1&!2&!3&4&!5&6&!7 | 0&!1&!2&!3&!4&5&!6&7 | +0&!1&!2&!3&!4&5&6&!7 | 0&!1&!2&!3&4&!5&!6&7 | 0&!1&!2&!3&4&!5&6&!7] 0 {1} +[!0&!1&2&3&!4&!5&!6&7 | !0&!1&2&3&!4&!5&6&!7 | !0&1&!2&3&!4&!5&!6&7 | +!0&1&!2&3&!4&!5&6&!7 | 0&!1&!2&3&!4&!5&!6&7 | 0&!1&!2&3&!4&!5&6&!7] 2 {2} +State: 11 +[!0&!1&2&!3&4&!5&!6&7 | !0&!1&2&!3&4&!5&6&!7 | !0&!1&2&3&!4&!5&!6&7 | +!0&!1&2&3&!4&!5&6&!7 | !0&1&!2&!3&4&!5&!6&7 | !0&1&!2&!3&4&!5&6&!7 | +!0&1&!2&3&!4&!5&!6&7 | !0&1&!2&3&!4&!5&6&!7 | 0&!1&!2&!3&4&!5&!6&7 | +0&!1&!2&!3&4&!5&6&!7 | 0&!1&!2&3&!4&!5&!6&7 | 0&!1&!2&3&!4&!5&6&!7] 0 {1} +[!0&!1&2&!3&!4&5&!6&7 | !0&!1&2&!3&!4&5&6&!7 | !0&1&!2&!3&!4&5&!6&7 | +!0&1&!2&!3&!4&5&6&!7 | 0&!1&!2&!3&!4&5&!6&7 | 0&!1&!2&!3&!4&5&6&!7] 1 {2} +State: 12 +[!0&!1&2&!3&!4&5&!6&7 | !0&!1&2&!3&!4&5&6&!7 | !0&1&!2&!3&!4&5&!6&7 | +!0&1&!2&!3&!4&5&6&!7 | 0&!1&!2&!3&!4&5&!6&7 | 0&!1&!2&!3&!4&5&6&!7] 1 {2} +[!0&!1&2&!3&4&!5&!6&7 | !0&!1&2&!3&4&!5&6&!7 | !0&!1&2&3&!4&!5&!6&7 | +!0&!1&2&3&!4&!5&6&!7 | !0&1&!2&!3&4&!5&!6&7 | !0&1&!2&!3&4&!5&6&!7 | +!0&1&!2&3&!4&!5&!6&7 | !0&1&!2&3&!4&!5&6&!7 | 0&!1&!2&!3&4&!5&!6&7 | +0&!1&!2&!3&4&!5&6&!7 | 0&!1&!2&3&!4&!5&!6&7 | 0&!1&!2&3&!4&!5&6&!7] 2 {2} +State: 13 +[!0&!1&2&!3&!4&5&6&!7 | !0&!1&2&!3&4&!5&6&!7 | !0&1&!2&!3&!4&5&!6&7 | +!0&1&!2&!3&!4&5&6&!7 | !0&1&!2&!3&4&!5&!6&7 | !0&1&!2&!3&4&!5&6&!7 | +0&!1&!2&!3&!4&5&!6&7 | 0&!1&!2&!3&!4&5&6&!7 | 0&!1&!2&!3&4&!5&!6&7 | +0&!1&!2&!3&4&!5&6&!7] 1 {1} +[!0&!1&2&3&!4&!5&6&!7 | !0&1&!2&3&!4&!5&!6&7 | !0&1&!2&3&!4&!5&6&!7 | +0&!1&!2&3&!4&!5&!6&7 | 0&!1&!2&3&!4&!5&6&!7] 2 {1} +[!0&!1&2&!3&!4&5&!6&7 | !0&!1&2&!3&4&!5&!6&7] 3 {1} +[!0&!1&2&3&!4&!5&!6&7] 5 {1} +State: 14 +[!0&!1&2&!3&!4&5&!6&7 | !0&!1&2&!3&!4&5&6&!7 | !0&!1&2&!3&4&!5&!6&7 | +!0&!1&2&!3&4&!5&6&!7 | !0&1&!2&!3&!4&5&!6&7 | !0&1&!2&!3&!4&5&6&!7 | +!0&1&!2&!3&4&!5&!6&7 | !0&1&!2&!3&4&!5&6&!7 | 0&!1&!2&!3&!4&5&!6&7 | +0&!1&!2&!3&!4&5&6&!7 | 0&!1&!2&!3&4&!5&!6&7 | 0&!1&!2&!3&4&!5&6&!7] 0 {1} +[!0&!1&2&3&!4&!5&!6&7 | !0&!1&2&3&!4&!5&6&!7 | !0&1&!2&3&!4&!5&!6&7 | +!0&1&!2&3&!4&!5&6&!7 | 0&!1&!2&3&!4&!5&!6&7 | 0&!1&!2&3&!4&!5&6&!7] 2 {1} +State: 15 +[!0&!1&2&!3&!4&5&6&!7 | !0&!1&2&!3&4&!5&6&!7 | !0&1&!2&!3&!4&5&!6&7 | +!0&1&!2&!3&!4&5&6&!7 | !0&1&!2&!3&4&!5&!6&7 | !0&1&!2&!3&4&!5&6&!7 | +0&!1&!2&!3&!4&5&!6&7 | 0&!1&!2&!3&!4&5&6&!7 | 0&!1&!2&!3&4&!5&!6&7 | +0&!1&!2&!3&4&!5&6&!7] 1 {1} +[!0&!1&2&3&!4&!5&6&!7 | !0&1&!2&3&!4&!5&!6&7 | !0&1&!2&3&!4&!5&6&!7 | +0&!1&!2&3&!4&!5&!6&7 | 0&!1&!2&3&!4&!5&6&!7] 2 {1} +[!0&!1&2&!3&!4&5&!6&7 | !0&!1&2&!3&4&!5&!6&7] 4 {1} +[!0&!1&2&3&!4&!5&!6&7] 6 {1} +State: 16 +[!0&!1&2&!3&!4&5&!6&7 | !0&!1&2&!3&!4&5&6&!7 | !0&!1&2&!3&4&!5&!6&7 | +!0&!1&2&!3&4&!5&6&!7 | !0&1&!2&!3&!4&5&!6&7 | !0&1&!2&!3&!4&5&6&!7 | +!0&1&!2&!3&4&!5&!6&7 | !0&1&!2&!3&4&!5&6&!7 | 0&!1&!2&!3&!4&5&!6&7 | +0&!1&!2&!3&!4&5&6&!7 | 0&!1&!2&!3&4&!5&!6&7 | 0&!1&!2&!3&4&!5&6&!7] 1 {1} +[!0&!1&2&3&!4&!5&!6&7 | !0&!1&2&3&!4&!5&6&!7 | !0&1&!2&3&!4&!5&!6&7 | +!0&1&!2&3&!4&!5&6&!7 | 0&!1&!2&3&!4&!5&!6&7 | 0&!1&!2&3&!4&!5&6&!7] 2 {1} +State: 17 +[!0&!1&2&!3&!4&5&!6&7 | !0&!1&2&!3&!4&5&6&!7 | !0&!1&2&!3&4&!5&!6&7 | +!0&!1&2&!3&4&!5&6&!7 | !0&1&!2&!3&!4&5&!6&7 | !0&1&!2&!3&!4&5&6&!7 | +!0&1&!2&!3&4&!5&!6&7 | !0&1&!2&!3&4&!5&6&!7 | 0&!1&!2&!3&!4&5&!6&7 | +0&!1&!2&!3&!4&5&6&!7 | 0&!1&!2&!3&4&!5&!6&7 | 0&!1&!2&!3&4&!5&6&!7] 0 {1} +[!0&!1&2&3&!4&!5&!6&7 | !0&!1&2&3&!4&!5&6&!7 | !0&1&!2&3&!4&!5&6&!7 | +0&!1&!2&3&!4&!5&!6&7 | 0&!1&!2&3&!4&!5&6&!7] 2 {1} +[!0&1&!2&3&!4&!5&!6&7] 6 {1} +State: 18 +[!0&!1&2&!3&!4&5&!6&7 | !0&!1&2&!3&!4&5&6&!7 | !0&!1&2&!3&4&!5&!6&7 | +!0&!1&2&!3&4&!5&6&!7 | !0&1&!2&!3&!4&5&!6&7 | !0&1&!2&!3&!4&5&6&!7 | +!0&1&!2&!3&4&!5&!6&7 | !0&1&!2&!3&4&!5&6&!7 | 0&!1&!2&!3&!4&5&!6&7 | +0&!1&!2&!3&!4&5&6&!7 | 0&!1&!2&!3&4&!5&!6&7 | 0&!1&!2&!3&4&!5&6&!7] 0 {1} +[!0&!1&2&3&!4&!5&!6&7 | !0&!1&2&3&!4&!5&6&!7 | !0&1&!2&3&!4&!5&6&!7 | +0&!1&!2&3&!4&!5&!6&7 | 0&!1&!2&3&!4&!5&6&!7] 2 {1} +[!0&1&!2&3&!4&!5&!6&7] 5 {1} +State: 19 +[!0&!1&2&!3&!4&5&!6&7 | !0&!1&2&!3&!4&5&6&!7 | !0&!1&2&!3&4&!5&!6&7 | +!0&!1&2&!3&4&!5&6&!7 | !0&1&!2&!3&!4&5&!6&7 | !0&1&!2&!3&!4&5&6&!7 | +!0&1&!2&!3&4&!5&!6&7 | !0&1&!2&!3&4&!5&6&!7 | 0&!1&!2&!3&!4&5&!6&7 | +0&!1&!2&!3&!4&5&6&!7 | 0&!1&!2&!3&4&!5&!6&7 | 0&!1&!2&!3&4&!5&6&!7] 0 {1} +[!0&!1&2&3&!4&!5&!6&7 | !0&!1&2&3&!4&!5&6&!7 | !0&1&!2&3&!4&!5&!6&7 | +0&!1&!2&3&!4&!5&!6&7 | 0&!1&!2&3&!4&!5&6&!7] 2 {1} +[!0&1&!2&3&!4&!5&6&!7] 6 {1} +State: 20 +[!0&!1&2&!3&4&!5&!6&7 | !0&!1&2&!3&4&!5&6&!7 | !0&!1&2&3&!4&!5&!6&7 | +!0&!1&2&3&!4&!5&6&!7 | !0&1&!2&!3&4&!5&!6&7 | !0&1&!2&!3&4&!5&6&!7 | +!0&1&!2&3&!4&!5&!6&7 | !0&1&!2&3&!4&!5&6&!7 | 0&!1&!2&!3&4&!5&!6&7 | +0&!1&!2&!3&4&!5&6&!7 | 0&!1&!2&3&!4&!5&!6&7 | 0&!1&!2&3&!4&!5&6&!7] 0 {1} +[!0&!1&2&!3&!4&5&6&!7 | !0&1&!2&!3&!4&5&!6&7 | !0&1&!2&!3&!4&5&6&!7 | +0&!1&!2&!3&!4&5&!6&7 | 0&!1&!2&!3&!4&5&6&!7] 1 {1} +[!0&!1&2&!3&!4&5&!6&7] 3 {1} +State: 21 +[!0&!1&2&!3&!4&5&!6&7 | !0&!1&2&!3&!4&5&6&!7 | !0&1&!2&!3&!4&5&!6&7 | +!0&1&!2&!3&!4&5&6&!7 | 0&!1&!2&!3&!4&5&!6&7 | 0&!1&!2&!3&!4&5&6&!7] 1 {1} +[!0&!1&2&!3&4&!5&!6&7 | !0&!1&2&!3&4&!5&6&!7 | !0&!1&2&3&!4&!5&!6&7 | +!0&!1&2&3&!4&!5&6&!7 | !0&1&!2&!3&4&!5&!6&7 | !0&1&!2&!3&4&!5&6&!7 | +!0&1&!2&3&!4&!5&!6&7 | !0&1&!2&3&!4&!5&6&!7 | 0&!1&!2&!3&4&!5&!6&7 | +0&!1&!2&!3&4&!5&6&!7 | 0&!1&!2&3&!4&!5&!6&7 | 0&!1&!2&3&!4&!5&6&!7] 2 {1} +State: 22 +[!0&!1&2&!3&4&!5&!6&7 | !0&!1&2&!3&4&!5&6&!7 | !0&!1&2&3&!4&!5&!6&7 | +!0&!1&2&3&!4&!5&6&!7 | !0&1&!2&!3&4&!5&!6&7 | !0&1&!2&!3&4&!5&6&!7 | +!0&1&!2&3&!4&!5&!6&7 | !0&1&!2&3&!4&!5&6&!7 | 0&!1&!2&!3&4&!5&!6&7 | +0&!1&!2&!3&4&!5&6&!7 | 0&!1&!2&3&!4&!5&!6&7 | 0&!1&!2&3&!4&!5&6&!7] 0 {1} +[!0&!1&2&!3&!4&5&6&!7 | !0&1&!2&!3&!4&5&!6&7 | !0&1&!2&!3&!4&5&6&!7 | +0&!1&!2&!3&!4&5&!6&7 | 0&!1&!2&!3&!4&5&6&!7] 1 {1} +[!0&!1&2&!3&!4&5&!6&7] 4 {1} +State: 23 +[!0&!1&2&!3&4&!5&!6&7 | !0&!1&2&!3&4&!5&6&!7 | !0&!1&2&3&!4&!5&!6&7 | +!0&!1&2&3&!4&!5&6&!7 | !0&1&!2&!3&4&!5&!6&7 | !0&1&!2&!3&4&!5&6&!7 | +!0&1&!2&3&!4&!5&!6&7 | !0&1&!2&3&!4&!5&6&!7 | 0&!1&!2&!3&4&!5&!6&7 | +0&!1&!2&!3&4&!5&6&!7 | 0&!1&!2&3&!4&!5&!6&7 | 0&!1&!2&3&!4&!5&6&!7] 0 {1} +[!0&!1&2&!3&!4&5&!6&7 | !0&!1&2&!3&!4&5&6&!7 | !0&1&!2&!3&!4&5&!6&7 | +!0&1&!2&!3&!4&5&6&!7 | 0&!1&!2&!3&!4&5&!6&7 | 0&!1&!2&!3&!4&5&6&!7] 1 {1} +State: 24 +[!0&!1&2&!3&!4&5&!6&7 | !0&!1&2&!3&!4&5&6&!7 | !0&1&!2&!3&!4&5&6&!7 | +0&!1&!2&!3&!4&5&!6&7 | 0&!1&!2&!3&!4&5&6&!7] 1 {1} +[!0&!1&2&!3&4&!5&!6&7 | !0&!1&2&!3&4&!5&6&!7 | !0&!1&2&3&!4&!5&!6&7 | +!0&!1&2&3&!4&!5&6&!7 | !0&1&!2&!3&4&!5&6&!7 | !0&1&!2&3&!4&!5&6&!7 | +0&!1&!2&!3&4&!5&!6&7 | 0&!1&!2&!3&4&!5&6&!7 | 0&!1&!2&3&!4&!5&!6&7 | +0&!1&!2&3&!4&!5&6&!7] 2 {1} +[!0&1&!2&!3&!4&5&!6&7] 4 {1} +[!0&1&!2&!3&4&!5&!6&7 | !0&1&!2&3&!4&!5&!6&7] 6 {1} +State: 25 +[!0&!1&2&!3&!4&5&!6&7 | !0&!1&2&!3&!4&5&6&!7 | !0&1&!2&!3&!4&5&6&!7 | +0&!1&!2&!3&!4&5&!6&7 | 0&!1&!2&!3&!4&5&6&!7] 1 {1} +[!0&!1&2&!3&4&!5&!6&7 | !0&!1&2&!3&4&!5&6&!7 | !0&!1&2&3&!4&!5&!6&7 | +!0&!1&2&3&!4&!5&6&!7 | !0&1&!2&!3&4&!5&6&!7 | !0&1&!2&3&!4&!5&6&!7 | +0&!1&!2&!3&4&!5&!6&7 | 0&!1&!2&!3&4&!5&6&!7 | 0&!1&!2&3&!4&!5&!6&7 | +0&!1&!2&3&!4&!5&6&!7] 2 {1} +[!0&1&!2&!3&!4&5&!6&7] 3 {1} +[!0&1&!2&!3&4&!5&!6&7 | !0&1&!2&3&!4&!5&!6&7] 5 {1} +State: 26 +[!0&!1&2&!3&!4&5&!6&7 | !0&!1&2&!3&!4&5&6&!7 | !0&1&!2&!3&!4&5&!6&7 | +0&!1&!2&!3&!4&5&!6&7 | 0&!1&!2&!3&!4&5&6&!7] 1 {1} +[!0&!1&2&!3&4&!5&!6&7 | !0&!1&2&!3&4&!5&6&!7 | !0&!1&2&3&!4&!5&!6&7 | +!0&!1&2&3&!4&!5&6&!7 | !0&1&!2&!3&4&!5&!6&7 | !0&1&!2&3&!4&!5&!6&7 | +0&!1&!2&!3&4&!5&!6&7 | 0&!1&!2&!3&4&!5&6&!7 | 0&!1&!2&3&!4&!5&!6&7 | +0&!1&!2&3&!4&!5&6&!7] 2 {1} +[!0&1&!2&!3&!4&5&6&!7] 4 {1} +[!0&1&!2&!3&4&!5&6&!7 | !0&1&!2&3&!4&!5&6&!7] 6 {1} +--END--""") + +assert spot.solve_game(game, si) + +games = spot.split_edges(game) +spot.set_state_players(games, spot.get_state_players(game)) +assert spot.solve_game(games, si) From fa6912a5745ed02f883fba03406949080a5a997b Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Tue, 29 Mar 2022 11:13:19 +0200 Subject: [PATCH 048/337] debian: simplify LTO configuration to work around newer libtool Libtool 2.4.7 breaks if AR_FLAGS contains a space. See https://lists.gnu.org/archive/html/bug-libtool/2022-03/msg00009.html * debian/rules: Use gcc-{nm,ar,ranlib} so we do not have to pass the plugin explicitly. --- debian/rules | 22 +++++++++------------- 1 file changed, 9 insertions(+), 13 deletions(-) diff --git a/debian/rules b/debian/rules index 0193e9a62..51daf21ed 100755 --- a/debian/rules +++ b/debian/rules @@ -20,20 +20,16 @@ include /usr/share/dpkg/default.mk %: dh $@ --with=python3 -# Find the LTO plugin, which we need to pass to ar, nm, and ranlib. -LTOPLUG := $(shell gcc -v 2>&1 | \ - sed -n 's:COLLECT_LTO_WRAPPER=\(/.*/\)[^/]*:\1:p')liblto_plugin.so - # ARFLAGS is for Automake -# AR_FLAGS is for Libtool -# These activate the LTO pluggin, but also remove the 'u' option -# from ar, since its now ignored with Debian's default to 'D'. -LTOSETUP = \ - LDFLAGS='-fuse-linker-plugin' \ - NM='nm --plugin $(LTOPLUG)' \ - ARFLAGS='cr --plugin $(LTOPLUG)' \ - AR_FLAGS='cr --plugin $(LTOPLUG)' \ - RANLIB='ranlib --plugin $(LTOPLUG)' \ +# AR_FLAGS is for Libtool, (but libtool 2.4.7 will now use ARFLAGS as well) +# The gcc-tools activate the LTO plugin. +LTOSETUP = \ + LDFLAGS='-fuse-linker-plugin' \ + NM='gcc-nm' \ + AR='gcc-ar' \ + ARFLAGS='cr' \ + AR_FLAGS='cr' \ + RANLIB='gcc-ranlib' \ VALGRIND=false GCDADIR := $(shell pwd)/gcda From 0f3ffd59cec1f152733c07458078f8a21ca1edc8 Mon Sep 17 00:00:00 2001 From: Florian Renkin Date: Tue, 12 Apr 2022 11:21:14 +0200 Subject: [PATCH 049/337] ltlsynt: don't solve games when we want to display them * bin/ltlsynt.cc: here --- bin/ltlsynt.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/bin/ltlsynt.cc b/bin/ltlsynt.cc index f5b2ade51..5ffa22f39 100644 --- a/bin/ltlsynt.cc +++ b/bin/ltlsynt.cc @@ -395,6 +395,8 @@ namespace && "Env needs first turn"); } print_game(arena); + if (want_game) + continue; if (!spot::solve_game(arena, *gi)) { std::cout << "UNREALIZABLE" << std::endl; From 385da8ebd0c8ec01c3e69c76830a7a1462996c74 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Mon, 2 May 2022 17:47:53 +0200 Subject: [PATCH 050/337] update NEWS for upcoming release * NEWS: Here. --- NEWS | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/NEWS b/NEWS index 301711588..f857fb97d 100644 --- a/NEWS +++ b/NEWS @@ -1,13 +1,20 @@ New in spot 2.10.4.dev (net yet released) - Nothing yet. - Bugs fixed: - reduce_parity() produced incorrect results when applied to automata with deleted edges. - - work around a portability issue in Flex 2.6.4 preventing + - An optimization of Zielonka could result in incorrect results + in some cases. + + - ltlsynt --print-pg incorrectly solved the game in addition to + printing it. + + - ltlsynt would fail if only one of --ins or --outs was set, and + if it was set empty. + + - Work around a portability issue in Flex 2.6.4 preventing compilation on OpenBSD. - Do not use the seq command in test cases, it is not available @@ -16,6 +23,12 @@ New in spot 2.10.4.dev (net yet released) - Do not erase the previous contents of the PYTHONPATH environment variable when running tests, prepend to it instead. + - Simplify Debian instructions for LTO build to work around newer + libtool version. + + - Fix invalid read in digraph::sort_edges_of_(), currently unused in + Spot. + New in spot 2.10.4 (2022-02-01) Bug fixed: From c70a06ae0adbd16ca9f16f67b6892f7e4306a2e8 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Tue, 3 May 2022 08:59:17 +0200 Subject: [PATCH 051/337] Release Spot 2.10.5 * NEWS, configure.ac, doc/org/setup.org: Update. --- NEWS | 2 +- configure.ac | 2 +- doc/org/setup.org | 10 +++++----- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/NEWS b/NEWS index f857fb97d..8f512fcb7 100644 --- a/NEWS +++ b/NEWS @@ -1,4 +1,4 @@ -New in spot 2.10.4.dev (net yet released) +New in spot 2.10.5 (2022-05-03) Bugs fixed: diff --git a/configure.ac b/configure.ac index 31002ccda..5815a2c13 100644 --- a/configure.ac +++ b/configure.ac @@ -21,7 +21,7 @@ # along with this program. If not, see . AC_PREREQ([2.69]) -AC_INIT([spot], [2.10.4.dev], [spot@lrde.epita.fr]) +AC_INIT([spot], [2.10.5], [spot@lrde.epita.fr]) AC_CONFIG_AUX_DIR([tools]) AC_CONFIG_MACRO_DIR([m4]) AM_INIT_AUTOMAKE([1.11 gnu tar-ustar color-tests parallel-tests]) diff --git a/doc/org/setup.org b/doc/org/setup.org index 3b8b1b404..c1b7e9235 100644 --- a/doc/org/setup.org +++ b/doc/org/setup.org @@ -1,11 +1,11 @@ #+OPTIONS: H:2 num:nil toc:t html-postamble:nil ^:nil #+EMAIL: spot@lrde.epita.fr #+HTML_LINK_HOME: index.html -#+MACRO: SPOTVERSION 2.10.4 -#+MACRO: LASTRELEASE 2.10.4 -#+MACRO: LASTTARBALL [[http://www.lrde.epita.fr/dload/spot/spot-2.10.4.tar.gz][=spot-2.10.4.tar.gz=]] -#+MACRO: LASTNEWS [[https://gitlab.lrde.epita.fr/spot/spot/blob/spot-2-10-4/NEWS][summary of the changes]] -#+MACRO: LASTDATE 2022-02-01 +#+MACRO: SPOTVERSION 2.10.5 +#+MACRO: LASTRELEASE 2.10.5 +#+MACRO: LASTTARBALL [[http://www.lrde.epita.fr/dload/spot/spot-2.10.5.tar.gz][=spot-2.10.5.tar.gz=]] +#+MACRO: LASTNEWS [[https://gitlab.lrde.epita.fr/spot/spot/blob/spot-2-10-5/NEWS][summary of the changes]] +#+MACRO: LASTDATE 2022-05-03 #+ATTR_HTML: :id spotlogo [[file:spot2.svg]] From 56666e0db592c3528ddf015f783a81bf77f74fb2 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Tue, 3 May 2022 09:01:03 +0200 Subject: [PATCH 052/337] * NEWS, configure.ac: Bump version to 2.10.5.dev. --- NEWS | 4 ++++ configure.ac | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index 8f512fcb7..031df5b71 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,7 @@ +New in spot 2.10.5.dev (not yet released) + + Nothing yet. + New in spot 2.10.5 (2022-05-03) Bugs fixed: diff --git a/configure.ac b/configure.ac index 5815a2c13..6527900ce 100644 --- a/configure.ac +++ b/configure.ac @@ -21,7 +21,7 @@ # along with this program. If not, see . AC_PREREQ([2.69]) -AC_INIT([spot], [2.10.5], [spot@lrde.epita.fr]) +AC_INIT([spot], [2.10.5.dev], [spot@lrde.epita.fr]) AC_CONFIG_AUX_DIR([tools]) AC_CONFIG_MACRO_DIR([m4]) AM_INIT_AUTOMAKE([1.11 gnu tar-ustar color-tests parallel-tests]) From 4a2bdd6e86e526e976a8c06d11fdf875899a3d7e Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Wed, 4 May 2022 17:40:17 +0200 Subject: [PATCH 053/337] Fix link to parity game example Reported by Florian Renkin. * doc/org/index.org: Here. --- doc/org/index.org | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/org/index.org b/doc/org/index.org index f676b8aa4..9af23dba4 100644 --- a/doc/org/index.org +++ b/doc/org/index.org @@ -25,7 +25,7 @@ checking. It has the following notable features: weak-DBA, removal of useless SCCs, acceptance-condition transformations, determinization, [[file:satmin.org][SAT-based minimization of deterministic automata]], [[https://spot.lrde.epita.fr/ipynb/zlktree.html][Alternating Cycle Decomposition]], etc. -- Support for [[file:tut40.org][Safety]] and [[https://spot-dev.lrde.epita.fr/ipynb/games.html][parity games]]. +- Support for [[file:tut40.org][Safety]] and [[https://spot.lrde.epita.fr/ipynb/games.html][parity games]]. - Applications to [[file:ltlsynt.org][reactive synthesis]] and [[https://spot.lrde.epita.fr/ipynb/atva16-fig2b.html][model checking]]. - In addition to the C++ interface, most of its algorithms are usable via [[file:tools.org][command-line tools]], and via [[file:tut.org][Python bindings]]. From ef9267a58ef71659c37f94c2c7458c84c51bb195 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Fri, 6 May 2022 13:58:52 +0200 Subject: [PATCH 054/337] parsetl: remove a superfluous diagnostic on some erroneous input * tests/core/neverclaimread.test: Adjust and remove FIXME. * spot/parsetl/parsetl.yy (try_recursive_parse): Return false on empty string. --- spot/parsetl/parsetl.yy | 2 +- tests/core/neverclaimread.test | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/spot/parsetl/parsetl.yy b/spot/parsetl/parsetl.yy index e6defffb3..117695404 100644 --- a/spot/parsetl/parsetl.yy +++ b/spot/parsetl/parsetl.yy @@ -296,7 +296,7 @@ using namespace spot; if (str.empty()) { error_list.emplace_back(location, "unexpected empty block"); - return nullptr; + return fnode::ff(); } spot::parsed_formula pf; diff --git a/tests/core/neverclaimread.test b/tests/core/neverclaimread.test index bf736f55d..09af8af58 100755 --- a/tests/core/neverclaimread.test +++ b/tests/core/neverclaimread.test @@ -350,10 +350,8 @@ digraph "-" { } EOF diff stdout expected -# FIXME: the "ignoring trailing garbage" is unwanted cat >expected.err < Date: Mon, 9 May 2022 13:42:20 +0200 Subject: [PATCH 055/337] twagraph: improve copy of kripke_graph Fix #505, Reported by Edmond Irani Liu. * spot/twa/twagraph.cc (copy): Deal with kripke_graph in a better way. * spot/twaalgos/hoa.cc: Do not force the use of named-states since when the input is a kripke_graph. * tests/python/kripke.py: Adjust test cases. * NEWS: Mention the change. * THANKS: Add Edmund. --- NEWS | 5 +++++ THANKS | 1 + spot/twa/twagraph.cc | 13 ++++++++++++- spot/twaalgos/hoa.cc | 7 ++++++- tests/python/kripke.py | 42 ++++++++++++++++++++++++++++++------------ 5 files changed, 54 insertions(+), 14 deletions(-) diff --git a/NEWS b/NEWS index 098a4948b..5bc21f22d 100644 --- a/NEWS +++ b/NEWS @@ -82,6 +82,11 @@ New in spot 2.10.5.dev (not yet released) averted in the parser by delaying the construction of such n-ary nodes until all children are known. + - make_twa_graph() will now preserve state number when copying a + kripke_graph object. As a consequence, print_dot() and + print_hoa() will now use state numbers matching those of the + kripke_graph (issue #505). + New in spot 2.10.5 (2022-05-03) Bugs fixed: diff --git a/THANKS b/THANKS index b49b3eb95..4a7259d46 100644 --- a/THANKS +++ b/THANKS @@ -11,6 +11,7 @@ Christian Dax Christopher Ziegler Clément Tamines David Müller +Edmond Irani Liu Ernesto Posse Étienne Renault Fabrice Kordon diff --git a/spot/twa/twagraph.cc b/spot/twa/twagraph.cc index b11ca12c5..c2bdd5650 100644 --- a/spot/twa/twagraph.cc +++ b/spot/twa/twagraph.cc @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -1600,6 +1601,17 @@ namespace spot return p.first->second; }; + // If the input is a kripke_graph and the number of states is + // not restricted, predeclare all states to keep their + // numbering, and also copy unreachable states. + if (max_states == -1U) + if (auto kg = std::dynamic_pointer_cast(aut)) + { + unsigned ns = kg->num_states(); + for (unsigned s = 0; s < ns; ++s) + new_state(kg->state_from_number(s)); + } + out->set_init_state(new_state(aut->get_init_state())); while (!todo.empty()) { @@ -1638,7 +1650,6 @@ namespace spot } } - auto s = seen.begin(); while (s != seen.end()) { diff --git a/spot/twaalgos/hoa.cc b/spot/twaalgos/hoa.cc index 1865a6d49..0e03b07f5 100644 --- a/spot/twaalgos/hoa.cc +++ b/spot/twaalgos/hoa.cc @@ -31,6 +31,7 @@ #include #include #include +#include using namespace std::string_literals; @@ -973,7 +974,11 @@ namespace spot strcpy(tmpopt, opt); tmpopt[n] = 'k'; tmpopt[n + 1] = 0; - preserve_names = true; + // Preserve names if we have some state names, or if we are + // not a kripke_graph. + auto sn = aut->get_named_prop>("state-names"); + preserve_names = + !!sn || !std::dynamic_pointer_cast(aut); } auto a = std::dynamic_pointer_cast(aut); diff --git a/tests/python/kripke.py b/tests/python/kripke.py index fa92b3fa9..b1669bb78 100644 --- a/tests/python/kripke.py +++ b/tests/python/kripke.py @@ -29,35 +29,53 @@ p2 = buddy.bdd_ithvar(k.register_ap("p2")) cond1 = p1 & p2 cond2 = p1 & -p2 cond3 = -p1 & -p2 -s2 = k.new_state(cond1) +s0 = k.new_state(cond1) s1 = k.new_state(cond2) -s3 = k.new_state(cond3) +s2 = k.new_state(cond3) +k.new_edge(s1, s0) +k.new_edge(s0, s0) k.new_edge(s1, s2) k.new_edge(s2, s2) -k.new_edge(s1, s3) -k.new_edge(s3, s3) -k.new_edge(s3, s2) +k.new_edge(s2, s0) k.set_init_state(s1) hoa = """HOA: v1 States: 3 -Start: 0 +Start: 1 AP: 2 "p1" "p2" acc-name: all Acceptance: 0 t properties: state-labels explicit-labels state-acc --BODY-- -State: [0&!1] 0 "1" -1 2 -State: [0&1] 1 "0" -1 -State: [!0&!1] 2 "2" -2 1 +State: [0&1] 0 +0 +State: [0&!1] 1 +0 2 +State: [!0&!1] 2 +2 0 --END--""" tc.assertEqual(hoa, k.to_str('HOA')) tc.assertEqual(k.num_states(), 3) tc.assertEqual(k.num_edges(), 5) +k.set_state_names(["s0", "s1", "s2"]) +hoa = """HOA: v1 +States: 3 +Start: 1 +AP: 2 "p1" "p2" +acc-name: all +Acceptance: 0 t +properties: state-labels explicit-labels state-acc +--BODY-- +State: [0&1] 0 "s0" +0 +State: [0&!1] 1 "s1" +0 2 +State: [!0&!1] 2 "s2" +2 0 +--END--""" +tc.assertEqual(hoa, k.to_str('HOA')) + res = [] for e in k.out(s1): res.append((e.src, e.dst)) From 3b809c0a1471d68d4c42500c6362653ad88efafb Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Tue, 17 May 2022 09:27:30 +0200 Subject: [PATCH 056/337] Fix compilation on MacOS X Patch by Shachar Itzhaky. * spot/parseaut/scanaut.ll, spot/parsetl/scantl.ll: Include libc-config.h instead of config.h. * NEWS: Mention the fix. * THANKS: Add Shachar. --- NEWS | 4 ++++ THANKS | 5 +++-- spot/parseaut/scanaut.ll | 11 +++++++---- spot/parsetl/scantl.ll | 11 +++++++---- 4 files changed, 21 insertions(+), 10 deletions(-) diff --git a/NEWS b/NEWS index 5bc21f22d..a9804623f 100644 --- a/NEWS +++ b/NEWS @@ -87,6 +87,10 @@ New in spot 2.10.5.dev (not yet released) print_hoa() will now use state numbers matching those of the kripke_graph (issue #505). + Bug fixed: + + - Fix compilation error on MacOS X. + New in spot 2.10.5 (2022-05-03) Bugs fixed: diff --git a/THANKS b/THANKS index 4a7259d46..8a9c1b630 100644 --- a/THANKS +++ b/THANKS @@ -47,10 +47,11 @@ Paul Guénézan Reuben Rowe Roei Nahum Rüdiger Ehlers -Silien Hong -Simon Jantsch +Shachar Itzhaky Shengping Shaw Shufang Zhu +Silien Hong +Simon Jantsch Sonali Dutta Tereza Šťastná Tobias Meggendorfer. diff --git a/spot/parseaut/scanaut.ll b/spot/parseaut/scanaut.ll index 8cccaec0e..711c74c64 100644 --- a/spot/parseaut/scanaut.ll +++ b/spot/parseaut/scanaut.ll @@ -25,12 +25,15 @@ /* %option debug */ %top{ -#include "config.h" +#include "libc-config.h" /* Flex 2.6.4's test for relies on __STDC_VERSION__ which is undefined in C++. So without that, it will define - its own integer types, including a broken SIZE_MAX definition. - So let's define __STDC_VERSION__ to make sure gets - included. */ + its own integer types, including a broken SIZE_MAX definition that + breaks compilation on OpenBSD. So let's define __STDC_VERSION__ to + make sure gets included. Redefining __STDC_VERSION__ + this way can break all sort of macros defined in , so + we include "libc-config.h" instead of "config.h" above to define + those macros first. */ #if HAVE_INTTYPES_H && !(defined __STDC_VERSION__) # define __STDC_VERSION__ 199901L #endif diff --git a/spot/parsetl/scantl.ll b/spot/parsetl/scantl.ll index 871f1300d..33667a849 100644 --- a/spot/parsetl/scantl.ll +++ b/spot/parsetl/scantl.ll @@ -27,12 +27,15 @@ %option never-interactive %top{ -#include "config.h" +#include "libc-config.h" /* Flex 2.6.4's test for relies on __STDC_VERSION__ which is undefined in C++. So without that, it will define - its own integer types, including a broken SIZE_MAX definition. - So let's define __STDC_VERSION__ to make sure gets - included. */ + its own integer types, including a broken SIZE_MAX definition that + breaks compilation on OpenBSD. So let's define __STDC_VERSION__ to + make sure gets included. Redefining __STDC_VERSION__ + this way can break all sort of macros defined in , so + we include "libc-config.h" instead of "config.h" above to define + those macros first. */ #if HAVE_INTTYPES_H && !(defined __STDC_VERSION__) # define __STDC_VERSION__ 199901L #endif From d697f57a97c586afada6ff90f17bdc2cbc4cb408 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Tue, 17 May 2022 10:49:06 +0200 Subject: [PATCH 057/337] bin: introduce a non-binary job_type This will later help improve the handling of different input types of ltlsynt. * bin/common_finput.hh (job_type): New enum. (job::type): Use it. * bin/autcross.cc, bin/autfilt.cc, bin/common_finput.cc, bin/dstar2tgba.cc, bin/ltl2tgba.cc, bin/ltl2tgta.cc, bin/ltlcross.cc, bin/ltldo.cc, bin/ltlfilt.cc, bin/ltlgrind.cc, bin/ltlsynt.cc: Adjust to use the job_type enum instead of a boolean. --- bin/autcross.cc | 4 ++-- bin/autfilt.cc | 4 ++-- bin/common_finput.cc | 28 ++++++++++++++++++---------- bin/common_finput.hh | 14 +++++++++----- bin/dstar2tgba.cc | 6 +++--- bin/ltl2tgba.cc | 11 +++++------ bin/ltl2tgta.cc | 11 +++++------ bin/ltlcross.cc | 4 ++-- bin/ltldo.cc | 4 ++-- bin/ltlfilt.cc | 6 +++--- bin/ltlgrind.cc | 4 ++-- bin/ltlsynt.cc | 2 +- 12 files changed, 54 insertions(+), 44 deletions(-) diff --git a/bin/autcross.cc b/bin/autcross.cc index 2aade5e49..e2224643d 100644 --- a/bin/autcross.cc +++ b/bin/autcross.cc @@ -162,7 +162,7 @@ parse_opt(int key, char* arg, struct argp_state*) switch (key) { case 'F': - jobs.emplace_back(arg, true); + jobs.emplace_back(arg, job_type::AUT_FILENAME); break; case 'q': quiet = true; @@ -216,7 +216,7 @@ parse_opt(int key, char* arg, struct argp_state*) break; case ARGP_KEY_ARG: if (arg[0] == '-' && !arg[1]) - jobs.emplace_back(arg, true); + jobs.emplace_back(arg, job_type::AUT_FILENAME); else tools_push_autproc(arg); break; diff --git a/bin/autfilt.cc b/bin/autfilt.cc index 8fe95c396..74fe44220 100644 --- a/bin/autfilt.cc +++ b/bin/autfilt.cc @@ -761,7 +761,7 @@ parse_opt(int key, char* arg, struct argp_state*) automaton_format = Count; break; case 'F': - jobs.emplace_back(arg, true); + jobs.emplace_back(arg, job_type::AUT_FILENAME); break; case 'n': opt_max_count = to_pos_int(arg, "-n/--max-count"); @@ -1252,7 +1252,7 @@ parse_opt(int key, char* arg, struct argp_state*) opt_art_sccs_set = true; break; case ARGP_KEY_ARG: - jobs.emplace_back(arg, true); + jobs.emplace_back(arg, job_type::AUT_FILENAME); break; default: diff --git a/bin/common_finput.cc b/bin/common_finput.cc index 8df1fb028..559a5f312 100644 --- a/bin/common_finput.cc +++ b/bin/common_finput.cc @@ -1,6 +1,6 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2012-2017, 2019, 2021 Laboratoire de Recherche et -// Développement de l'Epita (LRDE). +// Copyright (C) 2012-2017, 2019, 2021, 2022 Laboratoire de Recherche +// et Développement de l'Epita (LRDE). // // This file is part of Spot, a model checking library. // @@ -68,10 +68,10 @@ parse_opt_finput(int key, char* arg, struct argp_state*) switch (key) { case 'f': - jobs.emplace_back(arg, false); + jobs.emplace_back(arg, job_type::LTL_STRING); break; case 'F': - jobs.emplace_back(arg, true); + jobs.emplace_back(arg, job_type::LTL_FILENAME); break; case OPT_LBT: lbt_input = true; @@ -358,10 +358,18 @@ job_processor::run() int error = 0; for (auto& j: jobs) { - if (!j.file_p) - error |= process_string(j.str); - else - error |= process_file(j.str); + switch (j.type) + { + case job_type::LTL_STRING: + error |= process_string(j.str); + break; + case job_type::LTL_FILENAME: + case job_type::AUT_FILENAME: + error |= process_file(j.str); + break; + default: + throw std::runtime_error("unexpected job type"); + } if (abort_run) break; } @@ -376,7 +384,7 @@ void check_no_formula() error(2, 0, "No formula to translate? Run '%s --help' for help.\n" "Use '%s -' to force reading formulas from the standard " "input.", program_name, program_name); - jobs.emplace_back("-", true); + jobs.emplace_back("-", job_type::LTL_FILENAME); } void check_no_automaton() @@ -387,5 +395,5 @@ void check_no_automaton() error(2, 0, "No automaton to process? Run '%s --help' for help.\n" "Use '%s -' to force reading automata from the standard " "input.", program_name, program_name); - jobs.emplace_back("-", true); + jobs.emplace_back("-", job_type::AUT_FILENAME); } diff --git a/bin/common_finput.hh b/bin/common_finput.hh index 5d8feb3ed..54ced7f7b 100644 --- a/bin/common_finput.hh +++ b/bin/common_finput.hh @@ -1,6 +1,6 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2012-2017 Laboratoire de Recherche et Développement -// de l'Epita (LRDE). +// Copyright (C) 2012-2017, 2022 Laboratoire de Recherche et +// Développement de l'Epita (LRDE). // // This file is part of Spot, a model checking library. // @@ -25,13 +25,17 @@ #include #include +enum class job_type : char { LTL_STRING, + LTL_FILENAME, + AUT_FILENAME }; + struct job { const char* str; - bool file_p; // true if str is a filename, false if it is a formula + job_type type; - job(const char* str, bool file_p) noexcept - : str(str), file_p(file_p) + job(const char* str, job_type type) noexcept + : str(str), type(type) { } }; diff --git a/bin/dstar2tgba.cc b/bin/dstar2tgba.cc index 3bf5b9393..1d5cf8762 100644 --- a/bin/dstar2tgba.cc +++ b/bin/dstar2tgba.cc @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2013-2019 Laboratoire de Recherche et Développement +// Copyright (C) 2013-2019, 2022 Laboratoire de Recherche et Développement // de l'Epita (LRDE). // // This file is part of Spot, a model checking library. @@ -89,7 +89,7 @@ parse_opt(int key, char* arg, struct argp_state*) switch (key) { case 'F': - jobs.emplace_back(arg, true); + jobs.emplace_back(arg, job_type::AUT_FILENAME); break; case 'x': { @@ -99,7 +99,7 @@ parse_opt(int key, char* arg, struct argp_state*) } break; case ARGP_KEY_ARG: - jobs.emplace_back(arg, true); + jobs.emplace_back(arg, job_type::AUT_FILENAME); break; default: return ARGP_ERR_UNKNOWN; diff --git a/bin/ltl2tgba.cc b/bin/ltl2tgba.cc index f3de65a56..ee3d9f777 100644 --- a/bin/ltl2tgba.cc +++ b/bin/ltl2tgba.cc @@ -1,6 +1,6 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2012-2019 Laboratoire de Recherche et Développement -// de l'Epita (LRDE). +// Copyright (C) 2012-2019, 2022 Laboratoire de Recherche et +// Développement de l'Epita (LRDE). // // This file is part of Spot, a model checking library. // @@ -105,10 +105,9 @@ parse_opt(int key, char* arg, struct argp_state*) break; case ARGP_KEY_ARG: // FIXME: use stat() to distinguish filename from string? - if (*arg == '-' && !arg[1]) - jobs.emplace_back(arg, true); - else - jobs.emplace_back(arg, false); + jobs.emplace_back(arg, ((*arg == '-' && !arg[1]) + ? job_type::LTL_FILENAME + : job_type::LTL_STRING)); break; default: diff --git a/bin/ltl2tgta.cc b/bin/ltl2tgta.cc index ad3a64299..e3f241385 100644 --- a/bin/ltl2tgta.cc +++ b/bin/ltl2tgta.cc @@ -1,6 +1,6 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2012-2020 Laboratoire de Recherche et Développement -// de l'Epita (LRDE). +// Copyright (C) 2012-2020, 2022 Laboratoire de Recherche et +// Développement de l'Epita (LRDE). // // This file is part of Spot, a model checking library. // @@ -148,10 +148,9 @@ parse_opt(int key, char* arg, struct argp_state*) break; case ARGP_KEY_ARG: // FIXME: use stat() to distinguish filename from string? - if (*arg == '-' && !arg[1]) - jobs.emplace_back(arg, true); - else - jobs.emplace_back(arg, false); + jobs.emplace_back(arg, ((*arg == '-' && !arg[1]) + ? job_type::LTL_FILENAME + : job_type::LTL_STRING)); break; default: diff --git a/bin/ltlcross.cc b/bin/ltlcross.cc index d36478837..396806f96 100644 --- a/bin/ltlcross.cc +++ b/bin/ltlcross.cc @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2012-2020 Laboratoire de Recherche et Développement +// Copyright (C) 2012-2020, 2022 Laboratoire de Recherche et Développement // de l'Epita (LRDE). // // This file is part of Spot, a model checking library. @@ -484,7 +484,7 @@ parse_opt(int key, char* arg, struct argp_state*) break; case ARGP_KEY_ARG: if (arg[0] == '-' && !arg[1]) - jobs.emplace_back(arg, true); + jobs.emplace_back(arg, job_type::LTL_FILENAME); else tools_push_trans(arg); break; diff --git a/bin/ltldo.cc b/bin/ltldo.cc index f57a528b2..705e71105 100644 --- a/bin/ltldo.cc +++ b/bin/ltldo.cc @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2015-2020 Laboratoire de Recherche et Développement +// Copyright (C) 2015-2020, 2022 Laboratoire de Recherche et Développement // de l'Epita (LRDE). // // This file is part of Spot, a model checking library. @@ -193,7 +193,7 @@ parse_opt(int key, char* arg, struct argp_state*) break; case ARGP_KEY_ARG: if (arg[0] == '-' && !arg[1]) - jobs.emplace_back(arg, true); + jobs.emplace_back(arg, job_type::LTL_FILENAME); else tools_push_trans(arg); break; diff --git a/bin/ltlfilt.cc b/bin/ltlfilt.cc index af9316192..b74f7bc0c 100644 --- a/bin/ltlfilt.cc +++ b/bin/ltlfilt.cc @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2012-2021 Laboratoire de Recherche et Développement +// Copyright (C) 2012-2022 Laboratoire de Recherche et Développement // de l'Epita (LRDE). // // This file is part of Spot, a model checking library. @@ -387,7 +387,7 @@ parse_opt(int key, char* arg, struct argp_state*) break; case ARGP_KEY_ARG: // FIXME: use stat() to distinguish filename from string? - jobs.emplace_back(arg, true); + jobs.emplace_back(arg, job_type::LTL_FILENAME); break; case OPT_ACCEPT_WORD: try @@ -876,7 +876,7 @@ main(int argc, char** argv) exit(err); if (jobs.empty()) - jobs.emplace_back("-", 1); + jobs.emplace_back("-", job_type::LTL_FILENAME); if (boolean_to_isop && simplification_level == 0) simplification_level = 1; diff --git a/bin/ltlgrind.cc b/bin/ltlgrind.cc index 393656b00..b59569a59 100644 --- a/bin/ltlgrind.cc +++ b/bin/ltlgrind.cc @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2014, 2015, 2016, 2017, 2018, 2019 Laboratoire de Recherche et +// Copyright (C) 2014-2019, 2022 Laboratoire de Recherche et // Développement de l'Epita (LRDE). // // This file is part of Spot, a model checking library. @@ -143,7 +143,7 @@ parse_opt(int key, char* arg, struct argp_state*) break; case ARGP_KEY_ARG: // FIXME: use stat() to distinguish filename from string? - jobs.emplace_back(arg, true); + jobs.emplace_back(arg, job_type::LTL_FILENAME); break; case OPT_AP2CONST: opt_all = 0; diff --git a/bin/ltlsynt.cc b/bin/ltlsynt.cc index 50bae5f9e..0d89c2fc5 100644 --- a/bin/ltlsynt.cc +++ b/bin/ltlsynt.cc @@ -776,7 +776,7 @@ main(int argc, char **argv) static char arg4[] = "fully"; char* command[] = { arg0, arg1, arg2, arg3, arg4, opt_tlsf, nullptr }; opt_tlsf_string = read_stdout_of_command(command); - jobs.emplace_back(opt_tlsf_string.c_str(), false); + jobs.emplace_back(opt_tlsf_string.c_str(), job_type::LTL_STRING); if (!all_input_aps.has_value() && !all_output_aps.has_value()) { From d35f7bd37cc10e40ae539969e237212def7be566 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Tue, 17 May 2022 11:11:23 +0200 Subject: [PATCH 058/337] bin: reset column specification between files * bin/common_finput.cc (job_processor::process_file): Reset col_to_read. * tests/core/ltlfilt.test: Test it. * NEWS: Mention the bug. --- NEWS | 5 ++++- bin/common_finput.cc | 2 ++ tests/core/ltlfilt.test | 16 +++++++++++++++- 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/NEWS b/NEWS index a9804623f..204ae4a3b 100644 --- a/NEWS +++ b/NEWS @@ -87,10 +87,13 @@ New in spot 2.10.5.dev (not yet released) print_hoa() will now use state numbers matching those of the kripke_graph (issue #505). - Bug fixed: + Bugs fixed: - Fix compilation error on MacOS X. + - Using -Ffile/N to read column N of a CSV file would not reset the + /N specification for the next file. + New in spot 2.10.5 (2022-05-03) Bugs fixed: diff --git a/bin/common_finput.cc b/bin/common_finput.cc index 559a5f312..6f09601e0 100644 --- a/bin/common_finput.cc +++ b/bin/common_finput.cc @@ -305,6 +305,8 @@ job_processor::process_stream(std::istream& is, int job_processor::process_file(const char* filename) { + col_to_read = 0; + // Special case for stdin. if (filename[0] == '-' && filename[1] == 0) return process_stream(std::cin, filename); diff --git a/tests/core/ltlfilt.test b/tests/core/ltlfilt.test index 501ae94b2..43d50ce06 100755 --- a/tests/core/ltlfilt.test +++ b/tests/core/ltlfilt.test @@ -1,6 +1,6 @@ #! /bin/sh # -*- coding: utf-8 -*- -# Copyright (C) 2013-2020 Laboratoire de Recherche et Développement de +# Copyright (C) 2013-2020, 2022 Laboratoire de Recherche et Développement de # l'Epita (LRDE). # # This file is part of Spot, a model checking library. @@ -562,3 +562,17 @@ f1=`genltl --pps-arbiter-standard=2` f2=`genltl --pps-arbiter-strict=2` run 1 ltlfilt -f "$f2" --implied-by "$f1" run 0 ltlfilt -f "$f1" --implied-by "$f2" + + +# Reading two different columns of the same file +echo a,b > file +run 0 ltlfilt -Ffile/1 -Ffile/2 --stats=%f >out +cat >expected < Date: Tue, 17 May 2022 11:17:37 +0200 Subject: [PATCH 059/337] * spot/twa/bdddict.hh (bdd_info): Add noexcept, suggested by gcc 12. --- spot/twa/bdddict.hh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/spot/twa/bdddict.hh b/spot/twa/bdddict.hh index c9b39d8a5..f9c2ed6df 100644 --- a/spot/twa/bdddict.hh +++ b/spot/twa/bdddict.hh @@ -1,6 +1,6 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2011-2017 Laboratoire de Recherche et Développement -// de l'Epita (LRDE). +// Copyright (C) 2011-2017, 2022 Laboratoire de Recherche et +// Développement de l'Epita (LRDE). // Copyright (C) 2003, 2004, 2006 Laboratoire d'Informatique de Paris // 6 (LIP6), département Systèmes Répartis Coopératifs (SRC), // Université Pierre et Marie Curie. @@ -78,7 +78,7 @@ namespace spot enum var_type { anon = 0, var, acc }; struct bdd_info { - bdd_info() : type(anon) {} + bdd_info() noexcept: type(anon) {} var_type type; formula f; // Used unless t==anon. ref_set refs; From 5dab2ede416e8e66583984988e857006b8e76baa Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Tue, 17 May 2022 11:37:29 +0200 Subject: [PATCH 060/337] [buddy] remove some unused variables * src/reorder.c (reorder_win2ite): Do not set c=1. --- buddy/src/reorder.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/buddy/src/reorder.c b/buddy/src/reorder.c index d61630801..b107c8b6a 100644 --- a/buddy/src/reorder.c +++ b/buddy/src/reorder.c @@ -210,7 +210,6 @@ static BddTree *reorder_win2ite(BddTree *t) { BddTree *this, *first=t; int lastsize; - int c=1; if (t == NULL) return t; @@ -246,7 +245,6 @@ static BddTree *reorder_win2ite(BddTree *t) if (verbose > 1) printf(" %d nodes\n", reorder_nodenum()); - c++; } while (reorder_nodenum() != lastsize); From 3a234e24ae487fd75175a27c4b80b11cd154bbb8 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Tue, 17 May 2022 11:43:55 +0200 Subject: [PATCH 061/337] fix warnings from clang-15 * spot/twa/acc.cc (acc_cond::acc_code::symmetries): Fix weird loop. * spot/twaalgos/aiger.cc (aig::circ_step): Replace & by &&. --- spot/twa/acc.cc | 4 ++-- spot/twaalgos/aiger.cc | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/spot/twa/acc.cc b/spot/twa/acc.cc index 5b7985d70..ce5d463aa 100644 --- a/spot/twa/acc.cc +++ b/spot/twa/acc.cc @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2015-2021 Laboratoire de Recherche et Développement +// Copyright (C) 2015-2022 Laboratoire de Recherche et Développement // de l'Epita. // // This file is part of Spot, a model checking library. @@ -1029,7 +1029,7 @@ namespace spot int base = ba.allocate_variables(umax+2); assert(base == 0); std::vector r; - for (unsigned i = 0; r.size() < umax; ++i) + while (r.size() < umax) r.emplace_back(bdd_ithvar(base++)); bdd bddcode = to_bdd(&r[0]); bdd tmp; diff --git a/spot/twaalgos/aiger.cc b/spot/twaalgos/aiger.cc index 660d5b46a..e3c3bb6c5 100644 --- a/spot/twaalgos/aiger.cc +++ b/spot/twaalgos/aiger.cc @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2017-2021 Laboratoire de Recherche et Développement +// Copyright (C) 2017-2022 Laboratoire de Recherche et Développement // de l'Epita (LRDE). // // This file is part of Spot, a model checking library. @@ -1398,7 +1398,7 @@ namespace spot { unsigned var_g = gate_var(i); state_[var_g] = state_[and_gates_[i].first] - & state_[and_gates_[i].second]; + && state_[and_gates_[i].second]; state_[aig_not(var_g)] = !state_[var_g]; } // Update latches From e8f496bb6c77d29b241dcaed9d265472cec5f25e Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Tue, 17 May 2022 12:01:11 +0200 Subject: [PATCH 062/337] Fix a clang++15 warning * spot/parseaut/parseaut.yy: Move the try-block inside the code of the constructors, so that they can refer to non-static members. --- spot/parseaut/parseaut.yy | 52 ++++++++++++++++++++++----------------- 1 file changed, 29 insertions(+), 23 deletions(-) diff --git a/spot/parseaut/parseaut.yy b/spot/parseaut/parseaut.yy index 1e3de6781..71ab8aaea 100644 --- a/spot/parseaut/parseaut.yy +++ b/spot/parseaut/parseaut.yy @@ -2618,45 +2618,51 @@ namespace spot { automaton_stream_parser::automaton_stream_parser(const std::string& name, automaton_parser_options opt) - try : filename_(name), opts_(opt) { - if (hoayyopen(name, &scanner_)) - throw std::runtime_error("Cannot open file "s + name); - } - catch (...) - { - hoayyclose(scanner_); - throw; + try + { + if (hoayyopen(name, &scanner_)) + throw std::runtime_error("Cannot open file "s + name); + } + catch (...) + { + hoayyclose(scanner_); + throw; + } } automaton_stream_parser::automaton_stream_parser(int fd, const std::string& name, automaton_parser_options opt) - try : filename_(name), opts_(opt) { - if (hoayyopen(fd, &scanner_)) - throw std::runtime_error("Cannot open file "s + name); - } - catch (...) - { - hoayyclose(scanner_); - throw; + try + { + if (hoayyopen(fd, &scanner_)) + throw std::runtime_error("Cannot open file "s + name); + } + catch (...) + { + hoayyclose(scanner_); + throw; + } } automaton_stream_parser::automaton_stream_parser(const char* data, const std::string& filename, automaton_parser_options opt) - try : filename_(filename), opts_(opt) { - hoayystring(data, &scanner_); - } - catch (...) - { - hoayyclose(scanner_); - throw; + try + { + hoayystring(data, &scanner_); + } + catch (...) + { + hoayyclose(scanner_); + throw; + } } automaton_stream_parser::~automaton_stream_parser() From e4f8226c62d4d3f6eebea4de15aa9261fcae3e00 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Tue, 17 May 2022 15:25:17 +0200 Subject: [PATCH 063/337] work around spurious gcc 12 "potentially null dereference" The issue seems to be inside std::vector's copy constructor, but it highlighted places in Spot were we could avoid this copy. * spot/twaalgos/ltl2taa.cc: Avoid some copies of std::vector. --- spot/twaalgos/ltl2taa.cc | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/spot/twaalgos/ltl2taa.cc b/spot/twaalgos/ltl2taa.cc index 9c10777a9..eaba49e92 100644 --- a/spot/twaalgos/ltl2taa.cc +++ b/spot/twaalgos/ltl2taa.cc @@ -61,7 +61,8 @@ namespace spot { std::vector empty; res_->create_transition(init_, empty); - succ_state ss = { empty, f, empty }; + succ_state ss; + ss.condition = f; succ_.emplace_back(ss); return; } @@ -76,7 +77,8 @@ namespace spot std::vector empty; taa_tgba::transition* t = res_->create_transition(init_, empty); res_->add_condition(t, f); - succ_state ss = { empty, f, empty }; + succ_state ss; + ss.condition = f; succ_.emplace_back(ss); return; } @@ -90,7 +92,7 @@ namespace spot return; dst.emplace_back(v.init_); res_->create_transition(init_, dst); - succ_state ss = { dst, formula::tt(), a }; + succ_state ss = { std::move(dst), formula::tt(), std::move(a) }; succ_.emplace_back(ss); return; } @@ -206,7 +208,7 @@ namespace spot } t = res_->create_transition(init_, u); res_->add_condition(t, f); - succ_state ss = { u, f, a }; + succ_state ss = { std::move(u), f, std::move(a) }; succ_.emplace_back(ss); } From a23b30abdcacedcd70387194030c2fb0f072e0c4 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Tue, 17 May 2022 16:59:01 +0200 Subject: [PATCH 064/337] GCC 12 warns too much about See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105562 * m4/gccwarn.m4: Compile a small regex and add -Wno-maybe-uninitialized if needed. --- m4/gccwarn.m4 | 39 +++++++++++++++++++++++++++++++-------- 1 file changed, 31 insertions(+), 8 deletions(-) diff --git a/m4/gccwarn.m4 b/m4/gccwarn.m4 index 4f719e55f..dc6969add 100644 --- a/m4/gccwarn.m4 +++ b/m4/gccwarn.m4 @@ -21,6 +21,7 @@ AC_DEFUN([CF_GXX_WARNINGS], cat > conftest.$ac_ext < +#include int main(int argc, char *argv[[]]) { // This string comparison is here to detect superfluous @@ -33,19 +34,26 @@ int main(int argc, char *argv[[]]) std::string a{"foo"}, b{"bar"}; if (b < a) return 1; + // GCC 12 has spurious warnings about ininialized values in regex. + // See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105562 + // We need -Wno-maybe-uninitialized in this case. + std::regex r{"a"}; + (void)r; return argv[[argc-1]] == nullptr; } EOF cf_save_CXXFLAGS="$CXXFLAGS" - ac_cv_prog_gxx_warn_flags="-W -Wall" + ac_cv_prog_gxx_warn_flags="-W -Werror" +dnl The following list has options of the form OPT:BAD:GOOD +dnl if -OPT fails we try -OPT -BAD. If -OPT succeeds we add -GOOD. for cf_opt in \ - Werror \ + Wall:Wno-maybe-uninitialized:\ Wint-to-void-pointer-cast \ Wzero-as-null-pointer-constant \ Wcast-align \ Wpointer-arith \ Wwrite-strings \ - Wcast-qual \ + Wcast-qual::DXTSTRINGDEFINES \ Wdocumentation \ Wmissing-declarations \ Wnoexcept \ @@ -58,11 +66,26 @@ EOF Wsuggest-override \ Wpedantic do - CXXFLAGS="$cf_save_CXXFLAGS $ac_cv_prog_gxx_warn_flags -$cf_opt" - if AC_TRY_EVAL(ac_compile); then - ac_cv_prog_gxx_warn_flags="$ac_cv_prog_gxx_warn_flags -$cf_opt" - test "$cf_opt" = Wcast-qual && ac_cv_prog_gxx_warn_flags="$ac_cv_prog_gxx_warn_flags -DXTSTRINGDEFINES" - fi + fopt=${cf_opt%%:*} + CXXFLAGS="$cf_save_CXXFLAGS $ac_cv_prog_gxx_warn_flags -$fopt" + if AC_TRY_EVAL(ac_compile); then + ac_cv_prog_gxx_warn_flags="$ac_cv_prog_gxx_warn_flags -$fopt" + case $cf_opt in + *:*:);; + *:*:*)ac_cv_prog_gxx_warn_flags="$ac_cv_prog_gxx_warn_flags -${cf_opt##*:}";; + esac + else + case $cf_opt in + *::*);; + *:*:*) + sopt=${cf_opt%:*} + sopt=${sopt#*:} + CXXFLAGS="$cf_save_CXXFLAGS $ac_cv_prog_gxx_warn_flags -$fopt -$sopt" + if AC_TRY_EVAL(ac_compile); then + ac_cv_prog_gxx_warn_flags="$ac_cv_prog_gxx_warn_flags -$fopt -$sopt" + fi;; + esac + fi done rm -f conftest* CXXFLAGS="$cf_save_CXXFLAGS"]) From 2a4e68bfae3c5a93f37c4f3ceb3cd23d3deee8e9 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Tue, 17 May 2022 17:43:27 +0200 Subject: [PATCH 065/337] more noexcept to pleace GCC * spot/bricks/brick-hashset (Row): Add noexcept. * bin/autcross.cc (out_statistics): Likewise. * bin/ltlcross.cc (statistics): Likewise. --- bin/autcross.cc | 3 +-- bin/ltlcross.cc | 2 +- spot/bricks/brick-hashset | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/bin/autcross.cc b/bin/autcross.cc index e2224643d..21d21f2c7 100644 --- a/bin/autcross.cc +++ b/bin/autcross.cc @@ -345,7 +345,6 @@ struct in_statistics struct out_statistics { - // If OK is false, output statistics are not available. bool ok; const char* status_str; @@ -353,7 +352,7 @@ struct out_statistics double time; aut_statistics output; - out_statistics() + out_statistics() noexcept : ok(false), status_str(nullptr), status_code(0), diff --git a/bin/ltlcross.cc b/bin/ltlcross.cc index 396806f96..8e1005db6 100644 --- a/bin/ltlcross.cc +++ b/bin/ltlcross.cc @@ -264,7 +264,7 @@ end_error() struct statistics { - statistics() + statistics() noexcept : ok(false), alternating(false), status_str(nullptr), diff --git a/spot/bricks/brick-hashset b/spot/bricks/brick-hashset index 1c97c9618..7763d29ae 100644 --- a/spot/bricks/brick-hashset +++ b/spot/bricks/brick-hashset @@ -583,7 +583,7 @@ struct _ConcurrentHashSet : HashSetBase< Cell > return begin() + size(); } - Row() : _data( nullptr ), _size( 0 ) {} + Row() noexcept : _data( nullptr ), _size( 0 ) {} ~Row() { free(); } }; From 9ae2e9c03d1c44c455f8aac5875f9ed7b56594ff Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Wed, 4 May 2022 17:40:17 +0200 Subject: [PATCH 066/337] Fix link to parity game example Reported by Florian Renkin. * doc/org/index.org: Here. --- doc/org/index.org | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/org/index.org b/doc/org/index.org index f676b8aa4..9af23dba4 100644 --- a/doc/org/index.org +++ b/doc/org/index.org @@ -25,7 +25,7 @@ checking. It has the following notable features: weak-DBA, removal of useless SCCs, acceptance-condition transformations, determinization, [[file:satmin.org][SAT-based minimization of deterministic automata]], [[https://spot.lrde.epita.fr/ipynb/zlktree.html][Alternating Cycle Decomposition]], etc. -- Support for [[file:tut40.org][Safety]] and [[https://spot-dev.lrde.epita.fr/ipynb/games.html][parity games]]. +- Support for [[file:tut40.org][Safety]] and [[https://spot.lrde.epita.fr/ipynb/games.html][parity games]]. - Applications to [[file:ltlsynt.org][reactive synthesis]] and [[https://spot.lrde.epita.fr/ipynb/atva16-fig2b.html][model checking]]. - In addition to the C++ interface, most of its algorithms are usable via [[file:tools.org][command-line tools]], and via [[file:tut.org][Python bindings]]. From 506442450eaece31d3d8a11ca80c6b2dd85c7579 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Fri, 6 May 2022 13:58:52 +0200 Subject: [PATCH 067/337] parsetl: remove a superfluous diagnostic on some erroneous input * tests/core/neverclaimread.test: Adjust and remove FIXME. * spot/parsetl/parsetl.yy (try_recursive_parse): Return false on empty string. --- spot/parsetl/parsetl.yy | 2 +- tests/core/neverclaimread.test | 2 -- 2 files changed, 1 insertion(+), 3 deletions(-) diff --git a/spot/parsetl/parsetl.yy b/spot/parsetl/parsetl.yy index bbcdedcb5..dda0866f1 100644 --- a/spot/parsetl/parsetl.yy +++ b/spot/parsetl/parsetl.yy @@ -164,7 +164,7 @@ using namespace spot; if (str.empty()) { error_list.emplace_back(location, "unexpected empty block"); - return nullptr; + return fnode::ff(); } spot::parsed_formula pf; diff --git a/tests/core/neverclaimread.test b/tests/core/neverclaimread.test index bf736f55d..09af8af58 100755 --- a/tests/core/neverclaimread.test +++ b/tests/core/neverclaimread.test @@ -350,10 +350,8 @@ digraph "-" { } EOF diff stdout expected -# FIXME: the "ignoring trailing garbage" is unwanted cat >expected.err < Date: Mon, 9 May 2022 13:42:20 +0200 Subject: [PATCH 068/337] twagraph: improve copy of kripke_graph Fix #505, Reported by Edmond Irani Liu. * spot/twa/twagraph.cc (copy): Deal with kripke_graph in a better way. * spot/twaalgos/hoa.cc: Do not force the use of named-states since when the input is a kripke_graph. * tests/python/kripke.py: Adjust test cases. * NEWS: Mention the change. * THANKS: Add Edmund. --- NEWS | 5 +++++ THANKS | 1 + spot/twa/twagraph.cc | 13 ++++++++++++- spot/twaalgos/hoa.cc | 7 ++++++- tests/python/kripke.py | 42 ++++++++++++++++++++++++++++++------------ 5 files changed, 54 insertions(+), 14 deletions(-) diff --git a/NEWS b/NEWS index 031df5b71..86b03b39c 100644 --- a/NEWS +++ b/NEWS @@ -2,6 +2,11 @@ New in spot 2.10.5.dev (not yet released) Nothing yet. + - make_twa_graph() will now preserve state number when copying a + kripke_graph object. As a consequence, print_dot() and + print_hoa() will now use state numbers matching those of the + kripke_graph (issue #505). + New in spot 2.10.5 (2022-05-03) Bugs fixed: diff --git a/THANKS b/THANKS index 9eb566483..ea99ede61 100644 --- a/THANKS +++ b/THANKS @@ -11,6 +11,7 @@ Christian Dax Christopher Ziegler Clément Tamines David Müller +Edmond Irani Liu Ernesto Posse Étienne Renault Fabrice Kordon diff --git a/spot/twa/twagraph.cc b/spot/twa/twagraph.cc index 1fbe76f77..ef1c64d2c 100644 --- a/spot/twa/twagraph.cc +++ b/spot/twa/twagraph.cc @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -1275,6 +1276,17 @@ namespace spot return p.first->second; }; + // If the input is a kripke_graph and the number of states is + // not restricted, predeclare all states to keep their + // numbering, and also copy unreachable states. + if (max_states == -1U) + if (auto kg = std::dynamic_pointer_cast(aut)) + { + unsigned ns = kg->num_states(); + for (unsigned s = 0; s < ns; ++s) + new_state(kg->state_from_number(s)); + } + out->set_init_state(new_state(aut->get_init_state())); while (!todo.empty()) { @@ -1313,7 +1325,6 @@ namespace spot } } - auto s = seen.begin(); while (s != seen.end()) { diff --git a/spot/twaalgos/hoa.cc b/spot/twaalgos/hoa.cc index 6c474fb2a..e3a378bfc 100644 --- a/spot/twaalgos/hoa.cc +++ b/spot/twaalgos/hoa.cc @@ -31,6 +31,7 @@ #include #include #include +#include using namespace std::string_literals; @@ -771,7 +772,11 @@ namespace spot strcpy(tmpopt, opt); tmpopt[n] = 'k'; tmpopt[n + 1] = 0; - preserve_names = true; + // Preserve names if we have some state names, or if we are + // not a kripke_graph. + auto sn = aut->get_named_prop>("state-names"); + preserve_names = + !!sn || !std::dynamic_pointer_cast(aut); } auto a = std::dynamic_pointer_cast(aut); diff --git a/tests/python/kripke.py b/tests/python/kripke.py index f3ce218b2..e1fe12169 100644 --- a/tests/python/kripke.py +++ b/tests/python/kripke.py @@ -26,35 +26,53 @@ p2 = buddy.bdd_ithvar(k.register_ap("p2")) cond1 = p1 & p2 cond2 = p1 & -p2 cond3 = -p1 & -p2 -s2 = k.new_state(cond1) +s0 = k.new_state(cond1) s1 = k.new_state(cond2) -s3 = k.new_state(cond3) +s2 = k.new_state(cond3) +k.new_edge(s1, s0) +k.new_edge(s0, s0) k.new_edge(s1, s2) k.new_edge(s2, s2) -k.new_edge(s1, s3) -k.new_edge(s3, s3) -k.new_edge(s3, s2) +k.new_edge(s2, s0) k.set_init_state(s1) hoa = """HOA: v1 States: 3 -Start: 0 +Start: 1 AP: 2 "p1" "p2" acc-name: all Acceptance: 0 t properties: state-labels explicit-labels state-acc --BODY-- -State: [0&!1] 0 "1" -1 2 -State: [0&1] 1 "0" -1 -State: [!0&!1] 2 "2" -2 1 +State: [0&1] 0 +0 +State: [0&!1] 1 +0 2 +State: [!0&!1] 2 +2 0 --END--""" assert hoa == k.to_str('HOA') assert k.num_states() == 3 assert k.num_edges() == 5 +k.set_state_names(["s0", "s1", "s2"]) +hoa = """HOA: v1 +States: 3 +Start: 1 +AP: 2 "p1" "p2" +acc-name: all +Acceptance: 0 t +properties: state-labels explicit-labels state-acc +--BODY-- +State: [0&1] 0 "s0" +0 +State: [0&!1] 1 "s1" +0 2 +State: [!0&!1] 2 "s2" +2 0 +--END--""" +assert hoa == k.to_str('HOA') + res = [] for e in k.out(s1): res.append((e.src, e.dst)) From f14b0bb4bd0df63e3b002126f57324ee6f2b490c Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Tue, 17 May 2022 09:27:30 +0200 Subject: [PATCH 069/337] Fix compilation on MacOS X Patch by Shachar Itzhaky. * spot/parseaut/scanaut.ll, spot/parsetl/scantl.ll: Include libc-config.h instead of config.h. * NEWS: Mention the fix. * THANKS: Add Shachar. --- NEWS | 4 ++++ THANKS | 5 +++-- spot/parseaut/scanaut.ll | 11 +++++++---- spot/parsetl/scantl.ll | 11 +++++++---- 4 files changed, 21 insertions(+), 10 deletions(-) diff --git a/NEWS b/NEWS index 86b03b39c..79f1c890f 100644 --- a/NEWS +++ b/NEWS @@ -7,6 +7,10 @@ New in spot 2.10.5.dev (not yet released) print_hoa() will now use state numbers matching those of the kripke_graph (issue #505). + Bug fixed: + + - Fix compilation error on MacOS X. + New in spot 2.10.5 (2022-05-03) Bugs fixed: diff --git a/THANKS b/THANKS index ea99ede61..0258eb7c9 100644 --- a/THANKS +++ b/THANKS @@ -46,10 +46,11 @@ Paul Guénézan Reuben Rowe Roei Nahum Rüdiger Ehlers -Silien Hong -Simon Jantsch +Shachar Itzhaky Shengping Shaw Shufang Zhu +Silien Hong +Simon Jantsch Sonali Dutta Tereza Šťastná Tobias Meggendorfer. diff --git a/spot/parseaut/scanaut.ll b/spot/parseaut/scanaut.ll index bf35810ed..71e677044 100644 --- a/spot/parseaut/scanaut.ll +++ b/spot/parseaut/scanaut.ll @@ -25,12 +25,15 @@ /* %option debug */ %top{ -#include "config.h" +#include "libc-config.h" /* Flex 2.6.4's test for relies on __STDC_VERSION__ which is undefined in C++. So without that, it will define - its own integer types, including a broken SIZE_MAX definition. - So let's define __STDC_VERSION__ to make sure gets - included. */ + its own integer types, including a broken SIZE_MAX definition that + breaks compilation on OpenBSD. So let's define __STDC_VERSION__ to + make sure gets included. Redefining __STDC_VERSION__ + this way can break all sort of macros defined in , so + we include "libc-config.h" instead of "config.h" above to define + those macros first. */ #if HAVE_INTTYPES_H && !(defined __STDC_VERSION__) # define __STDC_VERSION__ 199901L #endif diff --git a/spot/parsetl/scantl.ll b/spot/parsetl/scantl.ll index 554c28298..b315f636c 100644 --- a/spot/parsetl/scantl.ll +++ b/spot/parsetl/scantl.ll @@ -27,12 +27,15 @@ %option never-interactive %top{ -#include "config.h" +#include "libc-config.h" /* Flex 2.6.4's test for relies on __STDC_VERSION__ which is undefined in C++. So without that, it will define - its own integer types, including a broken SIZE_MAX definition. - So let's define __STDC_VERSION__ to make sure gets - included. */ + its own integer types, including a broken SIZE_MAX definition that + breaks compilation on OpenBSD. So let's define __STDC_VERSION__ to + make sure gets included. Redefining __STDC_VERSION__ + this way can break all sort of macros defined in , so + we include "libc-config.h" instead of "config.h" above to define + those macros first. */ #if HAVE_INTTYPES_H && !(defined __STDC_VERSION__) # define __STDC_VERSION__ 199901L #endif From cb5bc38f35d70990a0e79f9e94ae5e6332d06bf1 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Tue, 17 May 2022 11:11:23 +0200 Subject: [PATCH 070/337] bin: reset column specification between files * bin/common_finput.cc (job_processor::process_file): Reset col_to_read. * tests/core/ltlfilt.test: Test it. * NEWS: Mention the bug. --- NEWS | 5 ++++- bin/common_finput.cc | 2 ++ tests/core/ltlfilt.test | 16 +++++++++++++++- 3 files changed, 21 insertions(+), 2 deletions(-) diff --git a/NEWS b/NEWS index 79f1c890f..1ffa26ad5 100644 --- a/NEWS +++ b/NEWS @@ -7,10 +7,13 @@ New in spot 2.10.5.dev (not yet released) print_hoa() will now use state numbers matching those of the kripke_graph (issue #505). - Bug fixed: + Bugs fixed: - Fix compilation error on MacOS X. + - Using -Ffile/N to read column N of a CSV file would not reset the + /N specification for the next file. + New in spot 2.10.5 (2022-05-03) Bugs fixed: diff --git a/bin/common_finput.cc b/bin/common_finput.cc index 8df1fb028..aae9e5b63 100644 --- a/bin/common_finput.cc +++ b/bin/common_finput.cc @@ -305,6 +305,8 @@ job_processor::process_stream(std::istream& is, int job_processor::process_file(const char* filename) { + col_to_read = 0; + // Special case for stdin. if (filename[0] == '-' && filename[1] == 0) return process_stream(std::cin, filename); diff --git a/tests/core/ltlfilt.test b/tests/core/ltlfilt.test index 501ae94b2..43d50ce06 100755 --- a/tests/core/ltlfilt.test +++ b/tests/core/ltlfilt.test @@ -1,6 +1,6 @@ #! /bin/sh # -*- coding: utf-8 -*- -# Copyright (C) 2013-2020 Laboratoire de Recherche et Développement de +# Copyright (C) 2013-2020, 2022 Laboratoire de Recherche et Développement de # l'Epita (LRDE). # # This file is part of Spot, a model checking library. @@ -562,3 +562,17 @@ f1=`genltl --pps-arbiter-standard=2` f2=`genltl --pps-arbiter-strict=2` run 1 ltlfilt -f "$f2" --implied-by "$f1" run 0 ltlfilt -f "$f1" --implied-by "$f2" + + +# Reading two different columns of the same file +echo a,b > file +run 0 ltlfilt -Ffile/1 -Ffile/2 --stats=%f >out +cat >expected < Date: Tue, 17 May 2022 11:17:37 +0200 Subject: [PATCH 071/337] * spot/twa/bdddict.hh (bdd_info): Add noexcept, suggested by gcc 12. --- spot/twa/bdddict.hh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/spot/twa/bdddict.hh b/spot/twa/bdddict.hh index c9b39d8a5..f9c2ed6df 100644 --- a/spot/twa/bdddict.hh +++ b/spot/twa/bdddict.hh @@ -1,6 +1,6 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2011-2017 Laboratoire de Recherche et Développement -// de l'Epita (LRDE). +// Copyright (C) 2011-2017, 2022 Laboratoire de Recherche et +// Développement de l'Epita (LRDE). // Copyright (C) 2003, 2004, 2006 Laboratoire d'Informatique de Paris // 6 (LIP6), département Systèmes Répartis Coopératifs (SRC), // Université Pierre et Marie Curie. @@ -78,7 +78,7 @@ namespace spot enum var_type { anon = 0, var, acc }; struct bdd_info { - bdd_info() : type(anon) {} + bdd_info() noexcept: type(anon) {} var_type type; formula f; // Used unless t==anon. ref_set refs; From b5f8e3c75df8528a7030696c4fdb50aa33426970 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Tue, 17 May 2022 11:37:29 +0200 Subject: [PATCH 072/337] [buddy] remove some unused variables * src/reorder.c (reorder_win2ite): Do not set c=1. --- buddy/src/reorder.c | 2 -- 1 file changed, 2 deletions(-) diff --git a/buddy/src/reorder.c b/buddy/src/reorder.c index d61630801..b107c8b6a 100644 --- a/buddy/src/reorder.c +++ b/buddy/src/reorder.c @@ -210,7 +210,6 @@ static BddTree *reorder_win2ite(BddTree *t) { BddTree *this, *first=t; int lastsize; - int c=1; if (t == NULL) return t; @@ -246,7 +245,6 @@ static BddTree *reorder_win2ite(BddTree *t) if (verbose > 1) printf(" %d nodes\n", reorder_nodenum()); - c++; } while (reorder_nodenum() != lastsize); From 8a628d96bcbf0d43d5de0f9fe349777e7f70df3f Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Tue, 17 May 2022 11:43:55 +0200 Subject: [PATCH 073/337] fix warnings from clang-15 * spot/twa/acc.cc (acc_cond::acc_code::symmetries): Fix weird loop. * spot/twaalgos/aiger.cc (aig::circ_step): Replace & by &&. --- spot/twa/acc.cc | 4 ++-- spot/twaalgos/aiger.cc | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/spot/twa/acc.cc b/spot/twa/acc.cc index 5b7985d70..ce5d463aa 100644 --- a/spot/twa/acc.cc +++ b/spot/twa/acc.cc @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2015-2021 Laboratoire de Recherche et Développement +// Copyright (C) 2015-2022 Laboratoire de Recherche et Développement // de l'Epita. // // This file is part of Spot, a model checking library. @@ -1029,7 +1029,7 @@ namespace spot int base = ba.allocate_variables(umax+2); assert(base == 0); std::vector r; - for (unsigned i = 0; r.size() < umax; ++i) + while (r.size() < umax) r.emplace_back(bdd_ithvar(base++)); bdd bddcode = to_bdd(&r[0]); bdd tmp; diff --git a/spot/twaalgos/aiger.cc b/spot/twaalgos/aiger.cc index ae7b665e9..8d730c34d 100644 --- a/spot/twaalgos/aiger.cc +++ b/spot/twaalgos/aiger.cc @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2017-2021 Laboratoire de Recherche et Développement +// Copyright (C) 2017-2022 Laboratoire de Recherche et Développement // de l'Epita (LRDE). // // This file is part of Spot, a model checking library. @@ -1359,7 +1359,7 @@ namespace spot { unsigned var_g = gate_var(i); state_[var_g] = state_[and_gates_[i].first] - & state_[and_gates_[i].second]; + && state_[and_gates_[i].second]; state_[aig_not(var_g)] = !state_[var_g]; } // Update latches From eecb9af21ea167346d897a652168c1b3e478dd58 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Tue, 17 May 2022 12:01:11 +0200 Subject: [PATCH 074/337] Fix a clang++15 warning * spot/parseaut/parseaut.yy: Move the try-block inside the code of the constructors, so that they can refer to non-static members. --- spot/parseaut/parseaut.yy | 52 ++++++++++++++++++++++----------------- 1 file changed, 29 insertions(+), 23 deletions(-) diff --git a/spot/parseaut/parseaut.yy b/spot/parseaut/parseaut.yy index c71636bde..4f57fcd35 100644 --- a/spot/parseaut/parseaut.yy +++ b/spot/parseaut/parseaut.yy @@ -2552,45 +2552,51 @@ namespace spot { automaton_stream_parser::automaton_stream_parser(const std::string& name, automaton_parser_options opt) - try : filename_(name), opts_(opt) { - if (hoayyopen(name, &scanner_)) - throw std::runtime_error("Cannot open file "s + name); - } - catch (...) - { - hoayyclose(scanner_); - throw; + try + { + if (hoayyopen(name, &scanner_)) + throw std::runtime_error("Cannot open file "s + name); + } + catch (...) + { + hoayyclose(scanner_); + throw; + } } automaton_stream_parser::automaton_stream_parser(int fd, const std::string& name, automaton_parser_options opt) - try : filename_(name), opts_(opt) { - if (hoayyopen(fd, &scanner_)) - throw std::runtime_error("Cannot open file "s + name); - } - catch (...) - { - hoayyclose(scanner_); - throw; + try + { + if (hoayyopen(fd, &scanner_)) + throw std::runtime_error("Cannot open file "s + name); + } + catch (...) + { + hoayyclose(scanner_); + throw; + } } automaton_stream_parser::automaton_stream_parser(const char* data, const std::string& filename, automaton_parser_options opt) - try : filename_(filename), opts_(opt) { - hoayystring(data, &scanner_); - } - catch (...) - { - hoayyclose(scanner_); - throw; + try + { + hoayystring(data, &scanner_); + } + catch (...) + { + hoayyclose(scanner_); + throw; + } } automaton_stream_parser::~automaton_stream_parser() From 10bc253dd855801d71765dcb0956060092c64e79 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Tue, 17 May 2022 15:25:17 +0200 Subject: [PATCH 075/337] work around spurious gcc 12 "potentially null dereference" The issue seems to be inside std::vector's copy constructor, but it highlighted places in Spot were we could avoid this copy. * spot/twaalgos/ltl2taa.cc: Avoid some copies of std::vector. --- spot/twaalgos/ltl2taa.cc | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/spot/twaalgos/ltl2taa.cc b/spot/twaalgos/ltl2taa.cc index 9c10777a9..eaba49e92 100644 --- a/spot/twaalgos/ltl2taa.cc +++ b/spot/twaalgos/ltl2taa.cc @@ -61,7 +61,8 @@ namespace spot { std::vector empty; res_->create_transition(init_, empty); - succ_state ss = { empty, f, empty }; + succ_state ss; + ss.condition = f; succ_.emplace_back(ss); return; } @@ -76,7 +77,8 @@ namespace spot std::vector empty; taa_tgba::transition* t = res_->create_transition(init_, empty); res_->add_condition(t, f); - succ_state ss = { empty, f, empty }; + succ_state ss; + ss.condition = f; succ_.emplace_back(ss); return; } @@ -90,7 +92,7 @@ namespace spot return; dst.emplace_back(v.init_); res_->create_transition(init_, dst); - succ_state ss = { dst, formula::tt(), a }; + succ_state ss = { std::move(dst), formula::tt(), std::move(a) }; succ_.emplace_back(ss); return; } @@ -206,7 +208,7 @@ namespace spot } t = res_->create_transition(init_, u); res_->add_condition(t, f); - succ_state ss = { u, f, a }; + succ_state ss = { std::move(u), f, std::move(a) }; succ_.emplace_back(ss); } From 99d030f5e1328f787888e94f13a6127cdcc8f3fd Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Tue, 17 May 2022 16:59:01 +0200 Subject: [PATCH 076/337] GCC 12 warns too much about See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105562 * m4/gccwarn.m4: Compile a small regex and add -Wno-maybe-uninitialized if needed. --- m4/gccwarn.m4 | 39 +++++++++++++++++++++++++++++++-------- 1 file changed, 31 insertions(+), 8 deletions(-) diff --git a/m4/gccwarn.m4 b/m4/gccwarn.m4 index 4f719e55f..dc6969add 100644 --- a/m4/gccwarn.m4 +++ b/m4/gccwarn.m4 @@ -21,6 +21,7 @@ AC_DEFUN([CF_GXX_WARNINGS], cat > conftest.$ac_ext < +#include int main(int argc, char *argv[[]]) { // This string comparison is here to detect superfluous @@ -33,19 +34,26 @@ int main(int argc, char *argv[[]]) std::string a{"foo"}, b{"bar"}; if (b < a) return 1; + // GCC 12 has spurious warnings about ininialized values in regex. + // See https://gcc.gnu.org/bugzilla/show_bug.cgi?id=105562 + // We need -Wno-maybe-uninitialized in this case. + std::regex r{"a"}; + (void)r; return argv[[argc-1]] == nullptr; } EOF cf_save_CXXFLAGS="$CXXFLAGS" - ac_cv_prog_gxx_warn_flags="-W -Wall" + ac_cv_prog_gxx_warn_flags="-W -Werror" +dnl The following list has options of the form OPT:BAD:GOOD +dnl if -OPT fails we try -OPT -BAD. If -OPT succeeds we add -GOOD. for cf_opt in \ - Werror \ + Wall:Wno-maybe-uninitialized:\ Wint-to-void-pointer-cast \ Wzero-as-null-pointer-constant \ Wcast-align \ Wpointer-arith \ Wwrite-strings \ - Wcast-qual \ + Wcast-qual::DXTSTRINGDEFINES \ Wdocumentation \ Wmissing-declarations \ Wnoexcept \ @@ -58,11 +66,26 @@ EOF Wsuggest-override \ Wpedantic do - CXXFLAGS="$cf_save_CXXFLAGS $ac_cv_prog_gxx_warn_flags -$cf_opt" - if AC_TRY_EVAL(ac_compile); then - ac_cv_prog_gxx_warn_flags="$ac_cv_prog_gxx_warn_flags -$cf_opt" - test "$cf_opt" = Wcast-qual && ac_cv_prog_gxx_warn_flags="$ac_cv_prog_gxx_warn_flags -DXTSTRINGDEFINES" - fi + fopt=${cf_opt%%:*} + CXXFLAGS="$cf_save_CXXFLAGS $ac_cv_prog_gxx_warn_flags -$fopt" + if AC_TRY_EVAL(ac_compile); then + ac_cv_prog_gxx_warn_flags="$ac_cv_prog_gxx_warn_flags -$fopt" + case $cf_opt in + *:*:);; + *:*:*)ac_cv_prog_gxx_warn_flags="$ac_cv_prog_gxx_warn_flags -${cf_opt##*:}";; + esac + else + case $cf_opt in + *::*);; + *:*:*) + sopt=${cf_opt%:*} + sopt=${sopt#*:} + CXXFLAGS="$cf_save_CXXFLAGS $ac_cv_prog_gxx_warn_flags -$fopt -$sopt" + if AC_TRY_EVAL(ac_compile); then + ac_cv_prog_gxx_warn_flags="$ac_cv_prog_gxx_warn_flags -$fopt -$sopt" + fi;; + esac + fi done rm -f conftest* CXXFLAGS="$cf_save_CXXFLAGS"]) From a6639e56cb3ce5be884c1f068479aedb67c89e9b Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Tue, 17 May 2022 17:43:27 +0200 Subject: [PATCH 077/337] more noexcept to pleace GCC * spot/bricks/brick-hashset (Row): Add noexcept. * bin/autcross.cc (out_statistics): Likewise. * bin/ltlcross.cc (statistics): Likewise. --- bin/autcross.cc | 3 +-- bin/ltlcross.cc | 2 +- spot/bricks/brick-hashset | 2 +- 3 files changed, 3 insertions(+), 4 deletions(-) diff --git a/bin/autcross.cc b/bin/autcross.cc index 81b6bcef5..0711562a2 100644 --- a/bin/autcross.cc +++ b/bin/autcross.cc @@ -338,7 +338,6 @@ struct in_statistics struct out_statistics { - // If OK is false, output statistics are not available. bool ok; const char* status_str; @@ -346,7 +345,7 @@ struct out_statistics double time; aut_statistics output; - out_statistics() + out_statistics() noexcept : ok(false), status_str(nullptr), status_code(0), diff --git a/bin/ltlcross.cc b/bin/ltlcross.cc index d36478837..f75075f82 100644 --- a/bin/ltlcross.cc +++ b/bin/ltlcross.cc @@ -264,7 +264,7 @@ end_error() struct statistics { - statistics() + statistics() noexcept : ok(false), alternating(false), status_str(nullptr), diff --git a/spot/bricks/brick-hashset b/spot/bricks/brick-hashset index 1c97c9618..7763d29ae 100644 --- a/spot/bricks/brick-hashset +++ b/spot/bricks/brick-hashset @@ -583,7 +583,7 @@ struct _ConcurrentHashSet : HashSetBase< Cell > return begin() + size(); } - Row() : _data( nullptr ), _size( 0 ) {} + Row() noexcept : _data( nullptr ), _size( 0 ) {} ~Row() { free(); } }; From e0de77d8a404014cbad5340ece60eccc02aa6530 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Wed, 18 May 2022 07:12:05 +0200 Subject: [PATCH 078/337] Release Spot 2.10.6 * NEWS, configure.ac, doc/org/setup.org: Set version to 2.10.6. --- NEWS | 17 +++++++++-------- configure.ac | 2 +- doc/org/setup.org | 10 +++++----- 3 files changed, 15 insertions(+), 14 deletions(-) diff --git a/NEWS b/NEWS index 1ffa26ad5..07f188ac2 100644 --- a/NEWS +++ b/NEWS @@ -1,11 +1,4 @@ -New in spot 2.10.5.dev (not yet released) - - Nothing yet. - - - make_twa_graph() will now preserve state number when copying a - kripke_graph object. As a consequence, print_dot() and - print_hoa() will now use state numbers matching those of the - kripke_graph (issue #505). +New in spot 2.10.6 (2022-05-18) Bugs fixed: @@ -14,6 +7,14 @@ New in spot 2.10.5.dev (not yet released) - Using -Ffile/N to read column N of a CSV file would not reset the /N specification for the next file. + - make_twa_graph() will now preserve state number when copying a + kripke_graph object. As a consequence, print_dot() and + print_hoa() will now use state numbers matching those of the + kripke_graph (issue #505). + + - Fix several compilation warning introduced by newer versions + of GCC and Clang. + New in spot 2.10.5 (2022-05-03) Bugs fixed: diff --git a/configure.ac b/configure.ac index 6527900ce..e14698a3f 100644 --- a/configure.ac +++ b/configure.ac @@ -21,7 +21,7 @@ # along with this program. If not, see . AC_PREREQ([2.69]) -AC_INIT([spot], [2.10.5.dev], [spot@lrde.epita.fr]) +AC_INIT([spot], [2.10.6], [spot@lrde.epita.fr]) AC_CONFIG_AUX_DIR([tools]) AC_CONFIG_MACRO_DIR([m4]) AM_INIT_AUTOMAKE([1.11 gnu tar-ustar color-tests parallel-tests]) diff --git a/doc/org/setup.org b/doc/org/setup.org index c1b7e9235..52aa02639 100644 --- a/doc/org/setup.org +++ b/doc/org/setup.org @@ -1,11 +1,11 @@ #+OPTIONS: H:2 num:nil toc:t html-postamble:nil ^:nil #+EMAIL: spot@lrde.epita.fr #+HTML_LINK_HOME: index.html -#+MACRO: SPOTVERSION 2.10.5 -#+MACRO: LASTRELEASE 2.10.5 -#+MACRO: LASTTARBALL [[http://www.lrde.epita.fr/dload/spot/spot-2.10.5.tar.gz][=spot-2.10.5.tar.gz=]] -#+MACRO: LASTNEWS [[https://gitlab.lrde.epita.fr/spot/spot/blob/spot-2-10-5/NEWS][summary of the changes]] -#+MACRO: LASTDATE 2022-05-03 +#+MACRO: SPOTVERSION 2.10.6 +#+MACRO: LASTRELEASE 2.10.6 +#+MACRO: LASTTARBALL [[http://www.lrde.epita.fr/dload/spot/spot-2.10.6.tar.gz][=spot-2.10.6.tar.gz=]] +#+MACRO: LASTNEWS [[https://gitlab.lrde.epita.fr/spot/spot/blob/spot-2-10-6/NEWS][summary of the changes]] +#+MACRO: LASTDATE 2022-05-18 #+ATTR_HTML: :id spotlogo [[file:spot2.svg]] From 457839df36762ce04a257e9eb06dcbe363212d55 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Wed, 18 May 2022 07:14:58 +0200 Subject: [PATCH 079/337] * NEWS, configure.ac: Bump version to 2.10.6. --- NEWS | 4 ++++ configure.ac | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index 07f188ac2..1d656e05f 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,7 @@ +New in spot 2.10.6.dev (not yet released) + + Nothing yet. + New in spot 2.10.6 (2022-05-18) Bugs fixed: diff --git a/configure.ac b/configure.ac index e14698a3f..162bf463b 100644 --- a/configure.ac +++ b/configure.ac @@ -21,7 +21,7 @@ # along with this program. If not, see . AC_PREREQ([2.69]) -AC_INIT([spot], [2.10.6], [spot@lrde.epita.fr]) +AC_INIT([spot], [2.10.6.dev], [spot@lrde.epita.fr]) AC_CONFIG_AUX_DIR([tools]) AC_CONFIG_MACRO_DIR([m4]) AM_INIT_AUTOMAKE([1.11 gnu tar-ustar color-tests parallel-tests]) From f784e405489edbeb024b34ff0a7730e161c17f3d Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Thu, 19 May 2022 16:38:02 +0200 Subject: [PATCH 080/337] =?UTF-8?q?complete:=20do=20not=20force=20B=C3=BCc?= =?UTF-8?q?hi=20on=20universal=20automata?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * spot/twaalgos/complete.hh: Adjust documentation. * spot/twaalgos/complete.cc: If the acceptance condition is a tautology, delay the forcing of Büchi acceptance until we are sure it is needed. * NEWS: Mention the change. --- NEWS | 3 +++ spot/twaalgos/complete.cc | 24 +++++++++++++++++++----- spot/twaalgos/complete.hh | 9 +++++---- 3 files changed, 27 insertions(+), 9 deletions(-) diff --git a/NEWS b/NEWS index 1cbd800e1..23739d738 100644 --- a/NEWS +++ b/NEWS @@ -82,6 +82,9 @@ New in spot 2.10.6.dev (not yet released) averted in the parser by delaying the construction of such n-ary nodes until all children are known. + - complement() used to always turn tautological acceptance conditions + into Büchi. It now only does that if the automaton is modified. + New in spot 2.10.6 (2022-05-18) Bugs fixed: diff --git a/spot/twaalgos/complete.cc b/spot/twaalgos/complete.cc index 20be2ea06..803b3f440 100644 --- a/spot/twaalgos/complete.cc +++ b/spot/twaalgos/complete.cc @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2013-2018 Laboratoire de Recherche et Développement +// Copyright (C) 2013-2018, 2022 Laboratoire de Recherche et Développement // de l'Epita. // // This file is part of Spot, a model checking library. @@ -29,6 +29,8 @@ namespace spot return; unsigned n = aut->num_states(); + bool need_acc_fix = false; + // UM is a pair (bool, mark). If the Boolean is false, the // acceptance is always satisfiable. Otherwise, MARK is an // example of unsatisfiable mark. @@ -36,10 +38,11 @@ namespace spot if (!um.first) { // We cannot safely complete an automaton if its - // acceptance is always satisfiable. - auto acc = aut->set_buchi(); - for (auto& t: aut->edge_vector()) - t.acc = acc; + // acceptance is always satisfiable, so we will + // have to fix the acceptance automaton. However + // postpone that until we are sure that the + // automaton really need to be completed. + need_acc_fix = true; } else { @@ -129,6 +132,8 @@ namespace spot // acceptance sets as the last outgoing edge of the // state. acc = t.acc; + // If a state already has a edge to a sink, remember it + // so we can add the missing conditions to it. if (t.dst == sink) edge_to_sink = aut->edge_number(t); } @@ -136,6 +141,15 @@ namespace spot // edge to some sink state. if (missingcond != bddfalse) { + if (need_acc_fix) + { + auto a = aut->set_buchi(); + for (auto& t: aut->edge_vector()) + t.acc = a; + if (aut->num_edges()) + acc = a; + need_acc_fix = false; + } // If we haven't found any sink, simply add one. if (sink == -1U) { diff --git a/spot/twaalgos/complete.hh b/spot/twaalgos/complete.hh index 87703dcc2..3525904be 100644 --- a/spot/twaalgos/complete.hh +++ b/spot/twaalgos/complete.hh @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2013, 2014, 2015, 2017 Laboratoire de Recherche et +// Copyright (C) 2013-2015, 2017, 2022 Laboratoire de Recherche et // Développement de l'Epita. // // This file is part of Spot, a model checking library. @@ -25,12 +25,13 @@ namespace spot { /// \brief Complete a twa_graph in place. /// - /// If the TωA has an acceptance condition that is a tautology, - /// it will be changed into a Büchi automaton. + /// If the TωA is incomplete and has an acceptance condition that is + /// a tautology, it will be changed into a Büchi automaton. SPOT_API void complete_here(twa_graph_ptr aut); /// \brief Clone a twa and complete it. /// - /// If the twa has no acceptance set, one will be added. + /// If the TωA is incomplete and has an acceptance condition that is + /// a tautology, it will be changed into a Büchi automaton. SPOT_API twa_graph_ptr complete(const const_twa_ptr& aut); } From b11208440b872c1a23621d5ccb1c03179b002d8c Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Fri, 20 May 2022 16:51:16 +0200 Subject: [PATCH 081/337] zlktree: use a cache in the construction of zielonka_tree This largely speeds up the computation for conditions like "Rabin n" sharing a lot of subtrees. Also implement options to stop the construction if the shape is wrong. * spot/twaalgos/zlktree.cc, spot/twaalgos/zlktree.hh: Implement the cache and the options. * tests/python/zlktree.ipynb, tests/python/zlktree.py: New tests. --- NEWS | 5 + spot/twaalgos/zlktree.cc | 74 +++++- spot/twaalgos/zlktree.hh | 67 ++++- tests/python/zlktree.ipynb | 513 +++++++++++++++++++++++++++---------- tests/python/zlktree.py | 9 + 5 files changed, 517 insertions(+), 151 deletions(-) diff --git a/NEWS b/NEWS index 23739d738..32a1b0f19 100644 --- a/NEWS +++ b/NEWS @@ -85,6 +85,11 @@ New in spot 2.10.6.dev (not yet released) - complement() used to always turn tautological acceptance conditions into Büchi. It now only does that if the automaton is modified. + - The zielonka_tree construction was optimized using the same + memoization trick that is used in ACD. Additionally it can now be + run with additional option to abort when the tree as an unwanted + shape, or to turn the tree into a DAG. + New in spot 2.10.6 (2022-05-18) Bugs fixed: diff --git a/spot/twaalgos/zlktree.cc b/spot/twaalgos/zlktree.cc index 2f87e6352..f31c46896 100644 --- a/spot/twaalgos/zlktree.cc +++ b/spot/twaalgos/zlktree.cc @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2021 Laboratoire de Recherche et Developpement de +// Copyright (C) 2021, 2022 Laboratoire de Recherche et Developpement de // l'Epita (LRDE). // // This file is part of Spot, a model checking library. @@ -109,7 +109,8 @@ namespace spot } } - zielonka_tree::zielonka_tree(const acc_cond& cond) + zielonka_tree::zielonka_tree(const acc_cond& cond, + zielonka_tree_options opt) { const acc_cond::acc_code& code = cond.get_acceptance(); auto all = cond.all_sets(); @@ -120,11 +121,47 @@ namespace spot nodes_[0].colors = all; nodes_[0].level = 0; + robin_hood::unordered_node_map nmap; + std::vector models; // This loop is a BFS over the increasing set of nodes. for (unsigned node = 0; node < nodes_.size(); ++node) { acc_cond::mark_t colors = nodes_[node].colors; + unsigned nextlvl = nodes_[node].level + 1; + + // Have we already seen this combination of colors previously? + // If yes, simply copy the children. + if (auto p = nmap.emplace(colors, node); !p.second) + { + unsigned fc = nodes_[p.first->second].first_child; + if (!fc) // this is a leaf + { + ++num_branches_; + continue; + } + if (!!(opt & zielonka_tree_options::MERGE_SUBTREES)) + { + nodes_[node].first_child = fc; + continue; + } + unsigned child = fc; + unsigned first = nodes_.size(); + nodes_[node].first_child = first; + do + { + auto& c = nodes_[child]; + child = c.next_sibling; + nodes_.push_back({node, static_cast(nodes_.size() + 1), + 0, nextlvl, c.colors}); + } + while (child != fc); + nodes_.back().next_sibling = first; + // We do not have to test the shape since this is the second time + // we see these colors; + continue; + } + bool is_accepting = code.accepting(colors); if (node == 0) is_even_ = is_accepting; @@ -145,15 +182,32 @@ namespace spot nodes_.reserve(first + num_children); for (auto& m: models) nodes_.push_back({node, static_cast(nodes_.size() + 1), - 0, nodes_[node].level + 1, m.model}); + 0, nextlvl, m.model}); nodes_.back().next_sibling = first; if (num_children > 1) { + bool abort = false; if (is_accepting) - has_rabin_shape_ = false; + { + has_rabin_shape_ = false; + if (!!(opt & zielonka_tree_options::ABORT_WRONG_SHAPE) + && !!(opt & zielonka_tree_options::CHECK_RABIN)) + abort = true; + } else - has_streett_shape_ = false; + { + has_streett_shape_ = false; + if (!!(opt & zielonka_tree_options::ABORT_WRONG_SHAPE) + && !!(opt & zielonka_tree_options::CHECK_STREETT)) + abort = true; + } + if (abort) + { + nodes_.clear(); + num_branches_ = 0; + return; + } } } @@ -523,14 +577,18 @@ namespace spot do { auto& c = nodes_[child]; + // We have to read anything we need from C + // before emplace_back, which may reallocate. + acc_cond::mark_t colors = c.colors; + unsigned minstate = c.minstate; + child = c.next_sibling; nodes_.emplace_back(c.edges, c.states); auto& n = nodes_.back(); n.parent = node; n.level = lvl + 1; n.scc = ref.scc; - n.colors = c.colors; - n.minstate = c.minstate; - child = c.next_sibling; + n.colors = colors; + n.minstate = minstate; } while (child != fc); chain_children(node, before, nodes_.size()); diff --git a/spot/twaalgos/zlktree.hh b/spot/twaalgos/zlktree.hh index 675224682..b8e47bc2a 100644 --- a/spot/twaalgos/zlktree.hh +++ b/spot/twaalgos/zlktree.hh @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2021 Laboratoire de Recherche et Developpement de +// Copyright (C) 2021, 2022 Laboratoire de Recherche et Developpement de // l'Epita (LRDE). // // This file is part of Spot, a model checking library. @@ -28,6 +28,68 @@ namespace spot { + /// \ingroup twa_acc_transform + /// \brief Options to alter the behavior of acd + enum class zielonka_tree_options + { + /// Build the ZlkTree, without checking its shape. + NONE = 0, + /// Check if the ZlkTree has Rabin shape. + /// This actually has no effect unless ABORT_WRONG_SHAPE is set, + /// because zielonka_tree always check the shape. + CHECK_RABIN = 1, + /// Check if the ZlkTree has Streett shape. + /// This actually has no effect unless ABORT_WRONG_SHAPE is set, + /// because zielonka_tree always check the shape. + CHECK_STREETT = 2, + /// Check if the ZlkTree has Parity shape + /// This actually has no effect unless ABORT_WRONG_SHAPE is set, + /// because zielonka_tree always check the shape. + CHECK_PARITY = CHECK_RABIN | CHECK_STREETT, + /// Abort the construction of the ZlkTree if it does not have the + /// shape that is tested. When that happens, num_branches() is set + /// to 0. + ABORT_WRONG_SHAPE = 4, + /// Fuse identical substree. This cannot be used with + /// zielonka_tree_transform(). However it saves memory if the + /// only use of the zielonka_tree to check the shape. + MERGE_SUBTREES = 8, + }; + +#ifndef SWIG + inline + bool operator!(zielonka_tree_options me) + { + return me == zielonka_tree_options::NONE; + } + + inline + zielonka_tree_options operator&(zielonka_tree_options left, + zielonka_tree_options right) + { + typedef std::underlying_type_t ut; + return static_cast(static_cast(left) + & static_cast(right)); + } + + inline + zielonka_tree_options operator|(zielonka_tree_options left, + zielonka_tree_options right) + { + typedef std::underlying_type_t ut; + return static_cast(static_cast(left) + | static_cast(right)); + } + + inline + zielonka_tree_options operator-(zielonka_tree_options left, + zielonka_tree_options right) + { + typedef std::underlying_type_t ut; + return static_cast(static_cast(left) + & ~static_cast(right)); + } +#endif /// \ingroup twa_acc_transform /// \brief Zielonka Tree implementation /// @@ -41,7 +103,8 @@ namespace spot { public: /// \brief Build a Zielonka tree from the acceptance condition. - zielonka_tree(const acc_cond& cond); + zielonka_tree(const acc_cond& cond, + zielonka_tree_options opt = zielonka_tree_options::NONE); /// \brief The number of branches in the Zielonka tree. /// diff --git a/tests/python/zlktree.ipynb b/tests/python/zlktree.ipynb index d46e2ce2c..ae44ad37d 100644 --- a/tests/python/zlktree.ipynb +++ b/tests/python/zlktree.ipynb @@ -216,7 +216,7 @@ "\n" ], "text/plain": [ - " >" + " >" ] }, "execution_count": 2, @@ -640,7 +640,7 @@ "\n" ], "text/plain": [ - " *' at 0x7f0c49328600> >" + " *' at 0x7f14701b7510> >" ] }, "execution_count": 10, @@ -1063,7 +1063,7 @@ "\n" ], "text/plain": [ - " *' at 0x7f0c493284b0> >" + " *' at 0x7f1470220960> >" ] }, "execution_count": 11, @@ -1256,7 +1256,7 @@ "\n" ], "text/plain": [ - " *' at 0x7f0c49328f30> >" + " *' at 0x7f14701b75d0> >" ] }, "execution_count": 13, @@ -1701,7 +1701,7 @@ "\n" ], "text/plain": [ - " *' at 0x7f0c49330420> >" + " *' at 0x7f1470142240> >" ] }, "execution_count": 14, @@ -2096,7 +2096,7 @@ "\n" ], "text/plain": [ - " >" + " >" ] }, "metadata": {}, @@ -2427,7 +2427,7 @@ "\n" ], "text/plain": [ - " >" + " >" ] }, "metadata": {}, @@ -2513,7 +2513,7 @@ "\n" ], "text/plain": [ - " >" + " >" ] }, "metadata": {}, @@ -2624,7 +2624,7 @@ "\n" ], "text/plain": [ - " >" + " >" ] }, "metadata": {}, @@ -2662,7 +2662,7 @@ "\n" ], "text/plain": [ - " >" + " >" ] }, "metadata": {}, @@ -2700,7 +2700,7 @@ "\n" ], "text/plain": [ - " >" + " >" ] }, "metadata": {}, @@ -2717,6 +2717,237 @@ " display(tcond)" ] }, + { + "cell_type": "markdown", + "id": "77db26c3", + "metadata": {}, + "source": [ + "## `zielonka_tree_options`\n", + "\n", + "The `zielonka_tree` class accepts a few options that can alter its behaviour.\n", + "\n", + "Options `CHECK_RABIN`, `CHECK_STREETT`, `CHECK_PARITY` can be combined with\n", + "`ABORT_WRONG_SHAPE` to abort the construction as soon as it is detected that the Zielonka tree has the wrong shape. When this happens, the number of branchs of the tree is set to 0.\n", + "\n", + "For instance we can check that the original acceptance condition does not behaves like a Parity condition." + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "4fa47daf", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "(4, (Fin(0) & Inf(1) & (Inf(2) | Fin(3))) | ((Inf(0) | Fin(1)) & Fin(2) & Inf(3)))\n", + "0\n" + ] + } + ], + "source": [ + "print(c)\n", + "z = spot.zielonka_tree(c, spot.zielonka_tree_options_ABORT_WRONG_SHAPE \n", + " | spot.zielonka_tree_options_CHECK_PARITY)\n", + "print(z.num_branches())" + ] + }, + { + "cell_type": "markdown", + "id": "4786f64c", + "metadata": {}, + "source": [ + "Option `MERGE_SUBTREE` will fuse identical nodes, turning the tree into a DAG. (Actually, because this tree is stored as a left-child right-sibling tree, only the children of identical nodes are merged.):" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "bc826090", + "metadata": {}, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "zielonka_tree\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "{0,1,2,3}\n", + "\n", + "\n", + "\n", + "1\n", + "\n", + "{1,2,3}\n", + "\n", + "\n", + "\n", + "0->1\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "2\n", + "\n", + "{0,1,3}\n", + "\n", + "\n", + "\n", + "0->2\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "3\n", + "\n", + "{2,3}\n", + "\n", + "\n", + "\n", + "1->3\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "4\n", + "\n", + "{1,3}\n", + "\n", + "\n", + "\n", + "1->4\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "5\n", + "\n", + "{1,3}\n", + "\n", + "\n", + "\n", + "2->5\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "6\n", + "\n", + "{0,1}\n", + "\n", + "\n", + "\n", + "2->6\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "7\n", + "\n", + "{3}\n", + "<7>\n", + "\n", + "\n", + "\n", + "3->7\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "8\n", + "\n", + "{1}\n", + "<8>\n", + "\n", + "\n", + "\n", + "4->8\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "9\n", + "\n", + "{3}\n", + "<9>\n", + "\n", + "\n", + "\n", + "4->9\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "5->8\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "5->9\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "10\n", + "\n", + "{1}\n", + "<10>\n", + "\n", + "\n", + "\n", + "6->10\n", + "\n", + "\n", + "\n", + "\n", + "\n" + ], + "text/plain": [ + " >" + ] + }, + "execution_count": 18, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "spot.zielonka_tree(c, spot.zielonka_tree_options_MERGE_SUBTREES)" + ] + }, + { + "cell_type": "markdown", + "id": "9d7688b3", + "metadata": {}, + "source": [ + "Such a DAG cannot be used by `zielonka_tree_transform()`, but it saves memory if we are only checking the shape of the tree/DAG." + ] + }, { "cell_type": "markdown", "id": "75838579", @@ -2731,7 +2962,7 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": 19, "id": "ea3488b1", "metadata": {}, "outputs": [], @@ -2763,7 +2994,7 @@ }, { "cell_type": "code", - "execution_count": 18, + "execution_count": 20, "id": "fad721c0", "metadata": {}, "outputs": [ @@ -3862,10 +4093,10 @@ "};$(\"#acdaut0 #E9\").addClass(\"acdN0\");$(\"#acdaut0 #E10\").addClass(\"acdN0\");$(\"#acdaut0 #E11\").addClass(\"acdN0\");$(\"#acdaut0 #E12\").addClass(\"acdN0\");$(\"#acdaut0 #E13\").addClass(\"acdN0\");$(\"#acdaut0 #E14\").addClass(\"acdN0\");$(\"#acdaut0 #E15\").addClass(\"acdN0\");$(\"#acdaut0 #E16\").addClass(\"acdN0\");$(\"#acdaut0 #E21\").addClass(\"acdN0\");$(\"#acdaut0 #E22\").addClass(\"acdN0\");$(\"#acdaut0 #E23\").addClass(\"acdN0\");$(\"#acdaut0 #E24\").addClass(\"acdN0\");$(\"#acdaut0 #E25\").addClass(\"acdN0\");$(\"#acdaut0 #E26\").addClass(\"acdN0\");$(\"#acdaut0 #E27\").addClass(\"acdN0\");$(\"#acdaut0 #E28\").addClass(\"acdN0\");$(\"#acdaut0 #E33\").addClass(\"acdN0\");$(\"#acdaut0 #E34\").addClass(\"acdN0\");$(\"#acdaut0 #E35\").addClass(\"acdN0\");$(\"#acdaut0 #E36\").addClass(\"acdN0\");$(\"#acdaut0 #E31\").addClass(\"acdN1\");$(\"#acdaut0 #E32\").addClass(\"acdN1\");$(\"#acdaut0 #E39\").addClass(\"acdN1\");$(\"#acdaut0 #E40\").addClass(\"acdN1\");$(\"#acdaut0 #E5\").addClass(\"acdN2\");$(\"#acdaut0 #E7\").addClass(\"acdN2\");$(\"#acdaut0 #E17\").addClass(\"acdN2\");$(\"#acdaut0 #E1\").addClass(\"acdN3\");$(\"#acdaut0 #E10\").addClass(\"acdN4\");$(\"#acdaut0 #E12\").addClass(\"acdN4\");$(\"#acdaut0 #E13\").addClass(\"acdN4\");$(\"#acdaut0 #E15\").addClass(\"acdN4\");$(\"#acdaut0 #E21\").addClass(\"acdN4\");$(\"#acdaut0 #E22\").addClass(\"acdN4\");$(\"#acdaut0 #E23\").addClass(\"acdN5\");$(\"#acdaut0 #E24\").addClass(\"acdN5\");$(\"#acdaut0 #E34\").addClass(\"acdN5\");$(\"#acdaut0 #E36\").addClass(\"acdN5\");$(\"#acdaut0 #E14\").addClass(\"acdN6\");$(\"#acdaut0 #E15\").addClass(\"acdN6\");$(\"#acdaut0 #E22\").addClass(\"acdN6\");$(\"#acdaut0 #E23\").addClass(\"acdN6\");$(\"#acdaut0 #E14\").addClass(\"acdN7\");$(\"#acdaut0 #E16\").addClass(\"acdN7\");$(\"#acdaut0 #E26\").addClass(\"acdN7\");$(\"#acdaut0 #E9\").addClass(\"acdN8\");$(\"#acdaut0 #E40\").addClass(\"acdN9\");$(\"#acdaut0 #E5\").addClass(\"acdN10\");$(\"#acdaut0 #E23\").addClass(\"acdN11\");$(\"#acdaut0 #E14\").addClass(\"acdN12\");$(\"#acdaut0 #E23\").addClass(\"acdN13\");$(\"#acdaut0 #E14\").addClass(\"acdN14\");$(\"#acdaut0 #E1\").click(function(){acd0_edge(1);});$(\"#acdaut0 #E2\").click(function(){acd0_edge(2);});$(\"#acdaut0 #E3\").click(function(){acd0_edge(3);});$(\"#acdaut0 #E4\").click(function(){acd0_edge(4);});$(\"#acdaut0 #E5\").click(function(){acd0_edge(5);});$(\"#acdaut0 #E6\").click(function(){acd0_edge(6);});$(\"#acdaut0 #E7\").click(function(){acd0_edge(7);});$(\"#acdaut0 #E8\").click(function(){acd0_edge(8);});$(\"#acdaut0 #E9\").click(function(){acd0_edge(9);});$(\"#acdaut0 #E10\").click(function(){acd0_edge(10);});$(\"#acdaut0 #E11\").click(function(){acd0_edge(11);});$(\"#acdaut0 #E12\").click(function(){acd0_edge(12);});$(\"#acdaut0 #E13\").click(function(){acd0_edge(13);});$(\"#acdaut0 #E14\").click(function(){acd0_edge(14);});$(\"#acdaut0 #E15\").click(function(){acd0_edge(15);});$(\"#acdaut0 #E16\").click(function(){acd0_edge(16);});$(\"#acdaut0 #E17\").click(function(){acd0_edge(17);});$(\"#acdaut0 #E18\").click(function(){acd0_edge(18);});$(\"#acdaut0 #E19\").click(function(){acd0_edge(19);});$(\"#acdaut0 #E20\").click(function(){acd0_edge(20);});$(\"#acdaut0 #E21\").click(function(){acd0_edge(21);});$(\"#acdaut0 #E22\").click(function(){acd0_edge(22);});$(\"#acdaut0 #E23\").click(function(){acd0_edge(23);});$(\"#acdaut0 #E24\").click(function(){acd0_edge(24);});$(\"#acdaut0 #E25\").click(function(){acd0_edge(25);});$(\"#acdaut0 #E26\").click(function(){acd0_edge(26);});$(\"#acdaut0 #E27\").click(function(){acd0_edge(27);});$(\"#acdaut0 #E28\").click(function(){acd0_edge(28);});$(\"#acdaut0 #E29\").click(function(){acd0_edge(29);});$(\"#acdaut0 #E30\").click(function(){acd0_edge(30);});$(\"#acdaut0 #E31\").click(function(){acd0_edge(31);});$(\"#acdaut0 #E32\").click(function(){acd0_edge(32);});$(\"#acdaut0 #E33\").click(function(){acd0_edge(33);});$(\"#acdaut0 #E34\").click(function(){acd0_edge(34);});$(\"#acdaut0 #E35\").click(function(){acd0_edge(35);});$(\"#acdaut0 #E36\").click(function(){acd0_edge(36);});$(\"#acdaut0 #E37\").click(function(){acd0_edge(37);});$(\"#acdaut0 #E38\").click(function(){acd0_edge(38);});$(\"#acdaut0 #E39\").click(function(){acd0_edge(39);});$(\"#acdaut0 #E40\").click(function(){acd0_edge(40);});$(\"#acdaut0 #S0\").click(function(){acd0_state(0);});$(\"#acdaut0 #S1\").click(function(){acd0_state(1);});$(\"#acdaut0 #S2\").click(function(){acd0_state(2);});$(\"#acdaut0 #S3\").click(function(){acd0_state(3);});$(\"#acdaut0 #S4\").click(function(){acd0_state(4);});$(\"#acdaut0 #S5\").click(function(){acd0_state(5);});$(\"#acdaut0 #S6\").click(function(){acd0_state(6);});$(\"#acdaut0 #S7\").click(function(){acd0_state(7);});$(\"#acdaut0 #S8\").click(function(){acd0_state(8);});$(\"#acdaut0 #S9\").click(function(){acd0_state(9);});$(\"#acd0 #N0\").click(function(){acd0_node(0, 0);});$(\"#acd0 #N1\").click(function(){acd0_node(1, 1);});$(\"#acd0 #N2\").click(function(){acd0_node(2, 1);});$(\"#acd0 #N3\").click(function(){acd0_node(3, 1);});$(\"#acd0 #N4\").click(function(){acd0_node(4, 1);});$(\"#acd0 #N5\").click(function(){acd0_node(5, 1);});$(\"#acd0 #N6\").click(function(){acd0_node(6, 1);});$(\"#acd0 #N7\").click(function(){acd0_node(7, 1);});$(\"#acd0 #N8\").click(function(){acd0_node(8, 1);});$(\"#acd0 #N9\").click(function(){acd0_node(9, 0);});$(\"#acd0 #N10\").click(function(){acd0_node(10, 0);});$(\"#acd0 #N11\").click(function(){acd0_node(11, 0);});$(\"#acd0 #N12\").click(function(){acd0_node(12, 0);});$(\"#acd0 #N13\").click(function(){acd0_node(13, 0);});$(\"#acd0 #N14\").click(function(){acd0_node(14, 0);});" ], "text/plain": [ - " >" + " >" ] }, - "execution_count": 18, + "execution_count": 20, "metadata": {}, "output_type": "execute_result" } @@ -3886,7 +4117,7 @@ }, { "cell_type": "code", - "execution_count": 19, + "execution_count": 21, "id": "859a993a", "metadata": {}, "outputs": [ @@ -3896,7 +4127,7 @@ "False" ] }, - "execution_count": 19, + "execution_count": 21, "metadata": {}, "output_type": "execute_result" } @@ -3932,7 +4163,7 @@ }, { "cell_type": "code", - "execution_count": 20, + "execution_count": 22, "id": "a8bd0844", "metadata": {}, "outputs": [ @@ -3942,7 +4173,7 @@ "(4, 1)" ] }, - "execution_count": 20, + "execution_count": 22, "metadata": {}, "output_type": "execute_result" } @@ -3953,7 +4184,7 @@ }, { "cell_type": "code", - "execution_count": 21, + "execution_count": 23, "id": "93116a05", "metadata": {}, "outputs": [ @@ -3963,7 +4194,7 @@ "(4, 1)" ] }, - "execution_count": 21, + "execution_count": 23, "metadata": {}, "output_type": "execute_result" } @@ -3984,7 +4215,7 @@ }, { "cell_type": "code", - "execution_count": 22, + "execution_count": 24, "id": "23940b6a", "metadata": {}, "outputs": [ @@ -3994,7 +4225,7 @@ "(12, 0)" ] }, - "execution_count": 22, + "execution_count": 24, "metadata": {}, "output_type": "execute_result" } @@ -4005,7 +4236,7 @@ }, { "cell_type": "code", - "execution_count": 23, + "execution_count": 25, "id": "de7cbd02", "metadata": {}, "outputs": [ @@ -4015,7 +4246,7 @@ "(8, 0)" ] }, - "execution_count": 23, + "execution_count": 25, "metadata": {}, "output_type": "execute_result" } @@ -4026,7 +4257,7 @@ }, { "cell_type": "code", - "execution_count": 24, + "execution_count": 26, "id": "8b0305d4", "metadata": {}, "outputs": [ @@ -4036,7 +4267,7 @@ "(4, 0)" ] }, - "execution_count": 24, + "execution_count": 26, "metadata": {}, "output_type": "execute_result" } @@ -4047,7 +4278,7 @@ }, { "cell_type": "code", - "execution_count": 25, + "execution_count": 27, "id": "4f0a10f5", "metadata": {}, "outputs": [ @@ -4057,7 +4288,7 @@ "(4, 1)" ] }, - "execution_count": 25, + "execution_count": 27, "metadata": {}, "output_type": "execute_result" } @@ -4094,7 +4325,7 @@ }, { "cell_type": "code", - "execution_count": 26, + "execution_count": 28, "id": "2bd04c1f", "metadata": {}, "outputs": [ @@ -4104,7 +4335,7 @@ "4" ] }, - "execution_count": 26, + "execution_count": 28, "metadata": {}, "output_type": "execute_result" } @@ -4131,7 +4362,7 @@ }, { "cell_type": "code", - "execution_count": 27, + "execution_count": 29, "id": "e28035e8", "metadata": {}, "outputs": [ @@ -4737,10 +4968,10 @@ "\n" ], "text/plain": [ - " *' at 0x7f0c49328840> >" + " *' at 0x7f14701670f0> >" ] }, - "execution_count": 27, + "execution_count": 29, "metadata": {}, "output_type": "execute_result" } @@ -4761,7 +4992,7 @@ }, { "cell_type": "code", - "execution_count": 28, + "execution_count": 30, "id": "numerical-education", "metadata": {}, "outputs": [ @@ -4771,7 +5002,7 @@ "True" ] }, - "execution_count": 28, + "execution_count": 30, "metadata": {}, "output_type": "execute_result" } @@ -4790,7 +5021,7 @@ }, { "cell_type": "code", - "execution_count": 29, + "execution_count": 31, "id": "3e239a0c", "metadata": {}, "outputs": [ @@ -5376,10 +5607,10 @@ "\n" ], "text/plain": [ - " *' at 0x7f0c49328e70> >" + " *' at 0x7f1470167210> >" ] }, - "execution_count": 29, + "execution_count": 31, "metadata": {}, "output_type": "execute_result" } @@ -5401,7 +5632,7 @@ }, { "cell_type": "code", - "execution_count": 30, + "execution_count": 32, "id": "4f62e612", "metadata": {}, "outputs": [ @@ -5411,7 +5642,7 @@ "15" ] }, - "execution_count": 30, + "execution_count": 32, "metadata": {}, "output_type": "execute_result" } @@ -5422,7 +5653,7 @@ }, { "cell_type": "code", - "execution_count": 31, + "execution_count": 33, "id": "20f2a45c", "metadata": {}, "outputs": [ @@ -5432,7 +5663,7 @@ "27" ] }, - "execution_count": 31, + "execution_count": 33, "metadata": {}, "output_type": "execute_result" } @@ -5461,7 +5692,7 @@ }, { "cell_type": "code", - "execution_count": 32, + "execution_count": 34, "id": "7727735d", "metadata": {}, "outputs": [], @@ -5471,7 +5702,7 @@ }, { "cell_type": "code", - "execution_count": 33, + "execution_count": 35, "id": "2d0bbc0b", "metadata": {}, "outputs": [ @@ -5505,7 +5736,7 @@ }, { "cell_type": "code", - "execution_count": 34, + "execution_count": 36, "id": "78643aae", "metadata": {}, "outputs": [ @@ -5523,7 +5754,7 @@ }, { "cell_type": "code", - "execution_count": 35, + "execution_count": 37, "id": "13a7796b", "metadata": {}, "outputs": [], @@ -5533,7 +5764,7 @@ }, { "cell_type": "code", - "execution_count": 36, + "execution_count": 38, "id": "3ee900b7", "metadata": {}, "outputs": [ @@ -5564,7 +5795,7 @@ }, { "cell_type": "code", - "execution_count": 37, + "execution_count": 39, "id": "e12bb020", "metadata": {}, "outputs": [], @@ -5574,7 +5805,7 @@ }, { "cell_type": "code", - "execution_count": 38, + "execution_count": 40, "id": "813d15ed", "metadata": {}, "outputs": [ @@ -6673,10 +6904,10 @@ "};$(\"#acdaut1 #E9\").addClass(\"acdN0\");$(\"#acdaut1 #E10\").addClass(\"acdN0\");$(\"#acdaut1 #E11\").addClass(\"acdN0\");$(\"#acdaut1 #E12\").addClass(\"acdN0\");$(\"#acdaut1 #E13\").addClass(\"acdN0\");$(\"#acdaut1 #E14\").addClass(\"acdN0\");$(\"#acdaut1 #E15\").addClass(\"acdN0\");$(\"#acdaut1 #E16\").addClass(\"acdN0\");$(\"#acdaut1 #E21\").addClass(\"acdN0\");$(\"#acdaut1 #E22\").addClass(\"acdN0\");$(\"#acdaut1 #E23\").addClass(\"acdN0\");$(\"#acdaut1 #E24\").addClass(\"acdN0\");$(\"#acdaut1 #E25\").addClass(\"acdN0\");$(\"#acdaut1 #E26\").addClass(\"acdN0\");$(\"#acdaut1 #E27\").addClass(\"acdN0\");$(\"#acdaut1 #E28\").addClass(\"acdN0\");$(\"#acdaut1 #E33\").addClass(\"acdN0\");$(\"#acdaut1 #E34\").addClass(\"acdN0\");$(\"#acdaut1 #E35\").addClass(\"acdN0\");$(\"#acdaut1 #E36\").addClass(\"acdN0\");$(\"#acdaut1 #E31\").addClass(\"acdN1\");$(\"#acdaut1 #E32\").addClass(\"acdN1\");$(\"#acdaut1 #E39\").addClass(\"acdN1\");$(\"#acdaut1 #E40\").addClass(\"acdN1\");$(\"#acdaut1 #E5\").addClass(\"acdN2\");$(\"#acdaut1 #E7\").addClass(\"acdN2\");$(\"#acdaut1 #E17\").addClass(\"acdN2\");$(\"#acdaut1 #E1\").addClass(\"acdN3\");$(\"#acdaut1 #E10\").addClass(\"acdN4\");$(\"#acdaut1 #E12\").addClass(\"acdN4\");$(\"#acdaut1 #E13\").addClass(\"acdN4\");$(\"#acdaut1 #E15\").addClass(\"acdN4\");$(\"#acdaut1 #E21\").addClass(\"acdN4\");$(\"#acdaut1 #E22\").addClass(\"acdN4\");$(\"#acdaut1 #E14\").addClass(\"acdN5\");$(\"#acdaut1 #E15\").addClass(\"acdN5\");$(\"#acdaut1 #E22\").addClass(\"acdN5\");$(\"#acdaut1 #E23\").addClass(\"acdN5\");$(\"#acdaut1 #E23\").addClass(\"acdN6\");$(\"#acdaut1 #E24\").addClass(\"acdN6\");$(\"#acdaut1 #E34\").addClass(\"acdN6\");$(\"#acdaut1 #E36\").addClass(\"acdN6\");$(\"#acdaut1 #E14\").addClass(\"acdN7\");$(\"#acdaut1 #E16\").addClass(\"acdN7\");$(\"#acdaut1 #E26\").addClass(\"acdN7\");$(\"#acdaut1 #E9\").addClass(\"acdN8\");$(\"#acdaut1 #E40\").addClass(\"acdN9\");$(\"#acdaut1 #E5\").addClass(\"acdN10\");$(\"#acdaut1 #E14\").addClass(\"acdN11\");$(\"#acdaut1 #E23\").addClass(\"acdN12\");$(\"#acdaut1 #E23\").addClass(\"acdN13\");$(\"#acdaut1 #E14\").addClass(\"acdN14\");$(\"#acdaut1 #E1\").click(function(){acd1_edge(1);});$(\"#acdaut1 #E2\").click(function(){acd1_edge(2);});$(\"#acdaut1 #E3\").click(function(){acd1_edge(3);});$(\"#acdaut1 #E4\").click(function(){acd1_edge(4);});$(\"#acdaut1 #E5\").click(function(){acd1_edge(5);});$(\"#acdaut1 #E6\").click(function(){acd1_edge(6);});$(\"#acdaut1 #E7\").click(function(){acd1_edge(7);});$(\"#acdaut1 #E8\").click(function(){acd1_edge(8);});$(\"#acdaut1 #E9\").click(function(){acd1_edge(9);});$(\"#acdaut1 #E10\").click(function(){acd1_edge(10);});$(\"#acdaut1 #E11\").click(function(){acd1_edge(11);});$(\"#acdaut1 #E12\").click(function(){acd1_edge(12);});$(\"#acdaut1 #E13\").click(function(){acd1_edge(13);});$(\"#acdaut1 #E14\").click(function(){acd1_edge(14);});$(\"#acdaut1 #E15\").click(function(){acd1_edge(15);});$(\"#acdaut1 #E16\").click(function(){acd1_edge(16);});$(\"#acdaut1 #E17\").click(function(){acd1_edge(17);});$(\"#acdaut1 #E18\").click(function(){acd1_edge(18);});$(\"#acdaut1 #E19\").click(function(){acd1_edge(19);});$(\"#acdaut1 #E20\").click(function(){acd1_edge(20);});$(\"#acdaut1 #E21\").click(function(){acd1_edge(21);});$(\"#acdaut1 #E22\").click(function(){acd1_edge(22);});$(\"#acdaut1 #E23\").click(function(){acd1_edge(23);});$(\"#acdaut1 #E24\").click(function(){acd1_edge(24);});$(\"#acdaut1 #E25\").click(function(){acd1_edge(25);});$(\"#acdaut1 #E26\").click(function(){acd1_edge(26);});$(\"#acdaut1 #E27\").click(function(){acd1_edge(27);});$(\"#acdaut1 #E28\").click(function(){acd1_edge(28);});$(\"#acdaut1 #E29\").click(function(){acd1_edge(29);});$(\"#acdaut1 #E30\").click(function(){acd1_edge(30);});$(\"#acdaut1 #E31\").click(function(){acd1_edge(31);});$(\"#acdaut1 #E32\").click(function(){acd1_edge(32);});$(\"#acdaut1 #E33\").click(function(){acd1_edge(33);});$(\"#acdaut1 #E34\").click(function(){acd1_edge(34);});$(\"#acdaut1 #E35\").click(function(){acd1_edge(35);});$(\"#acdaut1 #E36\").click(function(){acd1_edge(36);});$(\"#acdaut1 #E37\").click(function(){acd1_edge(37);});$(\"#acdaut1 #E38\").click(function(){acd1_edge(38);});$(\"#acdaut1 #E39\").click(function(){acd1_edge(39);});$(\"#acdaut1 #E40\").click(function(){acd1_edge(40);});$(\"#acdaut1 #S0\").click(function(){acd1_state(0);});$(\"#acdaut1 #S1\").click(function(){acd1_state(1);});$(\"#acdaut1 #S2\").click(function(){acd1_state(2);});$(\"#acdaut1 #S3\").click(function(){acd1_state(3);});$(\"#acdaut1 #S4\").click(function(){acd1_state(4);});$(\"#acdaut1 #S5\").click(function(){acd1_state(5);});$(\"#acdaut1 #S6\").click(function(){acd1_state(6);});$(\"#acdaut1 #S7\").click(function(){acd1_state(7);});$(\"#acdaut1 #S8\").click(function(){acd1_state(8);});$(\"#acdaut1 #S9\").click(function(){acd1_state(9);});$(\"#acd1 #N0\").click(function(){acd1_node(0, 0);});$(\"#acd1 #N1\").click(function(){acd1_node(1, 1);});$(\"#acd1 #N2\").click(function(){acd1_node(2, 1);});$(\"#acd1 #N3\").click(function(){acd1_node(3, 1);});$(\"#acd1 #N4\").click(function(){acd1_node(4, 1);});$(\"#acd1 #N5\").click(function(){acd1_node(5, 1);});$(\"#acd1 #N6\").click(function(){acd1_node(6, 1);});$(\"#acd1 #N7\").click(function(){acd1_node(7, 1);});$(\"#acd1 #N8\").click(function(){acd1_node(8, 1);});$(\"#acd1 #N9\").click(function(){acd1_node(9, 0);});$(\"#acd1 #N10\").click(function(){acd1_node(10, 0);});$(\"#acd1 #N11\").click(function(){acd1_node(11, 0);});$(\"#acd1 #N12\").click(function(){acd1_node(12, 0);});$(\"#acd1 #N13\").click(function(){acd1_node(13, 0);});$(\"#acd1 #N14\").click(function(){acd1_node(14, 0);});" ], "text/plain": [ - " >" + " >" ] }, - "execution_count": 38, + "execution_count": 40, "metadata": {}, "output_type": "execute_result" } @@ -6695,7 +6926,7 @@ }, { "cell_type": "code", - "execution_count": 39, + "execution_count": 41, "id": "00acde97", "metadata": {}, "outputs": [ @@ -6705,7 +6936,7 @@ "4" ] }, - "execution_count": 39, + "execution_count": 41, "metadata": {}, "output_type": "execute_result" } @@ -6716,7 +6947,7 @@ }, { "cell_type": "code", - "execution_count": 40, + "execution_count": 42, "id": "23c5f4df", "metadata": {}, "outputs": [ @@ -6726,7 +6957,7 @@ "8" ] }, - "execution_count": 40, + "execution_count": 42, "metadata": {}, "output_type": "execute_result" } @@ -6737,7 +6968,7 @@ }, { "cell_type": "code", - "execution_count": 41, + "execution_count": 43, "id": "da0bbcbe", "metadata": {}, "outputs": [ @@ -6747,7 +6978,7 @@ "0" ] }, - "execution_count": 41, + "execution_count": 43, "metadata": {}, "output_type": "execute_result" } @@ -6758,7 +6989,7 @@ }, { "cell_type": "code", - "execution_count": 42, + "execution_count": 44, "id": "da0dc5bc", "metadata": {}, "outputs": [ @@ -6768,7 +6999,7 @@ "8" ] }, - "execution_count": 42, + "execution_count": 44, "metadata": {}, "output_type": "execute_result" } @@ -6788,7 +7019,7 @@ }, { "cell_type": "code", - "execution_count": 43, + "execution_count": 45, "id": "94999c2e", "metadata": {}, "outputs": [ @@ -7586,10 +7817,10 @@ "\n" ], "text/plain": [ - " *' at 0x7f0c49342570> >" + " *' at 0x7f14700fe1e0> >" ] }, - "execution_count": 43, + "execution_count": 45, "metadata": {}, "output_type": "execute_result" } @@ -7611,7 +7842,7 @@ }, { "cell_type": "code", - "execution_count": 44, + "execution_count": 46, "id": "b57476cf", "metadata": {}, "outputs": [ @@ -7640,7 +7871,7 @@ }, { "cell_type": "code", - "execution_count": 45, + "execution_count": 47, "id": "f082b433", "metadata": {}, "outputs": [ @@ -7912,10 +8143,10 @@ "};$(\"#acdaut2 #E1\").addClass(\"acdN0\");$(\"#acdaut2 #E2\").addClass(\"acdN0\");$(\"#acdaut2 #E3\").addClass(\"acdN0\");$(\"#acdaut2 #E4\").addClass(\"acdN0\");$(\"#acdaut2 #E5\").addClass(\"acdN0\");$(\"#acdaut2 #E6\").addClass(\"acdN0\");$(\"#acdaut2 #E2\").addClass(\"acdN1\");$(\"#acdaut2 #E3\").addClass(\"acdN1\");$(\"#acdaut2 #E4\").addClass(\"acdN1\");$(\"#acdaut2 #E5\").addClass(\"acdN1\");$(\"#acdaut2 #E6\").addClass(\"acdN1\");$(\"#acdaut2 #E1\").addClass(\"acdN2\");$(\"#acdaut2 #E2\").addClass(\"acdN2\");$(\"#acdaut2 #E4\").addClass(\"acdN2\");$(\"#acdaut2 #E6\").addClass(\"acdN2\");$(\"#acdaut2 #E1\").click(function(){acd2_edge(1);});$(\"#acdaut2 #E2\").click(function(){acd2_edge(2);});$(\"#acdaut2 #E3\").click(function(){acd2_edge(3);});$(\"#acdaut2 #E4\").click(function(){acd2_edge(4);});$(\"#acdaut2 #E5\").click(function(){acd2_edge(5);});$(\"#acdaut2 #E6\").click(function(){acd2_edge(6);});$(\"#acdaut2 #S0\").click(function(){acd2_state(0);});$(\"#acdaut2 #S1\").click(function(){acd2_state(1);});$(\"#acdaut2 #S2\").click(function(){acd2_state(2);});$(\"#acd2 #N0\").click(function(){acd2_node(0, 1);});$(\"#acd2 #N1\").click(function(){acd2_node(1, 0);});$(\"#acd2 #N2\").click(function(){acd2_node(2, 0);});" ], "text/plain": [ - " >" + " >" ] }, - "execution_count": 45, + "execution_count": 47, "metadata": {}, "output_type": "execute_result" } @@ -7931,7 +8162,7 @@ }, { "cell_type": "code", - "execution_count": 46, + "execution_count": 48, "id": "597185c0", "metadata": {}, "outputs": [ @@ -7944,11 +8175,11 @@ "\n", "\n", - "\n", - "\n", - "\n", - "[Büchi]\n", + "\n", + "\n", + "\n", + "[Büchi]\n", "\n", "\n", "\n", @@ -8122,10 +8353,10 @@ "\n" ], "text/plain": [ - " *' at 0x7f0c480e0690> >" + " *' at 0x7f14700feb40> >" ] }, - "execution_count": 46, + "execution_count": 48, "metadata": {}, "output_type": "execute_result" } @@ -8154,7 +8385,7 @@ }, { "cell_type": "code", - "execution_count": 47, + "execution_count": 49, "id": "a4fd4105", "metadata": {}, "outputs": [ @@ -8426,10 +8657,10 @@ "};$(\"#acdaut3 #E1\").addClass(\"acdN0\");$(\"#acdaut3 #E2\").addClass(\"acdN0\");$(\"#acdaut3 #E3\").addClass(\"acdN0\");$(\"#acdaut3 #E4\").addClass(\"acdN0\");$(\"#acdaut3 #E5\").addClass(\"acdN0\");$(\"#acdaut3 #E6\").addClass(\"acdN0\");$(\"#acdaut3 #E1\").addClass(\"acdN1\");$(\"#acdaut3 #E2\").addClass(\"acdN1\");$(\"#acdaut3 #E4\").addClass(\"acdN1\");$(\"#acdaut3 #E6\").addClass(\"acdN1\");$(\"#acdaut3 #E2\").addClass(\"acdN2\");$(\"#acdaut3 #E3\").addClass(\"acdN2\");$(\"#acdaut3 #E4\").addClass(\"acdN2\");$(\"#acdaut3 #E5\").addClass(\"acdN2\");$(\"#acdaut3 #E6\").addClass(\"acdN2\");$(\"#acdaut3 #E1\").click(function(){acd3_edge(1);});$(\"#acdaut3 #E2\").click(function(){acd3_edge(2);});$(\"#acdaut3 #E3\").click(function(){acd3_edge(3);});$(\"#acdaut3 #E4\").click(function(){acd3_edge(4);});$(\"#acdaut3 #E5\").click(function(){acd3_edge(5);});$(\"#acdaut3 #E6\").click(function(){acd3_edge(6);});$(\"#acdaut3 #S0\").click(function(){acd3_state(0);});$(\"#acdaut3 #S1\").click(function(){acd3_state(1);});$(\"#acdaut3 #S2\").click(function(){acd3_state(2);});$(\"#acd3 #N0\").click(function(){acd3_node(0, 1);});$(\"#acd3 #N1\").click(function(){acd3_node(1, 0);});$(\"#acd3 #N2\").click(function(){acd3_node(2, 0);});" ], "text/plain": [ - " >" + " >" ] }, - "execution_count": 47, + "execution_count": 49, "metadata": {}, "output_type": "execute_result" } @@ -8440,7 +8671,7 @@ }, { "cell_type": "code", - "execution_count": 48, + "execution_count": 50, "id": "1a68f96a", "metadata": {}, "outputs": [ @@ -8453,11 +8684,11 @@ "\n", "\n", - "\n", - "\n", - "\n", - "[Büchi]\n", + "\n", + "\n", + "\n", + "[Büchi]\n", "\n", "\n", "\n", @@ -8610,10 +8841,10 @@ "\n" ], "text/plain": [ - " *' at 0x7f0c480e0de0> >" + " *' at 0x7f14700fea80> >" ] }, - "execution_count": 48, + "execution_count": 50, "metadata": {}, "output_type": "execute_result" } @@ -8636,7 +8867,7 @@ }, { "cell_type": "code", - "execution_count": 49, + "execution_count": 51, "id": "criminal-northwest", "metadata": {}, "outputs": [ @@ -8762,10 +8993,10 @@ "\n" ], "text/plain": [ - " *' at 0x7f0c480e21e0> >" + " *' at 0x7f1470107240> >" ] }, - "execution_count": 49, + "execution_count": 51, "metadata": {}, "output_type": "execute_result" } @@ -8796,7 +9027,7 @@ }, { "cell_type": "code", - "execution_count": 50, + "execution_count": 52, "id": "63c7c062", "metadata": {}, "outputs": [ @@ -8874,10 +9105,10 @@ "\n" ], "text/plain": [ - " >" + " >" ] }, - "execution_count": 50, + "execution_count": 52, "metadata": {}, "output_type": "execute_result" } @@ -8888,7 +9119,7 @@ }, { "cell_type": "code", - "execution_count": 51, + "execution_count": 53, "id": "balanced-investing", "metadata": {}, "outputs": [ @@ -9040,10 +9271,10 @@ "\n" ], "text/plain": [ - " *' at 0x7f0c480e2360> >" + " *' at 0x7f1470107030> >" ] }, - "execution_count": 51, + "execution_count": 53, "metadata": {}, "output_type": "execute_result" } @@ -9054,7 +9285,7 @@ }, { "cell_type": "code", - "execution_count": 52, + "execution_count": 54, "id": "nutritional-rugby", "metadata": {}, "outputs": [], @@ -9064,7 +9295,7 @@ }, { "cell_type": "code", - "execution_count": 53, + "execution_count": 55, "id": "criminal-marking", "metadata": {}, "outputs": [ @@ -9333,10 +9564,10 @@ "};$(\"#acdaut4 #E1\").addClass(\"acdN0\");$(\"#acdaut4 #E2\").addClass(\"acdN0\");$(\"#acdaut4 #E3\").addClass(\"acdN0\");$(\"#acdaut4 #E4\").addClass(\"acdN0\");$(\"#acdaut4 #E5\").addClass(\"acdN0\");$(\"#acdaut4 #E6\").addClass(\"acdN0\");$(\"#acdaut4 #E7\").addClass(\"acdN0\");$(\"#acdaut4 #E8\").addClass(\"acdN0\");$(\"#acdaut4 #E6\").addClass(\"acdN1\");$(\"#acdaut4 #E1\").click(function(){acd4_edge(1);});$(\"#acdaut4 #E2\").click(function(){acd4_edge(2);});$(\"#acdaut4 #E3\").click(function(){acd4_edge(3);});$(\"#acdaut4 #E4\").click(function(){acd4_edge(4);});$(\"#acdaut4 #E5\").click(function(){acd4_edge(5);});$(\"#acdaut4 #E6\").click(function(){acd4_edge(6);});$(\"#acdaut4 #E7\").click(function(){acd4_edge(7);});$(\"#acdaut4 #E8\").click(function(){acd4_edge(8);});$(\"#acdaut4 #S0\").click(function(){acd4_state(0);});$(\"#acdaut4 #S1\").click(function(){acd4_state(1);});$(\"#acd4 #N0\").click(function(){acd4_node(0, 1);});$(\"#acd4 #N1\").click(function(){acd4_node(1, 0);});" ], "text/plain": [ - " >" + " >" ] }, - "execution_count": 53, + "execution_count": 55, "metadata": {}, "output_type": "execute_result" } @@ -9347,7 +9578,7 @@ }, { "cell_type": "code", - "execution_count": 54, + "execution_count": 56, "id": "e7760d51", "metadata": {}, "outputs": [ @@ -9357,7 +9588,7 @@ "0" ] }, - "execution_count": 54, + "execution_count": 56, "metadata": {}, "output_type": "execute_result" } @@ -9368,7 +9599,7 @@ }, { "cell_type": "code", - "execution_count": 55, + "execution_count": 57, "id": "unusual-dependence", "metadata": { "scrolled": true @@ -9477,10 +9708,10 @@ "\n" ], "text/plain": [ - " *' at 0x7f0c480e2d50> >" + " *' at 0x7f1470107b70> >" ] }, - "execution_count": 55, + "execution_count": 57, "metadata": {}, "output_type": "execute_result" } @@ -9491,7 +9722,7 @@ }, { "cell_type": "code", - "execution_count": 56, + "execution_count": 58, "id": "d5440de1", "metadata": {}, "outputs": [ @@ -9504,11 +9735,11 @@ "\n", "\n", - "\n", - "\n", - "\n", - "[Büchi]\n", + "\n", + "\n", + "\n", + "[Büchi]\n", "\n", "\n", "\n", @@ -9624,10 +9855,10 @@ "\n" ], "text/plain": [ - " *' at 0x7f0c480ea150> >" + " *' at 0x7f147010d240> >" ] }, - "execution_count": 56, + "execution_count": 58, "metadata": {}, "output_type": "execute_result" } @@ -9638,7 +9869,7 @@ }, { "cell_type": "code", - "execution_count": 57, + "execution_count": 59, "id": "9ed0bc59", "metadata": {}, "outputs": [], @@ -9658,7 +9889,7 @@ }, { "cell_type": "code", - "execution_count": 58, + "execution_count": 60, "id": "deb92971", "metadata": {}, "outputs": [ @@ -9963,10 +10194,10 @@ "};$(\"#acdaut5 #E1\").addClass(\"acdN0\");$(\"#acdaut5 #E2\").addClass(\"acdN0\");$(\"#acdaut5 #E3\").addClass(\"acdN0\");$(\"#acdaut5 #E4\").addClass(\"acdN0\");$(\"#acdaut5 #E5\").addClass(\"acdN0\");$(\"#acdaut5 #E6\").addClass(\"acdN0\");$(\"#acdaut5 #E7\").addClass(\"acdN0\");$(\"#acdaut5 #E1\").addClass(\"acdN1\");$(\"#acdaut5 #E3\").addClass(\"acdN1\");$(\"#acdaut5 #E4\").addClass(\"acdN1\");$(\"#acdaut5 #E5\").addClass(\"acdN1\");$(\"#acdaut5 #E7\").addClass(\"acdN2\");$(\"#acdaut5 #E1\").click(function(){acd5_edge(1);});$(\"#acdaut5 #E2\").click(function(){acd5_edge(2);});$(\"#acdaut5 #E3\").click(function(){acd5_edge(3);});$(\"#acdaut5 #E4\").click(function(){acd5_edge(4);});$(\"#acdaut5 #E5\").click(function(){acd5_edge(5);});$(\"#acdaut5 #E6\").click(function(){acd5_edge(6);});$(\"#acdaut5 #E7\").click(function(){acd5_edge(7);});$(\"#acdaut5 #S0\").click(function(){acd5_state(0);});$(\"#acdaut5 #S1\").click(function(){acd5_state(1);});$(\"#acdaut5 #S2\").click(function(){acd5_state(2);});$(\"#acdaut5 #S3\").click(function(){acd5_state(3);});$(\"#acd5 #N0\").click(function(){acd5_node(0, 1);});$(\"#acd5 #N1\").click(function(){acd5_node(1, 0);});$(\"#acd5 #N2\").click(function(){acd5_node(2, 0);});" ], "text/plain": [ - " >" + " >" ] }, - "execution_count": 58, + "execution_count": 60, "metadata": {}, "output_type": "execute_result" } @@ -9984,7 +10215,7 @@ }, { "cell_type": "code", - "execution_count": 59, + "execution_count": 61, "id": "94a02201", "metadata": {}, "outputs": [ @@ -10091,10 +10322,10 @@ "\n" ], "text/plain": [ - " *' at 0x7f0c480e2300> >" + " *' at 0x7f147010d5a0> >" ] }, - "execution_count": 59, + "execution_count": 61, "metadata": {}, "output_type": "execute_result" } @@ -10105,7 +10336,7 @@ }, { "cell_type": "code", - "execution_count": 60, + "execution_count": 62, "id": "d484ba8f", "metadata": {}, "outputs": [ @@ -10118,11 +10349,11 @@ "\n", "\n", - "\n", - "\n", - "\n", - "[Büchi]\n", + "\n", + "\n", + "\n", + "[Büchi]\n", "\n", "\n", "\n", @@ -10221,10 +10452,10 @@ "\n" ], "text/plain": [ - " *' at 0x7f0c480f0420> >" + " *' at 0x7f147010d6f0> >" ] }, - "execution_count": 60, + "execution_count": 62, "metadata": {}, "output_type": "execute_result" } @@ -10235,7 +10466,7 @@ }, { "cell_type": "code", - "execution_count": 61, + "execution_count": 63, "id": "3332e850", "metadata": {}, "outputs": [ @@ -10501,10 +10732,10 @@ "\n" ], "text/plain": [ - " *' at 0x7f0c480f0840> >" + " *' at 0x7f1470116270> >" ] }, - "execution_count": 61, + "execution_count": 63, "metadata": {}, "output_type": "execute_result" } @@ -10525,7 +10756,7 @@ }, { "cell_type": "code", - "execution_count": 62, + "execution_count": 64, "id": "german-vienna", "metadata": {}, "outputs": [ @@ -10595,10 +10826,10 @@ "\n" ], "text/plain": [ - " *' at 0x7f0c480f30c0> >" + " *' at 0x7f1470116630> >" ] }, - "execution_count": 62, + "execution_count": 64, "metadata": {}, "output_type": "execute_result" } @@ -10622,7 +10853,7 @@ }, { "cell_type": "code", - "execution_count": 63, + "execution_count": 65, "id": "chemical-primary", "metadata": {}, "outputs": [ @@ -10632,7 +10863,7 @@ "(spot.trival_maybe(), spot.trival(True))" ] }, - "execution_count": 63, + "execution_count": 65, "metadata": {}, "output_type": "execute_result" } @@ -10643,7 +10874,7 @@ }, { "cell_type": "code", - "execution_count": 64, + "execution_count": 66, "id": "hispanic-floor", "metadata": {}, "outputs": [ @@ -10706,10 +10937,10 @@ "\n" ], "text/plain": [ - " *' at 0x7f0c480f3210> >" + " *' at 0x7f1470116450> >" ] }, - "execution_count": 64, + "execution_count": 66, "metadata": {}, "output_type": "execute_result" } @@ -10720,7 +10951,7 @@ }, { "cell_type": "code", - "execution_count": 65, + "execution_count": 67, "id": "central-london", "metadata": {}, "outputs": [ @@ -10730,7 +10961,7 @@ "(spot.trival(True), spot.trival(True))" ] }, - "execution_count": 65, + "execution_count": 67, "metadata": {}, "output_type": "execute_result" } @@ -10750,7 +10981,7 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 3", + "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, diff --git a/tests/python/zlktree.py b/tests/python/zlktree.py index e1b0c9e7b..c3cb262f2 100644 --- a/tests/python/zlktree.py +++ b/tests/python/zlktree.py @@ -152,3 +152,12 @@ tc.assertTrue(a.equivalent_to(b)) b = spot.acd_transform_sbacc(a, False) tc.assertEqual(str(b.acc()), '(2, Fin(0) & Inf(1))') tc.assertTrue(a.equivalent_to(b)) + + +# This used to be very slow. +c = spot.acc_cond("Rabin 9") +n = spot.zielonka_tree(c).num_branches() +tc.assertEqual(n, 362880) +opt = spot.zielonka_tree_options_MERGE_SUBTREES; +n = spot.zielonka_tree(c, opt).num_branches() +tc.assertEqual(n, 9) From e064726b64155d62aeaab7994649aa54c5829e05 Mon Sep 17 00:00:00 2001 From: Philipp Schlehuber-Caissier Date: Sun, 15 May 2022 23:54:18 +0200 Subject: [PATCH 082/337] Introducing a global variable to define the number of threads * NEWS: Announce * spot/Makefile.am: Add pthread to use threads * spot/misc/common.cc, spot/misc/common.hh: Add variable + getter/setter * spot/misc/Makefile.am: Add common.cc --- NEWS | 3 +++ spot/Makefile.am | 2 +- spot/misc/Makefile.am | 1 + spot/misc/common.cc | 33 +++++++++++++++++++++++++++++++++ spot/misc/common.hh | 8 ++++++++ 5 files changed, 46 insertions(+), 1 deletion(-) create mode 100644 spot/misc/common.cc diff --git a/NEWS b/NEWS index 32a1b0f19..33b74132c 100644 --- a/NEWS +++ b/NEWS @@ -23,6 +23,9 @@ New in spot 2.10.6.dev (not yet released) Library: + - A global variable, together with its setters and getters to define the + maximal number of threads is added to common.hh/common.cc + - The new function suffix_operator_normal_form() implements transformation of formulas to Suffix Operator Normal Form, described in [cimatti.06.fmcad]. diff --git a/spot/Makefile.am b/spot/Makefile.am index 821979f1d..c7bebfe6a 100644 --- a/spot/Makefile.am +++ b/spot/Makefile.am @@ -35,7 +35,7 @@ SUBDIRS = misc priv tl graph twa twacube twaalgos ta taalgos kripke \ lib_LTLIBRARIES = libspot.la libspot_la_SOURCES = -libspot_la_LDFLAGS = $(BUDDY_LDFLAGS) -no-undefined $(SYMBOLIC_LDFLAGS) +libspot_la_LDFLAGS = $(BUDDY_LDFLAGS) -no-undefined -pthread $(SYMBOLIC_LDFLAGS) libspot_la_LIBADD = \ kripke/libkripke.la \ misc/libmisc.la \ diff --git a/spot/misc/Makefile.am b/spot/misc/Makefile.am index e509dbe87..623a13c87 100644 --- a/spot/misc/Makefile.am +++ b/spot/misc/Makefile.am @@ -63,6 +63,7 @@ libmisc_la_SOURCES = \ bareword.cc \ bitset.cc \ bitvect.cc \ + common.cc \ escape.cc \ formater.cc \ intvcomp.cc \ diff --git a/spot/misc/common.cc b/spot/misc/common.cc new file mode 100644 index 000000000..adf9f2da0 --- /dev/null +++ b/spot/misc/common.cc @@ -0,0 +1,33 @@ +// -*- coding: utf-8 -*- +// Copyright (C) 2018 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 . + +#include "config.h" +#include + +static unsigned N_MAX_THREADS = 1; + +void set_nthreads(unsigned nthreads) +{ + N_MAX_THREADS = nthreads; +} + +unsigned get_nthreads() +{ + return N_MAX_THREADS; +} \ No newline at end of file diff --git a/spot/misc/common.hh b/spot/misc/common.hh index e38f9f15a..fc74a8ee7 100644 --- a/spot/misc/common.hh +++ b/spot/misc/common.hh @@ -169,3 +169,11 @@ namespace spot # define SPOT_make_shared_enabled__(TYPE, ...) \ std::make_shared(__VA_ARGS__) #endif + + +// Global variable to determine the maximal number of threads +SPOT_API void +set_nthreads(unsigned nthreads); + +SPOT_API unsigned +get_nthreads(); From 71c2a7b1a60e6aaaf4488d031659ebc75cc512ba Mon Sep 17 00:00:00 2001 From: Philipp Schlehuber-Caissier Date: Sun, 15 May 2022 23:56:19 +0200 Subject: [PATCH 083/337] Add a new function to sort edges sort_edge_srcfirst_ will sort the edge with respect to the src state, then sort each sub list with respect to the given predicate, possibly in parallel. * spot/graph/graph.hh: Here --- spot/graph/graph.hh | 70 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/spot/graph/graph.hh b/spot/graph/graph.hh index fa276131d..dc7ffc6ae 100644 --- a/spot/graph/graph.hh +++ b/spot/graph/graph.hh @@ -28,6 +28,7 @@ #include #include #include +#include namespace spot { @@ -1226,6 +1227,75 @@ namespace spot std::stable_sort(edges_.begin() + 1, edges_.end(), p); } + /// \brief Sort all edges by src first, then, within edges of the same + /// source use the predicate + /// + /// This will invalidate all iterators, and also destroy edge + /// chains. Call chain_edges_() immediately afterwards unless you + /// know what you are doing. + /// \note: for performance this will work in parallel (if enabled) + /// and make a temporary copy of the edges (needs more ram) + /// \pre This needs the edge_vector to be in a coherent state when called + template> + void sort_edges_srcfirst_(Predicate p = Predicate()) + { + //std::cerr << "\nbefore\n"; + //dump_storage(std::cerr); + const auto N = num_states(); + // Read threads once + const unsigned nthreads = get_nthreads(); + + auto idx_list = std::vector(N+1); + auto new_edges = edge_vector_t(); + new_edges.reserve(edges_.size()); + if (SPOT_UNLIKELY(edges_.empty())) + throw std::runtime_error("Empty edge vector!"); + new_edges.resize(1); + // This causes edge 0 to be considered as dead. + new_edges[0].next_succ = 0; + // Copy the edges such that they are sorted by src + for (auto s = 0u; s < N; ++s) + { + idx_list[s] = new_edges.size(); + for (const auto& e : out(s)) + new_edges.push_back(e); + } + idx_list[N] = new_edges.size(); + // New edge sorted by source + // If we have few edge or only one threads + // Benchmark few? + auto bne = new_edges.begin(); + if (nthreads == 1 || edges_.size() < 1000) + { + for (auto s = 0u; s < N; ++s) + std::stable_sort(bne + idx_list[s], + bne + idx_list[s+1], + p); + } + else + { + static auto tv = std::vector(); + SPOT_ASSERT(tv.empty()); + tv.resize(nthreads); + for (unsigned id = 0; id < nthreads; ++id) + tv[id] = std::thread( + [bne, id, N, &idx_list, p, nthreads]() + { + for (auto s = id; s < N; s+=nthreads) + std::stable_sort(bne + idx_list[s], + bne + idx_list[s+1], + p); + return; + }); + for (auto& t : tv) + t.join(); + tv.clear(); + } + // Done + std::swap(edges_, new_edges); + // Like after normal sort_edges, they need to be chained before usage + } + /// \brief Sort edges of the given states /// /// \tparam Predicate : Comparison type From d8cc0c5acbc437fbea741b494fde06de317e9309 Mon Sep 17 00:00:00 2001 From: Philipp Schlehuber-Caissier Date: Mon, 16 May 2022 00:04:04 +0200 Subject: [PATCH 084/337] Introduce a faster merge_states merge_states is now hash-based, uses the new edge-sorting with src first and can be executed in parallel. * spot/twa/twagraph.cc: Here * tests/python/mergedge.py: Test --- spot/twa/twagraph.cc | 161 +++++++++++++++------- tests/python/mergedge.py | 285 ++++++++++++++++++++------------------- 2 files changed, 253 insertions(+), 193 deletions(-) diff --git a/spot/twa/twagraph.cc b/spot/twa/twagraph.cc index c2bdd5650..2fd2eb070 100644 --- a/spot/twa/twagraph.cc +++ b/spot/twa/twagraph.cc @@ -372,13 +372,12 @@ namespace spot throw std::runtime_error( "twa_graph::merge_states() does not work on alternating automata"); + const unsigned nthreads = get_nthreads(); + typedef graph_t::edge_storage_t tr_t; - g_.sort_edges_([](const tr_t& lhs, const tr_t& rhs) + g_.sort_edges_srcfirst_([](const tr_t& lhs, const tr_t& rhs) { - if (lhs.src < rhs.src) - return true; - if (lhs.src > rhs.src) - return false; + assert(lhs.src == rhs.src); if (lhs.acc < rhs.acc) return true; if (lhs.acc > rhs.acc) @@ -449,7 +448,7 @@ namespace spot // Represents which states share a hash // Head is in the unordered_map, // hash_linked_list is like a linked list structure - // of false pointers + // of fake pointers auto hash_linked_list = std::vector(n_states, -1u); auto s_to_hash = std::vector(n_states, 0); @@ -530,8 +529,8 @@ namespace spot }; - static auto checked1 = std::vector(); - static auto checked2 = std::vector(); + thread_local auto checked1 = std::vector(); + thread_local auto checked2 = std::vector(); auto [i1, nsl1, sl1, e1] = e_idx[s1]; auto [i2, nsl2, sl2, e2] = e_idx[s2]; @@ -585,12 +584,10 @@ namespace spot // More efficient version? // Skip checked edges // Last element serves as break - for (; checked1[idx1 - i1]; ++idx1) - { - } - for (; checked2[idx2 - i2]; ++idx2) - { - } + while (checked1[idx1 - i1]) + ++idx1; + while (checked2[idx2 - i2]) + ++idx2; // If one is out of bounds, so is the other if (idx1 == e1) { @@ -614,47 +611,107 @@ namespace spot const unsigned nb_states = num_states(); std::vector remap(nb_states, -1U); - for (unsigned i = 0; i != nb_states; ++i) + // Check each hash + auto check_ix = [&](unsigned ix) { - auto j = spe && (*sp)[i] ? player_map.at(s_to_hash[i]).first - : env_map.at(s_to_hash[i]).first; - for (; j(); + v.clear(); + for (auto i = ix; i != -1U; i = hash_linked_list[i]) + v.push_back(i); + const unsigned N = v.size(); - // Because of the special self-loop tests we use above, - // it's possible that i can be mapped to remap[j] even - // if j was last compatible states found. Consider the - // following cases, taken from an actual test case: - // 18 is equal to 5, 35 is equal to 18, but 35 is not - // equal to 5. - // - // State: 5 - // [0&1&2] 8 {3} - // [!0&1&2] 10 {1} - // [!0&!1&!2] 18 {1} - // [!0&!1&2] 19 {1} - // [!0&1&!2] 20 {1} - // - // State: 18 - // [0&1&2] 8 {3} - // [!0&1&2] 10 {1} - // [!0&!1&!2] 18 {1} // self-loop - // [!0&!1&2] 19 {1} - // [!0&1&!2] 20 {1} - // - // State: 35 - // [0&1&2] 8 {3} - // [!0&1&2] 10 {1} - // [!0&!1&!2] 35 {1} // self-loop - // [!0&!1&2] 19 {1} - // [!0&1&!2] 20 {1} - break; + for (unsigned idx = 0; idx < N; ++idx) + { + auto i = v[idx]; + for (unsigned jdx = 0; jdx < idx; ++jdx) + { + auto j = v[jdx]; + if (state_equal(j, i)) + { + remap[i] = (remap[j] != -1U) ? remap[j] : j; + + // Because of the special self-loop tests we use above, + // it's possible that i can be mapped to remap[j] even + // if j was last compatible states found. Consider the + // following cases, taken from an actual test case: + // 18 is equal to 5, 35 is equal to 18, but 35 is not + // equal to 5. + // + // State: 5 + // [0&1&2] 8 {3} + // [!0&1&2] 10 {1} + // [!0&!1&!2] 18 {1} + // [!0&!1&2] 19 {1} + // [!0&1&!2] 20 {1} + // + // State: 18 + // [0&1&2] 8 {3} + // [!0&1&2] 10 {1} + // [!0&!1&!2] 18 {1} // self-loop + // [!0&!1&2] 19 {1} + // [!0&1&!2] 20 {1} + // + // State: 35 + // [0&1&2] 8 {3} + // [!0&1&2] 10 {1} + // [!0&!1&!2] 35 {1} // self-loop + // [!0&!1&2] 19 {1} + // [!0&1&!2] 20 {1} + break; + } } } - } + }; + + auto upd = [](auto& b, const auto&e, unsigned it) + { + while ((it > 0) & (b != e)) + { + --it; + ++b; + } + }; + + auto worker = [&upd, check_ix, nthreads](unsigned pid, auto begp, auto endp, + auto bege, auto ende) + { + upd(begp, endp, pid); + upd(bege, ende, pid); + for (; begp != endp; upd(begp, endp, nthreads)) + check_ix(begp->second.first); + for (; bege != ende; upd(bege, ende, nthreads)) + check_ix(bege->second.first); + }; + + { + auto begp = player_map.begin(); + auto endp = player_map.end(); + auto bege = env_map.begin(); + auto ende = env_map.end(); + + + if ((nthreads == 1) & (num_states() > 1000)) // Bound? + { + worker(0, begp, endp, bege, ende); + } + else + { + static auto tv = std::vector(); + assert(tv.empty()); + tv.resize(nthreads); + for (unsigned pid = 0; pid < nthreads; ++pid) + tv[pid] = std::thread( + [worker, pid, begp, endp, bege, ende]() + { + worker(pid, begp, endp, bege, ende); + return; + }); + for (auto& t : tv) + t.join(); + tv.clear(); + } + } for (auto& e: edges()) if (remap[e.dst] != -1U) @@ -765,7 +822,7 @@ namespace spot comp_classes_.clear(); // get all compatible classes // Candidate classes share a hash - // A state is compatible to a class if it is compatble + // A state is compatible to a class if it is compatible // to any of its states auto& cand_classes = equiv_class_[hi]; unsigned n_c_classes = cand_classes.size(); diff --git a/tests/python/mergedge.py b/tests/python/mergedge.py index 4e97abe23..2be4d4984 100644 --- a/tests/python/mergedge.py +++ b/tests/python/mergedge.py @@ -23,148 +23,151 @@ import spot from unittest import TestCase tc = TestCase() -aut = spot.automaton("""HOA: v1 States: 1 Start: 0 AP: 1 "a" -Acceptance: 1 Inf(0) --BODY-- State: 0 [0] 0 [0] 0 {0} --END--""") -tc.assertEqual(aut.num_edges(), 2) -aut.merge_edges() -tc.assertEqual(aut.num_edges(), 1) +for nthread in range(1, 16, 2): + spot.set_nthreads(nthread) + tc.assertEqual(spot.get_nthreads(), nthread) + aut = spot.automaton("""HOA: v1 States: 1 Start: 0 AP: 1 "a" + Acceptance: 1 Inf(0) --BODY-- State: 0 [0] 0 [0] 0 {0} --END--""") + tc.assertEqual(aut.num_edges(), 2) + aut.merge_edges() + tc.assertEqual(aut.num_edges(), 1) -aut = spot.automaton(""" -HOA: v1 -States: 2 -Start: 0 -AP: 2 "p0" "p1" -acc-name: Buchi -Acceptance: 1 Inf(0) -properties: trans-labels explicit-labels trans-acc complete ---BODY-- -State: 0 -[!0] 0 {0} -[0] 1 {0} -State: 1 -[!0&!1] 0 {0} -[0 | 1] 1 -[0&!1] 1 {0} ---END--""") -tc.assertEqual(aut.num_edges(), 5) -aut.merge_edges() -tc.assertEqual(aut.num_edges(), 5) -tc.assertFalse(spot.is_deterministic(aut)) -aut = spot.split_edges(aut) -tc.assertEqual(aut.num_edges(), 9) -aut.merge_edges() -tc.assertEqual(aut.num_edges(), 5) -tc.assertTrue(spot.is_deterministic(aut)) + aut = spot.automaton(""" + HOA: v1 + States: 2 + Start: 0 + AP: 2 "p0" "p1" + acc-name: Buchi + Acceptance: 1 Inf(0) + properties: trans-labels explicit-labels trans-acc complete + --BODY-- + State: 0 + [!0] 0 {0} + [0] 1 {0} + State: 1 + [!0&!1] 0 {0} + [0 | 1] 1 + [0&!1] 1 {0} + --END--""") + tc.assertEqual(aut.num_edges(), 5) + aut.merge_edges() + tc.assertEqual(aut.num_edges(), 5) + tc.assertFalse(spot.is_deterministic(aut)) + aut = spot.split_edges(aut) + tc.assertEqual(aut.num_edges(), 9) + aut.merge_edges() + tc.assertEqual(aut.num_edges(), 5) + tc.assertTrue(spot.is_deterministic(aut)) -aut = spot.automaton(""" -HOA: v1 -States: 3 -Start: 0 -AP: 1 "a" -acc-name: Buchi -Acceptance: 1 Inf(0) -properties: trans-labels explicit-labels trans-acc complete ---BODY-- -State: 0 -[!0] 1 {0} -[0] 2 {0} -State: 1 -[!0] 1 {0} -[0] 1 -State: 2 -[!0] 2 {0} -[0] 1 ---END--""") -aut.merge_states() -tc.assertEqual(aut.num_edges(), 4) -tc.assertEqual(aut.num_states(), 2) -tc.assertTrue(spot.is_deterministic(aut)) -tc.assertTrue(aut.prop_complete()) -aut.merge_states() -tc.assertEqual(aut.num_edges(), 4) -tc.assertEqual(aut.num_states(), 2) -tc.assertTrue(spot.is_deterministic(aut)) -tc.assertTrue(aut.prop_complete()) + aut = spot.automaton(""" + HOA: v1 + States: 3 + Start: 0 + AP: 1 "a" + acc-name: Buchi + Acceptance: 1 Inf(0) + properties: trans-labels explicit-labels trans-acc complete + --BODY-- + State: 0 + [!0] 1 {0} + [0] 2 {0} + State: 1 + [!0] 1 {0} + [0] 1 + State: 2 + [!0] 2 {0} + [0] 1 + --END--""") + aut.merge_states() + tc.assertEqual(aut.num_edges(), 4) + tc.assertEqual(aut.num_states(), 2) + tc.assertTrue(spot.is_deterministic(aut)) + tc.assertTrue(aut.prop_complete()) + aut.merge_states() + tc.assertEqual(aut.num_edges(), 4) + tc.assertEqual(aut.num_states(), 2) + tc.assertTrue(spot.is_deterministic(aut)) + tc.assertTrue(aut.prop_complete()) -aa = spot.automaton(""" -HOA: v1 States: 41 Start: 0 AP: 3 "allfinished" "finished_0" -"finished_1" acc-name: parity max odd 4 Acceptance: 4 Inf(3) | (Fin(2) -& (Inf(1) | Fin(0))) properties: trans-labels explicit-labels -trans-acc colored properties: deterministic --BODY-- State: 0 -[!0&!1&!2] 1 {1} [!0&!1&2] 2 {1} [!0&1&!2] 3 {1} [!0&1&2] 4 {1} -[0&!1&!2] 5 {1} [0&!1&2] 6 {1} [0&1&!2] 7 {1} [0&1&2] 8 {1} State: 1 -[!0&!1&!2] 1 {1} [!0&!1&2] 2 {1} [0&1&!2] 7 {1} [0&1&2] 8 {1} -[!0&1&!2] 9 {1} [!0&1&2] 10 {1} [0&!1&!2] 11 {1} [0&!1&2] 12 {1} -State: 2 [!0&!1&2] 2 {1} [0&1&!2] 7 {1} [0&1&2] 8 {1} [!0&1&2] 10 {1} -[0&!1&!2] 11 {1} [0&!1&2] 12 {1} [!0&!1&!2] 13 {1} [!0&1&!2] 14 {1} -State: 3 [0&1&!2] 7 {1} [0&1&2] 8 {1} [!0&1&!2] 9 {1} [!0&1&2] 10 {1} -[0&!1&!2] 11 {1} [0&!1&2] 12 {1} [!0&!1&!2] 15 {1} [!0&!1&2] 16 {1} -State: 4 [0&1&!2] 7 {1} [0&1&2] 8 {1} [!0&1&2] 10 {1} [0&!1&!2] 11 {1} -[0&!1&2] 12 {1} [!0&1&!2] 14 {1} [!0&!1&2] 16 {1} [!0&!1&!2] 17 {1} -State: 5 [0&1&2] 8 {3} [!0&1&2] 10 {1} [!0&!1&!2] 18 {1} [!0&!1&2] 19 -{1} [!0&1&!2] 20 {1} State: 6 [0&1&2] 8 {1} [!0&1&2] 10 {1} [!0&!1&2] -19 {1} [!0&1&!2] 20 {1} [!0&!1&!2] 21 {1} State: 7 [0&1&2] 8 {3} -[!0&1&2] 10 {1} [!0&!1&2] 19 {1} [!0&1&!2] 20 {1} [!0&!1&!2] 22 {1} -State: 8 [!0&!1&!2] 5 {1} [0&1&2] 8 {3} [!0&1&2] 10 {1} [!0&!1&2] 19 -{1} [!0&1&!2] 20 {1} State: 9 [0&!1&!2] 5 {1} [0&1&!2] 7 {1} [0&1&2] 8 -{1} [!0&1&!2] 9 {1} [!0&1&2] 10 {1} [0&!1&2] 12 {1} [!0&!1&!2] 23 {1} -[!0&!1&2] 24 {1} State: 10 [0&!1&!2] 5 {3} [0&1&!2] 7 {3} [0&1&2] 8 -{3} [!0&1&2] 10 {2} [0&!1&2] 12 {3} [!0&1&!2] 14 {1} [!0&!1&2] 24 {1} -[!0&!1&!2] 25 {1} State: 11 [0&1&2] 8 {3} [!0&1&2] 10 {1} [!0&!1&2] 19 -{1} [!0&!1&!2] 26 {1} [!0&1&!2] 27 {1} State: 12 [0&1&2] 8 {3} -[!0&1&2] 10 {1} [!0&!1&2] 19 {1} [!0&1&!2] 27 {1} [!0&!1&!2] 28 {1} -State: 13 [!0&!1&2] 2 {1} [0&1&!2] 7 {1} [0&1&2] 8 {1} [!0&1&2] 10 {1} -[0&!1&!2] 11 {1} [0&!1&2] 12 {1} [!0&!1&!2] 13 {1} [!0&1&!2] 14 {1} -State: 14 [0&!1&!2] 5 {3} [0&1&!2] 7 {3} [0&1&2] 8 {3} [!0&1&2] 10 {2} -[0&!1&2] 12 {3} [!0&1&!2] 14 {1} [!0&!1&2] 24 {2} [!0&!1&!2] 29 {1} -State: 15 [0&1&!2] 7 {1} [0&1&2] 8 {1} [!0&1&!2] 9 {1} [!0&1&2] 10 {1} -[0&!1&!2] 11 {1} [0&!1&2] 12 {1} [!0&!1&!2] 15 {1} [!0&!1&2] 16 {1} -State: 16 [0&1&!2] 7 {1} [0&1&2] 8 {1} [!0&1&2] 10 {1} [0&!1&!2] 11 -{1} [0&!1&2] 12 {1} [!0&1&!2] 14 {1} [!0&!1&2] 16 {1} [!0&!1&!2] 17 -{1} State: 17 [0&1&!2] 7 {1} [0&1&2] 8 {1} [!0&1&2] 10 {1} [0&!1&!2] -11 {1} [0&!1&2] 12 {1} [!0&1&!2] 14 {1} [!0&!1&2] 16 {1} [!0&!1&!2] 17 -{1} State: 18 [0&1&2] 8 {3} [!0&1&2] 10 {1} [!0&!1&!2] 18 {1} -[!0&!1&2] 19 {1} [!0&1&!2] 20 {1} State: 19 [0&1&!2] 7 {3} [0&1&2] 8 -{3} [!0&!1&2] 19 {1} [!0&!1&!2] 30 {1} [!0&1&!2] 31 {1} [!0&1&2] 32 -{1} State: 20 [0&1&2] 8 {3} [0&!1&2] 12 {1} [!0&1&!2] 20 {1} [!0&1&2] -32 {1} [!0&!1&!2] 33 {1} [!0&!1&2] 34 {1} State: 21 [0&1&2] 8 {1} -[!0&1&2] 10 {1} [!0&!1&!2] 18 {1} [!0&!1&2] 19 {1} [!0&1&!2] 20 {1} -State: 22 [0&1&2] 8 {3} [!0&1&2] 10 {1} [!0&!1&2] 19 {1} [!0&1&!2] 20 -{1} [!0&!1&!2] 35 {1} State: 23 [0&!1&!2] 5 {1} [0&1&!2] 7 {1} [0&1&2] -8 {1} [!0&1&!2] 9 {1} [!0&1&2] 10 {1} [0&!1&2] 12 {1} [!0&!1&!2] 23 -{1} [!0&!1&2] 24 {1} State: 24 [0&!1&!2] 5 {3} [0&1&!2] 7 {3} [0&1&2] -8 {3} [!0&1&2] 10 {2} [0&!1&2] 12 {3} [!0&1&!2] 14 {2} [!0&!1&2] 24 -{1} [!0&!1&!2] 25 {1} State: 25 [0&!1&!2] 5 {3} [0&1&!2] 7 {3} [0&1&2] -8 {3} [!0&1&2] 10 {2} [0&!1&2] 12 {3} [!0&1&!2] 14 {2} [!0&!1&2] 24 -{1} [!0&!1&!2] 25 {1} State: 26 [0&1&2] 8 {3} [!0&1&2] 10 {1} -[!0&!1&2] 19 {1} [!0&!1&!2] 26 {1} [!0&1&!2] 27 {1} State: 27 [0&1&2] -8 {3} [0&!1&2] 12 {3} [!0&1&!2] 27 {1} [!0&1&2] 32 {1} [!0&!1&!2] 36 -{1} [!0&!1&2] 37 {1} State: 28 [0&1&2] 8 {3} [!0&1&2] 10 {1} [!0&!1&2] -19 {1} [!0&!1&!2] 26 {1} [!0&1&!2] 27 {1} State: 29 [0&!1&!2] 5 {3} -[0&1&!2] 7 {3} [0&1&2] 8 {3} [!0&1&2] 10 {2} [0&!1&2] 12 {3} [!0&1&!2] -14 {1} [!0&!1&2] 24 {2} [!0&!1&!2] 29 {1} State: 30 [0&1&!2] 7 {3} -[0&1&2] 8 {3} [!0&!1&2] 19 {1} [!0&!1&!2] 30 {1} [!0&1&!2] 31 {1} -[!0&1&2] 32 {1} State: 31 [0&!1&!2] 5 {3} [0&1&!2] 7 {3} [0&1&2] 8 {3} -[0&!1&2] 12 {3} [!0&1&!2] 31 {1} [!0&1&2] 32 {2} [!0&!1&2] 37 {2} -[!0&!1&!2] 38 {1} State: 32 [0&!1&!2] 5 {3} [0&1&!2] 7 {3} [0&1&2] 8 -{3} [0&!1&2] 12 {3} [!0&1&!2] 31 {1} [!0&1&2] 32 {2} [!0&!1&2] 37 {1} -[!0&!1&!2] 39 {1} State: 33 [0&1&2] 8 {3} [0&!1&2] 12 {1} [!0&1&!2] 20 -{1} [!0&1&2] 32 {1} [!0&!1&!2] 33 {1} [!0&!1&2] 34 {1} State: 34 -[0&1&!2] 7 {3} [0&1&2] 8 {3} [0&!1&!2] 11 {1} [0&!1&2] 12 {1} -[!0&1&!2] 31 {1} [!0&1&2] 32 {1} [!0&!1&2] 34 {1} [!0&!1&!2] 40 {1} -State: 35 [0&1&2] 8 {3} [!0&1&2] 10 {1} [!0&!1&2] 19 {1} [!0&1&!2] 20 -{1} [!0&!1&!2] 35 {1} State: 36 [0&1&2] 8 {3} [0&!1&2] 12 {3} -[!0&1&!2] 27 {1} [!0&1&2] 32 {1} [!0&!1&!2] 36 {1} [!0&!1&2] 37 {1} -State: 37 [0&!1&!2] 5 {3} [0&1&!2] 7 {3} [0&1&2] 8 {3} [0&!1&2] 12 {3} -[!0&1&!2] 31 {2} [!0&1&2] 32 {2} [!0&!1&2] 37 {1} [!0&!1&!2] 39 {1} -State: 38 [0&!1&!2] 5 {3} [0&1&!2] 7 {3} [0&1&2] 8 {3} [0&!1&2] 12 {3} -[!0&1&!2] 31 {1} [!0&1&2] 32 {2} [!0&!1&2] 37 {2} [!0&!1&!2] 38 {1} -State: 39 [0&!1&!2] 5 {3} [0&1&!2] 7 {3} [0&1&2] 8 {3} [0&!1&2] 12 {3} -[!0&1&!2] 31 {2} [!0&1&2] 32 {2} [!0&!1&2] 37 {1} [!0&!1&!2] 39 {1} -State: 40 [0&1&!2] 7 {3} [0&1&2] 8 {3} [0&!1&!2] 11 {1} [0&!1&2] 12 -{1} [!0&1&!2] 31 {1} [!0&1&2] 32 {1} [!0&!1&2] 34 {1} [!0&!1&!2] 40 -{1} --END--""") -aa.merge_states() -# This used to cause a segfault reported by Philipp. -print(aa.to_str()) + aa = spot.automaton(""" + HOA: v1 States: 41 Start: 0 AP: 3 "allfinished" "finished_0" + "finished_1" acc-name: parity max odd 4 Acceptance: 4 Inf(3) | (Fin(2) + & (Inf(1) | Fin(0))) properties: trans-labels explicit-labels + trans-acc colored properties: deterministic --BODY-- State: 0 + [!0&!1&!2] 1 {1} [!0&!1&2] 2 {1} [!0&1&!2] 3 {1} [!0&1&2] 4 {1} + [0&!1&!2] 5 {1} [0&!1&2] 6 {1} [0&1&!2] 7 {1} [0&1&2] 8 {1} State: 1 + [!0&!1&!2] 1 {1} [!0&!1&2] 2 {1} [0&1&!2] 7 {1} [0&1&2] 8 {1} + [!0&1&!2] 9 {1} [!0&1&2] 10 {1} [0&!1&!2] 11 {1} [0&!1&2] 12 {1} + State: 2 [!0&!1&2] 2 {1} [0&1&!2] 7 {1} [0&1&2] 8 {1} [!0&1&2] 10 {1} + [0&!1&!2] 11 {1} [0&!1&2] 12 {1} [!0&!1&!2] 13 {1} [!0&1&!2] 14 {1} + State: 3 [0&1&!2] 7 {1} [0&1&2] 8 {1} [!0&1&!2] 9 {1} [!0&1&2] 10 {1} + [0&!1&!2] 11 {1} [0&!1&2] 12 {1} [!0&!1&!2] 15 {1} [!0&!1&2] 16 {1} + State: 4 [0&1&!2] 7 {1} [0&1&2] 8 {1} [!0&1&2] 10 {1} [0&!1&!2] 11 {1} + [0&!1&2] 12 {1} [!0&1&!2] 14 {1} [!0&!1&2] 16 {1} [!0&!1&!2] 17 {1} + State: 5 [0&1&2] 8 {3} [!0&1&2] 10 {1} [!0&!1&!2] 18 {1} [!0&!1&2] 19 + {1} [!0&1&!2] 20 {1} State: 6 [0&1&2] 8 {1} [!0&1&2] 10 {1} [!0&!1&2] + 19 {1} [!0&1&!2] 20 {1} [!0&!1&!2] 21 {1} State: 7 [0&1&2] 8 {3} + [!0&1&2] 10 {1} [!0&!1&2] 19 {1} [!0&1&!2] 20 {1} [!0&!1&!2] 22 {1} + State: 8 [!0&!1&!2] 5 {1} [0&1&2] 8 {3} [!0&1&2] 10 {1} [!0&!1&2] 19 + {1} [!0&1&!2] 20 {1} State: 9 [0&!1&!2] 5 {1} [0&1&!2] 7 {1} [0&1&2] 8 + {1} [!0&1&!2] 9 {1} [!0&1&2] 10 {1} [0&!1&2] 12 {1} [!0&!1&!2] 23 {1} + [!0&!1&2] 24 {1} State: 10 [0&!1&!2] 5 {3} [0&1&!2] 7 {3} [0&1&2] 8 + {3} [!0&1&2] 10 {2} [0&!1&2] 12 {3} [!0&1&!2] 14 {1} [!0&!1&2] 24 {1} + [!0&!1&!2] 25 {1} State: 11 [0&1&2] 8 {3} [!0&1&2] 10 {1} [!0&!1&2] 19 + {1} [!0&!1&!2] 26 {1} [!0&1&!2] 27 {1} State: 12 [0&1&2] 8 {3} + [!0&1&2] 10 {1} [!0&!1&2] 19 {1} [!0&1&!2] 27 {1} [!0&!1&!2] 28 {1} + State: 13 [!0&!1&2] 2 {1} [0&1&!2] 7 {1} [0&1&2] 8 {1} [!0&1&2] 10 {1} + [0&!1&!2] 11 {1} [0&!1&2] 12 {1} [!0&!1&!2] 13 {1} [!0&1&!2] 14 {1} + State: 14 [0&!1&!2] 5 {3} [0&1&!2] 7 {3} [0&1&2] 8 {3} [!0&1&2] 10 {2} + [0&!1&2] 12 {3} [!0&1&!2] 14 {1} [!0&!1&2] 24 {2} [!0&!1&!2] 29 {1} + State: 15 [0&1&!2] 7 {1} [0&1&2] 8 {1} [!0&1&!2] 9 {1} [!0&1&2] 10 {1} + [0&!1&!2] 11 {1} [0&!1&2] 12 {1} [!0&!1&!2] 15 {1} [!0&!1&2] 16 {1} + State: 16 [0&1&!2] 7 {1} [0&1&2] 8 {1} [!0&1&2] 10 {1} [0&!1&!2] 11 + {1} [0&!1&2] 12 {1} [!0&1&!2] 14 {1} [!0&!1&2] 16 {1} [!0&!1&!2] 17 + {1} State: 17 [0&1&!2] 7 {1} [0&1&2] 8 {1} [!0&1&2] 10 {1} [0&!1&!2] + 11 {1} [0&!1&2] 12 {1} [!0&1&!2] 14 {1} [!0&!1&2] 16 {1} [!0&!1&!2] 17 + {1} State: 18 [0&1&2] 8 {3} [!0&1&2] 10 {1} [!0&!1&!2] 18 {1} + [!0&!1&2] 19 {1} [!0&1&!2] 20 {1} State: 19 [0&1&!2] 7 {3} [0&1&2] 8 + {3} [!0&!1&2] 19 {1} [!0&!1&!2] 30 {1} [!0&1&!2] 31 {1} [!0&1&2] 32 + {1} State: 20 [0&1&2] 8 {3} [0&!1&2] 12 {1} [!0&1&!2] 20 {1} [!0&1&2] + 32 {1} [!0&!1&!2] 33 {1} [!0&!1&2] 34 {1} State: 21 [0&1&2] 8 {1} + [!0&1&2] 10 {1} [!0&!1&!2] 18 {1} [!0&!1&2] 19 {1} [!0&1&!2] 20 {1} + State: 22 [0&1&2] 8 {3} [!0&1&2] 10 {1} [!0&!1&2] 19 {1} [!0&1&!2] 20 + {1} [!0&!1&!2] 35 {1} State: 23 [0&!1&!2] 5 {1} [0&1&!2] 7 {1} [0&1&2] + 8 {1} [!0&1&!2] 9 {1} [!0&1&2] 10 {1} [0&!1&2] 12 {1} [!0&!1&!2] 23 + {1} [!0&!1&2] 24 {1} State: 24 [0&!1&!2] 5 {3} [0&1&!2] 7 {3} [0&1&2] + 8 {3} [!0&1&2] 10 {2} [0&!1&2] 12 {3} [!0&1&!2] 14 {2} [!0&!1&2] 24 + {1} [!0&!1&!2] 25 {1} State: 25 [0&!1&!2] 5 {3} [0&1&!2] 7 {3} [0&1&2] + 8 {3} [!0&1&2] 10 {2} [0&!1&2] 12 {3} [!0&1&!2] 14 {2} [!0&!1&2] 24 + {1} [!0&!1&!2] 25 {1} State: 26 [0&1&2] 8 {3} [!0&1&2] 10 {1} + [!0&!1&2] 19 {1} [!0&!1&!2] 26 {1} [!0&1&!2] 27 {1} State: 27 [0&1&2] + 8 {3} [0&!1&2] 12 {3} [!0&1&!2] 27 {1} [!0&1&2] 32 {1} [!0&!1&!2] 36 + {1} [!0&!1&2] 37 {1} State: 28 [0&1&2] 8 {3} [!0&1&2] 10 {1} [!0&!1&2] + 19 {1} [!0&!1&!2] 26 {1} [!0&1&!2] 27 {1} State: 29 [0&!1&!2] 5 {3} + [0&1&!2] 7 {3} [0&1&2] 8 {3} [!0&1&2] 10 {2} [0&!1&2] 12 {3} [!0&1&!2] + 14 {1} [!0&!1&2] 24 {2} [!0&!1&!2] 29 {1} State: 30 [0&1&!2] 7 {3} + [0&1&2] 8 {3} [!0&!1&2] 19 {1} [!0&!1&!2] 30 {1} [!0&1&!2] 31 {1} + [!0&1&2] 32 {1} State: 31 [0&!1&!2] 5 {3} [0&1&!2] 7 {3} [0&1&2] 8 {3} + [0&!1&2] 12 {3} [!0&1&!2] 31 {1} [!0&1&2] 32 {2} [!0&!1&2] 37 {2} + [!0&!1&!2] 38 {1} State: 32 [0&!1&!2] 5 {3} [0&1&!2] 7 {3} [0&1&2] 8 + {3} [0&!1&2] 12 {3} [!0&1&!2] 31 {1} [!0&1&2] 32 {2} [!0&!1&2] 37 {1} + [!0&!1&!2] 39 {1} State: 33 [0&1&2] 8 {3} [0&!1&2] 12 {1} [!0&1&!2] 20 + {1} [!0&1&2] 32 {1} [!0&!1&!2] 33 {1} [!0&!1&2] 34 {1} State: 34 + [0&1&!2] 7 {3} [0&1&2] 8 {3} [0&!1&!2] 11 {1} [0&!1&2] 12 {1} + [!0&1&!2] 31 {1} [!0&1&2] 32 {1} [!0&!1&2] 34 {1} [!0&!1&!2] 40 {1} + State: 35 [0&1&2] 8 {3} [!0&1&2] 10 {1} [!0&!1&2] 19 {1} [!0&1&!2] 20 + {1} [!0&!1&!2] 35 {1} State: 36 [0&1&2] 8 {3} [0&!1&2] 12 {3} + [!0&1&!2] 27 {1} [!0&1&2] 32 {1} [!0&!1&!2] 36 {1} [!0&!1&2] 37 {1} + State: 37 [0&!1&!2] 5 {3} [0&1&!2] 7 {3} [0&1&2] 8 {3} [0&!1&2] 12 {3} + [!0&1&!2] 31 {2} [!0&1&2] 32 {2} [!0&!1&2] 37 {1} [!0&!1&!2] 39 {1} + State: 38 [0&!1&!2] 5 {3} [0&1&!2] 7 {3} [0&1&2] 8 {3} [0&!1&2] 12 {3} + [!0&1&!2] 31 {1} [!0&1&2] 32 {2} [!0&!1&2] 37 {2} [!0&!1&!2] 38 {1} + State: 39 [0&!1&!2] 5 {3} [0&1&!2] 7 {3} [0&1&2] 8 {3} [0&!1&2] 12 {3} + [!0&1&!2] 31 {2} [!0&1&2] 32 {2} [!0&!1&2] 37 {1} [!0&!1&!2] 39 {1} + State: 40 [0&1&!2] 7 {3} [0&1&2] 8 {3} [0&!1&!2] 11 {1} [0&!1&2] 12 + {1} [!0&1&!2] 31 {1} [!0&1&2] 32 {1} [!0&!1&2] 34 {1} [!0&!1&!2] 40 + {1} --END--""") + aa.merge_states() + # This used to cause a segfault reported by Philipp. + print(aa.to_str()) From aca6bd90429b2c4d6828c61ba08ccd9d7e97f2b6 Mon Sep 17 00:00:00 2001 From: Florian Renkin Date: Tue, 31 May 2022 13:40:58 +0200 Subject: [PATCH 085/337] synthesis: Deletion of an incorrect case in the bypass With a formula like G(b1) & (GFi <-> GF(b1)), a direct strategy was created while it is unrealizable. * spot/twaalgos/synthesis.cc: here. * tests/core/ltlsynt.test: add tests --- spot/twaalgos/synthesis.cc | 55 ++++++++++++++++++++------------------ tests/core/ltlsynt.test | 41 ++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+), 26 deletions(-) diff --git a/spot/twaalgos/synthesis.cc b/spot/twaalgos/synthesis.cc index b249acce9..5a5d1297a 100644 --- a/spot/twaalgos/synthesis.cc +++ b/spot/twaalgos/synthesis.cc @@ -1168,6 +1168,27 @@ namespace spot //Anonymous for try_create_strat namespace { + // Checks that 2 sets have a common element. Use it instead + // of set_intersection when we just want to check if they have a common + // element because it avoids going through the rest of the sets after an + // element is found. + static bool + are_intersecting(const std::set &v1, + const std::set &v2) + { + auto v1_pos = v1.begin(), v2_pos = v2.begin(), v1_end = v1.end(), + v2_end = v2.end(); + while (v1_pos != v1_end && v2_pos != v2_end) + { + if (*v1_pos < *v2_pos) + ++v1_pos; + else if (*v2_pos < *v1_pos) + ++v2_pos; + else + return true; + } + return false; + } class formula_2_inout_props { private: @@ -1372,10 +1393,6 @@ namespace spot if (combin == -1U) return ret_sol_maybe(); - // We know that a strategy exists and we don't want to construct it. - if (!want_strategy) - return ret_sol_exists(nullptr); - formula f_left = f_other[(combin + 1) % 2]; formula f_right = f_other[combin % 2]; if (!(combin % 2)) @@ -1384,6 +1401,14 @@ namespace spot std::swap(left_outs, right_outs); } + auto [_, g_outs] = form2props.aps_of(f_g); + if (are_intersecting(g_outs, right_outs)) + return ret_sol_maybe(); + + // We know that a strategy exists and we don't want to construct it. + if (!want_strategy) + return ret_sol_exists(nullptr); + auto trans = create_translator(gi); trans.set_pref(postprocessor::Deterministic | postprocessor::Complete); @@ -1484,28 +1509,6 @@ namespace spot namespace // anonymous for subsformula { using namespace spot; - // Checks that 2 sets have a common element. Use it instead - // of set_intersection when we just want to check if they have a common - // element because it avoids going through the rest of the sets after an - // element is found. - static bool - are_intersecting(const std::set &v1, - const std::set &v2) - { - auto v1_pos = v1.begin(), v2_pos = v2.begin(), v1_end = v1.end(), - v2_end = v2.end(); - while (v1_pos != v1_end && v2_pos != v2_end) - { - if (*v1_pos < *v2_pos) - ++v1_pos; - else if (*v2_pos < *v1_pos) - ++v2_pos; - else - return true; - } - return false; - } - static std::pair, std::set> algo4(const std::vector &assumptions, const std::set &outs, diff --git a/tests/core/ltlsynt.test b/tests/core/ltlsynt.test index 1badb9b4b..9319d96a8 100644 --- a/tests/core/ltlsynt.test +++ b/tests/core/ltlsynt.test @@ -944,3 +944,44 @@ ltlsynt -f '(GFa <-> GFb) && (Gc)' --outs=b,c --verbose --bypass=no\ --algo=acd 2> out sed 's/ [0-9.e-]* seconds/ X seconds/g' out > outx diff outx exp + +# Bypass: check that in G(b1) ∧ (Büchi ↔ GF(b2)), b1 and b2 don't share an AP. +# We do it because G(o1 ∨ o2) ∧ (GFi ↔ GFo1) is realizable while +# G(o1) ∧ (GFi ↔ GFo1) is not realizable. So we cannot conclude if +# they share an AP. +cat >exp < GFo1) +direct strategy might exist but was not found. +translating formula done in X seconds +automaton has 1 states and 1 colors +LAR construction done in X seconds +DPA has 1 states, 1 colors +split inputs and outputs done in X seconds +automaton has 3 states +solving game with acceptance: Streett 1 +game solved in X seconds +EOF +ltlsynt -f "G(o1) & (GFi <-> GFo1)" --outs="o1" --verbose\ + --bypass=yes 2> out || true +sed 's/ [0-9.e-]* seconds/ X seconds/g' out > outx +diff outx exp + +cat >exp < GFo1) +direct strategy might exist but was not found. +translating formula done in X seconds +automaton has 1 states and 2 colors +LAR construction done in X seconds +DPA has 2 states, 2 colors +split inputs and outputs done in X seconds +automaton has 6 states +solving game with acceptance: parity max odd 4 +game solved in X seconds +simplification took X seconds +EOF +ltlsynt -f "G(o1|o2) & (GFi <-> GFo1)" --outs="o1,o2" --verbose\ + --bypass=yes 2> out +sed 's/ [0-9.e-]* seconds/ X seconds/g' out > outx +diff outx exp \ No newline at end of file From 721d5695ec73ca234dce7a45fa0fd76519cb2013 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Wed, 25 May 2022 17:02:38 +0200 Subject: [PATCH 086/337] add a newer version of the generic emptiness check MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As discussed with Jan Strejček. * spot/twa/acc.cc, spot/twa/acc.hh (fin_unit_one_split): New function. (fin_one_extract): Return the simplified acceptance condition as an optimization. * python/spot/impl.i: Bind this new function. * tests/python/acc.py: New file, to test it. * tests/Makefile.am: Add acc.py. * spot/twaalgos/genem.cc, spot/twaalgos/genem.hh: Implement the spot211 variant of the emptiness check. * tests/python/genem.py: Test it. * tests/python/acc_cond.ipynb: Adjust test for fin_one_extract. --- python/spot/impl.i | 11 +++++ spot/twa/acc.cc | 99 ++++++++++++++++++++++++++++++++++++- spot/twa/acc.hh | 75 +++++++++++++++++++++++----- spot/twaalgos/genem.cc | 27 ++++++++-- spot/twaalgos/genem.hh | 10 +++- tests/Makefile.am | 1 + tests/python/acc.py | 65 ++++++++++++++++++++++++ tests/python/acc_cond.ipynb | 41 ++++++++++++--- tests/python/genem.py | 9 ++-- 9 files changed, 308 insertions(+), 30 deletions(-) create mode 100644 tests/python/acc.py diff --git a/python/spot/impl.i b/python/spot/impl.i index b7f116201..a07709005 100644 --- a/python/spot/impl.i +++ b/python/spot/impl.i @@ -551,6 +551,17 @@ namespace std { } %apply std::vector &OUTPUT {std::vector& pairs} %apply std::vector &OUTPUT {std::vector& pairs} +// Must occur before the twa declaration +%typemap(out) SWIGTYPE spot::acc_cond::fin_unit_one_split %{ + { + auto& v = static_cast>($1); + $result = PyTuple_Pack(3, + swig::from(std::get<0>(v)), + swig::from(std::get<1>(v)), + swig::from(std::get<2>(v))); + } +%} + %include %template(pair_bool_mark) std::pair; diff --git a/spot/twa/acc.cc b/spot/twa/acc.cc index ce5d463aa..07aac36f9 100644 --- a/spot/twa/acc.cc +++ b/spot/twa/acc.cc @@ -2707,6 +2707,35 @@ namespace spot return false; } + // Check wheter pos looks like Fin(f) or Fin(f)&rest + bool is_conj_fin(const acc_cond::acc_word* pos, acc_cond::mark_t f) + { + auto sub = pos - pos->sub.size; + do + { + switch (pos->sub.op) + { + case acc_cond::acc_op::And: + --pos; + break; + case acc_cond::acc_op::Or: + pos -= pos->sub.size + 1; + break; + case acc_cond::acc_op::Fin: + if (pos[-1].mark & f) + return true; + SPOT_FALLTHROUGH; + case acc_cond::acc_op::Inf: + case acc_cond::acc_op::InfNeg: + case acc_cond::acc_op::FinNeg: + pos -= 2; + break; + } + } + while (sub < pos); + return false; + } + acc_cond::acc_code extract_fin(const acc_cond::acc_word* pos, acc_cond::mark_t f) { @@ -2716,7 +2745,7 @@ namespace spot case acc_cond::acc_op::And: case acc_cond::acc_op::Fin: case acc_cond::acc_op::Inf: - return pos; + return strip_rec(pos, f, true, false); case acc_cond::acc_op::Or: { --pos; @@ -2725,7 +2754,7 @@ namespace spot { if (uses_fin(pos, f)) { - acc_cond::acc_code tmp(pos); + auto tmp = strip_rec(pos, f, true, false); tmp |= std::move(res); std::swap(tmp, res); } @@ -2742,6 +2771,52 @@ namespace spot SPOT_UNREACHABLE(); return {}; } + + std::pair + split_top_fin(const acc_cond::acc_word* pos, acc_cond::mark_t f) + { + auto start = pos - pos->sub.size; + switch (pos->sub.op) + { + case acc_cond::acc_op::And: + case acc_cond::acc_op::Fin: + if (is_conj_fin(pos, f)) + return {pos, acc_cond::acc_code::f()}; + SPOT_FALLTHROUGH; + case acc_cond::acc_op::Inf: + return {acc_cond::acc_code::f(), pos}; + case acc_cond::acc_op::Or: + { + --pos; + auto left = acc_cond::acc_code::f(); + auto right = acc_cond::acc_code::f(); + do + { + if (is_conj_fin(pos, f)) + { + auto tmp = strip_rec(pos, f, true, false); + tmp |= std::move(left); + std::swap(tmp, left); + } + else + { + acc_cond::acc_code tmp(pos); + tmp |= std::move(right); + std::swap(tmp, right); + } + pos -= pos->sub.size + 1; + } + while (pos > start); + return {std::move(left), std::move(right)}; + } + case acc_cond::acc_op::FinNeg: + case acc_cond::acc_op::InfNeg: + SPOT_UNREACHABLE(); + return {acc_cond::acc_code::f(), acc_cond::acc_code::f()}; + } + SPOT_UNREACHABLE(); + return {acc_cond::acc_code::f(), acc_cond::acc_code::f()}; + } } std::pair @@ -2756,6 +2831,26 @@ namespace spot return {selected_fin, extract_fin(pos, {(unsigned) selected_fin})}; } + std::tuple + acc_cond::acc_code::fin_unit_one_split() const + { + if (SPOT_UNLIKELY(is_t() || is_f())) + err: + throw std::runtime_error("fin_unit_one_split(): no Fin"); + const acc_cond::acc_word* pos = &back(); + int selected_fin = has_top_fin(pos); + if (selected_fin >= 0) + { + auto [left, right] = split_top_fin(pos, {(unsigned) selected_fin}); + return {selected_fin, std::move(left), std::move(right)}; + } + selected_fin = fin_one(); + if (selected_fin < 0) + goto err; + acc_cond::mark_t fo_m = {(unsigned) selected_fin}; + return {selected_fin, extract_fin(pos, fo_m), force_inf(fo_m)}; + } + namespace { bool diff --git a/spot/twa/acc.hh b/spot/twa/acc.hh index 455850f35..905f5c40a 100644 --- a/spot/twa/acc.hh +++ b/spot/twa/acc.hh @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2014-2021 Laboratoire de Recherche et Développement +// Copyright (C) 2014-2022 Laboratoire de Recherche et Développement // de l'Epita. // // This file is part of Spot, a model checking library. @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -1271,7 +1272,8 @@ namespace spot int fin_one() const; /// \brief Return one acceptance set i that appears as `Fin(i)` - /// in the condition, and all disjuncts containing it. + /// in the condition, and all disjuncts containing it with + /// Fin(i) changed to true and Inf(i) to false. /// /// If the condition is a disjunction and one of the disjunct /// has the shape `...&Fin(i)&...`, then `i` will be prefered @@ -1282,13 +1284,34 @@ namespace spot /// `Fin(i)` have been removed. /// /// For example on - /// `Fin(1)&Inf(2)|Inf(3)&Inf(4)|Inf(5)&(Fin(1)|Fin(7))` - /// the output would be the pair - /// `(1, Fin(1)&Inf(2)|Inf(5)&(Fin(1)|Fin(7)))`. - /// On that example `Fin(1)` is prefered to `Fin(7)` because - /// it appears at the top-level. + /// `Fin(1)&Inf(2)|Inf(3)&Inf(4)|Inf(5)&(Fin(1)|Fin(7))` the + /// output would be the pair, we would select `Fin(1)` over + /// `Fin(7)` because it appears at the top-level. Then we would + /// collect the disjuncts containing `Fin(1)`, that is, + /// `Fin(1)&Inf(2)|Inf(5)&(Fin(1)|Fin(7)))`. Finally we would + /// replace `Fin(1)` by true and `Inf(1)` by false. The return + /// value would then be `(1, Inf(2)|Inf(5))`. std::pair fin_one_extract() const; + /// \brief Split an acceptance condition, trying to select one + /// unit-Fin. + /// + /// If the condition is a disjunction and one of the disjunct as + /// has the shape `...&Fin(i)&...`, then this will return + /// (i, left, right), where left is all disjunct of this form, and + /// right are all the others. + /// + /// If the input formula has the shape `...&Fin(i)&...` then left + /// is set to the entire formula, and right is empty. + /// + /// If no disjunct has the right shape, then a random Fin(i) is + /// searched in the formula, and the output (i, left, right). + /// is such that left contains all disjuncts containing Fin(i) + /// (at any depth), and right contains the original formlula + /// where Fin(i) has been replaced by false. + std::tuple + fin_unit_one_split() const; + /// \brief Help closing accepting or rejecting cycle. /// /// Assuming you have a partial cycle visiting all acceptance @@ -2203,7 +2226,8 @@ namespace spot } /// \brief Return one acceptance set i that appears as `Fin(i)` - /// in the condition, and all disjuncts containing it. + /// in the condition, and all disjuncts containing it with + /// Fin(i) changed to true and Inf(i) to false. /// /// If the condition is a disjunction and one of the disjunct /// has the shape `...&Fin(i)&...`, then `i` will be prefered @@ -2214,17 +2238,42 @@ namespace spot /// `Fin(i)` have been removed. /// /// For example on - /// `Fin(1)&Inf(2)|Inf(3)&Inf(4)|Inf(5)&(Fin(1)|Fin(7))` - /// the output would be the pair - /// `(1, Fin(1)&Inf(2)|Inf(5)&(Fin(1)|Fin(7)))`. - /// On that example `Fin(1)` is prefered to `Fin(7)` because - /// it appears at the top-level. + /// `Fin(1)&Inf(2)|Inf(3)&Inf(4)|Inf(5)&(Fin(1)|Fin(7))` the + /// output would be the pair, we would select `Fin(1)` over + /// `Fin(7)` because it appears at the top-level. Then we would + /// collect the disjuncts containing `Fin(1)`, that is, + /// `Fin(1)&Inf(2)|Inf(5)&(Fin(1)|Fin(7)))`. Finally we would + /// replace `Fin(1)` by true and `Inf(1)` by false. The return + /// value would then be `(1, Inf(2)|Inf(5))`. std::pair fin_one_extract() const { auto [f, c] = code_.fin_one_extract(); return {f, {num_sets(), std::move(c)}}; } + /// \brief Split an acceptance condition, trying to select one + /// unit-Fin. + /// + /// If the condition is a disjunction and one of the disjunct as + /// has the shape `...&Fin(i)&...`, then this will return + /// (i, left, right), where left is all disjunct of this form, and + /// right are all the others. + /// + /// If the input formula has the shape `...&Fin(i)&...` then left + /// is set to the entire formula, and right is empty. + /// + /// If no disjunct has the right shape, then a random Fin(i) is + /// searched in the formula, and the output (i, left, right). + /// is such that left contains all disjuncts containing Fin(i) + /// (at any depth), and right contains the original formlula + /// where Fin(i) has been replaced by false. + std::tuple + fin_unit_one_split() const + { + auto [f, l, r] = code_.fin_unit_one_split(); + return {f, {num_sets(), std::move(l)}, {num_sets(), std::move(r)}}; + } + /// \brief Return the top-level disjuncts. /// /// For instance, if the formula is diff --git a/spot/twaalgos/genem.cc b/spot/twaalgos/genem.cc index e49f5b07c..51b2ea903 100644 --- a/spot/twaalgos/genem.cc +++ b/spot/twaalgos/genem.cc @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2017-2021 Laboratoire de Recherche et Developpement +// Copyright (C) 2017-2022 Laboratoire de Recherche et Developpement // de l'Epita (LRDE). // // This file is part of Spot, a model checking library. @@ -25,7 +25,7 @@ namespace spot { namespace { - enum genem_version_t { spot28, atva19, spot29, spot210 }; + enum genem_version_t { spot28, atva19, spot29, spot210, spot211 }; static genem_version_t genem_version = spot29; } @@ -33,6 +33,8 @@ namespace spot { if (emversion == nullptr || !strcasecmp(emversion, "spot29")) genem_version = spot29; + else if (!strcasecmp(emversion, "spot211")) + genem_version = spot211; else if (!strcasecmp(emversion, "spot210")) genem_version = spot210; else if (!strcasecmp(emversion, "spot28")) @@ -41,7 +43,8 @@ namespace spot genem_version = atva19; else throw std::invalid_argument("generic_emptiness_check version should be " - "one of {spot28, atva19, spot29, spot210}"); + "one of {spot28, atva19, spot29, spot210, " + "spot211}"); } namespace @@ -84,6 +87,8 @@ namespace spot scc_split_check(const scc_info& si, unsigned scc, const acc_cond& acc, Extra extra, acc_cond::mark_t tocut) { + if (genem_version == spot211 || genem_version == spot210) + tocut |= acc.fin_unit(); scc_and_mark_filter filt(si, scc, tocut); filt.override_acceptance(acc); scc_info upper_si(filt, EarlyStop @@ -118,13 +123,27 @@ namespace spot // Try to accept when Fin(fo) == true acc_cond::mark_t fo_m = {(unsigned) fo}; if (!scc_split_check - (si, scc, fpart.remove(fo_m, true), extra, fo_m)) + (si, scc, fpart, extra, fo_m)) if constexpr (EarlyStop) return false; // Try to accept when Fin(fo) == false acc = acc.force_inf(fo_m); } while (!acc.is_f()); + else if (genem_version == spot211) + { + do + { + auto [fo, fpart, rest] = acc.fin_unit_one_split(); + acc_cond::mark_t fo_m = {(unsigned) fo}; + if (!scc_split_check + (si, scc, fpart, extra, fo_m)) + if constexpr (EarlyStop) + return false; + acc = rest; + } + while (!acc.is_f()); + } else if (genem_version == spot29) do { diff --git a/spot/twaalgos/genem.hh b/spot/twaalgos/genem.hh index 2d1ded4c7..3c3e5de51 100644 --- a/spot/twaalgos/genem.hh +++ b/spot/twaalgos/genem.hh @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2017-2021 Laboratoire de Recherche et Developpement +// Copyright (C) 2017-2022 Laboratoire de Recherche et Developpement // de l'Epita (LRDE). // // This file is part of Spot, a model checking library. @@ -100,7 +100,13 @@ namespace spot /// - "spot29" improves upon the worst case of atva19. This is /// the default. /// - "spot210" improves upon "spot29" in a few cases where a Fin - /// is shared by multiple disjuncts. + /// is shared by multiple disjuncts. This improve the worst + /// case complexity of EL-automata in the general case, but worsen + /// the complexity of Hyper-Rabin in particular. + /// - "spot211" is another attempt at fixing worst case complexities. + /// Compared to atva19, this improves the complexities for Rabin, + /// GeneralizedRabin, and EL without worsening the complexity of + /// Hyper-Rabin. SPOT_API void generic_emptiness_check_select_version(const char* emversion = nullptr); diff --git a/tests/Makefile.am b/tests/Makefile.am index 3582a8493..b4627f3e6 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -391,6 +391,7 @@ TESTS_python = \ python/_altscc.ipynb \ python/_autparserr.ipynb \ python/_aux.ipynb \ + python/acc.py \ python/accparse2.py \ python/alarm.py \ python/aliases.py \ diff --git a/tests/python/acc.py b/tests/python/acc.py new file mode 100644 index 000000000..8a23dcd46 --- /dev/null +++ b/tests/python/acc.py @@ -0,0 +1,65 @@ +# -*- mode: python; coding: utf-8 -*- +# Copyright (C) 2022 Laboratoire de Recherche et Développement +# de l'Epita +# +# 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 . + +import spot +from unittest import TestCase +tc = TestCase() + +a = spot.acc_cond('parity min odd 5') +tc.assertEqual(str(a.fin_unit_one_split()), + '(0, {}, spot.acc_cond(5, "f"))'.format(repr(a))) + +a.set_acceptance('Rabin 3') +tc.assertEqual(str(a.fin_unit_one_split()), + '(0, spot.acc_cond(5, "Inf(1)"), ' + 'spot.acc_cond(5, "(Fin(2) & Inf(3)) | (Fin(4) & Inf(5))"))') + +a.set_acceptance('(Fin(0)|Inf(3))&(Fin(1)|Inf(4))&(Fin(2)|Inf(5)) |\ +(Fin(0)|Inf(4))&(Fin(1)|Inf(5))&(Fin(2)|Inf(3)) |\ +(Fin(0)|Inf(5))&(Fin(1)|Inf(3))&(Fin(2)|Inf(4))') + +tc.maxDiff = None +tc.assertEqual(str(a.fin_unit_one_split()), + '(0, spot.acc_cond(5, ' + '"((Fin(1) | Inf(4)) & (Fin(2) | Inf(5))) | ' + '((Fin(1) | Inf(5)) & (Fin(2) | Inf(3))) | ' + '((Fin(1) | Inf(3)) & (Fin(2) | Inf(4)))"), ' + 'spot.acc_cond(5, ' + '"(Inf(3) & (Fin(1) | Inf(4)) & (Fin(2) | Inf(5))) | ' + '(Inf(4) & (Fin(1) | Inf(5)) & (Fin(2) | Inf(3))) | ' + '(Inf(5) & (Fin(1) | Inf(3)) & (Fin(2) | Inf(4)))"))') + +a = a.remove([4], True) +tc.assertEqual(str(a.fin_unit_one_split()), + '(1, spot.acc_cond(5, ' + '"(Fin(0) | Inf(3)) & (Fin(2) | Inf(5))"), ' + 'spot.acc_cond(5, ' + '"(Fin(0) & (Fin(1) | Inf(5)) & (Fin(2) | Inf(3))) | ' + '((Fin(0) | Inf(5)) & (Fin(1) | Inf(3)) & Fin(2))"))') + +def report_missing_exception(): + raise RuntimeError("missing exception") + +a.set_acceptance("Inf(0)") +try: + a.fin_unit_one_split() +except RuntimeError as e: + tc.assertIn('no Fin', str(e)) +else: + report_missing_exception() diff --git a/tests/python/acc_cond.ipynb b/tests/python/acc_cond.ipynb index 76580fcdd..492c416ca 100644 --- a/tests/python/acc_cond.ipynb +++ b/tests/python/acc_cond.ipynb @@ -1416,7 +1416,7 @@ "source": [ "`fin_one()` return the number of one color `x` that appears as `Fin(x)` in the formula, or `-1` if the formula is Fin-less.\n", "\n", - "The variant `fin_one_extract()` consider the acceptance condition as a disjunction (if the top-level operator is not a disjunction, we just assume the formula is a disjunction with only one disjunct), and return a pair `(x,c)` where `c` is the disjunction of all disjuncts of the original formula where `Fin(x)` appear. Also this function tries to choose an `x` such that one of the disjunct has the form `...&Fin(x)&...` if possible: this is visible in the third example, where 5 is prefered to 2." + "The variant `fin_one_extract()` consider the acceptance condition as a disjunction (if the top-level operator is not a disjunction, we just assume the formula is a disjunction with only one disjunct), and return a pair `(x,c)` where `c` is the disjunction of all disjuncts of the original formula where `Fin(x)` used to appear but where `Fin(x)` have been replaced by `true`, and `Inf(x)` by `false`. Also this function tries to choose an `x` such that one of the disjunct has the form `...&Fin(x)&...` if possible: this is visible in the third example, where 5 is prefered to 2." ] }, { @@ -1430,7 +1430,7 @@ "text": [ "(4, (Fin(0) | Inf(1)) & (Fin(2) | Inf(3)))\n", "0\n", - "(0, spot.acc_cond(4, \"(Fin(0) | Inf(1)) & (Fin(2) | Inf(3))\"))\n" + "(0, spot.acc_cond(4, \"Fin(2) | Inf(3)\"))\n" ] } ], @@ -1451,7 +1451,7 @@ "text": [ "(6, (Fin(0) & Inf(1)) | (Fin(2) & Inf(3)) | (Fin(4) & Inf(5)))\n", "0\n", - "(0, spot.acc_cond(6, \"Fin(0) & Inf(1)\"))\n" + "(0, spot.acc_cond(6, \"Inf(1)\"))\n" ] } ], @@ -1473,7 +1473,7 @@ "text": [ "(6, (Inf(0) & (Fin(2) | Inf(3))) | (Inf(4) & Fin(5)) | ((Inf(0)&Inf(5)) & (Fin(0)|Fin(5))))\n", "2\n", - "(5, spot.acc_cond(6, \"(Inf(4) & Fin(5)) | ((Inf(0)&Inf(5)) & (Fin(0)|Fin(5)))\"))\n" + "(5, spot.acc_cond(6, \"Inf(4)\"))\n" ] } ], @@ -1483,11 +1483,40 @@ "print(acc3.fin_one())\n", "print(acc3.fin_one_extract())" ] + }, + { + "cell_type": "code", + "execution_count": 58, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "(8, (Fin(1) & Inf(2)) | (Inf(3)&Inf(4)) | (Inf(5) & (Fin(1)|Fin(7))))\n", + "1\n", + "(1, spot.acc_cond(8, \"Inf(2) | Inf(5)\"))\n" + ] + } + ], + "source": [ + "acc4 = spot.acc_cond('Fin(1)&Inf(2)|Inf(3)&Inf(4)|Inf(5)&(Fin(1)|Fin(7))')\n", + "print(acc4)\n", + "print(acc4.fin_one())\n", + "print(acc4.fin_one_extract())" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] } ], "metadata": { "kernelspec": { - "display_name": "Python 3", + "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, @@ -1501,7 +1530,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.7.3" + "version": "3.10.5" } }, "nbformat": 4, diff --git a/tests/python/genem.py b/tests/python/genem.py index 0c9d0809a..962112ac0 100644 --- a/tests/python/genem.py +++ b/tests/python/genem.py @@ -305,14 +305,17 @@ def run_bench(automata): res3c = spot.generic_emptiness_check(aut) spot.generic_emptiness_check_select_version("spot210") res3d = spot.generic_emptiness_check(aut) + spot.generic_emptiness_check_select_version("spot211") + res3e = spot.generic_emptiness_check(aut) + spot.generic_emptiness_check_select_version("spot29") res2 = spot.remove_fin(aut).is_empty() res1 = generic_emptiness2(aut) res = (str(res1)[0] + str(res2)[0] + str(res3a)[0] + str(res3b)[0] + str(res3c)[0] + str(res3d)[0] - + str(res4)[0] + str(res5)[0]) + + str(res3e)[0] + str(res4)[0] + str(res5)[0]) print(res) - tc.assertIn(res, ('TTTTTTTT', 'FFFFFFFF')) - if res == 'FFFFFFFF': + tc.assertIn(res, ('TTTTTTTTT', 'FFFFFFFFF')) + if res == 'FFFFFFFFF': run3 = spot.generic_accepting_run(aut) tc.assertTrue(run3.replay(spot.get_cout())) From 23908f3d2f2854872c7f80053e3961a52d8a60f2 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Thu, 16 Jun 2022 23:43:50 +0200 Subject: [PATCH 087/337] Add a --enable-pthread option to activate experimental threading code * NEWS, README, doc/org/compile.org: Mention the option and its effect on compilation requirements. * configure.ac: Add the --enable-pthread option, and ENABLE_PTHREAD macro. * doc/org/g++wrap.in, spot/Makefile.am, spot/libspot.pc.in: Compile with -pthread conditionally. * spot/graph/graph.hh, spot/twa/twagraph.cc: Adjust the code to not use thread-local variables, and let the pthread code be optional. * .gitlab-ci.yml: Activate --enable-pthread in two configurations. --- .gitlab-ci.yml | 8 ++++---- NEWS | 12 ++++++++++++ README | 6 ++++++ configure.ac | 11 ++++++++++- doc/org/compile.org | 5 ++++- doc/org/g++wrap.in | 4 ++-- spot/Makefile.am | 4 ++-- spot/graph/graph.hh | 13 +++++++++---- spot/libspot.pc.in | 2 +- spot/twa/twagraph.cc | 46 ++++++++++++++++++++++++++++---------------- 10 files changed, 79 insertions(+), 32 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 3c66af0b7..a1bb0ca9a 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -22,9 +22,9 @@ debian-stable-gcc: image: gitlab-registry.lrde.epita.fr/spot/buildenv/debian:stable script: - autoreconf -vfi - - ./configure --enable-max-accsets=256 + - ./configure --enable-max-accsets=256 --enable-pthread - make - - make distcheck + - make distcheck DISTCHECK_CONFIGURE_FLAGS='--enable-max-accsets=256 --enable-pthread' artifacts: when: always paths: @@ -89,7 +89,7 @@ debian-gcc-snapshot: - autoreconf -vfi - ./configure --with-included-ltdl CXX='g++' - make - - make distcheck DISTCHECK_CONFIGURE_FLAGS='--with-included-ltdl' + - make distcheck DISTCHECK_CONFIGURE_FLAGS='--with-included-ltdl' allow_failure: true artifacts: when: always @@ -111,7 +111,7 @@ alpine-gcc: - autoreconf -vfi - ./configure - make - - make distcheck || { chmod -R u+w ./spot-*; false; } + - make distcheck DISTCHECK_CONFIGURE_FLAGS='--enable-pthread' || { chmod -R u+w ./spot-*; false; } artifacts: when: always paths: diff --git a/NEWS b/NEWS index 33b74132c..b8067d47a 100644 --- a/NEWS +++ b/NEWS @@ -1,5 +1,17 @@ New in spot 2.10.6.dev (not yet released) + Build: + + - A new configure option --enable-pthread enable the compilation of + Spot with -pthread, and activate the parallel version of some + algorithms. If Spot is compiled with -pthread enabled, any user + linking with Spot should also link with the pthread library. In + order to not break existing build setups using Spot, this option + is currently disabled by default in this release. We plan to turn + it on by default in some future release. Third-party project + using Spot may want to start linking with -pthread in prevision + for this change. + Command-line tools: - autfilt has a new options --aliases=drop|keep to specify diff --git a/README b/README index 836bc51b0..7581df4ad 100644 --- a/README +++ b/README @@ -173,6 +173,12 @@ flags specific to Spot: client code should be compiled with -D_GLIBCXX_DEBUG as well. This options should normally only be useful to run Spot's test-suite. + --enable-pthread + Build and link with the -pthread option, and activate a few + parallel variants of the algorithms. This is currently disabled + by default, as it require all third-party tools using Spot to + build with -pthread as well. + --enable-c++20 Build everything in C++20 mode. We use that in our build farm to ensure that Spot can be used in C++20 projects as well. diff --git a/configure.ac b/configure.ac index 2ac9ff616..7c0ff62ae 100644 --- a/configure.ac +++ b/configure.ac @@ -53,6 +53,15 @@ AC_ARG_ENABLE([c++20], [Compile in C++20 mode.])], [enable_20=$enableval], [enable_20=no]) +AC_ARG_ENABLE([pthread], + [AS_HELP_STRING([--enable-pthread], + [Allow libspot to use POSIX threads.])], + [enable_pthread=$enableval], [enable_pthread=no]) +if test "$enable_pthread" = yes; then + AC_DEFINE([ENABLE_PTHREAD], [1], [Whether Spot is compiled with -pthread.]) + AC_SUBST([LIBSPOT_PTHREAD], [-pthread]) +fi + AC_ARG_ENABLE([doxygen], [AS_HELP_STRING([--enable-doxygen], [enable generation of Doxygen documentation (requires Doxygen)])], @@ -150,7 +159,7 @@ AX_CHECK_BUDDY AC_CHECK_FUNCS([times kill alarm sigaction sched_getcpu]) oLIBS=$LIBS -LIBS="$LIBS -lpthread" +LIBS="$LIBS -pthread" AC_CHECK_FUNCS([pthread_setaffinity_np]) LIBS=$oLIBS diff --git a/doc/org/compile.org b/doc/org/compile.org index 8d4a3b1ca..6c2f8e6c6 100644 --- a/doc/org/compile.org +++ b/doc/org/compile.org @@ -210,11 +210,14 @@ one library requiring another, you will need to link with the =bddx= library. This should be as simple as adding =-lbddx= after =-lspot= in the first three cases. +Similarly, if Spot has been configured with =--enable-pthread=, you +will need to add =-pthread= to the compiler flags. + In the fourth case where =libtool= is used to link against =libspot.la= linking against =libbddx.la= should not be necessary because Libtool already handles such dependencies. However the version of =libtool= distributed with Debian is patched to ignore those dependencies, so in this -case you 2 +case you have to list all dependencies. * Additional suggestions diff --git a/doc/org/g++wrap.in b/doc/org/g++wrap.in index c4a61a39c..b176af28b 100755 --- a/doc/org/g++wrap.in +++ b/doc/org/g++wrap.in @@ -1,6 +1,6 @@ #!/bin/sh # This is a wrapper around the compiler, to ensure that the code -# example run from the org-mode file are all linked with Spot. +# examples run from org-mode files are all linked with Spot. # # Also we save errors to org.errors, so that we can detect issues # after org-mode has exported everything. Otherwise these errors @@ -8,7 +8,7 @@ @top_builddir@/libtool link @CXX@ @CXXFLAGS@ @CPPFLAGS@ -Wall -Werror \ -I@abs_top_builddir@ -I@abs_top_srcdir@ -I@abs_top_srcdir@/buddy/src \ "$@" @abs_top_builddir@/spot/libspot.la \ - @abs_top_builddir@/buddy/src/libbddx.la 2> errors.$$ + @abs_top_builddir@/buddy/src/libbddx.la @LIBSPOT_PTHREAD@ 2> errors.$$ code=$? if test $code -ne 0 && test -s errors.$$; then cat errors.$$ >>org.errors diff --git a/spot/Makefile.am b/spot/Makefile.am index c7bebfe6a..72cbef22d 100644 --- a/spot/Makefile.am +++ b/spot/Makefile.am @@ -1,5 +1,5 @@ ## -*- coding: utf-8 -*- -## Copyright (C) 2009, 2010, 2012, 2013, 2014, 2015, 2016, 2017, 2020 +## Copyright (C) 2009, 2010, 2012, 2013, 2014, 2015, 2016, 2017, 2020, 2022 ## Laboratoire de Recherche et Développement de l'Epita (LRDE). ## Copyright (C) 2003, 2004 Laboratoire d'Informatique de Paris 6 (LIP6), ## département Systèmes Répartis Coopératifs (SRC), Université Pierre @@ -35,7 +35,7 @@ SUBDIRS = misc priv tl graph twa twacube twaalgos ta taalgos kripke \ lib_LTLIBRARIES = libspot.la libspot_la_SOURCES = -libspot_la_LDFLAGS = $(BUDDY_LDFLAGS) -no-undefined -pthread $(SYMBOLIC_LDFLAGS) +libspot_la_LDFLAGS = $(BUDDY_LDFLAGS) -no-undefined @LIBSPOT_PTHREAD@ $(SYMBOLIC_LDFLAGS) libspot_la_LIBADD = \ kripke/libkripke.la \ misc/libmisc.la \ diff --git a/spot/graph/graph.hh b/spot/graph/graph.hh index dc7ffc6ae..6419fc27a 100644 --- a/spot/graph/graph.hh +++ b/spot/graph/graph.hh @@ -20,6 +20,7 @@ #pragma once #include +#include #include #include #include @@ -28,7 +29,9 @@ #include #include #include -#include +#ifdef SPOT_ENABLE_PTHREAD +# include +#endif // SPOT_ENABLE_PTHREAD namespace spot { @@ -1242,8 +1245,6 @@ namespace spot //std::cerr << "\nbefore\n"; //dump_storage(std::cerr); const auto N = num_states(); - // Read threads once - const unsigned nthreads = get_nthreads(); auto idx_list = std::vector(N+1); auto new_edges = edge_vector_t(); @@ -1265,13 +1266,17 @@ namespace spot // If we have few edge or only one threads // Benchmark few? auto bne = new_edges.begin(); +#ifdef SPOT_ENABLE_PTHREAD + const unsigned nthreads = get_nthreads(); if (nthreads == 1 || edges_.size() < 1000) +#endif { for (auto s = 0u; s < N; ++s) std::stable_sort(bne + idx_list[s], bne + idx_list[s+1], p); } +#ifdef SPOT_ENABLE_PTHREAD else { static auto tv = std::vector(); @@ -1291,7 +1296,7 @@ namespace spot t.join(); tv.clear(); } - // Done +#endif std::swap(edges_, new_edges); // Like after normal sort_edges, they need to be chained before usage } diff --git a/spot/libspot.pc.in b/spot/libspot.pc.in index 2dac1de5d..9cb877b34 100644 --- a/spot/libspot.pc.in +++ b/spot/libspot.pc.in @@ -8,5 +8,5 @@ Description: A library of LTL and omega-automata algorithms for model checking URL: https://spot.lrde.epita.fr/ Version: @PACKAGE_VERSION@ Cflags: -I${includedir} -Libs: -L${libdir} -lspot +Libs: -L${libdir} -lspot @LIBSPOT_PTHREAD@ Requires: libbddx diff --git a/spot/twa/twagraph.cc b/spot/twa/twagraph.cc index 2fd2eb070..5b4da10a3 100644 --- a/spot/twa/twagraph.cc +++ b/spot/twa/twagraph.cc @@ -372,7 +372,11 @@ namespace spot throw std::runtime_error( "twa_graph::merge_states() does not work on alternating automata"); +#ifdef ENABLE_PTHREAD const unsigned nthreads = get_nthreads(); +#else + constexpr unsigned nthreads = 1; +#endif typedef graph_t::edge_storage_t tr_t; g_.sort_edges_srcfirst_([](const tr_t& lhs, const tr_t& rhs) @@ -511,9 +515,12 @@ namespace spot // << ((env_map.size()+player_map.size())/((float)n_states)) // << '\n'; + // Check whether we can merge two states // and takes into account the self-loops - auto state_equal = [&](unsigned s1, unsigned s2) + auto state_equal = [&e_vec, &e_chain, &e_idx](unsigned s1, unsigned s2, + std::vector& checked1, + std::vector& checked2) { auto edge_data_comp = [](const auto& lhs, const auto& rhs) @@ -528,10 +535,6 @@ namespace spot return false; }; - - thread_local auto checked1 = std::vector(); - thread_local auto checked2 = std::vector(); - auto [i1, nsl1, sl1, e1] = e_idx[s1]; auto [i2, nsl2, sl2, e2] = e_idx[s2]; @@ -612,10 +615,10 @@ namespace spot std::vector remap(nb_states, -1U); // Check each hash - auto check_ix = [&](unsigned ix) + auto check_ix = [&](unsigned ix, std::vector& v, + std::vector& checked1, + std::vector& checked2) { - // Reduce cache miss - thread_local auto v = std::vector(); v.clear(); for (auto i = ix; i != -1U; i = hash_linked_list[i]) v.push_back(i); @@ -627,7 +630,7 @@ namespace spot for (unsigned jdx = 0; jdx < idx; ++jdx) { auto j = v[jdx]; - if (state_equal(j, i)) + if (state_equal(j, i, checked1, checked2)) { remap[i] = (remap[j] != -1U) ? remap[j] : j; @@ -675,13 +678,19 @@ namespace spot auto worker = [&upd, check_ix, nthreads](unsigned pid, auto begp, auto endp, auto bege, auto ende) - { - upd(begp, endp, pid); - upd(bege, ende, pid); - for (; begp != endp; upd(begp, endp, nthreads)) - check_ix(begp->second.first); - for (; bege != ende; upd(bege, ende, nthreads)) - check_ix(bege->second.first); + { + // Temporary storage for list of edges to reduce cache misses + std::vector v; + // Vector reused by all invocations of state_equal to mark edges + // that have been matched already. + std::vector checked1; + std::vector checked2; + upd(begp, endp, pid); + upd(bege, ende, pid); + for (; begp != endp; upd(begp, endp, nthreads)) + check_ix(begp->second.first, v, checked1, checked2); + for (; bege != ende; upd(bege, ende, nthreads)) + check_ix(bege->second.first, v, checked1, checked2); }; { @@ -690,10 +699,12 @@ namespace spot auto bege = env_map.begin(); auto ende = env_map.end(); - +#ifdef ENABLE_PTHREAD if ((nthreads == 1) & (num_states() > 1000)) // Bound? { +#endif // ENABLE_PTHREAD worker(0, begp, endp, bege, ende); +#ifdef ENABLE_PTHREAD } else { @@ -711,6 +722,7 @@ namespace spot t.join(); tv.clear(); } +#endif // ENABLE_PTHREAD } for (auto& e: edges()) From 8161a8c53176cdbd0fd196fddc140a0416aad060 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Tue, 21 Jun 2022 14:24:08 +0200 Subject: [PATCH 088/337] tests: workaround test not failing if the Spot support more colors * configure.ac (MAX_ACCSETS): Add AC_SUBST. * tests/run.in: Define MAX_ACCSETS. * tests/core/prodchain.test: Test MAX_ACCSETS. --- configure.ac | 1 + tests/core/prodchain.test | 12 ++++++++---- tests/run.in | 5 ++++- 3 files changed, 13 insertions(+), 5 deletions(-) diff --git a/configure.ac b/configure.ac index 7c0ff62ae..2d6b4be1f 100644 --- a/configure.ac +++ b/configure.ac @@ -79,6 +79,7 @@ if test 0 -eq `expr $enable_max_accsets % $default_max_accsets` then AC_DEFINE_UNQUOTED([MAX_ACCSETS], [$enable_max_accsets], [The maximal number of acceptance sets supported (also known as acceptance marks)]) + AC_SUBST([MAX_ACCSETS], [$enable_max_accsets]) else AC_MSG_ERROR([The argument of --enable-max-accsets must be a multiple of $default_max_accsets]) fi diff --git a/tests/core/prodchain.test b/tests/core/prodchain.test index 0a7f1a1d9..e00422148 100755 --- a/tests/core/prodchain.test +++ b/tests/core/prodchain.test @@ -32,8 +32,10 @@ for i in *.hoa; do shift done shift -autfilt "$@" 2> error && exit 1 -grep 'Too many acceptance sets used' error +if $MAX_ACCSETS -eq 32; then + autfilt "$@" 2> error && exit 1 + grep 'Too many acceptance sets used' error +fi autfilt -B "$@" > result test "127,253,508,1" = `autfilt --stats=%s,%e,%t,%a result` @@ -44,7 +46,9 @@ for i in *.hoa; do shift done shift -autfilt "$@" 2> error && exit 1 -grep 'Too many acceptance sets used' error +if $MAX_ACCSETS -eq 32; then + autfilt "$@" 2> error && exit 1 + grep 'Too many acceptance sets used' error +fi autfilt -B "$@" > result test "45,89,180,1" = `autfilt --stats=%s,%e,%t,%a result` diff --git a/tests/run.in b/tests/run.in index 7eaa7732c..3b9470bef 100755 --- a/tests/run.in +++ b/tests/run.in @@ -1,6 +1,6 @@ #!/bin/sh # -*- coding: utf-8 -*- -# Copyright (C) 2010-2011, 2014-2016, 2018-2021 Laboratoire de Recherche +# Copyright (C) 2010-2011, 2014-2016, 2018-2022 Laboratoire de Recherche # et Developpement de l'EPITA (LRDE). # Copyright (C) 2003, 2004 Laboratoire d'Informatique de Paris 6 # (LIP6), département Systèmes Répartis Coopératifs (SRC), Université @@ -75,6 +75,9 @@ export SPOT_DOTDEFAULT= SPOT_UNINSTALLED=1 export SPOT_UNINSTALLED +MAX_ACCSETS=@MAX_ACCSETS@ +export MAX_ACCSETS + case $1 in */*) dir=${1%/*} From df685433f473b2830cf5aae93581489b0964ad0b Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Wed, 22 Jun 2022 14:33:16 +0200 Subject: [PATCH 089/337] bin: separate process_file() for aut and ltl * bin/common_finput.cc, bin/common_finput.hh, bin/common_hoaread.hh (process_file): Split into... (process_ltl_file, process_aut_filt): ... these, as we will need both in ltlsynt. --- bin/common_finput.cc | 12 ++++++++++-- bin/common_finput.hh | 5 ++++- bin/common_hoaread.hh | 8 +++++--- 3 files changed, 19 insertions(+), 6 deletions(-) diff --git a/bin/common_finput.cc b/bin/common_finput.cc index 6f09601e0..8e78b5599 100644 --- a/bin/common_finput.cc +++ b/bin/common_finput.cc @@ -303,7 +303,13 @@ job_processor::process_stream(std::istream& is, } int -job_processor::process_file(const char* filename) +job_processor::process_aut_file(const char*) +{ + throw std::runtime_error("process_aut_file not defined for this tool"); +} + +int +job_processor::process_ltl_file(const char* filename) { col_to_read = 0; @@ -366,8 +372,10 @@ job_processor::run() error |= process_string(j.str); break; case job_type::LTL_FILENAME: + error |= process_ltl_file(j.str); + break; case job_type::AUT_FILENAME: - error |= process_file(j.str); + error |= process_aut_file(j.str); break; default: throw std::runtime_error("unexpected job type"); diff --git a/bin/common_finput.hh b/bin/common_finput.hh index 54ced7f7b..44a78bada 100644 --- a/bin/common_finput.hh +++ b/bin/common_finput.hh @@ -72,7 +72,10 @@ public: process_stream(std::istream& is, const char* filename); virtual int - process_file(const char* filename); + process_ltl_file(const char* filename); + + virtual int + process_aut_file(const char* filename); virtual int run(); diff --git a/bin/common_hoaread.hh b/bin/common_hoaread.hh index b3cc912a5..e66967393 100644 --- a/bin/common_hoaread.hh +++ b/bin/common_hoaread.hh @@ -1,6 +1,6 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2015, 2017, 2018 Laboratoire de Recherche et Développement de -// l'Epita (LRDE). +// Copyright (C) 2015, 2017, 2018, 2022 Laboratoire de Recherche et +// Développement de l'Epita (LRDE). // // This file is part of Spot, a model checking library. // @@ -80,8 +80,10 @@ public: } int - process_file(const char* filename) override + process_aut_file(const char* filename) override { + col_to_read = 0; + // If we have a filename like "foo/NN" such // that: // ① foo/NN is not a file, From 04d718ab9cd8efc8c6b7953468a9eef2ec58bd2e Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Wed, 22 Jun 2022 15:20:54 +0200 Subject: [PATCH 090/337] ltlsynt: support multiple --tlsf options * bin/common_finput.cc, bin/common_finput.hh: Add support for process_tlsf_file. * bin/ltlsynt.cc: Implement it. * tests/core/syfco.test: Adjust test case. --- bin/common_finput.cc | 9 +++++ bin/common_finput.hh | 6 +++- bin/ltlsynt.cc | 81 +++++++++++++++++++++++-------------------- tests/core/syfco.test | 7 ++-- 4 files changed, 63 insertions(+), 40 deletions(-) diff --git a/bin/common_finput.cc b/bin/common_finput.cc index 8e78b5599..80aca5df7 100644 --- a/bin/common_finput.cc +++ b/bin/common_finput.cc @@ -308,6 +308,12 @@ job_processor::process_aut_file(const char*) throw std::runtime_error("process_aut_file not defined for this tool"); } +int +job_processor::process_tlsf_file(const char*) +{ + throw std::runtime_error("process_tlsf_file not defined for this tool"); +} + int job_processor::process_ltl_file(const char* filename) { @@ -377,6 +383,9 @@ job_processor::run() case job_type::AUT_FILENAME: error |= process_aut_file(j.str); break; + case job_type::TLSF_FILENAME: + error |= process_tlsf_file(j.str); + break; default: throw std::runtime_error("unexpected job type"); } diff --git a/bin/common_finput.hh b/bin/common_finput.hh index 44a78bada..2a5815fc3 100644 --- a/bin/common_finput.hh +++ b/bin/common_finput.hh @@ -27,7 +27,8 @@ enum class job_type : char { LTL_STRING, LTL_FILENAME, - AUT_FILENAME }; + AUT_FILENAME, + TLSF_FILENAME }; struct job { @@ -77,6 +78,9 @@ public: virtual int process_aut_file(const char* filename); + virtual int + process_tlsf_file(const char* filename); + virtual int run(); diff --git a/bin/ltlsynt.cc b/bin/ltlsynt.cc index 0d89c2fc5..b5d4289f5 100644 --- a/bin/ltlsynt.cc +++ b/bin/ltlsynt.cc @@ -157,8 +157,6 @@ static const char* opt_print_hoa_args = nullptr; static bool opt_real = false; static bool opt_do_verify = false; static const char* opt_print_aiger = nullptr; -static char* opt_tlsf = nullptr; -static std::string opt_tlsf_string; static spot::synthesis_info* gi; @@ -582,6 +580,18 @@ namespace return 0; } + static void + split_aps(std::string arg, std::vector& where) + { + std::istringstream aps(arg); + std::string ap; + while (std::getline(aps, ap, ',')) + { + ap.erase(remove_if(ap.begin(), ap.end(), isspace), ap.end()); + where.push_back(str_tolower(ap)); + } + } + class ltl_processor final : public job_processor { private: @@ -658,19 +668,40 @@ namespace print_csv(f); return res; } - }; -} -static void -split_aps(std::string arg, std::vector& where) -{ - std::istringstream aps(arg); - std::string ap; - while (std::getline(aps, ap, ',')) + int + process_tlsf_file(const char* filename) override { - ap.erase(remove_if(ap.begin(), ap.end(), isspace), ap.end()); - where.push_back(str_tolower(ap)); + static char arg0[] = "syfco"; + static char arg1[] = "-f"; + static char arg2[] = "ltlxba"; + static char arg3[] = "-m"; + static char arg4[] = "fully"; + char* command[] = { arg0, arg1, arg2, arg3, arg4, + const_cast(filename), nullptr }; + std::string tlsf_string = read_stdout_of_command(command); + + // The set of atomic proposition will be temporary set to those + // given by syfco, unless they were forced from the command-line. + bool reset_aps = false; + if (!input_aps_.has_value() && !output_aps_.has_value()) + { + reset_aps = true; + static char arg5[] = "--print-output-signals"; + char* command[] = { arg0, arg5, + const_cast(filename), nullptr }; + std::string res = read_stdout_of_command(command); + + output_aps_.emplace(std::vector{}); + split_aps(res, *output_aps_); + } + int res = process_string(tlsf_string, filename); + if (reset_aps) + output_aps_.reset(); + return res; } + + }; } static int @@ -726,9 +757,7 @@ parse_opt(int key, char *arg, struct argp_state *) simplify_args, simplify_values); break; case OPT_TLSF: - if (opt_tlsf) - error(2, 0, "option --tlsf may only be used once"); - opt_tlsf = arg; + jobs.emplace_back(arg, job_type::TLSF_FILENAME); break; case OPT_VERBOSE: gi->verbose_stream = &std::cerr; @@ -767,28 +796,6 @@ main(int argc, char **argv) if (int err = argp_parse(&ap, argc, argv, ARGP_NO_HELP, nullptr, nullptr)) exit(err); - if (opt_tlsf) - { - static char arg0[] = "syfco"; - static char arg1[] = "-f"; - static char arg2[] = "ltlxba"; - static char arg3[] = "-m"; - static char arg4[] = "fully"; - char* command[] = { arg0, arg1, arg2, arg3, arg4, opt_tlsf, nullptr }; - opt_tlsf_string = read_stdout_of_command(command); - jobs.emplace_back(opt_tlsf_string.c_str(), job_type::LTL_STRING); - - if (!all_input_aps.has_value() && !all_output_aps.has_value()) - { - static char arg5[] = "--print-output-signals"; - char* command[] = { arg0, arg5, opt_tlsf, nullptr }; - std::string res = read_stdout_of_command(command); - - all_output_aps.emplace(std::vector{}); - split_aps(res, *all_output_aps); - } - } - check_no_formula(); // Check if inputs and outputs are distinct diff --git a/tests/core/syfco.test b/tests/core/syfco.test index b63f729a8..7141f0b4c 100755 --- a/tests/core/syfco.test +++ b/tests/core/syfco.test @@ -44,5 +44,8 @@ test REALIZABLE = `ltlsynt --tlsf test.tlsf --realizability` test UNREALIZABLE = `ltlsynt --tlsf test.tlsf --outs=foo --realizability` test UNREALIZABLE = `ltlsynt --outs=foo --tlsf test.tlsf --realizability` -ltlsynt --tlsf test.tlsf --tlsf test.tlsf 2>stderr && exit 0 -grep 'option --tlsf may only be used once' stderr +# --tlsf can be used several time +ltlsynt --tlsf test.tlsf > out1 +ltlsynt --tlsf test.tlsf --tlsf test.tlsf > out2 +cat out1 out1 > out11 +diff out11 out2 From be28365db4a9c1290074f6f5a8a1005c70c9fc55 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Wed, 22 Jun 2022 19:31:24 +0200 Subject: [PATCH 091/337] ltlsynt: add --from-pgame option to read parity games * bin/common_file.cc, bin/common_file.hh (output_file): Add a force_append option. * bin/ltlsynt.cc: Implement the --from-pgame option, and fix suppot for --csv when multiple inputs are processed. * NEWS: Mention the new option. * tests/core/syfco.test: Add a test case. * tests/core/ltlsynt-pgame.test: New file. * tests/Makefile.am: Add it. --- NEWS | 5 +- bin/common_file.cc | 11 ++- bin/common_file.hh | 4 +- bin/ltlsynt.cc | 170 ++++++++++++++++++++++++++++++++-- tests/Makefile.am | 1 + tests/core/ltlsynt-pgame.test | 157 +++++++++++++++++++++++++++++++ tests/core/syfco.test | 4 + 7 files changed, 336 insertions(+), 16 deletions(-) create mode 100755 tests/core/ltlsynt-pgame.test diff --git a/NEWS b/NEWS index b8067d47a..307cc4a3a 100644 --- a/NEWS +++ b/NEWS @@ -29,10 +29,13 @@ New in spot 2.10.6.dev (not yet released) - autcross learned a --language-complemented option to assist in the case one is testing tools that complement automata. (issue #504). - - ltlsynt as a new option --tlsf that takes the filename of a TLSF + - ltlsynt has a new option --tlsf that takes the filename of a TLSF specification and calls syfco (which must be installed) to convert it into an LTL formula. + - ltlsynt has a new option --from-pgame that takes a parity game in + extended HOA format, as used in the Synthesis Competition. + Library: - A global variable, together with its setters and getters to define the diff --git a/bin/common_file.cc b/bin/common_file.cc index ab89fbfe4..005bb5479 100644 --- a/bin/common_file.cc +++ b/bin/common_file.cc @@ -1,6 +1,6 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2015, 2016 Laboratoire de Recherche et Développement de -// l'Epita (LRDE). +// Copyright (C) 2015, 2016, 2022 Laboratoire de Recherche et +// Développement de l'Epita (LRDE). // // This file is part of Spot, a model checking library. // @@ -22,15 +22,18 @@ #include -output_file::output_file(const char* name) +output_file::output_file(const char* name, bool force_append) { std::ios_base::openmode mode = std::ios_base::trunc; if (name[0] == '>' && name[1] == '>') { - mode = std::ios_base::app; append_ = true; name += 2; } + if (force_append) + append_ = true; + if (append_) + mode = std::ios_base::app; if (name[0] == '-' && name[1] == 0) { os_ = &std::cout; diff --git a/bin/common_file.hh b/bin/common_file.hh index fba62dec0..b8f9842b8 100644 --- a/bin/common_file.hh +++ b/bin/common_file.hh @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2015, 2016 Laboratoire de Recherche et Développement de +// Copyright (C) 2015, 2016, 2022 Laboratoire de Recherche et Développement de // l'Epita (LRDE). // // This file is part of Spot, a model checking library. @@ -33,7 +33,7 @@ public: // Open a file for output. "-" is interpreted as stdout. // Names that start with ">>" are opened for append. // The function calls error() on... error. - output_file(const char* name); + output_file(const char* name, bool force_append = false); void close(const std::string& name); diff --git a/bin/ltlsynt.cc b/bin/ltlsynt.cc index b5d4289f5..e7d6b73d1 100644 --- a/bin/ltlsynt.cc +++ b/bin/ltlsynt.cc @@ -23,6 +23,7 @@ #include "common_aoutput.hh" #include "common_finput.hh" +#include "common_hoaread.hh" #include "common_setup.hh" #include "common_sys.hh" #include "common_trans.hh" @@ -48,6 +49,7 @@ enum OPT_BYPASS, OPT_CSV, OPT_DECOMPOSE, + OPT_FROM_PGAME, OPT_INPUT, OPT_OUTPUT, OPT_PRINT, @@ -73,6 +75,9 @@ static const argp_option options[] = { "tlsf", OPT_TLSF, "FILENAME", 0, "Read a TLSF specification from FILENAME, and call syfco to " "convert it into LTL", 0 }, + { "from-pgame", OPT_FROM_PGAME, "FILENAME", 0, + "Read a parity game in Extended HOA format instead of building it.", + 0 }, /**************************************************/ { nullptr, 0, nullptr, 0, "Fine tuning:", 10 }, { "algo", OPT_ALGO, "sd|ds|ps|lar|lar.old|acd", 0, @@ -250,7 +255,7 @@ namespace }; static void - print_csv(const spot::formula& f) + print_csv(const spot::formula& f, const char* filename = nullptr) { auto& vs = gi->verbose_stream; auto& bv = gi->bv; @@ -259,7 +264,9 @@ namespace if (vs) *vs << "writing CSV to " << opt_csv << '\n'; - output_file outf(opt_csv); + static bool not_first_time = false; + output_file outf(opt_csv, not_first_time); + not_first_time = true; // force append on next print. std::ostream& out = outf.ostream(); // Do not output the header line if we append to a file. @@ -284,10 +291,15 @@ namespace out << '\n'; } std::ostringstream os; - os << f; - spot::escape_rfc4180(out << '"', os.str()); - out << "\",\"" << algo_names[(int) gi->s] - << "\"," << bv->total_time + if (filename) + os << filename; + else + os << f; + spot::escape_rfc4180(out << '"', os.str()) << "\","; + // if a filename was given, assume the game has been read directly + if (!filename) + out << '"' << algo_names[(int) gi->s] << '"'; + out << ',' << bv->total_time << ',' << bv->trans_time << ',' << bv->split_time << ',' << bv->paritize_time; @@ -319,6 +331,8 @@ namespace const std::vector& input_aps, const std::vector& output_aps) { + if (opt_csv) // reset benchmark data + gi->bv = spot::synthesis_info::bench_var(); spot::stopwatch sw; if (gi->bv) sw.start(); @@ -386,7 +400,7 @@ namespace [](const spot::twa_graph_ptr& game)->void { if (opt_print_pg) - pg_print(std::cout, game); + spot::pg_print(std::cout, game); else spot::print_hoa(std::cout, game, opt_print_hoa_args) << '\n'; } @@ -438,6 +452,8 @@ namespace safe_tot_time(); return 1; } + if (gi->bv) + gi->bv->realizable = true; // Create the (partial) strategy // only if we need it if (!opt_real) @@ -701,6 +717,141 @@ namespace return res; } + int process_pgame(spot::twa_graph_ptr arena, + const std::string& location) + { + if (opt_csv) // reset benchmark data + gi->bv = spot::synthesis_info::bench_var(); + spot::stopwatch sw_global; + spot::stopwatch sw_local; + if (gi->bv) + { + sw_global.start(); + sw_local.start(); + } + if (!arena->get_named_prop("synthesis-outputs")) + { + std::cerr << location << ": controllable-AP is not specified\n"; + return 2; + } + if (!arena->get_named_prop>("state-player")) + arena = spot::split_2step(arena, true); + // FIXME: If we do not split the game, we should check that it is + // alternating. + spot::change_parity_here(arena, + spot::parity_kind_max, + spot::parity_style_odd); + spot::colorize_parity_here(arena, true); + if (gi->bv) + { + gi->bv->split_time += sw_local.stop(); + gi->bv->nb_states_arena += arena->num_states(); + auto spptr = + arena->get_named_prop>("state-player"); + assert(spptr); + gi->bv->nb_states_arena_env += + std::count(spptr->cbegin(), spptr->cend(), false); + } + if (opt_print_pg || opt_print_hoa) + { + if (opt_print_pg) + spot::pg_print(std::cout, arena); + else + spot::print_hoa(std::cout, arena, opt_print_hoa_args) << '\n'; + return 0; + } + auto safe_tot_time = [&]() { + if (gi->bv) + gi->bv->total_time = sw_global.stop(); + }; + if (!spot::solve_game(arena, *gi)) + { + std::cout << "UNREALIZABLE" << std::endl; + safe_tot_time(); + return 1; + } + if (gi->bv) + gi->bv->realizable = true; + std::cout << "REALIZABLE" << std::endl; + if (opt_real) + { + safe_tot_time(); + return 0; + } + sw_local.start(); + spot::twa_graph_ptr mealy_like = + spot::solved_game_to_mealy(arena, *gi); + // Keep the machine split for aiger otherwise, separate it. + spot::simplify_mealy_here(mealy_like, *gi, opt_print_aiger); + + automaton_printer printer; + spot::process_timer timer_printer_dummy; + if (opt_print_aiger) + { + if (gi->bv) + sw_local.start(); + spot::aig_ptr saig = + spot::mealy_machine_to_aig(mealy_like, opt_print_aiger); + if (gi->bv) + { + gi->bv->aig_time = sw_local.stop(); + gi->bv->nb_latches = saig->num_latches(); + gi->bv->nb_gates = saig->num_gates(); + } + if (gi->verbose_stream) + { + *gi->verbose_stream << "AIG circuit was created in " + << gi->bv->aig_time + << " seconds and has " << saig->num_latches() + << " latches and " + << saig->num_gates() << " gates\n"; + } + spot::print_aiger(std::cout, saig) << '\n'; + } + else + { + printer.print(mealy_like, timer_printer_dummy); + } + safe_tot_time(); + return 0; + } + + int + process_aut_file(const char* filename) override + { + spot::automaton_stream_parser hp(filename); + int err = 0; + while (!abort_run) + { + spot::parsed_aut_ptr haut = hp.parse(spot::make_bdd_dict()); + if (!haut->aut && haut->errors.empty()) + break; + if (haut->format_errors(std::cerr)) + err = 2; + if (!haut->aut /*|| (err && abort_on_error_)*/) + { + error(2, 0, "failed to read automaton from %s", + haut->filename.c_str()); + } + else if (haut->aborted) + { + std::cerr << haut->filename << ':' << haut->loc + << ": aborted input automaton\n"; + err = std::max(err, 2); + } + else + { + std::ostringstream os; + os << haut->filename << ':' << haut->loc; + std::string loc = os.str(); + int res = process_pgame(haut->aut, loc); + if (res < 2 && opt_csv) + print_csv(nullptr, loc.c_str()); + err = std::max(err, res); + } + } + return err; + } }; } @@ -719,13 +870,14 @@ parse_opt(int key, char *arg, struct argp_state *) break; case OPT_CSV: opt_csv = arg ? arg : "-"; - if (not gi->bv) - gi->bv = spot::synthesis_info::bench_var(); break; case OPT_DECOMPOSE: opt_decompose_ltl = XARGMATCH("--decompose", arg, decompose_args, decompose_values); break; + case OPT_FROM_PGAME: + jobs.emplace_back(arg, job_type::AUT_FILENAME); + break; case OPT_INPUT: { all_input_aps.emplace(std::vector{}); diff --git a/tests/Makefile.am b/tests/Makefile.am index b4627f3e6..1a3d440c3 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -342,6 +342,7 @@ TESTS_twa = \ core/parity.test \ core/parity2.test \ core/ltlsynt.test \ + core/ltlsynt-pgame.test \ core/syfco.test \ core/rabin2parity.test \ core/twacube.test diff --git a/tests/core/ltlsynt-pgame.test b/tests/core/ltlsynt-pgame.test new file mode 100755 index 000000000..b4bada798 --- /dev/null +++ b/tests/core/ltlsynt-pgame.test @@ -0,0 +1,157 @@ +#! /bin/sh +# -*- coding: utf-8 -*- +# Copyright (C) 2022 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 . + +. ./defs || exit 1 + +set -e + +# From SYNTCOMP +cat >aut7.hoa < Xa)" +Start: 0 +acc-name: Buchi +Acceptance: 1 Inf(0) +AP: 2 "a" "b" +controllable-AP: 1 +properties: explicit-labels trans-labels +--BODY-- +State: 0 + [t] 1 + [1] 2 + [!1] 3 +State: 1 "GFa" + [0] 1 {0} + [!0] 1 +State: 2 "a & G(b <-> Xa)" {0} + [0&1] 2 + [0&!1] 3 +State: 3 "!a & G(b <-> Xa)" {0} + [!0&1] 2 + [!0&!1] 3 +--END-- +EOF + +test UNREALIZABLE = `ltlsynt --realizability --from-pgame aut7.hoa` + +grep -v controllable-AP aut7.hoa > aut7b.hoa +run 2 ltlsynt --realizability --from-pgame aut7b.hoa 2>stderr +grep 'aut7b.*controllable-AP' stderr + + +# From SYNTCOMP +cat >UnderapproxDemo2.ehoa <starve.ehoa <expect <results +diff expect results + +ltlsynt --realizability --from-pgame starve.ehoa \ + --from-pgame UnderapproxDemo2.ehoa \ + --from-pgame aut7.hoa --csv=out.csv >result || : +cat >expect <result || : +test 4 = `wc -l < out.csv` +cut -d, -f 9,10,11,12,13 right +end='"strat_num_states","strat_num_edges"' +cat >expect < out1 ltlsynt --tlsf test.tlsf --tlsf test.tlsf > out2 cat out1 out1 > out11 diff out11 out2 + +ltlsynt --tlsf test.tlsf --tlsf test.tlsf --print-game > pgame.hoa +ltlsynt --from-pgame pgame.hoa > out3 +diff out2 out3 From 288b1c79586472828d31e74dfd2de0708c0ce86a Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Wed, 22 Jun 2022 23:43:07 +0200 Subject: [PATCH 092/337] contains: generalize second argument to a twa This was triggered by a question from Pierre Ganty on the mailing list. * spot/twaalgos/contains.hh, spot/twaalgos/contains.cc (contains): Generalize second argument to const_twa_ptr instead of const_twa_graph_ptr. * NEWS: Mention this. * tests/python/ltsmin-pml.ipynb: Show that it work. * THANKS: Mention Pierre. --- NEWS | 5 ++++ THANKS | 1 + spot/twaalgos/contains.cc | 6 ++--- spot/twaalgos/contains.hh | 11 +++++--- tests/python/ltsmin-pml.ipynb | 51 ++++++++++++++++++++++++++--------- 5 files changed, 56 insertions(+), 18 deletions(-) diff --git a/NEWS b/NEWS index 307cc4a3a..a1e2361f5 100644 --- a/NEWS +++ b/NEWS @@ -108,6 +108,11 @@ New in spot 2.10.6.dev (not yet released) run with additional option to abort when the tree as an unwanted shape, or to turn the tree into a DAG. + - contains() can now take a twa as a second argument, not just a + twa_graph. This allows for instance to do contains(ltl, kripke) + to obtain a simple model checker (that returns true or false, + without counterexample). + New in spot 2.10.6 (2022-05-18) Bugs fixed: diff --git a/THANKS b/THANKS index 8a9c1b630..c53a0aafb 100644 --- a/THANKS +++ b/THANKS @@ -44,6 +44,7 @@ Ming-Hsien Tsai Nikos Gorogiannis Ondřej Lengál Paul Guénézan +Pierre Ganty Reuben Rowe Roei Nahum Rüdiger Ehlers diff --git a/spot/twaalgos/contains.cc b/spot/twaalgos/contains.cc index 6c76249d5..cf2680d01 100644 --- a/spot/twaalgos/contains.cc +++ b/spot/twaalgos/contains.cc @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2018, 2019 Laboratoire de Recherche et Développement de +// Copyright (C) 2018, 2019, 2022 Laboratoire de Recherche et Développement de // l'Epita. // // This file is part of Spot, a model checking library. @@ -34,7 +34,7 @@ namespace spot } } - bool contains(const_twa_graph_ptr left, const_twa_graph_ptr right) + bool contains(const_twa_graph_ptr left, const_twa_ptr right) { return !complement(left)->intersects(right); } @@ -44,7 +44,7 @@ namespace spot return contains(left, translate(right, left->get_dict())); } - bool contains(formula left, const_twa_graph_ptr right) + bool contains(formula left, const_twa_ptr right) { return !translate(formula::Not(left), right->get_dict())->intersects(right); } diff --git a/spot/twaalgos/contains.hh b/spot/twaalgos/contains.hh index 61c53076a..a1d64f1b1 100644 --- a/spot/twaalgos/contains.hh +++ b/spot/twaalgos/contains.hh @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2018 Laboratoire de Recherche et Développement de +// Copyright (C) 2018, 2022 Laboratoire de Recherche et Développement de // l'Epita. // // This file is part of Spot, a model checking library. @@ -38,10 +38,15 @@ namespace spot /// associated to the complement of \a left. It helps if \a left /// is a deterministic automaton or a formula (because in both cases /// complementation is easier). + /// + /// Complementation is only supported on twa_graph automata, so that + /// is the reason \a left must be a twa_graph. Right will be + /// explored on-the-fly if it is not a twa_graph. + /// /// @{ - SPOT_API bool contains(const_twa_graph_ptr left, const_twa_graph_ptr right); + SPOT_API bool contains(const_twa_graph_ptr left, const_twa_ptr right); SPOT_API bool contains(const_twa_graph_ptr left, formula right); - SPOT_API bool contains(formula left, const_twa_graph_ptr right); + SPOT_API bool contains(formula left, const_twa_ptr right); SPOT_API bool contains(formula left, formula right); /// @} diff --git a/tests/python/ltsmin-pml.ipynb b/tests/python/ltsmin-pml.ipynb index 120ab11f5..5d25b207f 100644 --- a/tests/python/ltsmin-pml.ipynb +++ b/tests/python/ltsmin-pml.ipynb @@ -40,8 +40,8 @@ "SpinS Promela Compiler - version 1.1 (3-Feb-2015)\n", "(C) University of Twente, Formal Methods and Tools group\n", "\n", - "Parsing tmprn9_nun3.pml...\n", - "Parsing tmprn9_nun3.pml done (0.1 sec)\n", + "Parsing tmpwot5yb9c.pml...\n", + "Parsing tmpwot5yb9c.pml done (0.0 sec)\n", "\n", "Optimizing graphs...\n", " StateMerging changed 0 states/transitions.\n", @@ -84,8 +84,8 @@ " Found 2 / 2 (100.0%) Commuting actions \n", "Generating guard dependency matrices done (0.0 sec)\n", "\n", - "Written C code to /home/adl/git/spot/tests/python/tmprn9_nun3.pml.spins.c\n", - "Compiled C code to PINS library tmprn9_nun3.pml.spins\n", + "Written C code to /home/adl/git/spot/tests/python/tmpwot5yb9c.pml.spins.c\n", + "Compiled C code to PINS library tmpwot5yb9c.pml.spins\n", "\n" ] } @@ -419,7 +419,7 @@ "\n" ], "text/plain": [ - " *' at 0x7fb7d8049450> >" + " *' at 0x7f7f9849ee20> >" ] }, "execution_count": 4, @@ -1120,6 +1120,33 @@ "k.show('.1K')" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Since a kripke structure is a `twa`, can be used on the right-hand side of `contains`. Here we show that every path of `k` contains a step where `P_0.a < 2`." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "data": { + "text/plain": [ + "True" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "spot.contains('F\"P_0.a < 2\"', k)" + ] + }, { "cell_type": "markdown", "metadata": {}, @@ -1132,7 +1159,7 @@ }, { "cell_type": "code", - "execution_count": 7, + "execution_count": 8, "metadata": {}, "outputs": [], "source": [ @@ -1141,7 +1168,7 @@ }, { "cell_type": "code", - "execution_count": 8, + "execution_count": 9, "metadata": {}, "outputs": [ { @@ -1173,7 +1200,7 @@ }, { "cell_type": "code", - "execution_count": 9, + "execution_count": 10, "metadata": {}, "outputs": [ { @@ -1239,7 +1266,7 @@ }, { "cell_type": "code", - "execution_count": 10, + "execution_count": 11, "metadata": {}, "outputs": [ { @@ -1251,7 +1278,7 @@ " P_0.b: int" ] }, - "execution_count": 10, + "execution_count": 11, "metadata": {}, "output_type": "execute_result" } @@ -1262,7 +1289,7 @@ }, { "cell_type": "code", - "execution_count": 11, + "execution_count": 12, "metadata": {}, "outputs": [], "source": [ @@ -1286,7 +1313,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.2" + "version": "3.10.5" } }, "nbformat": 4, From 166a26417c10e1a97f834b384c2e700b6efc5505 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Thu, 23 Jun 2022 15:52:24 +0200 Subject: [PATCH 093/337] graph: fix creation of universal edge * spot/graph/graph.hh: Use a temporary array to store the destination vector if the passed range belong to the dests_ vector. Otherwise the passed begin/end risk being invalidated when dests_ is reallocated. * NEWS: Mention the bug. --- NEWS | 7 +++++++ spot/graph/graph.hh | 19 +++++++++++++++++-- 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/NEWS b/NEWS index a1e2361f5..3b94d43ab 100644 --- a/NEWS +++ b/NEWS @@ -113,6 +113,13 @@ New in spot 2.10.6.dev (not yet released) to obtain a simple model checker (that returns true or false, without counterexample). + Bugs fixed: + + - calling twa_graph::new_univ_edge(src, begin, end, cond, acc) could + produce unexpected result if begin and end where already pointing + into the universal edge vector, since the later can be + reallocated during that process. + New in spot 2.10.6 (2022-05-18) Bugs fixed: diff --git a/spot/graph/graph.hh b/spot/graph/graph.hh index 6419fc27a..06ddf0997 100644 --- a/spot/graph/graph.hh +++ b/spot/graph/graph.hh @@ -804,8 +804,23 @@ namespace spot return *dst_begin; SPOT_ASSERT(sz > 1); unsigned d = dests_.size(); - dests_.emplace_back(sz); - dests_.insert(dests_.end(), dst_begin, dst_end); + if (!dests_.empty() + && &*dst_begin >= &dests_.front() + && &*dst_begin <= &dests_.back() + && (dests_.capacity() - dests_.size()) < (sz + 1)) + { + // If dst_begin...dst_end points into dests_ and dests_ risk + // being reallocated, we have to savea the destination + // states before we lose them. + std::vector tmp(dst_begin, dst_end); + dests_.emplace_back(sz); + dests_.insert(dests_.end(), tmp.begin(), tmp.end()); + } + else + { + dests_.emplace_back(sz); + dests_.insert(dests_.end(), dst_begin, dst_end); + } return ~d; } From b4279d3a120bf555e6ec3eab3e352438600f64e9 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Thu, 23 Jun 2022 16:19:50 +0200 Subject: [PATCH 094/337] dot: quote identifiers containing a minus * spot/twaalgos/dot.cc: Quote identifiers containing a minus. * tests/core/alternating.test: Add test case. * NEWS: Mention the bug. --- NEWS | 4 ++++ spot/twaalgos/dot.cc | 25 +++++++++++++++++++---- tests/core/alternating.test | 40 ++++++++++++++++++++++++++++++++++++- 3 files changed, 64 insertions(+), 5 deletions(-) diff --git a/NEWS b/NEWS index 3b94d43ab..a08fa6d10 100644 --- a/NEWS +++ b/NEWS @@ -120,6 +120,10 @@ New in spot 2.10.6.dev (not yet released) into the universal edge vector, since the later can be reallocated during that process. + - Printing an alternating automaton with print_dot() using 'u' to + hide true state could produce some incorrect GraphViz output if + the automaton as a true state as part of a universal group. + New in spot 2.10.6 (2022-05-18) Bugs fixed: diff --git a/spot/twaalgos/dot.cc b/spot/twaalgos/dot.cc index 66804f304..70b707edc 100644 --- a/spot/twaalgos/dot.cc +++ b/spot/twaalgos/dot.cc @@ -578,10 +578,27 @@ namespace spot return tmp_dst.str(); } - template - void print_true_state(U to, V from) const + void print_hidden_true_name(unsigned to, unsigned from) const { - os_ << " T" << to << 'T' << from << " [label=\"\", style=invis, "; + os_ << 'T' << to << 'T' << from; + } + + void print_hidden_true_name(unsigned to, const std::string& from) const + { + bool neg = from[0] == '-'; + if (neg) + os_ << '"'; + os_ << 'T' << to << 'T' << from; + if (neg) + os_ << '"'; + } + + template + void print_true_state(unsigned to, F from) const + { + os_ << " "; + print_hidden_true_name(to, from); + os_ << " [label=\"\", style=invis, "; os_ << (opt_vertical_ ? "height=0]\n" : "width=0]\n"); } @@ -606,7 +623,7 @@ namespace spot print_true_state(d, dest); os_ << " " << dest << " -> "; if (dst_is_hidden_true_state) - os_ << 'T' << d << 'T' << dest; + print_hidden_true_name(d, dest); else os_ << d; if ((style && *style) || opt_id_) diff --git a/tests/core/alternating.test b/tests/core/alternating.test index 17f012675..df4e47624 100755 --- a/tests/core/alternating.test +++ b/tests/core/alternating.test @@ -1,6 +1,6 @@ #!/bin/sh # -*- coding: utf-8 -*- -# Copyright (C) 2016-2018, 2020-2021 Laboratoire de Recherche et +# Copyright (C) 2016-2018, 2020-2022 Laboratoire de Recherche et # Développement de l'Epita (LRDE). # # This file is part of Spot, a model checking library. @@ -1009,3 +1009,41 @@ test '2_0_1_1_1_1_3_3' = "`autfilt --stats=$stats in`" autfilt --stats='%[x]U' in 2>stderr && exit2 grep '%\[x\]U' stderr + +cat >in <out.dot +# T0T-1 is not a valid name for GraphViz, it has to be quoted. +cat >exp.dot < 1 + 1 [label="1"] + 1 -> -1 [label="1\n{0}", arrowhead=onormal] + -1 [label=<>,shape=point,width=0.05,height=0.05] + "T0T-1" [label="", style=invis, width=0] + -1 -> "T0T-1" + -1 -> 1 + T0T1 [label="", style=invis, width=0] + 1 -> T0T1 [label="a"] +} +EOF +diff out.dot exp.dot From 9222e9713b5764396fb3a8d479acf263044f0f88 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Thu, 23 Jun 2022 17:19:09 +0200 Subject: [PATCH 095/337] parseaut: fix handling of [ outside HOA Fixes #509. * spot/parseaut/scanaut.ll: Reset ->str whenever a [ is read, so that we do not attempt to clear ->str while reading garbage. * NEWS: Mention the bug. * tests/core/parseaut.test: Test it. --- NEWS | 4 ++++ spot/parseaut/scanaut.ll | 5 +++++ tests/core/parseaut.test | 10 ++++++++++ 3 files changed, 19 insertions(+) diff --git a/NEWS b/NEWS index a08fa6d10..d7f3f4923 100644 --- a/NEWS +++ b/NEWS @@ -124,6 +124,10 @@ New in spot 2.10.6.dev (not yet released) hide true state could produce some incorrect GraphViz output if the automaton as a true state as part of a universal group. + - Due to an optimization introduces in 2.10 to parse HOA label more + efficiently, the automaton parser could crash when parsing random + input (not HOA) containing '[' (issue #509). + New in spot 2.10.6 (2022-05-18) Bugs fixed: diff --git a/spot/parseaut/scanaut.ll b/spot/parseaut/scanaut.ll index 711c74c64..db8ae75c6 100644 --- a/spot/parseaut/scanaut.ll +++ b/spot/parseaut/scanaut.ll @@ -454,6 +454,11 @@ identifier [[:alpha:]_][[:alnum:]_.-]* } } +"[" { + yylval->str = nullptr; + return *yytext; + } + . return *yytext; %{ diff --git a/tests/core/parseaut.test b/tests/core/parseaut.test index 6501bee02..56f2d54eb 100755 --- a/tests/core/parseaut.test +++ b/tests/core/parseaut.test @@ -2961,3 +2961,13 @@ EOF # At some point, this crashed with # input buffer overflow, can't enlarge buffer because scanner uses REJECT run 0 autfilt -q bigaut + + +# This issued to segfault, because the parser assumed a '[' token was +# always attached to a string, while that was only true in HOA mode. +cat >input < Date: Tue, 29 Sep 2020 16:38:07 +0200 Subject: [PATCH 096/337] Modifying Zielonka * spot/twaalgos/game.cc: solve_parity_game now works for any of the four parity types and partially colored graphs. Also removing unnescessary steps from Zielonka. h: Update * tests/python/game.py: Update and additional tests * tests/python/except.py: Remove outdated exception --- spot/twaalgos/game.cc | 464 ++++++++++++++++++++++------------------- tests/python/except.py | 10 - tests/python/game.py | 99 ++++++++- 3 files changed, 351 insertions(+), 222 deletions(-) diff --git a/spot/twaalgos/game.cc b/spot/twaalgos/game.cc index e1d23e381..3f041ac1c 100644 --- a/spot/twaalgos/game.cc +++ b/spot/twaalgos/game.cc @@ -30,6 +30,16 @@ namespace spot { namespace { + constexpr unsigned unseen_mark = std::numeric_limits::max(); + using par_t = int; + constexpr par_t limit_par_even = + std::numeric_limits::max() & 1 + ? std::numeric_limits::max()-3 + : std::numeric_limits::max()-2; + using strat_t = long long; + constexpr strat_t no_strat_mark = std::numeric_limits::min(); + + static const std::vector* ensure_game(const const_twa_graph_ptr& arena, const char* fnname) { @@ -48,15 +58,11 @@ namespace spot ensure_parity_game(const const_twa_graph_ptr& arena, const char* fnname) { bool max, odd; - arena->acc().is_parity(max, odd, true); - if (!(max && odd)) + bool is_par = arena->acc().is_parity(max, odd, true); + if (!is_par) throw std::runtime_error (std::string(fnname) + - ": arena must have max-odd acceptance condition"); - for (const auto& e : arena->edges()) - if (!e.acc) - throw std::runtime_error - (std::string(fnname) + ": arena must be colorized"); + ": arena must have one of the four parity acceptance conditions"); return ensure_game(arena, fnname); } @@ -71,10 +77,7 @@ namespace spot { // returns true if player p wins v // false otherwise - if (!has_winner_[v]) - return false; - - return winner_[v] == p; + return has_winner_[v] ? winner_[v] == p : false; } inline void set(unsigned v, bool p) @@ -95,40 +98,27 @@ namespace spot } }; // winner_t - // When using scc decomposition we need to track the - // changes made to the graph - struct edge_stash_t - { - edge_stash_t(unsigned num, unsigned dst, acc_cond::mark_t acc) noexcept - : e_num(num), - e_dst(dst), - e_acc(acc) - { - } - const unsigned e_num, e_dst; - const acc_cond::mark_t e_acc; - }; // edge_stash_t - // Internal structs used by parity_game // Struct to change recursive calls to stack struct work_t { - work_t(unsigned wstep_, unsigned rd_, unsigned min_par_, - unsigned max_par_) noexcept + work_t(unsigned wstep_, unsigned rd_, par_t min_par_, + par_t max_par_) noexcept : wstep(wstep_), rd(rd_), min_par(min_par_), max_par(max_par_) { } - const unsigned wstep, rd, min_par, max_par; + const unsigned wstep, rd; + const par_t min_par, max_par; }; // work_t // Collects information about an scc // Used to detect special cases struct subgame_info_t { - typedef std::set> all_parities_t; + typedef std::set> all_parities_t; subgame_info_t() noexcept { @@ -159,7 +149,7 @@ namespace spot { public: - bool solve(const twa_graph_ptr &arena) + bool solve(const twa_graph_ptr& arena) { // todo check if reordering states according to scc is worth it set_up(arena); @@ -167,11 +157,25 @@ namespace spot subgame_info_t subgame_info; for (c_scc_idx_ = 0; c_scc_idx_ < info_->scc_count(); ++c_scc_idx_) { + // Testing + // Make sure that every state that has a winner also + // belongs to a subgame + assert([&]() + { + for (unsigned i = 0; i < arena_->num_states(); ++i) + if (w_.has_winner_[i] + && (subgame_[i] == unseen_mark)) + return false; + return true; + }()); // Useless SCCs are winning for player 0. if (!info_->is_useful_scc(c_scc_idx_)) { + // This scc also gets its own subgame + ++rd_; for (unsigned v: c_states()) { + subgame_[v] = rd_; w_.set(v, false); // The strategy for player 0 is to take the first // available edge. @@ -197,27 +201,35 @@ namespace spot { // "Regular" solver max_abs_par_ = *subgame_info.all_parities.begin(); - w_stack_.emplace_back(0, 0, 0, max_abs_par_); + w_stack_.emplace_back(0, 0, + min_par_graph_, max_abs_par_); zielonka(); } } } - // All done -> restore graph, i.e. undo self-looping - restore(); - + // Every state needs a winner assert(std::all_of(w_.has_winner_.cbegin(), w_.has_winner_.cend(), [](bool b) { return b; })); - assert(std::all_of(s_.cbegin(), s_.cend(), - [](unsigned e_idx) - { return e_idx > 0; })); + // Only the states owned by the winner need a strategy + assert([&]() + { + for (unsigned v = 0; v < arena_->num_states(); ++v) + { + if (((*owner_ptr_)[v] == w_.winner(v)) + && ((s_[v] <= 0) || (s_[v] > arena_->num_edges()))) + return false; + } + return true; + }()); // Put the solution as named property region_t &w = *arena->get_or_set_named_prop("state-winner"); strategy_t &s = *arena->get_or_set_named_prop("strategy"); w.swap(w_.winner_); - s.resize(s_.size()); - std::copy(s_.begin(), s_.end(), s.begin()); + s.reserve(s_.size()); + for (auto as : s_) + s.push_back(as == no_strat_mark ? 0 : (unsigned) as); clean_up(); return w[arena->get_init_state_number()]; @@ -234,7 +246,7 @@ namespace spot return info_->states_of(c_scc_idx_); } - void set_up(const twa_graph_ptr &arena) + void set_up(const twa_graph_ptr& arena) { owner_ptr_ = ensure_parity_game(arena, "solve_parity_game()"); arena_ = arena; @@ -247,22 +259,63 @@ namespace spot w_.winner_.clear(); w_.winner_.resize(n_states, 0); s_.clear(); - s_.resize(n_states, -1); + s_.resize(n_states, no_strat_mark); // Init rd_ = 0; - max_abs_par_ = arena_->get_acceptance().used_sets().max_set() - 1; info_ = std::make_unique(arena_); - // Every edge leaving an scc needs to be "fixed" - // at some point. - // We store: number of edge fixed, original dst, original acc - change_stash_.clear(); - change_stash_.reserve(info_->scc_count() * 2); + // Create all the parities + // we want zielonka to work with any of the four parity types + // and we want it to work on partially colored arenas + // However the actually algorithm still supposes max odd. + // Therefore (and in order to avoid the manipulation of the mark + // at each step) we generate a vector directly storing the + // "equivalent" parity for each edge + bool max, odd; + arena_->acc().is_parity(max, odd, true); + max_abs_par_ = arena_->acc().all_sets().max_set()-1; + // Make it the next larger odd + par_t next_max_par = max_abs_par_ + 1; + all_edge_par_.resize(arena_->edge_vector().size(), + std::numeric_limits::max()); + + // The parities are modified much like for colorize_parity + // however if the acceptance condition is "min", we negate all + // parities to get "max" + // The algorithm works on negative or positive parities alike + //| kind/style | n | empty tr. | other tr. | result | min par + //|------------+-----+---------------+------------+--------------|--------- + //| max odd | any | set to {-1} | unchanged | max odd n | -1 + //| max even | any | set to {0} | add 1 | max odd n+1 | 0 + //| min odd | any | set to {-n} | negate | max odd 0 | -n + //| min even | any | set to {-n+1} | negate + 1 | max odd +1 | -n + 1 + min_par_graph_ = -(!max*max_abs_par_) - (max*odd); + max_par_graph_ = max*(max_abs_par_ + !odd) + !max*!odd; + + // Takes an edge and returns the "equivalent" max odd parity + auto equiv_par = [max, odd, next_max_par, inv = 2*max-1](const auto& e) + { + par_t e_par = e.acc.max_set() - 1; // -1 for empty + // If "min" and empty -> set to n + if (!max & (e_par == -1)) + e_par = next_max_par; + // Negate if min + e_par *= inv; + // even -> odd + e_par += !odd; + return e_par; + }; + + for (const auto& e : arena_->edges()) + { + unsigned e_idx = arena_->edge_number(e); + all_edge_par_[e_idx] = equiv_par(e); + } } // Checks if an scc is empty and reports the occurring parities // or special cases inline subgame_info_t - inspect_scc(unsigned max_par) + inspect_scc(par_t max_par) { subgame_info_t info; info.is_empty = true; @@ -278,7 +331,7 @@ namespace spot if (subgame_[e.dst] == unseen_mark) { info.is_empty = false; - unsigned this_par = e.acc.max_set() - 1; + par_t this_par = to_par(e); if (this_par <= max_par) { info.all_parities.insert(this_par); @@ -301,107 +354,78 @@ namespace spot return info; } - // Checks if an scc can be trivially solved, - // that is, all vertices of the scc belong to the - // attractor of a transition leaving the scc + // Computes the trivially solvable part of the scc + // That is the states that can be attracted to an + // outgoing transition inline subgame_info_t fix_scc() { - auto scc_acc = info_->acc_sets_of(c_scc_idx_); - // We will override all parities of edges leaving the scc - // Currently game is colored max odd - // So there is at least one color - bool added[] = {false, false}; - unsigned par_pair[2]; - unsigned scc_new_par = std::max(scc_acc.max_set(), 1u); - bool player_color_larger; - if (scc_new_par&1) - { - player_color_larger = false; - par_pair[1] = scc_new_par; - par_pair[0] = scc_new_par+1; - } - else - { - player_color_larger = true; - par_pair[1] = scc_new_par+1; - par_pair[0] = scc_new_par; - } - acc_cond::mark_t even_mark({par_pair[0]}); - acc_cond::mark_t odd_mark({par_pair[1]}); + // Note that the winner of the transitions + // leaving the scc are already determined + // attr(...) will only modify the + // states within the current scc + // but we have to "trick" it into + // not disregarding the transitions leaving the scc + // dummy needed to pass asserts + max_abs_par_ = limit_par_even+2; + // The attractors should define their own subgame + // but as we want to compute the attractors of the + // leaving transitions, we need to make + // sure that + // a) no transition is excluded due to its parity + // b) no transition is considered accepting/winning + // due to its parity + // Final note: Attractors cannot intersect by definition + // therefore the order in which they are computed + // is irrelevant + unsigned dummy_rd = 0; + // Attractor of outgoing transitions winning for env + attr(dummy_rd, false, limit_par_even, true, limit_par_even, false); + // Attractor of outgoing transitions winning for player + attr(dummy_rd, true, limit_par_even+1, true, limit_par_even+1, false); - // Only necessary to pass tests - max_abs_par_ = std::max(par_pair[0], par_pair[1]); + // No strategy fix need + // assert if all winning states of the current scc have a valid strategy - for (unsigned v : c_states()) - { - assert(subgame_[v] == unseen_mark); - bool owner = (*owner_ptr_)[v]; - for (auto &e : arena_->out(v)) - { - // The outgoing edges are taken finitely often - // -> disregard parity - if (info_->scc_of(e.dst) != c_scc_idx_) - { - // Edge leaving the scc - change_stash_.emplace_back(arena_->edge_number(e), - e.dst, e.acc); - if (w_.winner(e.dst)) - { - // Winning region off player -> - // odd mark if player - // else 1 (smallest loosing for env) - e.acc = owner ? odd_mark - : acc_cond::mark_t({1}); - added[1] = true; - } - else - { - // Winning region of env -> - // even mark for env, - // else 0 (smallest loosing for player) - e.acc = !owner ? even_mark - : acc_cond::mark_t({0}); - added[0] = true; - } - // Replace with self-loop - e.dst = e.src; - } - } // e - } // v + assert([&]() + { + for (unsigned v : c_states()) + { + if (!w_.has_winner_[v]) + continue; + // We only need a strategy if the winner + // of the state is also the owner + if (w_.winner(v) != (*owner_ptr_)[v]) + continue; + if (s_[v] <= 0) + { + std::cerr << "state " << v << " has a winner " + << w_.winner(v) << " and owner " + << (*owner_ptr_)[v] + << " but no strategy " + << s_[v] << '\n'; + return false; + } + const auto& e = arena_->edge_storage(s_[v]); + if (!w_.has_winner_[e.dst] + || (w_.winner(e.src) != w_.winner(e.dst))) + { + std::cerr << "state " << v << " has a winner " + << w_.winner(v) + << " but no valid strategy\n"; + return false; + } + } + return true; + }()); - // Compute the attractors of the self-loops/transitions leaving scc - // These can be directly added to the winning states - // To avoid disregarding edges in attr computation we - // need to start with the larger color - // Todo come up with a test for this - unsigned dummy_rd; - - for (bool p : {player_color_larger, - !player_color_larger}) - { - if (added[p]) - { - // Always take the larger, - // Otherwise states with an transition to a winning AND - // a loosing scc are treated incorrectly - attr(dummy_rd, p, par_pair[p], true, par_pair[p]); - } - } - - if (added[0] || added[1]) - // Fix "negative" strategy - for (unsigned v : c_states()) - if (subgame_[v] != unseen_mark) - s_[v] = std::abs(s_[v]); - - return inspect_scc(unseen_mark); + auto ins = inspect_scc(limit_par_even); + return ins; } // fix_scc inline bool - attr(unsigned &rd, bool p, unsigned max_par, - bool acc_par, unsigned min_win_par, - bool no_check=false) + attr(unsigned &rd, bool p, par_t max_par, + bool acc_par, par_t min_win_par, bool respect_sg=true) { // In fix_scc, the attr computation is // abused so we can not check ertain things @@ -418,8 +442,8 @@ namespace spot // As proposed in Oink! / PGSolver // Needs the transposed graph however - assert((no_check || !acc_par) || (acc_par && (max_par&1) == p)); - assert(!acc_par || (0 < min_win_par)); + assert((!acc_par) || (acc_par && to_player(max_par) == p)); + assert(!acc_par || (min_par_graph_ <= min_win_par)); assert((min_win_par <= max_par) && (max_par <= max_abs_par_)); bool grown = false; @@ -435,19 +459,16 @@ namespace spot do { - if (!to_add.empty()) + grown |= !to_add.empty(); + for (unsigned v : to_add) { - grown = true; - for (unsigned v : to_add) + // v is winning + w_.set(v, p); + // Mark if demanded + if (acc_par) { - // v is winning - w_.set(v, p); - // Mark if demanded - if (acc_par) - { - assert(subgame_[v] == unseen_mark); - subgame_[v] = rd; - } + assert(subgame_[v] == unseen_mark); + subgame_[v] = rd; } } to_add.clear(); @@ -455,7 +476,7 @@ namespace spot for (unsigned v : c_states()) { if ((subgame_[v] < rd) || (w_(v, p))) - // Not in subgame or winning + // Not in subgame or winning for p continue; bool is_owned = (*owner_ptr_)[v] == p; @@ -465,11 +486,12 @@ namespace spot // Optim: If given the choice, // we seek to go to the "oldest" subgame // That is the subgame with the lowest rd value - unsigned min_subgame_idx = -1u; + unsigned min_subgame_idx = unseen_mark; for (const auto &e: arena_->out(v)) { - unsigned this_par = e.acc.max_set() - 1; - if ((subgame_[e.dst] >= rd) && (this_par <= max_par)) + par_t this_par = to_par(e); + if ((!respect_sg || (subgame_[e.dst] >= rd)) + && (this_par <= max_par)) { // Check if winning if (w_(e.dst, p) @@ -477,7 +499,7 @@ namespace spot { assert(!acc_par || (this_par < min_win_par) || (acc_par && (min_win_par <= this_par) && - ((this_par&1) == p))); + (to_player(this_par) == p))); if (is_owned) { wins = true; @@ -528,7 +550,7 @@ namespace spot // We need to check if transitions that are accepted due // to their parity remain in the winning region of p inline bool - fix_strat_acc(unsigned rd, bool p, unsigned min_win_par, unsigned max_par) + fix_strat_acc(unsigned rd, bool p, par_t min_win_par, par_t max_par) { for (unsigned v : c_states()) { @@ -544,28 +566,28 @@ namespace spot const auto &e_s = arena_->edge_storage(s_[v]); // Optimization only for player if (!p && w_(e_s.dst, p)) - // If current strat is admissible -> nothing to do - // for env + // If current strat is admissible -> + // nothing to do for env continue; // This is an accepting edge that is no longer admissible // or we seek a more desirable edge (for player) - assert(min_win_par <= e_s.acc.max_set() - 1); - assert(e_s.acc.max_set() - 1 <= max_par); + assert(min_win_par <= to_par(e_s)); + assert(to_par(e_s) <= max_par); // Strategy heuristic : go to the oldest subgame - unsigned min_subgame_idx = -1u; + unsigned min_subgame_idx = unseen_mark; - s_[v] = -1; + s_[v] = no_strat_mark; for (const auto &e_fix : arena_->out(v)) { if (subgame_[e_fix.dst] >= rd) { - unsigned this_par = e_fix.acc.max_set() - 1; + par_t this_par = to_par(e_fix); // This edge must have less than max_par, // otherwise it would have already been attracted assert((this_par <= max_par) - || ((this_par&1) != (max_par&1))); + || (to_player(this_par) != (max_par&1))); // if it is accepting and leads to the winning region // -> valid fix if ((min_win_par <= this_par) @@ -579,7 +601,7 @@ namespace spot } } } - if (s_[v] == -1) + if (s_[v] == no_strat_mark) // NO fix found // This state is NOT won by p due to any accepting edges return true; // true -> grown @@ -600,7 +622,7 @@ namespace spot case (0): { assert(this_work.rd == 0); - assert(this_work.min_par == 0); + assert(this_work.min_par == min_par_graph_); unsigned rd; assert(this_work.max_par <= max_abs_par_); @@ -623,18 +645,20 @@ namespace spot // -> Priority compression // Optional, improves performance // Highest actually occurring - unsigned max_par = *subgame_info.all_parities.begin(); - unsigned min_win_par = max_par; - while ((min_win_par > 2) && - (!subgame_info.all_parities.count(min_win_par-1))) + // Attention in partially colored graphs + // the parity -1 and 0 appear + par_t max_par = *subgame_info.all_parities.begin(); + par_t min_win_par = max_par; + while ((min_win_par >= (min_par_graph_+2)) && + (!subgame_info.all_parities.count(min_win_par - 1))) min_win_par -= 2; - assert(max_par > 0); + assert(min_win_par >= min_par_graph_); + assert(max_par >= min_win_par); + assert((max_par&1) == (min_win_par&1)); assert(!subgame_info.all_parities.empty()); - assert(min_win_par > 0); // Get the player - bool p = min_win_par&1; - assert((max_par&1) == (min_win_par&1)); + bool p = to_player(min_win_par); // Attraction to highest par // This increases rd_ and passes it to rd attr(rd, p, max_par, true, min_win_par); @@ -643,17 +667,17 @@ namespace spot // Continuation w_stack_.emplace_back(1, rd, min_win_par, max_par); // Recursion - w_stack_.emplace_back(0, 0, 0, min_win_par-1); + w_stack_.emplace_back(0, 0, min_par_graph_, min_win_par - 1); // Others attracted will have higher counts in subgame break; } case (1): { unsigned rd = this_work.rd; - unsigned min_win_par = this_work.min_par; - unsigned max_par = this_work.max_par; - assert((min_win_par&1) == (max_par&1)); - bool p = min_win_par&1; + par_t min_win_par = this_work.min_par; + par_t max_par = this_work.max_par; + assert(to_player(min_win_par) == to_player(max_par)); + bool p = to_player(min_win_par); // Check if the attractor of w_[!p] is equal to w_[!p] // if so, player wins if there remain accepting transitions // for max_par (see fix_strat_acc) @@ -683,9 +707,9 @@ namespace spot // Mark as unseen subgame_[v] = unseen_mark; // Unset strat for testing - s_[v] = -1; + s_[v] = no_strat_mark; } - w_stack_.emplace_back(0, 0, 0, max_par); + w_stack_.emplace_back(0, 0, min_par_graph_, max_par); // No need to do anything else // the attractor of !p of this level is not changed } @@ -697,20 +721,6 @@ namespace spot } // while } // zielonka - // Undo change to the graph made along the way - inline void restore() - { - // "Unfix" the edges leaving the sccs - // This is called once the game has been solved - for (auto &e_stash : change_stash_) - { - auto &e = arena_->edge_storage(e_stash.e_num); - e.dst = e_stash.e_dst; - e.acc = e_stash.e_acc; - } - // Done - } - // Empty all internal variables inline void clean_up() { @@ -721,12 +731,11 @@ namespace spot s_.clear(); rd_ = 0; max_abs_par_ = 0; - change_stash_.clear(); } // Dedicated solver for special cases inline void one_par_subgame_solver(const subgame_info_t &info, - unsigned max_par) + par_t max_par) { assert(info.all_parities.size() == 1); // The entire subgame is won by the player of the only parity @@ -735,8 +744,8 @@ namespace spot // This subgame gets its own counter ++rd_; unsigned rd = rd_; - unsigned one_par = *info.all_parities.begin(); - bool winner = one_par & 1; + par_t one_par = *info.all_parities.begin(); + bool winner = to_player(one_par); assert(one_par <= max_par); for (unsigned v : c_states()) @@ -747,10 +756,10 @@ namespace spot subgame_[v] = rd; w_.set(v, winner); // Get the strategy - assert(s_[v] == -1); + assert(s_[v] == no_strat_mark); for (const auto &e : arena_->out(v)) { - unsigned this_par = e.acc.max_set() - 1; + par_t this_par = to_par(e); if ((subgame_[e.dst] >= rd) && (this_par <= max_par)) { assert(this_par == one_par); @@ -764,7 +773,18 @@ namespace spot // Done } - const unsigned unseen_mark = std::numeric_limits::max(); + template + inline par_t + to_par(const EDGE& e) + { + return all_edge_par_[arena_->edge_number(e)]; + } + + inline bool + to_player(par_t par) + { + return par & 1; + } twa_graph_ptr arena_; const std::vector *owner_ptr_; @@ -775,18 +795,22 @@ namespace spot // strategies for env and player; For synthesis only player is needed // We need a signed value here in order to "fix" the strategy // during construction - std::vector s_; + std::vector s_; // Informations about sccs andthe current scc std::unique_ptr info_; - unsigned max_abs_par_; // Max parity occurring in the current scc + par_t max_abs_par_; // Max parity occurring in the current scc + // Minimal and maximal parity occurring in the entire graph + par_t min_par_graph_, max_par_graph_; // Info on the current scc unsigned c_scc_idx_; - // Fixes made to the sccs that have to be undone - // before returning - std::vector change_stash_; // Change recursive calls to stack std::vector w_stack_; + // Directly store a vector of parities + // This vector will be created such + // that it takes care of the actual parity condition + // and after that zielonka can be called as if max odd + std::vector all_edge_par_; }; } // anonymous @@ -813,6 +837,24 @@ namespace spot void pg_print(std::ostream& os, const const_twa_graph_ptr& arena) { auto owner = ensure_parity_game(arena, "pg_print"); + // Ensure coloring + assert([&]() + { + bool max; + bool odd; + arena->acc().is_parity(max, odd, true); + return max && odd; + }() && "pg_printer needs max-odd parity"); + assert([&]() + { + for (unsigned ie = 0; ie < arena->num_edges(); ++ie) + { + const auto& es = arena->edge_storage(ie+1); + if (!es.acc) + return false; + } + return true; + }() && "Arena must be colorized"); unsigned ns = arena->num_states(); unsigned init = arena->get_init_state_number(); diff --git a/tests/python/except.py b/tests/python/except.py index 8674721c9..508ffd7f9 100644 --- a/tests/python/except.py +++ b/tests/python/except.py @@ -243,16 +243,6 @@ except RuntimeError as e: else: report_missing_exception() -try: - spot.solve_parity_game(a1) -except RuntimeError as e: - tc.assertIn( - "solve_parity_game(): arena must have max-odd acceptance condition", - str(e)) -else: - report_missing_exception() - - try: spot.formula_Star(spot.formula("a"), 10, 333) except OverflowError as e: diff --git a/tests/python/game.py b/tests/python/game.py index cea09f295..647c8d347 100644 --- a/tests/python/game.py +++ b/tests/python/game.py @@ -18,7 +18,7 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . -import spot +import spot, buddy from unittest import TestCase tc = TestCase() @@ -274,3 +274,100 @@ games = spot.split_edges(game) spot.set_state_players(games, spot.get_state_players(game)) tc.assertTrue(spot.solve_game(games, si)) +g = spot.translate("GF(a&X(a)) -> GFb") +a = buddy.bdd_ithvar(g.register_ap("a")) +b = buddy.bdd_ithvar(g.register_ap("b")) +gdpa = spot.tgba_determinize(spot.degeneralize_tba(g), + False, True, True, False) +spot.change_parity_here(gdpa, spot.parity_kind_max, spot.parity_style_odd) +gsdpa = spot.split_2step(gdpa, b, True) +spot.colorize_parity_here(gsdpa, True) +tc.assertTrue(spot.solve_parity_game(gsdpa)) +tc.assertEqual(spot.highlight_strategy(gsdpa).to_str("HOA", "1.1"), +"""HOA: v1.1 +States: 18 +Start: 0 +AP: 2 "a" "b" +acc-name: parity max odd 5 +Acceptance: 5 Fin(4) & (Inf(3) | (Fin(2) & (Inf(1) | Fin(0)))) +properties: trans-labels explicit-labels trans-acc colored complete +properties: deterministic +spot.highlight.states: 0 4 1 4 2 4 3 4 4 4 5 4 6 4 7 4 8 4 9 4 """ ++"""10 4 11 4 12 4 13 4 14 4 15 4 16 4 17 4 +spot.highlight.edges: 15 4 17 4 20 4 22 4 24 4 26 4 28 4 30 4 31 4 32 4 33 4 +spot.state-player: 0 0 0 0 0 0 0 1 1 1 1 1 1 1 1 1 1 1 +controllable-AP: 1 +--BODY-- +State: 0 +[!0] 7 {0} +[0] 8 {0} +State: 1 +[!0] 9 {3} +[0] 10 {3} +State: 2 +[!0] 11 {1} +[0] 12 {1} +State: 3 +[!0] 9 {3} +[0] 13 {4} +State: 4 +[!0] 11 {1} +[0] 14 {2} +State: 5 +[!0] 15 {3} +[0] 16 {3} +State: 6 +[!0] 15 {3} +[0] 17 {4} +State: 7 +[!1] 1 {0} +[1] 2 {0} +State: 8 +[!1] 3 {0} +[1] 4 {0} +State: 9 +[!1] 1 {3} +[1] 5 {3} +State: 10 +[!1] 3 {3} +[1] 6 {3} +State: 11 +[!1] 2 {1} +[1] 2 {3} +State: 12 +[!1] 4 {1} +[1] 4 {3} +State: 13 +[!1] 3 {4} +[1] 4 {4} +State: 14 +[!1] 4 {2} +[1] 4 {3} +State: 15 +[t] 5 {3} +State: 16 +[t] 6 {3} +State: 17 +[t] 4 {4} +--END--""" +) + +# Test the different parity conditions +gdpa = spot.tgba_determinize(spot.degeneralize_tba(g), + False, True, True, False) + +g_test = spot.change_parity(gdpa, spot.parity_kind_max, spot.parity_style_odd) +g_test_split = spot.split_2step(g_test, b, True) +sp = spot.get_state_players(g_test_split) +g_test_split_c = spot.colorize_parity(g_test_split) +spot.set_state_players(g_test_split_c, sp) +tc.assertTrue(spot.solve_parity_game(g_test_split_c)) +c_strat = spot.get_strategy(g_test_split_c) +# All versions of parity need to result in the same strategy +for kind in [spot.parity_kind_min, spot.parity_kind_max]: + for style in [spot.parity_style_even, spot.parity_style_odd]: + g_test_split1 = spot.change_parity(g_test_split, kind, style) + spot.set_state_players(g_test_split1, sp) + tc.assertTrue(spot.solve_parity_game(g_test_split1)) + c_strat1 = spot.get_strategy(g_test_split1) + tc.assertTrue(c_strat == c_strat1) From 6bc1dd0467025240e09b2d678c3d669feee899f6 Mon Sep 17 00:00:00 2001 From: Philipp Schlehuber-Caissier Date: Fri, 24 Jun 2022 14:12:50 +0200 Subject: [PATCH 097/337] Use new zielonka for synthesis Remove all now unnecessary colorize_parity and change_parity calls. * spot/twaalgos/synthesis.cc: Change here * spot/twaalgos/game.cc: Adjust pg-print * tests/core/ltlsynt.test, tests/python/_mealy.ipynb, tests/python/games.ipynb, tests/python/synthesis.ipynb, tests/python/synthesis.py: Adjust tests --- spot/twaalgos/game.cc | 97 +-- spot/twaalgos/synthesis.cc | 9 +- tests/core/ltlsynt.test | 28 +- tests/python/_mealy.ipynb | 76 +-- tests/python/games.ipynb | 454 ++++++------- tests/python/synthesis.ipynb | 1186 +++++++++++++++------------------- tests/python/synthesis.py | 14 +- 7 files changed, 848 insertions(+), 1016 deletions(-) diff --git a/spot/twaalgos/game.cc b/spot/twaalgos/game.cc index 3f041ac1c..419b33fe3 100644 --- a/spot/twaalgos/game.cc +++ b/spot/twaalgos/game.cc @@ -836,54 +836,63 @@ namespace spot void pg_print(std::ostream& os, const const_twa_graph_ptr& arena) { - auto owner = ensure_parity_game(arena, "pg_print"); - // Ensure coloring - assert([&]() - { - bool max; - bool odd; - arena->acc().is_parity(max, odd, true); - return max && odd; - }() && "pg_printer needs max-odd parity"); - assert([&]() - { - for (unsigned ie = 0; ie < arena->num_edges(); ++ie) - { - const auto& es = arena->edge_storage(ie+1); - if (!es.acc) - return false; - } - return true; - }() && "Arena must be colorized"); + ensure_parity_game(arena, "pg_print"); - unsigned ns = arena->num_states(); - unsigned init = arena->get_init_state_number(); - os << "parity " << ns - 1 << ";\n"; - std::vector seen(ns, false); - std::vector todo({init}); - while (!todo.empty()) + auto do_print = [&os](const const_twa_graph_ptr& arena) { - unsigned src = todo.back(); - todo.pop_back(); - if (seen[src]) - continue; - seen[src] = true; - os << src << ' '; - os << arena->out(src).begin()->acc.max_set() - 1 << ' '; - os << (*owner)[src] << ' '; - bool first = true; - for (auto& e: arena->out(src)) + const region_t& owner = get_state_players(arena); + + unsigned ns = arena->num_states(); + unsigned init = arena->get_init_state_number(); + os << "parity " << ns - 1 << ";\n"; + std::vector seen(ns, false); + std::vector todo({init}); + while (!todo.empty()) { - if (!first) - os << ','; - first = false; - os << e.dst; - if (!seen[e.dst]) - todo.push_back(e.dst); + unsigned src = todo.back(); + todo.pop_back(); + if (seen[src]) + continue; + seen[src] = true; + os << src << ' '; + os << arena->out(src).begin()->acc.max_set() - 1 << ' '; + os << owner[src] << ' '; + bool first = true; + for (auto& e: arena->out(src)) + { + if (!first) + os << ','; + first = false; + os << e.dst; + if (!seen[e.dst]) + todo.push_back(e.dst); + } + if (src == init) + os << " \"INIT\""; + os << ";\n"; } - if (src == init) - os << " \"INIT\""; - os << ";\n"; + }; + // Ensure coloring + // PGSolver format expects max odd and colored + bool is_par, max, odd; + is_par = arena->acc().is_parity(max, odd, true); + assert(is_par && "pg_printer needs parity condition"); + bool is_colored = (max & odd) ? std::all_of(arena->edges().begin(), + arena->edges().end(), + [](const auto& e) + { + return (bool) e.acc; + }) + : false; + if (is_colored) + do_print(arena); + else + { + auto arena2 = change_parity(arena, parity_kind_max, parity_style_odd); + colorize_parity_here(arena2, true); + set_state_players(arena2, + get_state_players(arena)); + do_print(arena2); } } diff --git a/spot/twaalgos/synthesis.cc b/spot/twaalgos/synthesis.cc index 5a5d1297a..41aa736e2 100644 --- a/spot/twaalgos/synthesis.cc +++ b/spot/twaalgos/synthesis.cc @@ -837,13 +837,10 @@ namespace spot if (force_sbacc) dpa = sbacc(dpa); reduce_parity_here(dpa, true); - change_parity_here(dpa, parity_kind_max, - parity_style_odd); assert(( [&dpa]() -> bool { bool max, odd; - dpa->acc().is_parity(max, odd); - return max && odd; + return dpa->acc().is_parity(max, odd); }())); assert(is_deterministic(dpa)); return dpa; @@ -936,7 +933,6 @@ namespace spot if (bv) sw.start(); dpa = split_2step(tmp, outs, true); - colorize_parity_here(dpa, true); if (bv) bv->split_time += sw.stop(); if (vs) @@ -959,7 +955,6 @@ namespace spot if (bv) sw.start(); dpa = split_2step(aut, outs, true); - colorize_parity_here(dpa, true); if (bv) bv->split_time += sw.stop(); if (vs) @@ -1031,8 +1026,6 @@ namespace spot if (bv) sw.start(); dpa = split_2step(dpa, outs, true); - change_parity_here(dpa, parity_kind_max, parity_style_odd); - colorize_parity_here(dpa, true); if (bv) bv->split_time += sw.stop(); if (vs) diff --git a/tests/core/ltlsynt.test b/tests/core/ltlsynt.test index 9319d96a8..6cb449012 100644 --- a/tests/core/ltlsynt.test +++ b/tests/core/ltlsynt.test @@ -232,7 +232,7 @@ LAR construction done in X seconds DPA has 4 states, 1 colors split inputs and outputs done in X seconds automaton has 12 states -solving game with acceptance: parity max odd 3 +solving game with acceptance: co-Büchi game solved in X seconds EOF ltlsynt -f "G(Fi0 && Fi1 && Fi2) -> G(i1 <-> o0)" --outs="o0" --algo=lar \ @@ -708,7 +708,7 @@ LAR construction done in X seconds DPA has 4 states, 1 colors split inputs and outputs done in X seconds automaton has 9 states -solving game with acceptance: Streett 1 +solving game with acceptance: Büchi game solved in X seconds simplification took X seconds AIG circuit was created in X seconds and has 0 latches and 0 gates @@ -727,7 +727,7 @@ LAR construction done in X seconds DPA has 2 states, 0 colors split inputs and outputs done in X seconds automaton has 4 states -solving game with acceptance: Streett 1 +solving game with acceptance: all game solved in X seconds simplification took X seconds trying to create strategy directly for (a | x) -> x @@ -738,7 +738,7 @@ LAR construction done in X seconds DPA has 2 states, 0 colors split inputs and outputs done in X seconds automaton has 4 states -solving game with acceptance: Streett 1 +solving game with acceptance: all game solved in X seconds simplification took X seconds AIG circuit was created in X seconds and has 0 latches and 0 gates @@ -797,7 +797,7 @@ LAR construction done in X seconds DPA has 2 states, 1 colors split inputs and outputs done in X seconds automaton has 5 states -solving game with acceptance: Streett 1 +solving game with acceptance: Büchi game solved in X seconds simplification took X seconds AIG circuit was created in X seconds and has 0 latches and 0 gates @@ -819,7 +819,7 @@ LAR construction done in X seconds DPA has 2 states, 0 colors split inputs and outputs done in X seconds automaton has 4 states -solving game with acceptance: Streett 1 +solving game with acceptance: all game solved in X seconds simplification took X seconds trying to create strategy directly for a -> c @@ -830,7 +830,7 @@ LAR construction done in X seconds DPA has 2 states, 0 colors split inputs and outputs done in X seconds automaton has 4 states -solving game with acceptance: Streett 1 +solving game with acceptance: all game solved in X seconds simplification took X seconds trying to create strategy directly for a -> d @@ -841,7 +841,7 @@ LAR construction done in X seconds DPA has 2 states, 0 colors split inputs and outputs done in X seconds automaton has 4 states -solving game with acceptance: Streett 1 +solving game with acceptance: all game solved in X seconds simplification took X seconds AIG circuit was created in X seconds and has 0 latches and 0 gates @@ -903,7 +903,7 @@ LAR construction done in X seconds DPA has 1 states, 0 colors split inputs and outputs done in X seconds automaton has 2 states -solving game with acceptance: Streett 1 +solving game with acceptance: all game solved in X seconds translating formula done in X seconds automaton has 2 states and 2 colors @@ -911,7 +911,7 @@ LAR construction done in X seconds DPA has 2 states, 2 colors split inputs and outputs done in X seconds automaton has 5 states -solving game with acceptance: parity max odd 4 +solving game with acceptance: Streett 1 game solved in X seconds EOF @@ -927,7 +927,7 @@ ACD construction done in X seconds DPA has 2 states, 2 colors split inputs and outputs done in X seconds automaton has 6 states -solving game with acceptance: parity max odd 4 +solving game with acceptance: generalized-Streett 1 1 game solved in X seconds simplification took X seconds translating formula done in X seconds @@ -936,7 +936,7 @@ ACD construction done in X seconds DPA has 1 states, 0 colors split inputs and outputs done in X seconds automaton has 2 states -solving game with acceptance: Streett 1 +solving game with acceptance: all game solved in X seconds simplification took X seconds EOF @@ -959,7 +959,7 @@ LAR construction done in X seconds DPA has 1 states, 1 colors split inputs and outputs done in X seconds automaton has 3 states -solving game with acceptance: Streett 1 +solving game with acceptance: Büchi game solved in X seconds EOF ltlsynt -f "G(o1) & (GFi <-> GFo1)" --outs="o1" --verbose\ @@ -977,7 +977,7 @@ LAR construction done in X seconds DPA has 2 states, 2 colors split inputs and outputs done in X seconds automaton has 6 states -solving game with acceptance: parity max odd 4 +solving game with acceptance: Streett 1 game solved in X seconds simplification took X seconds EOF diff --git a/tests/python/_mealy.ipynb b/tests/python/_mealy.ipynb index 4e7374852..c2aeb125c 100644 --- a/tests/python/_mealy.ipynb +++ b/tests/python/_mealy.ipynb @@ -65,78 +65,70 @@ "\n", "\n", - "\n", - "\n", - "\n", - "Inf(\n", - "\n", - ") | Fin(\n", - "\n", - ")\n", - "[Streett 1]\n", + "\n", + "\n", + "\n", + "t\n", + "[all]\n", "\n", "\n", "\n", "0\n", - "\n", - "0\n", + "\n", + "0\n", "\n", "\n", "\n", "I->0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "1\n", - "\n", - "1\n", + "\n", + "1\n", "\n", "\n", "\n", "0->1\n", - "\n", - "\n", - "!a & !c\n", - "\n", + "\n", + "\n", + "!a & !c\n", "\n", "\n", "\n", "2\n", - "\n", - "2\n", + "\n", + "2\n", "\n", "\n", "\n", "0->2\n", - "\n", - "\n", - "a | c\n", - "\n", + "\n", + "\n", + "a | c\n", "\n", "\n", "\n", "1->0\n", - "\n", - "\n", - "!b & !d\n", - "\n", + "\n", + "\n", + "!b & !d\n", "\n", "\n", "\n", "2->0\n", - "\n", - "\n", - "b | d\n", - "\n", + "\n", + "\n", + "b | d\n", "\n", "\n", "\n" ], "text/plain": [ - " *' at 0x7f32ec50ce40> >" + " *' at 0x7fc1244a3d50> >" ] }, "execution_count": 4, @@ -216,7 +208,7 @@ "\n" ], "text/plain": [ - " *' at 0x7f32ec571c30> >" + " *' at 0x7fc124439570> >" ] }, "execution_count": 6, @@ -290,7 +282,7 @@ "\n" ], "text/plain": [ - " *' at 0x7f32ec571c30> >" + " *' at 0x7fc124439570> >" ] }, "execution_count": 8, @@ -301,6 +293,14 @@ "source": [ "x" ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "923a59d6", + "metadata": {}, + "outputs": [], + "source": [] } ], "metadata": { @@ -319,7 +319,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.2" + "version": "3.8.10" } }, "nbformat": 4, diff --git a/tests/python/games.ipynb b/tests/python/games.ipynb index ac3490b2b..891ebcd94 100644 --- a/tests/python/games.ipynb +++ b/tests/python/games.ipynb @@ -689,246 +689,230 @@ "\n", "\n", - "\n", - "\n", - "\n", - "Fin(\n", - "\n", - ") & (Inf(\n", - "\n", - ") | Fin(\n", - "\n", - "))\n", - "[parity max odd 3]\n", + "\n", + "\n", + "\n", + "Fin(\n", + "\n", + ")\n", + "[co-Büchi]\n", "\n", "\n", "\n", "4\n", - "\n", - "4\n", + "\n", + "4\n", "\n", "\n", "\n", "I->4\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "10\n", - "\n", - "10\n", + "\n", + "10\n", "\n", "\n", "\n", "4->10\n", - "\n", - "\n", - "!a\n", - "\n", + "\n", + "\n", + "!a\n", "\n", "\n", "\n", "11\n", - "\n", - "11\n", + "\n", + "11\n", "\n", "\n", "\n", "4->11\n", - "\n", - "\n", - "a\n", - "\n", + "\n", + "\n", + "a\n", "\n", "\n", "\n", "0\n", - "\n", - "0\n", + "\n", + "0\n", "\n", "\n", "\n", "5\n", - "\n", - "5\n", + "\n", + "5\n", "\n", "\n", "\n", "0->5\n", - "\n", - "\n", - "!a\n", - "\n", + "\n", + "\n", + "!a\n", "\n", "\n", "\n", "6\n", - "\n", - "6\n", + "\n", + "6\n", "\n", "\n", "\n", "0->6\n", - "\n", - "\n", - "a\n", - "\n", + "\n", + "\n", + "a\n", + "\n", "\n", "\n", "\n", "5->0\n", - "\n", - "\n", - "1\n", - "\n", + "\n", + "\n", + "1\n", "\n", "\n", "\n", "1\n", - "\n", - "1\n", + "\n", + "1\n", "\n", "\n", "\n", "6->1\n", - "\n", - "\n", - "1\n", - "\n", + "\n", + "\n", + "1\n", + "\n", "\n", "\n", "\n", "1->6\n", - "\n", - "\n", - "a\n", - "\n", + "\n", + "\n", + "a\n", + "\n", "\n", "\n", "\n", "7\n", - "\n", - "7\n", + "\n", + "7\n", "\n", "\n", "\n", "1->7\n", - "\n", - "\n", - "!a\n", - "\n", + "\n", + "\n", + "!a\n", + "\n", "\n", "\n", "\n", "7->0\n", - "\n", - "\n", - "1\n", - "\n", + "\n", + "\n", + "1\n", + "\n", "\n", "\n", "\n", "2\n", - "\n", - "2\n", + "\n", + "2\n", "\n", "\n", "\n", "8\n", - "\n", - "8\n", + "\n", + "8\n", "\n", "\n", "\n", "2->8\n", - "\n", - "\n", - "1\n", - "\n", + "\n", + "\n", + "1\n", "\n", "\n", "\n", "8->2\n", - "\n", - "\n", - "1\n", - "\n", + "\n", + "\n", + "1\n", "\n", "\n", "\n", "3\n", - "\n", - "3\n", + "\n", + "3\n", "\n", "\n", "\n", "9\n", - "\n", - "9\n", + "\n", + "9\n", "\n", "\n", "\n", "3->9\n", - "\n", - "\n", - "1\n", - "\n", + "\n", + "\n", + "1\n", "\n", "\n", "\n", "9->2\n", - "\n", - "\n", - "!b\n", - "\n", + "\n", + "\n", + "!b\n", "\n", "\n", "\n", "9->3\n", - "\n", - "\n", - "b\n", - "\n", + "\n", + "\n", + "b\n", + "\n", "\n", "\n", "\n", "10->0\n", - "\n", - "\n", - "!b\n", - "\n", + "\n", + "\n", + "!b\n", "\n", "\n", "\n", "10->3\n", - "\n", - "\n", - "b\n", - "\n", + "\n", + "\n", + "b\n", "\n", "\n", "\n", "11->1\n", - "\n", - "\n", - "!b\n", - "\n", + "\n", + "\n", + "!b\n", "\n", "\n", "\n", "11->3\n", - "\n", - "\n", - "b\n", - "\n", + "\n", + "\n", + "b\n", "\n", "\n", "\n" ], "text/plain": [ - " *' at 0x7f202420db10> >" + " *' at 0x7f657c403180> >" ] }, "execution_count": 8, @@ -963,43 +947,43 @@ "States: 12\n", "Start: 4\n", "AP: 2 \"b\" \"a\"\n", - "acc-name: parity max odd 3\n", - "Acceptance: 3 Fin(2) & (Inf(1) | Fin(0))\n", - "properties: trans-labels explicit-labels trans-acc colored complete\n", + "acc-name: co-Buchi\n", + "Acceptance: 1 Fin(0)\n", + "properties: trans-labels explicit-labels trans-acc complete\n", "properties: deterministic\n", "spot-state-player: 0 0 0 0 0 1 1 1 1 1 1 1\n", "controllable-AP: 0\n", "--BODY--\n", "State: 0\n", - "[!1] 5 {1}\n", - "[1] 6 {2}\n", + "[!1] 5\n", + "[1] 6 {0}\n", "State: 1\n", - "[1] 6 {2}\n", - "[!1] 7 {2}\n", + "[1] 6 {0}\n", + "[!1] 7 {0}\n", "State: 2\n", - "[t] 8 {1}\n", + "[t] 8\n", "State: 3\n", - "[t] 9 {1}\n", + "[t] 9\n", "State: 4\n", - "[!1] 10 {1}\n", - "[1] 11 {1}\n", + "[!1] 10\n", + "[1] 11\n", "State: 5\n", - "[t] 0 {1}\n", + "[t] 0\n", "State: 6\n", - "[t] 1 {2}\n", + "[t] 1 {0}\n", "State: 7\n", - "[t] 0 {2}\n", + "[t] 0 {0}\n", "State: 8\n", - "[t] 2 {1}\n", + "[t] 2\n", "State: 9\n", - "[!0] 2 {1}\n", - "[0] 3 {2}\n", + "[!0] 2\n", + "[0] 3 {0}\n", "State: 10\n", - "[!0] 0 {1}\n", - "[0] 3 {1}\n", + "[!0] 0\n", + "[0] 3\n", "State: 11\n", - "[!0] 1 {1}\n", - "[0] 3 {1}\n", + "[!0] 1\n", + "[0] 3\n", "--END--\n" ] } @@ -1049,246 +1033,230 @@ "\n", "\n", - "\n", - "\n", - "\n", - "Fin(\n", - "\n", - ") & (Inf(\n", - "\n", - ") | Fin(\n", - "\n", - "))\n", - "[parity max odd 3]\n", + "\n", + "\n", + "\n", + "Fin(\n", + "\n", + ")\n", + "[co-Büchi]\n", "\n", "\n", "\n", "4\n", - "\n", - "4\n", + "\n", + "4\n", "\n", "\n", "\n", "I->4\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "10\n", - "\n", - "10\n", + "\n", + "10\n", "\n", "\n", "\n", "4->10\n", - "\n", - "\n", - "!a\n", - "\n", + "\n", + "\n", + "!a\n", "\n", "\n", "\n", "11\n", - "\n", - "11\n", + "\n", + "11\n", "\n", "\n", "\n", "4->11\n", - "\n", - "\n", - "a\n", - "\n", + "\n", + "\n", + "a\n", "\n", "\n", "\n", "0\n", - "\n", - "0\n", + "\n", + "0\n", "\n", "\n", "\n", "5\n", - "\n", - "5\n", + "\n", + "5\n", "\n", "\n", "\n", "0->5\n", - "\n", - "\n", - "!a\n", - "\n", + "\n", + "\n", + "!a\n", "\n", "\n", "\n", "6\n", - "\n", - "6\n", + "\n", + "6\n", "\n", "\n", "\n", "0->6\n", - "\n", - "\n", - "a\n", - "\n", + "\n", + "\n", + "a\n", + "\n", "\n", "\n", "\n", "5->0\n", - "\n", - "\n", - "1\n", - "\n", + "\n", + "\n", + "1\n", "\n", "\n", "\n", "1\n", - "\n", - "1\n", + "\n", + "1\n", "\n", "\n", "\n", "6->1\n", - "\n", - "\n", - "1\n", - "\n", + "\n", + "\n", + "1\n", + "\n", "\n", "\n", "\n", "1->6\n", - "\n", - "\n", - "a\n", - "\n", + "\n", + "\n", + "a\n", + "\n", "\n", "\n", "\n", "7\n", - "\n", - "7\n", + "\n", + "7\n", "\n", "\n", "\n", "1->7\n", - "\n", - "\n", - "!a\n", - "\n", + "\n", + "\n", + "!a\n", + "\n", "\n", "\n", "\n", "7->0\n", - "\n", - "\n", - "1\n", - "\n", + "\n", + "\n", + "1\n", + "\n", "\n", "\n", "\n", "2\n", - "\n", - "2\n", + "\n", + "2\n", "\n", "\n", "\n", "8\n", - "\n", - "8\n", + "\n", + "8\n", "\n", "\n", "\n", "2->8\n", - "\n", - "\n", - "1\n", - "\n", + "\n", + "\n", + "1\n", "\n", "\n", "\n", "8->2\n", - "\n", - "\n", - "1\n", - "\n", + "\n", + "\n", + "1\n", "\n", "\n", "\n", "3\n", - "\n", - "3\n", + "\n", + "3\n", "\n", "\n", "\n", "9\n", - "\n", - "9\n", + "\n", + "9\n", "\n", "\n", "\n", "3->9\n", - "\n", - "\n", - "1\n", - "\n", + "\n", + "\n", + "1\n", "\n", "\n", "\n", "9->2\n", - "\n", - "\n", - "!b\n", - "\n", + "\n", + "\n", + "!b\n", "\n", "\n", "\n", "9->3\n", - "\n", - "\n", - "b\n", - "\n", + "\n", + "\n", + "b\n", + "\n", "\n", "\n", "\n", "10->0\n", - "\n", - "\n", - "!b\n", - "\n", + "\n", + "\n", + "!b\n", "\n", "\n", "\n", "10->3\n", - "\n", - "\n", - "b\n", - "\n", + "\n", + "\n", + "b\n", "\n", "\n", "\n", "11->1\n", - "\n", - "\n", - "!b\n", - "\n", + "\n", + "\n", + "!b\n", "\n", "\n", "\n", "11->3\n", - "\n", - "\n", - "b\n", - "\n", + "\n", + "\n", + "b\n", "\n", "\n", "\n" ], "text/plain": [ - " *' at 0x7f202420df90> >" + " *' at 0x7f658c612f30> >" ] }, "execution_count": 11, diff --git a/tests/python/synthesis.ipynb b/tests/python/synthesis.ipynb index 08af437e2..3e8b4f5ea 100644 --- a/tests/python/synthesis.ipynb +++ b/tests/python/synthesis.ipynb @@ -59,644 +59,587 @@ "\n", "\n", - "\n", - "\n", - "\n", - "Fin(\n", - "\n", - ") & (Inf(\n", - "\n", - ") | Fin(\n", - "\n", - "))\n", - "[parity max odd 3]\n", + "\n", + "\n", + "\n", + "Fin(\n", + "\n", + ")\n", + "[co-Büchi]\n", "\n", "\n", "\n", "9\n", - "\n", - "9\n", + "\n", + "9\n", "\n", "\n", "\n", "I->9\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "25\n", - "\n", - "25\n", + "\n", + "25\n", "\n", "\n", "\n", "9->25\n", - "\n", - "\n", - "!i0 & !i1\n", - "\n", + "\n", + "\n", + "!i0 & !i1\n", "\n", "\n", "\n", "26\n", - "\n", - "26\n", + "\n", + "26\n", "\n", "\n", "\n", "9->26\n", - "\n", - "\n", - "i0 & !i1\n", - "\n", + "\n", + "\n", + "i0 & !i1\n", "\n", "\n", "\n", "27\n", - "\n", - "27\n", + "\n", + "27\n", "\n", "\n", "\n", "9->27\n", - "\n", - "\n", - "!i0 & i1\n", - "\n", + "\n", + "\n", + "!i0 & i1\n", "\n", "\n", "\n", "28\n", - "\n", - "28\n", + "\n", + "28\n", "\n", "\n", "\n", "9->28\n", - "\n", - "\n", - "i0 & i1\n", - "\n", + "\n", + "\n", + "i0 & i1\n", "\n", "\n", "\n", "0\n", - "\n", - "0\n", + "\n", + "0\n", "\n", "\n", "\n", "10\n", - "\n", - "10\n", + "\n", + "10\n", "\n", "\n", "\n", "0->10\n", - "\n", - "\n", - "!i1\n", - "\n", + "\n", + "\n", + "!i1\n", "\n", "\n", "\n", "11\n", - "\n", - "11\n", + "\n", + "11\n", "\n", "\n", "\n", "0->11\n", - "\n", - "\n", - "i1\n", - "\n", + "\n", + "\n", + "i1\n", "\n", "\n", "\n", "1\n", - "\n", - "1\n", + "\n", + "1\n", "\n", "\n", "\n", "10->1\n", - "\n", - "\n", - "o0\n", - "\n", + "\n", + "\n", + "o0\n", "\n", "\n", "\n", "11->0\n", - "\n", - "\n", - "o0\n", - "\n", + "\n", + "\n", + "o0\n", "\n", "\n", "\n", "12\n", - "\n", - "12\n", + "\n", + "12\n", "\n", "\n", "\n", "1->12\n", - "\n", - "\n", - "!i1\n", - "\n", + "\n", + "\n", + "!i1\n", "\n", "\n", "\n", "13\n", - "\n", - "13\n", + "\n", + "13\n", "\n", "\n", "\n", "1->13\n", - "\n", - "\n", - "i1\n", - "\n", + "\n", + "\n", + "i1\n", "\n", "\n", "\n", "12->1\n", - "\n", - "\n", - "!o0\n", - "\n", + "\n", + "\n", + "!o0\n", "\n", "\n", "\n", "13->0\n", - "\n", - "\n", - "!o0\n", - "\n", + "\n", + "\n", + "!o0\n", "\n", "\n", "\n", "2\n", - "\n", - "2\n", + "\n", + "2\n", "\n", "\n", "\n", "14\n", - "\n", - "14\n", + "\n", + "14\n", "\n", "\n", "\n", "2->14\n", - "\n", - "\n", - "i1\n", - "\n", + "\n", + "\n", + "i1\n", "\n", "\n", "\n", "16\n", - "\n", - "16\n", + "\n", + "16\n", "\n", "\n", "\n", "2->16\n", - "\n", - "\n", - "!i1\n", - "\n", + "\n", + "\n", + "!i1\n", "\n", "\n", "\n", "15\n", - "\n", - "15\n", + "\n", + "15\n", "\n", "\n", "\n", "14->15\n", - "\n", - "\n", - "1\n", - "\n", + "\n", + "\n", + "1\n", + "\n", "\n", "\n", "\n", "16->2\n", - "\n", - "\n", - "1\n", - "\n", + "\n", + "\n", + "1\n", "\n", "\n", "\n", "3\n", - "\n", - "3\n", + "\n", + "3\n", "\n", "\n", "\n", "3->13\n", - "\n", - "\n", - "i1\n", - "\n", + "\n", + "\n", + "i1\n", "\n", "\n", "\n", "17\n", - "\n", - "17\n", + "\n", + "17\n", "\n", "\n", "\n", "3->17\n", - "\n", - "\n", - "!i1\n", - "\n", + "\n", + "\n", + "!i1\n", "\n", "\n", "\n", "17->2\n", - "\n", - "\n", - "o0\n", - "\n", + "\n", + "\n", + "o0\n", "\n", "\n", "\n", "17->3\n", - "\n", - "\n", - "!o0\n", - "\n", + "\n", + "\n", + "!o0\n", "\n", "\n", "\n", "4\n", - "\n", - "4\n", + "\n", + "4\n", "\n", "\n", "\n", "4->14\n", - "\n", - "\n", - "i0\n", - "\n", + "\n", + "\n", + "i0\n", "\n", "\n", "\n", "18\n", - "\n", - "18\n", + "\n", + "18\n", "\n", "\n", "\n", "4->18\n", - "\n", - "\n", - "!i0\n", - "\n", + "\n", + "\n", + "!i0\n", "\n", "\n", "\n", "18->4\n", - "\n", - "\n", - "1\n", - "\n", + "\n", + "\n", + "1\n", "\n", "\n", "\n", "5\n", - "\n", - "5\n", + "\n", + "5\n", "\n", "\n", "\n", "5->14\n", - "\n", - "\n", - "i0 & i1\n", - "\n", + "\n", + "\n", + "i0 & i1\n", "\n", "\n", "\n", "5->16\n", - "\n", - "\n", - "i0 & !i1\n", - "\n", + "\n", + "\n", + "i0 & !i1\n", "\n", "\n", "\n", "5->18\n", - "\n", - "\n", - "!i0 & i1\n", - "\n", + "\n", + "\n", + "!i0 & i1\n", "\n", "\n", "\n", "19\n", - "\n", - "19\n", + "\n", + "19\n", "\n", "\n", "\n", "5->19\n", - "\n", - "\n", - "!i0 & !i1\n", - "\n", + "\n", + "\n", + "!i0 & !i1\n", "\n", "\n", "\n", "19->5\n", - "\n", - "\n", - "1\n", - "\n", + "\n", + "\n", + "1\n", "\n", "\n", "\n", "6\n", - "\n", - "6\n", + "\n", + "6\n", "\n", "\n", "\n", "6->10\n", - "\n", - "\n", - "i0 & !i1\n", - "\n", + "\n", + "\n", + "i0 & !i1\n", "\n", "\n", "\n", "6->11\n", - "\n", - "\n", - "i0 & i1\n", - "\n", + "\n", + "\n", + "i0 & i1\n", "\n", "\n", "\n", "20\n", - "\n", - "20\n", + "\n", + "20\n", "\n", "\n", "\n", "6->20\n", - "\n", - "\n", - "!i0 & !i1\n", - "\n", + "\n", + "\n", + "!i0 & !i1\n", "\n", "\n", "\n", "21\n", - "\n", - "21\n", + "\n", + "21\n", "\n", "\n", "\n", "6->21\n", - "\n", - "\n", - "!i0 & i1\n", - "\n", + "\n", + "\n", + "!i0 & i1\n", "\n", "\n", "\n", "20->4\n", - "\n", - "\n", - "!o0\n", - "\n", + "\n", + "\n", + "!o0\n", "\n", "\n", "\n", "7\n", - "\n", - "7\n", + "\n", + "7\n", "\n", "\n", "\n", "20->7\n", - "\n", - "\n", - "o0\n", - "\n", + "\n", + "\n", + "o0\n", "\n", "\n", "\n", "21->4\n", - "\n", - "\n", - "!o0\n", - "\n", + "\n", + "\n", + "!o0\n", "\n", "\n", "\n", "21->6\n", - "\n", - "\n", - "o0\n", - "\n", + "\n", + "\n", + "o0\n", "\n", "\n", "\n", "7->12\n", - "\n", - "\n", - "i0 & !i1\n", - "\n", + "\n", + "\n", + "i0 & !i1\n", "\n", "\n", "\n", "7->13\n", - "\n", - "\n", - "i0 & i1\n", - "\n", + "\n", + "\n", + "i0 & i1\n", "\n", "\n", "\n", "22\n", - "\n", - "22\n", + "\n", + "22\n", "\n", "\n", "\n", "7->22\n", - "\n", - "\n", - "!i0 & !i1\n", - "\n", + "\n", + "\n", + "!i0 & !i1\n", "\n", "\n", "\n", "23\n", - "\n", - "23\n", + "\n", + "23\n", "\n", "\n", "\n", "7->23\n", - "\n", - "\n", - "!i0 & i1\n", - "\n", + "\n", + "\n", + "!i0 & i1\n", "\n", "\n", "\n", "22->4\n", - "\n", - "\n", - "o0\n", - "\n", + "\n", + "\n", + "o0\n", "\n", "\n", "\n", "22->7\n", - "\n", - "\n", - "!o0\n", - "\n", + "\n", + "\n", + "!o0\n", "\n", "\n", "\n", "23->4\n", - "\n", - "\n", - "o0\n", - "\n", + "\n", + "\n", + "o0\n", "\n", "\n", "\n", "23->6\n", - "\n", - "\n", - "!o0\n", - "\n", + "\n", + "\n", + "!o0\n", "\n", "\n", "\n", "8\n", - "\n", - "8\n", + "\n", + "8\n", "\n", "\n", "\n", "8->13\n", - "\n", - "\n", - "i0 & i1\n", - "\n", + "\n", + "\n", + "i0 & i1\n", "\n", "\n", "\n", "8->17\n", - "\n", - "\n", - "i0 & !i1\n", - "\n", + "\n", + "\n", + "i0 & !i1\n", "\n", "\n", "\n", "8->23\n", - "\n", - "\n", - "!i0 & i1\n", - "\n", + "\n", + "\n", + "!i0 & i1\n", "\n", "\n", "\n", "24\n", - "\n", - "24\n", + "\n", + "24\n", "\n", "\n", "\n", "8->24\n", - "\n", - "\n", - "!i0 & !i1\n", - "\n", + "\n", + "\n", + "!i0 & !i1\n", "\n", "\n", "\n", "24->5\n", - "\n", - "\n", - "o0\n", - "\n", + "\n", + "\n", + "o0\n", "\n", "\n", "\n", "24->8\n", - "\n", - "\n", - "!o0\n", - "\n", + "\n", + "\n", + "!o0\n", "\n", "\n", "\n", "25->8\n", - "\n", - "\n", - "1\n", - "\n", + "\n", + "\n", + "1\n", "\n", "\n", "\n", "26->3\n", - "\n", - "\n", - "1\n", - "\n", + "\n", + "\n", + "1\n", "\n", "\n", "\n", "27->6\n", - "\n", - "\n", - "1\n", - "\n", + "\n", + "\n", + "1\n", "\n", "\n", "\n", "28->0\n", - "\n", - "\n", - "1\n", - "\n", + "\n", + "\n", + "1\n", "\n", "\n", "\n", "15->14\n", - "\n", - "\n", - "1\n", - "\n", + "\n", + "\n", + "1\n", + "\n", "\n", "\n", "\n" ], "text/plain": [ - " *' at 0x7f01fc12f030> >" + " *' at 0x7fc9680c32d0> >" ] }, "metadata": {}, @@ -743,583 +686,526 @@ "\n", "\n", - "\n", - "\n", - "\n", - "Fin(\n", - "\n", - ") & (Inf(\n", - "\n", - ") | Fin(\n", - "\n", - "))\n", - "[parity max odd 3]\n", + "\n", + "\n", + "\n", + "Fin(\n", + "\n", + ")\n", + "[co-Büchi]\n", "\n", "\n", "\n", "9\n", - "\n", - "9\n", + "\n", + "9\n", "\n", "\n", "\n", "I->9\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "25\n", - "\n", - "25\n", + "\n", + "25\n", "\n", "\n", "\n", "9->25\n", - "\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "26\n", - "\n", - "26\n", + "\n", + "26\n", "\n", "\n", "\n", "9->26\n", - "\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "27\n", - "\n", - "27\n", + "\n", + "27\n", "\n", "\n", "\n", "9->27\n", - "\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "28\n", - "\n", - "28\n", + "\n", + "28\n", "\n", "\n", "\n", "9->28\n", - "\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "0\n", - "\n", - "0\n", + "\n", + "0\n", "\n", "\n", "\n", "10\n", - "\n", - "10\n", + "\n", + "10\n", "\n", "\n", "\n", "0->10\n", - "\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "11\n", - "\n", - "11\n", + "\n", + "11\n", "\n", "\n", "\n", "0->11\n", - "\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "1\n", - "\n", - "1\n", + "\n", + "1\n", "\n", "\n", "\n", "10->1\n", - "\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "11->0\n", - "\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "12\n", - "\n", - "12\n", + "\n", + "12\n", "\n", "\n", "\n", "1->12\n", - "\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "13\n", - "\n", - "13\n", + "\n", + "13\n", "\n", "\n", "\n", "1->13\n", - "\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "12->1\n", - "\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "13->0\n", - "\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "2\n", - "\n", - "2\n", + "\n", + "2\n", "\n", "\n", "\n", "14\n", - "\n", - "14\n", + "\n", + "14\n", "\n", "\n", "\n", "2->14\n", - "\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "16\n", - "\n", - "16\n", + "\n", + "16\n", "\n", "\n", "\n", "2->16\n", - "\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "15\n", - "\n", - "15\n", + "\n", + "15\n", "\n", "\n", "\n", "14->15\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "16->2\n", - "\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "3\n", - "\n", - "3\n", + "\n", + "3\n", "\n", "\n", "\n", "3->13\n", - "\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "17\n", - "\n", - "17\n", + "\n", + "17\n", "\n", "\n", "\n", "3->17\n", - "\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "17->2\n", - "\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "17->3\n", - "\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "4\n", - "\n", - "4\n", + "\n", + "4\n", "\n", "\n", "\n", "4->14\n", - "\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "18\n", - "\n", - "18\n", + "\n", + "18\n", "\n", "\n", "\n", "4->18\n", - "\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "18->4\n", - "\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "5\n", - "\n", - "5\n", + "\n", + "5\n", "\n", "\n", "\n", "5->14\n", - "\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "5->16\n", - "\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "5->18\n", - "\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "19\n", - "\n", - "19\n", + "\n", + "19\n", "\n", "\n", "\n", "5->19\n", - "\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "19->5\n", - "\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "6\n", - "\n", - "6\n", + "\n", + "6\n", "\n", "\n", "\n", "6->10\n", - "\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "6->11\n", - "\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "20\n", - "\n", - "20\n", + "\n", + "20\n", "\n", "\n", "\n", "6->20\n", - "\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "21\n", - "\n", - "21\n", + "\n", + "21\n", "\n", "\n", "\n", "6->21\n", - "\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "20->4\n", - "\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "7\n", - "\n", - "7\n", + "\n", + "7\n", "\n", "\n", "\n", "20->7\n", - "\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "21->4\n", - "\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "21->6\n", - "\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "7->12\n", - "\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "7->13\n", - "\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "22\n", - "\n", - "22\n", + "\n", + "22\n", "\n", "\n", "\n", "7->22\n", - "\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "23\n", - "\n", - "23\n", + "\n", + "23\n", "\n", "\n", "\n", "7->23\n", - "\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "22->4\n", - "\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "22->7\n", - "\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "23->4\n", - "\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "23->6\n", - "\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "8\n", - "\n", - "8\n", + "\n", + "8\n", "\n", "\n", "\n", "8->13\n", - "\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "8->17\n", - "\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "8->23\n", - "\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "24\n", - "\n", - "24\n", + "\n", + "24\n", "\n", "\n", "\n", "8->24\n", - "\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "24->5\n", - "\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "24->8\n", - "\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "25->8\n", - "\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "26->3\n", - "\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "27->6\n", - "\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "28->0\n", - "\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "15->14\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n" @@ -1668,7 +1554,7 @@ "\n" ], "text/plain": [ - " *' at 0x7f01fc1b5f00> >" + " *' at 0x7fc9682230f0> >" ] }, "metadata": {}, @@ -1855,7 +1741,7 @@ "\n" ], "text/plain": [ - " *' at 0x7f01fc12fd80> >" + " *' at 0x7fc968069180> >" ] }, "metadata": {}, @@ -1992,7 +1878,7 @@ "\n" ], "text/plain": [ - " *' at 0x7f01fc1b5bd0> >" + " *' at 0x7fc968069210> >" ] }, "metadata": {}, @@ -2085,7 +1971,7 @@ "\n" ], "text/plain": [ - " *' at 0x7f01fc1b5cc0> >" + " *' at 0x7fc968069180> >" ] }, "metadata": {}, @@ -2178,7 +2064,7 @@ "\n" ], "text/plain": [ - " *' at 0x7f01fc2a8b70> >" + " *' at 0x7fc968069210> >" ] }, "metadata": {}, @@ -2315,7 +2201,7 @@ "\n" ], "text/plain": [ - " *' at 0x7f01fc1b5de0> >" + " *' at 0x7fc968069060> >" ] }, "metadata": {}, @@ -2715,7 +2601,7 @@ "\n" ], "text/plain": [ - " >" + " >" ] }, "metadata": {}, @@ -2879,56 +2765,52 @@ "\n", "\n", - "\n", - "\n", - "\n", - "Inf(\n", - "\n", - ") | Fin(\n", - "\n", - ")\n", - "[Streett 1]\n", + "\n", + "\n", + "\n", + "Inf(\n", + "\n", + ")\n", + "[Büchi]\n", "\n", "\n", "\n", "3\n", - "\n", - "3\n", + "\n", + "3\n", "\n", "\n", "\n", "I->3\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "6\n", - "\n", - "6\n", + "\n", + "6\n", "\n", "\n", "\n", "3->6\n", - "\n", - "\n", - "i0\n", - "\n", + "\n", + "\n", + "i0\n", "\n", "\n", "\n", "7\n", - "\n", - "7\n", + "\n", + "7\n", "\n", "\n", "\n", "3->7\n", - "\n", - "\n", - "!i0\n", - "\n", + "\n", + "\n", + "!i0\n", "\n", "\n", "\n", @@ -2948,7 +2830,7 @@ "\n", "\n", "1\n", - "\n", + "\n", "\n", "\n", "\n", @@ -2956,7 +2838,7 @@ "\n", "\n", "1\n", - "\n", + "\n", "\n", "\n", "\n", @@ -2976,7 +2858,7 @@ "\n", "\n", "1\n", - "\n", + "\n", "\n", "\n", "\n", @@ -2984,51 +2866,47 @@ "\n", "\n", "!o0\n", - "\n", + "\n", "\n", "\n", "\n", "2\n", - "\n", - "2\n", + "\n", + "2\n", "\n", "\n", "\n", "2->6\n", - "\n", - "\n", - "1\n", - "\n", + "\n", + "\n", + "1\n", "\n", "\n", "\n", "6->0\n", - "\n", - "\n", - "o0\n", - "\n", + "\n", + "\n", + "o0\n", "\n", "\n", "\n", "6->2\n", - "\n", - "\n", - "!o0\n", - "\n", + "\n", + "\n", + "!o0\n", "\n", "\n", "\n", "7->1\n", - "\n", - "\n", - "!o0\n", - "\n", + "\n", + "\n", + "!o0\n", "\n", "\n", "\n" ], "text/plain": [ - " *' at 0x7f01fc14bb10> >" + " *' at 0x7fc968143d80> >" ] }, "metadata": {}, @@ -3145,7 +3023,7 @@ "\n" ], "text/plain": [ - " *' at 0x7f01fc12f090> >" + " *' at 0x7fc9680c3330> >" ] }, "metadata": {}, @@ -3378,7 +3256,7 @@ "\n" ], "text/plain": [ - " >" + " >" ] }, "metadata": {}, @@ -3507,7 +3385,7 @@ "\n" ], "text/plain": [ - " >" + " >" ] }, "metadata": {}, @@ -3559,72 +3437,64 @@ "\n", "\n", - "\n", - "\n", - "\n", - "Inf(\n", - "\n", - ") | Fin(\n", - "\n", - ")\n", - "[Streett 1]\n", + "\n", + "\n", + "\n", + "t\n", + "[all]\n", "\n", "\n", "\n", "0\n", - "\n", - "0\n", + "\n", + "0\n", "\n", "\n", "\n", "I->0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "1\n", - "\n", - "1\n", + "\n", + "1\n", "\n", "\n", "\n", "0->1\n", - "\n", - "\n", - "(!i0 & !i1) | (i0 & i1)\n", - "\n", + "\n", + "\n", + "(!i0 & !i1) | (i0 & i1)\n", "\n", "\n", "\n", "2\n", - "\n", - "2\n", + "\n", + "2\n", "\n", "\n", "\n", "0->2\n", - "\n", - "\n", - "(!i0 & i1) | (i0 & !i1)\n", - "\n", + "\n", + "\n", + "(!i0 & i1) | (i0 & !i1)\n", "\n", "\n", "\n", "1->0\n", - "\n", - "\n", - "!o0\n", - "\n", + "\n", + "\n", + "!o0\n", "\n", "\n", "\n", "2->0\n", - "\n", - "\n", - "o0\n", - "\n", + "\n", + "\n", + "o0\n", "\n", "\n", "\n", @@ -3634,72 +3504,64 @@ "\n", "\n", - "\n", - "\n", - "\n", - "Inf(\n", - "\n", - ") | Fin(\n", - "\n", - ")\n", - "[Streett 1]\n", + "\n", + "\n", + "\n", + "t\n", + "[all]\n", "\n", "\n", "\n", "0\n", - "\n", - "0\n", + "\n", + "0\n", "\n", "\n", "\n", "I->0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "1\n", - "\n", - "1\n", + "\n", + "1\n", "\n", "\n", "\n", "0->1\n", - "\n", - "\n", - "(!i0 & !i1) | (i0 & i1)\n", - "\n", + "\n", + "\n", + "(!i0 & !i1) | (i0 & i1)\n", "\n", "\n", "\n", "2\n", - "\n", - "2\n", + "\n", + "2\n", "\n", "\n", "\n", "0->2\n", - "\n", - "\n", - "(!i0 & i1) | (i0 & !i1)\n", - "\n", + "\n", + "\n", + "(!i0 & i1) | (i0 & !i1)\n", "\n", "\n", "\n", "1->0\n", - "\n", - "\n", - "o1\n", - "\n", + "\n", + "\n", + "o1\n", "\n", "\n", "\n", "2->0\n", - "\n", - "\n", - "!o1\n", - "\n", + "\n", + "\n", + "!o1\n", "\n", "\n", "\n", @@ -3939,7 +3801,7 @@ "\n" ], "text/plain": [ - " >" + " >" ] }, "metadata": {}, @@ -4004,7 +3866,7 @@ "\n" ], "text/plain": [ - " *' at 0x7f01fc14bae0> >" + " *' at 0x7fc968143bd0> >" ] }, "metadata": {}, @@ -4117,7 +3979,7 @@ "\n" ], "text/plain": [ - " >" + " >" ] }, "metadata": {}, @@ -4297,7 +4159,7 @@ "\n" ], "text/plain": [ - " >" + " >" ] }, "metadata": {}, @@ -4421,7 +4283,7 @@ "\n" ], "text/plain": [ - " *' at 0x7f01fc1b5f90> >" + " *' at 0x7fc968069ed0> >" ] }, "execution_count": 16, diff --git a/tests/python/synthesis.py b/tests/python/synthesis.py index 559dc2d24..98ac889d8 100644 --- a/tests/python/synthesis.py +++ b/tests/python/synthesis.py @@ -35,18 +35,18 @@ tc.assertEqual(game.to_str(), """HOA: v1 States: 3 Start: 0 AP: 1 "a" -acc-name: Streett 1 -Acceptance: 2 Fin(0) | Inf(1) -properties: trans-labels explicit-labels trans-acc colored complete +acc-name: Buchi +Acceptance: 1 Inf(0) +properties: trans-labels explicit-labels trans-acc complete properties: deterministic spot-state-player: 0 1 1 controllable-AP: --BODY-- State: 0 -[!0] 1 {0} -[0] 2 {1} +[!0] 1 +[0] 2 {0} State: 1 -[t] 0 {0} +[t] 0 State: 2 -[t] 0 {1} +[t] 0 {0} --END--""") From ddbdcd39cb4f721c75c67004fe46265aec3e17fd Mon Sep 17 00:00:00 2001 From: Philipp Schlehuber-Caissier Date: Wed, 29 Jun 2022 01:21:00 +0200 Subject: [PATCH 098/337] Adept ltlsynt pgame to new solver * bin/ltlsynt.cc: Remove change/colorize_parity, check alternating --- bin/ltlsynt.cc | 42 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 36 insertions(+), 6 deletions(-) diff --git a/bin/ltlsynt.cc b/bin/ltlsynt.cc index e7d6b73d1..bcd9d41d9 100644 --- a/bin/ltlsynt.cc +++ b/bin/ltlsynt.cc @@ -736,12 +736,42 @@ namespace } if (!arena->get_named_prop>("state-player")) arena = spot::split_2step(arena, true); - // FIXME: If we do not split the game, we should check that it is - // alternating. - spot::change_parity_here(arena, - spot::parity_kind_max, - spot::parity_style_odd); - spot::colorize_parity_here(arena, true); + else + { + // Check if the game is alternating and fix trivial cases + const unsigned N = arena->num_states(); + // Can not use get_state_players because we need a non-const version + auto spptr = + arena->get_named_prop>("state-player"); + assert(spptr); + const bdd& outs = get_synthesis_outputs(arena); + for (unsigned n = 0; n < N; ++n) + { + const bool p = (*spptr)[n]; + for (auto& e : arena->out(n)) + { + if (p != (*spptr)[e.dst]) + continue; // All good + // Check if the condition is a simply conjunction of input and + // output. If so insert an intermediate state + // This also covers trivial self-loops + bdd cond = e.cond; + bdd i_cond = bdd_exist(cond, outs); + bdd o_cond = bdd_existcomp(cond, outs); + if ((i_cond & o_cond) == cond) + { + unsigned inter = arena->new_state(); + spptr->push_back(!p); + e.cond = p ? o_cond : i_cond; + e.dst = inter; + arena->new_edge(inter, e.dst, !p ? o_cond : i_cond); + } + else + throw std::runtime_error("ltlsynt: given parity game is not" + "alternating and not trivially fixable!"); + } + } + } if (gi->bv) { gi->bv->split_time += sw_local.stop(); From 99bf152673592223114d308d426d86c248d274f3 Mon Sep 17 00:00:00 2001 From: Philipp Schlehuber-Caissier Date: Wed, 29 Jun 2022 14:02:25 +0200 Subject: [PATCH 099/337] propagate_marks_here can break state-acc prop * spot/twaalgos/degen.cc: Fix * tests/python/pdegen.py: Test --- spot/twaalgos/degen.cc | 4 ++++ tests/python/pdegen.py | 41 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 45 insertions(+) diff --git a/spot/twaalgos/degen.cc b/spot/twaalgos/degen.cc index 11092ddac..0c07ccfa8 100644 --- a/spot/twaalgos/degen.cc +++ b/spot/twaalgos/degen.cc @@ -1168,5 +1168,9 @@ namespace spot unsigned idx = aut->edge_number(e); e.acc = marks[idx]; } + // If aut was state-based acc before, this might no longer + // this might no longer be the case + if (aut->prop_state_acc() == 1) + aut->prop_state_acc(0); } } diff --git a/tests/python/pdegen.py b/tests/python/pdegen.py index 12bc9e39a..7df9f0878 100644 --- a/tests/python/pdegen.py +++ b/tests/python/pdegen.py @@ -442,3 +442,44 @@ si = spot.scc_info(aut15) aut15b = si.split_on_sets(2, [])[0]; d aut15c = spot.partial_degeneralize(aut15b) tc.assertTrue(aut15c.equivalent_to(aut15b)) + + +# Testing property propagation/update +# for propagate_marks_here + +s = """HOA: v1 +States: 3 +Start: 0 +AP: 1 "a" +acc-name: Buchi +Acceptance: 1 Inf(0) +properties: trans-labels explicit-labels state-acc +--BODY-- +State: 0 +[0] 1 +[!0] 2 +State: 1 {0} +[0] 0 +State: 2 +[!0] 0 +--END--""" +aut = spot.automaton(s) +spot.propagate_marks_here(aut) +s2 = aut.to_str("hoa") + +tc.assertEqual(s2, """HOA: v1 +States: 3 +Start: 0 +AP: 1 "a" +acc-name: Buchi +Acceptance: 1 Inf(0) +properties: trans-labels explicit-labels trans-acc deterministic +--BODY-- +State: 0 +[0] 1 {0} +[!0] 2 +State: 1 +[0] 0 {0} +State: 2 +[!0] 0 +--END--""") \ No newline at end of file From db725ffaf81fd90f44b1455d517a7d65a438dc84 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Thu, 30 Jun 2022 09:18:03 +0200 Subject: [PATCH 100/337] * spot/twaalgos/degen.cc (propagate_marks_here): Cleanup previous patch. --- spot/twaalgos/degen.cc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/spot/twaalgos/degen.cc b/spot/twaalgos/degen.cc index 0c07ccfa8..333efe6e6 100644 --- a/spot/twaalgos/degen.cc +++ b/spot/twaalgos/degen.cc @@ -1,6 +1,6 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2012-2020 Laboratoire de Recherche -// et Développement de l'Epita (LRDE). +// Copyright (C) 2012-2020, 2022 Laboratoire de Recherche et +// Développement de l'Epita (LRDE). // // This file is part of Spot, a model checking library. // @@ -1170,7 +1170,7 @@ namespace spot } // If aut was state-based acc before, this might no longer // this might no longer be the case - if (aut->prop_state_acc() == 1) - aut->prop_state_acc(0); + if (aut->prop_state_acc().is_true()) + aut->prop_state_acc(false); } } From 1fc94ee6f2c0097086557416a8cad75d638285cc Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Thu, 30 Jun 2022 09:36:59 +0200 Subject: [PATCH 101/337] gnulib: install the environ module This should fix compilation on OSX, as reported by Yann Thierry-Mieg. * m4/environ.m4: New file. * m4/gnulib-cache.m4, m4/gnulib-comp.m4: Update. * bin/common_trans.cc [HAVE_SPAWN_H]: Do not define environ. --- bin/common_trans.cc | 2 -- m4/environ.m4 | 46 +++++++++++++++++++++++++++++++++++++++++++++ m4/gnulib-cache.m4 | 3 ++- m4/gnulib-comp.m4 | 4 +++- 4 files changed, 51 insertions(+), 4 deletions(-) create mode 100644 m4/environ.m4 diff --git a/bin/common_trans.cc b/bin/common_trans.cc index 9ab719a5b..e34f3d77d 100644 --- a/bin/common_trans.cc +++ b/bin/common_trans.cc @@ -840,8 +840,6 @@ exec_command(const char* cmd) SPOT_UNREACHABLE(); return; } -#else -extern char **environ; #endif int diff --git a/m4/environ.m4 b/m4/environ.m4 new file mode 100644 index 000000000..ae5329108 --- /dev/null +++ b/m4/environ.m4 @@ -0,0 +1,46 @@ +# environ.m4 serial 8 +dnl Copyright (C) 2001-2004, 2006-2021 Free Software Foundation, Inc. +dnl This file is free software; the Free Software Foundation +dnl gives unlimited permission to copy and/or distribute it, +dnl with or without modifications, as long as this notice is preserved. + +AC_DEFUN_ONCE([gl_ENVIRON], +[ + AC_REQUIRE([gl_UNISTD_H_DEFAULTS]) + dnl Persuade glibc to declare environ. + AC_REQUIRE([gl_USE_SYSTEM_EXTENSIONS]) + + AC_CHECK_HEADERS_ONCE([unistd.h]) + gt_CHECK_VAR_DECL( + [#if HAVE_UNISTD_H + #include + #endif + /* mingw, BeOS, Haiku declare environ in , not in . */ + #include + ], + [environ]) + if test $gt_cv_var_environ_declaration != yes; then + HAVE_DECL_ENVIRON=0 + fi +]) + +# Check if a variable is properly declared. +# gt_CHECK_VAR_DECL(includes,variable) +AC_DEFUN([gt_CHECK_VAR_DECL], +[ + define([gt_cv_var], [gt_cv_var_]$2[_declaration]) + AC_CACHE_CHECK([if $2 is properly declared], [gt_cv_var], + [AC_COMPILE_IFELSE( + [AC_LANG_PROGRAM( + [[$1 + typedef struct { int foo; } foo_t; + extern foo_t $2;]], + [[$2.foo = 1;]])], + [gt_cv_var=no], + [gt_cv_var=yes])]) + if test $gt_cv_var = yes; then + AC_DEFINE([HAVE_]m4_translit($2, [a-z], [A-Z])[_DECL], 1, + [Define if you have the declaration of $2.]) + fi + undefine([gt_cv_var]) +]) diff --git a/m4/gnulib-cache.m4 b/m4/gnulib-cache.m4 index ad3802b82..e7f448f36 100644 --- a/m4/gnulib-cache.m4 +++ b/m4/gnulib-cache.m4 @@ -1,4 +1,4 @@ -# Copyright (C) 2002-2020 Free Software Foundation, Inc. +# Copyright (C) 2002-2020, 2022 Free Software Foundation, Inc. # # This file is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -57,6 +57,7 @@ gl_MODULES([ argmatch argp closeout + environ error isatty mkstemp diff --git a/m4/gnulib-comp.m4 b/m4/gnulib-comp.m4 index 54215ad69..66d18ca01 100644 --- a/m4/gnulib-comp.m4 +++ b/m4/gnulib-comp.m4 @@ -1,5 +1,5 @@ # DO NOT EDIT! GENERATED AUTOMATICALLY! -# Copyright (C) 2002-2020 Free Software Foundation, Inc. +# Copyright (C) 2002-2020, 2022 Free Software Foundation, Inc. # # This file is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -246,6 +246,8 @@ AC_SUBST([LTALLOCA]) AC_LIBOBJ([lstat]) gl_PREREQ_LSTAT fi + gl_ENVIRON + gl_UNISTD_MODULE_INDICATOR([environ]) gl_SYS_STAT_MODULE_INDICATOR([lstat]) gl_FUNC_MALLOC_GNU if test $REPLACE_MALLOC = 1; then From 833fcdebc16551e515c3536440a2d81e787b604b Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Tue, 5 Jul 2022 10:38:51 +0200 Subject: [PATCH 102/337] work around GCC bug 106159 * m4/gccwarn.m4: Add an example of multiple inheritance of virtual classes to trigger to new -Woverloaded-virtual warning on the destructor. --- m4/gccwarn.m4 | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/m4/gccwarn.m4 b/m4/gccwarn.m4 index dc6969add..13f770ccc 100644 --- a/m4/gccwarn.m4 +++ b/m4/gccwarn.m4 @@ -22,6 +22,13 @@ AC_DEFUN([CF_GXX_WARNINGS], #line __oline__ "configure" #include #include + +// From GCC bug 106159 +// https://gcc.gnu.org/bugzilla/show_bug.cgi?id=106159 +struct left { virtual ~left() {} }; +struct right { virtual ~right() {} }; +struct both: public left, public right {}; + int main(int argc, char *argv[[]]) { // This string comparison is here to detect superfluous From efee1c4130f8d9b93a2540d69053b30614c7092d Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Tue, 5 Jul 2022 10:41:24 +0200 Subject: [PATCH 103/337] * spot/twaalgos/mealy_machine.cc (is_complete_): Define in debug only. --- spot/twaalgos/mealy_machine.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/spot/twaalgos/mealy_machine.cc b/spot/twaalgos/mealy_machine.cc index f985da506..6bbb9c4f7 100644 --- a/spot/twaalgos/mealy_machine.cc +++ b/spot/twaalgos/mealy_machine.cc @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2021 Laboratoire de Recherche et Développement +// Copyright (C) 2021, 2022 Laboratoire de Recherche et Développement // de l'Epita (LRDE). // // This file is part of Spot, a model checking library. @@ -67,6 +67,7 @@ namespace return true; } +#ifndef NDEBUG bool is_complete_(const const_twa_graph_ptr& m, const bdd& outs) { @@ -84,6 +85,7 @@ namespace } return true; } +#endif } From ff896013068e31aaa55761717b05d5525df118d1 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Tue, 5 Jul 2022 10:56:57 +0200 Subject: [PATCH 104/337] utf8: Update to match current version * utf8/LICENSE, utf8/utf8/cpp11.h, utf8/utf8/cpp17.h: New files. * Makefile.am: Distribute them. * utf8/README.md, utf8/utf8/checked.h, utf8/utf8/core.h, utf8/utf8/unchecked.h: Update to the current version of utfcpp. * README: Add a link to the upstream github. --- Makefile.am | 7 +- README | 4 +- utf8/LICENSE | 23 ++ utf8/README.md | 681 +++++++++++++++++++++++++++++++++--------- utf8/utf8/checked.h | 50 ++-- utf8/utf8/core.h | 42 +-- utf8/utf8/cpp11.h | 103 +++++++ utf8/utf8/cpp17.h | 103 +++++++ utf8/utf8/unchecked.h | 78 ++++- 9 files changed, 897 insertions(+), 194 deletions(-) create mode 100644 utf8/LICENSE create mode 100644 utf8/utf8/cpp11.h create mode 100644 utf8/utf8/cpp17.h diff --git a/Makefile.am b/Makefile.am index a0dc9a316..db7a60d9b 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,5 +1,5 @@ ## -*- coding: utf-8 -*- -## Copyright (C) 2011-2017, 2020 Laboratoire de Recherche et Développement +## Copyright (C) 2011-2017, 2020, 2022 Laboratoire de Recherche et Développement ## de l'Epita (LRDE). ## Copyright (C) 2003, 2005 Laboratoire d'Informatique de Paris 6 (LIP6), ## département Systèmes Répartis Coopératifs (SRC), Université Pierre @@ -36,8 +36,9 @@ DOC_SUBDIR = doc SUBDIRS = picosat buddy lib ltdl spot bin tests $(PYTHON_SUBDIR) $(DOC_SUBDIR) \ $(NEVER_SUBDIRS) -UTF8 = utf8/README.md utf8/utf8.h \ - utf8/utf8/checked.h utf8/utf8/core.h utf8/utf8/unchecked.h +UTF8 = utf8/README.md utf8/LICENSE utf8/utf8.h \ + utf8/utf8/checked.h utf8/utf8/core.h utf8/utf8/unchecked.h \ + utf8/utf8/cpp11.h utf8/utf8/cpp17.h DEBIAN = \ debian/changelog \ diff --git a/README b/README index 7581df4ad..a0b7c9579 100644 --- a/README +++ b/README @@ -325,13 +325,13 @@ bench/ Benchmarks for ... wdba/ ... WDBA minimization (for obligation properties). python/ Python bindings for Spot and BuDDy -Third party software +Third-party software -------------------- buddy/ A customized version of BuDDy 2.3 (a BDD library). ltdl/ Libtool's portable dlopen() wrapper library. lib/ Gnulib's portability modules. -utf8/ Nemanja Trifunovic's utf-8 routines. +utf8/ Trifunovic's utf-8 routines. https://github.com/nemtrif/utfcpp elisp/ Related emacs modes, used for building the documentation. picosat/ A distribution of PicoSAT 965 (a satsolver library). spot/bricks/ A collection of useful C++ code provided by DiVinE diff --git a/utf8/LICENSE b/utf8/LICENSE new file mode 100644 index 000000000..36b7cd93c --- /dev/null +++ b/utf8/LICENSE @@ -0,0 +1,23 @@ +Boost Software License - Version 1.0 - August 17th, 2003 + +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. diff --git a/utf8/README.md b/utf8/README.md index d4369e85d..a519cdb96 100644 --- a/utf8/README.md +++ b/utf8/README.md @@ -3,9 +3,9 @@ ## Introduction -Many C++ developers miss an easy and portable way of handling Unicode encoded strings. The original C++ Standard (known as C++98 or C++03) is Unicode agnostic. C++11 provides some support for Unicode on core language and library level: u8, u, and U character and string literals, char16_t and char32_t character types, u16string and u32string library classes, and codecvt support for conversions between Unicode encoding forms. In the meantime, developers use third party libraries like ICU, OS specific capabilities, or simply roll out their own solutions. +C++ developers miss an easy and portable way of handling Unicode encoded strings. The original C++ Standard (known as C++98 or C++03) is Unicode agnostic. C++11 provides some support for Unicode on core language and library level: u8, u, and U character and string literals, char16_t and char32_t character types, u16string and u32string library classes, and codecvt support for conversions between Unicode encoding forms. In the meantime, developers use third party libraries like ICU, OS specific capabilities, or simply roll out their own solutions. -In order to easily handle UTF-8 encoded Unicode strings, I came up with a small, C++98 compatible generic library. For anybody used to work with STL algorithms and iterators, it should be easy and natural to use. The code is freely available for any purpose - check out the license at the beginning of the utf8.h file. The library has been used a lot in the past ten years both in commercial and open-source projects and is considered feature-complete now. If you run into bugs or performance issues, please let me know and I'll do my best to address them. +In order to easily handle UTF-8 encoded Unicode strings, I came up with a small, C++98 compatible generic library. For anybody used to work with STL algorithms and iterators, it should be easy and natural to use. The code is freely available for any purpose - check out the [license](./LICENSE). The library has been used a lot in the past ten years both in commercial and open-source projects and is considered feature-complete now. If you run into bugs or performance issues, please let me know and I'll do my best to address them. The purpose of this article is not to offer an introduction to Unicode in general, and UTF-8 in particular. If you are not familiar with Unicode, be sure to check out [Unicode Home Page](http://www.unicode.org/) or some other source of information for Unicode. Also, it is not my aim to advocate the use of UTF-8 encoded strings in C++ programs; if you want to handle UTF-8 encoded strings from C++, I am sure you have good reasons for it. @@ -28,50 +28,78 @@ int main(int argc, char** argv) cout << "\nUsage: docsample filename\n"; return 0; } - const char* test_file_path = argv[1]; - // Open the test file (contains UTF-8 encoded text) + // Open the test file (must be UTF-8 encoded) ifstream fs8(test_file_path); if (!fs8.is_open()) { - cout << "Could not open " << test_file_path << endl; - return 0; + cout << "Could not open " << test_file_path << endl; + return 0; } unsigned line_count = 1; string line; // Play with all the lines in the file while (getline(fs8, line)) { - // check for invalid utf-8 (for a simple yes/no check, there is also utf8::is_valid function) + // check for invalid utf-8 (for a simple yes/no check, there is also utf8::is_valid function) +#if __cplusplus >= 201103L // C++ 11 or later + auto end_it = utf8::find_invalid(line.begin(), line.end()); +#else string::iterator end_it = utf8::find_invalid(line.begin(), line.end()); +#endif // C++ 11 if (end_it != line.end()) { cout << "Invalid UTF-8 encoding detected at line " << line_count << "\n"; cout << "This part is fine: " << string(line.begin(), end_it) << "\n"; } - // Get the line length (at least for the valid part) int length = utf8::distance(line.begin(), end_it); cout << "Length of line " << line_count << " is " << length << "\n"; // Convert it to utf-16 +#if __cplusplus >= 201103L // C++ 11 or later + u16string utf16line = utf8::utf8to16(line); +#else vector utf16line; utf8::utf8to16(line.begin(), end_it, back_inserter(utf16line)); - - // And back to utf-8 +#endif // C++ 11 + // And back to utf-8; +#if __cplusplus >= 201103L // C++ 11 or later + string utf8line = utf8::utf16to8(utf16line); +#else string utf8line; utf8::utf16to8(utf16line.begin(), utf16line.end(), back_inserter(utf8line)); - +#endif // C++ 11 // Confirm that the conversion went OK: if (utf8line != string(line.begin(), end_it)) cout << "Error in UTF-16 conversion at line: " << line_count << "\n"; line_count++; - } + } + return 0; } ``` In the previous code sample, for each line we performed a detection of invalid UTF-8 sequences with `find_invalid`; the number of characters (more precisely - the number of Unicode code points, including the end of line and even BOM if there is one) in each line was determined with a use of `utf8::distance`; finally, we have converted each line to UTF-16 encoding with `utf8to16` and back to UTF-8 with `utf16to8`. +Note a different pattern of usage for old compilers. For instance, this is how we convert +a UTF-8 encoded string to a UTF-16 encoded one with a pre - C++11 compiler: +```cpp + vector utf16line; + utf8::utf8to16(line.begin(), end_it, back_inserter(utf16line)); +``` + +With a more modern compiler, the same operation would look like: +```cpp + u16string utf16line = utf8::utf8to16(line); +``` +If `__cplusplus` macro points to a C++ 11 or later, the library exposes API that takes into +account C++ standard Unicode strings and move semantics. With an older compiler, it is still +possible to use the same functionality, just in a little less convenient way + +In case you do not trust the `__cplusplus` macro or, for instance, do not want to include +the C++ 11 helper functions even with a modern compiler, define `UTF_CPP_CPLUSPLUS` macro +before including `utf8.h` and assign it a value for the standard you want to use - the values are the same as for the `__cplusplus` macro. This can be also useful with compilers that are conservative in setting the `__cplusplus` macro even if they have a good support for a recent standard edition - Microsoft's Visual C++ is one example. + ### Checking if a file contains valid UTF-8 text Here is a function that checks whether the content of a file is valid UTF-8 encoded text without reading the content into the memory: @@ -90,7 +118,7 @@ bool valid_utf8_file(const char* file_name) } ``` -Because the function `utf8::is_valid()` works with input iterators, we were able to pass an `istreambuf_iterator` to it and read the content of the file directly without loading it to the memory first. +Because the function `utf8::is_valid()` works with input iterators, we were able to pass an `istreambuf_iterator` to `it` and read the content of the file directly without loading it to the memory first. Note that other functions that take input iterator arguments can be used in a similar way. For instance, to read the content of a UTF-8 encoded text file and convert the text to UTF-16, just do something like: @@ -113,10 +141,56 @@ void fix_utf8_string(std::string& str) The function will replace any invalid UTF-8 sequence with a Unicode replacement character. There is an overloaded function that enables the caller to supply their own replacement character. + +## Points of interest + +#### Design goals and decisions + +The library was designed to be: + +1. Generic: for better or worse, there are many C++ string classes out there, and the library should work with as many of them as possible. +2. Portable: the library should be portable both accross different platforms and compilers. The only non-portable code is a small section that declares unsigned integers of different sizes: three typedefs. They can be changed by the users of the library if they don't match their platform. The default setting should work for Windows (both 32 and 64 bit), and most 32 bit and 64 bit Unix derivatives. Support for post C++03 language features is included for modern compilers at API level only, so the library should work even with pretty old compilers. +3. Lightweight: follow the "pay only for what you use" guideline. +4. Unintrusive: avoid forcing any particular design or even programming style on the user. This is a library, not a framework. + +#### Alternatives + +In case you want to look into other means of working with UTF-8 strings from C++, here is the list of solutions I am aware of: + +1. [ICU Library](http://icu.sourceforge.net/). It is very powerful, complete, feature-rich, mature, and widely used. Also big, intrusive, non-generic, and doesn't play well with the Standard Library. I definitelly recommend looking at ICU even if you don't plan to use it. +2. C++11 language and library features. Still far from complete, and not easy to use. +3. [Glib::ustring](http://www.gtkmm.org/gtkmm2/docs/tutorial/html/ch03s04.html). A class specifically made to work with UTF-8 strings, and also feel like `std::string`. If you prefer to have yet another string class in your code, it may be worth a look. Be aware of the licensing issues, though. +4. Platform dependent solutions: Windows and POSIX have functions to convert strings from one encoding to another. That is only a subset of what my library offers, but if that is all you need it may be good enough. + + ## Reference ### Functions From utf8 Namespace +#### utf8::append + +Available in version 3.0 and later. Requires a C++ 11 compliant compiler. + +Encodes a 32 bit code point as a UTF-8 sequence of octets and appends the sequence to a UTF-8 string. + +```cpp +void append(char32_t cp, std::string& s); +``` + +`cp`: a code point to append to the string. +`s`: a utf-8 encoded string to append the code point to. + +Example of use: + +```cpp +std::string u; +append(0x0448, u); +assert (u[0] == char(0xd1) && u[1] == char(0x88) && u.length() == 2); +``` + +In case of an invalid code point, a `utf8::invalid_code_point` exception is thrown. + + #### utf8::append Available in version 1.0 and later. @@ -238,39 +312,6 @@ In case `start` is reached before a UTF-8 lead octet is hit, or if an invalid UT In case `start` equals `it`, a `not_enough_room` exception is thrown. -#### utf8::previous - -Deprecated in version 1.02 and later. - -Given a reference to an iterator pointing to an octet in a UTF-8 seqence, it decreases the iterator until it hits the beginning of the previous UTF-8 encoded code point and returns the 32 bits representation of the code point. - -```cpp -template -uint32_t previous(octet_iterator& it, octet_iterator pass_start); -``` - -`octet_iterator`: a random access iterator. -`it`: a reference pointing to an octet within a UTF-8 encoded string. After the function returns, it is decremented to point to the beginning of the previous code point. -`pass_start`: an iterator to the point in the sequence where the search for the beginning of a code point is aborted if no result was reached. It is a safety measure to prevent passing the beginning of the string in the search for a UTF-8 lead octet. -Return value: the 32 bit representation of the previous code point. - -Example of use: - -```cpp -char* twochars = "\xe6\x97\xa5\xd1\x88"; -unsigned char* w = twochars + 3; -int cp = previous (w, twochars - 1); -assert (cp == 0x65e5); -assert (w == twochars); -``` - - -`utf8::previous` is deprecated, and `utf8::prior` should be used instead, although the existing code can continue using this function. The problem is the parameter `pass_start` that points to the position just before the beginning of the sequence. Standard containers don't have the concept of "pass start" and the function can not be used with their iterators. - -`it` will typically point to the beginning of a code point, and `pass_start` will point to the octet just before the beginning of the string to ensure we don't go backwards too far. `it` is decreased until it points to a lead UTF-8 octet, and then the UTF-8 sequence beginning with that octet is decoded to a 32 bit representation and returned. - -In case `pass_start` is reached before a UTF-8 lead octet is hit, or if an invalid UTF-8 sequence is started by the lead octet, an `invalid_utf8` exception is thrown - #### utf8::advance Available in version 1.0 and later. @@ -284,8 +325,8 @@ void advance (octet_iterator& it, distance_type n, octet_iterator end); `octet_iterator`: an input iterator. `distance_type`: an integral type convertible to `octet_iterator`'s difference type. `it`: a reference to an iterator pointing to the beginning of an UTF-8 encoded code point. After the function returns, it is incremented to point to the nth following code point. -`n`: a positive integer that shows how many code points we want to advance. -`end`: end of the UTF-8 sequence to be processed. If `it` gets equal to `end` during the extraction of a code point, an `utf8::not_enough_room` exception is thrown. +`n`: number of code points `it` should be advanced. A negative value means decrement. +`end`: limit of the UTF-8 sequence to be processed. If `n` is positive and `it` gets equal to `end` during the extraction of a code point, an `utf8::not_enough_room` exception is thrown. If `n` is negative and `it` reaches `end` while `it` points t a trail byte of a UTF-8 sequence, a `utf8::invalid_code_point` exception is thrown. Example of use: @@ -294,10 +335,10 @@ char* twochars = "\xe6\x97\xa5\xd1\x88"; unsigned char* w = twochars; advance (w, 2, twochars + 6); assert (w == twochars + 5); +advance (w, -2, twochars); +assert (w == twochars); ``` -This function works only "forward". In case of a negative `n`, there is no effect. - In case of an invalid code point, a `utf8::invalid_code_point` exception is thrown. #### utf8::distance @@ -328,6 +369,54 @@ This function is used to find the length (in code points) of a UTF-8 encoded str In case of an invalid UTF-8 seqence, a `utf8::invalid_utf8` exception is thrown. If `last` does not point to the past-of-end of a UTF-8 seqence, a `utf8::not_enough_room` exception is thrown. +#### utf8::utf16to8 + +Available in version 3.0 and later. Requires a C++ 11 compliant compiler. + +Converts a UTF-16 encoded string to UTF-8. + +```cpp +std::string utf16to8(const std::u16string& s); +``` + +`s`: a UTF-16 encoded string. +Return value: A UTF-8 encoded string. + +Example of use: + +```cpp + u16string utf16string = {0x41, 0x0448, 0x65e5, 0xd834, 0xdd1e}; + string u = utf16to8(utf16string); + assert (u.size() == 10); +``` + +In case of invalid UTF-16 sequence, a `utf8::invalid_utf16` exception is thrown. + +#### utf8::utf16to8 + +Available in version 3.2 and later. Requires a C++ 17 compliant compiler. + +Converts a UTF-16 encoded string to UTF-8. + +```cpp +std::string utf16to8(std::u16string_view s); +``` + +`s`: a UTF-16 encoded string. +Return value: A UTF-8 encoded string. + +Example of use: + +```cpp + u16string utf16string = {0x41, 0x0448, 0x65e5, 0xd834, 0xdd1e}; + u16string_view utf16stringview(u16string); + string u = utf16to8(utf16string); + assert (u.size() == 10); +``` + +In case of invalid UTF-16 sequence, a `utf8::invalid_utf16` exception is thrown. + + #### utf8::utf16to8 Available in version 1.0 and later. @@ -357,6 +446,57 @@ assert (utf8result.size() == 10); In case of invalid UTF-16 sequence, a `utf8::invalid_utf16` exception is thrown. +#### utf8::utf8to16 + +Available in version 3.0 and later. Requires a C++ 11 compliant compiler. + +Converts an UTF-8 encoded string to UTF-16. + +```cpp +std::u16string utf8to16(const std::string& s); +``` + +`s`: an UTF-8 encoded string to convert. +Return value: A UTF-16 encoded string + +Example of use: + +```cpp +string utf8_with_surrogates = "\xe6\x97\xa5\xd1\x88\xf0\x9d\x84\x9e"; +u16string utf16result = utf8to16(utf8_with_surrogates); +assert (utf16result.length() == 4); +assert (utf16result[2] == 0xd834); +assert (utf16result[3] == 0xdd1e); +``` + +In case of an invalid UTF-8 seqence, a `utf8::invalid_utf8` exception is thrown. + +#### utf8::utf8to16 + +Available in version 3.2 and later. Requires a C++ 17 compliant compiler. + +Converts an UTF-8 encoded string to UTF-16. + +```cpp +std::u16string utf8to16(std::string_view s); +``` + +`s`: an UTF-8 encoded string to convert. +Return value: A UTF-16 encoded string + +Example of use: + +```cpp +string_view utf8_with_surrogates = "\xe6\x97\xa5\xd1\x88\xf0\x9d\x84\x9e"; +u16string utf16result = utf8to16(utf8_with_surrogates); +assert (utf16result.length() == 4); +assert (utf16result[2] == 0xd834); +assert (utf16result[3] == 0xdd1e); +``` + +In case of an invalid UTF-8 seqence, a `utf8::invalid_utf8` exception is thrown. + + #### utf8::utf8to16 Available in version 1.0 and later. @@ -387,6 +527,54 @@ assert (utf16result[3] == 0xdd1e); In case of an invalid UTF-8 seqence, a `utf8::invalid_utf8` exception is thrown. If `end` does not point to the past-of-end of a UTF-8 seqence, a `utf8::not_enough_room` exception is thrown. +#### utf8::utf32to8 + +Available in version 3.0 and later. Requires a C++ 11 compliant compiler. + +Converts a UTF-32 encoded string to UTF-8. + +```cpp +std::string utf32to8(const std::u32string& s); +``` + +`s`: a UTF-32 encoded string. +Return value: a UTF-8 encoded string. + +Example of use: + +```cpp +u32string utf32string = {0x448, 0x65E5, 0x10346}; +string utf8result = utf32to8(utf32string); +assert (utf8result.size() == 9); +``` + +In case of invalid UTF-32 string, a `utf8::invalid_code_point` exception is thrown. + +#### utf8::utf32to8 + +Available in version 3.2 and later. Requires a C++ 17 compliant compiler. + +Converts a UTF-32 encoded string to UTF-8. + +```cpp +std::string utf32to8(std::u32string_view s); +``` + +`s`: a UTF-32 encoded string. +Return value: a UTF-8 encoded string. + +Example of use: + +```cpp +u32string utf32string = {0x448, 0x65E5, 0x10346}; +u32string_view utf32stringview(utf32string); +string utf8result = utf32to8(utf32stringview); +assert (utf8result.size() == 9); +``` + +In case of invalid UTF-32 string, a `utf8::invalid_code_point` exception is thrown. + + #### utf8::utf32to8 Available in version 1.0 and later. @@ -407,7 +595,7 @@ Return value: An iterator pointing to the place after the appended UTF-8 string. Example of use: -``` +```cpp int utf32string[] = {0x448, 0x65E5, 0x10346, 0}; vector utf8result; utf32to8(utf32string, utf32string + 3, back_inserter(utf8result)); @@ -416,6 +604,53 @@ assert (utf8result.size() == 9); In case of invalid UTF-32 string, a `utf8::invalid_code_point` exception is thrown. +#### utf8::utf8to32 + +Available in version 3.0 and later. Requires a C++ 11 compliant compiler. + +Converts a UTF-8 encoded string to UTF-32. + +```cpp +std::u32string utf8to32(const std::string& s); +``` + +`s`: a UTF-8 encoded string. +Return value: a UTF-32 encoded string. + +Example of use: + +```cpp +const char* twochars = "\xe6\x97\xa5\xd1\x88"; +u32string utf32result = utf8to32(twochars); +assert (utf32result.size() == 2); +``` + +In case of an invalid UTF-8 seqence, a `utf8::invalid_utf8` exception is thrown. + +#### utf8::utf8to32 + +Available in version 3.2 and later. Requires a C++ 17 compliant compiler. + +Converts a UTF-8 encoded string to UTF-32. + +```cpp +std::u32string utf8to32(std::string_view s); +``` + +`s`: a UTF-8 encoded string. +Return value: a UTF-32 encoded string. + +Example of use: + +```cpp +string_view twochars = "\xe6\x97\xa5\xd1\x88"; +u32string utf32result = utf8to32(twochars); +assert (utf32result.size() == 2); +``` + +In case of an invalid UTF-8 seqence, a `utf8::invalid_utf8` exception is thrown. + + #### utf8::utf8to32 Available in version 1.0 and later. @@ -445,6 +680,53 @@ assert (utf32result.size() == 2); In case of an invalid UTF-8 seqence, a `utf8::invalid_utf8` exception is thrown. If `end` does not point to the past-of-end of a UTF-8 seqence, a `utf8::not_enough_room` exception is thrown. +#### utf8::find_invalid + +Available in version 3.0 and later. Requires a C++ 11 compliant compiler. + +Detects an invalid sequence within a UTF-8 string. + +```cpp +std::size_t find_invalid(const std::string& s); +``` + +`s`: a UTF-8 encoded string. +Return value: the index of the first invalid octet in the UTF-8 string. In case none were found, equals `std::string::npos`. + +Example of use: + +```cpp +string utf_invalid = "\xe6\x97\xa5\xd1\x88\xfa"; +auto invalid = find_invalid(utf_invalid); +assert (invalid == 5); +``` + +This function is typically used to make sure a UTF-8 string is valid before processing it with other functions. It is especially important to call it if before doing any of the _unchecked_ operations on it. + +#### utf8::find_invalid + +Available in version 3.2 and later. Requires a C++ 17 compliant compiler. + +Detects an invalid sequence within a UTF-8 string. + +```cpp +std::size_t find_invalid(std::string_view s); +``` + +`s`: a UTF-8 encoded string. +Return value: the index of the first invalid octet in the UTF-8 string. In case none were found, equals `std::string_view::npos`. + +Example of use: + +```cpp +string_view utf_invalid = "\xe6\x97\xa5\xd1\x88\xfa"; +auto invalid = find_invalid(utf_invalid); +assert (invalid == 5); +``` + +This function is typically used to make sure a UTF-8 string is valid before processing it with other functions. It is especially important to call it if before doing any of the _unchecked_ operations on it. + + #### utf8::find_invalid Available in version 1.0 and later. @@ -471,6 +753,53 @@ assert (invalid == utf_invalid + 5); This function is typically used to make sure a UTF-8 string is valid before processing it with other functions. It is especially important to call it if before doing any of the _unchecked_ operations on it. +#### utf8::is_valid + +Available in version 3.0 and later. Requires a C++ 11 compliant compiler. + +Checks whether a string object contains valid UTF-8 encoded text. + +```cpp +bool is_valid(const std::string& s); +``` + +`s`: a UTF-8 encoded string. +Return value: `true` if the string contains valid UTF-8 encoded text; `false` if not. + +Example of use: + +```cpp +char utf_invalid[] = "\xe6\x97\xa5\xd1\x88\xfa"; +bool bvalid = is_valid(utf_invalid); +assert (bvalid == false); +``` + +You may want to use `is_valid` to make sure that a string contains valid UTF-8 text without the need to know where it fails if it is not valid. + +#### utf8::is_valid + +Available in version 3.2 and later. Requires a C++ 17 compliant compiler. + +Checks whether a string object contains valid UTF-8 encoded text. + +```cpp +bool is_valid(std::string_view s); +``` + +`s`: a UTF-8 encoded string. +Return value: `true` if the string contains valid UTF-8 encoded text; `false` if not. + +Example of use: + +```cpp +string_view utf_invalid = "\xe6\x97\xa5\xd1\x88\xfa"; +bool bvalid = is_valid(utf_invalid); +assert (bvalid == false); +``` + +You may want to use `is_valid` to make sure that a string contains valid UTF-8 text without the need to know where it fails if it is not valid. + + #### utf8::is_valid Available in version 1.0 and later. @@ -497,6 +826,59 @@ assert (bvalid == false); `is_valid` is a shorthand for `find_invalid(start, end) == end;`. You may want to use it to make sure that a byte seqence is a valid UTF-8 string without the need to know where it fails if it is not valid. +#### utf8::replace_invalid + +Available in version 3.0 and later. Requires a C++ 11 compliant compiler. + +Replaces all invalid UTF-8 sequences within a string with a replacement marker. + +```cpp +std::string replace_invalid(const std::string& s, char32_t replacement); +std::string replace_invalid(const std::string& s); +``` + +`s`: a UTF-8 encoded string. +`replacement`: A Unicode code point for the replacement marker. The version without this parameter assumes the value `0xfffd` +Return value: A UTF-8 encoded string with replaced invalid sequences. + +Example of use: + +```cpp +string invalid_sequence = "a\x80\xe0\xa0\xc0\xaf\xed\xa0\x80z"; +string replace_invalid_result = replace_invalid(invalid_sequence, '?'); +bvalid = is_valid(replace_invalid_result); +assert (bvalid); +const string fixed_invalid_sequence = "a????z"; +assert (fixed_invalid_sequence == replace_invalid_result); +``` + +#### utf8::replace_invalid + +Available in version 3.2 and later. Requires a C++ 17 compliant compiler. + +Replaces all invalid UTF-8 sequences within a string with a replacement marker. + +```cpp +std::string replace_invalid(std::string_view s, char32_t replacement); +std::string replace_invalid(std::string_view s); +``` + +`s`: a UTF-8 encoded string. +`replacement`: A Unicode code point for the replacement marker. The version without this parameter assumes the value `0xfffd` +Return value: A UTF-8 encoded string with replaced invalid sequences. + +Example of use: + +```cpp +string_view invalid_sequence = "a\x80\xe0\xa0\xc0\xaf\xed\xa0\x80z"; +string replace_invalid_result = replace_invalid(invalid_sequence, '?'); +bool bvalid = is_valid(replace_invalid_result); +assert (bvalid); +const string fixed_invalid_sequence = "a????z"; +assert(fixed_invalid_sequence, replace_invalid_result); +``` + + #### utf8::replace_invalid Available in version 2.0 and later. @@ -532,11 +914,64 @@ assert (std::equal(replace_invalid_result.begin(), replace_invalid_result.end(), `replace_invalid` does not perform in-place replacement of invalid sequences. Rather, it produces a copy of the original string with the invalid sequences replaced with a replacement marker. Therefore, `out` must not be in the `[start, end]` range. -If `end` does not point to the past-of-end of a UTF-8 sequence, a `utf8::not_enough_room` exception is thrown. +#### utf8::starts_with_bom + +Available in version 3.0 and later. Requires a C++ 11 compliant compiler. + +Checks whether a string starts with a UTF-8 byte order mark (BOM) + +```cpp +bool starts_with_bom(const std::string& s); +``` + +`s`: a UTF-8 encoded string. +Return value: `true` if the string starts with a UTF-8 byte order mark; `false` if not. + +Example of use: + +```cpp +string byte_order_mark = {char(0xef), char(0xbb), char(0xbf)}; +bool bbom = starts_with_bom(byte_order_mark); +assert (bbom == true); +string threechars = "\xf0\x90\x8d\x86\xe6\x97\xa5\xd1\x88"; +bool no_bbom = starts_with_bom(threechars); +assert (no_bbom == false); + ``` + +The typical use of this function is to check the first three bytes of a file. If they form the UTF-8 BOM, we want to skip them before processing the actual UTF-8 encoded text. + #### utf8::starts_with_bom -Available in version 2.3 and later. Relaces deprecated `is_bom()` function. +Available in version 3.2 and later. Requires a C++ 17 compliant compiler. + +Checks whether a string starts with a UTF-8 byte order mark (BOM) + +```cpp +bool starts_with_bom(std::string_view s); +``` + +`s`: a UTF-8 encoded string. +Return value: `true` if the string starts with a UTF-8 byte order mark; `false` if not. + +Example of use: + +```cpp +string byte_order_mark = {char(0xef), char(0xbb), char(0xbf)}; +string_view byte_order_mark_view(byte_order_mark); +bool bbom = starts_with_bom(byte_order_mark_view); +assert (bbom); +string_view threechars = "\xf0\x90\x8d\x86\xe6\x97\xa5\xd1\x88"; +bool no_bbom = starts_with_bom(threechars); +assert (!no_bbom); + ``` + +The typical use of this function is to check the first three bytes of a file. If they form the UTF-8 BOM, we want to skip them before processing the actual UTF-8 encoded text. + + +#### utf8::starts_with_bom + +Available in version 2.3 and later. Checks whether an octet sequence starts with a UTF-8 byte order mark (BOM) @@ -560,33 +995,6 @@ assert (bbom == true); The typical use of this function is to check the first three bytes of a file. If they form the UTF-8 BOM, we want to skip them before processing the actual UTF-8 encoded text. -#### utf8::is_bom - -Available in version 1.0 and later. Deprecated in version 2.3\. `starts_with_bom()` should be used instead. - -Checks whether a sequence of three octets is a UTF-8 byte order mark (BOM) - -```cpp -template -bool is_bom (octet_iterator it); // Deprecated -``` - -`octet_iterator`: an input iterator. -`it`: beginning of the 3-octet sequence to check -Return value: `true` if the sequence is UTF-8 byte order mark; `false` if not. - -Example of use: - -```cpp -unsigned char byte_order_mark[] = {0xef, 0xbb, 0xbf}; -bool bbom = is_bom(byte_order_mark); -assert (bbom == true); -``` - -The typical use of this function is to check the first three bytes of a file. If they form the UTF-8 BOM, we want to skip them before processing the actual UTF-8 encoded text. - -If a sequence is shorter than three bytes, an invalid iterator will be dereferenced. Therefore, this function is deprecated in favor of `starts_with_bom()`that takes the end of sequence as an argument. - ### Types From utf8 Namespace #### utf8::exception @@ -678,15 +1086,24 @@ class iterator; ##### Member functions -`iterator();` the deafult constructor; the underlying octet_iterator is constructed with its default constructor. +`iterator();` the deafult constructor; the underlying octet_iterator is constructed with its default constructor. + `explicit iterator (const octet_iterator& octet_it, const octet_iterator& range_start, const octet_iterator& range_end);` a constructor that initializes the underlying octet_iterator with octet_it and sets the range in which the iterator is considered valid. + `octet_iterator base () const;` returns the underlying octet_iterator. + `uint32_t operator * () const;` decodes the utf-8 sequence the underlying octet_iterator is pointing to and returns the code point. + `bool operator == (const iterator& rhs) const;` returns `true` if the two underlaying iterators are equal. + `bool operator != (const iterator& rhs) const;` returns `true` if the two underlaying iterators are not equal. + `iterator& operator ++ ();` the prefix increment - moves the iterator to the next UTF-8 encoded code point. + `iterator operator ++ (int);` the postfix increment - moves the iterator to the next UTF-8 encoded code point and returns the current one. + `iterator& operator -- ();` the prefix decrement - moves the iterator to the previous UTF-8 encoded code point. + `iterator operator -- (int);` the postfix decrement - moves the iterator to the previous UTF-8 encoded code point and returns the current one. Example of use: @@ -824,34 +1241,6 @@ assert (w == twochars); This is a faster but less safe version of `utf8::prior`. It does not check for validity of the supplied UTF-8 sequence and offers no boundary checking. -#### utf8::unchecked::previous (deprecated, see utf8::unchecked::prior) - -Deprecated in version 1.02 and later. - -Given a reference to an iterator pointing to an octet in a UTF-8 seqence, it decreases the iterator until it hits the beginning of the previous UTF-8 encoded code point and returns the 32 bits representation of the code point. - -```cpp -template -uint32_t previous(octet_iterator& it); -``` - -`it`: a reference pointing to an octet within a UTF-8 encoded string. After the function returns, it is decremented to point to the beginning of the previous code point. -Return value: the 32 bit representation of the previous code point. - -Example of use: - -```cpp -char* twochars = "\xe6\x97\xa5\xd1\x88"; -char* w = twochars + 3; -int cp = unchecked::previous (w); -assert (cp == 0x65e5); -assert (w == twochars); -``` - -The reason this function is deprecated is just the consistency with the "checked" versions, where `prior` should be used instead of `previous`. In fact, `unchecked::previous` behaves exactly the same as `unchecked::prior` - -This is a faster but less safe version of `utf8::previous`. It does not check for validity of the supplied UTF-8 sequence and offers no boundary checking. - #### utf8::unchecked::advance Available in version 1.0 and later. @@ -863,8 +1252,8 @@ template void advance (octet_iterator& it, distance_type n); ``` -`it`: a reference to an iterator pointing to the beginning of an UTF-8 encoded code point. After the function returns, it is incremented to point to the nth following code point. -`n`: a positive integer that shows how many code points we want to advance. +`it`: a reference to an iterator pointing to the beginning of an UTF-8 encoded code point. After the function returns, it is incremented to point to the nth following code point. +`n`: number of code points `it` should be advanced. A negative value means decrement. Example of use: @@ -875,8 +1264,6 @@ unchecked::advance (w, 2); assert (w == twochars + 5); ``` -This function works only "forward". In case of a negative `n`, there is no effect. - This is a faster but less safe version of `utf8::advance`. It does not check for validity of the supplied UTF-8 sequence and offers no boundary checking. #### utf8::unchecked::distance @@ -1013,6 +1400,43 @@ assert (utf32result.size() == 2); This is a faster but less safe version of `utf8::utf8to32`. It does not check for validity of the supplied UTF-8 sequence. +#### utf8::unchecked::replace_invalid + +Available in version 3.1 and later. + +Replaces all invalid UTF-8 sequences within a string with a replacement marker. + +```cpp +template +output_iterator replace_invalid(octet_iterator start, octet_iterator end, output_iterator out, uint32_t replacement); +template +output_iterator replace_invalid(octet_iterator start, octet_iterator end, output_iterator out); +``` + +`octet_iterator`: an input iterator. +`output_iterator`: an output iterator. +`start`: an iterator pointing to the beginning of the UTF-8 string to look for invalid UTF-8 sequences. +`end`: an iterator pointing to pass-the-end of the UTF-8 string to look for invalid UTF-8 sequences. +`out`: An output iterator to the range where the result of replacement is stored. +`replacement`: A Unicode code point for the replacement marker. The version without this parameter assumes the value `0xfffd` +Return value: An iterator pointing to the place after the UTF-8 string with replaced invalid sequences. + +Example of use: + +```cpp +char invalid_sequence[] = "a\x80\xe0\xa0\xc0\xaf\xed\xa0\x80z"; +vector replace_invalid_result; +unchecked::replace_invalid (invalid_sequence, invalid_sequence + sizeof(invalid_sequence), back_inserter(replace_invalid_result), '?'); +bvalid = utf8::is_valid(replace_invalid_result.begin(), replace_invalid_result.end()); +assert (bvalid); +char* fixed_invalid_sequence = "a????z"; +assert (std::equal(replace_invalid_result.begin(), replace_invalid_result.end(), fixed_invalid_sequence)); +``` + +`replace_invalid` does not perform in-place replacement of invalid sequences. Rather, it produces a copy of the original string with the invalid sequences replaced with a replacement marker. Therefore, `out` must not be in the `[start, end]` range. + +Unlike `utf8::replace_invalid`, this function does not verify validity of the replacement marker. + ### Types From utf8::unchecked Namespace #### utf8::iterator @@ -1029,14 +1453,23 @@ class iterator; ##### Member functions `iterator();` the deafult constructor; the underlying octet_iterator is constructed with its default constructor. -`explicit iterator (const octet_iterator& octet_it);` a constructor that initializes the underlying octet_iterator with `octet_it` + +`explicit iterator (const octet_iterator& octet_it);` a constructor that initializes the underlying octet_iterator with `octet_it`. + `octet_iterator base () const;` returns the underlying octet_iterator. + `uint32_t operator * () const;` decodes the utf-8 sequence the underlying octet_iterator is pointing to and returns the code point. + `bool operator == (const iterator& rhs) const;` returns `true` if the two underlaying iterators are equal. + `bool operator != (const iterator& rhs) const;` returns `true` if the two underlaying iterators are not equal. + `iterator& operator ++ ();` the prefix increment - moves the iterator to the next UTF-8 encoded code point. + `iterator operator ++ (int);` the postfix increment - moves the iterator to the next UTF-8 encoded code point and returns the current one. + `iterator& operator -- ();` the prefix decrement - moves the iterator to the previous UTF-8 encoded code point. + `iterator operator -- (int);` the postfix decrement - moves the iterator to the previous UTF-8 encoded code point and returns the current one. Example of use: @@ -1062,26 +1495,6 @@ assert (*un_it == 0x10346); This is an unchecked version of `utf8::iterator`. It is faster in many cases, but offers no validity or range checks. -## Points of interest - -#### Design goals and decisions - -The library was designed to be: - -1. Generic: for better or worse, there are many C++ string classes out there, and the library should work with as many of them as possible. -2. Portable: the library should be portable both accross different platforms and compilers. The only non-portable code is a small section that declares unsigned integers of different sizes: three typedefs. They can be changed by the users of the library if they don't match their platform. The default setting should work for Windows (both 32 and 64 bit), and most 32 bit and 64 bit Unix derivatives. At this point I don't plan to use any post C++03 features, so the library should work even with pretty old compilers. -3. Lightweight: follow the "pay only for what you use" guideline. -4. Unintrusive: avoid forcing any particular design or even programming style on the user. This is a library, not a framework. - -#### Alternatives - -In case you want to look into other means of working with UTF-8 strings from C++, here is the list of solutions I am aware of: - -1. [ICU Library](http://icu.sourceforge.net/). It is very powerful, complete, feature-rich, mature, and widely used. Also big, intrusive, non-generic, and doesn't play well with the Standard Library. I definitelly recommend looking at ICU even if you don't plan to use it. -2. C++11 language and library features. Still far from complete, and not easy to use. -3. [Glib::ustring](http://www.gtkmm.org/gtkmm2/docs/tutorial/html/ch03s04.html). A class specifically made to work with UTF-8 strings, and also feel like `std::string`. If you prefer to have yet another string class in your code, it may be worth a look. Be aware of the licensing issues, though. -4. Platform dependent solutions: Windows and POSIX have functions to convert strings from one encoding to another. That is only a subset of what my library offers, but if that is all you need it may be good enough. - ## Links 1. [The Unicode Consortium](http://www.unicode.org/). diff --git a/utf8/utf8/checked.h b/utf8/utf8/checked.h index 2aef5838d..993b7f7c5 100644 --- a/utf8/utf8/checked.h +++ b/utf8/utf8/checked.h @@ -42,7 +42,7 @@ namespace utf8 uint32_t cp; public: invalid_code_point(uint32_t codepoint) : cp(codepoint) {} - virtual const char* what() const throw() { return "Invalid code point"; } + virtual const char* what() const UTF_CPP_NOEXCEPT UTF_CPP_OVERRIDE { return "Invalid code point"; } uint32_t code_point() const {return cp;} }; @@ -50,7 +50,7 @@ namespace utf8 uint8_t u8; public: invalid_utf8 (uint8_t u) : u8(u) {} - virtual const char* what() const throw() { return "Invalid UTF-8"; } + virtual const char* what() const UTF_CPP_NOEXCEPT UTF_CPP_OVERRIDE { return "Invalid UTF-8"; } uint8_t utf8_octet() const {return u8;} }; @@ -58,13 +58,13 @@ namespace utf8 uint16_t u16; public: invalid_utf16 (uint16_t u) : u16(u) {} - virtual const char* what() const throw() { return "Invalid UTF-16"; } + virtual const char* what() const UTF_CPP_NOEXCEPT UTF_CPP_OVERRIDE { return "Invalid UTF-16"; } uint16_t utf16_word() const {return u16;} }; class not_enough_room : public exception { public: - virtual const char* what() const throw() { return "Not enough space"; } + virtual const char* what() const UTF_CPP_NOEXCEPT UTF_CPP_OVERRIDE { return "Not enough space"; } }; /// The library API - functions intended to be called by the users @@ -107,7 +107,9 @@ namespace utf8 *out++ = *it; break; case internal::NOT_ENOUGH_ROOM: - throw not_enough_room(); + out = utf8::append (replacement, out); + start = end; + break; case internal::INVALID_LEAD: out = utf8::append (replacement, out); ++start; @@ -174,23 +176,19 @@ namespace utf8 return utf8::peek_next(it, end); } - /// Deprecated in versions that include "prior" - template - uint32_t previous(octet_iterator& it, octet_iterator pass_start) - { - octet_iterator end = it; - while (utf8::internal::is_trail(*(--it))) - if (it == pass_start) - throw invalid_utf8(*it); // error - no lead byte in the sequence - octet_iterator temp = it; - return utf8::next(temp, end); - } - template void advance (octet_iterator& it, distance_type n, octet_iterator end) { - for (distance_type i = 0; i < n; ++i) - utf8::next(it, end); + const distance_type zero(0); + if (n < zero) { + // backward + for (distance_type i = n; i < zero; ++i) + utf8::prior(it, end); + } else { + // forward + for (distance_type i = zero; i < n; ++i) + utf8::next(it, end); + } } template @@ -265,11 +263,16 @@ namespace utf8 // The iterator class template - class iterator : public std::iterator { + class iterator { octet_iterator it; octet_iterator range_start; octet_iterator range_end; public: + typedef uint32_t value_type; + typedef uint32_t* pointer; + typedef uint32_t& reference; + typedef std::ptrdiff_t difference_type; + typedef std::bidirectional_iterator_tag iterator_category; iterator () {} explicit iterator (const octet_iterator& octet_it, const octet_iterator& rangestart, @@ -322,6 +325,11 @@ namespace utf8 } // namespace utf8 +#if UTF_CPP_CPLUSPLUS >= 201703L // C++ 17 or later +#include "cpp17.h" +#elif UTF_CPP_CPLUSPLUS >= 201103L // C++ 11 or later +#include "cpp11.h" +#endif // C++ 11 or later + #endif //header guard - diff --git a/utf8/utf8/core.h b/utf8/utf8/core.h index ae0f367db..de6199f2a 100644 --- a/utf8/utf8/core.h +++ b/utf8/utf8/core.h @@ -30,6 +30,23 @@ DEALINGS IN THE SOFTWARE. #include +// Determine the C++ standard version. +// If the user defines UTF_CPP_CPLUSPLUS, use that. +// Otherwise, trust the unreliable predefined macro __cplusplus + +#if !defined UTF_CPP_CPLUSPLUS + #define UTF_CPP_CPLUSPLUS __cplusplus +#endif + +#if UTF_CPP_CPLUSPLUS >= 201103L // C++ 11 or later + #define UTF_CPP_OVERRIDE override + #define UTF_CPP_NOEXCEPT noexcept +#else // C++ 98/03 + #define UTF_CPP_OVERRIDE + #define UTF_CPP_NOEXCEPT throw() +#endif // C++ 11 or later + + namespace utf8 { // The typedefs for 8-bit, 16-bit and 32-bit unsigned integers @@ -49,8 +66,8 @@ namespace internal const uint16_t LEAD_SURROGATE_MAX = 0xdbffu; const uint16_t TRAIL_SURROGATE_MIN = 0xdc00u; const uint16_t TRAIL_SURROGATE_MAX = 0xdfffu; - const uint16_t LEAD_OFFSET = LEAD_SURROGATE_MIN - (0x10000 >> 10); - const uint32_t SURROGATE_OFFSET = 0x10000u - (LEAD_SURROGATE_MIN << 10) - TRAIL_SURROGATE_MIN; + const uint16_t LEAD_OFFSET = 0xd7c0u; // LEAD_SURROGATE_MIN - (0x10000 >> 10) + const uint32_t SURROGATE_OFFSET = 0xfca02400u; // 0x10000u - (LEAD_SURROGATE_MIN << 10) - TRAIL_SURROGATE_MIN // Maximum valid value for a Unicode code point const uint32_t CODE_POINT_MAX = 0x0010ffffu; @@ -142,7 +159,7 @@ namespace internal if (!utf8::internal::is_trail(*it)) return INCOMPLETE_SEQUENCE; - + return UTF8_OK; } @@ -165,7 +182,7 @@ namespace internal { if (it == end) return NOT_ENOUGH_ROOM; - + code_point = utf8::internal::mask8(*it); UTF8_CPP_INCREASE_AND_RETURN_ON_ERROR(it, end) @@ -222,7 +239,7 @@ namespace internal template utf_error validate_next(octet_iterator& it, octet_iterator end, uint32_t& code_point) { - if (it == end) + if (it == end) return NOT_ENOUGH_ROOM; // Save the original value of it so we can go back in case of failure @@ -237,7 +254,7 @@ namespace internal // Get trail octets and calculate the code point utf_error err = UTF8_OK; switch (length) { - case 0: + case 0: return INVALID_LEAD; case 1: err = utf8::internal::get_sequence_1(it, end, cp); @@ -313,18 +330,7 @@ namespace internal ((it != end) && (utf8::internal::mask8(*it++)) == bom[1]) && ((it != end) && (utf8::internal::mask8(*it)) == bom[2]) ); - } - - //Deprecated in release 2.3 - template - inline bool is_bom (octet_iterator it) - { - return ( - (utf8::internal::mask8(*it++)) == bom[0] && - (utf8::internal::mask8(*it++)) == bom[1] && - (utf8::internal::mask8(*it)) == bom[2] - ); - } + } } // namespace utf8 #endif // header guard diff --git a/utf8/utf8/cpp11.h b/utf8/utf8/cpp11.h new file mode 100644 index 000000000..d93961b04 --- /dev/null +++ b/utf8/utf8/cpp11.h @@ -0,0 +1,103 @@ +// Copyright 2018 Nemanja Trifunovic + +/* +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +*/ + + +#ifndef UTF8_FOR_CPP_a184c22c_d012_11e8_a8d5_f2801f1b9fd1 +#define UTF8_FOR_CPP_a184c22c_d012_11e8_a8d5_f2801f1b9fd1 + +#include "checked.h" +#include + +namespace utf8 +{ + + inline void append(char32_t cp, std::string& s) + { + append(uint32_t(cp), std::back_inserter(s)); + } + + inline std::string utf16to8(const std::u16string& s) + { + std::string result; + utf16to8(s.begin(), s.end(), std::back_inserter(result)); + return result; + } + + inline std::u16string utf8to16(const std::string& s) + { + std::u16string result; + utf8to16(s.begin(), s.end(), std::back_inserter(result)); + return result; + } + + inline std::string utf32to8(const std::u32string& s) + { + std::string result; + utf32to8(s.begin(), s.end(), std::back_inserter(result)); + return result; + } + + inline std::u32string utf8to32(const std::string& s) + { + std::u32string result; + utf8to32(s.begin(), s.end(), std::back_inserter(result)); + return result; + } + + inline std::size_t find_invalid(const std::string& s) + { + std::string::const_iterator invalid = find_invalid(s.begin(), s.end()); + return (invalid == s.end()) ? std::string::npos : (invalid - s.begin()); + } + + inline bool is_valid(const std::string& s) + { + return is_valid(s.begin(), s.end()); + } + + inline std::string replace_invalid(const std::string& s, char32_t replacement) + { + std::string result; + replace_invalid(s.begin(), s.end(), std::back_inserter(result), replacement); + return result; + } + + inline std::string replace_invalid(const std::string& s) + { + std::string result; + replace_invalid(s.begin(), s.end(), std::back_inserter(result)); + return result; + } + + inline bool starts_with_bom(const std::string& s) + { + return starts_with_bom(s.begin(), s.end()); + } + +} // namespace utf8 + +#endif // header guard + diff --git a/utf8/utf8/cpp17.h b/utf8/utf8/cpp17.h new file mode 100644 index 000000000..7bfa86994 --- /dev/null +++ b/utf8/utf8/cpp17.h @@ -0,0 +1,103 @@ +// Copyright 2018 Nemanja Trifunovic + +/* +Permission is hereby granted, free of charge, to any person or organization +obtaining a copy of the software and accompanying documentation covered by +this license (the "Software") to use, reproduce, display, distribute, +execute, and transmit the Software, and to prepare derivative works of the +Software, and to permit third-parties to whom the Software is furnished to +do so, all subject to the following: + +The copyright notices in the Software and this entire statement, including +the above license grant, this restriction and the following disclaimer, +must be included in all copies of the Software, in whole or in part, and +all derivative works of the Software, unless such copies or derivative +works are solely in the form of machine-executable object code generated by +a source language processor. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE, TITLE AND NON-INFRINGEMENT. IN NO EVENT +SHALL THE COPYRIGHT HOLDERS OR ANYONE DISTRIBUTING THE SOFTWARE BE LIABLE +FOR ANY DAMAGES OR OTHER LIABILITY, WHETHER IN CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER +DEALINGS IN THE SOFTWARE. +*/ + + +#ifndef UTF8_FOR_CPP_7e906c01_03a3_4daf_b420_ea7ea952b3c9 +#define UTF8_FOR_CPP_7e906c01_03a3_4daf_b420_ea7ea952b3c9 + +#include "checked.h" +#include + +namespace utf8 +{ + + inline void append(char32_t cp, std::string& s) + { + append(uint32_t(cp), std::back_inserter(s)); + } + + inline std::string utf16to8(std::u16string_view s) + { + std::string result; + utf16to8(s.begin(), s.end(), std::back_inserter(result)); + return result; + } + + inline std::u16string utf8to16(std::string_view s) + { + std::u16string result; + utf8to16(s.begin(), s.end(), std::back_inserter(result)); + return result; + } + + inline std::string utf32to8(std::u32string_view s) + { + std::string result; + utf32to8(s.begin(), s.end(), std::back_inserter(result)); + return result; + } + + inline std::u32string utf8to32(std::string_view s) + { + std::u32string result; + utf8to32(s.begin(), s.end(), std::back_inserter(result)); + return result; + } + + inline std::size_t find_invalid(std::string_view s) + { + std::string_view::const_iterator invalid = find_invalid(s.begin(), s.end()); + return (invalid == s.end()) ? std::string_view::npos : (invalid - s.begin()); + } + + inline bool is_valid(std::string_view s) + { + return is_valid(s.begin(), s.end()); + } + + inline std::string replace_invalid(std::string_view s, char32_t replacement) + { + std::string result; + replace_invalid(s.begin(), s.end(), std::back_inserter(result), replacement); + return result; + } + + inline std::string replace_invalid(std::string_view s) + { + std::string result; + replace_invalid(s.begin(), s.end(), std::back_inserter(result)); + return result; + } + + inline bool starts_with_bom(std::string_view s) + { + return starts_with_bom(s.begin(), s.end()); + } + +} // namespace utf8 + +#endif // header guard + diff --git a/utf8/utf8/unchecked.h b/utf8/utf8/unchecked.h index cb2427166..0e1b51cc7 100644 --- a/utf8/utf8/unchecked.h +++ b/utf8/utf8/unchecked.h @@ -32,13 +32,13 @@ DEALINGS IN THE SOFTWARE. namespace utf8 { - namespace unchecked + namespace unchecked { template octet_iterator append(uint32_t cp, octet_iterator result) { if (cp < 0x80) // one octet - *(result++) = static_cast(cp); + *(result++) = static_cast(cp); else if (cp < 0x800) { // two octets *(result++) = static_cast((cp >> 6) | 0xc0); *(result++) = static_cast((cp & 0x3f) | 0x80); @@ -57,6 +57,46 @@ namespace utf8 return result; } + template + output_iterator replace_invalid(octet_iterator start, octet_iterator end, output_iterator out, uint32_t replacement) + { + while (start != end) { + octet_iterator sequence_start = start; + internal::utf_error err_code = utf8::internal::validate_next(start, end); + switch (err_code) { + case internal::UTF8_OK : + for (octet_iterator it = sequence_start; it != start; ++it) + *out++ = *it; + break; + case internal::NOT_ENOUGH_ROOM: + out = utf8::unchecked::append (replacement, out); + start = end; + break; + case internal::INVALID_LEAD: + out = utf8::unchecked::append (replacement, out); + ++start; + break; + case internal::INCOMPLETE_SEQUENCE: + case internal::OVERLONG_SEQUENCE: + case internal::INVALID_CODE_POINT: + out = utf8::unchecked::append (replacement, out); + ++start; + // just one replacement mark for the sequence + while (start != end && utf8::internal::is_trail(*start)) + ++start; + break; + } + } + return out; + } + + template + inline output_iterator replace_invalid(octet_iterator start, octet_iterator end, output_iterator out) + { + static const uint32_t replacement_marker = utf8::internal::mask16(0xfffd); + return utf8::unchecked::replace_invalid(start, end, out, replacement_marker); + } + template uint32_t next(octet_iterator& it) { @@ -85,13 +125,13 @@ namespace utf8 break; } ++it; - return cp; + return cp; } template uint32_t peek_next(octet_iterator it) { - return utf8::unchecked::next(it); + return utf8::unchecked::next(it); } template @@ -102,18 +142,19 @@ namespace utf8 return utf8::unchecked::next(temp); } - // Deprecated in versions that include prior, but only for the sake of consistency (see utf8::previous) - template - inline uint32_t previous(octet_iterator& it) - { - return utf8::unchecked::prior(it); - } - template void advance (octet_iterator& it, distance_type n) { - for (distance_type i = 0; i < n; ++i) - utf8::unchecked::next(it); + const distance_type zero(0); + if (n < zero) { + // backward + for (distance_type i = n; i < zero; ++i) + utf8::unchecked::prior(it); + } else { + // forward + for (distance_type i = zero; i < n; ++i) + utf8::unchecked::next(it); + } } template @@ -128,7 +169,7 @@ namespace utf8 template octet_iterator utf16to8 (u16bit_iterator start, u16bit_iterator end, octet_iterator result) - { + { while (start != end) { uint32_t cp = utf8::internal::mask16(*start++); // Take care of surrogate pairs first @@ -138,7 +179,7 @@ namespace utf8 } result = utf8::unchecked::append(cp, result); } - return result; + return result; } template @@ -176,9 +217,14 @@ namespace utf8 // The iterator class template - class iterator : public std::iterator { + class iterator { octet_iterator it; public: + typedef uint32_t value_type; + typedef uint32_t* pointer; + typedef uint32_t& reference; + typedef std::ptrdiff_t difference_type; + typedef std::bidirectional_iterator_tag iterator_category; iterator () {} explicit iterator (const octet_iterator& octet_it): it(octet_it) {} // the default "big three" are OK From a66c305609a14380015bfb79bc03e2087a0cdc5c Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Tue, 5 Jul 2022 11:10:43 +0200 Subject: [PATCH 105/337] gen: work around a warning on red hat When n is an int, doing "new formula[n];" gives us "warning: argument 1 value '18446744073709551615' exceeds maximum object size 9223372036854775807" on Red Hat. * spot/gen/formulas.cc (pps_arbiter): Pass n as unsigned. Also fix some determinism in the strict variant. --- spot/gen/formulas.cc | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/spot/gen/formulas.cc b/spot/gen/formulas.cc index a94512970..3f63b07e7 100644 --- a/spot/gen/formulas.cc +++ b/spot/gen/formulas.cc @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2012-2019 Laboratoire de Recherche et Developpement +// Copyright (C) 2012-2019, 2022 Laboratoire de Recherche et Developpement // de l'EPITA (LRDE). // // This file is part of Spot, a model checking library. @@ -1198,13 +1198,13 @@ namespace spot } static formula - pps_arbiter(std::string r_, std::string g_, int n, bool strict_) + pps_arbiter(std::string r_, std::string g_, unsigned n, bool strict_) { formula* r = new formula[n]; formula* g = new formula[n]; std::vector res; - for (int i = 0; i < n; ++i) + for (unsigned i = 0; i < n; ++i) { r[i] = formula::ap(r_ + std::to_string(i + 1)); g[i] = formula::ap(g_ + std::to_string(i + 1)); @@ -1218,17 +1218,17 @@ namespace spot formula phi_s; { std::vector res; - for (int i = 0; i < n; ++i) + for (unsigned i = 0; i < n; ++i) res.push_back(formula::Not(r[i])); theta_e = formula::And(res); res.clear(); - for (int i = 0; i < n; ++i) + for (unsigned i = 0; i < n; ++i) res.push_back(formula::Not(g[i])); theta_s = formula::And(res); res.clear(); - for (int i = 0; i < n; ++i) + for (unsigned i = 0; i < n; ++i) { formula left = formula::Xor(r[i], g[i]); formula right = formula::Equiv(r[i], formula::X(r[i])); @@ -1237,9 +1237,9 @@ namespace spot psi_e = formula::And(res); res.clear(); - for (int i = 0; i < n; ++i) + for (unsigned i = 0; i < n; ++i) { - for (int j = 0; j < i; ++j) + for (unsigned j = 0; j < i; ++j) res.push_back(formula::Not(formula::And({g[i], g[j]}))); formula left = formula::Equiv(r[i], g[i]); formula right = formula::Equiv(g[i], formula::X(g[i])); @@ -1248,7 +1248,7 @@ namespace spot psi_s = formula::And(res); res.clear(); - for (int i = 0; i < n; ++i) + for (unsigned i = 0; i < n; ++i) { formula f = formula::Not(formula::And({r[i], g[i]})); res.push_back(formula::G(formula::F(f))); @@ -1256,7 +1256,7 @@ namespace spot phi_e = formula::And(res); res.clear(); - for (int i = 0; i < n; ++i) + for (unsigned i = 0; i < n; ++i) { res.push_back(formula::G(formula::F(formula::Equiv(r[i], g[i])))); } @@ -1267,9 +1267,9 @@ namespace spot if (!strict_) { + formula left = formula::And({formula::G(psi_e), phi_e}); formula imp = - formula::Implies(formula::And({formula::G(psi_e), phi_e}), - formula::And({formula::G(psi_s), phi_s})); + formula::Implies(left, formula::And({formula::G(psi_s), phi_s})); return formula::Implies(theta_e, formula::And({theta_s, imp})); } else From ee55dabfaaba3754c4a7ac3c5fd22655f75612d9 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Tue, 5 Jul 2022 23:56:36 +0200 Subject: [PATCH 106/337] * spot/mc/cndfs.hh: Fix a unused variable warning in NDEBUG. --- spot/mc/cndfs.hh | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/spot/mc/cndfs.hh b/spot/mc/cndfs.hh index 9b414764f..02768144b 100644 --- a/spot/mc/cndfs.hh +++ b/spot/mc/cndfs.hh @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2015, 2016, 2017, 2018, 2019, 2020 Laboratoire de Recherche et +// Copyright (C) 2015-2020, 2022 Laboratoire de Recherche et // Developpement de l'Epita // // This file is part of Spot, a model checking library. @@ -191,9 +191,7 @@ namespace spot { // Try to insert the new state in the shared map. auto it = map_.insert(s); - bool b = it.isnew(); - - SPOT_ASSERT(!b); // should never be new in a red DFS + SPOT_ASSERT(!it.isnew()); // should never be new in a red DFS bool red = ((*it)).colors->red.load(); bool cyan = ((*it)).colors->l[tid_].cyan; bool in_Rp = ((*it)).colors->l[tid_].is_in_Rp; From 69eba6fd9a699829590d525c8b10382efaece8ef Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Wed, 6 Jul 2022 16:34:01 +0200 Subject: [PATCH 107/337] bloemen: fix a unused variable warning As discussed in #510. * spot/mc/bloemen_ec.hh: Here. --- spot/mc/bloemen_ec.hh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/spot/mc/bloemen_ec.hh b/spot/mc/bloemen_ec.hh index c31f0231f..9ffb32f5c 100644 --- a/spot/mc/bloemen_ec.hh +++ b/spot/mc/bloemen_ec.hh @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2015, 2016, 2017, 2018, 2019, 2020 Laboratoire de Recherche et +// Copyright (C) 2015-2020, 2022 Laboratoire de Recherche et // Developpement de l'Epita // // This file is part of Spot, a model checking library. @@ -556,8 +556,8 @@ namespace spot { auto root = uf_.find(w.second); - std::lock_guard lock(w.second->acc_mutex_); - scc_acc = w.second->acc; + std::lock_guard lock(root->acc_mutex_); + scc_acc = root->acc; } // cycle found in SCC and it contains acceptance condition From 1cfb4a14ec0a938bba1b13a92fc160683a4e0908 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Wed, 6 Jul 2022 17:11:55 +0200 Subject: [PATCH 108/337] bloemen: simplify style Fixes #510. * spot/mc/bloemen_ec.hh: Here. --- spot/mc/bloemen_ec.hh | 29 ++++++++++------------------- 1 file changed, 10 insertions(+), 19 deletions(-) diff --git a/spot/mc/bloemen_ec.hh b/spot/mc/bloemen_ec.hh index 9ffb32f5c..6e581a0ac 100644 --- a/spot/mc/bloemen_ec.hh +++ b/spot/mc/bloemen_ec.hh @@ -251,7 +251,7 @@ namespace spot uf_element* q; uf_element* r; - while (true) + do { a_root = find(a); b_root = find(b); @@ -261,28 +261,24 @@ namespace spot // Update acceptance condition { std::lock_guard rlock(a_root->acc_mutex_); - a_root->acc |= acc; acc |= a_root->acc; + a_root->acc = acc; } while (a_root->parent.load() != a_root) { a_root = find(a_root); std::lock_guard rlock(a_root->acc_mutex_); - a_root->acc |= acc; acc |= a_root->acc; + a_root->acc = acc; } return acc; } r = std::max(a_root, b_root); q = std::min(a_root, b_root); - - if (!lock_root(q)) - continue; - - break; } + while (!lock_root(q)); uf_element* a_list = lock_list(a); if (a_list == nullptr) @@ -329,9 +325,8 @@ namespace spot { std::lock_guard rlock(r->acc_mutex_); std::lock_guard qlock(q->acc_mutex_); - q->acc |= acc; - r->acc |= q->acc; - acc |= r->acc; + acc |= r->acc | q->acc; + r->acc = q->acc = acc; } while (r->parent.load() != r) @@ -339,8 +334,8 @@ namespace spot r = find(r); std::lock_guard rlock(r->acc_mutex_); std::lock_guard qlock(q->acc_mutex_); - r->acc |= q->acc; - acc |= r->acc; + acc |= r->acc | q->acc; + r->acc = acc; } unlock_list(a_list); @@ -360,9 +355,7 @@ namespace spot a_status = a->list_status_.load(); if (a_status == list_status::BUSY) - { - return a; - } + return a; if (a_status == list_status::DONE) break; @@ -407,9 +400,7 @@ namespace spot b_status = b->list_status_.load(); if (b_status == list_status::BUSY) - { - return b; - } + return b; if (b_status == list_status::DONE) break; From 5b8350bc9b8e5440d35636c16867e04a2e3aae10 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Thu, 7 Jul 2022 15:51:11 +0200 Subject: [PATCH 109/337] rpm: omit *.la files * spot.spec.in: It seems RedHat does not distribute *.la files anymore. --- spot.spec.in | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/spot.spec.in b/spot.spec.in index 238647606..714d8589e 100755 --- a/spot.spec.in +++ b/spot.spec.in @@ -71,16 +71,16 @@ logic (LTL & PSL). %files -n libspot %{_libdir}/libbddx.a -%{_libdir}/libbddx.la +%exclude %{_libdir}/libbddx.la %{_libdir}/libbddx.so* %{_libdir}/libspot.a -%{_libdir}/libspot.la +%exclude %{_libdir}/libspot.la %{_libdir}/libspot.so* %{_libdir}/libspotgen.a -%{_libdir}/libspotgen.la +%exclude %{_libdir}/libspotgen.la %{_libdir}/libspotgen.so* %{_libdir}/libspotltsmin.a -%{_libdir}/libspotltsmin.la +%exclude %{_libdir}/libspotltsmin.la %{_libdir}/libspotltsmin.so* %license COPYING %doc AUTHORS COPYING NEWS README THANKS @@ -121,7 +121,7 @@ temporal logic (LTL & PSL). %dir %{python3_sitearch}/spot %{python3_sitearch}/spot/* %{python3_sitearch}/_buddy.*.a -%{python3_sitearch}/_buddy.*.la +%exclude %{python3_sitearch}/_buddy.*.la %{python3_sitearch}/_buddy.*.so %license COPYING %doc AUTHORS COPYING NEWS README THANKS From 3f333792ffac814d6961e2de55a95f568581a241 Mon Sep 17 00:00:00 2001 From: Florian Renkin Date: Wed, 22 Jun 2022 10:25:35 +0200 Subject: [PATCH 110/337] Add a procedure that detects if an automaton is parity-type * spot/twaalgos/genem.cc, spot/twaalgos/genem.hh: add detection of edges that are in at least one accepting cycle. * spot/twaalgos/toparity.cc, spot/twaalgos/toparity.hh: add parity_type_to_parity and buchi_type_to_buchi. --- spot/twaalgos/genem.cc | 77 +++++++++++ spot/twaalgos/genem.hh | 30 +++++ spot/twaalgos/toparity.cc | 276 ++++++++++++++++++++++++++++++++++++++ spot/twaalgos/toparity.hh | 28 +++- 4 files changed, 410 insertions(+), 1 deletion(-) diff --git a/spot/twaalgos/genem.cc b/spot/twaalgos/genem.cc index 51b2ea903..237b10118 100644 --- a/spot/twaalgos/genem.cc +++ b/spot/twaalgos/genem.cc @@ -309,5 +309,82 @@ namespace spot return !scc_split_check_filtered(upper_si, forced_acc, callback, {}); } + // return ⊤ if there exists at least one accepting transition. + static bool + accepting_transitions_aux(const scc_info &si, unsigned scc, + const acc_cond acc, + acc_cond::mark_t removed_colors, + acc_cond::mark_t tocut, + std::vector &accepting_transitions, + const bitvect& kept) + { + bool result = false; + scc_and_mark_filter filt(si, scc, tocut, kept); + filt.override_acceptance(acc); + scc_info upper_si(filt, scc_info_options::ALL); + for (unsigned sc = 0; sc < upper_si.scc_count(); ++sc) + result |= accepting_transitions_scc(upper_si, sc, acc, removed_colors, + accepting_transitions, kept); + return result; + } + bool + accepting_transitions_scc(const scc_info &si, unsigned scc, + const acc_cond aut_acc, + acc_cond::mark_t removed_colors, + std::vector& accepting_transitions, + const bitvect& kept) + { + // The idea is the same as in is_scc_empty() + bool result = false; + acc_cond::mark_t sets = si.acc_sets_of(scc); + acc_cond acc = aut_acc.restrict_to(sets); + acc = acc.remove(si.common_sets_of(scc), false); + + auto inner_edges = si.inner_edges_of(scc); + + if (si.is_trivial(scc)) + return false; + if (acc.is_t() || acc.accepting(acc.get_acceptance().used_sets())) + { + for (auto& e : inner_edges) + if ((e.acc & removed_colors) == acc_cond::mark_t {}) + accepting_transitions[si.get_aut()->edge_number(e)] = true; + return true; + } + else if (acc.is_f()) + return false; + acc_cond::acc_code rest = acc_cond::acc_code::f(); + for (const acc_cond& disjunct: acc.top_disjuncts()) + if (acc_cond::mark_t fu = disjunct.fin_unit()) + result |= accepting_transitions_aux(si, scc, acc.remove(fu, true), + (removed_colors | fu), fu, + accepting_transitions, kept); + else + rest |= disjunct.get_acceptance(); + if (!rest.is_f()) + { + acc_cond::mark_t m = { (unsigned) acc.fin_one() }; + result |= accepting_transitions_aux(si, scc, acc.remove(m, true), + (removed_colors | m), m, accepting_transitions, + kept); + result |= accepting_transitions_scc(si, scc, acc.remove(m, false), + removed_colors, accepting_transitions, + kept); + } + return result; + } + + std::vector + accepting_transitions(const const_twa_graph_ptr aut, acc_cond cond) + { + auto aut_vector_size = aut->edge_vector().size(); + std::vector result(aut_vector_size, false); + auto kept = make_bitvect(aut_vector_size); + scc_info si(aut); + for (unsigned scc = 0; scc < si.scc_count(); ++scc) + accepting_transitions_scc(si, scc, cond, {}, result, *kept); + delete kept; + return result; + } } diff --git a/spot/twaalgos/genem.hh b/spot/twaalgos/genem.hh index 3c3e5de51..3fefcdc77 100644 --- a/spot/twaalgos/genem.hh +++ b/spot/twaalgos/genem.hh @@ -110,4 +110,34 @@ namespace spot SPOT_API void generic_emptiness_check_select_version(const char* emversion = nullptr); + /// \ingroup emptiness_check_algorithms + /// + /// Give the set of transitions contained in + /// an accepting cycle of the SCC \a scc of \a aut. + /// + /// \param si scc_info used to describle the automaton + /// \param scc SCC to consider + /// \param aut_acc Acceptance condition used for this SCC + /// \param removed_colors A set of colors that can't appear on a transition + /// \param accepting_transitions The result. Must be a vector of size at least + /// the max index + 1 of a transition of the SCC scc and the value of each + /// index of a transition of this SCC must be set to false + /// \param kept A list of booleans that say if a transition is kept even if + /// it does not have an element of removed_colors + /// \return True if there is an accepting transition + SPOT_API bool + accepting_transitions_scc(const scc_info &si, unsigned scc, + const acc_cond aut_acc, + acc_cond::mark_t removed_colors, + std::vector& accepting_transitions, + const bitvect& kept); + + /// \ingroup emptiness_check_algorithms + /// + /// Give the set of transitions contained in an accepting cycle of \a aut. + /// \param aut Automaton to process + /// \param cond Acceptance condition associated + SPOT_API std::vector + accepting_transitions(const const_twa_graph_ptr aut, acc_cond cond); + } diff --git a/spot/twaalgos/toparity.cc b/spot/twaalgos/toparity.cc index 0b46e6224..3c3f03607 100644 --- a/spot/twaalgos/toparity.cc +++ b/spot/twaalgos/toparity.cc @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -39,6 +40,281 @@ namespace spot { + inline void + assign_color(acc_cond::mark_t &mark, unsigned col) + { + if (col < SPOT_MAX_ACCSETS) + mark.set(col); + else + acc_cond::mark_t{SPOT_MAX_ACCSETS}; + } + + // Describes if we want to test if it is a Büchi, co-Büchi,… type automaton. + enum cond_kind + { + BUCHI, + CO_BUCHI, + // A parity condition with a Inf as outermost term + INF_PARITY, + // A parity condition with a Fin as outermost term + FIN_PARITY + }; + + // This enum describes the status of an edge + enum edge_status + { + NOT_MARKED, + MARKED, + IMPOSSIBLE, + LINK_SCC + }; + + static bool + cond_type_main_aux(const twa_graph_ptr &aut, const cond_kind kind, + const bool need_equivalent, + std::vector &status, + std::vector &res_colors, + acc_cond &new_cond, bool &was_able_to_color) + { + auto &ev = aut->edge_vector(); + const auto ev_size = ev.size(); + const auto aut_init = aut->get_init_state_number(); + was_able_to_color = false; + status = std::vector(ev_size, NOT_MARKED); + res_colors = std::vector(ev_size); + // Used by accepting_transitions_scc. + auto keep = std::unique_ptr(make_bitvect(ev_size)); + keep->set_all(); + + // Number of edges colored by the procedure, used to test equivalence for + // parity + unsigned nb_colored = 0; + + // We need to say that a transition between 2 SCC doesn't have to get a + // color. + scc_info si(aut, aut_init, nullptr, nullptr, scc_info_options::NONE); + status[0] = LINK_SCC; + if (si.scc_count() > 1) + { + for (unsigned edge_number = 1; edge_number < ev_size; ++edge_number) + { + auto &e = ev[edge_number]; + if (si.scc_of(e.src) != si.scc_of(e.dst)) + { + status[edge_number] = LINK_SCC; + ++nb_colored; + } + } + } + + // If we need to convert to (co-)Büchi, we have to search one accepting + // set. With parity there is no limit. + bool want_parity = kind == cond_kind::FIN_PARITY || + kind == cond_kind::INF_PARITY; + unsigned max_iter = want_parity ? -1U : 1; + + unsigned color = want_parity ? SPOT_MAX_ACCSETS - 1 : 0; + // Do we want always accepting transitions? + // Don't consider CO_BUCHI as it is done by Büchi + bool search_inf = kind != cond_kind::FIN_PARITY; + + using filter_data_t = std::pair &>; + + scc_info::edge_filter filter = + [](const twa_graph::edge_storage_t &t, unsigned, void *data) + -> scc_info::edge_filter_choice + { + auto &d = *static_cast(data); + // We only keep transitions that can be marked + if (d.second[d.first->edge_number(t)] == NOT_MARKED) + return scc_info::edge_filter_choice::keep; + else + return scc_info::edge_filter_choice::cut; + }; + std::vector not_decidable_transitions(ev_size, false); + auto aut_acc = aut->get_acceptance(); + auto aut_acc_comp = aut_acc.complement(); + for (unsigned iter = 0; iter < max_iter; ++iter) + { + // Share the code with Büchi-type + if (kind == CO_BUCHI) + std::swap(aut_acc, aut_acc_comp); + std::fill(not_decidable_transitions.begin(), + not_decidable_transitions.end(), false); + auto cond = acc_cond(search_inf ? aut_acc_comp : aut_acc); + auto filter_data = filter_data_t{aut, status}; + scc_info si(aut, aut_init, filter, &filter_data, + scc_info_options::TRACK_STATES); + bool worked = false; + unsigned ssc_size = si.scc_count(); + for (unsigned scc = 0; scc < ssc_size; ++scc) + { + // scc_info can detect that we will not be able to find an + // accepting/rejecting cycle. + if (!((search_inf && !si.is_accepting_scc(scc)) || + (!search_inf && !si.is_rejecting_scc(scc)))) + { + accepting_transitions_scc(si, scc, cond, {}, + not_decidable_transitions, *keep); + for (auto &e : si.inner_edges_of(scc)) + { + auto edge_number = aut->edge_number(e); + if (!not_decidable_transitions[edge_number]) + { + assert(!res_colors[edge_number]); + if (color != -1U) + assign_color(res_colors[edge_number], color); + was_able_to_color = true; + status[edge_number] = MARKED; + ++nb_colored; + keep->clear(edge_number); + worked = true; + } + } + } + } + + if (color-- == -1U) + break; + search_inf = !search_inf; + // If we were not able to add color, we have to add status 2 to + // remaining transitions. + if (!worked && !need_equivalent) + { + std::replace(status.begin(), status.end(), NOT_MARKED, IMPOSSIBLE); + break; + } + } + + acc_cond::acc_code new_code; + switch (kind) + { + case cond_kind::BUCHI: + new_code = acc_cond::acc_code::buchi(); + break; + case cond_kind::CO_BUCHI: + new_code = acc_cond::acc_code::cobuchi(); + break; + case cond_kind::INF_PARITY: + case cond_kind::FIN_PARITY: + new_code = acc_cond::acc_code::parity_max( + kind == cond_kind::INF_PARITY, SPOT_MAX_ACCSETS); + break; + } + + // We check parity + if (need_equivalent) + { + // For parity, it's equivalent if every transition has a color + // (status 1) or links 2 SCCs. + if (kind == cond_kind::INF_PARITY || kind == cond_kind::FIN_PARITY) + return nb_colored == ev_size - 1; + else + { + // For Büchi, we remove the transitions that have {0} in the + // result from aut and if there is an accepting cycle, res is not + // equivalent to aut. + // For co-Büchi, it's the same but we don't want to find a + // rejecting cycle. + using filter_data_t = std::pair; + + scc_info::edge_filter filter = + [](const twa_graph::edge_storage_t &t, unsigned, void *data) + -> scc_info::edge_filter_choice + { + auto &d = *static_cast(data); + if (d.second.get(d.first->edge_number(t))) + return scc_info::edge_filter_choice::keep; + else + return scc_info::edge_filter_choice::cut; + }; + + if (kind == CO_BUCHI) + aut->set_acceptance(acc_cond(aut_acc)); + + filter_data_t filter_data = {aut, *keep}; + scc_info si(aut, aut_init, filter, &filter_data); + si.determine_unknown_acceptance(); + const auto num_scc = si.scc_count(); + for (unsigned scc = 0; scc < num_scc; ++scc) + if (si.is_accepting_scc(scc)) + { + if (kind == CO_BUCHI) + aut->set_acceptance(acc_cond(aut_acc_comp)); + return false; + } + if (kind == CO_BUCHI) + aut->set_acceptance(acc_cond(aut_acc_comp)); + } + } + new_cond = acc_cond(new_code); + return true; + } + + static twa_graph_ptr + cond_type_main(const twa_graph_ptr &aut, const cond_kind kind, + bool &was_able_to_color) + { + std::vector res_colors; + std::vector status; + acc_cond new_cond; + if (cond_type_main_aux(aut, kind, true, status, res_colors, new_cond, + was_able_to_color)) + { + auto res = make_twa_graph(aut, twa::prop_set::all()); + auto &res_vector = res->edge_vector(); + unsigned rv_size = res_vector.size(); + for (unsigned i = 1; i < rv_size; ++i) + res_vector[i].acc = res_colors[i]; + res->set_acceptance(new_cond); + return res; + } + return nullptr; + } + + twa_graph_ptr + parity_type_to_parity(const twa_graph_ptr &aut) + { + bool odd_cond, max_cond; + bool parit = aut->acc().is_parity(max_cond, odd_cond); + // If it is parity, we just copy + if (parit) + { + if (!max_cond) + return change_parity(aut, parity_kind_max, parity_style_any); + auto res = make_twa_graph(aut, twa::prop_set::all()); + res->copy_acceptance_of(aut); + return res; + } + bool was_able_to_color; + // If the automaton is parity-type with a condition that has Inf as + // outermost term + auto res = cond_type_main(aut, cond_kind::INF_PARITY, was_able_to_color); + + // If it was impossible to find an accepting edge, it is perhaps possible + // to find a rejecting transition + if (res == nullptr && !was_able_to_color) + res = cond_type_main(aut, cond_kind::FIN_PARITY, was_able_to_color); + if (res) + reduce_parity_here(res); + return res; + } + + twa_graph_ptr + buchi_type_to_buchi(const twa_graph_ptr &aut) + { + bool useless; + return cond_type_main(aut, cond_kind::BUCHI, useless); + } + + twa_graph_ptr + co_buchi_type_to_co_buchi(const twa_graph_ptr &aut) + { + bool useless; + return cond_type_main(aut, cond_kind::CO_BUCHI, useless); + } + // Old version of IAR. namespace { diff --git a/spot/twaalgos/toparity.hh b/spot/twaalgos/toparity.hh index 7d2701581..6aecf7659 100644 --- a/spot/twaalgos/toparity.hh +++ b/spot/twaalgos/toparity.hh @@ -156,4 +156,30 @@ namespace spot SPOT_API twa_graph_ptr // deprecated since Spot 2.9 iar_maybe(const const_twa_graph_ptr& aut, bool pretty_print = false); -} // namespace spot + /// \ingroup twa_acc_transform + /// \brief Convert an automaton into a parity max automaton preserving + /// structure when possible. + /// + /// Return nullptr if no such automaton is found. + /// \param aut Automaton that we want to convert + SPOT_API twa_graph_ptr + parity_type_to_parity(const twa_graph_ptr &aut); + + /// \ingroup twa_acc_transform + /// \brief Convert an automaton into a Büchi automaton preserving structure + /// when possible. + /// + /// Return nullptr if no such automaton is found. + /// \param aut Automaton that we want to convert + SPOT_API twa_graph_ptr + buchi_type_to_buchi(const twa_graph_ptr &aut); + + /// \ingroup twa_acc_transform + /// \brief Convert an automaton into a co-Büchi automaton preserving structure + /// when possible. + /// + /// Return nullptr if no such automaton is found. + /// \param aut Automaton that we want to convert + SPOT_API twa_graph_ptr + co_buchi_type_to_co_buchi(const twa_graph_ptr &aut); +} From 6dd99e049b13ad168cbf60338e5733c8e08e803b Mon Sep 17 00:00:00 2001 From: Florian Renkin Date: Wed, 22 Jun 2022 10:27:33 +0200 Subject: [PATCH 111/337] to_parity: Rewrite the function and add new transformations * spot/twaalgos/synthesis.cc: Now needs to call reduce_parity. * spot/twaalgos/toparity.cc, spot/twaalgos/toparity.hh: here. * spot/twaalgos/zlktree.hh: make zielonka_node public * tests/core/ltlsynt.test, tests/python/games.ipynb, tests/python/synthesis.ipynb, tests/python/toparity.py: update tests --- spot/twaalgos/synthesis.cc | 2 +- spot/twaalgos/toparity.cc | 4131 ++++++++++++++++++++------------- spot/twaalgos/toparity.hh | 69 +- spot/twaalgos/zlktree.hh | 2 +- tests/core/ltlsynt.test | 22 +- tests/python/games.ipynb | 999 ++++---- tests/python/synthesis.ipynb | 4224 +++++++++++++++++----------------- tests/python/toparity.py | 133 +- 8 files changed, 5226 insertions(+), 4356 deletions(-) diff --git a/spot/twaalgos/synthesis.cc b/spot/twaalgos/synthesis.cc index 41aa736e2..6fb126ff8 100644 --- a/spot/twaalgos/synthesis.cc +++ b/spot/twaalgos/synthesis.cc @@ -1005,7 +1005,7 @@ namespace spot if (gi.s == algo::LAR) { dpa = to_parity(aut); - // reduce_parity is called by to_parity() + reduce_parity_here(dpa, false); } else if (gi.s == algo::LAR_OLD) { diff --git a/spot/twaalgos/toparity.cc b/spot/twaalgos/toparity.cc index 3c3f03607..c936ef57b 100644 --- a/spot/twaalgos/toparity.cc +++ b/spot/twaalgos/toparity.cc @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2018-2020 Laboratoire de Recherche et Développement +// Copyright (C) 2018-2020, 2022 Laboratoire de Recherche et Développement // de l'Epita. // // This file is part of Spot, a model checking library. @@ -18,28 +18,49 @@ // along with this program. If not, see . #include "config.h" -#include -#include -#include -#include -#include +#include #include #include #include -#include -#include -#include #include #include -#include +#include #include -#include +#include + +#include +#include +#include + +namespace std +{ + template + inline void hash_combine(size_t &seed, T const &v) + { + seed ^= std::hash()(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2); + } + + template + struct hash> + { + typedef vector argument_type; + typedef std::size_t result_type; + result_type operator()(argument_type const &in) const + { + size_t size = in.size(); + size_t seed = 0; + for (size_t i = 0; i < size; i++) + // Combine the hash of the current vector with the hashes of the + // previous ones + hash_combine(seed, in[i]); + return seed; + } + }; +} -#include namespace spot { - inline void assign_color(acc_cond::mark_t &mark, unsigned col) { @@ -76,7 +97,7 @@ namespace spot std::vector &res_colors, acc_cond &new_cond, bool &was_able_to_color) { - auto &ev = aut->edge_vector(); + auto& ev = aut->edge_vector(); const auto ev_size = ev.size(); const auto aut_init = aut->get_init_state_number(); was_able_to_color = false; @@ -98,7 +119,7 @@ namespace spot { for (unsigned edge_number = 1; edge_number < ev_size; ++edge_number) { - auto &e = ev[edge_number]; + auto& e = ev[edge_number]; if (si.scc_of(e.src) != si.scc_of(e.dst)) { status[edge_number] = LINK_SCC; @@ -260,7 +281,7 @@ namespace spot std::vector status; acc_cond new_cond; if (cond_type_main_aux(aut, kind, true, status, res_colors, new_cond, - was_able_to_color)) + was_able_to_color)) { auto res = make_twa_graph(aut, twa::prop_set::all()); auto &res_vector = res->edge_vector(); @@ -315,10 +336,2285 @@ namespace spot return cond_type_main(aut, cond_kind::CO_BUCHI, useless); } - // Old version of IAR. +// New version for paritizing + +// data type used in a memory for CAR and IAR. +// TAR is a particular case +#if MAX_ACCSETS < UCHAR_MAX + using memory_type = unsigned char; + #define MAX_MEM_ELEM UCHAR_MAX +#elif MAX_ACCSETS < USHRT_MAX + using memory_type = unsigned short; + #define MAX_MEM_ELEM USHRT_MAX +#else + using memory_type = unsigned; + #define MAX_MEM_ELEM UINT_MAX +#endif + + template + using memory = std::vector; + + // Maps a state of the automaton to a parity_state + class state_2_lar + { + public: + // If to_parity wants to find the newest or the oldest or both, we + // adapt the algorithms + enum memory_order + { + ONLY_NEWEST, + ONLY_OLDEST, + BOTH + }; + + class node + { + public: + // Color that lead to this node + memory_type color_; + // For a state in states_, any child can be taken. While a unique state + // could be used when we search an existing state, here we have + // to consider opt_.search_ex = False, opt_.use_last_post_process = True. + // This configuration can lead to 2 states in the same node. For example + // if we add [0 1 | 2 3] and [0 1 | 3 2] where '|' says which part of the + // memory can be reordered (right). + std::vector states_; + std::vector children_; + // A timer used to detect which child is the oldest + unsigned timer_; + + node() : node(MAX_MEM_ELEM, -1U) + { + } + + node(memory_type val, unsigned timer) : color_(val), timer_(timer) + { + } + + ~node() + { + for (auto c : children_) + delete c; + } + }; + + std::vector nodes_; + memory_order order_; + unsigned timer_; + + state_2_lar() : timer_(0) + { + } + + void + init(unsigned nb_states, memory_order order) + { + order_ = order; + nodes_.reserve(nb_states); + for (unsigned i = 0; i < nb_states; ++i) + nodes_.push_back(new node()); + } + + ~state_2_lar() + { + for (auto x : nodes_) + delete x; + } + + void + add_new_path(unsigned state, const memory &vals, + unsigned res_state, unsigned nb_seen) + { + ++timer_; + node *current = nodes_[state]; + // Position in vals + int pos = vals.size() - 1; + while (true) + { + if (pos == (int)(nb_seen - 1)) + current->states_.push_back(res_state); + if (pos == -1) + break; + const unsigned current_val = vals[pos]; + auto child = std::find_if(current->children_.begin(), + current->children_.end(), + [&](const auto &child) constexpr + { return child->color_ == current_val; }); + // If we don't have a child with the corresponding color… + if (child == current->children_.end()) + { + auto nn = new node(current_val, timer_); + current->children_.push_back(nn); + current = nn; + } + else + { + // If get_compatible_state wants the most recent + // (opt_.use_last or opt_.use_last_post_process), we help this + // function by moving this node to the last position. + // Otherwise the oldest leaf will be reachable from the first child. + // If we have use_last = false and use_last_post_process = true, + // we need to access to the oldest and newest child. As the tree is + // smallest when we want to access to the oldest value, we continue + // to move the value to the last position and compute the oldest + // child in get_compatible_state. + if (order_ != memory_order::ONLY_OLDEST) + { + std::iter_swap(child, current->children_.end() - 1); + current = current->children_.back(); + } + else + current = *child; + } + --pos; + } + } + + unsigned + get_compatible_state(unsigned state, const memory &m, + unsigned seen_nb, + bool use_last) const + { + int pos = m.size() - 1; + unsigned res = -1U; + node *current = nodes_[state]; + while (true) + { + const auto ¤t_states = current->states_; + if (!current_states.empty()) + res = use_last ? current_states.back() : current_states.front(); + + const auto ¤t_children = current->children_; + if (current_children.empty()) + { + assert(current->color_ == MAX_MEM_ELEM || pos == -1); + return res; + } + // If we are in the part of the memory where the order does not matter, + // we just take the oldest/newest state. + if (pos < (int)seen_nb) + { + if (order_ == BOTH) + { + if (!use_last) + current = *std::min_element( + current_children.begin(), current_children.end(), + [](const auto &x, const auto &y) constexpr + { return x->timer_ < y->timer_; }); + else + current = current_children.back(); + } + else + { + // add_new_path constructed the tree such that the oldest/newest + // leaf is reachable from the first child. + current = use_last ? current_children.back() + : current_children.front(); + } + } + else + { + auto current_val = m[pos]; + auto ch = std::find_if( + current_children.begin(), current_children.end(), + [&](const auto &x) constexpr + { return x->color_ == current_val; }); + if (ch != current_children.end()) + current = *ch; + else + return -1U; + } + --pos; + } + } + }; + + class to_parity_generator + { + private: + class relation + { + public: + // Size of the matrix + unsigned size_; + // A line/column is indexed by a partial memory + const std::vector> labels_; + // Matrix such that vals_[x][y] = ⊤ ⇔ vals_[x] > vals_[y] + std::vector vals_; + + inline bool + at(unsigned i, unsigned j) const + { + return vals_.at(i * size_ + j); + } + + inline void + set(unsigned i, unsigned j, bool val) + { + vals_[i * size_ + j] = val; + } + + // Test if m1 ⊆ m2 + bool is_included(memory m1, memory m2) + { + if (m1.size() > m2.size()) + return false; + assert(std::is_sorted(m1.begin(), m1.end())); + assert(std::is_sorted(m2.begin(), m2.end())); + memory diff; + std::set_union(m1.begin(), m1.end(), m2.begin(), m2.end(), + std::inserter(diff, diff.begin())); + return diff.size() == m2.size(); + } + + // Supposes that there is no duplicates. + relation(std::vector> &labels) + : size_(labels.size()), labels_(labels) + { + unsigned long long size_vals; + if (__builtin_umulll_overflow(size_, size_, &size_vals)) + throw std::bad_alloc(); + vals_ = std::vector(size_vals); + for (unsigned i = 0; i < size_; ++i) + for (unsigned j = 0; j < size_; ++j) + // We cannot have vals_[i] > vals_[j] and vals_[j] > vals_[i] + if (!at(j, i)) + set(i, j, (i != j && is_included(labels_[j], labels_[i]))); + // Remove x > z if ∃y s.t. x > y > z + simplify_relation(); + } + + // Apply a transitive reduction + void + simplify_relation() + { + for (unsigned j = 0; j < size_; ++j) + for (unsigned i = 0; i < size_; ++i) + if (at(i, j)) + for (unsigned k = 0; k < size_; ++k) + if (at(j, k)) + set(i, k, false); + } + + template + void + add_to_res_(const memory ¤t, + const memory &other, + memory &result) + { + assert(std::is_sorted(current.begin(), current.end())); + assert(std::is_sorted(other.begin(), other.end())); + std::set_difference(current.begin(), current.end(), + other.begin(), other.end(), + std::inserter(result, result.end())); + } + + // Gives a compatible ordered partial memory for the partial memory + // partial_mem. + memory + find_order(const memory &partial_mem) + { + // Now if we want to find an order, we start from the line + // that contains partial_mem in the matrix, we find a more restrictive + // order and add the value that are used in partial_mem but not in this + // "child" value. + // The call to simplify_relation implies that we are sure we have + // used the longest possible path. + memory result; + auto elem = std::find(labels_.begin(), labels_.end(), partial_mem); + assert(elem != labels_.end()); + // Line that contains partial_mem + unsigned i = std::distance(labels_.begin(), elem); + while (true) + { + // The interval corresponding to the line i + auto vals_i_begin = vals_.begin() + (i * size_); + auto vals_i_end = vals_i_begin + size_; + // End of line i + auto child = std::find(vals_i_begin, vals_i_end, true); + // If there is a more restrictive memory, we use this "child" + if (child != vals_i_end) + { + unsigned child_pos = std::distance(vals_i_begin, child); + add_to_res_(labels_[i], labels_[child_pos], result); + i = child_pos; + } + // If there is no more restrictive memory, we just add the remaining + // memory. + else + { + add_to_res_(labels_[i], {}, result); + break; + } + } + // The order want that a value that is in the lowest value is a + // the head. + std::reverse(result.begin(), result.end()); + return result; + } + }; + + class scc_info_to_parity + { + private: + scc_info si_; + + public: + scc_info_to_parity(const const_twa_graph_ptr aut, + const acc_cond::mark_t removed = {}) + : si_(scc_and_mark_filter(aut, removed)) + { + } + + scc_info_to_parity(const scc_info lower_si, + const std::shared_ptr keep) + : si_(scc_and_mark_filter(lower_si, 0, acc_cond::mark_t{}, *keep), + scc_info_options::NONE) + { + } + + std::vector + split_aut(acc_cond::mark_t mark = {}) + { + auto aut = si_.get_aut(); + const auto num_scc = si_.scc_count(); + const unsigned aut_num_states = aut->num_states(); + std::vector res(num_scc); + std::vector aut_to_res; + aut_to_res.reserve(aut_num_states); + for (auto &g : res) + { + g = make_twa_graph(aut->get_dict()); + g->copy_ap_of(aut); + g->copy_acceptance_of(aut); + g->prop_copy(aut, {true, true, false, false, false, true}); + auto orig = new std::vector(); + g->set_named_prop("original-states", orig); + } + const auto tp_orig_aut = + aut->get_named_prop>("original-states"); + for (unsigned i = 0; i < aut_num_states; ++i) + { + unsigned scc_i = si_.scc_of(i); + auto &g = res[scc_i]; + unsigned ns = g->new_state(); + unsigned ori = tp_orig_aut ? (*tp_orig_aut)[i] : i; + auto pr = g->get_named_prop>("original-states"); + pr->push_back(ori); + aut_to_res.push_back(ns); + } + + for (auto &e : aut->edges()) + { + unsigned src_scc = si_.scc_of(e.src); + unsigned dst_scc = si_.scc_of(e.dst); + if (src_scc == dst_scc && !(e.acc & mark)) + res[src_scc]->new_edge(aut_to_res[e.src], aut_to_res[e.dst], + e.cond, e.acc); + } + return res; + } + + std::vector + split_aut(const std::shared_ptr &keep) + { + auto aut = si_.get_aut(); + const auto num_scc = si_.scc_count(); + const unsigned aut_num_states = aut->num_states(); + std::vector res(num_scc); + std::vector aut_to_res; + aut_to_res.reserve(aut_num_states); + for (auto &g : res) + { + g = make_twa_graph(aut->get_dict()); + g->copy_ap_of(aut); + g->copy_acceptance_of(aut); + g->prop_copy(aut, {true, true, false, false, false, true}); + auto orig = new std::vector(); + g->set_named_prop("original-states", orig); + } + const auto tp_orig_aut = + aut->get_named_prop>("original-states"); + for (unsigned i = 0; i < aut_num_states; ++i) + { + unsigned scc_i = si_.scc_of(i); + auto &g = res[scc_i]; + unsigned ns = g->new_state(); + unsigned ori = tp_orig_aut ? (*tp_orig_aut)[i] : i; + auto pr = g->get_named_prop>("original-states"); + pr->push_back(ori); + aut_to_res.push_back(ns); + } + + const auto &ev = si_.get_aut()->edge_vector(); + auto ev_size = ev.size(); + for (unsigned i = 0; i < ev_size; ++i) + if (keep->get(i)) + { + auto &e = ev[i]; + unsigned scc_src = si_.scc_of(e.src); + if (scc_src == si_.scc_of(e.dst)) + res[scc_src]->new_edge(aut_to_res[e.src], aut_to_res[e.dst], + e.cond, e.acc); + } + return res; + } + + unsigned scc_count() + { + return si_.scc_count(); + } + + unsigned scc_of(unsigned state) + { + return si_.scc_of(state); + } + }; + + // Original automaton + const const_twa_graph_ptr aut_; + // Resulting parity automaton + twa_graph_ptr res_; + // options + to_parity_options opt_; + // nullptr if opt_.pretty_print is false + std::vector *names_ = nullptr; + // original_states. Is propagated if the original automaton has + // this named property + std::vector *orig_ = nullptr; + scc_info_to_parity si_; + bool need_purge_ = false; + // Tells if we are constructing a parity max odd + bool is_odd_ = false; + // min_color used in the automaton + 1 (result of max_set). + std::optional min_color_used_; + std::optional max_color_scc_; + std::optional max_color_used_; + std::vector state_to_res_; + std::vector res_to_aut_; + // Map a state of aut_ to every copy of this state. Used by a recursive call + // to to_parity by parity_prefix for example + std::vector> *state_to_nums_ = nullptr; + unsigned algo_used_ = 0; + + enum algorithm + { + CAR = 1, + IAR_RABIN = 1 << 1, + IAR_STREETT = 1 << 2, + TAR = 1 << 3, + RABIN_TO_BUCHI = 1 << 4, + STREETT_TO_COBUCHI = 1 << 5, + PARITY_TYPE = 1 << 6, + BUCHI_TYPE = 1 << 7, + CO_BUCHI_TYPE = 1 << 8, + PARITY_EQUIV = 1 << 9, + PARITY_PREFIX = 1 << 10, + PARITY_PREFIX_GENERAL = 1 << 11, + GENERIC_EMPTINESS = 1 << 12, + PARTIAL_DEGEN = 1 << 13, + ACC_CLEAN = 1 << 14, + NONE = 1 << 15 + }; + + static std::string + algorithm_to_str(const algorithm &algo) + { + switch (algo) + { + case CAR: + return "CAR"; + case IAR_RABIN: + return "IAR (Rabin)"; + case IAR_STREETT: + return "IAR (Streett)"; + case TAR: + return "TAR"; + case NONE: + return "None"; + case BUCHI_TYPE: + return "Büchi-type"; + case CO_BUCHI_TYPE: + return "co-Büchi-type"; + case PARITY_TYPE: + return "Parity-type"; + case PARITY_EQUIV: + return "Parity equivalent"; + case GENERIC_EMPTINESS: + return "Generic emptiness"; + case STREETT_TO_COBUCHI: + return "Streett to co-Büchi"; + case RABIN_TO_BUCHI: + return "Rabin to Büchi"; + case PARITY_PREFIX: + return "Parity-prefix"; + case PARITY_PREFIX_GENERAL: + return "Parity-prefix general"; + case PARTIAL_DEGEN: + return "Partial degeneralization"; + case ACC_CLEAN: + return "acceptance cleanup"; + } + SPOT_UNREACHABLE(); + } + + template + struct to_parity_state + { + unsigned state; + unsigned state_scc; + memory mem; + + to_parity_state(unsigned st, unsigned st_scc, memory m) : + state(st), state_scc(st_scc), mem(m) + {} + + to_parity_state(const to_parity_state &) = default; + to_parity_state(to_parity_state &&) noexcept = default; + + ~to_parity_state() noexcept = default; + + bool + operator<(const to_parity_state &other) const + { + if (state < other.state) + return true; + if (state > other.state) + return false; + if (state_scc < other.state_scc) + return true; + if (state_scc > other.state_scc) + return false; + if (mem < other.mem) + return true; + return false; + } + + std::string + to_str(const algorithm &algo) const + { + std::stringstream s; + s << state; + // An empty memory does not mean that we don't use LAR. For example + // if the condition is true. We don't display a useless memory. + if (!mem.empty()) + { + s << ",["; + const char delim = ','; + s << ((unsigned)mem[0]); + auto mem_size = mem.size(); + for (unsigned i = 1; i < mem_size; ++i) + s << delim << ((unsigned)mem[i]); + s << ']'; + } + s << ',' << algorithm_to_str(algo); + return s.str(); + } + + bool operator==(const to_parity_state &other) const + { + return state == other.state && state_scc == other.state_scc + && mem == other.mem; + } + }; + + template + struct to_parity_hash + { + size_t operator()(to_parity_state const &tp) const + { + size_t result = std::hash>{}(tp.mem); + std::hash_combine(result, tp.state); + std::hash_combine(result, tp.state_scc); + return result; + } + }; + + template + unsigned + add_res_state(const algorithm &algo, const to_parity_state &ps) + { + if (names_) + names_->emplace_back(ps.to_str(algo)); + orig_->push_back(ps.state); + auto res = res_->new_state(); + if (opt_.datas) + { + algo_used_ |= algo; + ++opt_.datas->nb_states_created; + } + assert(ps.state < aut_->num_states()); + // state_to_res_ could be updated even if there is already a value. + // However it would lead to a result close to BSCC. + // So it is easier to show the influence of BSCC when the value is not + // changed when there is already a value. + if (state_to_res_[ps.state] == -1U) + state_to_res_[ps.state] = res; + if (state_to_nums_) + { + assert(ps.state < state_to_nums_->size()); + (*state_to_nums_)[ps.state].push_back(res); + } + res_to_aut_.push_back(ps.state); + return res; + } + + unsigned + add_res_edge(unsigned res_src, unsigned res_dst, + const acc_cond::mark_t &mark, const bdd &cond, + const bool can_merge_edge = true, + robin_hood::unordered_map* + edge_cache = nullptr) + { + // In a parity automaton we just need the maximal value + auto simax = mark.max_set(); + + const bool need_cache = edge_cache != nullptr && can_merge_edge; + long long key = 0; + if (need_cache) + { + constexpr auto unsignedsize = sizeof(unsigned) * 8; + key = (long long)simax << unsignedsize | res_dst; + auto cache_value = edge_cache->find(key); + if (cache_value != edge_cache->end()) + { + auto edge_index = cache_value->second; + auto &existing_edge = res_->edge_vector()[edge_index]; + existing_edge.cond |= cond; + return edge_index; + } + } + + auto simplified = mark ? acc_cond::mark_t{simax - 1} + : acc_cond::mark_t{}; + assert(res_src != -1U); + assert(res_dst != -1U); + + // No edge already done in the current scc. + if (!max_color_scc_.has_value()) + max_color_scc_.emplace(simax); + else + max_color_scc_.emplace(std::max(*max_color_scc_, simax)); + + // If it is the first edge of the result + if (!min_color_used_.has_value()) + { + assert(!max_color_used_.has_value()); + max_color_used_.emplace(simax); + min_color_used_.emplace(simax); + } + else + { + min_color_used_.emplace(std::min(*min_color_used_, simax)); + max_color_used_.emplace(std::max(*max_color_used_, simax)); + } + + auto new_edge_num = res_->new_edge(res_src, res_dst, cond, simplified); + if (need_cache) + edge_cache->emplace(std::make_pair(key, new_edge_num)); + + if (opt_.datas) + ++opt_.datas->nb_edges_created; + return new_edge_num; + } + + // copy + using coloring_function = + std::function; + + void + apply_copy_general(const const_twa_graph_ptr &sub_automaton, + const coloring_function &col_fun, + const algorithm &algo) + { + if (opt_.datas) + algo_used_ |= algo; + auto init_states = + sub_automaton->get_named_prop>("original-states"); + assert(init_states); + std::vector state_2_res_local; + auto sub_aut_ns = sub_automaton->num_states(); + state_2_res_local.reserve(sub_aut_ns); + for (unsigned state = 0; state < sub_aut_ns; ++state) + { + to_parity_state ps = {(*init_states)[state], state, {}}; + state_2_res_local.push_back(add_res_state(algo, ps)); + } + for (auto &e : sub_automaton->edges()) + { + auto new_mark = col_fun(e); + add_res_edge(state_2_res_local[e.src], state_2_res_local[e.dst], + new_mark, e.cond); + } + } + + // Case where one color is replaced by another. + // new_colors is a vector such that new_colors[i + 1] = j means that the + // color i is replaced by j. new_colors[0] is the value for an uncolored + // edge. + void + apply_copy(const const_twa_graph_ptr &sub_aut, + const std::vector &new_colors, + const algorithm &algo) + { + auto col_fun = [&](const twa_graph::edge_storage_t &edge) + { + acc_cond::mark_t res{}; + for (auto c : edge.acc.sets()) + { + auto new_col = new_colors[c + 1]; + if (new_col != -1U) + assign_color(res, new_col); + } + if (!edge.acc && new_colors[0] != -1U) + assign_color(res, new_colors[0]); + return res; + }; + apply_copy_general(sub_aut, col_fun, algo); + } + + // Case where new_color is a function such that edge_vector[i] should + // be colored by new_color[i]. + void + apply_copy_edge_index(const const_twa_graph_ptr &sub_aut, + const std::vector &new_color, + const algorithm &algo) + { + auto col_fun = [&](const twa_graph::edge_storage_t &edge) + { + auto res = new_color[sub_aut->edge_number(edge)]; + if (res == -1U) + return acc_cond::mark_t{}; + return acc_cond::mark_t{res}; + }; + apply_copy_general(sub_aut, col_fun, algo); + } + + // Create a memory for the first state created by apply_lar. + // If the algorithm is IAR, it also fills pairs_indices that + // contains the indices of the pairs that can be moved to the head of + // the memory. + template + memory + initial_memory_of(const const_twa_graph_ptr &sub_aut, + const std::vector &pairs, + std::vector, memory>> &relations) + { + unsigned init_state = sub_aut->get_init_state_number(); + if constexpr (algo == algorithm::CAR) + { + unsigned max_set = sub_aut->get_acceptance().used_sets().max_set(); + memory values(max_set); + std::iota(values.begin(), values.end(), 0); + if (opt_.force_order) + apply_move_heuristic(init_state, values, max_set, relations); + return values; + } + else if constexpr (algo == algorithm::TAR) + { + if (UINT_MAX < sub_aut->num_edges()) + { + throw std::runtime_error("Too many edges for TAR"); + } + const auto &ev = sub_aut->edge_vector(); + const auto ev_size = ev.size(); + memory values(ev_size - 1); + // 0 is not an edge number + std::iota(values.begin(), values.end(), 1); + if (opt_.force_order && sub_aut->num_states() > 1) + { + unsigned free_pos = 0; + // If a transition goes to state, it is at the head of the memory. + for (unsigned i = 1; i < ev_size; ++i) + if (ev[i].dst == init_state) + { + std::swap(values[i - 1], values[free_pos]); + ++free_pos; + } + } + return values; + } + else + { + static_assert(algo == IAR_RABIN || algo == IAR_STREETT); + memory values(pairs.size()); + std::iota(values.begin(), values.end(), 0); + if (opt_.force_order) + apply_move_heuristic(init_state, values, values.size(), relations); + return values; + } + } + + // LAR + algorithm + choose_lar(const acc_cond &scc_condition, + std::vector &pairs, + const unsigned num_edges) + { + std::vector pairs1, pairs2; + bool is_rabin_like = scc_condition.is_rabin_like(pairs1); + bool is_streett_like = scc_condition.is_streett_like(pairs2); + // If we cannot apply IAR and TAR and CAR are not used + if ((!(is_rabin_like || is_streett_like) || !opt_.iar) + && !(opt_.car || opt_.tar)) + throw std::runtime_error("to_parity needs CAR or TAR to process " + "a condition that is not a Rabin or Streett " + "condition or if IAR is not enabled"); + remove_duplicates(pairs1); + remove_duplicates(pairs2); + unsigned num_col = scc_condition.num_sets(); + + auto num_pairs1 = (opt_.iar && is_streett_like) ? pairs2.size() : -1UL; + auto num_pairs2 = (opt_.iar && is_rabin_like) ? pairs1.size() : -1UL; + + // In practice, if the number of pairs is bigger than the number of + // colors, it will create a color greater than SPOT_MAX_ACCSETS, so + // we don't consider that it is a Rabin condition. + // In this case, if CAR or TAR is not used, it will throw a Runtime + // Error. + + bool iar_overflow = false; + if ((num_pairs1 > MAX_MEM_ELEM) && (num_pairs2 > MAX_MEM_ELEM)) + { + num_pairs1 = num_pairs2 = -1U; + iar_overflow = true; + } + + const std::vector + number_elements = + { + (opt_.iar && is_streett_like) ? pairs2.size() : -1UL, + (opt_.iar && is_rabin_like) ? pairs1.size() : -1UL, + opt_.car ? num_col : -1UL, + opt_.tar ? num_edges : -1UL}; + constexpr std::array algos = {IAR_STREETT, IAR_RABIN, CAR, + TAR}; + int min_pos = std::distance(number_elements.begin(), + std::min_element(number_elements.begin(), + number_elements.end())); + + if (number_elements[min_pos] == -1U && iar_overflow) + throw std::runtime_error( + "Too many Rabin/Streett pairs, try to increase SPOT_MAX_ACCSETS"); + algorithm algo = algos[min_pos]; + if (algo == IAR_RABIN) + pairs = pairs1; + else if (algo == IAR_STREETT) + pairs = pairs2; + return algo; + } + + // Remove duplicates in pairs without changing the order. + static void + remove_duplicates(std::vector &pairs) + { + std::vector res; + res.reserve(pairs.size()); + for (auto &elem : pairs) + if (std::find(res.begin(), res.end(), elem) == res.end()) + res.emplace_back(elem); + pairs = res; + } + + template + acc_cond::mark_t + fin(const std::vector &pairs, unsigned k) + { + static_assert(algo == IAR_RABIN || algo == IAR_STREETT); + if constexpr (algo == IAR_RABIN) + return pairs[k].fin; + else + return pairs[k].inf; + } + + template + acc_cond::mark_t + inf(const std::vector &pairs, unsigned k) + { + static_assert(algo == IAR_RABIN || algo == IAR_STREETT); + if constexpr (algo == IAR_RABIN) + return pairs[k].inf; + else + return pairs[k].fin; + } + + template + std::vector, memory>> + find_relations(const const_twa_graph_ptr &sub_aut, + const std::vector &pairs, + const std::set &pairs_indices) + { + static_assert(algo == IAR_RABIN || algo == IAR_STREETT || algo == CAR); + const unsigned sub_aut_num_states = sub_aut->num_states(); + // Set of memory elements that can be at the head of the memory for + // a given state. + std::vector>> incomem(sub_aut_num_states); + // Add a mark with all colors/pairs to deal with the order of the + // original state + if constexpr (algo == algorithm::CAR) + { + auto ms = sub_aut->get_acceptance().used_sets().max_set(); + memory m(ms); + std::iota(m.begin(), m.end(), 0); + incomem[sub_aut->get_init_state_number()].insert(std::move(m)); + } + else if constexpr (algo == IAR_RABIN || algo == IAR_STREETT) + { + memory m(pairs_indices.begin(), pairs_indices.end()); + incomem[sub_aut->get_init_state_number()].insert(std::move(m)); + } + + for (auto &e : sub_aut->edges()) + { + auto e_sets = e.acc.sets(); + if constexpr (algo == algorithm::CAR) + incomem[e.dst].insert({e_sets.begin(), e_sets.end()}); + // IAR + else + { + memory parti; + for (unsigned k : pairs_indices) + if (e.acc & fin(pairs, k)) + parti.push_back(k); + incomem[e.dst].insert(parti); + } + } + std::vector, memory>> res; + res.reserve(sub_aut_num_states); + for (unsigned i = 0; i < sub_aut_num_states; ++i) + { + std::map, memory> ma; + // Memory incoming to state i. + std::vector> elem(incomem[i].begin(), + incomem[i].end()); + relation rel(elem); + for (auto &x : rel.labels_) + ma.insert({x, rel.find_order(x)}); + res.emplace_back(ma); + } + return res; + } + + void + apply_move_heuristic(unsigned state, memory &m, + unsigned nb_seen, + std::vector, + memory>> &relations) + { + // If we move 0 or 1 color we cannot change the order + if (nb_seen < 2) + return; + memory seen{m.begin(), m.begin() + nb_seen}; + const auto &new_prefix = relations[state][seen]; + + unsigned new_prefix_size = new_prefix.size(); + for (unsigned i = 0; i < new_prefix_size; ++i) + m[i] = new_prefix[i]; + } + + template + void + find_new_memory(unsigned state, memory &m, unsigned edge_number, + const acc_cond::mark_t &colors, + const std::vector &pairs, + const std::set &pairs_indices, + unsigned &nb_seen, + unsigned &h, + std::vector, memory>> &relations) + { + if constexpr (algo == TAR) + { + (void)state; + auto pos = std::find(m.begin(), m.end(), edge_number); + assert(pos != m.end()); + h = std::distance(m.begin(), pos); + std::rotate(m.begin(), pos, pos + 1); + } + else if constexpr (algo == CAR) + { + (void)edge_number; + for (auto k : colors.sets()) + { + auto it = std::find(m.begin(), m.end(), k); + // A color can exist in the automaton but not in the condition. + if (it != m.end()) + { + h = std::max(h, (unsigned)(it - m.begin()) + 1); + std::rotate(m.begin(), it, it + 1); + ++nb_seen; + } + } + if (opt_.force_order) + { + // apply_move_heuristic needs an increasing list of values + std::reverse(m.begin(), m.begin() + nb_seen); + apply_move_heuristic(state, m, nb_seen, relations); + } + } + else if constexpr (algo == IAR_RABIN || algo == IAR_STREETT) + { + (void)edge_number; + for (auto k = pairs_indices.rbegin(); k != pairs_indices.rend(); ++k) + if (colors & fin(pairs, *k)) + { + ++nb_seen; + auto it = std::find(m.begin(), m.end(), *k); + assert(it != m.end()); + // move the pair in front of the permutation + std::rotate(m.begin(), it, it + 1); + } + if (opt_.force_order) + { + // As with CAR, in relation the partial memory is sorted. That is + // why the previous loop use a reverse iterator. + assert(std::is_sorted(m.begin(), m.begin() + nb_seen)); + apply_move_heuristic(state, m, nb_seen, relations); + } + } + } + + template + void + compute_new_color_lar(const const_twa_graph_ptr &sub_aut, + const memory ¤t_mem, + const memory &new_perm, + unsigned &h, + const acc_cond::mark_t &edge_colors, + acc_cond::mark_t &acc, + const std::vector &pairs, + robin_hood::unordered_map& + acc_cache) + { + // This function should not be called with algo ∉ [CAR, IAR, TAR]. + static_assert(algo == CAR || algo == IAR_RABIN || algo == IAR_STREETT + || algo == TAR); + assert(!acc); + auto sub_aut_cond = sub_aut->acc(); + if constexpr (algo == CAR) + { + acc_cond::mark_t m(new_perm.begin(), new_perm.begin() + h); + auto cc = acc_cache.find(m); + bool rej; + if (cc != acc_cache.end()) + rej = cc->second; + else + { + rej = !sub_aut_cond.accepting(m); + acc_cache.insert({m, rej}); + } + unsigned value = 2 * h + rej - 1; + if (value != -1U) + assign_color(acc, value); + return; + } + else if constexpr (algo == TAR) + { + auto &edge_vector = sub_aut->edge_vector(); + acc_cond::mark_t acc_seen {}; + for (unsigned i = 0; i <= h; ++i) + acc_seen |= edge_vector[new_perm[i]].acc; + + auto cc = acc_cache.find(acc_seen); + bool rej; + if (cc != acc_cache.end()) + rej = cc->second; + else + { + rej = !sub_aut_cond.accepting(acc_seen); + acc_cache.insert({acc_seen, rej}); + } + + unsigned acc_col = 2 * h + rej - 1; + if (acc_col != -1U) + assign_color(acc, acc_col); + } + else + { + // IAR_RABIN produces a parity max even condition. If res_ + // is parity max odd, we add 1 to a transition to produce a parity max + // odd automaton. + unsigned delta_acc = ((algo == IAR_RABIN) && is_odd_) - 1; + + unsigned maxint = -1U; + for (int k = current_mem.size() - 1; k >= 0; --k) + { + unsigned pk = current_mem[k]; + + if (!inf(pairs, pk) || (edge_colors + & (pairs[pk].fin | pairs[pk].inf))) + { + maxint = k; + break; + } + } + + unsigned value; + if (maxint == -1U) + value = delta_acc; + else if (edge_colors & fin(pairs, current_mem[maxint])) + value = 2 * maxint + 2 + delta_acc; + else + value = 2 * maxint + 1 + delta_acc; + + if (value != -1U) + assign_color(acc, value); + } + } + + void + change_to_odd() + { + if (is_odd_) + return; + is_odd_ = true; + // We can reduce if we don't have an edge without color. + bool can_reduce = (min_color_used_.has_value() && *min_color_used_ != 0); + int shift; + + if (can_reduce) + shift = -1 * (*min_color_used_ - (*min_color_used_ % 2) + 1); + else + shift = 1; + + // If we cannot decrease and we already the the maximum color, we don't + // have to try. Constructs a mark_t to avoid to make report_too_many_sets + // public. + if (!can_reduce && max_color_used_.value_or(-1) + shift == MAX_ACCSETS) + acc_cond::mark_t {SPOT_MAX_ACCSETS}; + if (max_color_used_.has_value()) + *max_color_used_ += shift; + if (min_color_used_.has_value()) + *min_color_used_ += shift; + for (auto &e : res_->edges()) + { + auto new_val = e.acc.max_set() - 1 + shift; + if (new_val != -1U) + e.acc = { new_val }; + else + e.acc = {}; + } + } + + template + void + apply_lar(twa_graph_ptr &sub_aut, + const std::vector &pairs) + { + if constexpr (algo != IAR_RABIN) + change_to_odd(); + // avoids to call LAR if there is one color/pair/transition. + // LAR can work with this kind of condition but some optimizations + // like searching an existing state suppose that there is at least + // one element. + if ((algo == CAR && sub_aut->acc().num_sets() == 0) + || ((algo == IAR_RABIN || algo == IAR_STREETT) && pairs.empty()) + || (algo == TAR && sub_aut->num_edges() == 0)) + { + bool need_col = sub_aut->acc().is_t() != is_odd_; + auto col_fun = [&](const twa_graph::edge_storage_t &) + { + return need_col ? acc_cond::mark_t{0} : acc_cond::mark_t{}; + }; + apply_copy_general(sub_aut, col_fun, algo); + return; + } + // We sometimes need to have a list of the states + // of res_ constructed by this call to apply_lar. + const bool use_bscc = opt_.bscc; + const bool use_last_post_process = opt_.use_last_post_process; + constexpr bool is_tar = algo == TAR; + const bool need_tree = !is_tar + && (opt_.search_ex || use_last_post_process); + const bool need_state_list = use_last_post_process || use_bscc; + const bool is_dfs = opt_.lar_dfs; + // state_2_lar adapts add_new_state such that depending on the + // value of use_last in get_compatible_state, we will be able + // to find a compatible state faster. + state_2_lar::memory_order order; + if (!opt_.use_last) + { + if (opt_.use_last_post_process) + order = state_2_lar::memory_order::BOTH; + else + order = state_2_lar::memory_order::ONLY_OLDEST; + } + else + order = state_2_lar::memory_order::ONLY_NEWEST; + state_2_lar s2l; + if (need_tree) + s2l.init(sub_aut->num_states(), order); + std::vector states_scc_res; + if (need_state_list) + states_scc_res.reserve(sub_aut->num_states()); + auto init = + sub_aut->get_named_prop>("original-states"); + + if (opt_.propagate_col) + propagate_marks_here(sub_aut); + + auto init_state = sub_aut->get_init_state_number(); + robin_hood::unordered_map, + unsigned, to_parity_hash> ps_2_num; + unsigned lb_size; + if constexpr (algo == TAR) + lb_size = aut_->num_edges(); + else if constexpr (algo == CAR) + lb_size = aut_->num_states() * aut_->acc().num_sets(); + else + lb_size = aut_->num_states() * pairs.size(); + // num_2_ps maps a state of the result to a parity_state. As this function + // does not always create the first state, we need to add + // "- nb_states_before" to get a value. + const unsigned nb_states_before = res_->num_states(); + std::vector> num_2_ps; + // At least one copy of each state will be created. + num_2_ps.reserve(lb_size + num_2_ps.size()); + ps_2_num.reserve(lb_size + num_2_ps.size()); + + std::deque todo; + // return a pair new_state, is_new such that + // ps is associated to the state new_state in res_ + // and is_new is true if a new state was created by + // get_state + // We store 2 unsigned in a long long. + static_assert(sizeof(long long) >= 2 * sizeof(unsigned)); + robin_hood::unordered_map* edge_cache = nullptr; + if (!use_last_post_process) + { + edge_cache = new robin_hood::unordered_map(); + edge_cache->reserve(sub_aut->num_edges()); + } + auto get_state = [&](const to_parity_state &&ps) constexpr + { + auto it = ps_2_num.find(ps); + if (it == ps_2_num.end()) + { + unsigned nb = add_res_state(algo, ps); + ps_2_num[ps] = nb; + assert(nb == num_2_ps.size() + nb_states_before); + num_2_ps.emplace_back(ps); + todo.push_back(nb); + if (need_state_list) + states_scc_res.push_back(nb); + return std::pair{nb, true}; + } + return std::pair{it->second, false}; + }; + + std::set pairs_indices; + std::vector, memory>> relations; + if constexpr (algo == IAR_STREETT || algo == IAR_RABIN) + { + const auto num_pairs = pairs.size(); + for (unsigned k = 0; k < num_pairs; ++k) + if (fin(pairs, k)) + pairs_indices.insert(k); + } + + if constexpr (algo != TAR) + if (opt_.force_order) + relations = find_relations(sub_aut, pairs, pairs_indices); + + auto m = initial_memory_of(sub_aut, pairs, relations); + + assert(init); + auto init_res = get_state({(*init)[init_state], init_state, m}).first; + // A path is added when it is a destination. That is why we need to + // add the initial state. + unsigned nb_edges_before = res_->num_edges(); + std::vector edge_to_seen_nb; + if (use_last_post_process && algo != TAR) + edge_to_seen_nb.reserve(sub_aut->num_edges()); + if constexpr(!is_tar) + if (need_tree) + s2l.add_new_path(init_state, m, init_res, 0); + + robin_hood::unordered_map acc_cache; + // Main loop + while (!todo.empty()) + { + if (edge_cache) + edge_cache->clear(); + // If we want to process the most recent state of the result, we + // take the last value + unsigned res_current = is_dfs ? todo.back() : todo.front(); + unsigned res_index = res_current - nb_states_before; + const auto ¤t_ps = num_2_ps[res_index]; + const auto current_mem = current_ps.mem; + if (is_dfs) + todo.pop_back(); + else + todo.pop_front(); + + // For each edge leaving the state corresponding to res_state in sub_aut + for (auto &e : sub_aut->out(current_ps.state_scc)) + { + // We create a new memory and update it + memory mem(current_mem); + unsigned nb_seen = 0, + h = 0; + find_new_memory(e.dst, mem, sub_aut->edge_number(e), e.acc, + pairs, pairs_indices, nb_seen, h, relations); + // Now we try to find a way to move the elements and obtain an + // existing memory. + unsigned res_dst = -1U; + if constexpr (algo != TAR) + if (opt_.search_ex) + res_dst = s2l.get_compatible_state(e.dst, mem, nb_seen, + opt_.use_last); + // If it doesn't exist, we create a new state… + if (res_dst == -1U) + { + auto gs = get_state({(*init)[e.dst], e.dst, mem}); + res_dst = gs.first; + // And add it to the "tree" used to find a compatible state + if constexpr (!is_tar) + { + if (need_tree && gs.second) + s2l.add_new_path(e.dst, mem, res_dst, nb_seen); + } + } + + // We compute the color assigned to the new edge. + acc_cond::mark_t new_edge_color{}; + compute_new_color_lar(sub_aut, current_mem, mem, h, e.acc, + new_edge_color, pairs, acc_cache); + + // As we can assign a new destination later when + // use_last_post_process is true, we cannot try to find a compatible + // edge. + auto edge_res_num = add_res_edge(res_current, res_dst, + new_edge_color, e.cond, + !use_last_post_process, + edge_cache); + (void) edge_res_num; + // We have to remember how many colors were seen if we do a post + // processing + if constexpr (algo != TAR) + if (use_last_post_process) + { + assert(edge_res_num == + edge_to_seen_nb.size() + nb_edges_before + 1); + edge_to_seen_nb.push_back(nb_seen); + } + } + } + + // We used the most recent compatible state but perhaps that another + // state was created after. We do a new search. As TAR always moves one + // element we don't need it. + if constexpr (algo != TAR) + if (use_last_post_process) + { + for (auto &res_state : states_scc_res) + for (auto &e : res_->out(res_state)) + { + auto e_dst = e.dst; + if (e.src == e_dst) + continue; + auto edge_num = res_->edge_number(e); + const auto &ps = num_2_ps[e_dst - nb_states_before]; + unsigned seen_nb = + edge_to_seen_nb[edge_num - nb_edges_before - 1]; + assert(seen_nb < SPOT_MAX_ACCSETS); + auto new_dst = s2l.get_compatible_state(ps.state_scc, ps.mem, + seen_nb, true); + if (new_dst != e_dst) + { + assert(new_dst != -1U); + need_purge_ = true; + e.dst = new_dst; + } + } + } + if (use_bscc) + { + // Contrary to the (old) implementation of IAR, adding an edge between + // 2 SCCs of the result is the last thing done. It means that + // we don't need to use a filter when we compute the BSCC. + // A state s is in the BSCC if scc_of(s) is 0. + scc_info sub_scc(res_, init_res, nullptr, nullptr, + scc_info_options::NONE); + if (sub_scc.scc_count() > 1) + { + need_purge_ = true; + for (auto &state_produced : states_scc_res) + if (sub_scc.scc_of(state_produced) == 0) + state_to_res_[res_to_aut_[state_produced]] = state_produced; + } + } + delete edge_cache; + } + + void + link_sccs() + { + if (si_.scc_count() > 1) + { + const unsigned res_num_states = res_->num_states(); + for (unsigned i = 0; i < res_num_states; ++i) + { + auto aut_i = res_to_aut_[i]; + auto aut_i_scc = si_.scc_of(aut_i); + for (auto &e : aut_->out(aut_i)) + if (aut_i_scc != si_.scc_of(e.dst)) + { + auto e_dst_repr = state_to_res_[e.dst]; + add_res_edge(i, e_dst_repr, {}, e.cond); + } + } + } + } + + bool + try_parity_equivalence(const zielonka_tree &tree, + const twa_graph_ptr &sub_aut) + { + if (tree.has_parity_shape()) + { + bool first_is_accepting = tree.is_even(); + // A vector that stores the difference between 2 levels. + std::vector colors_diff; + auto &tree_nodes = tree.nodes_; + // Supposes that the index of the root is 0. + unsigned current_index = 0; + auto current_node = tree_nodes[current_index]; + // While the current node has a child + while (current_node.first_child != 0) + { + auto child_index = current_node.first_child; + auto child = tree_nodes[child_index]; + acc_cond::mark_t diff = current_node.colors - child.colors; + colors_diff.emplace_back(diff); + current_node = child; + } + // We have to deal with the edge between the last node and ∅. + bool is_empty_accepting = sub_aut->acc().accepting({}); + bool is_current_accepting = (current_node.level % 2) != tree.is_even(); + if (is_empty_accepting != is_current_accepting) + colors_diff.emplace_back(current_node.colors); + // + 1 as we need to know which value should be given to an uncolored + // edge. + std::vector new_colors( + sub_aut->get_acceptance().used_sets().max_set() + 1, -1U); + unsigned current_col = colors_diff.size() - 1; + for (auto &diff : colors_diff) + { + for (auto col : diff.sets()) + new_colors[col + 1] = current_col; + --current_col; + } + bool is_max_even = first_is_accepting == (colors_diff.size() % 2); + if (!is_max_even) + change_to_odd(); + + bool is_even_in_odd_world = is_odd_ && is_max_even; + if (is_even_in_odd_world) + for (auto &x : new_colors) + ++x; + apply_copy(sub_aut, new_colors, PARITY_EQUIV); + return true; + } + return false; + } + + bool + try_parity_prefix(const zielonka_tree &tree, const twa_graph_ptr &sub_aut) + { + unsigned index = 0; + auto current = tree.nodes_[index]; + std::vector prefixes; + bool first_is_accepting = tree.is_even(); + + acc_cond::mark_t removed_cols{}; + auto has_one_child = [&](const auto node) constexpr + { + auto fc = node.first_child; + return tree.nodes_[fc].next_sibling == fc; + }; + while (has_one_child(current)) + { + auto child = tree.nodes_[current.first_child]; + acc_cond::mark_t diff{}; + const bool is_leaf = current.first_child == 0; + if (is_leaf) + diff = current.colors; + else + diff = current.colors - child.colors; + prefixes.emplace_back(diff); + removed_cols |= diff; + if (is_leaf) + break; + current = child; + } + if (prefixes.empty()) + return false; + + if (opt_.datas) + algo_used_ |= algorithm::PARITY_PREFIX; + + // As we want to remove the prefix we need to remove it from the + // condition. As an unused color is not always removed (acc_clean false), + // we do it here. + auto used_cols = sub_aut->get_acceptance().used_sets() - removed_cols; + auto new_cond = sub_aut->acc().restrict_to(used_cols); + scc_info_to_parity sub(sub_aut, removed_cols); + // The recursive call will add some informations to help + // to add missing edges + state_to_nums_ = + new std::vector>(aut_->num_states()); + opt_.parity_prefix = false; + bool old_pp_gen = opt_.parity_prefix_general; + opt_.parity_prefix_general = false; + + auto max_scc_color_rec = max_color_scc_; + for (auto x : sub.split_aut({removed_cols})) + { + x->set_acceptance(new_cond); + process_scc(x, algorithm::PARITY_PREFIX); + if (max_color_scc_.has_value()) + { + if (!max_scc_color_rec.has_value()) + max_scc_color_rec.emplace(*max_color_scc_); + else + max_scc_color_rec.emplace( + std::max(*max_scc_color_rec, *max_color_scc_)); + } + } + opt_.parity_prefix = true; + opt_.parity_prefix_general = old_pp_gen; + + assert(max_scc_color_rec.has_value()); + auto max_used_is_accepting = ((*max_scc_color_rec - 1) % 2) == is_odd_; + bool last_prefix_acc = (prefixes.size() % 2) != first_is_accepting; + + unsigned m = prefixes.size() + (max_used_is_accepting != last_prefix_acc) + + *max_scc_color_rec - 1; + auto sub_aut_orig = + sub_aut->get_named_prop>("original-states"); + assert(sub_aut_orig); + for (auto &e : sub_aut->edges()) + if (e.acc & removed_cols) + { + auto el = std::find_if(prefixes.begin(), prefixes.end(), + [&](acc_cond::mark_t &x) + { return x & e.acc; }); + assert(el != prefixes.end()); + unsigned pos = std::distance(prefixes.begin(), el); + const unsigned col = m - pos; + // As it is a parity prefix we should never get a lower value than + // the color recursively produced. + assert(!max_scc_color_rec.has_value() || *max_scc_color_rec == 0 + || col + 1 > *max_scc_color_rec); + unsigned dst = state_to_res_[(*sub_aut_orig)[e.dst]]; + for (auto src : (*state_to_nums_)[(*sub_aut_orig)[e.src]]) + if (col != -1U) + add_res_edge(src, dst, {col}, e.cond); + else + add_res_edge(src, dst, {}, e.cond); + } + // As when we need to use link_scc, a set of edges that link 2 SCC + // need to be added and don't need to have a color. + else if (sub.scc_of(e.src) != sub.scc_of(e.dst)) + { + unsigned dst = state_to_res_[(*sub_aut_orig)[e.dst]]; + for (auto src : (*state_to_nums_)[(*sub_aut_orig)[e.src]]) + add_res_edge(src, dst, {}, e.cond); + } + delete state_to_nums_; + state_to_nums_ = nullptr; + + return true; + } + + bool + try_parity_prefix_general(twa_graph_ptr &sub_aut) + { + // This function should not be applied on an "empty" automaton as + // it must create an empty SCC with the algorithm NONE. + assert(sub_aut->num_edges() > 0); + static_assert((MAX_ACCSETS % 2) == 0, + "MAX_ACCSETS is supposed to be even"); + std::vector res_colors; + std::vector status; + acc_cond new_cond; + bool was_able_to_color; + // Is the maximal color accepting? + bool start_inf = true; + cond_type_main_aux(sub_aut, cond_kind::INF_PARITY, false, status, + res_colors, new_cond, was_able_to_color); + // Otherwise we can try to find a rejecting transition as first step + if (!was_able_to_color) + { + cond_type_main_aux(sub_aut, cond_kind::FIN_PARITY, false, status, + res_colors, new_cond, was_able_to_color); + if (!was_able_to_color) + return false; + start_inf = false; + } + + // If we have a parity-type automaton, it is just a copy. + if (std::find(status.begin(), status.end(), edge_status::IMPOSSIBLE) + == status.end()) + { + std::vector res_cols; + res_cols.reserve(res_colors.size()); + + auto min_set = + std::min_element(res_colors.begin() + 1, res_colors.end())->max_set(); + // Does the minimal color has the same parity than the maximal parity? + bool same_acceptance_min_max = (min_set % 2); + // Do we need to shift to match the parity of res_? + bool odd_shift = start_inf != is_odd_; + unsigned shift_col = min_set - (same_acceptance_min_max != odd_shift); + std::transform(res_colors.begin(), res_colors.end(), + std::back_inserter(res_cols), [&](auto &x) + { return x.max_set() - 1 - shift_col; }); + apply_copy_edge_index(sub_aut, res_cols, + algorithm::PARITY_PREFIX_GENERAL); + return true; + } + + // At this moment, a prefix exists + auto& ev = sub_aut->edge_vector(); + const auto ev_size = ev.size(); + auto keep = std::shared_ptr(make_bitvect(ev_size)); + const unsigned status_size = status.size(); + for (unsigned i = 1; i < status_size; ++i) + if (status[i] == edge_status::IMPOSSIBLE) + keep->set(i); + else + keep->clear(i); + + // Avoid recursive parity prefix + opt_.parity_prefix_general = false; + bool old_pp = opt_.parity_prefix; + opt_.parity_prefix = false; + + auto max_scc_color_rec = max_color_scc_; + scc_info lower_scc(sub_aut, scc_info_options::TRACK_STATES); + scc_info_to_parity sub(lower_scc, keep); + state_to_nums_ = + new std::vector>(aut_->num_states()); + for (auto x : sub.split_aut(keep)) + { + process_scc(x, algorithm::PARITY_PREFIX_GENERAL); + if (!max_scc_color_rec.has_value()) + max_scc_color_rec = max_color_scc_; + else if (max_color_scc_.has_value()) + max_scc_color_rec.emplace( + std::max(*max_scc_color_rec, *max_color_scc_)); + } + + // restore options + opt_.parity_prefix_general = true; + opt_.parity_prefix = old_pp; + + assert(sub_aut->num_edges() > 0); + + // Compute the minimal color used by parity prefix. + unsigned min_set_prefix = -2U; + for (unsigned i = 1; i < ev_size; ++i) + if (status[i] == edge_status::MARKED) + { + auto e_mark = res_colors[i].max_set(); + if (min_set_prefix == -2U) + min_set_prefix = e_mark - 1; + else + min_set_prefix = std::min(min_set_prefix + 1, e_mark) - 1; + } + + // At least one transition should be marked here. + assert(min_set_prefix != -2U); + + // Reduce the colors used by parity_prefix. + const bool min_prefix_accepting = (min_set_prefix % 2) == start_inf; + // max_scc_color_rec has a value as the automaton is not parity-type, + // so there was a recursive paritisation + assert(max_scc_color_rec.has_value()); + const bool max_rec_accepting = ((*max_scc_color_rec - 1) % 2) == is_odd_; + const bool same_prio = min_prefix_accepting == max_rec_accepting; + const unsigned delta = + min_set_prefix - (*max_scc_color_rec + 1) - !same_prio; + + auto sub_aut_orig = + sub_aut->get_named_prop>("original-states"); + assert(sub_aut_orig); + for (unsigned e_num = 1; e_num < ev_size; ++e_num) + { + auto& e = ev[e_num]; + if (status[e_num] == edge_status::MARKED) + { + unsigned dst = state_to_res_[(*sub_aut_orig)[e.dst]]; + for (auto src : (*state_to_nums_)[(*sub_aut_orig)[e.src]]) + { + auto col = res_colors[e_num].max_set() - delta - 1; + if (col == -1U) + add_res_edge(src, dst, {}, e.cond); + else + add_res_edge(src, dst, {col}, e.cond); + } + } + } + + delete state_to_nums_; + state_to_nums_ = nullptr; + + return true; + } + + bool + try_emptiness(const const_twa_graph_ptr &sub_aut, bool &tried) + { + tried = true; + if (generic_emptiness_check(sub_aut)) + { + auto col_fun = + [col = is_odd_ ? acc_cond::mark_t{0} : acc_cond::mark_t{}] + (const twa_graph::edge_storage_t &) noexcept + { + return col; + }; + apply_copy_general(sub_aut, col_fun, GENERIC_EMPTINESS); + return true; + } + return false; + } + + bool + try_rabin_to_buchi(twa_graph_ptr &sub_aut) + { + algorithm algo = RABIN_TO_BUCHI; + auto buch_aut = rabin_to_buchi_if_realizable(sub_aut); + if (buch_aut == nullptr) + { + algo = STREETT_TO_COBUCHI; + auto old_cond = sub_aut->get_acceptance(); + sub_aut->set_acceptance(acc_cond(old_cond.complement())); + buch_aut = rabin_to_buchi_if_realizable(sub_aut); + sub_aut->set_acceptance(acc_cond(old_cond)); + } + if (buch_aut != nullptr) + { + if (algo == STREETT_TO_COBUCHI) + change_to_odd(); + unsigned shift = (algo == RABIN_TO_BUCHI) && is_odd_; + + auto &buch_aut_ev = buch_aut->edge_vector(); + // 0 is not an edge, so we assign -1; + std::vector colors; + colors.reserve(buch_aut_ev.size()); + colors.push_back(-1U); + std::transform( + buch_aut_ev.begin() + 1, buch_aut_ev.end(), + std::back_inserter(colors), + [&](const twa_graph::edge_storage_t &e) { + return e.acc.max_set() - 1 + shift; + }); + apply_copy_edge_index(sub_aut, colors, algo); + return true; + } + return false; + } + + bool + try_buchi_type(const twa_graph_ptr &sub_aut) + { + std::vector status; + std::vector res_colors; + acc_cond new_cond; + bool is_co_bu = false; + bool was_able_to_color; + if (!cond_type_main_aux(sub_aut, cond_kind::BUCHI, true, status, + res_colors, new_cond, was_able_to_color)) + { + is_co_bu = true; + if (!cond_type_main_aux(sub_aut, cond_kind::CO_BUCHI, true, status, + res_colors, new_cond, was_able_to_color)) + return false; + change_to_odd(); + } + // Tests if all edges are colored or all edges are uncolored + auto [min, max] = + std::minmax_element(res_colors.begin() + 1, res_colors.end()); + const bool one_color = min->max_set() == max->max_set(); + const bool is_colored = min->max_set(); + auto col_fun = [&](const twa_graph::edge_storage_t &edge) + { + // If there one color in the automaton, we can simplify. + if (one_color) + { + bool z = (is_colored && !is_odd_) || (!is_colored && is_odd_); + // When we do co-buchi, we reverse + if (is_co_bu) + z = !z; + return z ? acc_cond::mark_t{0} : acc_cond::mark_t{}; + } + // Otherwise, copy the color + auto edge_number = sub_aut->edge_number(edge); + unsigned mc = res_colors[edge_number].max_set() - 1; + mc += (!is_co_bu && is_odd_); + if (mc == -1U) + return acc_cond::mark_t{}; + return acc_cond::mark_t{mc}; + }; + apply_copy_general(sub_aut, col_fun, is_co_bu ? algorithm::CO_BUCHI_TYPE + : algorithm::BUCHI_TYPE); + return true; + } + + bool + try_parity_type(const twa_graph_ptr &sub_aut) + { + std::vector status; + std::vector res_colors; + acc_cond new_cond; + bool was_able_to_color; + if (!cond_type_main_aux(sub_aut, cond_kind::INF_PARITY, true, status, + res_colors, new_cond, was_able_to_color)) + { + if (!cond_type_main_aux(sub_aut, cond_kind::FIN_PARITY, true, status, + res_colors, new_cond, was_able_to_color)) + return false; + } + bool is_max, is_odd; + new_cond.is_parity(is_max, is_odd); + auto [min, max] = + std::minmax_element(res_colors.begin() + 1, res_colors.end()); + // cond_type_main_aux returns a parity max condition + assert(is_max); + auto col_fun = + [shift = (is_odd != is_odd_) - (min->max_set() + (min->max_set() % 2)), + &res_colors, &sub_aut] + (const twa_graph::edge_storage_t &edge) + { + auto edge_number = sub_aut->edge_number(edge); + unsigned mc = res_colors[edge_number].max_set() - 1; + mc += shift; + if (mc == -1U) + return acc_cond::mark_t{}; + return acc_cond::mark_t{mc}; + }; + apply_copy_general(sub_aut, col_fun, PARITY_TYPE); + return true; + } + + // Keeps the result of the partial degeneralization if it reduces the number + // of colors or it allows to apply IAR. + bool + keep_deg(const const_twa_graph_ptr &sub_aut, const const_twa_graph_ptr °) + { + if (!opt_.reduce_col_deg) + return true; + unsigned nb_col_orig = sub_aut->get_acceptance().used_sets().count(); + + if (deg->get_acceptance().used_sets().count() < nb_col_orig) + return true; + std::vector pairs; + if (deg->acc().is_rabin_like(pairs)) + { + remove_duplicates(pairs); + if (pairs.size() < nb_col_orig) + return true; + } + if (deg->acc().is_streett_like(pairs)) + { + remove_duplicates(pairs); + if (pairs.size() < nb_col_orig) + return true; + } + return false; + } + + // Process a SCC. If there is no edge in the automaton, a new state is + // created and we say (if pretty_print is true) that none_algo created + // this state. + void + process_scc(twa_graph_ptr &sub_aut, + const algorithm none_algo = algorithm::NONE) + { + // Init the maximal color produced when processing this SCC. + max_color_scc_.reset(); + // If the sub_automaton is "empty", we don't need to apply an algorithm. + if (sub_aut->num_edges() == 0) + { + apply_copy(sub_aut, {}, none_algo); + return; + } + + bool tried_emptiness = false; + bool changed_structure = true; + while (true) + { + auto cond_before_simpl = sub_aut->acc(); + if (opt_.acc_clean) + simplify_acceptance_here(sub_aut); + if (opt_.propagate_col) + { + propagate_marks_here(sub_aut); + if (opt_.acc_clean) + simplify_acceptance_here(sub_aut); + } + if (opt_.datas && sub_aut->acc() != cond_before_simpl) + algo_used_ |= algorithm::ACC_CLEAN; + + if (opt_.parity_equiv || opt_.parity_prefix) + { + // If we don't try to find a parity prefix, we can stop + // to construct the tree when it has not parity shape. + zielonka_tree_options zopt = zielonka_tree_options::MERGE_SUBTREES + | zielonka_tree_options::CHECK_PARITY; + if (!opt_.parity_prefix) + zopt = zopt | zielonka_tree_options::ABORT_WRONG_SHAPE; + auto tree = zielonka_tree(sub_aut->acc(), zopt); + // If it is not parity shape, tree.nodes_ will be empty + if (tree.num_branches() != 0 && opt_.parity_equiv + && try_parity_equivalence(tree, sub_aut)) + return; + if (opt_.parity_prefix && try_parity_prefix(tree, sub_aut)) + return; + } + + if (changed_structure && opt_.parity_prefix_general + && try_parity_prefix_general(sub_aut)) + return; + + if (opt_.generic_emptiness && !tried_emptiness + && try_emptiness(sub_aut, tried_emptiness)) + return; + + // Buchi_type_to_buchi is more general that Rabin_to_buchi so + // we just call rabin_to_buchi if buchi_type_to_buchi is false. + if (!opt_.buchi_type_to_buchi && !opt_.parity_type_to_parity + && opt_.rabin_to_buchi + && try_rabin_to_buchi(sub_aut)) + return; + + // As parity_type_to_parity is stronger, we don't + // try if this option is used. + if (opt_.buchi_type_to_buchi && !opt_.parity_type_to_parity + && try_buchi_type(sub_aut)) + return; + + // We don't do it if parity_prefix_general is true as on a parity-type + // automaton parity_prefix_general removes all the transitions and + // we also get a parity-type automaton. + if (!opt_.parity_prefix_general && opt_.parity_type_to_parity + && try_parity_type(sub_aut)) + return; + + if (opt_.partial_degen + && is_partially_degeneralizable(sub_aut, true, true)) + { + auto deg = sub_aut; + std::vector forbid; + auto m = is_partially_degeneralizable(sub_aut, true, true, forbid); + bool changed = false; + while (m) + { + auto tmp = partial_degeneralize(deg, m); + simplify_acceptance_here(tmp); + if (keep_deg(deg, tmp)) + { + algo_used_ |= algorithm::PARTIAL_DEGEN; + deg = tmp; + changed = true; + changed_structure = true; + } + else + forbid.emplace_back(m); + m = is_partially_degeneralizable(deg, true, true, forbid); + } + + if (changed) + { + sub_aut = deg; + continue; + } + } + break; + } + if (opt_.use_generalized_rabin) + { + auto gen_rab = to_generalized_rabin(sub_aut); + // to_generalized_rabin does not propagate original-states. + auto sub_aut_orig = + sub_aut->get_named_prop>("original-states"); + assert(sub_aut_orig); + auto orig = new std::vector(); + const auto sub_aut_num_states = sub_aut->num_states(); + orig->reserve(sub_aut_num_states); + gen_rab->set_named_prop("original-states", orig); + for (unsigned i = 0; i < sub_aut_num_states; ++i) + orig->push_back((*sub_aut_orig)[i]); + sub_aut = partial_degeneralize(gen_rab); + } + std::vector pairs; + algorithm algo = choose_lar(sub_aut->acc(), pairs, sub_aut->num_edges()); + if (opt_.datas) + algo_used_ |= algo; + if (algo == CAR) + apply_lar(sub_aut, pairs); + else if (algo == IAR_STREETT) + apply_lar(sub_aut, pairs); + else if (algo == IAR_RABIN) + apply_lar(sub_aut, pairs); + else if (algo == TAR) + apply_lar(sub_aut, pairs); + else + SPOT_UNREACHABLE(); + } + + public: + twa_graph_ptr + run() + { + res_ = make_twa_graph(aut_->get_dict()); + res_->copy_ap_of(aut_); + const unsigned num_scc = si_.scc_count(); + auto orig_aut = + aut_->get_named_prop>("original-states"); + std::optional> orig_st; + if (orig_aut) + { + orig_st.emplace(std::vector{*orig_aut}); + std::const_pointer_cast(aut_) + ->set_named_prop("original-states", nullptr); + } + auto sccs = si_.split_aut(); + for (unsigned scc = 0; scc < num_scc; ++scc) + { + auto sub_automaton = sccs[scc]; + process_scc(sub_automaton); + } + + link_sccs(); + // During the execution, to_parity works on its own + // original-states and we must combine it with the property original + // states of aut_ to propagate the information. + if (orig_st) + for (unsigned i = 0; i < orig_->size(); ++i) + (*orig_)[i] = (*orig_aut)[(*orig_)[i]]; + res_->set_named_prop("original-states", orig_); + if (opt_.pretty_print) + res_->set_named_prop("state-names", names_); + if (res_->num_states() == 0) + add_res_state(NONE, {0, 0, {}}); + res_->set_init_state(state_to_res_[aut_->get_init_state_number()]); + // There is only a subset of algorithm that can create an unreachable + // state + if (need_purge_) + res_->purge_unreachable_states(); + // A special case is an automaton without edge. It implies + // max_color_used_ has not value so we need to test it. + if (!max_color_used_.has_value()) + { + assert(aut_->num_edges() == 0); + res_->set_acceptance(acc_cond(acc_cond::acc_code::f())); + } + else + { + res_->set_acceptance(acc_cond( + acc_cond::acc_code::parity(true, is_odd_, *max_color_used_))); + } + if (opt_.datas) + { + constexpr std::array + algos = {BUCHI_TYPE, CAR, CO_BUCHI_TYPE, GENERIC_EMPTINESS, IAR_RABIN, + IAR_STREETT, NONE, PARITY_EQUIV, PARITY_PREFIX, + PARITY_PREFIX_GENERAL, PARITY_TYPE, RABIN_TO_BUCHI, + STREETT_TO_COBUCHI, TAR}; + for (auto al : algos) + if (algo_used_ & al) + opt_.datas->algorithms_used.emplace_back(algorithm_to_str(al)); + } + return res_; + } + + to_parity_generator(const const_twa_graph_ptr &aut, + const to_parity_options opt) + : aut_(aut), + opt_(opt), + si_(aut), + state_to_res_(aut->num_states(), -1U) + { + auto aut_num = aut->num_states(); + res_to_aut_.reserve(aut_num); + orig_ = new std::vector(); + orig_->reserve(aut_num); + if (opt.pretty_print) + { + names_ = new std::vector(); + names_->reserve(aut_num); + } + } + }; + + twa_graph_ptr + to_parity(const const_twa_graph_ptr &aut, + const to_parity_options options) + { + bool is_max, is_odd; + if (aut->acc().is_parity(is_max, is_odd, false)) + { + if (!is_max) + return change_parity(aut, parity_kind::parity_kind_max, + parity_style::parity_style_any); + else + { + auto res = make_twa_graph(aut, twa::prop_set::all()); + res->copy_acceptance_of(aut); + return res; + } + } + to_parity_generator gen(aut, options); + return gen.run(); + } + + // Old version of CAR + namespace { + struct lar_state + { + unsigned state; + std::vector perm; + bool operator<(const lar_state &s) const + { + return state == s.state ? perm < s.perm : state < s.state; + } + + std::string to_string() const + { + std::ostringstream s; + s << state << " ["; + unsigned ps = perm.size(); + for (unsigned i = 0; i < ps; ++i) + { + if (i > 0) + s << ','; + s << perm[i]; + } + s << ']'; + return s.str(); + } + }; + + class lar_generator + { + const const_twa_graph_ptr &aut_; + twa_graph_ptr res_; + const bool pretty_print; + + std::map lar2num; + + public: + explicit lar_generator(const const_twa_graph_ptr &a, bool pretty_print) + : aut_(a), res_(nullptr), pretty_print(pretty_print) + { + } + + twa_graph_ptr run() + { + res_ = make_twa_graph(aut_->get_dict()); + res_->copy_ap_of(aut_); + + std::deque todo; + auto get_state = [this, &todo](const lar_state &s) + { + auto it = lar2num.emplace(s, -1U); + if (it.second) // insertion took place + { + unsigned nb = res_->new_state(); + it.first->second = nb; + todo.push_back(s); + } + return it.first->second; + }; + + std::vector initial_perm(aut_->num_sets()); + std::iota(initial_perm.begin(), initial_perm.end(), 0); + { + lar_state s0{aut_->get_init_state_number(), initial_perm}; + res_->set_init_state(get_state(s0)); + } + + scc_info si(aut_, scc_info_options::NONE); + // main loop + while (!todo.empty()) + { + lar_state current = todo.front(); + todo.pop_front(); + + // TODO: todo could store this number to avoid one lookup + unsigned src_num = get_state(current); + + unsigned source_scc = si.scc_of(current.state); + for (const auto &e : aut_->out(current.state)) + { + // find the new permutation + std::vector new_perm = current.perm; + unsigned h = 0; + for (unsigned k : e.acc.sets()) + { + auto it = std::find(new_perm.begin(), new_perm.end(), k); + h = std::max(h, unsigned(new_perm.end() - it)); + std::rotate(it, it + 1, new_perm.end()); + } + + if (source_scc != si.scc_of(e.dst)) + { + new_perm = initial_perm; + h = 0; + } + + lar_state dst{e.dst, new_perm}; + unsigned dst_num = get_state(dst); + + // Do the h last elements satisfy the acceptance condition? + // If they do, emit 2h, if they don't emit 2h+1. + acc_cond::mark_t m(new_perm.end() - h, new_perm.end()); + bool rej = !aut_->acc().accepting(m); + res_->new_edge(src_num, dst_num, e.cond, {2 * h + rej}); + } + } + + // parity max even + unsigned sets = 2 * aut_->num_sets() + 2; + res_->set_acceptance(sets, acc_cond::acc_code::parity_max_even(sets)); + + if (pretty_print) + { + auto names = new std::vector(res_->num_states()); + for (const auto &p : lar2num) + (*names)[p.second] = p.first.to_string(); + res_->set_named_prop("state-names", names); + } + + return res_; + } + }; + } + + twa_graph_ptr + to_parity_old(const const_twa_graph_ptr &aut, bool pretty_print) + { + if (!aut->is_existential()) + throw std::runtime_error("LAR does not handle alternation"); + // if aut is already parity return it as is + if (aut->acc().is_parity()) + return std::const_pointer_cast(aut); + + lar_generator gen(aut, pretty_print); + return gen.run(); + } + + // Old version of IAR + + namespace + { using perm_t = std::vector; struct iar_state { @@ -326,18 +2622,18 @@ namespace spot perm_t perm; bool - operator<(const iar_state& other) const + operator<(const iar_state &other) const { return state == other.state ? perm < other.perm : state < other.state; } }; - template + template class iar_generator { // helper functions: access fin and inf parts of the pairs // these functions negate the Streett condition to see it as a Rabin one - const acc_cond::mark_t& + const acc_cond::mark_t & fin(unsigned k) const { if (is_rabin) @@ -353,16 +2649,15 @@ namespace spot else return pairs_[k].fin; } + public: - explicit iar_generator(const const_twa_graph_ptr& a, - const std::vector& p, + explicit iar_generator(const const_twa_graph_ptr &a, + const std::vector &p, const bool pretty_print) - : aut_(a) - , pairs_(p) - , scc_(scc_info(a)) - , pretty_print_(pretty_print) - , state2pos_iar_states(aut_->num_states(), -1U) - {} + : aut_(a), pairs_(p), scc_(scc_info(a)), pretty_print_(pretty_print), + state2pos_iar_states(aut_->num_states(), -1U) + { + } twa_graph_ptr run() @@ -386,9 +2681,6 @@ namespace spot res_->set_init_state(s); } - // there could be quite a number of unreachable states, prune them - res_->purge_unreachable_states(); - if (pretty_print_) { unsigned nstates = res_->num_states(); @@ -396,13 +2688,13 @@ namespace spot for (auto e : res_->edges()) { unsigned s = e.src; - iar_state iar = num2iar.at(s); + iar_state iar = num2iar[s]; std::ostringstream st; st << iar.state << ' '; if (iar.perm.empty()) st << '['; char sep = '['; - for (unsigned h: iar.perm) + for (unsigned h : iar.perm) { st << sep << h; sep = ','; @@ -413,6 +2705,8 @@ namespace spot res_->set_named_prop("state-names", names); } + // there could be quite a number of unreachable states, prune them + res_->purge_unreachable_states(); return res_; } @@ -423,44 +2717,44 @@ namespace spot unsigned init = scc_.one_state_of(scc_num); std::deque todo; - auto get_state = [&](const iar_state& s) + auto get_state = [&](const iar_state &s) + { + auto it = iar2num.find(s); + if (it == iar2num.end()) { - auto it = iar2num.find(s); - if (it == iar2num.end()) - { - unsigned nb = res_->new_state(); - iar2num[s] = nb; - num2iar[nb] = s; - unsigned iar_pos = iar_states.size(); - unsigned old_newest_pos = state2pos_iar_states[s.state]; - state2pos_iar_states[s.state] = iar_pos; - iar_states.push_back({s, old_newest_pos}); - todo.push_back(s); - return nb; - } - return it->second; - }; + unsigned nb = res_->new_state(); + iar2num[s] = nb; + num2iar[nb] = s; + unsigned iar_pos = iar_states.size(); + unsigned old_newest_pos = state2pos_iar_states[s.state]; + state2pos_iar_states[s.state] = iar_pos; + iar_states.push_back({s, old_newest_pos}); + todo.push_back(s); + return nb; + } + return it->second; + }; auto get_other_scc = [this](unsigned state) - { - auto it = state2iar.find(state); - // recursively build the destination SCC if we detect that it has - // not been already built. - if (it == state2iar.end()) - build_iar_scc(scc_.scc_of(state)); - return iar2num.at(state2iar.at(state)); - }; + { + auto it = state2iar.find(state); + // recursively build the destination SCC if we detect that it has + // not been already built. + if (it == state2iar.end()) + build_iar_scc(scc_.scc_of(state)); + return iar2num.at(state2iar.at(state)); + }; if (scc_.is_trivial(scc_num)) - { - iar_state iar_s{init, perm_t()}; - state2iar[init] = iar_s; - unsigned src_num = get_state(iar_s); - // Do not forget to connect to subsequent SCCs - for (const auto& e : aut_->out(init)) - res_->new_edge(src_num, get_other_scc(e.dst), e.cond); - return; - } + { + iar_state iar_s{init, perm_t()}; + state2iar[init] = iar_s; + unsigned src_num = get_state(iar_s); + // Do not forget to connect to subsequent SCCs + for (const auto &e : aut_->out(init)) + res_->new_edge(src_num, get_other_scc(e.dst), e.cond); + return; + } // determine the pairs that appear in the SCC auto colors = scc_.acc_sets_of(scc_num); @@ -478,109 +2772,110 @@ namespace spot // the main loop while (!todo.empty()) + { + iar_state current = todo.front(); + todo.pop_front(); + + unsigned src_num = get_state(current); + + for (const auto &e : aut_->out(current.state)) { - iar_state current = todo.front(); - todo.pop_front(); + // connect to the appropriate state + if (scc_.scc_of(e.dst) != scc_num) + res_->new_edge(src_num, get_other_scc(e.dst), e.cond); + else + { + // find the new permutation + perm_t new_perm = current.perm; + // Count pairs whose fin-part is seen on this transition + unsigned seen_nb = 0; + // consider the pairs for this SCC only + for (unsigned k : scc_pairs) + if (e.acc & fin(k)) + { + ++seen_nb; + auto it = std::find(new_perm.begin(), + new_perm.end(), + k); + // move the pair in front of the permutation + std::rotate(new_perm.begin(), it, it + 1); + } - unsigned src_num = get_state(current); + iar_state dst; + unsigned dst_num = -1U; - for (const auto& e : aut_->out(current.state)) + // Optimization: when several indices are seen in the + // transition, they move at the front of new_perm in any + // order. Check whether there already exists an iar_state + // that matches this condition. + + auto iar_pos = state2pos_iar_states[e.dst]; + while (iar_pos != -1U) { - // connect to the appropriate state - if (scc_.scc_of(e.dst) != scc_num) - res_->new_edge(src_num, get_other_scc(e.dst), e.cond); - else - { - // find the new permutation - perm_t new_perm = current.perm; - // Count pairs whose fin-part is seen on this transition - unsigned seen_nb = 0; - // consider the pairs for this SCC only - for (unsigned k : scc_pairs) - if (e.acc & fin(k)) - { - ++seen_nb; - auto it = std::find(new_perm.begin(), - new_perm.end(), - k); - // move the pair in front of the permutation - std::rotate(new_perm.begin(), it, it+1); - } - - iar_state dst; - unsigned dst_num = -1U; - - // Optimization: when several indices are seen in the - // transition, they move at the front of new_perm in any - // order. Check whether there already exists an iar_state - // that matches this condition. - - auto iar_pos = state2pos_iar_states[e.dst]; - while (iar_pos != -1U) - { - iar_state& tmp = iar_states[iar_pos].first; - iar_pos = iar_states[iar_pos].second; - if (std::equal(new_perm.begin() + seen_nb, - new_perm.end(), - tmp.perm.begin() + seen_nb)) - { - dst = tmp; - dst_num = iar2num[dst]; - break; - } - } - // if such a state was not found, build it - if (dst_num == -1U) - { - dst = iar_state{e.dst, new_perm}; - dst_num = get_state(dst); - } - - // find the maximal index encountered by this transition - unsigned maxint = -1U; - for (int k = current.perm.size() - 1; k >= 0; --k) - { - unsigned pk = current.perm[k]; - if (!inf(pk) || - (e.acc & (pairs_[pk].fin | pairs_[pk].inf))) { - maxint = k; - break; - } - } - - acc_cond::mark_t acc = {}; - if (maxint == -1U) - acc = {0}; - else if (e.acc & fin(current.perm[maxint])) - acc = {2*maxint+2}; - else - acc = {2*maxint+1}; - - res_->new_edge(src_num, dst_num, e.cond, acc); - } + iar_state &tmp = iar_states[iar_pos].first; + iar_pos = iar_states[iar_pos].second; + if (std::equal(new_perm.begin() + seen_nb, + new_perm.end(), + tmp.perm.begin() + seen_nb)) + { + dst = tmp; + dst_num = iar2num[dst]; + break; + } } + // if such a state was not found, build it + if (dst_num == -1U) + { + dst = iar_state{e.dst, new_perm}; + dst_num = get_state(dst); + } + + // find the maximal index encountered by this transition + unsigned maxint = -1U; + for (int k = current.perm.size() - 1; k >= 0; --k) + { + unsigned pk = current.perm[k]; + if (!inf(pk) || + (e.acc & (pairs_[pk].fin | pairs_[pk].inf))) + { + maxint = k; + break; + } + } + + acc_cond::mark_t acc{}; + if (maxint == -1U) + acc.set(0); + else if (e.acc & fin(current.perm[maxint])) + assign_color(acc, 2 * maxint + 2); + else + assign_color(acc, 2 * maxint + 1); + + res_->new_edge(src_num, dst_num, e.cond, acc); + } } + } // Optimization: find the bottom SCC of the sub-automaton we have just // built. To that end, we have to ignore edges going out of scc_num. - auto leaving_edge = [&](unsigned d) - { - return scc_.scc_of(num2iar.at(d).state) != scc_num; - }; - auto filter_edge = [](const twa_graph::edge_storage_t&, + auto leaving_edge = [&](unsigned d) constexpr + { + return scc_.scc_of(num2iar.at(d).state) != scc_num; + }; + auto filter_edge = [](const twa_graph::edge_storage_t &, unsigned dst, - void* filter_data) - { - decltype(leaving_edge)* data = - static_cast(filter_data); + void *filter_data) constexpr + { + decltype(leaving_edge) *data = + static_cast(filter_data); - if ((*data)(dst)) - return scc_info::edge_filter_choice::ignore; - return scc_info::edge_filter_choice::keep; - }; + if ((*data)(dst)) + return scc_info::edge_filter_choice::ignore; + return scc_info::edge_filter_choice::keep; + }; scc_info sub_scc(res_, get_state(s0), filter_edge, &leaving_edge); - // SCCs are numbered in reverse topological order, so the bottom SCC has - // index 0. + // SCCs are numbered in reverse topological order, so the bottom SCC + // has index 0. const unsigned bscc = 0; assert(sub_scc.succ(0).empty()); assert( @@ -590,23 +2885,23 @@ namespace spot if (sub_scc.succ(s).empty()) return false; return true; - } ()); + }()); assert(sub_scc.states_of(bscc).size() - >= scc_.states_of(scc_num).size()); + >= scc_.states_of(scc_num).size()); // update state2iar for (unsigned scc_state : sub_scc.states_of(bscc)) - { - iar_state& iar = num2iar.at(scc_state); - if (state2iar.find(iar.state) == state2iar.end()) - state2iar[iar.state] = iar; - } + { + iar_state &iar = num2iar.at(scc_state); + if (state2iar.find(iar.state) == state2iar.end()) + state2iar[iar.state] = iar; + } } private: - const const_twa_graph_ptr& aut_; - const std::vector& pairs_; + const const_twa_graph_ptr &aut_; + const std::vector &pairs_; const scc_info scc_; twa_graph_ptr res_; bool pretty_print_; @@ -625,1520 +2920,36 @@ namespace spot // Make this a function different from iar_maybe(), so that // iar() does not have to call a deprecated function. static twa_graph_ptr - iar_maybe_(const const_twa_graph_ptr& aut, bool pretty_print) + iar_maybe_(const const_twa_graph_ptr &aut, bool pretty_print) { std::vector pairs; if (!aut->acc().is_rabin_like(pairs)) if (!aut->acc().is_streett_like(pairs)) return nullptr; else - { - iar_generator gen(aut, pairs, pretty_print); - return gen.run(); - } - else { - iar_generator gen(aut, pairs, pretty_print); + iar_generator gen(aut, pairs, pretty_print); return gen.run(); } + else + { + iar_generator gen(aut, pairs, pretty_print); + return gen.run(); + } } } twa_graph_ptr - iar_maybe(const const_twa_graph_ptr& aut, bool pretty_print) - { - return iar_maybe_(aut, pretty_print); - } - - twa_graph_ptr - iar(const const_twa_graph_ptr& aut, bool pretty_print) + iar(const const_twa_graph_ptr &aut, bool pretty_print) { if (auto res = iar_maybe_(aut, pretty_print)) return res; throw std::runtime_error("iar() expects Rabin-like or Streett-like input"); } -// New version for paritizing -namespace -{ -struct node -{ - // A color of the permutation or a state. - unsigned label; - std::vector children; - // is_leaf is true if the label is a state of res_. - bool is_leaf; - - node() - : node(0, 0){ - } - - node(int label_, bool is_leaf_) - : label(label_) - , children(0) - , is_leaf(is_leaf_){ - } - - ~node() - { - for (auto c : children) - delete c; - } - - // Add a permutation to the tree. - void - add_new_perm(const std::vector& permu, int pos, unsigned state) - { - if (pos == -1) - children.push_back(new node(state, true)); - else - { - auto lab = permu[pos]; - auto child = std::find_if(children.begin(), children.end(), - [lab](node* n){ - return n->label == lab; - }); - if (child == children.end()) - { - node* new_child = new node(lab, false); - children.push_back(new_child); - new_child->add_new_perm(permu, pos - 1, state); - } - else - (*child)->add_new_perm(permu, pos - 1, state); - } - } - - node* - get_sub_tree(const std::vector& elements, int pos) - { - if (pos < 0) - return this; - unsigned lab = elements[pos]; - auto child = std::find_if(children.begin(), children.end(), - [lab](node* n){ - return n->label == lab; - }); - assert(child != children.end()); - return (*child)->get_sub_tree(elements, pos - 1); - } - - // Gives a state of res_ (if it exists) reachable from this node. - // If use_last is true, we take the most recent, otherwise we take - // the oldest. - unsigned - get_end(bool use_last) - { - if (children.empty()) - { - if (!is_leaf) - return -1U; - return label; - } - if (use_last) - return children[children.size() - 1]->get_end(use_last); - return children[0]->get_end(use_last); - } - - // Try to find a state compatible with the permu when seen_nb colors are - // moved. - unsigned - get_existing(const std::vector& permu, unsigned seen_nb, int pos, - bool use_last) - { - if (pos < (int) seen_nb) - return get_end(use_last); - else - { - auto lab = permu[pos]; - auto child = std::find_if(children.begin(), children.end(), - [lab](node* n){ - return n->label == lab; - }); - if (child == children.end()) - return -1U; - return (*child)->get_existing(permu, seen_nb, pos - 1, use_last); - } - } -}; - -class state_2_car_scc -{ -std::vector nodes; - -public: -state_2_car_scc(unsigned nb_states) - : nodes(nb_states, node()){ -} - -// Try to find a state compatible with the permu when seen_nb colors are -// moved. If use_last is true, it return the last created compatible state. -// If it is false, it returns the oldest. -unsigned -get_res_state(unsigned state, const std::vector& permu, - unsigned seen_nb, bool use_last) -{ - return nodes[state].get_existing(permu, seen_nb, - permu.size() - 1, use_last); -} - -void -add_res_state(unsigned initial, unsigned state, - const std::vector& permu) -{ - nodes[initial].add_new_perm(permu, ((int) permu.size()) - 1, state); -} - -node* -get_sub_tree(const std::vector& elements, unsigned state) -{ - return nodes[state].get_sub_tree(elements, elements.size() - 1); -} -}; - -class car_generator -{ -enum algorithm { - // Try to have a Büchi condition if we have Rabin. - Rabin_to_Buchi, - Streett_to_Buchi, - // IAR - IAR_Streett, - IAR_Rabin, - // CAR - CAR, - // Changing colors transforms acceptance to max even/odd copy. - Copy_even, - Copy_odd, - // If a condition is "t" or "f", we just have to copy the automaton. - False_clean, - True_clean, - None -}; - - -static std::string -algorithm_to_str(algorithm algo) -{ - std::string algo_str; - switch (algo) - { - case IAR_Streett: - algo_str = "IAR (Streett)"; - break; - case IAR_Rabin: - algo_str = "IAR (Rabin)"; - break; - case CAR: - algo_str = "CAR"; - break; - case Copy_even: - algo_str = "Copy even"; - break; - case Copy_odd: - algo_str = "Copy odd"; - break; - case False_clean: - algo_str = "False clean"; - break; - case True_clean: - algo_str = "True clean"; - break; - case Streett_to_Buchi: - algo_str = "Streett to Büchi"; - break; - case Rabin_to_Buchi: - algo_str = "Rabin to Büchi"; - break; - default: - algo_str = "None"; - break; - } - return algo_str; -} - -using perm_t = std::vector; - -struct car_state -{ - // State of the original automaton - unsigned state; - // We create a new automaton for each SCC of the original automaton - // so we keep a link between a car_state and the state of the - // subautomaton. - unsigned state_scc; - // Permutation used by IAR and CAR. - perm_t perm; - - bool - operator<(const car_state &other) const - { - if (state < other.state) - return true; - if (state > other.state) - return false; - if (perm < other.perm) - return true; - if (perm > other.perm) - return false; - return state_scc < other.state_scc; - } - - std::string - to_string(algorithm algo) const - { - std::stringstream s; - s << state; - unsigned ps = perm.size(); - if (ps > 0) - { - s << " ["; - for (unsigned i = 0; i != ps; ++i) - { - if (i > 0) - s << ','; - s << perm[i]; - } - s << ']'; - } - s << ", "; - s << algorithm_to_str(algo); - return s.str(); - } -}; - -const acc_cond::mark_t & -fin(const std::vector& pairs, unsigned k, algorithm algo) -const -{ - if (algo == IAR_Rabin) - return pairs[k].fin; - else - return pairs[k].inf; -} - -acc_cond::mark_t -inf(const std::vector& pairs, unsigned k, algorithm algo) -const -{ - if (algo == IAR_Rabin) - return pairs[k].inf; - else - return pairs[k].fin; -} - -// Gives for each state a set of marks incoming to this state. -std::vector> -get_inputs_states(const twa_graph_ptr& aut) -{ - auto used = aut->acc().get_acceptance().used_sets(); - std::vector> inputs(aut->num_states()); - for (auto e : aut->edges()) - { - auto elements = e.acc & used; - if (elements.has_many()) - inputs[e.dst].insert(elements); - } - return inputs; -} - -// Gives for each state a set of pairs incoming to this state. -std::vector>> -get_inputs_iar(const twa_graph_ptr& aut, algorithm algo, - const std::set& perm_elem, - const std::vector& pairs) -{ - std::vector>> inputs(aut->num_states()); - for (auto e : aut->edges()) - { - auto acc = e.acc; - std::vector new_vect; - for (unsigned k : perm_elem) - if (acc & fin(pairs, k, algo)) - new_vect.push_back(k); - std::sort(std::begin(new_vect), std::end(new_vect)); - inputs[e.dst].insert(new_vect); - } - return inputs; -} -// Give an order from the set of marks -std::vector -group_to_vector(const std::set& group) -{ - // In this function, we have for example the marks {1, 2}, {1, 2, 3}, {2} - // A compatible order is [2, 1, 3] - std::vector group_vect(group.begin(), group.end()); - - // We sort the elements by inclusion. This function is called on a - // set of marks such that each mark is included or includes the others. - std::sort(group_vect.begin(), group_vect.end(), - [](const acc_cond::mark_t left, const acc_cond::mark_t right) - { - return (left != right) && ((left & right) == left); - }); - // At this moment, we have the vector [{2}, {1, 2}, {1, 2, 3}]. - // In order to create the order, we add the elements of the first element. - // Then we add the elements of the second mark (without duplication), etc. - std::vector result; - for (auto mark : group_vect) - { - for (unsigned col : mark.sets()) - if (std::find(result.begin(), result.end(), col) == result.end()) - result.push_back(col); - } - return result; -} - -// Give an order from the set of indices of pairs -std::vector -group_to_vector_iar(const std::set>& group) -{ - std::vector> group_vect(group.begin(), group.end()); - for (auto& vec : group_vect) - std::sort(std::begin(vec), std::end(vec)); - std::sort(group_vect.begin(), group_vect.end(), - [](const std::vector left, - const std::vector right) - { - return (right != left) - && std::includes(right.begin(), right.end(), - left.begin(), left.end()); - }); - std::vector result; - for (auto vec : group_vect) - for (unsigned col : vec) - if (std::find(result.begin(), result.end(), col) == result.end()) - result.push_back(col); - return result; -} - -// Give a correspondance between a mark and an order for CAR -std::map> -get_groups(const std::set& marks_input) -{ - std::map> result; - - std::vector> groups; - for (acc_cond::mark_t mark : marks_input) - { - bool added = false; - for (unsigned group = 0; group < groups.size(); ++group) - { - if (std::all_of(groups[group].begin(), groups[group].end(), - [mark](acc_cond::mark_t element) - { - return ((element | mark) == mark) - || ((element | mark) == element); - })) - { - groups[group].insert(mark); - added = true; - break; - } - } - if (!added) - groups.push_back({mark}); - } - for (auto& group : groups) - { - auto new_vector = group_to_vector(group); - for (auto mark : group) - result.insert({mark, new_vector}); - } - return result; -} - -// Give a correspondance between a mark and an order for IAR -std::map, std::vector> -get_groups_iar(const std::set>& marks_input) -{ - std::map, std::vector> result; - - std::vector>> groups; - for (auto vect : marks_input) - { - bool added = false; - for (unsigned group = 0; group < groups.size(); ++group) - if (std::all_of(groups[group].begin(), groups[group].end(), - [vect](std::vector element) - { - return std::includes(vect.begin(), vect.end(), - element.begin(), element.end()) - || std::includes(element.begin(), element.end(), - vect.begin(), vect.end()); - })) - { - groups[group].insert(vect); - added = true; - break; - } - if (!added) - groups.push_back({vect}); - } - for (auto& group : groups) - { - auto new_vector = group_to_vector_iar(group); - for (auto vect : group) - result.insert({vect, new_vector}); - } - return result; -} - -// Give for each state the correspondance between a mark and an order (CAR) -std::vector>> -get_mark_to_vector(const twa_graph_ptr& aut) -{ - std::vector>> result; - auto inputs = get_inputs_states(aut); - for (unsigned state = 0; state < inputs.size(); ++state) - result.push_back(get_groups(inputs[state])); - return result; -} - -// Give for each state the correspondance between a mark and an order (IAR) -std::vector, std::vector>> -get_iar_to_vector(const twa_graph_ptr& aut, algorithm algo, - const std::set& perm_elem, - const std::vector& pairs) -{ - std::vector, std::vector>> result; - auto inputs = get_inputs_iar(aut, algo, perm_elem, pairs); - for (unsigned state = 0; state < inputs.size(); ++state) - result.push_back(get_groups_iar(inputs[state])); - return result; -} - -public: -explicit car_generator(const const_twa_graph_ptr &a, to_parity_options options) - : aut_(a) - , scc_(scc_info(a)) - , is_odd(false) - , options(options) -{ - if (options.pretty_print) - names = new std::vector(); - else - names = nullptr; -} - -// During the creation of the states, we had to choose between a set of -// compatible states. But it is possible to create another compatible state -// after. This function checks if a compatible state was created after and -// use it. -void -change_transitions_destination(twa_graph_ptr& aut, -const std::vector& states, -std::map>& partial_history, -state_2_car_scc& state_2_car) -{ - for (auto s : states) - for (auto& edge : aut->out(s)) - { - unsigned - src = edge.src, - dst = edge.dst; - // We don't change loops - if (src == dst) - continue; - unsigned dst_scc = num2car[dst].state_scc; - auto cant_change = partial_history[aut->edge_number(edge)]; - edge.dst = state_2_car.get_sub_tree(cant_change, dst_scc) - ->get_end(true); - } -} - -unsigned -apply_false_true_clean(const twa_graph_ptr &sub_automaton, bool is_true, - const std::vector& inf_fin_prefix, - unsigned max_free_color, - std::map& state2car_local, - std::map& car2num_local) -{ - std::vector* init_states = sub_automaton-> - get_named_prop>("original-states"); - - for (unsigned state = 0; state < sub_automaton->num_states(); ++state) - { - unsigned s_aut = (*init_states)[state]; - - car_state new_car = { s_aut, state, perm_t() }; - auto new_state = res_->new_state(); - car2num_local[new_car] = new_state; - num2car.insert(num2car.begin() + new_state, new_car); - if (options.pretty_print) - names->push_back( - new_car.to_string(is_true ? True_clean : False_clean)); - state2car_local[s_aut] = new_car; - } - for (unsigned state = 0; state < sub_automaton->num_states(); ++state) - { - unsigned s_aut = (*init_states)[state]; - car_state src = { s_aut, state, perm_t() }; - unsigned src_state = car2num_local[src]; - for (auto e : aut_->out(s_aut)) - { - auto col = is_true ^ !is_odd; - if (((unsigned)col) > max_free_color) - throw std::runtime_error("CAR needs more sets"); - if (scc_.scc_of(s_aut) == scc_.scc_of(e.dst)) - { - for (auto c : e.acc.sets()) - if (inf_fin_prefix[c] + is_odd > col) - col = inf_fin_prefix[c] + is_odd; - acc_cond::mark_t cond = { (unsigned) col }; - res_->new_edge( - src_state, car2num_local[state2car_local[e.dst]], - e.cond, cond); - } - } - } - return sub_automaton->num_states(); -} - -unsigned -apply_copy(const twa_graph_ptr &sub_automaton, - const std::vector &permut, - bool copy_odd, - const std::vector& inf_fin_prefix, - std::map& state2car_local, - std::map& car2num_local) -{ - std::vector* init_states = sub_automaton - ->get_named_prop>("original-states"); - for (unsigned state = 0; state < sub_automaton->num_states(); ++state) - { - car_state new_car = { (*init_states)[state], state, perm_t() }; - auto new_state = res_->new_state(); - car2num_local[new_car] = new_state; - num2car.insert(num2car.begin() + new_state, new_car); - state2car_local[(*init_states)[state]] = new_car; - if (options.pretty_print) - names->push_back( - new_car.to_string(copy_odd ? Copy_odd : Copy_even)); - } - auto cond_col = sub_automaton->acc().get_acceptance().used_sets(); - for (unsigned s = 0; s < sub_automaton->num_states(); ++s) - { - for (auto e : sub_automaton->out(s)) - { - acc_cond::mark_t mark = { }; - int max_edge = -1; - for (auto col : e.acc.sets()) - { - if (cond_col.has(col)) - max_edge = std::max(max_edge, (int) permut[col]); - if (inf_fin_prefix[col] + (is_odd || copy_odd) > max_edge) - max_edge = inf_fin_prefix[col] + (is_odd || copy_odd); - } - if (max_edge != -1) - mark.set((unsigned) max_edge); - car_state src = { (*init_states)[s], s, perm_t() }, - dst = { (*init_states)[e.dst], e.dst, perm_t() }; - unsigned src_state = car2num_local[src], - dst_state = car2num_local[dst]; - res_->new_edge(src_state, dst_state, e.cond, mark); - } - } - return sub_automaton->num_states(); -} - -unsigned -apply_to_Buchi(const twa_graph_ptr& sub_automaton, - const twa_graph_ptr& buchi, - bool is_streett_to_buchi, - const std::vector& inf_fin_prefix, - unsigned max_free_color, - std::map& state2car_local, - std::map& car2num_local) -{ - std::vector* init_states = sub_automaton - ->get_named_prop>("original-states"); - - for (unsigned state = 0; state < buchi->num_states(); ++state) - { - car_state new_car = { (*init_states)[state], state, perm_t() }; - auto new_state = res_->new_state(); - car2num_local[new_car] = new_state; - num2car.insert(num2car.begin() + new_state, new_car); - state2car_local[(*init_states)[state]] = new_car; - if (options.pretty_print) - names->push_back(new_car.to_string( - is_streett_to_buchi ? Streett_to_Buchi : Rabin_to_Buchi)); - } - auto g = buchi->get_graph(); - for (unsigned s = 0; s < buchi->num_states(); ++s) - { - unsigned b = g.state_storage(s).succ; - while (b) - { - auto& e = g.edge_storage(b); - auto acc = e.acc; - acc <<= (is_odd + is_streett_to_buchi); - if ((is_odd || is_streett_to_buchi) && acc == acc_cond::mark_t{ }) - acc = { (unsigned) (is_streett_to_buchi && is_odd) }; - car_state src = { (*init_states)[s], s, perm_t() }, - dst = { (*init_states)[e.dst], e.dst, perm_t() }; - unsigned src_state = car2num_local[src], - dst_state = car2num_local[dst]; - int col = ((int) acc.max_set()) - 1; - if (col > (int) max_free_color) - throw std::runtime_error("CAR needs more sets"); - auto& e2 = sub_automaton->get_graph().edge_storage(b); - for (auto c : e2.acc.sets()) - { - if (inf_fin_prefix[c] + is_odd > col) - col = inf_fin_prefix[c] + is_odd; - } - if (col != -1) - acc = { (unsigned) col }; - else - acc = {}; - res_->new_edge(src_state, dst_state, e.cond, acc); - b = e.next_succ; - } - } - return buchi->num_states(); -} - -// Create a permutation for the first state of a SCC (IAR) -void -initial_perm_iar(std::set &perm_elem, perm_t &p0, - algorithm algo, const acc_cond::mark_t &colors, - const std::vector &pairs) -{ - for (unsigned k = 0; k != pairs.size(); ++k) - if (!inf(pairs, k, algo) || (colors & (pairs[k].fin | pairs[k].inf))) - { - perm_elem.insert(k); - p0.push_back(k); - } -} - -// Create a permutation for the first state of a SCC (CAR) -void -initial_perm_car(perm_t &p0, const acc_cond::mark_t &colors) -{ - auto cont = colors.sets(); - p0.assign(cont.begin(), cont.end()); -} - -void -find_new_perm_iar(perm_t &new_perm, - const std::vector &pairs, - const acc_cond::mark_t &acc, - algorithm algo, const std::set &perm_elem, - unsigned &seen_nb) -{ - for (unsigned k : perm_elem) - if (acc & fin(pairs, k, algo)) - { - ++seen_nb; - auto it = std::find(new_perm.begin(), new_perm.end(), k); - - // move the pair in front of the permutation - std::rotate(new_perm.begin(), it, it + 1); - } -} - -// Given the set acc of colors appearing on an edge, create a new -// permutation new_perm, and give the number seen_nb of colors moved to -// the head of the permutation. -void -find_new_perm_car(perm_t &new_perm, const acc_cond::mark_t &acc, - unsigned &seen_nb, unsigned &h) -{ - for (unsigned k : acc.sets()) - { - auto it = std::find(new_perm.begin(), new_perm.end(), k); - if (it != new_perm.end()) - { - h = std::max(h, unsigned(it - new_perm.begin()) + 1); - std::rotate(new_perm.begin(), it, it + 1); - ++seen_nb; - } - } -} - -void -get_acceptance_iar(algorithm algo, const perm_t ¤t_perm, - const std::vector &pairs, - const acc_cond::mark_t &e_acc, acc_cond::mark_t &acc) -{ - unsigned delta_acc = (algo == IAR_Streett) && is_odd; - - // find the maximal index encountered by this transition - unsigned maxint = -1U; - - for (int k = current_perm.size() - 1; k >= 0; --k) - { - unsigned pk = current_perm[k]; - - if (!inf(pairs, pk, - algo) - || (e_acc & (pairs[pk].fin | pairs[pk].inf))) - { - maxint = k; - break; - } - } - unsigned value; - - if (maxint == -1U) - value = delta_acc; - else if (e_acc & fin(pairs, current_perm[maxint], algo)) - value = 2 * maxint + 2 + delta_acc; - else - value = 2 * maxint + 1 + delta_acc; - acc = { value }; -} - -void -get_acceptance_car(const acc_cond &sub_aut_cond, const perm_t &new_perm, - unsigned h, acc_cond::mark_t &acc) -{ - acc_cond::mark_t m(new_perm.begin(), new_perm.begin() + h); - bool rej = !sub_aut_cond.accepting(m); - unsigned value = 2 * h + rej + is_odd; - acc = { value }; -} - -unsigned -apply_lar(const twa_graph_ptr &sub_automaton, - unsigned init, std::vector &pairs, - algorithm algo, unsigned scc_num, - const std::vector& inf_fin_prefix, - unsigned max_free_color, - std::map& state2car_local, - std::map& car2num_local, - unsigned max_states) -{ - auto maps = get_mark_to_vector(sub_automaton); - // For each edge e of res_, we store the elements of the permutation - // that are not moved, and we respect the order. - std::map> edge_to_colors; - unsigned nb_created_states = 0; - auto state_2_car = state_2_car_scc(sub_automaton->num_states()); - std::vector* init_states = sub_automaton-> - get_named_prop>("original-states"); - std::deque todo; - auto get_state = - [&](const car_state &s){ - auto it = car2num_local.find(s); - - if (it == car2num_local.end()) - { - ++nb_created_states; - unsigned nb = res_->new_state(); - if (options.search_ex) - state_2_car.add_res_state(s.state_scc, nb, s.perm); - car2num_local[s] = nb; - num2car.insert(num2car.begin() + nb, s); - - todo.push_back(s); - if (options.pretty_print) - names->push_back(s.to_string(algo)); - return nb; - } - return it->second; - }; - - auto colors = sub_automaton->acc().get_acceptance().used_sets(); - std::set perm_elem; - - perm_t p0 = { }; - switch (algo) - { - case IAR_Streett: - case IAR_Rabin: - initial_perm_iar(perm_elem, p0, algo, colors, pairs); - break; - case CAR: - initial_perm_car(p0, colors); - break; - default: - assert(false); - break; - } - - std::vector, std::vector>> - iar_maps; - if (algo == IAR_Streett || algo == IAR_Rabin) - iar_maps = get_iar_to_vector(sub_automaton, algo, perm_elem, pairs); - - car_state s0{ (*init_states)[init], init, p0 }; - get_state(s0); // put s0 in todo - - // the main loop - while (!todo.empty()) - { - car_state current = todo.front(); - todo.pop_front(); - - unsigned src_num = get_state(current); - for (const auto &e : sub_automaton->out(current.state_scc)) - { - perm_t new_perm = current.perm; - - // Count pairs whose fin-part is seen on this transition - unsigned seen_nb = 0; - - // consider the pairs for this SCC only - unsigned h = 0; - - switch (algo) - { - case IAR_Rabin: - case IAR_Streett: - find_new_perm_iar(new_perm, pairs, e.acc, algo, - perm_elem, seen_nb); - break; - case CAR: - find_new_perm_car(new_perm, e.acc, seen_nb, h); - break; - default: - assert(false); - } - - std::vector not_moved(new_perm.begin() + seen_nb, - new_perm.end()); - - if (options.force_order) - { - if (algo == CAR && seen_nb > 1) - { - auto map = maps[e.dst]; - acc_cond::mark_t first_vals( - new_perm.begin(), new_perm.begin() + seen_nb); - auto new_start = map.find(first_vals); - assert(new_start->second.size() >= seen_nb); - assert(new_start != map.end()); - for (unsigned i = 0; i < seen_nb; ++i) - new_perm[i] = new_start->second[i]; - } - else if ((algo == IAR_Streett || algo == IAR_Rabin) - && seen_nb > 1) - { - auto map = iar_maps[e.dst]; - std::vector first_vals( - new_perm.begin(), new_perm.begin() + seen_nb); - std::sort(std::begin(first_vals), std::end(first_vals)); - auto new_start = map.find(first_vals); - assert(new_start->second.size() >= seen_nb); - assert(new_start != map.end()); - for (unsigned i = 0; i < seen_nb; ++i) - new_perm[i] = new_start->second[i]; - } - } - - // Optimization: when several indices are seen in the - // transition, they move at the front of new_perm in any - // order. Check whether there already exists an car_state - // that matches this condition. - car_state dst; - unsigned dst_num = -1U; - - if (options.search_ex) - dst_num = state_2_car.get_res_state(e.dst, new_perm, seen_nb, - options.use_last); - - if (dst_num == -1U) - { - auto dst = car_state{ (*init_states)[e.dst], e.dst, new_perm }; - dst_num = get_state(dst); - if (nb_created_states > max_states) - return -1U; - } - - acc_cond::mark_t acc = { }; - - switch (algo) - { - case IAR_Rabin: - case IAR_Streett: - get_acceptance_iar(algo, current.perm, pairs, e.acc, acc); - break; - case CAR: - get_acceptance_car(sub_automaton->acc(), new_perm, h, acc); - break; - default: - assert(false); - } - - unsigned acc_col = acc.min_set() - 1; - if (options.parity_prefix) - { - if (acc_col > max_free_color) - throw std::runtime_error("CAR needs more sets"); - // parity prefix - for (auto col : e.acc.sets()) - { - if (inf_fin_prefix[col] + is_odd > (int) acc_col) - acc_col = (unsigned) inf_fin_prefix[col] + is_odd; - } - } - auto new_e = res_->new_edge(src_num, dst_num, e.cond, { acc_col }); - edge_to_colors.insert({new_e, not_moved}); - } - } - if (options.search_ex && options.use_last) - { - std::vector added_states; - std::transform(car2num_local.begin(), car2num_local.end(), - std::back_inserter(added_states), - [](std::pair pair) { - return pair.second; - }); - change_transitions_destination( - res_, added_states, edge_to_colors, state_2_car); - } - auto leaving_edge = - [&](unsigned d){ - return scc_.scc_of(num2car.at(d).state) != scc_num; - }; - auto filter_edge = - [](const twa_graph::edge_storage_t &, - unsigned dst, - void* filter_data){ - decltype(leaving_edge) *data = - static_cast(filter_data); - - if ((*data)(dst)) - return scc_info::edge_filter_choice::ignore; - - return scc_info::edge_filter_choice::keep; - }; - scc_info sub_scc(res_, get_state(s0), filter_edge, &leaving_edge); - - // SCCs are numbered in reverse topological order, so the bottom SCC has - // index 0. - const unsigned bscc = 0; - assert(sub_scc.scc_count() != 0); - assert(sub_scc.succ(0).empty()); - assert( - [&](){ - for (unsigned s = 1; s != sub_scc.scc_count(); ++s) - if (sub_scc.succ(s).empty()) - return false; - - return true; - } ()); - - assert(sub_scc.states_of(bscc).size() >= sub_automaton->num_states()); - - // update state2car - for (unsigned scc_state : sub_scc.states_of(bscc)) - { - car_state &car = num2car.at(scc_state); - - if (state2car_local.find(car.state) == state2car_local.end()) - state2car_local[car.state] = car; - } - return sub_scc.states_of(bscc).size(); -} - -algorithm -chooseAlgo(twa_graph_ptr &sub_automaton, - twa_graph_ptr &rabin_aut, - std::vector &pairs, - std::vector &permut) -{ - auto scc_condition = sub_automaton->acc(); - if (options.parity_equiv) - { - if (scc_condition.is_f()) - return False_clean; - if (scc_condition.is_t()) - return True_clean; - std::vector permut_tmp(scc_condition.all_sets().max_set(), -1); - - if (!is_odd && scc_condition.is_parity_max_equiv(permut_tmp, true)) - { - for (auto c : permut_tmp) - permut.push_back((unsigned) c); - - scc_condition.apply_permutation(permut); - sub_automaton->apply_permutation(permut); - return Copy_even; - } - std::fill(permut_tmp.begin(), permut_tmp.end(), -1); - if (scc_condition.is_parity_max_equiv(permut_tmp, false)) - { - for (auto c : permut_tmp) - permut.push_back((unsigned) c); - scc_condition.apply_permutation(permut); - sub_automaton->apply_permutation(permut); - return Copy_odd; - } - } - - if (options.rabin_to_buchi) - { - auto ra = rabin_to_buchi_if_realizable(sub_automaton); - if (ra != nullptr) - { - rabin_aut = ra; - return Rabin_to_Buchi; - } - else - { - bool streett_buchi = false; - auto sub_cond = sub_automaton->get_acceptance(); - sub_automaton->set_acceptance(sub_cond.complement()); - auto ra = rabin_to_buchi_if_realizable(sub_automaton); - streett_buchi = (ra != nullptr); - sub_automaton->set_acceptance(sub_cond); - if (streett_buchi) - { - rabin_aut = ra; - return Streett_to_Buchi; - } - } - } - - auto pairs1 = std::vector(); - auto pairs2 = std::vector(); - std::sort(pairs1.begin(), pairs1.end()); - pairs1.erase(std::unique(pairs1.begin(), pairs1.end()), pairs1.end()); - std::sort(pairs2.begin(), pairs2.end()); - pairs2.erase(std::unique(pairs2.begin(), pairs2.end()), pairs2.end()); - bool is_r_like = scc_condition.is_rabin_like(pairs1); - bool is_s_like = scc_condition.is_streett_like(pairs2); - unsigned num_cols = scc_condition.get_acceptance().used_sets().count(); - if (is_r_like) - { - if ((is_s_like && pairs1.size() < pairs2.size()) || !is_s_like) - { - if (pairs1.size() > num_cols) - return CAR; - pairs = pairs1; - return IAR_Rabin; - } - else if (is_s_like) - { - if (pairs2.size() > num_cols) - return CAR; - pairs = pairs2; - return IAR_Streett; - } - } - else - { - if (is_s_like) - { - if (pairs2.size() > num_cols) - return CAR; - pairs = pairs2; - return IAR_Streett; - } - } - return CAR; -} - -unsigned -build_scc(twa_graph_ptr &sub_automaton, - unsigned scc_num, - std::map& state2car_local, - std::map&car2num_local, - algorithm& algo, - unsigned max_states = -1U) -{ - - std::vector parity_prefix_colors (SPOT_MAX_ACCSETS, - - SPOT_MAX_ACCSETS - 2); - unsigned min_prefix_color = SPOT_MAX_ACCSETS + 1; - if (options.parity_prefix) - { - auto new_acc = sub_automaton->acc(); - auto colors = std::vector(); - bool inf_start = - sub_automaton->acc().has_parity_prefix(new_acc, colors); - sub_automaton->set_acceptance(new_acc); - for (unsigned i = 0; i < colors.size(); ++i) - parity_prefix_colors[colors[i]] = - SPOT_MAX_ACCSETS - 4 - i - !inf_start; - if (colors.size() > 0) - min_prefix_color = - SPOT_MAX_ACCSETS - 4 - colors.size() - 1 - !inf_start; - } - --min_prefix_color; - - unsigned init = 0; - - std::vector pairs = { }; - auto permut = std::vector(); - twa_graph_ptr rabin_aut = nullptr; - algo = chooseAlgo(sub_automaton, rabin_aut, pairs, permut); - switch (algo) - { - case False_clean: - case True_clean: - return apply_false_true_clean(sub_automaton, (algo == True_clean), - parity_prefix_colors, min_prefix_color, - state2car_local, car2num_local); - break; - case IAR_Streett: - case IAR_Rabin: - case CAR: - return apply_lar(sub_automaton, init, pairs, algo, scc_num, - parity_prefix_colors, min_prefix_color, - state2car_local, car2num_local, max_states); - break; - case Copy_odd: - case Copy_even: - return apply_copy(sub_automaton, permut, algo == Copy_odd, - parity_prefix_colors, state2car_local, - car2num_local); - break; - case Rabin_to_Buchi: - case Streett_to_Buchi: - return apply_to_Buchi(sub_automaton, rabin_aut, - (algo == Streett_to_Buchi), - parity_prefix_colors, min_prefix_color, - state2car_local, car2num_local); - break; - default: - break; - } - return -1U; -} - -public: -twa_graph_ptr -run() -{ - res_ = make_twa_graph(aut_->get_dict()); - res_->copy_ap_of(aut_); - for (unsigned scc = 0; scc < scc_.scc_count(); ++scc) - { - if (!scc_.is_useful_scc(scc)) - continue; - auto sub_automata = scc_.split_on_sets(scc, { }, true); - if (sub_automata.empty()) - { - for (auto state : scc_.states_of(scc)) - { - auto new_state = res_->new_state(); - car_state new_car = { state, state, perm_t() }; - car2num[new_car] = new_state; - num2car.insert(num2car.begin() + new_state, new_car); - if (options.pretty_print) - names->push_back(new_car.to_string(None)); - state2car[state] = new_car; - } - continue; - } - - auto sub_automaton = sub_automata[0]; - auto deg = sub_automaton; - if (options.acc_clean) - simplify_acceptance_here(sub_automaton); - bool has_degeneralized = false; - if (options.partial_degen) - { - std::vector forbid; - auto m = - is_partially_degeneralizable(sub_automaton, true, - true, forbid); - while (m != acc_cond::mark_t {}) - { - auto tmp = partial_degeneralize(deg, m); - simplify_acceptance_here(tmp); - if (tmp->get_acceptance().used_sets().count() - < deg->get_acceptance().used_sets().count() || - !(options.reduce_col_deg)) - { - deg = tmp; - has_degeneralized = true; - } - else - forbid.push_back(m); - m = is_partially_degeneralizable(deg, true, true, forbid); - } - } - - if (options.propagate_col) - { - propagate_marks_here(sub_automaton); - if (deg != sub_automaton) - propagate_marks_here(deg); - } - - std::map state2car_sub, state2car_deg; - std::map car2num_sub, car2num_deg; - - unsigned nb_states_deg = -1U, - nb_states_sub = -1U; - - algorithm algo_sub, algo_deg; - unsigned max_states_sub_car = -1U; - // We try with and without degeneralization and we keep the best. - if (has_degeneralized) - { - nb_states_deg = - build_scc(deg, scc, state2car_deg, car2num_deg, algo_deg); - // We suppose that if we see nb_states_deg + 1000 states when - // when construct the version without degeneralization during the - // construction, we will not be able to have nb_states_deg after - // removing useless states. So we will stop the execution. - max_states_sub_car = - 10000 + nb_states_deg - 1; - } - if (!options.force_degen || !has_degeneralized) - nb_states_sub = - build_scc(sub_automaton, scc, state2car_sub, car2num_sub, - algo_sub, max_states_sub_car); - if (nb_states_deg < nb_states_sub) - { - state2car.insert(state2car_deg.begin(), state2car_deg.end()); - car2num.insert(car2num_deg.begin(), car2num_deg.end()); - algo_sub = algo_deg; - } - else - { - state2car.insert(state2car_sub.begin(), state2car_sub.end()); - car2num.insert(car2num_sub.begin(), car2num_sub.end()); - } - if ((algo_sub == IAR_Rabin || algo_sub == Copy_odd) && !is_odd) - { - is_odd = true; - for (auto &edge : res_->edges()) - { - if (scc_.scc_of(num2car[edge.src].state) != scc - && scc_.scc_of(num2car[edge.dst].state) != scc) - { - if (edge.acc == acc_cond::mark_t{}) - edge.acc = { 0 }; - else - edge.acc <<= 1; - } - } - } - } - - for (unsigned state = 0; state < res_->num_states(); ++state) - { - unsigned original_state = num2car.at(state).state; - auto state_scc = scc_.scc_of(original_state); - for (auto edge : aut_->out(original_state)) - { - if (scc_.scc_of(edge.dst) != state_scc) - { - auto car = state2car.find(edge.dst); - if (car != state2car.end()) - { - unsigned res_dst = car2num.at(car->second); - res_->new_edge(state, res_dst, edge.cond, { }); - } - } - } - } - unsigned initial_state = aut_->get_init_state_number(); - auto initial_car_ptr = state2car.find(initial_state); - car_state initial_car; - // If we take an automaton with one state and without transition, - // the SCC was useless so state2car doesn't have initial_state - if (initial_car_ptr == state2car.end()) - { - assert(res_->num_states() == 0); - auto new_state = res_->new_state(); - car_state new_car = {initial_state, 0, perm_t()}; - state2car[initial_state] = new_car; - if (options.pretty_print) - names->push_back(new_car.to_string(None)); - num2car.insert(num2car.begin() + new_state, new_car); - car2num[new_car] = new_state; - initial_car = new_car; - } - else - initial_car = initial_car_ptr->second; - auto initial_state_res = car2num.find(initial_car); - if (initial_state_res != car2num.end()) - res_->set_init_state(initial_state_res->second); - else - res_->new_state(); - if (options.pretty_print) - res_->set_named_prop("state-names", names); - - res_->purge_unreachable_states(); - // If parity_prefix is used, we use all available colors by - // default: The IAR/CAR are using lower indices, and the prefix is - // using the upper indices. So we use reduce_parity() to clear - // the mess. If parity_prefix is not used, - unsigned max_color = SPOT_MAX_ACCSETS; - if (!options.parity_prefix) - { - acc_cond::mark_t all = {}; - for (auto& e: res_->edges()) - all |= e.acc; - max_color = all.max_set(); - } - res_->set_acceptance(acc_cond::acc_code::parity_max(is_odd, max_color)); - if (options.parity_prefix) - reduce_parity_here(res_); - return res_; -} - -private: -const const_twa_graph_ptr &aut_; -const scc_info scc_; -twa_graph_ptr res_; -// Says if we constructing an odd or even max -bool is_odd; - -std::vector num2car; -std::map state2car; -std::map car2num; - -to_parity_options options; - -std::vector* names; -}; // car_generator - -}// namespace - - -twa_graph_ptr -to_parity(const const_twa_graph_ptr &aut, const to_parity_options options) -{ - return car_generator(aut, options).run(); -} - - // Old version of CAR. - namespace - { - struct lar_state - { - unsigned state; - std::vector perm; - - bool operator<(const lar_state& s) const - { - return state == s.state ? perm < s.perm : state < s.state; - } - - std::string to_string() const - { - std::stringstream s; - s << state << " ["; - unsigned ps = perm.size(); - for (unsigned i = 0; i != ps; ++i) - { - if (i > 0) - s << ','; - s << perm[i]; - } - s << ']'; - return s.str(); - } - }; - - class lar_generator - { - const const_twa_graph_ptr& aut_; - twa_graph_ptr res_; - const bool pretty_print; - - std::map lar2num; - public: - explicit lar_generator(const const_twa_graph_ptr& a, bool pretty_print) - : aut_(a) - , res_(nullptr) - , pretty_print(pretty_print) - {} - - twa_graph_ptr run() - { - res_ = make_twa_graph(aut_->get_dict()); - res_->copy_ap_of(aut_); - - std::deque todo; - auto get_state = [this, &todo](const lar_state& s) - { - auto it = lar2num.emplace(s, -1U); - if (it.second) // insertion took place - { - unsigned nb = res_->new_state(); - it.first->second = nb; - todo.push_back(s); - } - return it.first->second; - }; - - std::vector initial_perm(aut_->num_sets()); - std::iota(initial_perm.begin(), initial_perm.end(), 0); - { - lar_state s0{aut_->get_init_state_number(), initial_perm}; - res_->set_init_state(get_state(s0)); - } - - scc_info si(aut_, scc_info_options::NONE); - // main loop - while (!todo.empty()) - { - lar_state current = todo.front(); - todo.pop_front(); - - // TODO: todo could store this number to avoid one lookup - unsigned src_num = get_state(current); - - unsigned source_scc = si.scc_of(current.state); - for (const auto& e : aut_->out(current.state)) - { - // find the new permutation - std::vector new_perm = current.perm; - unsigned h = 0; - for (unsigned k : e.acc.sets()) - { - auto it = std::find(new_perm.begin(), new_perm.end(), k); - h = std::max(h, unsigned(new_perm.end() - it)); - std::rotate(it, it+1, new_perm.end()); - } - - if (source_scc != si.scc_of(e.dst)) - { - new_perm = initial_perm; - h = 0; - } - - lar_state dst{e.dst, new_perm}; - unsigned dst_num = get_state(dst); - - // Do the h last elements satisfy the acceptance condition? - // If they do, emit 2h, if they don't emit 2h+1. - acc_cond::mark_t m(new_perm.end() - h, new_perm.end()); - bool rej = !aut_->acc().accepting(m); - res_->new_edge(src_num, dst_num, e.cond, {2*h + rej}); - } - } - - // parity max even - unsigned sets = 2*aut_->num_sets() + 2; - res_->set_acceptance(sets, acc_cond::acc_code::parity_max_even(sets)); - - if (pretty_print) - { - auto names = new std::vector(res_->num_states()); - for (const auto& p : lar2num) - (*names)[p.second] = p.first.to_string(); - res_->set_named_prop("state-names", names); - } - - return res_; - } - }; - } - twa_graph_ptr - to_parity_old(const const_twa_graph_ptr& aut, bool pretty_print) + iar_maybe(const const_twa_graph_ptr &aut, bool pretty_print) { - if (!aut->is_existential()) - throw std::runtime_error("LAR does not handle alternation"); - // if aut is already parity return it as is - if (aut->acc().is_parity()) - return std::const_pointer_cast(aut); - - lar_generator gen(aut, pretty_print); - return gen.run(); + return iar_maybe_(aut, pretty_print); } - } diff --git a/spot/twaalgos/toparity.hh b/spot/twaalgos/toparity.hh index 6aecf7659..d82403aa5 100644 --- a/spot/twaalgos/toparity.hh +++ b/spot/twaalgos/toparity.hh @@ -19,10 +19,24 @@ #pragma once -#include +#include +#include +#include namespace spot { + /// Structure used by to_parity to store some information about the + /// construction + struct to_parity_data + { + /// Total number of states created + unsigned nb_states_created = 0; + /// Total number of edges created + unsigned nb_edges_created = 0; + /// Name of algorithms used + std::vector algorithms_used; + }; + /// \ingroup twa_acc_transform /// \brief Options to control various optimizations of to_parity(). struct to_parity_options @@ -35,6 +49,9 @@ namespace spot /// most recent state when we find multiple existing state /// compatible with the current move. bool use_last = true; + /// If \c use_last_post_process is true, then when LAR ends, it tries to + /// replace the destination of an edge by the newest compatible state. + bool use_last_post_process = false; /// If \c force_order is true, we force to use an order when CAR or IAR is /// applied. Given a state s and a set E ({0}, {0 1}, {2} for example) we /// construct an order on colors. With the given example, we ask to have @@ -45,16 +62,26 @@ namespace spot /// degeneralization to remove occurrences of acceptance /// subformulas such as `Fin(x) | Fin(y)` or `Inf(x) & Inf(y)`. bool partial_degen = true; - /// If \c force_degen is false, to_parity will checks if we can - /// get a better result if we don't apply partial_degeneralize. - bool force_degen = true; /// If \c scc_acc_clean is true, to_parity() will ignore colors /// not occurring in an SCC while processing this SCC. bool acc_clean = true; /// If \c parity_equiv is true, to_parity() will check if there - /// exists a permutations of colors such that the acceptance - /// condition is a parity condition. + /// exists a way to see the acceptance condition as a parity max one. bool parity_equiv = true; + /// If \c Car is true, to_parity will try to apply CAR. It is a version of + /// LAR that tracks colors. + bool car = true; + /// If \c tar is true, to_parity will try to apply TAR. It is a version of + /// LAR that tracks transitions instead of colors. + bool tar = false; + /// If \c iar is true, to_parity will try to apply IAR. + bool iar = true; + /// if \c lar_dfs is true, then when LAR is used the next state of the + /// result that will be processed is the last created state. + bool lar_dfs = true; + /// If \c bscc is true, to_parity() will only keep the bottommost automaton + /// when it applies LAR. + bool bscc = true; /// If \c parity_prefix is true, to_parity() will use a special /// handling for acceptance conditions of the form `Inf(m0) | /// (Fin(m1) & (Inf(m2) | (… β)))` that start as a parity @@ -62,30 +89,42 @@ namespace spot /// `β` can be an arbitrary formula. In this case, the paritization /// algorithm is really applied only to `β`, and the marks of the /// prefix are appended after a suitable renumbering. - /// - /// For technical reasons, activating this option (and this is the - /// default) causes reduce_parity() to be called at the end to - /// minimize the number of colors used. It is therefore - /// recommended to disable this option when one wants to follow - /// the output CAR/IAR constructions. bool parity_prefix = true; + /// If \c parity_prefix_general is true, to_parity() will rewrite the + /// acceptance condition as `Inf(m0) | (Fin(m1) & (Inf(m2) | (… β)))` before + /// applying the same construction as with the option \c parity_prefix. + bool parity_prefix_general = false; + /// If \c generic_emptiness is true, to_parity() will check if the automaton + /// does not accept any word with an emptiness check algorithm. + bool generic_emptiness = false; /// If \c rabin_to_buchi is true, to_parity() tries to convert a Rabin or /// Streett condition to Büchi or co-Büchi with /// rabin_to_buchi_if_realizable(). bool rabin_to_buchi = true; - /// Only allow degeneralization if it reduces the number of colors in the - /// acceptance condition. + /// If \c buchi_type_to_buchi is true, to_parity converts a + /// (co-)Büchi type automaton to a (co-)Büchi automaton. + bool buchi_type_to_buchi = false; + /// If \c parity_type_to_parity is true, to_parity converts a + /// parity type automaton to a parity automaton. + bool parity_type_to_parity = false; + /// Only allow partial degeneralization if it reduces the number of colors + /// in the acceptance condition or if it implies to apply IAR instead of + /// CAR. bool reduce_col_deg = false; /// Use propagate_marks_here to increase the number of marks on transition /// in order to move more colors (and increase the number of /// compatible states) when we apply LAR. bool propagate_col = true; + /// If \c use_generalized_buchi is true, each SCC will use a generalized + /// Rabin acceptance in order to avoid CAR. + bool use_generalized_rabin = false; /// If \c pretty_print is true, states of the output automaton are /// named to help debugging. bool pretty_print = false; + /// Structure used to store some information about the construction. + to_parity_data* datas = nullptr; }; - /// \ingroup twa_acc_transform /// \brief Take an automaton with any acceptance condition and return an /// equivalent parity automaton. diff --git a/spot/twaalgos/zlktree.hh b/spot/twaalgos/zlktree.hh index b8e47bc2a..d210033e3 100644 --- a/spot/twaalgos/zlktree.hh +++ b/spot/twaalgos/zlktree.hh @@ -181,7 +181,6 @@ namespace spot /// \brief Render the tree as in GraphViz format. void dot(std::ostream&) const; - private: struct zielonka_node { unsigned parent; @@ -191,6 +190,7 @@ namespace spot acc_cond::mark_t colors; }; std::vector nodes_; + private: unsigned one_branch_ = 0; unsigned num_branches_ = 0; bool is_even_; diff --git a/tests/core/ltlsynt.test b/tests/core/ltlsynt.test index 6cb449012..95e0bf4d7 100644 --- a/tests/core/ltlsynt.test +++ b/tests/core/ltlsynt.test @@ -58,11 +58,11 @@ parity 13; 13 1 1 6,10; 1 1 1 6,3; parity 5; -1 1 0 4,5 "INIT"; +0 1 0 2,3 "INIT"; +3 3 1 1; +1 1 0 4,5; 5 2 1 1,1; 4 3 1 0,1; -0 1 0 2,3; -3 3 1 1; 2 1 1 0,0; EOF @@ -414,13 +414,13 @@ grep 'DPA has 29 states' err ltlsynt --verbose -x wdba-minimize=1 --algo=ps --outs=p1 --ins=p0 -f "$f" 2>err grep 'DPA has 12 states' err -ltlsynt --outs=p1 -f "$f" -x"dpa-simul=1" --simpl=no | grep 'States: 5' -ltlsynt --outs=p1 -f "$f" -x"dpa-simul=1" --simpl=bisim | grep 'States: 5' -ltlsynt --outs=p1 -f "$f" -x"dpa-simul=1" --simpl=bwoa | grep 'States: 4' -ltlsynt --outs=p1 -f "$f" -x"dpa-simul=1" | grep 'States: 4' -ltlsynt --outs=p1 -f "$f" -x"dpa-simul=1" --simpl=sat | grep 'States: 2' -ltlsynt --outs=p1 -f "$f" -x"dpa-simul=1" --simpl=bisim-sat | grep 'States: 2' -ltlsynt --outs=p1 -f "$f" -x"dpa-simul=1" --simpl=bwoa-sat | grep 'States: 4' +ltlsynt --outs=p1 -f "$f" -x"dpa-simul=1" --simpl=no | grep 'States: 7' +ltlsynt --outs=p1 -f "$f" -x"dpa-simul=1" --simpl=bisim | grep 'States: 7' +ltlsynt --outs=p1 -f "$f" -x"dpa-simul=1" --simpl=bwoa | grep 'States: 6' +ltlsynt --outs=p1 -f "$f" -x"dpa-simul=1" | grep 'States: 6' +ltlsynt --outs=p1 -f "$f" -x"dpa-simul=1" --simpl=sat | grep 'States: 3' +ltlsynt --outs=p1 -f "$f" -x"dpa-simul=1" --simpl=bisim-sat | grep 'States: 3' +ltlsynt --outs=p1 -f "$f" -x"dpa-simul=1" --simpl=bwoa-sat | grep 'States: 6' # The following used to raise an exception because of a bug in # split_2step_fast_here(). @@ -707,7 +707,7 @@ automaton has 4 states and 1 colors LAR construction done in X seconds DPA has 4 states, 1 colors split inputs and outputs done in X seconds -automaton has 9 states +automaton has 10 states solving game with acceptance: Büchi game solved in X seconds simplification took X seconds diff --git a/tests/python/games.ipynb b/tests/python/games.ipynb index 891ebcd94..f3ffd7502 100644 --- a/tests/python/games.ipynb +++ b/tests/python/games.ipynb @@ -47,153 +47,153 @@ "\n", "\n", - "\n", "\n", "\n", "\n", - "\n", - "t\n", - "[all]\n", + "\n", + "t\n", + "[all]\n", "\n", "\n", "\n", "0\n", - "\n", - "0\n", + "\n", + "0\n", "\n", "\n", "\n", "I->0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "1\n", - "\n", - "1\n", + "\n", + "1\n", "\n", "\n", "\n", "0->1\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "3\n", - "\n", - "3\n", + "\n", + "3\n", "\n", "\n", "\n", "0->3\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "1->0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "2\n", - "\n", - "2\n", + "\n", + "2\n", "\n", "\n", "\n", "1->2\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "3->2\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "4\n", - "\n", - "4\n", + "\n", + "4\n", "\n", "\n", "\n", "3->4\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "6\n", - "\n", - "6\n", + "\n", + "6\n", "\n", "\n", "\n", "3->6\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "2->1\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "5\n", - "\n", - "5\n", + "\n", + "5\n", "\n", "\n", "\n", "2->5\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "7\n", - "\n", - "7\n", + "\n", + "7\n", "\n", "\n", "\n", "6->7\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "7->6\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "8\n", - "\n", - "8\n", + "\n", + "8\n", "\n", "\n", "\n", "7->8\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "8->5\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n" @@ -285,153 +285,153 @@ "\n", "\n", - "\n", "\n", "\n", "\n", - "\n", - "t\n", - "[all]\n", + "\n", + "t\n", + "[all]\n", "\n", "\n", "\n", "0\n", - "\n", - "0\n", + "\n", + "0\n", "\n", "\n", "\n", "I->0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "1\n", - "\n", - "1\n", + "\n", + "1\n", "\n", "\n", "\n", "0->1\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "3\n", - "\n", - "3\n", + "\n", + "3\n", "\n", "\n", "\n", "0->3\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "1->0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "2\n", - "\n", - "2\n", + "\n", + "2\n", "\n", "\n", "\n", "1->2\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "3->2\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "4\n", - "\n", - "4\n", + "\n", + "4\n", "\n", "\n", "\n", "3->4\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "6\n", - "\n", - "6\n", + "\n", + "6\n", "\n", "\n", "\n", "3->6\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "2->1\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "5\n", - "\n", - "5\n", + "\n", + "5\n", "\n", "\n", "\n", "2->5\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "7\n", - "\n", - "7\n", + "\n", + "7\n", "\n", "\n", "\n", "6->7\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "7->6\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "8\n", - "\n", - "8\n", + "\n", + "8\n", "\n", "\n", "\n", "7->8\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "8->5\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n" @@ -501,153 +501,153 @@ "\n", "\n", - "\n", "\n", "\n", "\n", - "\n", - "t\n", - "[all]\n", + "\n", + "t\n", + "[all]\n", "\n", "\n", "\n", "0\n", "\n", - "0\n", + "0\n", "\n", "\n", "\n", "I->0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "1\n", "\n", - "1\n", + "1\n", "\n", "\n", "\n", "0->1\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "3\n", "\n", - "3\n", + "3\n", "\n", "\n", "\n", "0->3\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "1->0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "2\n", "\n", - "2\n", + "2\n", "\n", "\n", "\n", "1->2\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "3->2\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "4\n", "\n", - "4\n", + "4\n", "\n", "\n", "\n", "3->4\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "6\n", "\n", - "6\n", + "6\n", "\n", "\n", "\n", "3->6\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "2->1\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "5\n", "\n", - "5\n", + "5\n", "\n", "\n", "\n", "2->5\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "7\n", "\n", - "7\n", + "7\n", "\n", "\n", "\n", "6->7\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "7->6\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "8\n", "\n", - "8\n", + "8\n", "\n", "\n", "\n", "7->8\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "8->5\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n" @@ -686,233 +686,218 @@ "\n", "\n", - "\n", "\n", - "\n", - "\n", - "\n", - "Fin(\n", - "\n", - ")\n", - "[co-Büchi]\n", + "\n", + "\n", + "\n", + "Fin(\n", + "\n", + ")\n", + "[co-Büchi]\n", "\n", - "\n", - "\n", - "4\n", - "\n", - "4\n", - "\n", - "\n", - "\n", - "I->4\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "10\n", - "\n", - "10\n", - "\n", - "\n", - "\n", - "4->10\n", - "\n", - "\n", - "!a\n", - "\n", - "\n", - "\n", - "11\n", - "\n", - "11\n", - "\n", - "\n", - "\n", - "4->11\n", - "\n", - "\n", - "a\n", - "\n", "\n", - "\n", + "\n", "0\n", - "\n", - "0\n", + "\n", + "0\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", "\n", "\n", - "\n", + "\n", "5\n", - "\n", - "5\n", + "\n", + "5\n", "\n", "\n", "\n", "0->5\n", - "\n", - "\n", - "!a\n", + "\n", + "\n", + "!a\n", "\n", "\n", - "\n", + "\n", "6\n", - "\n", - "6\n", + "\n", + "6\n", "\n", "\n", "\n", "0->6\n", - "\n", - "\n", - "a\n", - "\n", - "\n", - "\n", - "\n", - "5->0\n", - "\n", - "\n", - "1\n", + "\n", + "\n", + "a\n", "\n", "\n", - "\n", + "\n", "1\n", - "\n", - "1\n", + "\n", + "1\n", "\n", - "\n", + "\n", + "\n", + "5->1\n", + "\n", + "\n", + "!b\n", + "\n", + "\n", + "\n", + "3\n", + "\n", + "3\n", + "\n", + "\n", "\n", - "6->1\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "\n", - "1->6\n", - "\n", - "\n", - "a\n", - "\n", - "\n", - "\n", - "\n", - "7\n", - "\n", - "7\n", - "\n", - "\n", - "\n", - "1->7\n", - "\n", - "\n", - "!a\n", - "\n", - "\n", - "\n", - "\n", - "7->0\n", - "\n", - "\n", - "1\n", - "\n", + "5->3\n", + "\n", + "\n", + "b\n", "\n", "\n", "\n", "2\n", - "\n", - "2\n", + "\n", + "2\n", + "\n", + "\n", + "\n", + "6->2\n", + "\n", + "\n", + "!b\n", + "\n", + "\n", + "\n", + "6->3\n", + "\n", + "\n", + "b\n", + "\n", + "\n", + "\n", + "7\n", + "\n", + "7\n", + "\n", + "\n", + "\n", + "1->7\n", + "\n", + "\n", + "!a\n", "\n", "\n", - "\n", + "\n", "8\n", - "\n", - "8\n", + "\n", + "8\n", "\n", - "\n", - "\n", - "2->8\n", - "\n", - "\n", - "1\n", + "\n", + "\n", + "1->8\n", + "\n", + "\n", + "a\n", + "\n", + "\n", + "\n", + "\n", + "7->1\n", + "\n", + "\n", + "1\n", "\n", "\n", - "\n", + "\n", "8->2\n", - "\n", - "\n", - "1\n", + "\n", + "\n", + "1\n", + "\n", "\n", - "\n", - "\n", - "3\n", - "\n", - "3\n", + "\n", + "\n", + "2->7\n", + "\n", + "\n", + "!a\n", + "\n", + "\n", + "\n", + "2->8\n", + "\n", + "\n", + "a\n", + "\n", "\n", "\n", - "\n", + "\n", "9\n", - "\n", - "9\n", + "\n", + "9\n", "\n", "\n", - "\n", + "\n", "3->9\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "9->2\n", - "\n", - "\n", - "!b\n", + "\n", + "\n", + "1\n", "\n", "\n", - "\n", - "9->3\n", - "\n", - "\n", - "b\n", - "\n", - "\n", - "\n", "\n", - "10->0\n", - "\n", - "\n", - "!b\n", + "9->3\n", + "\n", + "\n", + "b\n", + "\n", "\n", - "\n", + "\n", + "\n", + "4\n", + "\n", + "4\n", + "\n", + "\n", "\n", - "10->3\n", - "\n", - "\n", - "b\n", + "9->4\n", + "\n", + "\n", + "!b\n", "\n", - "\n", + "\n", + "\n", + "10\n", + "\n", + "10\n", + "\n", + "\n", + "\n", + "4->10\n", + "\n", + "\n", + "1\n", + "\n", + "\n", "\n", - "11->1\n", - "\n", - "\n", - "!b\n", - "\n", - "\n", - "\n", - "11->3\n", - "\n", - "\n", - "b\n", + "10->4\n", + "\n", + "\n", + "1\n", "\n", "\n", "\n" ], "text/plain": [ - " *' at 0x7f657c403180> >" + " *' at 0x7f6be431fbd0> >" ] }, "execution_count": 8, @@ -944,46 +929,44 @@ "output_type": "stream", "text": [ "HOA: v1\n", - "States: 12\n", - "Start: 4\n", + "States: 11\n", + "Start: 0\n", "AP: 2 \"b\" \"a\"\n", "acc-name: co-Buchi\n", "Acceptance: 1 Fin(0)\n", "properties: trans-labels explicit-labels trans-acc complete\n", "properties: deterministic\n", - "spot-state-player: 0 0 0 0 0 1 1 1 1 1 1 1\n", + "spot-state-player: 0 0 0 0 0 1 1 1 1 1 1\n", "controllable-AP: 0\n", "--BODY--\n", "State: 0\n", "[!1] 5\n", - "[1] 6 {0}\n", + "[1] 6\n", "State: 1\n", - "[1] 6 {0}\n", - "[!1] 7 {0}\n", + "[!1] 7\n", + "[1] 8 {0}\n", "State: 2\n", - "[t] 8\n", + "[!1] 7\n", + "[1] 8 {0}\n", "State: 3\n", "[t] 9\n", "State: 4\n", - "[!1] 10\n", - "[1] 11\n", + "[t] 10\n", "State: 5\n", - "[t] 0\n", - "State: 6\n", - "[t] 1 {0}\n", - "State: 7\n", - "[t] 0 {0}\n", - "State: 8\n", - "[t] 2\n", - "State: 9\n", - "[!0] 2\n", - "[0] 3 {0}\n", - "State: 10\n", - "[!0] 0\n", - "[0] 3\n", - "State: 11\n", "[!0] 1\n", "[0] 3\n", + "State: 6\n", + "[!0] 2\n", + "[0] 3\n", + "State: 7\n", + "[t] 1\n", + "State: 8\n", + "[t] 2 {0}\n", + "State: 9\n", + "[0] 3 {0}\n", + "[!0] 4\n", + "State: 10\n", + "[t] 4\n", "--END--\n" ] } @@ -1030,233 +1013,218 @@ "\n", "\n", - "\n", "\n", - "\n", - "\n", - "\n", - "Fin(\n", - "\n", - ")\n", - "[co-Büchi]\n", + "\n", + "\n", + "\n", + "Fin(\n", + "\n", + ")\n", + "[co-Büchi]\n", "\n", - "\n", - "\n", - "4\n", - "\n", - "4\n", - "\n", - "\n", - "\n", - "I->4\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "10\n", - "\n", - "10\n", - "\n", - "\n", - "\n", - "4->10\n", - "\n", - "\n", - "!a\n", - "\n", - "\n", - "\n", - "11\n", - "\n", - "11\n", - "\n", - "\n", - "\n", - "4->11\n", - "\n", - "\n", - "a\n", - "\n", "\n", - "\n", + "\n", "0\n", - "\n", - "0\n", + "\n", + "0\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", "\n", "\n", - "\n", + "\n", "5\n", - "\n", - "5\n", + "\n", + "5\n", "\n", "\n", "\n", "0->5\n", - "\n", - "\n", - "!a\n", + "\n", + "\n", + "!a\n", "\n", "\n", - "\n", + "\n", "6\n", - "\n", - "6\n", + "\n", + "6\n", "\n", "\n", "\n", "0->6\n", - "\n", - "\n", - "a\n", - "\n", - "\n", - "\n", - "\n", - "5->0\n", - "\n", - "\n", - "1\n", + "\n", + "\n", + "a\n", "\n", "\n", - "\n", + "\n", "1\n", - "\n", - "1\n", + "\n", + "1\n", "\n", - "\n", + "\n", + "\n", + "5->1\n", + "\n", + "\n", + "!b\n", + "\n", + "\n", + "\n", + "3\n", + "\n", + "3\n", + "\n", + "\n", "\n", - "6->1\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "\n", - "1->6\n", - "\n", - "\n", - "a\n", - "\n", - "\n", - "\n", - "\n", - "7\n", - "\n", - "7\n", - "\n", - "\n", - "\n", - "1->7\n", - "\n", - "\n", - "!a\n", - "\n", - "\n", - "\n", - "\n", - "7->0\n", - "\n", - "\n", - "1\n", - "\n", + "5->3\n", + "\n", + "\n", + "b\n", "\n", "\n", "\n", "2\n", - "\n", - "2\n", + "\n", + "2\n", + "\n", + "\n", + "\n", + "6->2\n", + "\n", + "\n", + "!b\n", + "\n", + "\n", + "\n", + "6->3\n", + "\n", + "\n", + "b\n", + "\n", + "\n", + "\n", + "7\n", + "\n", + "7\n", + "\n", + "\n", + "\n", + "1->7\n", + "\n", + "\n", + "!a\n", "\n", "\n", - "\n", + "\n", "8\n", - "\n", - "8\n", + "\n", + "8\n", "\n", - "\n", - "\n", - "2->8\n", - "\n", - "\n", - "1\n", + "\n", + "\n", + "1->8\n", + "\n", + "\n", + "a\n", + "\n", + "\n", + "\n", + "\n", + "7->1\n", + "\n", + "\n", + "1\n", "\n", "\n", - "\n", + "\n", "8->2\n", - "\n", - "\n", - "1\n", + "\n", + "\n", + "1\n", + "\n", "\n", - "\n", - "\n", - "3\n", - "\n", - "3\n", + "\n", + "\n", + "2->7\n", + "\n", + "\n", + "!a\n", + "\n", + "\n", + "\n", + "2->8\n", + "\n", + "\n", + "a\n", + "\n", "\n", "\n", - "\n", + "\n", "9\n", - "\n", - "9\n", + "\n", + "9\n", "\n", "\n", - "\n", + "\n", "3->9\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "9->2\n", - "\n", - "\n", - "!b\n", + "\n", + "\n", + "1\n", "\n", "\n", - "\n", - "9->3\n", - "\n", - "\n", - "b\n", - "\n", - "\n", - "\n", "\n", - "10->0\n", - "\n", - "\n", - "!b\n", + "9->3\n", + "\n", + "\n", + "b\n", + "\n", "\n", - "\n", + "\n", + "\n", + "4\n", + "\n", + "4\n", + "\n", + "\n", "\n", - "10->3\n", - "\n", - "\n", - "b\n", + "9->4\n", + "\n", + "\n", + "!b\n", "\n", - "\n", + "\n", + "\n", + "10\n", + "\n", + "10\n", + "\n", + "\n", + "\n", + "4->10\n", + "\n", + "\n", + "1\n", + "\n", + "\n", "\n", - "11->1\n", - "\n", - "\n", - "!b\n", - "\n", - "\n", - "\n", - "11->3\n", - "\n", - "\n", - "b\n", + "10->4\n", + "\n", + "\n", + "1\n", "\n", "\n", "\n" ], "text/plain": [ - " *' at 0x7f658c612f30> >" + " *' at 0x7f6be431fcf0> >" ] }, "execution_count": 11, @@ -1267,18 +1235,11 @@ "source": [ "spot.highlight_strategy(game)" ] - }, - { - "cell_type": "code", - "execution_count": null, - "metadata": {}, - "outputs": [], - "source": [] } ], "metadata": { "kernelspec": { - "display_name": "Python 3 (ipykernel)", + "display_name": "Python 3", "language": "python", "name": "python3" }, @@ -1292,7 +1253,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.10" + "version": "3.7.3" } }, "nbformat": 4, diff --git a/tests/python/synthesis.ipynb b/tests/python/synthesis.ipynb index 3e8b4f5ea..654d22873 100644 --- a/tests/python/synthesis.ipynb +++ b/tests/python/synthesis.ipynb @@ -3,7 +3,6 @@ { "cell_type": "code", "execution_count": 1, - "id": "7a864ea1", "metadata": {}, "outputs": [], "source": [ @@ -14,7 +13,6 @@ }, { "cell_type": "markdown", - "id": "9a294cae", "metadata": {}, "source": [ "This notebook presents functions that can be used to solve the Reactive Synthesis problem using games.\n", @@ -39,7 +37,6 @@ { "cell_type": "code", "execution_count": 2, - "id": "70429a41", "metadata": {}, "outputs": [ { @@ -56,590 +53,590 @@ "\n", "\n", - "\n", "\n", - "\n", - "\n", - "\n", - "Fin(\n", - "\n", - ")\n", - "[co-Büchi]\n", + "\n", + "\n", + "\n", + "Fin(\n", + "\n", + ")\n", + "[co-Büchi]\n", "\n", - "\n", - "\n", - "9\n", - "\n", - "9\n", - "\n", - "\n", - "\n", - "I->9\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "25\n", - "\n", - "25\n", - "\n", - "\n", - "\n", - "9->25\n", - "\n", - "\n", - "!i0 & !i1\n", - "\n", - "\n", - "\n", - "26\n", - "\n", - "26\n", - "\n", - "\n", - "\n", - "9->26\n", - "\n", - "\n", - "i0 & !i1\n", - "\n", - "\n", - "\n", - "27\n", - "\n", - "27\n", - "\n", - "\n", - "\n", - "9->27\n", - "\n", - "\n", - "!i0 & i1\n", - "\n", - "\n", - "\n", - "28\n", - "\n", - "28\n", - "\n", - "\n", - "\n", - "9->28\n", - "\n", - "\n", - "i0 & i1\n", - "\n", "\n", - "\n", + "\n", "0\n", - "\n", - "0\n", + "\n", + "0\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", "\n", "\n", - "\n", + "\n", "10\n", - "\n", - "10\n", + "\n", + "10\n", "\n", "\n", "\n", "0->10\n", - "\n", - "\n", - "!i1\n", + "\n", + "\n", + "!i0 & !i1\n", "\n", "\n", - "\n", + "\n", "11\n", - "\n", - "11\n", + "\n", + "11\n", "\n", "\n", "\n", "0->11\n", - "\n", - "\n", - "i1\n", - "\n", - "\n", - "\n", - "1\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "10->1\n", - "\n", - "\n", - "o0\n", - "\n", - "\n", - "\n", - "11->0\n", - "\n", - "\n", - "o0\n", + "\n", + "\n", + "i0 & !i1\n", "\n", "\n", - "\n", + "\n", "12\n", - "\n", - "12\n", + "\n", + "12\n", "\n", - "\n", + "\n", "\n", - "1->12\n", - "\n", - "\n", - "!i1\n", + "0->12\n", + "\n", + "\n", + "!i0 & i1\n", "\n", "\n", - "\n", + "\n", "13\n", - "\n", - "13\n", + "\n", + "13\n", "\n", - "\n", + "\n", "\n", - "1->13\n", - "\n", - "\n", - "i1\n", + "0->13\n", + "\n", + "\n", + "i0 & i1\n", "\n", - "\n", + "\n", + "\n", + "8\n", + "\n", + "8\n", + "\n", + "\n", + "\n", + "10->8\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "7\n", + "\n", + "7\n", + "\n", + "\n", + "\n", + "11->7\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "9\n", + "\n", + "9\n", + "\n", + "\n", "\n", - "12->1\n", - "\n", - "\n", - "!o0\n", + "12->9\n", + "\n", + "\n", + "1\n", "\n", - "\n", + "\n", + "\n", + "5\n", + "\n", + "5\n", + "\n", + "\n", "\n", - "13->0\n", - "\n", - "\n", - "!o0\n", + "13->5\n", + "\n", + "\n", + "1\n", "\n", - "\n", - "\n", - "2\n", - "\n", - "2\n", + "\n", + "\n", + "1\n", + "\n", + "1\n", "\n", "\n", - "\n", + "\n", "14\n", - "\n", - "14\n", + "\n", + "14\n", "\n", - "\n", + "\n", "\n", - "2->14\n", - "\n", - "\n", - "i1\n", + "1->14\n", + "\n", + "\n", + "i0\n", "\n", "\n", - "\n", + "\n", "16\n", - "\n", - "16\n", + "\n", + "16\n", "\n", - "\n", + "\n", "\n", - "2->16\n", - "\n", - "\n", - "!i1\n", + "1->16\n", + "\n", + "\n", + "!i0\n", "\n", "\n", "\n", "15\n", - "\n", - "15\n", + "\n", + "15\n", "\n", "\n", "\n", "14->15\n", - "\n", - "\n", - "1\n", - "\n", + "\n", + "\n", + "1\n", + "\n", "\n", - "\n", + "\n", "\n", - "16->2\n", - "\n", - "\n", - "1\n", + "16->1\n", + "\n", + "\n", + "1\n", "\n", - "\n", - "\n", - "3\n", - "\n", - "3\n", + "\n", + "\n", + "2\n", + "\n", + "2\n", "\n", - "\n", + "\n", "\n", - "3->13\n", - "\n", - "\n", - "i1\n", + "2->14\n", + "\n", + "\n", + "i1\n", "\n", "\n", - "\n", + "\n", "17\n", - "\n", - "17\n", + "\n", + "17\n", "\n", - "\n", + "\n", "\n", - "3->17\n", - "\n", - "\n", - "!i1\n", + "2->17\n", + "\n", + "\n", + "!i1\n", "\n", "\n", "\n", "17->2\n", - "\n", - "\n", - "o0\n", + "\n", + "\n", + "1\n", "\n", - "\n", + "\n", + "\n", + "3\n", + "\n", + "3\n", + "\n", + "\n", + "\n", + "3->14\n", + "\n", + "\n", + "i0 & i1\n", + "\n", + "\n", + "\n", + "3->16\n", + "\n", + "\n", + "!i0 & i1\n", + "\n", + "\n", + "\n", + "3->17\n", + "\n", + "\n", + "i0 & !i1\n", + "\n", + "\n", + "\n", + "18\n", + "\n", + "18\n", + "\n", + "\n", + "\n", + "3->18\n", + "\n", + "\n", + "!i0 & !i1\n", + "\n", + "\n", "\n", - "17->3\n", - "\n", - "\n", - "!o0\n", + "18->3\n", + "\n", + "\n", + "1\n", "\n", "\n", "\n", "4\n", - "\n", - "4\n", - "\n", - "\n", - "\n", - "4->14\n", - "\n", - "\n", - "i0\n", - "\n", - "\n", - "\n", - "18\n", - "\n", - "18\n", - "\n", - "\n", - "\n", - "4->18\n", - "\n", - "\n", - "!i0\n", - "\n", - "\n", - "\n", - "18->4\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "5\n", - "\n", - "5\n", - "\n", - "\n", - "\n", - "5->14\n", - "\n", - "\n", - "i0 & i1\n", - "\n", - "\n", - "\n", - "5->16\n", - "\n", - "\n", - "i0 & !i1\n", - "\n", - "\n", - "\n", - "5->18\n", - "\n", - "\n", - "!i0 & i1\n", + "\n", + "4\n", "\n", "\n", - "\n", + "\n", "19\n", - "\n", - "19\n", + "\n", + "19\n", "\n", - "\n", - "\n", - "5->19\n", - "\n", - "\n", - "!i0 & !i1\n", - "\n", - "\n", - "\n", - "19->5\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "6\n", - "\n", - "6\n", - "\n", - "\n", - "\n", - "6->10\n", - "\n", - "\n", - "i0 & !i1\n", - "\n", - "\n", - "\n", - "6->11\n", - "\n", - "\n", - "i0 & i1\n", + "\n", + "\n", + "4->19\n", + "\n", + "\n", + "!i1\n", "\n", "\n", - "\n", + "\n", "20\n", - "\n", - "20\n", + "\n", + "20\n", "\n", - "\n", - "\n", - "6->20\n", - "\n", - "\n", - "!i0 & !i1\n", + "\n", + "\n", + "4->20\n", + "\n", + "\n", + "i1\n", + "\n", + "\n", + "\n", + "19->4\n", + "\n", + "\n", + "!o0\n", + "\n", + "\n", + "\n", + "20->5\n", + "\n", + "\n", + "!o0\n", "\n", "\n", - "\n", + "\n", "21\n", - "\n", - "21\n", + "\n", + "21\n", "\n", - "\n", - "\n", - "6->21\n", - "\n", - "\n", - "!i0 & i1\n", - "\n", - "\n", - "\n", - "20->4\n", - "\n", - "\n", - "!o0\n", - "\n", - "\n", - "\n", - "7\n", - "\n", - "7\n", - "\n", - "\n", - "\n", - "20->7\n", - "\n", - "\n", - "o0\n", - "\n", - "\n", - "\n", - "21->4\n", - "\n", - "\n", - "!o0\n", - "\n", - "\n", - "\n", - "21->6\n", - "\n", - "\n", - "o0\n", - "\n", - "\n", - "\n", - "7->12\n", - "\n", - "\n", - "i0 & !i1\n", - "\n", - "\n", - "\n", - "7->13\n", - "\n", - "\n", - "i0 & i1\n", + "\n", + "\n", + "5->21\n", + "\n", + "\n", + "!i1\n", "\n", "\n", - "\n", + "\n", "22\n", - "\n", - "22\n", + "\n", + "22\n", "\n", - "\n", - "\n", - "7->22\n", - "\n", - "\n", - "!i0 & !i1\n", + "\n", + "\n", + "5->22\n", + "\n", + "\n", + "i1\n", + "\n", + "\n", + "\n", + "21->4\n", + "\n", + "\n", + "o0\n", + "\n", + "\n", + "\n", + "22->5\n", + "\n", + "\n", + "o0\n", + "\n", + "\n", + "\n", + "6\n", + "\n", + "6\n", + "\n", + "\n", + "\n", + "6->19\n", + "\n", + "\n", + "i0 & !i1\n", + "\n", + "\n", + "\n", + "6->20\n", + "\n", + "\n", + "i0 & i1\n", "\n", "\n", - "\n", + "\n", "23\n", - "\n", - "23\n", + "\n", + "23\n", "\n", - "\n", - "\n", - "7->23\n", - "\n", - "\n", - "!i0 & i1\n", - "\n", - "\n", - "\n", - "22->4\n", - "\n", - "\n", - "o0\n", - "\n", - "\n", - "\n", - "22->7\n", - "\n", - "\n", - "!o0\n", - "\n", - "\n", - "\n", - "23->4\n", - "\n", - "\n", - "o0\n", - "\n", - "\n", - "\n", - "23->6\n", - "\n", - "\n", - "!o0\n", - "\n", - "\n", - "\n", - "8\n", - "\n", - "8\n", - "\n", - "\n", - "\n", - "8->13\n", - "\n", - "\n", - "i0 & i1\n", - "\n", - "\n", - "\n", - "8->17\n", - "\n", - "\n", - "i0 & !i1\n", - "\n", - "\n", - "\n", - "8->23\n", - "\n", - "\n", - "!i0 & i1\n", + "\n", + "\n", + "6->23\n", + "\n", + "\n", + "!i0 & !i1\n", "\n", "\n", - "\n", + "\n", "24\n", - "\n", - "24\n", + "\n", + "24\n", + "\n", + "\n", + "\n", + "6->24\n", + "\n", + "\n", + "!i0 & i1\n", + "\n", + "\n", + "\n", + "23->1\n", + "\n", + "\n", + "o0\n", + "\n", + "\n", + "\n", + "23->6\n", + "\n", + "\n", + "!o0\n", + "\n", + "\n", + "\n", + "24->1\n", + "\n", + "\n", + "o0\n", + "\n", + "\n", + "\n", + "24->9\n", + "\n", + "\n", + "!o0\n", + "\n", + "\n", + "\n", + "7->20\n", + "\n", + "\n", + "i1\n", + "\n", + "\n", + "\n", + "25\n", + "\n", + "25\n", + "\n", + "\n", + "\n", + "7->25\n", + "\n", + "\n", + "!i1\n", + "\n", + "\n", + "\n", + "25->2\n", + "\n", + "\n", + "o0\n", + "\n", + "\n", + "\n", + "25->7\n", + "\n", + "\n", + "!o0\n", + "\n", + "\n", + "\n", + "8->20\n", + "\n", + "\n", + "i0 & i1\n", "\n", "\n", - "\n", + "\n", "8->24\n", - "\n", - "\n", - "!i0 & !i1\n", + "\n", + "\n", + "!i0 & i1\n", "\n", - "\n", - "\n", - "24->5\n", - "\n", - "\n", - "o0\n", + "\n", + "\n", + "8->25\n", + "\n", + "\n", + "i0 & !i1\n", "\n", - "\n", - "\n", - "24->8\n", - "\n", - "\n", - "!o0\n", + "\n", + "\n", + "26\n", + "\n", + "26\n", "\n", - "\n", - "\n", - "25->8\n", - "\n", - "\n", - "1\n", + "\n", + "\n", + "8->26\n", + "\n", + "\n", + "!i0 & !i1\n", "\n", "\n", - "\n", + "\n", "26->3\n", - "\n", - "\n", - "1\n", + "\n", + "\n", + "o0\n", + "\n", + "\n", + "\n", + "26->8\n", + "\n", + "\n", + "!o0\n", + "\n", + "\n", + "\n", + "9->21\n", + "\n", + "\n", + "i0 & !i1\n", + "\n", + "\n", + "\n", + "9->22\n", + "\n", + "\n", + "i0 & i1\n", + "\n", + "\n", + "\n", + "27\n", + "\n", + "27\n", + "\n", + "\n", + "\n", + "9->27\n", + "\n", + "\n", + "!i0 & !i1\n", + "\n", + "\n", + "\n", + "28\n", + "\n", + "28\n", + "\n", + "\n", + "\n", + "9->28\n", + "\n", + "\n", + "!i0 & i1\n", + "\n", + "\n", + "\n", + "27->1\n", + "\n", + "\n", + "!o0\n", "\n", "\n", - "\n", + "\n", "27->6\n", - "\n", - "\n", - "1\n", + "\n", + "\n", + "o0\n", "\n", - "\n", + "\n", + "\n", + "28->1\n", + "\n", + "\n", + "!o0\n", + "\n", + "\n", "\n", - "28->0\n", - "\n", - "\n", - "1\n", + "28->9\n", + "\n", + "\n", + "o0\n", "\n", "\n", "\n", "15->14\n", - "\n", - "\n", - "1\n", - "\n", + "\n", + "\n", + "1\n", + "\n", "\n", "\n", "\n" ], "text/plain": [ - " *' at 0x7fc9680c32d0> >" + " *' at 0x7f0e584de570> >" ] }, "metadata": {}, @@ -658,7 +655,6 @@ }, { "cell_type": "markdown", - "id": "c02b2d8f", "metadata": {}, "source": [ "Solving the game, is done with `solve_game()` as with any game. There is also a version that takes a `synthesis_info` as second argument in case the time it takes has to be recorded. Here passing `si` or not makes no difference." @@ -667,7 +663,6 @@ { "cell_type": "code", "execution_count": 3, - "id": "d08e7b9f", "metadata": {}, "outputs": [ { @@ -683,529 +678,529 @@ "\n", "\n", - "\n", "\n", - "\n", - "\n", - "\n", - "Fin(\n", - "\n", - ")\n", - "[co-Büchi]\n", + "\n", + "\n", + "\n", + "Fin(\n", + "\n", + ")\n", + "[co-Büchi]\n", "\n", - "\n", - "\n", - "9\n", - "\n", - "9\n", - "\n", - "\n", - "\n", - "I->9\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "25\n", - "\n", - "25\n", - "\n", - "\n", - "\n", - "9->25\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "26\n", - "\n", - "26\n", - "\n", - "\n", - "\n", - "9->26\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "27\n", - "\n", - "27\n", - "\n", - "\n", - "\n", - "9->27\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "28\n", - "\n", - "28\n", - "\n", - "\n", - "\n", - "9->28\n", - "\n", - "\n", - "\n", "\n", - "\n", + "\n", "0\n", - "\n", - "0\n", + "\n", + "0\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", "\n", "\n", - "\n", + "\n", "10\n", - "\n", - "10\n", + "\n", + "10\n", "\n", "\n", "\n", "0->10\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", - "\n", + "\n", "11\n", - "\n", - "11\n", + "\n", + "11\n", "\n", "\n", "\n", "0->11\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "1\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "10->1\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "11->0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", - "\n", + "\n", "12\n", - "\n", - "12\n", + "\n", + "12\n", "\n", - "\n", + "\n", "\n", - "1->12\n", - "\n", - "\n", + "0->12\n", + "\n", + "\n", "\n", "\n", - "\n", + "\n", "13\n", - "\n", - "13\n", + "\n", + "13\n", "\n", - "\n", + "\n", "\n", - "1->13\n", - "\n", - "\n", + "0->13\n", + "\n", + "\n", "\n", - "\n", + "\n", + "\n", + "8\n", + "\n", + "8\n", + "\n", + "\n", + "\n", + "10->8\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "7\n", + "\n", + "7\n", + "\n", + "\n", + "\n", + "11->7\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "9\n", + "\n", + "9\n", + "\n", + "\n", "\n", - "12->1\n", - "\n", - "\n", + "12->9\n", + "\n", + "\n", "\n", - "\n", + "\n", + "\n", + "5\n", + "\n", + "5\n", + "\n", + "\n", "\n", - "13->0\n", - "\n", - "\n", + "13->5\n", + "\n", + "\n", "\n", - "\n", - "\n", - "2\n", - "\n", - "2\n", + "\n", + "\n", + "1\n", + "\n", + "1\n", "\n", "\n", - "\n", + "\n", "14\n", - "\n", - "14\n", + "\n", + "14\n", "\n", - "\n", + "\n", "\n", - "2->14\n", - "\n", - "\n", + "1->14\n", + "\n", + "\n", "\n", "\n", - "\n", + "\n", "16\n", - "\n", - "16\n", + "\n", + "16\n", "\n", - "\n", + "\n", "\n", - "2->16\n", - "\n", - "\n", + "1->16\n", + "\n", + "\n", "\n", "\n", "\n", "15\n", - "\n", - "15\n", + "\n", + "15\n", "\n", "\n", "\n", "14->15\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", - "\n", + "\n", "\n", - "16->2\n", - "\n", - "\n", + "16->1\n", + "\n", + "\n", "\n", - "\n", - "\n", - "3\n", - "\n", - "3\n", + "\n", + "\n", + "2\n", + "\n", + "2\n", "\n", - "\n", + "\n", "\n", - "3->13\n", - "\n", - "\n", + "2->14\n", + "\n", + "\n", "\n", "\n", - "\n", + "\n", "17\n", - "\n", - "17\n", + "\n", + "17\n", "\n", - "\n", + "\n", "\n", - "3->17\n", - "\n", - "\n", + "2->17\n", + "\n", + "\n", "\n", "\n", "\n", "17->2\n", - "\n", - "\n", + "\n", + "\n", "\n", - "\n", + "\n", + "\n", + "3\n", + "\n", + "3\n", + "\n", + "\n", + "\n", + "3->14\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "3->16\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "3->17\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "18\n", + "\n", + "18\n", + "\n", + "\n", + "\n", + "3->18\n", + "\n", + "\n", + "\n", + "\n", "\n", - "17->3\n", - "\n", - "\n", + "18->3\n", + "\n", + "\n", "\n", "\n", "\n", "4\n", - "\n", - "4\n", - "\n", - "\n", - "\n", - "4->14\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "18\n", - "\n", - "18\n", - "\n", - "\n", - "\n", - "4->18\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "18->4\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "5\n", - "\n", - "5\n", - "\n", - "\n", - "\n", - "5->14\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "5->16\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "5->18\n", - "\n", - "\n", + "\n", + "4\n", "\n", "\n", - "\n", + "\n", "19\n", - "\n", - "19\n", + "\n", + "19\n", "\n", - "\n", - "\n", - "5->19\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "19->5\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "6\n", - "\n", - "6\n", - "\n", - "\n", - "\n", - "6->10\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "6->11\n", - "\n", - "\n", + "\n", + "\n", + "4->19\n", + "\n", + "\n", "\n", "\n", - "\n", + "\n", "20\n", - "\n", - "20\n", + "\n", + "20\n", "\n", - "\n", - "\n", - "6->20\n", - "\n", - "\n", + "\n", + "\n", + "4->20\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "19->4\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "20->5\n", + "\n", + "\n", "\n", "\n", - "\n", + "\n", "21\n", - "\n", - "21\n", + "\n", + "21\n", "\n", - "\n", - "\n", - "6->21\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "20->4\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "7\n", - "\n", - "7\n", - "\n", - "\n", - "\n", - "20->7\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "21->4\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "21->6\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "7->12\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "7->13\n", - "\n", - "\n", + "\n", + "\n", + "5->21\n", + "\n", + "\n", "\n", "\n", - "\n", + "\n", "22\n", - "\n", - "22\n", + "\n", + "22\n", "\n", - "\n", - "\n", - "7->22\n", - "\n", - "\n", + "\n", + "\n", + "5->22\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "21->4\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "22->5\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "6\n", + "\n", + "6\n", + "\n", + "\n", + "\n", + "6->19\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "6->20\n", + "\n", + "\n", "\n", "\n", - "\n", + "\n", "23\n", - "\n", - "23\n", + "\n", + "23\n", "\n", - "\n", - "\n", - "7->23\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "22->4\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "22->7\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "23->4\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "23->6\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "8\n", - "\n", - "8\n", - "\n", - "\n", - "\n", - "8->13\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "8->17\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "8->23\n", - "\n", - "\n", + "\n", + "\n", + "6->23\n", + "\n", + "\n", "\n", "\n", - "\n", + "\n", "24\n", - "\n", - "24\n", + "\n", + "24\n", + "\n", + "\n", + "\n", + "6->24\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "23->1\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "23->6\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "24->1\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "24->9\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "7->20\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "25\n", + "\n", + "25\n", + "\n", + "\n", + "\n", + "7->25\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "25->2\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "25->7\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "8->20\n", + "\n", + "\n", "\n", "\n", - "\n", + "\n", "8->24\n", - "\n", - "\n", + "\n", + "\n", "\n", - "\n", - "\n", - "24->5\n", - "\n", - "\n", + "\n", + "\n", + "8->25\n", + "\n", + "\n", "\n", - "\n", - "\n", - "24->8\n", - "\n", - "\n", + "\n", + "\n", + "26\n", + "\n", + "26\n", "\n", - "\n", - "\n", - "25->8\n", - "\n", - "\n", + "\n", + "\n", + "8->26\n", + "\n", + "\n", "\n", "\n", - "\n", + "\n", "26->3\n", - "\n", - "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "26->8\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "9->21\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "9->22\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "27\n", + "\n", + "27\n", + "\n", + "\n", + "\n", + "9->27\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "28\n", + "\n", + "28\n", + "\n", + "\n", + "\n", + "9->28\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "27->1\n", + "\n", + "\n", "\n", "\n", - "\n", + "\n", "27->6\n", - "\n", - "\n", + "\n", + "\n", "\n", - "\n", + "\n", + "\n", + "28->1\n", + "\n", + "\n", + "\n", + "\n", "\n", - "28->0\n", - "\n", - "\n", + "28->9\n", + "\n", + "\n", "\n", "\n", "\n", "15->14\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n" @@ -1227,7 +1222,6 @@ }, { "cell_type": "markdown", - "id": "9590cf55", "metadata": {}, "source": [ "Once a strategy has been found, it can be extracted as an automaton and simplified using 6 different levels (the default is 2). The output should be interpreted as a Mealy automaton, where transition have the form `(ins)&(outs)` where `ins` and `outs` are Boolean formulas representing possible inputs and outputs (they could be more than just conjunctions of atomic proposition). Mealy machines with this type of labels are called \"separated\" in Spot." @@ -1236,7 +1230,6 @@ { "cell_type": "code", "execution_count": 4, - "id": "d6cb467d", "metadata": {}, "outputs": [ { @@ -1252,309 +1245,309 @@ "\n", "\n", - "\n", "\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "0\n", - "\n", - "0\n", + "\n", + "0\n", "\n", "\n", "\n", "I->0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "1\n", - "\n", - "1\n", + "\n", + "1\n", "\n", "\n", "\n", "0->1\n", - "\n", - "\n", - "\n", - "!i0 & !i1\n", - "/\n", - "\n", - "1\n", + "\n", + "\n", + "\n", + "!i0 & !i1\n", + "/\n", + "\n", + "1\n", "\n", "\n", "\n", "2\n", - "\n", - "2\n", + "\n", + "2\n", "\n", "\n", "\n", "0->2\n", - "\n", - "\n", - "\n", - "i0 & !i1\n", - "/\n", - "\n", - "1\n", + "\n", + "\n", + "\n", + "i0 & !i1\n", + "/\n", + "\n", + "1\n", "\n", "\n", "\n", "3\n", - "\n", - "3\n", + "\n", + "3\n", "\n", "\n", "\n", "0->3\n", - "\n", - "\n", - "\n", - "!i0 & i1\n", - "/\n", - "\n", - "1\n", + "\n", + "\n", + "\n", + "!i0 & i1\n", + "/\n", + "\n", + "1\n", "\n", "\n", "\n", "4\n", - "\n", - "4\n", + "\n", + "4\n", "\n", "\n", "\n", "0->4\n", - "\n", - "\n", - "\n", - "i0 & i1\n", - "/\n", - "\n", - "1\n", + "\n", + "\n", + "\n", + "i0 & i1\n", + "/\n", + "\n", + "1\n", "\n", "\n", "\n", "1->1\n", - "\n", - "\n", - "\n", - "!i0 & !i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "!i0 & !i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "1->2\n", - "\n", - "\n", - "\n", - "i0 & !i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "i0 & !i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "1->3\n", - "\n", - "\n", - "\n", - "!i0 & i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "!i0 & i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "1->4\n", - "\n", - "\n", - "\n", - "i0 & i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "i0 & i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "2->2\n", - "\n", - "\n", - "\n", - "!i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "!i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "2->4\n", - "\n", - "\n", - "\n", - "i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "3->3\n", - "\n", - "\n", - "\n", - "!i0 & i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "!i0 & i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "3->4\n", - "\n", - "\n", - "\n", - "i0 & i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "i0 & i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "5\n", - "\n", - "5\n", + "\n", + "5\n", "\n", "\n", "\n", "3->5\n", - "\n", - "\n", - "\n", - "i0 & !i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "i0 & !i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "6\n", - "\n", - "6\n", + "\n", + "6\n", "\n", "\n", "\n", "3->6\n", - "\n", - "\n", - "\n", - "!i0 & !i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "!i0 & !i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "4->4\n", - "\n", - "\n", - "\n", - "i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "4->5\n", - "\n", - "\n", - "\n", - "!i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "!i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "5->4\n", - "\n", - "\n", - "\n", - "i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "5->5\n", - "\n", - "\n", - "\n", - "!i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "!i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "6->3\n", - "\n", - "\n", - "\n", - "!i0 & i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "!i0 & i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "6->4\n", - "\n", - "\n", - "\n", - "i0 & i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "i0 & i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "6->5\n", - "\n", - "\n", - "\n", - "i0 & !i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "i0 & !i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "6->6\n", - "\n", - "\n", - "\n", - "!i0 & !i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "!i0 & !i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n" ], "text/plain": [ - " *' at 0x7fc9682230f0> >" + " *' at 0x7f0e5855c9f0> >" ] }, "metadata": {}, @@ -1573,175 +1566,175 @@ "\n", "\n", - "\n", "\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "0\n", - "\n", - "0\n", + "\n", + "0\n", "\n", "\n", "\n", "I->0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "1\n", - "\n", - "1\n", + "\n", + "1\n", "\n", "\n", "\n", "0->1\n", - "\n", - "\n", - "\n", - "!i0 & !i1\n", - "/\n", - "\n", - "1\n", + "\n", + "\n", + "\n", + "!i0 & !i1\n", + "/\n", + "\n", + "1\n", "\n", "\n", "\n", "0->1\n", - "\n", - "\n", - "\n", - "i0 & !i1\n", - "/\n", - "\n", - "1\n", + "\n", + "\n", + "\n", + "i0 & !i1\n", + "/\n", + "\n", + "1\n", "\n", "\n", "\n", "2\n", - "\n", - "2\n", + "\n", + "2\n", "\n", "\n", "\n", "0->2\n", - "\n", - "\n", - "\n", - "!i0 & i1\n", - "/\n", - "\n", - "1\n", + "\n", + "\n", + "\n", + "!i0 & i1\n", + "/\n", + "\n", + "1\n", "\n", "\n", "\n", "0->2\n", - "\n", - "\n", - "\n", - "i0 & i1\n", - "/\n", - "\n", - "1\n", + "\n", + "\n", + "\n", + "i0 & i1\n", + "/\n", + "\n", + "1\n", "\n", "\n", "\n", "1->1\n", - "\n", - "\n", - "\n", - "i0 & !i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "i0 & !i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "1->1\n", - "\n", - "\n", - "\n", - "!i0 & !i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "!i0 & !i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "1->2\n", - "\n", - "\n", - "\n", - "i0 & i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "i0 & i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "1->2\n", - "\n", - "\n", - "\n", - "!i0 & i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "!i0 & i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "2->1\n", - "\n", - "\n", - "\n", - "i0 & !i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "i0 & !i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "2->1\n", - "\n", - "\n", - "\n", - "!i0 & !i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "!i0 & !i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "2->2\n", - "\n", - "\n", - "\n", - "i0 & i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "i0 & i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "2->2\n", - "\n", - "\n", - "\n", - "!i0 & i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "!i0 & i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n" ], "text/plain": [ - " *' at 0x7fc968069180> >" + " *' at 0x7f0e5855cb10> >" ] }, "metadata": {}, @@ -1760,125 +1753,125 @@ "\n", "\n", - "\n", "\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "1\n", - "\n", - "1\n", + "\n", + "1\n", "\n", "\n", "\n", "I->1\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "1->1\n", - "\n", - "\n", - "\n", - "i0 & i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "i0 & i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "1->1\n", - "\n", - "\n", - "\n", - "!i0 & i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "!i0 & i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "0\n", - "\n", - "0\n", + "\n", + "0\n", "\n", "\n", "\n", "1->0\n", - "\n", - "\n", - "\n", - "i0 & !i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "i0 & !i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "1->0\n", - "\n", - "\n", - "\n", - "!i0 & !i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "!i0 & !i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "0->1\n", - "\n", - "\n", - "\n", - "i0 & i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "i0 & i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "0->1\n", - "\n", - "\n", - "\n", - "!i0 & i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "!i0 & i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "0->0\n", - "\n", - "\n", - "\n", - "i0 & !i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "i0 & !i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "0->0\n", - "\n", - "\n", - "\n", - "!i0 & !i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "!i0 & !i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n" ], "text/plain": [ - " *' at 0x7fc968069210> >" + " *' at 0x7f0e5855ccf0> >" ] }, "metadata": {}, @@ -1897,81 +1890,81 @@ "\n", "\n", - "\n", "\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "0\n", - "\n", - "0\n", + "\n", + "0\n", "\n", "\n", "\n", "I->0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "0->0\n", - "\n", - "\n", - "\n", - "i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "1\n", - "\n", - "1\n", + "\n", + "1\n", "\n", "\n", "\n", "0->1\n", - "\n", - "\n", - "\n", - "!i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "!i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "1->0\n", - "\n", - "\n", - "\n", - "i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "1->1\n", - "\n", - "\n", - "\n", - "!i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "!i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n" ], "text/plain": [ - " *' at 0x7fc968069180> >" + " *' at 0x7f0e5855cd80> >" ] }, "metadata": {}, @@ -1990,81 +1983,81 @@ "\n", "\n", - "\n", "\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "0\n", - "\n", - "0\n", + "\n", + "0\n", "\n", "\n", "\n", "I->0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "0->0\n", - "\n", - "\n", - "\n", - "!i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "!i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "1\n", - "\n", - "1\n", + "\n", + "1\n", "\n", "\n", "\n", "0->1\n", - "\n", - "\n", - "\n", - "i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "1->0\n", - "\n", - "\n", - "\n", - "!i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "!i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "1->1\n", - "\n", - "\n", - "\n", - "i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n" ], "text/plain": [ - " *' at 0x7fc968069210> >" + " *' at 0x7f0e584defc0> >" ] }, "metadata": {}, @@ -2083,125 +2076,125 @@ "\n", "\n", - "\n", "\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "0\n", - "\n", - "0\n", + "\n", + "0\n", "\n", "\n", "\n", "I->0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "0->0\n", - "\n", - "\n", - "\n", - "i0 & i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "i0 & i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "0->0\n", - "\n", - "\n", - "\n", - "!i0 & i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "!i0 & i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "1\n", - "\n", - "1\n", + "\n", + "1\n", "\n", "\n", "\n", "0->1\n", - "\n", - "\n", - "\n", - "i0 & !i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "i0 & !i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "0->1\n", - "\n", - "\n", - "\n", - "!i0 & !i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "!i0 & !i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "1->0\n", - "\n", - "\n", - "\n", - "i0 & i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "i0 & i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "1->0\n", - "\n", - "\n", - "\n", - "!i0 & i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "!i0 & i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "1->1\n", - "\n", - "\n", - "\n", - "i0 & !i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "i0 & !i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "1->1\n", - "\n", - "\n", - "\n", - "!i0 & !i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "!i0 & !i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n" ], "text/plain": [ - " *' at 0x7fc968069060> >" + " *' at 0x7f0e5855ca20> >" ] }, "metadata": {}, @@ -2235,7 +2228,6 @@ }, { "cell_type": "markdown", - "id": "7ee86443", "metadata": {}, "source": [ "If needed, a separated Mealy machine can be turned into game shape using `split_sepearated_mealy()`, which is more efficient than `split_2step()`." @@ -2244,7 +2236,6 @@ { "cell_type": "code", "execution_count": 5, - "id": "80510b01", "metadata": {}, "outputs": [ { @@ -2253,260 +2244,260 @@ "
\n", "\n", - "\n", "\n", - "\n", - "\n", - "\n", + "\n", + "\n", + "\n", "\n", "\n", "\n", "0\n", - "\n", - "0\n", + "\n", + "0\n", "\n", "\n", "\n", "I->0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "0->0\n", - "\n", - "\n", - "\n", - "i0 & i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "i0 & i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "0->0\n", - "\n", - "\n", - "\n", - "!i0 & i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "!i0 & i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "1\n", - "\n", - "1\n", + "\n", + "1\n", "\n", "\n", "\n", "0->1\n", - "\n", - "\n", - "\n", - "i0 & !i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "i0 & !i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "0->1\n", - "\n", - "\n", - "\n", - "!i0 & !i1\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "!i0 & !i1\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "1->0\n", - "\n", - "\n", - "\n", - "i0 & i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "i0 & i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "1->0\n", - "\n", - "\n", - "\n", - "!i0 & i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "!i0 & i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "1->1\n", - "\n", - "\n", - "\n", - "i0 & !i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "i0 & !i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "1->1\n", - "\n", - "\n", - "\n", - "!i0 & !i1\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "!i0 & !i1\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "
\n", "\n", - "\n", "\n", - "\n", - "\n", - "\n", - "t\n", - "[all]\n", + "\n", + "\n", + "\n", + "t\n", + "[all]\n", "\n", "\n", "\n", "0\n", - "\n", - "0\n", + "\n", + "0\n", "\n", "\n", "\n", "I->0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "2\n", - "\n", - "2\n", + "\n", + "2\n", "\n", "\n", "\n", "0->2\n", - "\n", - "\n", - "i0 & !i1\n", + "\n", + "\n", + "i0 & !i1\n", "\n", "\n", "\n", "0->2\n", - "\n", - "\n", - "!i0 & !i1\n", + "\n", + "\n", + "!i0 & !i1\n", "\n", "\n", "\n", "3\n", - "\n", - "3\n", + "\n", + "3\n", "\n", "\n", "\n", "0->3\n", - "\n", - "\n", - "i0 & i1\n", + "\n", + "\n", + "i0 & i1\n", "\n", "\n", "\n", "0->3\n", - "\n", - "\n", - "!i0 & i1\n", + "\n", + "\n", + "!i0 & i1\n", "\n", "\n", "\n", "1\n", - "\n", - "1\n", + "\n", + "1\n", "\n", "\n", "\n", "2->1\n", - "\n", - "\n", - "o0\n", + "\n", + "\n", + "o0\n", "\n", "\n", "\n", "3->0\n", - "\n", - "\n", - "o0\n", + "\n", + "\n", + "o0\n", "\n", "\n", "\n", "4\n", - "\n", - "4\n", + "\n", + "4\n", "\n", "\n", "\n", "1->4\n", - "\n", - "\n", - "i0 & i1\n", + "\n", + "\n", + "i0 & i1\n", "\n", "\n", "\n", "1->4\n", - "\n", - "\n", - "!i0 & i1\n", + "\n", + "\n", + "!i0 & i1\n", "\n", "\n", "\n", "5\n", - "\n", - "5\n", + "\n", + "5\n", "\n", "\n", "\n", "1->5\n", - "\n", - "\n", - "i0 & !i1\n", + "\n", + "\n", + "i0 & !i1\n", "\n", "\n", "\n", "1->5\n", - "\n", - "\n", - "!i0 & !i1\n", + "\n", + "\n", + "!i0 & !i1\n", "\n", "\n", "\n", "4->0\n", - "\n", - "\n", - "!o0\n", + "\n", + "\n", + "!o0\n", "\n", "\n", "\n", "5->1\n", - "\n", - "\n", - "!o0\n", + "\n", + "\n", + "!o0\n", "\n", "\n", "\n", @@ -2526,7 +2517,6 @@ }, { "cell_type": "markdown", - "id": "8f97aa04", "metadata": {}, "source": [ "# Converting the separated Mealy machine to AIG\n", @@ -2539,7 +2529,6 @@ { "cell_type": "code", "execution_count": 6, - "id": "9c6d9e8b", "metadata": {}, "outputs": [ { @@ -2548,60 +2537,60 @@ "\n", "\n", - "\n", "\n", - "\n", + "\n", "\n", - "\n", + "\n", "\n", "\n", "6\n", - "\n", - "L0_out\n", + "\n", + "L0_out\n", "\n", "\n", "\n", "o0\n", - "\n", - "o0\n", + "\n", + "o0\n", "\n", "\n", "\n", "6->o0:s\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "L0\n", - "\n", - "L0_in\n", + "\n", + "L0_in\n", "\n", "\n", "\n", "2\n", - "\n", - "i1\n", + "\n", + "i1\n", "\n", "\n", "\n", "2->L0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "4\n", - "\n", - "i0\n", + "\n", + "i0\n", "\n", "\n", "\n" ], "text/plain": [ - " >" + " >" ] }, "metadata": {}, @@ -2615,7 +2604,6 @@ }, { "cell_type": "markdown", - "id": "d67f8bce", "metadata": {}, "source": [ "While we are at it, let us mention that you can render those circuits horizontally as follows:" @@ -2624,7 +2612,6 @@ { "cell_type": "code", "execution_count": 7, - "id": "3a363374", "metadata": {}, "outputs": [ { @@ -2633,54 +2620,54 @@ "\n", "\n", - "\n", "\n", - "\n", + "\n", "\n", - "\n", + "\n", "\n", "\n", "6\n", - "\n", - "L0_out\n", + "\n", + "L0_out\n", "\n", "\n", "\n", "o0\n", - "\n", - "o0\n", + "\n", + "o0\n", "\n", "\n", "\n", "6->o0:w\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "L0\n", - "\n", - "L0_in\n", + "\n", + "L0_in\n", "\n", "\n", "\n", "2\n", - "\n", - "i1\n", + "\n", + "i1\n", "\n", "\n", "\n", "2->L0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "4\n", - "\n", - "i0\n", + "\n", + "i0\n", "\n", "\n", "\n" @@ -2700,7 +2687,6 @@ }, { "cell_type": "markdown", - "id": "e4f607c3", "metadata": {}, "source": [ "To encode the circuit in the AIGER format (ASCII version) use:" @@ -2709,7 +2695,6 @@ { "cell_type": "code", "execution_count": 8, - "id": "564f7d0b", "metadata": {}, "outputs": [ { @@ -2733,7 +2718,6 @@ }, { "cell_type": "markdown", - "id": "cf2d4831", "metadata": {}, "source": [ "# Adding more inputs and outputs by force" @@ -2741,7 +2725,6 @@ }, { "cell_type": "markdown", - "id": "874a108e", "metadata": {}, "source": [ "It can happen that propositions declared as output are ommited in the aig circuit (either because they are not part of the specification, or because they do not appear in the winning strategy). In that case those \n", @@ -2753,7 +2736,6 @@ { "cell_type": "code", "execution_count": 9, - "id": "1fc4c566", "metadata": {}, "outputs": [ { @@ -2762,151 +2744,151 @@ "\n", "\n", - "\n", "\n", - "\n", - "\n", - "\n", - "Inf(\n", - "\n", - ")\n", - "[Büchi]\n", + "\n", + "\n", + "\n", + "Inf(\n", + "\n", + ")\n", + "[Büchi]\n", "\n", "\n", "\n", "3\n", - "\n", - "3\n", + "\n", + "3\n", "\n", "\n", "\n", "I->3\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "6\n", - "\n", - "6\n", + "\n", + "6\n", "\n", "\n", "\n", "3->6\n", - "\n", - "\n", - "i0\n", + "\n", + "\n", + "i0\n", "\n", "\n", "\n", "7\n", - "\n", - "7\n", + "\n", + "7\n", "\n", "\n", "\n", "3->7\n", - "\n", - "\n", - "!i0\n", + "\n", + "\n", + "!i0\n", "\n", "\n", "\n", "0\n", - "\n", - "0\n", + "\n", + "0\n", "\n", "\n", "\n", "4\n", - "\n", - "4\n", + "\n", + "4\n", "\n", "\n", "\n", "0->4\n", - "\n", - "\n", - "1\n", - "\n", + "\n", + "\n", + "1\n", + "\n", "\n", "\n", "\n", "4->0\n", - "\n", - "\n", - "1\n", - "\n", + "\n", + "\n", + "1\n", + "\n", "\n", "\n", "\n", "1\n", - "\n", - "1\n", + "\n", + "1\n", "\n", "\n", "\n", "5\n", - "\n", - "5\n", + "\n", + "5\n", "\n", "\n", "\n", "1->5\n", - "\n", - "\n", - "1\n", - "\n", + "\n", + "\n", + "1\n", + "\n", "\n", "\n", "\n", "5->1\n", - "\n", - "\n", - "!o0\n", - "\n", + "\n", + "\n", + "!o0\n", + "\n", "\n", "\n", "\n", "2\n", - "\n", - "2\n", + "\n", + "2\n", "\n", "\n", "\n", "2->6\n", - "\n", - "\n", - "1\n", + "\n", + "\n", + "1\n", "\n", "\n", "\n", "6->0\n", - "\n", - "\n", - "o0\n", + "\n", + "\n", + "o0\n", "\n", "\n", "\n", "6->2\n", - "\n", - "\n", - "!o0\n", + "\n", + "\n", + "!o0\n", "\n", "\n", "\n", "7->1\n", - "\n", - "\n", - "!o0\n", + "\n", + "\n", + "!o0\n", "\n", "\n", "\n" ], "text/plain": [ - " *' at 0x7fc968143d80> >" + " *' at 0x7f0e5855cb70> >" ] }, "metadata": {}, @@ -2918,112 +2900,112 @@ "\n", "\n", - "\n", "\n", "\n", "\n", - "\n", - "t\n", - "[all]\n", + "\n", + "t\n", + "[all]\n", "\n", "\n", "\n", "0\n", - "\n", - "0\n", + "\n", + "0\n", "\n", "\n", "\n", "I->0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "2\n", - "\n", - "2\n", + "\n", + "2\n", "\n", "\n", "\n", "0->2\n", - "\n", - "\n", - "i0\n", + "\n", + "\n", + "i0\n", "\n", "\n", "\n", "4\n", - "\n", - "4\n", + "\n", + "4\n", "\n", "\n", "\n", "0->4\n", - "\n", - "\n", - "!i0\n", + "\n", + "\n", + "!i0\n", "\n", "\n", "\n", "1\n", - "\n", - "1\n", + "\n", + "1\n", "\n", "\n", "\n", "2->1\n", - "\n", - "\n", - "o0\n", + "\n", + "\n", + "o0\n", "\n", "\n", "\n", "3\n", - "\n", - "3\n", + "\n", + "3\n", "\n", "\n", "\n", "4->3\n", - "\n", - "\n", - "!o0\n", + "\n", + "\n", + "!o0\n", "\n", "\n", "\n", "5\n", - "\n", - "5\n", + "\n", + "5\n", "\n", "\n", "\n", "1->5\n", - "\n", - "\n", - "1\n", + "\n", + "\n", + "1\n", "\n", "\n", "\n", "5->1\n", - "\n", - "\n", - "1\n", + "\n", + "\n", + "1\n", "\n", "\n", "\n", "3->4\n", - "\n", - "\n", - "1\n", + "\n", + "\n", + "1\n", "\n", "\n", "\n" ], "text/plain": [ - " *' at 0x7fc9680c3330> >" + " *' at 0x7f0e5855cc60> >" ] }, "metadata": {}, @@ -3035,144 +3017,144 @@ "
\n", "\n", - "\n", "\n", "\n", - "\n", - "\n", - "t\n", - "[all]\n", + " viewBox=\"0.00 0.00 282.00 148.79\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n", + "\n", + "\n", + "t\n", + "[all]\n", "\n", "\n", "\n", "0\n", - "\n", - "0\n", + "\n", + "0\n", "\n", "\n", "\n", "I->0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "2\n", - "\n", - "2\n", + "\n", + "2\n", "\n", "\n", "\n", "0->2\n", - "\n", - "\n", - "i0\n", + "\n", + "\n", + "i0\n", "\n", "\n", "\n", "3\n", - "\n", - "3\n", + "\n", + "3\n", "\n", "\n", "\n", "0->3\n", - "\n", - "\n", - "!i0\n", + "\n", + "\n", + "!i0\n", "\n", "\n", "\n", "2->0\n", - "\n", - "\n", - "o0\n", + "\n", + "\n", + "o0\n", "\n", "\n", "\n", "1\n", - "\n", - "1\n", + "\n", + "1\n", "\n", "\n", "\n", "3->1\n", - "\n", - "\n", - "!o0\n", + "\n", + "\n", + "!o0\n", "\n", "\n", "\n", "1->3\n", - "\n", - "\n", - "1\n", + "\n", + "\n", + "1\n", "\n", "\n", "\n", "
\n", "\n", - "\n", "\n", "\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "0\n", - "\n", - "0\n", + "\n", + "0\n", "\n", "\n", "\n", "I->0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "0->0\n", - "\n", - "\n", - "\n", - "i0\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "i0\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "1\n", - "\n", - "1\n", + "\n", + "1\n", "\n", "\n", "\n", "0->1\n", - "\n", - "\n", + "\n", + "\n", "\n", - "!i0\n", - "/\n", + "!i0\n", + "/\n", "\n", - "!o0\n", + "!o0\n", "\n", "\n", "\n", "1->1\n", - "\n", - "\n", + "\n", + "\n", "\n", - "1\n", - "/\n", + "1\n", + "/\n", "\n", - "!o0\n", + "!o0\n", "\n", "\n", "\n", @@ -3191,72 +3173,72 @@ "\n", "\n", - "\n", "\n", "\n", + " viewBox=\"0.00 0.00 143.20 352.00\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\">\n", "\n", - "\n", + "\n", "\n", "\n", "4\n", - "\n", - "L0_out\n", + "\n", + "L0_out\n", "\n", "\n", "\n", "6\n", - "\n", - "6\n", + "\n", + "6\n", "\n", "\n", "\n", "4->6\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "L0\n", - "\n", - "L0_in\n", + "\n", + "L0_in\n", "\n", "\n", "\n", "6->L0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "o0\n", - "\n", - "o0\n", + "\n", + "o0\n", "\n", "\n", "\n", "6->o0:s\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "2\n", - "\n", - "i0\n", + "\n", + "i0\n", "\n", "\n", "\n", "2->6\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n" ], "text/plain": [ - " >" + " >" ] }, "metadata": {}, @@ -3278,7 +3260,6 @@ }, { "cell_type": "markdown", - "id": "f8dab019", "metadata": {}, "source": [ "To force the presence of extra variables in the circuit, they can be passed to `mealy_machine_to_aig()`." @@ -3287,7 +3268,6 @@ { "cell_type": "code", "execution_count": 10, - "id": "091d7c97", "metadata": {}, "outputs": [ { @@ -3296,96 +3276,96 @@ "\n", "\n", - "\n", "\n", - "\n", + "\n", "\n", - "\n", + "\n", "\n", "\n", "6\n", - "\n", - "L0_out\n", + "\n", + "L0_out\n", "\n", "\n", "\n", "8\n", - "\n", - "8\n", + "\n", + "8\n", "\n", "\n", "\n", "6->8\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "L0\n", - "\n", - "L0_in\n", + "\n", + "L0_in\n", "\n", "\n", "\n", "8->L0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "o0\n", - "\n", - "o0\n", + "\n", + "o0\n", "\n", "\n", "\n", "8->o0:s\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "o1\n", - "\n", - "o1\n", + "\n", + "o1\n", "\n", "\n", "\n", "2\n", - "\n", - "i0\n", + "\n", + "i0\n", "\n", "\n", "\n", "2->8\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "4\n", - "\n", - "i1\n", + "\n", + "i1\n", "\n", "\n", "\n", "0\n", - "\n", - "False\n", + "\n", + "False\n", "\n", "\n", "\n", "0->o1:s\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n" ], "text/plain": [ - " >" + " >" ] }, "metadata": {}, @@ -3398,7 +3378,6 @@ }, { "cell_type": "markdown", - "id": "364c8d76", "metadata": {}, "source": [ "# Combining Mealy machines\n", @@ -3418,7 +3397,6 @@ { "cell_type": "code", "execution_count": 11, - "id": "57b3b51d", "metadata": {}, "outputs": [ { @@ -3434,134 +3412,134 @@ "
\n", "\n", - "\n", "\n", - "\n", - "\n", - "\n", - "t\n", - "[all]\n", + "\n", + "\n", + "\n", + "t\n", + "[all]\n", "\n", "\n", "\n", "0\n", - "\n", - "0\n", + "\n", + "0\n", "\n", "\n", "\n", "I->0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "1\n", - "\n", - "1\n", + "\n", + "1\n", "\n", "\n", "\n", "0->1\n", - "\n", - "\n", - "(!i0 & !i1) | (i0 & i1)\n", + "\n", + "\n", + "(!i0 & !i1) | (i0 & i1)\n", "\n", "\n", "\n", "2\n", - "\n", - "2\n", + "\n", + "2\n", "\n", "\n", "\n", "0->2\n", - "\n", - "\n", - "(!i0 & i1) | (i0 & !i1)\n", + "\n", + "\n", + "(!i0 & i1) | (i0 & !i1)\n", "\n", "\n", "\n", "1->0\n", - "\n", - "\n", - "!o0\n", + "\n", + "\n", + "!o0\n", "\n", "\n", "\n", "2->0\n", - "\n", - "\n", - "o0\n", + "\n", + "\n", + "o0\n", "\n", "\n", "\n", "
\n", "\n", - "\n", "\n", - "\n", - "\n", - "\n", - "t\n", - "[all]\n", + "\n", + "\n", + "\n", + "t\n", + "[all]\n", "\n", "\n", "\n", "0\n", - "\n", - "0\n", + "\n", + "0\n", "\n", "\n", "\n", "I->0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "1\n", - "\n", - "1\n", + "\n", + "1\n", "\n", "\n", "\n", "0->1\n", - "\n", - "\n", - "(!i0 & !i1) | (i0 & i1)\n", + "\n", + "\n", + "(!i0 & !i1) | (i0 & i1)\n", "\n", "\n", "\n", "2\n", - "\n", - "2\n", + "\n", + "2\n", "\n", "\n", "\n", "0->2\n", - "\n", - "\n", - "(!i0 & i1) | (i0 & !i1)\n", + "\n", + "\n", + "(!i0 & i1) | (i0 & !i1)\n", "\n", "\n", "\n", "1->0\n", - "\n", - "\n", - "o1\n", + "\n", + "\n", + "o1\n", "\n", "\n", "\n", "2->0\n", - "\n", - "\n", - "!o1\n", + "\n", + "\n", + "!o1\n", "\n", "\n", "\n", @@ -3587,94 +3565,94 @@ "
\n", "\n", - "\n", "\n", - "\n", + "\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "0\n", - "\n", - "0\n", + "\n", + "0\n", "\n", "\n", "\n", "I->0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "0->0\n", - "\n", - "\n", - "\n", - "(!i0 & !i1) | (i0 & i1)\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "(!i0 & !i1) | (i0 & i1)\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "0->0\n", - "\n", - "\n", - "\n", - "(!i0 & i1) | (i0 & !i1)\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "(!i0 & i1) | (i0 & !i1)\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "
\n", "\n", - "\n", "\n", - "\n", + "\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "0\n", - "\n", - "0\n", + "\n", + "0\n", "\n", "\n", "\n", "I->0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "0->0\n", - "\n", - "\n", - "\n", - "(!i0 & !i1) | (i0 & i1)\n", - "/\n", - "\n", - "o1\n", + "\n", + "\n", + "\n", + "(!i0 & !i1) | (i0 & i1)\n", + "/\n", + "\n", + "o1\n", "\n", "\n", "\n", "0->0\n", - "\n", - "\n", - "\n", - "(!i0 & i1) | (i0 & !i1)\n", - "/\n", - "\n", - "!o1\n", + "\n", + "\n", + "\n", + "(!i0 & i1) | (i0 & !i1)\n", + "/\n", + "\n", + "!o1\n", "\n", "\n", "\n", @@ -3700,108 +3678,108 @@ "\n", "\n", - "\n", "\n", "\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "6\n", - "\n", - "6\n", + "\n", + "6\n", "\n", "\n", "\n", "10\n", - "\n", - "10\n", + "\n", + "10\n", "\n", "\n", "\n", "6->10\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "8\n", - "\n", - "8\n", + "\n", + "8\n", "\n", "\n", "\n", "8->10\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "o0\n", - "\n", - "o0\n", + "\n", + "o0\n", "\n", "\n", "\n", "10->o0:s\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "o1\n", - "\n", - "o1\n", + "\n", + "o1\n", "\n", "\n", "\n", "10->o1:s\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "2\n", - "\n", - "i0\n", + "\n", + "i0\n", "\n", "\n", "\n", "2->6\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "2->8\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "4\n", - "\n", - "i1\n", + "\n", + "i1\n", "\n", "\n", "\n", "4->6\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "4->8\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n" ], "text/plain": [ - " >" + " >" ] }, "metadata": {}, @@ -3820,53 +3798,53 @@ "\n", "\n", - "\n", "\n", - "\n", + "\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "0\n", - "\n", - "0,0\n", + "\n", + "0,0\n", "\n", "\n", "\n", "I->0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "0->0\n", - "\n", - "\n", - "\n", - "(!i0 & !i1) | (i0 & i1)\n", - "/\n", - "\n", - "!o0 & o1\n", + "\n", + "\n", + "\n", + "(!i0 & !i1) | (i0 & i1)\n", + "/\n", + "\n", + "!o0 & o1\n", "\n", "\n", "\n", "0->0\n", - "\n", - "\n", - "\n", - "(!i0 & i1) | (i0 & !i1)\n", - "/\n", - "\n", - "o0 & !o1\n", + "\n", + "\n", + "\n", + "(!i0 & i1) | (i0 & !i1)\n", + "/\n", + "\n", + "o0 & !o1\n", "\n", "\n", "\n" ], "text/plain": [ - " *' at 0x7fc968143bd0> >" + " *' at 0x7f0e5855cd20> >" ] }, "metadata": {}, @@ -3878,108 +3856,108 @@ "\n", "\n", - "\n", "\n", "\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "6\n", - "\n", - "6\n", + "\n", + "6\n", "\n", "\n", "\n", "10\n", - "\n", - "10\n", + "\n", + "10\n", "\n", "\n", "\n", "6->10\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "8\n", - "\n", - "8\n", + "\n", + "8\n", "\n", "\n", "\n", "8->10\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "o0\n", - "\n", - "o0\n", + "\n", + "o0\n", "\n", "\n", "\n", "10->o0:s\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "o1\n", - "\n", - "o1\n", + "\n", + "o1\n", "\n", "\n", "\n", "10->o1:s\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "2\n", - "\n", - "i0\n", + "\n", + "i0\n", "\n", "\n", "\n", "2->6\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "2->8\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "4\n", - "\n", - "i1\n", + "\n", + "i1\n", "\n", "\n", "\n", "4->6\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "4->8\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n" ], "text/plain": [ - " >" + " >" ] }, "metadata": {}, @@ -4013,7 +3991,6 @@ }, { "cell_type": "markdown", - "id": "7d5a8a32", "metadata": {}, "source": [ "# Reading an AIGER-file\n", @@ -4028,7 +4005,6 @@ { "cell_type": "code", "execution_count": 12, - "id": "9da1f39e", "metadata": {}, "outputs": [], "source": [ @@ -4049,7 +4025,6 @@ { "cell_type": "code", "execution_count": 13, - "id": "7295f20a", "metadata": {}, "outputs": [ { @@ -4058,108 +4033,108 @@ "\n", "\n", - "\n", "\n", "\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "6\n", - "\n", - "6\n", + "\n", + "6\n", "\n", "\n", "\n", "10\n", - "\n", - "10\n", + "\n", + "10\n", "\n", "\n", "\n", "6->10\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "o1\n", - "\n", - "d\n", + "\n", + "d\n", "\n", "\n", "\n", "6->o1:s\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "8\n", - "\n", - "8\n", + "\n", + "8\n", "\n", "\n", "\n", "8->10\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "o0\n", - "\n", - "c\n", + "\n", + "c\n", "\n", "\n", "\n", "10->o0:s\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "2\n", - "\n", - "a\n", + "\n", + "a\n", "\n", "\n", "\n", "2->6\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "2->8\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "4\n", - "\n", - "b\n", + "\n", + "b\n", "\n", "\n", "\n", "4->6\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "4->8\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n" ], "text/plain": [ - " >" + " >" ] }, "metadata": {}, @@ -4174,7 +4149,6 @@ { "cell_type": "code", "execution_count": 14, - "id": "730952f7", "metadata": {}, "outputs": [ { @@ -4203,7 +4177,6 @@ { "cell_type": "code", "execution_count": 15, - "id": "38b5b8a1", "metadata": {}, "outputs": [ { @@ -4220,7 +4193,6 @@ }, { "cell_type": "markdown", - "id": "6bde5eac", "metadata": {}, "source": [ "An AIG circuit can be transformed into a monitor/Mealy machine. This can be used for instance to check that it does not intersect the negation of the specification." @@ -4229,7 +4201,6 @@ { "cell_type": "code", "execution_count": 16, - "id": "14f89c9b", "metadata": {}, "outputs": [ { @@ -4238,52 +4209,52 @@ "\n", "\n", - "\n", "\n", - "\n", + "\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "0\n", - "\n", - "0\n", + "\n", + "0\n", "\n", "\n", "\n", "I->0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "0->0\n", - "\n", - "\n", - "\n", - "!a & !b\n", - "/\n", - "\n", - "!c & !d\n", - "\n", - "a & b\n", - "/\n", - "\n", - "!c & d\n", - "\n", - "(!a & b) | (a & !b)\n", - "/\n", - "\n", - "c & !d\n", + "\n", + "\n", + "\n", + "!a & !b\n", + "/\n", + "\n", + "!c & !d\n", + "\n", + "a & b\n", + "/\n", + "\n", + "!c & d\n", + "\n", + "(!a & b) | (a & !b)\n", + "/\n", + "\n", + "c & !d\n", "\n", "\n", "\n" ], "text/plain": [ - " *' at 0x7fc968069ed0> >" + " *' at 0x7f0e584ee4e0> >" ] }, "execution_count": 16, @@ -4297,7 +4268,6 @@ }, { "cell_type": "markdown", - "id": "e1f01aa0", "metadata": {}, "source": [ "Note that the generation of aiger circuits from Mealy machines is flexible and accepts separated Mealy machines\n", @@ -4307,7 +4277,6 @@ { "cell_type": "code", "execution_count": 17, - "id": "93e1fc70", "metadata": {}, "outputs": [ { @@ -4316,114 +4285,114 @@ "
\n", "\n", - "\n", "\n", - "\n", + "\n", "\n", - "\n", + "\n", "\n", "\n", "\n", "0\n", - "\n", - "0\n", + "\n", + "0\n", "\n", "\n", "\n", "I->0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "0->0\n", - "\n", - "\n", - "\n", - "(!i0 & !i1) | (i0 & i1)\n", - "/\n", - "\n", - "!o0\n", + "\n", + "\n", + "\n", + "(!i0 & !i1) | (i0 & i1)\n", + "/\n", + "\n", + "!o0\n", "\n", "\n", "\n", "0->0\n", - "\n", - "\n", - "\n", - "(!i0 & i1) | (i0 & !i1)\n", - "/\n", - "\n", - "o0\n", + "\n", + "\n", + "\n", + "(!i0 & i1) | (i0 & !i1)\n", + "/\n", + "\n", + "o0\n", "\n", "\n", "\n", "
\n", "\n", - "\n", "\n", - "\n", - "\n", - "\n", - "t\n", - "[all]\n", + "\n", + "\n", + "\n", + "t\n", + "[all]\n", "\n", "\n", "\n", "0\n", - "\n", - "0\n", + "\n", + "0\n", "\n", "\n", "\n", "I->0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "1\n", - "\n", - "1\n", + "\n", + "1\n", "\n", "\n", "\n", "0->1\n", - "\n", - "\n", - "(!i0 & !i1) | (i0 & i1)\n", + "\n", + "\n", + "(!i0 & !i1) | (i0 & i1)\n", "\n", "\n", "\n", "2\n", - "\n", - "2\n", + "\n", + "2\n", "\n", "\n", "\n", "0->2\n", - "\n", - "\n", - "(!i0 & i1) | (i0 & !i1)\n", + "\n", + "\n", + "(!i0 & i1) | (i0 & !i1)\n", "\n", "\n", "\n", "1->0\n", - "\n", - "\n", - "!o0\n", + "\n", + "\n", + "!o0\n", "\n", "\n", "\n", "2->0\n", - "\n", - "\n", - "o0\n", + "\n", + "\n", + "o0\n", "\n", "\n", "\n", @@ -4455,7 +4424,6 @@ { "cell_type": "code", "execution_count": 18, - "id": "6cb96c81", "metadata": {}, "outputs": [ { @@ -4464,180 +4432,180 @@ "
\n", "\n", - "\n", "\n", "\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "6\n", - "\n", - "6\n", + "\n", + "6\n", "\n", "\n", "\n", "10\n", - "\n", - "10\n", + "\n", + "10\n", "\n", "\n", "\n", "6->10\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "8\n", - "\n", - "8\n", + "\n", + "8\n", "\n", "\n", "\n", "8->10\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "o0\n", - "\n", - "o0\n", + "\n", + "o0\n", "\n", "\n", "\n", "10->o0:s\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "2\n", - "\n", - "i0\n", + "\n", + "i0\n", "\n", "\n", "\n", "2->6\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "2->8\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "4\n", - "\n", - "i1\n", + "\n", + "i1\n", "\n", "\n", "\n", "4->6\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "4->8\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "
\n", "\n", - "\n", "\n", "\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "6\n", - "\n", - "6\n", + "\n", + "6\n", "\n", "\n", "\n", "10\n", - "\n", - "10\n", + "\n", + "10\n", "\n", "\n", "\n", "6->10\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "8\n", - "\n", - "8\n", + "\n", + "8\n", "\n", "\n", "\n", "8->10\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "o0\n", - "\n", - "o0\n", + "\n", + "o0\n", "\n", "\n", "\n", "10->o0:s\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "2\n", - "\n", - "i0\n", + "\n", + "i0\n", "\n", "\n", "\n", "2->6\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "2->8\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "4\n", - "\n", - "i1\n", + "\n", + "i1\n", "\n", "\n", "\n", "4->6\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "4->8\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", @@ -4658,7 +4626,7 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 3 (ipykernel)", + "display_name": "Python 3", "language": "python", "name": "python3" }, @@ -4672,7 +4640,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.10" + "version": "3.7.3" } }, "nbformat": 4, diff --git a/tests/python/toparity.py b/tests/python/toparity.py index 37e111f9b..ad9bc6e0b 100644 --- a/tests/python/toparity.py +++ b/tests/python/toparity.py @@ -26,57 +26,135 @@ tc = TestCase() # Tests for the new version of to_parity +# It is no more a no_option as we now have more options (like iar, bscc, …) no_option = spot.to_parity_options() no_option.search_ex = False no_option.use_last = False -no_option.force_order = False +no_option.use_last_post_process = False no_option.partial_degen = False no_option.acc_clean = False no_option.parity_equiv = False +no_option.tar = False +no_option.iar = True +no_option.lar_dfs = True +no_option.bscc = True no_option.parity_prefix = False +no_option.parity_prefix_general = False +no_option.generic_emptiness = False no_option.rabin_to_buchi = False +no_option.buchi_type_to_buchi = False +no_option.parity_type_to_parity = False +no_option.reduce_col_deg = False no_option.propagate_col = False +no_option.use_generalized_rabin = False acc_clean_search_opt = spot.to_parity_options() -acc_clean_search_opt.force_order = False -acc_clean_search_opt.partial_degen = False -acc_clean_search_opt.parity_equiv = False -acc_clean_search_opt.parity_prefix = False -acc_clean_search_opt.rabin_to_buchi = False -acc_clean_search_opt.propagate_col = False +no_option.search_ex = False +no_option.use_last = False +no_option.use_last_post_process = False +no_option.force_order = False +no_option.partial_degen = False +no_option.acc_clean = True +no_option.parity_equiv = False +no_option.tar = False +no_option.iar = True +no_option.lar_dfs = True +no_option.bscc = True +no_option.parity_prefix = False +no_option.parity_prefix_general = False +no_option.generic_emptiness = False +no_option.rabin_to_buchi = False +no_option.buchi_type_to_buchi = False +no_option.parity_type_to_parity = False +no_option.reduce_col_deg = False +no_option.propagate_col = False +no_option.use_generalized_rabin = False partial_degen_opt = spot.to_parity_options() partial_degen_opt.search_ex = False +partial_degen_opt.use_last = False +partial_degen_opt.use_last_post_process = False partial_degen_opt.force_order = False +partial_degen_opt.partial_degen = True +partial_degen_opt.acc_clean = False partial_degen_opt.parity_equiv = False +partial_degen_opt.tar = False +partial_degen_opt.iar = True +partial_degen_opt.lar_dfs = True +partial_degen_opt.bscc = True partial_degen_opt.parity_prefix = False +partial_degen_opt.parity_prefix_general = False +partial_degen_opt.generic_emptiness = False partial_degen_opt.rabin_to_buchi = False +partial_degen_opt.buchi_type_to_buchi = False +partial_degen_opt.parity_type_to_parity = False +partial_degen_opt.reduce_col_deg = False partial_degen_opt.propagate_col = False +partial_degen_opt.use_generalized_rabin = False parity_equiv_opt = spot.to_parity_options() parity_equiv_opt.search_ex = False parity_equiv_opt.use_last = False -parity_equiv_opt.force_order = False +parity_equiv_opt.use_last_post_process = False parity_equiv_opt.partial_degen = False +parity_equiv_opt.acc_clean = False +parity_equiv_opt.parity_equiv = True +parity_equiv_opt.tar = False +parity_equiv_opt.iar = True +parity_equiv_opt.lar_dfs = True +parity_equiv_opt.bscc = True parity_equiv_opt.parity_prefix = False +parity_equiv_opt.parity_prefix_general = False +parity_equiv_opt.generic_emptiness = False parity_equiv_opt.rabin_to_buchi = False +parity_equiv_opt.buchi_type_to_buchi = False +parity_equiv_opt.parity_type_to_parity = False +parity_equiv_opt.reduce_col_deg = False parity_equiv_opt.propagate_col = False +parity_equiv_opt.use_generalized_rabin = False rab_to_buchi_opt = spot.to_parity_options() +rab_to_buchi_opt.search_ex = False rab_to_buchi_opt.use_last = False -rab_to_buchi_opt.force_order = False +rab_to_buchi_opt.use_last_post_process = False rab_to_buchi_opt.partial_degen = False -rab_to_buchi_opt.parity_equiv = False +rab_to_buchi_opt.acc_clean = False +rab_to_buchi_opt.parity_equiv = True +rab_to_buchi_opt.tar = False +rab_to_buchi_opt.iar = True +rab_to_buchi_opt.lar_dfs = False +rab_to_buchi_opt.bscc = False rab_to_buchi_opt.parity_prefix = False +rab_to_buchi_opt.parity_prefix_general = False +rab_to_buchi_opt.generic_emptiness = False +rab_to_buchi_opt.rabin_to_buchi = True +rab_to_buchi_opt.buchi_type_to_buchi = False +rab_to_buchi_opt.parity_type_to_parity = False +rab_to_buchi_opt.reduce_col_deg = False rab_to_buchi_opt.propagate_col = False +rab_to_buchi_opt.use_generalized_rabin = False -# Force to use CAR or IAR for each SCC +# Force to use CAR, IAR or TAR for each SCC use_car_opt = spot.to_parity_options() +use_car_opt.search_ex = True +use_car_opt.use_last = True +use_car_opt.use_last_post_process = True use_car_opt.partial_degen = False +use_car_opt.acc_clean = False use_car_opt.parity_equiv = False +use_car_opt.tar = True +use_car_opt.iar = True +use_car_opt.lar_dfs = True +use_car_opt.bscc = True use_car_opt.parity_prefix = False +use_car_opt.parity_prefix_general = False +use_car_opt.generic_emptiness = False use_car_opt.rabin_to_buchi = False +use_car_opt.buchi_type_to_buchi = False +use_car_opt.parity_type_to_parity = False +use_car_opt.reduce_col_deg = False use_car_opt.propagate_col = False +use_car_opt.use_generalized_rabin = False all_opt = spot.to_parity_options() all_opt.pretty_print = True @@ -100,15 +178,28 @@ def test(aut, expected_num_states=[], full=True): p1 = spot.to_parity(aut, search_ex = opt.search_ex, use_last = opt.use_last, + use_last_post_process = \ + opt.use_last_post_process, force_order = opt.force_order, partial_degen = opt.partial_degen, acc_clean = opt.acc_clean, parity_equiv = opt.parity_equiv, + tar = opt.tar, + iar = opt.iar, + lar_dfs = opt.lar_dfs, + bscc = opt.bscc, parity_prefix = opt.parity_prefix, + parity_prefix_general = \ + opt.parity_prefix_general, + generic_emptiness = opt.generic_emptiness, rabin_to_buchi = opt.rabin_to_buchi, + buchi_type_to_buchi = opt.buchi_type_to_buchi, + parity_type_to_parity = \ + opt.parity_type_to_parity, reduce_col_deg = opt.reduce_col_deg, propagate_col = opt.propagate_col, - pretty_print = opt.pretty_print, + use_generalized_rabin = \ + opt.use_generalized_rabin ) else: p1 = spot.acd_transform(aut) @@ -205,7 +296,7 @@ State: 13 [0&1] 5 [!0&!1] 10 {0 1 3 5} [0&!1] 13 {1 3} ---END--"""), [35, 30, 23, 32, 31, 28, 22, 21]) +--END--"""), [32, 22, 23, 30, 33, 45, 22, 21]) test(spot.automaton(""" HOA: v1 @@ -223,7 +314,7 @@ State: 1 [0&!1] 1 {4} [!0&1] 1 {0 1 2 3} [!0&!1] 1 {0 3} ---END--"""), [7, 5, 3, 6, 5, 5, 3, 3]) +--END--"""), [6, 3, 3, 5, 5, 26, 3, 3]) test(spot.automaton("""HOA: v1 States: 2 @@ -239,7 +330,7 @@ State: 0 State: 1 [0&1] 1 {2 3 4} [!0&!1] 0 {1 2} ---END--"""), [9, 3, 2, 3, 3, 3, 2, 2]) +--END--"""), [3, 2, 2, 9, 9, 10, 2, 2]) for i,f in enumerate(spot.randltl(10, 200)): test(spot.translate(f, "det", "G"), full=(i<50)) @@ -279,7 +370,7 @@ State: 3 [!0&1] 2 {1 4} [0&1] 3 {0} --END-- -"""), [80, 47, 104, 104, 102, 29, 6, 5]) +"""), [104, 6, 80, 23, 27, 17, 6, 5]) test(spot.automaton(""" HOA: v1 @@ -313,7 +404,7 @@ State: 4 [0&!1] 4 [0&1] 4 {1 2 4} --END-- -"""), [9, 6, 7, 7, 6, 6, 6, 6]) +"""), [6, 6, 7, 9, 9, 10, 6, 6]) test(spot.automaton(""" HOA: v1 @@ -335,7 +426,7 @@ State: 1 [0&!1] 1 {2 3} [0&1] 1 {1 2 4} --END-- -"""), [11, 3, 2, 3, 3, 3, 2, 2]) +"""), [3, 2, 2, 6, 6, 6, 2, 2]) # Tests both the old and new version of to_parity @@ -366,7 +457,7 @@ explicit-labels trans-acc --BODY-- State: 0 [0&1] 2 {4 5} [0&1] 4 {0 4} p = spot.to_parity_old(a, True) tc.assertEqual(p.num_states(), 22) tc.assertTrue(spot.are_equivalent(a, p)) -test(a, [8, 6, 6, 6, 6, 6, 6, 6]) +test(a, [6, 6, 7, 8, 6, 7, 6, 6]) # Force a few edges to false, to make sure to_parity() is OK with that. for e in a.out(2): @@ -380,7 +471,7 @@ for e in a.out(3): p = spot.to_parity_old(a, True) tc.assertEqual(p.num_states(), 22) tc.assertTrue(spot.are_equivalent(a, p)) -test(a, [7, 6, 6, 6, 6, 6, 6, 6]) +test(a, [6, 6, 7, 8, 6, 7, 6, 6]) for f in spot.randltl(4, 400): d = spot.translate(f, "det", "G") @@ -396,4 +487,4 @@ for f in spot.randltl(5, 2000): a = spot.translate('!(GFa -> (GFb & GF(!b & !Xb)))', 'gen', 'det') b = spot.to_parity_old(a, True) tc.assertTrue(a.equivalent_to(b)) -test(a, [7, 7, 3, 7, 7, 7, 3, 3]) +test(a, [7, 3, 3, 8, 8, 7, 3, 3]) From 12920c44e3b930903452ca2171c6a62b7edf2d68 Mon Sep 17 00:00:00 2001 From: Antoine Martin Date: Wed, 4 May 2022 17:25:58 +0200 Subject: [PATCH 112/337] Trigger archival services on new release * .gitlab-ci.yml: curl Software Heritage and Internet Archive endpoints to trigger archival on push to stable --- .gitlab-ci.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index a1bb0ca9a..86d89f9d7 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -366,6 +366,8 @@ publish-stable: - tgz=`ls spot-*.tar.* | head -n 1` - case $tgz in *[0-9].tar.*) scp $tgz doc@perso:/var/www/dload/spot/;; esac - curl -X POST -F ref=master -F token=$TRIGGER_SPOT_WEB -F "variables[spot_branch]=stable" https://gitlab.lrde.epita.fr/api/v4/projects/131/trigger/pipeline + - curl -X POST "https://archive.softwareheritage.org/api/1/origin/save/git/url/https://gitlab.lrde.epita.fr/spot/spot/" + - curl "https://web.archive.org/save/https://www.lrde.epita.fr/dload/spot/$tgz" publish-unstable: only: From 3e2201bd80e3d995b19a75487de0309c1834e1f6 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Tue, 12 Jul 2022 11:47:02 +0200 Subject: [PATCH 113/337] tests: add figures from CAV'22 paper * tests/python/cav22-figs.ipynb: New file. * doc/org/tut.org, tests/Makefile.am: Add it. --- doc/org/tut.org | 1 + tests/Makefile.am | 1 + tests/python/cav22-figs.ipynb | 1582 +++++++++++++++++++++++++++++++++ 3 files changed, 1584 insertions(+) create mode 100644 tests/python/cav22-figs.ipynb diff --git a/doc/org/tut.org b/doc/org/tut.org index 598276f38..8ae701bc5 100644 --- a/doc/org/tut.org +++ b/doc/org/tut.org @@ -89,6 +89,7 @@ real notebooks instead. automata. - [[https://spot.lrde.epita.fr/ipynb/atva16-fig2a.html][=atva16-fig2a.ipynb=]] first example from our [[https://www.lrde.epita.fr/~adl/dl/adl/duret.16.atva2.pdf][ATVA'16 tool paper]]. - [[https://spot.lrde.epita.fr/ipynb/atva16-fig2b.html][=atva16-fig2b.ipynb=]] second example from our [[https://www.lrde.epita.fr/~adl/dl/adl/duret.16.atva2.pdf][ATVA'16 tool paper]]. +- [[https://spot.lrde.epita.fr/ipynb/cav22-figs.html][=cav22-figs.ipynb=]] figures from our [[https://www.lrde.epita.fr/~adl/dl/adl/duret.22.cav.pdf][CAV'22 tool paper]]. - [[https://spot.lrde.epita.fr/ipynb/alternation.html][=alternation.ipynb=]] examples of alternating automata. - [[https://spot.lrde.epita.fr/ipynb/stutter-inv.html][=stutter-inv.ipynb=]] working with stutter-invariant formulas properties. - [[https://spot.lrde.epita.fr/ipynb/satmin.html][=satmin.ipynb=]] Python interface for [[file:satmin.org][SAT-based minimization of deterministic ω-automata]]. diff --git a/tests/Makefile.am b/tests/Makefile.am index 1a3d440c3..a13a495b3 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -362,6 +362,7 @@ TESTS_ipython = \ python/atva16-fig2b.ipynb \ python/automata-io.ipynb \ python/automata.ipynb \ + python/cav22-figs.ipynb \ python/contains.ipynb \ python/decompose.ipynb \ python/formulas.ipynb \ diff --git a/tests/python/cav22-figs.ipynb b/tests/python/cav22-figs.ipynb new file mode 100644 index 000000000..ea84319a2 --- /dev/null +++ b/tests/python/cav22-figs.ipynb @@ -0,0 +1,1582 @@ +{ + "cells": [ + { + "cell_type": "markdown", + "id": "4d225dd2-8b41-4bb4-9cae-136c314bbcc9", + "metadata": {}, + "source": [ + "This notebook reproduces the examples shown in our CAV'22 paper, as well as a few more. It was part of the CAV'22 artifact, but has been updated to keep up with recent version of Spot." + ] + }, + { + "cell_type": "code", + "execution_count": 1, + "id": "a1948c94-e737-4b8b-88b8-00d896c5c928", + "metadata": {}, + "outputs": [], + "source": [ + "import spot\n", + "from spot.jupyter import display_inline\n", + "from buddy import bdd_ithvar\n", + "spot.setup()" + ] + }, + { + "cell_type": "markdown", + "id": "c03f8776-c657-4f87-99b5-a56ed1fdcbe3", + "metadata": {}, + "source": [ + "# Figure 1\n", + "\n", + "Fig. 1 of the paper shows (1) how to convert an LTL formula to an automaton with arbitrary acceptance condition, and (2) how to display the internal representation of the automaton." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "d7b6e2d6-7472-4136-8114-ecb03dde1edd", + "metadata": {}, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "Fin(\n", + "\n", + ") & Inf(\n", + "\n", + ")\n", + "[Rabin 1]\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "a & !b\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "a & b\n", + "\n", + "\n", + "\n", + "\n", + "1\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "0->1\n", + "\n", + "\n", + "!a & b\n", + "\n", + "\n", + "\n", + "0->1\n", + "\n", + "\n", + "!a & !b\n", + "\n", + "\n", + "\n", + "\n", + "1->0\n", + "\n", + "\n", + "a & b\n", + "\n", + "\n", + "\n", + "1->0\n", + "\n", + "\n", + "a & !b\n", + "\n", + "\n", + "\n", + "\n", + "1->1\n", + "\n", + "\n", + "!a & !b\n", + "\n", + "\n", + "\n", + "\n", + "1->1\n", + "\n", + "\n", + "!a & b\n", + "\n", + "\n", + "\n", + "\n" + ], + "text/plain": [ + " *' at 0x7fb6a430f5a0> >" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "aut = spot.translate('GF(a <-> Xa) & FGb', 'det', 'gen')\n", + "aut" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "18248bd4-8d80-4ae7-a466-c347e1ea5ad4", + "metadata": {}, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "g\n", + "\n", + "\n", + "\n", + "states\n", + "\n", + "\n", + "states\n", + "\n", + "\n", + "0\n", + "\n", + "\n", + "1\n", + "\n", + "succ\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "5\n", + "\n", + "succ_tail\n", + "\n", + "\n", + "4\n", + "\n", + "\n", + "8\n", + "\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "2\n", + "\n", + "\n", + "3\n", + "\n", + "\n", + "4\n", + "\n", + "\n", + "5\n", + "\n", + "\n", + "6\n", + "\n", + "\n", + "7\n", + "\n", + "\n", + "8\n", + "\n", + "cond\n", + "\n", + "a & !b\n", + "\n", + "a & b\n", + "\n", + "!a & b\n", + "\n", + "!a & !b\n", + "\n", + "a & b\n", + "\n", + "a & !b\n", + "\n", + "!a & !b\n", + "\n", + "!a & b\n", + "\n", + "acc\n", + "\n", + "{0}\n", + "\n", + "{1}\n", + "\n", + "{}\n", + "\n", + "{0}\n", + "\n", + "{}\n", + "\n", + "{0}\n", + "\n", + "{0}\n", + "\n", + "{1}\n", + "\n", + "dst\n", + "\n", + "\n", + "0\n", + "\n", + "\n", + "0\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "0\n", + "\n", + "\n", + "0\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "1\n", + "\n", + "next_succ\n", + "\n", + "\n", + "2\n", + "\n", + "\n", + "3\n", + "\n", + "\n", + "4\n", + "\n", + "0\n", + "\n", + "\n", + "6\n", + "\n", + "\n", + "7\n", + "\n", + "\n", + "8\n", + "\n", + "0\n", + "\n", + "src\n", + "\n", + "\n", + "0\n", + "\n", + "\n", + "0\n", + "\n", + "\n", + "0\n", + "\n", + "\n", + "0\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "meta\n", + "init_state:\n", + "\n", + "0\n", + "num_sets:\n", + "2\n", + "acceptance:\n", + "Fin(0) & Inf(1)\n", + "ap_vars:\n", + "b a\n", + "\n", + "\n", + "\n", + "\n", + "props\n", + "prop_state_acc:\n", + "maybe\n", + "prop_inherently_weak:\n", + "maybe\n", + "prop_terminal:\n", + "no\n", + "prop_weak:\n", + "maybe\n", + "prop_very_weak:\n", + "maybe\n", + "prop_complete:\n", + "maybe\n", + "prop_universal:\n", + "yes\n", + "prop_unambiguous:\n", + "yes\n", + "prop_semi_deterministic:\n", + "yes\n", + "prop_stutter_invariant:\n", + "maybe\n", + "\n", + "\n", + "\n", + "\n" + ], + "text/plain": [ + "" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "aut.show_storage()" + ] + }, + { + "cell_type": "markdown", + "id": "ef2e2a61-046c-42f0-b0a1-8dc7f1f2b37a", + "metadata": {}, + "source": [ + "# Figure 2\n", + "\n", + "Fig.2 shows an example of alternating automaton, represented in two different ways, along with its internal representation." + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "5d2dd179-470c-47c1-b9f2-c5df9f76b2b8", + "metadata": {}, + "outputs": [], + "source": [ + "# We enter the automaton using the HOA format.\n", + "aut2 = spot.automaton(\"\"\"\n", + "HOA: v1\n", + "States: 5\n", + "Start: 3\n", + "acc-name: co-Buchi\n", + "Acceptance: 1 Fin(0)\n", + "AP: 2 \"a\" \"b\"\n", + "--BODY--\n", + "State: 0 {0} \n", + "[0] 1\n", + "[!0] 2\n", + "State: 1 {0} \n", + "[0&1] 0&1\n", + "State: 2 \n", + "[t] 2 \n", + "State: 3 \n", + "[0] 4&0\n", + "State: 4 \n", + "[t] 3 \n", + "--END--\n", + "\"\"\")" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "e1517479-6947-43fd-8369-c4fcdca72e1d", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "[co-Büchi]\n", + "\n", + "\n", + "\n", + "3\n", + "\n", + "3\n", + "\n", + "\n", + "\n", + "I->3\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "-4\n", + "\n", + "\n", + "\n", + "\n", + "3->-4\n", + "\n", + "\n", + "a\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "\n", + "0\n", + "\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "0->1\n", + "\n", + "\n", + "a\n", + "\n", + "\n", + "\n", + "2\n", + "\n", + "2\n", + "\n", + "\n", + "\n", + "0->2\n", + "\n", + "\n", + "!a\n", + "\n", + "\n", + "\n", + "-1\n", + "\n", + "\n", + "\n", + "\n", + "1->-1\n", + "\n", + "\n", + "a & b\n", + "\n", + "\n", + "\n", + "2->2\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "-1->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "-1->1\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "-4->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "4\n", + "\n", + "4\n", + "\n", + "\n", + "\n", + "-4->4\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "4->3\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "[co-Büchi]\n", + "\n", + "\n", + "\n", + "3\n", + "\n", + "3\n", + "\n", + "\n", + "\n", + "I->3\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "-4\n", + "\n", + "\n", + "\n", + "\n", + "3->-4\n", + "\n", + "\n", + "a\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "\n", + "0\n", + "\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "0->1\n", + "\n", + "\n", + "a\n", + "\n", + "\n", + "\n", + "\n", + "0->T2T0\n", + "\n", + "\n", + "!a\n", + "\n", + "\n", + "\n", + "-1\n", + "\n", + "\n", + "\n", + "\n", + "1->-1\n", + "\n", + "\n", + "a & b\n", + "\n", + "\n", + "\n", + "-1->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "-1->1\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "-4->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "4\n", + "\n", + "4\n", + "\n", + "\n", + "\n", + "-4->4\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "4->3\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "
" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "display_inline(aut2, aut2.show('.u'), per_row=2)" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "ef26e6e8-3206-4e51-9858-33f8d27f915c", + "metadata": {}, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "g\n", + "\n", + "\n", + "\n", + "states\n", + "\n", + "\n", + "states\n", + "\n", + "\n", + "0\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "2\n", + "\n", + "\n", + "3\n", + "\n", + "\n", + "4\n", + "\n", + "succ\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "3\n", + "\n", + "\n", + "4\n", + "\n", + "\n", + "5\n", + "\n", + "\n", + "6\n", + "\n", + "succ_tail\n", + "\n", + "\n", + "2\n", + "\n", + "\n", + "3\n", + "\n", + "\n", + "4\n", + "\n", + "\n", + "5\n", + "\n", + "\n", + "6\n", + "\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "edges\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "2\n", + "\n", + "\n", + "3\n", + "\n", + "\n", + "4\n", + "\n", + "\n", + "5\n", + "\n", + "\n", + "6\n", + "\n", + "cond\n", + "\n", + "a\n", + "\n", + "!a\n", + "\n", + "a & b\n", + "\n", + "1\n", + "\n", + "a\n", + "\n", + "1\n", + "\n", + "acc\n", + "\n", + "{0}\n", + "\n", + "{0}\n", + "\n", + "{0}\n", + "\n", + "{}\n", + "\n", + "{}\n", + "\n", + "{}\n", + "\n", + "dst\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "2\n", + "\n", + "\n", + "~0\n", + "\n", + "\n", + "2\n", + "\n", + "\n", + "~3\n", + "\n", + "\n", + "3\n", + "\n", + "next_succ\n", + "\n", + "\n", + "2\n", + "\n", + "0\n", + "\n", + "0\n", + "\n", + "0\n", + "\n", + "0\n", + "\n", + "0\n", + "\n", + "src\n", + "\n", + "\n", + "0\n", + "\n", + "\n", + "0\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "2\n", + "\n", + "\n", + "3\n", + "\n", + "\n", + "4\n", + "\n", + "\n", + "\n", + "dests\n", + "\n", + "\n", + "dests\n", + "\n", + "\n", + "~0\n", + "\n", + "\n", + "\n", + "\n", + "~3\n", + "\n", + "\n", + "\n", + "#cnt/dst\n", + "\n", + "#2\n", + "\n", + "\n", + "0\n", + "\n", + "\n", + "1\n", + "\n", + "#2\n", + "\n", + "\n", + "0\n", + "\n", + "\n", + "4\n", + "\n", + "\n", + "\n", + "meta\n", + "init_state:\n", + "\n", + "3\n", + "num_sets:\n", + "1\n", + "acceptance:\n", + "Fin(0)\n", + "ap_vars:\n", + "b a\n", + "\n", + "\n", + "\n", + "\n", + "props\n", + "prop_state_acc:\n", + "yes\n", + "prop_inherently_weak:\n", + "maybe\n", + "prop_terminal:\n", + "maybe\n", + "prop_weak:\n", + "maybe\n", + "prop_very_weak:\n", + "maybe\n", + "prop_complete:\n", + "no\n", + "prop_universal:\n", + "yes\n", + "prop_unambiguous:\n", + "yes\n", + "prop_semi_deterministic:\n", + "yes\n", + "prop_stutter_invariant:\n", + "maybe\n", + "\n", + "\n", + "\n", + "\n" + ], + "text/plain": [ + "" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "aut2.show_storage()" + ] + }, + { + "cell_type": "markdown", + "id": "f4f651b6-974e-4783-a54a-17a280d30782", + "metadata": {}, + "source": [ + "# Figure 3\n", + "\n", + "Fig. 3 shows an example of game generated by `ltlsynt` from the LTL specification of a reactive controler, and then how this game can be encoded into an And-Inverter-Graph.\n", + "First we retrieve the game generated by `ltlsynt` (any argument passed to `spot.automaton` is interpreted as a command if it ends with a pipe), then we solve it to compute a possible winning strategy. \n", + "\n", + "Player 0 plays from round states and tries to violate the acceptance condition; Player 1 plays from diamond states and tries to satisfy the acceptance condition. Once a game has been solved, the `highlight_strategy` function will decorate the automaton with winning region and computed strategies for player 0 and 1 in red and green respectively. Therefore this game is winning for player 1 from the initial state.\n", + "\n", + "Compared to the paper, the production of parity automata in `ltlsynt` has been improved, and it generates a Büchi game instead (but Büchi can be seen one case of parity)." + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "ac90284d-2493-428b-9db7-cc7aa63384cb", + "metadata": {}, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "[Büchi]\n", + "\n", + "\n", + "\n", + "4\n", + "\n", + "\n", + "4\n", + "\n", + "\n", + "\n", + "I->4\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "7\n", + "\n", + "\n", + "7\n", + "\n", + "\n", + "\n", + "4->7\n", + "\n", + "\n", + "!a\n", + "\n", + "\n", + "\n", + "12\n", + "\n", + "\n", + "12\n", + "\n", + "\n", + "\n", + "4->12\n", + "\n", + "\n", + "a\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "\n", + "0\n", + "\n", + "\n", + "\n", + "6\n", + "\n", + "\n", + "6\n", + "\n", + "\n", + "\n", + "0->6\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "6->0\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "1->7\n", + "\n", + "\n", + "!a\n", + "\n", + "\n", + "\n", + "8\n", + "\n", + "\n", + "8\n", + "\n", + "\n", + "\n", + "1->8\n", + "\n", + "\n", + "a\n", + "\n", + "\n", + "\n", + "7->4\n", + "\n", + "\n", + "!b\n", + "\n", + "\n", + "\n", + "2\n", + "\n", + "2\n", + "\n", + "\n", + "\n", + "7->2\n", + "\n", + "\n", + "b\n", + "\n", + "\n", + "\n", + "8->0\n", + "\n", + "\n", + "b\n", + "\n", + "\n", + "\n", + "5\n", + "\n", + "5\n", + "\n", + "\n", + "\n", + "8->5\n", + "\n", + "\n", + "!b\n", + "\n", + "\n", + "\n", + "9\n", + "\n", + "9\n", + "\n", + "\n", + "\n", + "2->9\n", + "\n", + "\n", + "!a\n", + "\n", + "\n", + "\n", + "10\n", + "\n", + "10\n", + "\n", + "\n", + "\n", + "2->10\n", + "\n", + "\n", + "a\n", + "\n", + "\n", + "\n", + "9->2\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "3\n", + "\n", + "3\n", + "\n", + "\n", + "\n", + "10->3\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "3->9\n", + "\n", + "\n", + "!a\n", + "\n", + "\n", + "\n", + "11\n", + "\n", + "11\n", + "\n", + "\n", + "\n", + "3->11\n", + "\n", + "\n", + "a\n", + "\n", + "\n", + "\n", + "11->0\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "12->1\n", + "\n", + "\n", + "!b\n", + "\n", + "\n", + "\n", + "12->3\n", + "\n", + "\n", + "b\n", + "\n", + "\n", + "\n", + "13\n", + "\n", + "13\n", + "\n", + "\n", + "\n", + "5->13\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "13->0\n", + "\n", + "\n", + "b\n", + "\n", + "\n", + "\n", + "13->5\n", + "\n", + "\n", + "!b\n", + "\n", + "\n", + "\n" + ], + "text/plain": [ + " *' at 0x7fb6a430f300> >" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "game = spot.automaton(\"ltlsynt --outs=b -f 'F(a & Xa) <-> Fb' --print-game-hoa |\")\n", + "spot.solve_game(game)\n", + "spot.highlight_strategy(game)\n", + "game" + ] + }, + { + "cell_type": "markdown", + "id": "d8b3ad5a-fef2-498b-8fd3-2d3940dacbf5", + "metadata": {}, + "source": [ + "The `solved_game_to_mealy()` shown in the paper does not always produce the same type of output, so it is\n", + "better to explicitely call `solved_game_to_split_mealy()` or `solved_game_to_separated_mealy()` depending on the type of output one need. We also show how to use the `reduce_mealy()` method to simplify one." + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "39156f1a-945c-46db-bac2-01565d17b82e", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "\n", + "!a\n", + "/\n", + "\n", + "!b\n", + "\n", + "\n", + "\n", + "1\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "0->1\n", + "\n", + "\n", + "\n", + "a\n", + "/\n", + "\n", + "!b\n", + "\n", + "\n", + "\n", + "1->0\n", + "\n", + "\n", + "\n", + "!a\n", + "/\n", + "\n", + "!b\n", + "\n", + "\n", + "\n", + "2\n", + "\n", + "2\n", + "\n", + "\n", + "\n", + "1->2\n", + "\n", + "\n", + "\n", + "a\n", + "/\n", + "\n", + "b\n", + "\n", + "\n", + "\n", + "2->2\n", + "\n", + "\n", + "\n", + "1\n", + "/\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "\n", + "!a\n", + "/\n", + "\n", + "!b\n", + "\n", + "\n", + "\n", + "1\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "0->1\n", + "\n", + "\n", + "\n", + "a\n", + "/\n", + "\n", + "!b\n", + "\n", + "\n", + "\n", + "1->0\n", + "\n", + "\n", + "\n", + "!a\n", + "/\n", + "\n", + "!b\n", + "\n", + "\n", + "\n", + "1->1\n", + "\n", + "\n", + "\n", + "a\n", + "/\n", + "\n", + "b\n", + "\n", + "\n", + "\n", + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "4\n", + "\n", + "L0_out\n", + "\n", + "\n", + "\n", + "6\n", + "\n", + "6\n", + "\n", + "\n", + "\n", + "4->6\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "o0\n", + "\n", + "b\n", + "\n", + "\n", + "\n", + "6->o0:s\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "L0\n", + "\n", + "L0_in\n", + "\n", + "\n", + "\n", + "2\n", + "\n", + "a\n", + "\n", + "\n", + "\n", + "2->6\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "2->L0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "mealy = spot.solved_game_to_separated_mealy(game)\n", + "mealy_min = spot.reduce_mealy(mealy, True)\n", + "aig = spot.mealy_machine_to_aig(mealy_min, \"isop\")\n", + "display_inline(mealy, mealy_min, aig)" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.10.5" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} From d6b3c757d09700e1117e4167853af5dad719e535 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Tue, 12 Jul 2022 15:43:39 +0200 Subject: [PATCH 114/337] test: ignore OpenBSD's "Terminated" messages For #501. * tests/core/autcross4.test: Here. --- tests/core/autcross4.test | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/tests/core/autcross4.test b/tests/core/autcross4.test index 9e0d68638..13f770d1c 100755 --- a/tests/core/autcross4.test +++ b/tests/core/autcross4.test @@ -1,6 +1,6 @@ #!/bin/sh # -*- coding: utf-8 -*- -# Copyright (C) 2018, 2019 Laboratoire de Recherche et Développement de +# Copyright (C) 2018, 2019, 2022 Laboratoire de Recherche et Développement de # l'Epita (LRDE). # # This file is part of Spot, a model checking library. @@ -85,7 +85,9 @@ autcross -T3 -q --language-preserved --ignore-execution-failures \ --fail-on-timeout \ 'sleep 10; autfilt %H>%O' 'false %H %O' 2>err -Fin && exit 1 cat err -test 4 = `wc -l err && exit 1 From 444e2b5b89175e203d880a20e29f607d94bd2323 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Fri, 22 Jul 2022 10:49:23 +0200 Subject: [PATCH 115/337] parseaut: Add support for PGSolver's format * spot/parseaut/parseaut.yy, spot/parseaut/scanaut.ll: Add rules for PGSolver's format. * spot/parseaut/public.hh: PGAME is a new type of output. * tests/core/pgsolver.test: New file. * tests/Makefile.am: Add it. * tests/python/games.ipynb: More exemples. * NEWS: Mention the new feature. --- NEWS | 6 +- spot/parseaut/parseaut.yy | 110 +++- spot/parseaut/public.hh | 19 +- spot/parseaut/scanaut.ll | 47 +- tests/Makefile.am | 1 + tests/core/pgsolver.test | 265 ++++++++++ tests/python/games.ipynb | 1038 +++++++++++++++++++++++++------------ 7 files changed, 1144 insertions(+), 342 deletions(-) create mode 100755 tests/core/pgsolver.test diff --git a/NEWS b/NEWS index d7f3f4923..7e6bd6a40 100644 --- a/NEWS +++ b/NEWS @@ -65,6 +65,10 @@ New in spot 2.10.6.dev (not yet released) property of Spot to the controllable-AP header for the Extended HOA format used in SyntComp. https://arxiv.org/abs/1912.05793 + - The automaton parser learned to parse games in the PGSolver format. + See the bottom of https://spot.lrde.epita.fr/ipynb/games.html for + an example. + - "aliases" is a new named property that is filled by the HOA parser using the list of aliases declared in the HOA file, and then used by the HOA printer on a best-effort basis. Aliases can be used to @@ -105,7 +109,7 @@ New in spot 2.10.6.dev (not yet released) - The zielonka_tree construction was optimized using the same memoization trick that is used in ACD. Additionally it can now be - run with additional option to abort when the tree as an unwanted + run with additional options to abort when the tree as an unwanted shape, or to turn the tree into a DAG. - contains() can now take a twa as a second argument, not just a diff --git a/spot/parseaut/parseaut.yy b/spot/parseaut/parseaut.yy index 71ab8aaea..52d448c16 100644 --- a/spot/parseaut/parseaut.yy +++ b/spot/parseaut/parseaut.yy @@ -44,6 +44,7 @@ #include "spot/priv/accmap.hh" #include #include +#include using namespace std::string_literals; @@ -256,6 +257,11 @@ extern "C" int strverscmp(const char *s1, const char *s2); %token ENDOFFILE 0 "end of file" %token '[' +%token LINEDIRECTIVE "#line" +%token BDD + +/**** DSTAR tokens ****/ +%token ENDDSTAR "end of DSTAR automaton" %token DRA "DRA" %token DSA "DSA" %token V2 "v2" @@ -263,14 +269,12 @@ extern "C" int strverscmp(const char *s1, const char *s2); %token ACCPAIRS "Acceptance-Pairs:" %token ACCSIG "Acc-Sig:" %token ENDOFHEADER "---" -%token LINEDIRECTIVE "#line" -%token BDD %left '|' %left '&' %precedence '!' -%type init-state-conj-2 state-conj-2 state-conj-checked +%type init-state-conj-2 state-conj-2 state-conj-checked pgame_succs %type checked-state-num state-num acc-set sign %type label-expr %type acc-sig acc-sets trans-acc_opt state-acc_opt @@ -299,10 +303,14 @@ extern "C" int strverscmp(const char *s1, const char *s2); %type nc-one-ident nc-ident-list %type acceptance-cond +/**** PGAME tokens ****/ +// Also using INT, STRING +%token PGAME "start of PGSolver game" +%token ENDPGAME "end of PGSolver game" + /**** LBTT tokens *****/ - // Also using INT, STRING +// Also using INT, STRING %token ENDAUT "-1" -%token ENDDSTAR "end of DSTAR automaton" %token LBTT "LBTT header" %token INT_S "state acceptance" %token LBTT_EMPTY "acceptance sets for empty automaton" @@ -364,6 +372,7 @@ aut-1: hoa { res.h->type = spot::parsed_aut_type::HOA; } | never { res.h->type = spot::parsed_aut_type::NeverClaim; } | lbtt { res.h->type = spot::parsed_aut_type::LBTT; } | dstar /* will set type as DSA or DRA while parsing first line */ + | pgame { res.h->type = spot::parsed_aut_type::PGAME; } /**********************************************************************/ /* Rules for HOA */ @@ -1765,7 +1774,7 @@ dstar_header: dstar_sizes if (res.states > 0) { - res.h->aut->new_states(res.states);; + res.h->aut->new_states(res.states); res.info_states.resize(res.states); } res.acc_style = State_Acc; @@ -1908,6 +1917,93 @@ dstar_states: %empty res.h->aut->new_edge(res.cur_state, i.first, i.second, $3); } +/**********************************************************************/ +/* Rules for PGSolver games */ +/**********************************************************************/ + +pgamestart: PGAME + { + if (res.opts.want_kripke) + { + error(@$, + "cannot read a Kripke structure out of a PGSolver game."); + YYABORT; + } + } + +pgame: pgamestart pgame_nodes ENDPGAME + { + unsigned n = res.accset; + auto p = spot::acc_cond::acc_code::parity_max_odd(n); + res.h->aut->set_acceptance(n, p); + res.acc_style = State_Acc; + // Pretend that we have declared all states. + n = res.h->aut->num_states(); + res.info_states.resize(n); + for (auto& p: res.info_states) + p.declared = true; + } + | pgamestart error ENDPGAME + { + error(@$, "failed to parse this as a PGSolver game"); + } + +pgame_nodes: pgame_node ';' + | pgame_nodes pgame_node ';' + +pgame_succs: INT + { $$ = new std::vector{$1}; } + | pgame_succs ',' INT + { + $$ = $1; + $$->emplace_back($3); + } + +pgame_node: INT INT INT pgame_succs string_opt + { + unsigned state = $1; + unsigned owner = $3; + if (owner > 1) + { + error(@3, "node owner should be 0 or 1"); + owner = 0; + } + // Create any missing state + unsigned max_state = state; + for (unsigned s: *$4) + max_state = std::max(max_state, s); + unsigned n = res.h->aut->num_states(); + if (n <= max_state) + res.h->aut->new_states(max_state + 1 - n); + + // assume the source of the first edge is initial + if (res.start.empty()) + res.start.emplace_back(@$, std::vector{state}); + + // Create all edges with priority $2 + spot::acc_cond::mark_t m({$2}); + for (unsigned s: *$4) + res.h->aut->new_edge(state, s, bddtrue, m); + res.accset = std::max(res.accset, 1 + (int) $2); + + n = res.h->aut->num_states(); + if (!res.state_player) + res.state_player = new std::vector(n); + else if (res.state_player->size() < n) + res.state_player->resize(n); + (*res.state_player)[state] = owner; + + if (std::string* name = $5) + { + if (!res.state_names) + res.state_names = new std::vector(n); + else if (res.state_names->size() < n) + res.state_names->resize(n); + (*res.state_names)[state] = std::move(*name); + delete name; + } + } + /**********************************************************************/ /* Rules for neverclaims */ /**********************************************************************/ @@ -2487,7 +2583,7 @@ static void fix_initial_state(result_& r) start.resize(std::distance(start.begin(), res)); assert(start.size() >= 1); - if (start.size() == 1) + if (start.size() == 1) { if (r.opts.want_kripke) r.h->ks->set_init_state(start.front().front()); diff --git a/spot/parseaut/public.hh b/spot/parseaut/public.hh index d1c1793be..ec16b3ad7 100644 --- a/spot/parseaut/public.hh +++ b/spot/parseaut/public.hh @@ -44,7 +44,14 @@ namespace spot struct parse_aut_error_list {}; #endif - enum class parsed_aut_type { HOA, NeverClaim, LBTT, DRA, DSA, Unknown }; + enum class parsed_aut_type { + HOA, + NeverClaim, + LBTT, + DRA, /* DSTAR format for Rabin */ + DSA, /* DSTAR format for Streett */ + PGAME, /* PG Solver Game */ + Unknown }; /// \brief Result of the automaton parser struct SPOT_API parsed_aut final @@ -91,11 +98,11 @@ namespace spot struct automaton_parser_options final { - bool ignore_abort = false; ///< Skip aborted automata - bool debug = false; ///< Run the parser in debug mode? - bool trust_hoa = true; ///< Trust properties in HOA files - bool raise_errors = false; ///< Raise errors as exceptions. - bool want_kripke = false; ///< Parse as a Kripke structure. + bool ignore_abort = false; ///< Skip aborted automata + bool debug = false; ///< Run the parser in debug mode? + bool trust_hoa = true; ///< Trust properties in HOA files + bool raise_errors = false; ///< Raise errors as exceptions. + bool want_kripke = false; ///< Parse as a Kripke structure. }; /// \brief Parse a stream of automata diff --git a/spot/parseaut/scanaut.ll b/spot/parseaut/scanaut.ll index db8ae75c6..c04834975 100644 --- a/spot/parseaut/scanaut.ll +++ b/spot/parseaut/scanaut.ll @@ -65,12 +65,18 @@ eol \n+|\r+ eol2 (\n\r)+|(\r\n)+ eols ({eol}|{eol2})* identifier [[:alpha:]_][[:alnum:]_.-]* +pgameinit "parity"[ \t]+[0-9]+[ \t]*; +oldpgameinit [0-9]+[ \t]+[0-9]+[ \t]+[01]+[ \t]+[0-9,]+([ \t]+".*")?[ \t]*; +/* A pattern than match the start of an automaton, in order +to detect the end of the previous one. We do not try to match +LBTT automata here. */ +startaut {eols}("HOA:"|"never"|"DSA"|"DRA"|{pgameinit}) %x in_COMMENT in_STRING in_NEVER_PAR %s in_HOA in_NEVER in_LBTT_HEADER %s in_LBTT_STATE in_LBTT_INIT in_LBTT_TRANS %s in_LBTT_T_ACC in_LBTT_S_ACC in_LBTT_GUARD -%s in_DSTAR +%s in_DSTAR in_PGAME %% %{ @@ -127,7 +133,20 @@ identifier [[:alpha:]_][[:alnum:]_.-]* "never" BEGIN(in_NEVER); return token::NEVER; "DSA" BEGIN(in_DSTAR); return token::DSA; "DRA" BEGIN(in_DSTAR); return token::DRA; - +{pgameinit} { + BEGIN(in_PGAME); + char* end = nullptr; + errno = 0; + unsigned long n = strtoul(yytext + 7, &end, 10); + yylval->num = n; + return token::PGAME; + } +{oldpgameinit} { + BEGIN(in_PGAME); + yylval->num = 0; + yyless(0); + return token::PGAME; + } [0-9]+[ \t][0-9]+[ts]? { BEGIN(in_LBTT_HEADER); char* end = nullptr; @@ -229,10 +248,8 @@ identifier [[:alpha:]_][[:alnum:]_.-]* return token::INT; } [0-9]+ parse_int(); return token::INT; - /* The start of any automaton is the end of this one. - We do not try to detect LBTT automata, as that would - be too hard to distinguish from state numbers. */ - {eols}("HOA:"|"never"|"DSA"|"DRA") { + /* The start of any automaton is the end of this one. */ + {startaut} { yylloc->end = yylloc->begin; yyless(0); BEGIN(INITIAL); @@ -270,6 +287,24 @@ identifier [[:alpha:]_][[:alnum:]_.-]* } } +{ + /* Handle short numbers without going through parse_int() for efficiency. */ + [0-9] yylval->num = *yytext - '0'; return token::INT; + [0-9][0-9] { + yylval->num = (yytext[0] * 10) + yytext[1] - '0' * 11; + return token::INT; + } + [0-9]+ parse_int(); return token::INT; + /* The start of any automaton is the end of this one. */ + {startaut} { + yylloc->end = yylloc->begin; + yyless(0); + BEGIN(INITIAL); + return token::ENDPGAME; + } + <> return token::ENDPGAME; +} + /* Note: the LBTT format is scanf friendly, but not Bison-friendly. If we only tokenize it as a stream of INTs, the parser will have a very hard time recognizing what is a state from what is a diff --git a/tests/Makefile.am b/tests/Makefile.am index a13a495b3..91d3f10ea 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -341,6 +341,7 @@ TESTS_twa = \ core/dnfstreett.test \ core/parity.test \ core/parity2.test \ + core/pgsolver.test \ core/ltlsynt.test \ core/ltlsynt-pgame.test \ core/syfco.test \ diff --git a/tests/core/pgsolver.test b/tests/core/pgsolver.test new file mode 100755 index 000000000..e767e1953 --- /dev/null +++ b/tests/core/pgsolver.test @@ -0,0 +1,265 @@ +#!/bin/sh +# -*- coding: utf-8 -*- +# Copyright (C) 2022 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 . + +. ./defs + +set -e + +# This is example 6 is the manual of pgsolver 4.1 +cat >example1.pg <out + +rest='(Fin(6) & (Inf(5) | (Fin(4) & (Inf(3) | (Fin(2) & (Inf(1) | Fin(0)))))))' +cat >example1.hoa <out +diff out example1.hoa + + +# Test streaming. +cat >example2.pg < assert(!(false)) } + od; +accept_all: + skip +} +EOF +autfilt example2.pg >out +parity15=`randaut -A'parity max odd 15' -Q1 0 | grep Acceptance` +parity31=`randaut -A'parity max odd 31' -Q1 0 | grep Acceptance` +cat > example2.hoa <example3.pg <stdout 2>stderr && exit 1 +cat >expected.err< example3.hoa +diff stdout example3.hoa diff --git a/tests/python/games.ipynb b/tests/python/games.ipynb index f3ffd7502..fcc2bf12c 100644 --- a/tests/python/games.ipynb +++ b/tests/python/games.ipynb @@ -47,153 +47,153 @@ "\n", "\n", - "\n", "\n", "\n", "\n", - "\n", - "t\n", - "[all]\n", + "\n", + "t\n", + "[all]\n", "\n", "\n", "\n", "0\n", - "\n", - "0\n", + "\n", + "0\n", "\n", "\n", "\n", "I->0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "1\n", - "\n", - "1\n", + "\n", + "1\n", "\n", "\n", "\n", "0->1\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "3\n", - "\n", - "3\n", + "\n", + "3\n", "\n", "\n", "\n", "0->3\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "1->0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "2\n", - "\n", - "2\n", + "\n", + "2\n", "\n", "\n", "\n", "1->2\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "3->2\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "4\n", - "\n", - "4\n", + "\n", + "4\n", "\n", "\n", "\n", "3->4\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "6\n", - "\n", - "6\n", + "\n", + "6\n", "\n", "\n", "\n", "3->6\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "2->1\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "5\n", - "\n", - "5\n", + "\n", + "5\n", "\n", "\n", "\n", "2->5\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "7\n", - "\n", - "7\n", + "\n", + "7\n", "\n", "\n", "\n", "6->7\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "7->6\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "8\n", - "\n", - "8\n", + "\n", + "8\n", "\n", "\n", "\n", "7->8\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "8->5\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n" @@ -285,153 +285,153 @@ "\n", "\n", - "\n", "\n", "\n", "\n", - "\n", - "t\n", - "[all]\n", + "\n", + "t\n", + "[all]\n", "\n", "\n", "\n", "0\n", - "\n", - "0\n", + "\n", + "0\n", "\n", "\n", "\n", "I->0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "1\n", - "\n", - "1\n", + "\n", + "1\n", "\n", "\n", "\n", "0->1\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "3\n", - "\n", - "3\n", + "\n", + "3\n", "\n", "\n", "\n", "0->3\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "1->0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "2\n", - "\n", - "2\n", + "\n", + "2\n", "\n", "\n", "\n", "1->2\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "3->2\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "4\n", - "\n", - "4\n", + "\n", + "4\n", "\n", "\n", "\n", "3->4\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "6\n", - "\n", - "6\n", + "\n", + "6\n", "\n", "\n", "\n", "3->6\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "2->1\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "5\n", - "\n", - "5\n", + "\n", + "5\n", "\n", "\n", "\n", "2->5\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "7\n", - "\n", - "7\n", + "\n", + "7\n", "\n", "\n", "\n", "6->7\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "7->6\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "8\n", - "\n", - "8\n", + "\n", + "8\n", "\n", "\n", "\n", "7->8\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "8->5\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n" @@ -501,153 +501,153 @@ "\n", "\n", - "\n", "\n", "\n", "\n", - "\n", - "t\n", - "[all]\n", + "\n", + "t\n", + "[all]\n", "\n", "\n", "\n", "0\n", "\n", - "0\n", + "0\n", "\n", "\n", "\n", "I->0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "1\n", "\n", - "1\n", + "1\n", "\n", "\n", "\n", "0->1\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "3\n", "\n", - "3\n", + "3\n", "\n", "\n", "\n", "0->3\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "1->0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "2\n", "\n", - "2\n", + "2\n", "\n", "\n", "\n", "1->2\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "3->2\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "4\n", "\n", - "4\n", + "4\n", "\n", "\n", "\n", "3->4\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "6\n", "\n", - "6\n", + "6\n", "\n", "\n", "\n", "3->6\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "2->1\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "5\n", "\n", - "5\n", + "5\n", "\n", "\n", "\n", "2->5\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "7\n", "\n", - "7\n", + "7\n", "\n", "\n", "\n", "6->7\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "7->6\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "8\n", "\n", - "8\n", + "8\n", "\n", "\n", "\n", "7->8\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "8->5\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n" @@ -670,7 +670,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "## Input/Output\n", + "## Input/Output in HOA format\n", "\n", "An extension of the HOA format makes it possible to store the `state-player` property. This allows us to read the parity game constructed by `ltlsynt` using `spot.automaton()` like any other automaton." ] @@ -686,218 +686,218 @@ "\n", "\n", - "\n", "\n", - "\n", - "\n", - "\n", - "Fin(\n", - "\n", - ")\n", - "[co-Büchi]\n", + "\n", + "\n", + "\n", + "Fin(\n", + "\n", + ")\n", + "[co-Büchi]\n", "\n", "\n", "\n", "0\n", - "\n", - "0\n", + "\n", + "0\n", "\n", "\n", "\n", "I->0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "5\n", - "\n", - "5\n", + "\n", + "5\n", "\n", "\n", "\n", "0->5\n", - "\n", - "\n", - "!a\n", + "\n", + "\n", + "!a\n", "\n", "\n", "\n", "6\n", - "\n", - "6\n", + "\n", + "6\n", "\n", "\n", "\n", "0->6\n", - "\n", - "\n", - "a\n", + "\n", + "\n", + "a\n", "\n", "\n", "\n", "1\n", - "\n", - "1\n", + "\n", + "1\n", "\n", "\n", "\n", "5->1\n", - "\n", - "\n", - "!b\n", + "\n", + "\n", + "!b\n", "\n", "\n", "\n", "3\n", - "\n", - "3\n", + "\n", + "3\n", "\n", "\n", "\n", "5->3\n", - "\n", - "\n", - "b\n", + "\n", + "\n", + "b\n", "\n", "\n", "\n", "2\n", - "\n", - "2\n", + "\n", + "2\n", "\n", "\n", "\n", "6->2\n", - "\n", - "\n", - "!b\n", + "\n", + "\n", + "!b\n", "\n", "\n", "\n", "6->3\n", - "\n", - "\n", - "b\n", + "\n", + "\n", + "b\n", "\n", "\n", "\n", "7\n", - "\n", - "7\n", + "\n", + "7\n", "\n", "\n", "\n", "1->7\n", - "\n", - "\n", - "!a\n", + "\n", + "\n", + "!a\n", "\n", "\n", "\n", "8\n", - "\n", - "8\n", + "\n", + "8\n", "\n", "\n", "\n", "1->8\n", - "\n", - "\n", - "a\n", - "\n", + "\n", + "\n", + "a\n", + "\n", "\n", "\n", "\n", "7->1\n", - "\n", - "\n", - "1\n", + "\n", + "\n", + "1\n", "\n", "\n", "\n", "8->2\n", - "\n", - "\n", - "1\n", - "\n", + "\n", + "\n", + "1\n", + "\n", "\n", "\n", "\n", "2->7\n", - "\n", - "\n", - "!a\n", + "\n", + "\n", + "!a\n", "\n", "\n", "\n", "2->8\n", - "\n", - "\n", - "a\n", - "\n", + "\n", + "\n", + "a\n", + "\n", "\n", "\n", "\n", "9\n", - "\n", - "9\n", + "\n", + "9\n", "\n", "\n", "\n", "3->9\n", - "\n", - "\n", - "1\n", + "\n", + "\n", + "1\n", "\n", "\n", "\n", "9->3\n", - "\n", - "\n", - "b\n", - "\n", + "\n", + "\n", + "b\n", + "\n", "\n", "\n", "\n", "4\n", - "\n", - "4\n", + "\n", + "4\n", "\n", "\n", "\n", "9->4\n", - "\n", - "\n", - "!b\n", + "\n", + "\n", + "!b\n", "\n", "\n", "\n", "10\n", - "\n", - "10\n", + "\n", + "10\n", "\n", "\n", "\n", "4->10\n", - "\n", - "\n", - "1\n", + "\n", + "\n", + "1\n", "\n", "\n", "\n", "10->4\n", - "\n", - "\n", - "1\n", + "\n", + "\n", + "1\n", "\n", "\n", "\n" ], "text/plain": [ - " *' at 0x7f6be431fbd0> >" + " *' at 0x7f8f1861bd20> >" ] }, "execution_count": 8, @@ -1013,218 +1013,218 @@ "\n", "\n", - "\n", "\n", - "\n", - "\n", - "\n", - "Fin(\n", - "\n", - ")\n", - "[co-Büchi]\n", + "\n", + "\n", + "\n", + "Fin(\n", + "\n", + ")\n", + "[co-Büchi]\n", "\n", "\n", "\n", "0\n", - "\n", - "0\n", + "\n", + "0\n", "\n", "\n", "\n", "I->0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "5\n", - "\n", - "5\n", + "\n", + "5\n", "\n", "\n", "\n", "0->5\n", - "\n", - "\n", - "!a\n", + "\n", + "\n", + "!a\n", "\n", "\n", "\n", "6\n", - "\n", - "6\n", + "\n", + "6\n", "\n", "\n", "\n", "0->6\n", - "\n", - "\n", - "a\n", + "\n", + "\n", + "a\n", "\n", "\n", "\n", "1\n", - "\n", - "1\n", + "\n", + "1\n", "\n", "\n", "\n", "5->1\n", - "\n", - "\n", - "!b\n", + "\n", + "\n", + "!b\n", "\n", "\n", "\n", "3\n", - "\n", - "3\n", + "\n", + "3\n", "\n", "\n", "\n", "5->3\n", - "\n", - "\n", - "b\n", + "\n", + "\n", + "b\n", "\n", "\n", "\n", "2\n", - "\n", - "2\n", + "\n", + "2\n", "\n", "\n", "\n", "6->2\n", - "\n", - "\n", - "!b\n", + "\n", + "\n", + "!b\n", "\n", "\n", "\n", "6->3\n", - "\n", - "\n", - "b\n", + "\n", + "\n", + "b\n", "\n", "\n", "\n", "7\n", - "\n", - "7\n", + "\n", + "7\n", "\n", "\n", "\n", "1->7\n", - "\n", - "\n", - "!a\n", + "\n", + "\n", + "!a\n", "\n", "\n", "\n", "8\n", - "\n", - "8\n", + "\n", + "8\n", "\n", "\n", "\n", "1->8\n", - "\n", - "\n", - "a\n", - "\n", + "\n", + "\n", + "a\n", + "\n", "\n", "\n", "\n", "7->1\n", - "\n", - "\n", - "1\n", + "\n", + "\n", + "1\n", "\n", "\n", "\n", "8->2\n", - "\n", - "\n", - "1\n", - "\n", + "\n", + "\n", + "1\n", + "\n", "\n", "\n", "\n", "2->7\n", - "\n", - "\n", - "!a\n", + "\n", + "\n", + "!a\n", "\n", "\n", "\n", "2->8\n", - "\n", - "\n", - "a\n", - "\n", + "\n", + "\n", + "a\n", + "\n", "\n", "\n", "\n", "9\n", - "\n", - "9\n", + "\n", + "9\n", "\n", "\n", "\n", "3->9\n", - "\n", - "\n", - "1\n", + "\n", + "\n", + "1\n", "\n", "\n", "\n", "9->3\n", - "\n", - "\n", - "b\n", - "\n", + "\n", + "\n", + "b\n", + "\n", "\n", "\n", "\n", "4\n", - "\n", - "4\n", + "\n", + "4\n", "\n", "\n", "\n", "9->4\n", - "\n", - "\n", - "!b\n", + "\n", + "\n", + "!b\n", "\n", "\n", "\n", "10\n", - "\n", - "10\n", + "\n", + "10\n", "\n", "\n", "\n", "4->10\n", - "\n", - "\n", - "1\n", + "\n", + "\n", + "1\n", "\n", "\n", "\n", "10->4\n", - "\n", - "\n", - "1\n", + "\n", + "\n", + "1\n", "\n", "\n", "\n" ], "text/plain": [ - " *' at 0x7f6be431fcf0> >" + " *' at 0x7f8f187f5ef0> >" ] }, "execution_count": 11, @@ -1235,11 +1235,405 @@ "source": [ "spot.highlight_strategy(game)" ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "# Input in PGSolver format\n", + "\n", + "The automaton parser is also able to parse the [PGSolver](https://github.com/tcsprojects/pgsolver) format. Here are two examples from the manual of PGSolver. The support for C-style comments is not part of the PGSolver format.\n", + "\n", + "Note that we use diamond node for player 1, while PGSolver use those of player 0. Also in Spot the acceptance condition is what Player 1 should satisfy; player 0 has two way to not satisfy it: leading to a rejecting cycle, or to a state without successor. In PGSolver, the graph is assumed to be total (i.e. each state has a successor), so player 0 can only win by reaching a rejecting cycle, which is equivalent to a `parity max even` acceptance." + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "metadata": {}, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "Fin(\n", + "\n", + ") & (Inf(\n", + "\n", + ") | (Fin(\n", + "\n", + ") & (Inf(\n", + "\n", + ") | (Fin(\n", + "\n", + ") & (Inf(\n", + "\n", + ") | (Fin(\n", + "\n", + ") & (Inf(\n", + "\n", + ") | Fin(\n", + "\n", + "))))))))\n", + "[parity max odd 9]\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "Africa\n", + "\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "4\n", + "\n", + "Antarctica\n", + "\n", + "\n", + "\n", + "\n", + "0->4\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "2\n", + "\n", + "Asia\n", + "\n", + "\n", + "\n", + "\n", + "0->2\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "4->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "2->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "2->4\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "1\n", + "\n", + "America\n", + "\n", + "\n", + "\n", + "\n", + "2->1\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "3\n", + "\n", + "Australia\n", + "\n", + "\n", + "\n", + "\n", + "2->3\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "1->4\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "1->2\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "1->3\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "3->4\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "3->2\n", + "\n", + "\n", + "\n", + "\n", + "\n" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "Inf(\n", + "\n", + ") | Fin(\n", + "\n", + ")\n", + "[Streett 1]\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0\n", + "\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "1\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "\n", + "0->1\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "2\n", + "\n", + "2\n", + "\n", + "\n", + "\n", + "\n", + "0->2\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "1->2\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "3\n", + "\n", + "3\n", + "\n", + "\n", + "\n", + "\n", + "1->3\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "2->3\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "4\n", + "\n", + "4\n", + "\n", + "\n", + "\n", + "\n", + "2->4\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "3->4\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "5\n", + "\n", + "5\n", + "\n", + "\n", + "\n", + "\n", + "3->5\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "4->5\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "6\n", + "\n", + "6\n", + "\n", + "\n", + "\n", + "\n", + "4->6\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "5->6\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "7\n", + "\n", + "7\n", + "\n", + "\n", + "\n", + "\n", + "5->7\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "6->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "6->7\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "7->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "7->1\n", + "\n", + "\n", + "\n", + "\n", + "\n" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "a,b = spot.automata(\"\"\"\n", + "parity 4; /* Example 6 in the manual for PGSolver 4.1 */\n", + "0 6 1 4,2 \"Africa\";\n", + "4 5 1 0 \"Antarctica\";\n", + "1 8 1 2,4,3 \"America\";\n", + "3 6 0 4,2 \"Australia\";\n", + "2 7 0 3,1,0,4 \"Asia\";\n", + "parity 8; /* Example 7 in the manual for PGSolver 4.1 */\n", + "0 0 0 1,2;\n", + "1 1 1 2,3;\n", + "2 0 0 3,4;\n", + "3 1 1 4,5;\n", + "4 0 0 5,6;\n", + "5 1 1 6,7;\n", + "6 0 0 7,0;\n", + "7 1 1 0,1;\n", + "\"\"\")\n", + "spot.solve_game(a)\n", + "spot.highlight_strategy(a)\n", + "spot.solve_game(b)\n", + "spot.highlight_strategy(b)\n", + "display(a.show('.g'), b.show('.g'))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] } ], "metadata": { "kernelspec": { - "display_name": "Python 3", + "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, @@ -1253,7 +1647,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.7.3" + "version": "3.10.5" } }, "nbformat": 4, From b3e994c249d8b9aba80775cb06513748ba7473dd Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Fri, 22 Jul 2022 10:54:15 +0200 Subject: [PATCH 116/337] * spot/twaalgos/hoa.cc: Typo in error message. --- spot/twaalgos/hoa.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spot/twaalgos/hoa.cc b/spot/twaalgos/hoa.cc index 0e03b07f5..e6147afda 100644 --- a/spot/twaalgos/hoa.cc +++ b/spot/twaalgos/hoa.cc @@ -794,7 +794,7 @@ namespace spot os << (v1_1 ? "spot." : "spot-") << "state-player:"; if (player->size() != num_states) throw std::runtime_error("print_hoa(): state-player property has" - " (" + std::to_string(player->size()) + + " " + std::to_string(player->size()) + " states but automaton has " + std::to_string(num_states)); unsigned n = 0; From 8b93b6967dc40af805109c952ef3c2b43c48e127 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Fri, 22 Jul 2022 16:52:03 +0200 Subject: [PATCH 117/337] rename pg_print() as print_pg() and add it to to_str() * NEWS: Mention those change. * spot/twaalgos/game.hh (print_pg): New function. (pg_print): Mark as deprecated. * spot/twaalgos/game.cc (pg_print): Redirect to print_pg(). (print_pg): Update to output state names. * python/spot/__init__.py: Teach to_str() about print_pg(). * bin/ltlsynt.cc: Adjust to call print_pg(). * tests/python/games.ipynb: Add an example. * tests/core/ltlsynt.test: Adjust to remove the "INIT" note. --- NEWS | 11 ++++ bin/ltlsynt.cc | 4 +- python/spot/__init__.py | 8 ++- spot/twaalgos/game.cc | 115 ++++++++++++++++++++------------------- spot/twaalgos/game.hh | 22 +++++++- tests/core/ltlsynt.test | 8 +-- tests/python/games.ipynb | 45 ++++++++++++++- 7 files changed, 145 insertions(+), 68 deletions(-) diff --git a/NEWS b/NEWS index 7e6bd6a40..77c4e081f 100644 --- a/NEWS +++ b/NEWS @@ -117,6 +117,17 @@ New in spot 2.10.6.dev (not yet released) to obtain a simple model checker (that returns true or false, without counterexample). + Python bindings: + + - The to_str() method of automata can now export a parity game into + the PG-Solver format by passing option 'pg'. See + https://spot.lrde.epita.fr/ipynb/games.html for an example. + + Deprectation notice: + + - spot::pg_print() has been deprecated in favor of spot::print_pg() + for consistency with the rest of the API. + Bugs fixed: - calling twa_graph::new_univ_edge(src, begin, end, cond, acc) could diff --git a/bin/ltlsynt.cc b/bin/ltlsynt.cc index bcd9d41d9..06c29db88 100644 --- a/bin/ltlsynt.cc +++ b/bin/ltlsynt.cc @@ -400,7 +400,7 @@ namespace [](const spot::twa_graph_ptr& game)->void { if (opt_print_pg) - spot::pg_print(std::cout, game); + spot::print_pg(std::cout, game); else spot::print_hoa(std::cout, game, opt_print_hoa_args) << '\n'; } @@ -785,7 +785,7 @@ namespace if (opt_print_pg || opt_print_hoa) { if (opt_print_pg) - spot::pg_print(std::cout, arena); + spot::print_pg(std::cout, arena); else spot::print_hoa(std::cout, arena, opt_print_hoa_args) << '\n'; return 0; diff --git a/python/spot/__init__.py b/python/spot/__init__.py index a351e9c54..340eba00a 100644 --- a/python/spot/__init__.py +++ b/python/spot/__init__.py @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2014-2021 Laboratoire de +# Copyright (C) 2014-2022 Laboratoire de # Recherche et Développement de l'Epita (LRDE). # # This file is part of Spot, a model checking library. @@ -261,6 +261,12 @@ class twa: ostr = ostringstream() print_lbtt(ostr, a, opt) return ostr.str() + if format == 'pg': + if opt is not None: + raise ValueError("print_pg() has no option") + ostr = ostringstream() + print_pg(ostr, a) + return ostr.str() raise ValueError("unknown string format: " + format) def save(a, filename, format='hoa', opt=None, append=False): diff --git a/spot/twaalgos/game.cc b/spot/twaalgos/game.cc index 419b33fe3..ccb3b818e 100644 --- a/spot/twaalgos/game.cc +++ b/spot/twaalgos/game.cc @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2017-2018, 2020-2021 Laboratoire de Recherche et +// Copyright (C) 2017-2018, 2020-2022 Laboratoire de Recherche et // Développement de l'Epita (LRDE). // // This file is part of Spot, a model checking library. @@ -23,7 +23,7 @@ #include #include -#include +#include #include namespace spot @@ -834,66 +834,71 @@ namespace spot return solve_parity_game(arena); } + // backward compatibility void pg_print(std::ostream& os, const const_twa_graph_ptr& arena) { - ensure_parity_game(arena, "pg_print"); + print_pg(os, arena); + } - auto do_print = [&os](const const_twa_graph_ptr& arena) - { - const region_t& owner = get_state_players(arena); - - unsigned ns = arena->num_states(); - unsigned init = arena->get_init_state_number(); - os << "parity " << ns - 1 << ";\n"; - std::vector seen(ns, false); - std::vector todo({init}); - while (!todo.empty()) - { - unsigned src = todo.back(); - todo.pop_back(); - if (seen[src]) - continue; - seen[src] = true; - os << src << ' '; - os << arena->out(src).begin()->acc.max_set() - 1 << ' '; - os << owner[src] << ' '; - bool first = true; - for (auto& e: arena->out(src)) - { - if (!first) - os << ','; - first = false; - os << e.dst; - if (!seen[e.dst]) - todo.push_back(e.dst); - } - if (src == init) - os << " \"INIT\""; - os << ";\n"; - } - }; - // Ensure coloring - // PGSolver format expects max odd and colored + std::ostream& print_pg(std::ostream& os, const const_twa_graph_ptr& arena) + { bool is_par, max, odd; is_par = arena->acc().is_parity(max, odd, true); - assert(is_par && "pg_printer needs parity condition"); - bool is_colored = (max & odd) ? std::all_of(arena->edges().begin(), - arena->edges().end(), - [](const auto& e) - { - return (bool) e.acc; - }) - : false; - if (is_colored) - do_print(arena); - else + if (!is_par) + throw std::runtime_error("print_pg: arena must have a parity acceptance"); + const region_t& owner = *ensure_game(arena, "print_pg"); + + bool max_odd_colored = + max && odd && std::all_of(arena->edges().begin(), + arena->edges().end(), + [](const auto& e) + { + return (bool) e.acc; + }); + const_twa_graph_ptr towork = arena; + if (!max_odd_colored) { - auto arena2 = change_parity(arena, parity_kind_max, parity_style_odd); - colorize_parity_here(arena2, true); - set_state_players(arena2, - get_state_players(arena)); - do_print(arena2); + twa_graph_ptr tmp = + change_parity(arena, parity_kind_max, parity_style_odd); + colorize_parity_here(tmp, true); + towork = tmp; } + + auto sn = arena->get_named_prop>("state-names"); + unsigned ns = towork->num_states(); + unsigned init = towork->get_init_state_number(); + os << "parity " << ns - 1 << ";\n"; + std::vector seen(ns, false); + std::vector todo({init}); + while (!todo.empty()) + { + unsigned src = todo.back(); + todo.pop_back(); + if (seen[src]) + continue; + seen[src] = true; + os << src << ' '; + os << towork->out(src).begin()->acc.max_set() - 1 << ' '; + os << owner[src] << ' '; + bool first = true; + for (auto& e: arena->out(src)) + { + if (!first) + os << ','; + first = false; + os << e.dst; + if (!seen[e.dst]) + todo.push_back(e.dst); + } + if (sn && sn->size() > src && !(*sn)[src].empty()) + { + os << " \""; + escape_str(os, (*sn)[src]); + os << '"'; + } + os << ";\n"; + } + return os; } void alternate_players(spot::twa_graph_ptr& arena, diff --git a/spot/twaalgos/game.hh b/spot/twaalgos/game.hh index 64f8d52c8..df5d27439 100644 --- a/spot/twaalgos/game.hh +++ b/spot/twaalgos/game.hh @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2017-2021 Laboratoire de Recherche et Développement +// Copyright (C) 2017-2022 Laboratoire de Recherche et Développement // de l'Epita (LRDE). // // This file is part of Spot, a model checking library. @@ -112,10 +112,26 @@ namespace spot /// \ingroup games - /// \brief Print a max odd parity game using PG-solver syntax + /// \brief Print a parity game using PG-solver syntax + /// + /// The input automaton should have parity acceptance and should + /// define state owner. Since the PG solver format want player 1 to + /// solve a max odd condition, the acceptance condition will be + /// adapted to max odd if necessary. + /// + /// The output will list the initial state as first state (because + /// that is the convention of our parser), and list only reachable + /// states. + /// + /// If states are named, the names will be output as well. + /// @{ + SPOT_API + std::ostream& print_pg(std::ostream& os, const const_twa_graph_ptr& arena); + + SPOT_DEPRECATED("use print_pg() instead") SPOT_API void pg_print(std::ostream& os, const const_twa_graph_ptr& arena); - + /// @} /// \ingroup games /// \brief Highlight the edges of a strategy on an automaton. diff --git a/tests/core/ltlsynt.test b/tests/core/ltlsynt.test index 95e0bf4d7..33369dcde 100644 --- a/tests/core/ltlsynt.test +++ b/tests/core/ltlsynt.test @@ -24,7 +24,7 @@ set -e cat >exp < GFo1)" --outs="o1,o2" --verbose\ --bypass=yes 2> out sed 's/ [0-9.e-]* seconds/ X seconds/g' out > outx -diff outx exp \ No newline at end of file +diff outx exp diff --git a/tests/python/games.ipynb b/tests/python/games.ipynb index fcc2bf12c..a6168b07e 100644 --- a/tests/python/games.ipynb +++ b/tests/python/games.ipynb @@ -897,7 +897,7 @@ "\n" ], "text/plain": [ - " *' at 0x7f8f1861bd20> >" + " *' at 0x7feee9b0ebb0> >" ] }, "execution_count": 8, @@ -1224,7 +1224,7 @@ "\n" ], "text/plain": [ - " *' at 0x7f8f187f5ef0> >" + " *' at 0x7fef001c87b0> >" ] }, "execution_count": 11, @@ -1240,7 +1240,7 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "# Input in PGSolver format\n", + "# Input/Output in PGSolver format\n", "\n", "The automaton parser is also able to parse the [PGSolver](https://github.com/tcsprojects/pgsolver) format. Here are two examples from the manual of PGSolver. The support for C-style comments is not part of the PGSolver format.\n", "\n", @@ -1623,6 +1623,45 @@ "display(a.show('.g'), b.show('.g'))" ] }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "To output a parity-game in PG-solver format, use `to_str('pg')`." + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "parity 4;\n", + "0 6 1 4,2 \"Africa\";\n", + "2 7 0 3,1,0,4 \"Asia\";\n", + "4 5 1 0 \"Antarctica\";\n", + "1 8 1 2,4,3 \"America\";\n", + "3 6 0 4,2 \"Australia\";\n", + "parity 7;\n", + "0 0 0 1,2;\n", + "2 0 0 3,4;\n", + "4 0 0 5,6;\n", + "6 0 0 7,0;\n", + "7 1 1 0,1;\n", + "1 1 1 2,3;\n", + "3 1 1 4,5;\n", + "5 1 1 6,7;\n", + "\n" + ] + } + ], + "source": [ + "print(a.to_str('pg') + b.to_str('pg'))" + ] + }, { "cell_type": "code", "execution_count": null, From b0165cf39c726cb251d58efd1a843179083261e7 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Fri, 5 Aug 2022 18:55:44 +0200 Subject: [PATCH 118/337] * doc/org/tut10.org: Use the same formula in C++ as in Python and sh. --- doc/org/tut10.org | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/doc/org/tut10.org b/doc/org/tut10.org index 419a33197..d4c45708a 100644 --- a/doc/org/tut10.org +++ b/doc/org/tut10.org @@ -139,7 +139,7 @@ automaton. Finally, the output as a never claim is done via the int main() { - spot::parsed_formula pf = spot::parse_infix_psl("[]<>a || <>[]b"); + spot::parsed_formula pf = spot::parse_infix_psl("GFa -> GFb"); if (pf.format_errors(std::cerr)) return 1; spot::translator trans; @@ -158,22 +158,22 @@ never { T0_init: if :: (true) -> goto T0_init - :: (a) -> goto accept_S1 - :: (b) -> goto accept_S2 + :: (b) -> goto accept_S1 + :: (!(a)) -> goto accept_S2 fi; accept_S1: if - :: (a) -> goto accept_S1 - :: (!(a)) -> goto T0_S3 + :: (b) -> goto accept_S1 + :: (!(b)) -> goto T0_S3 fi; accept_S2: if - :: (b) -> goto accept_S2 + :: (!(a)) -> goto accept_S2 fi; T0_S3: if - :: (a) -> goto accept_S1 - :: (!(a)) -> goto T0_S3 + :: (b) -> goto accept_S1 + :: (!(b)) -> goto T0_S3 fi; } From de9041bb31557267d1085bc3a340617eb28bd944 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Sat, 6 Aug 2022 14:06:52 +0200 Subject: [PATCH 119/337] mealy: make output_assignment the default for reduce_mealy * spot/twaalgos/mealy_machine.hh: Here. Also cite the FORTE paper. * doc/spot.bib (renkin.22.forte): New entry. --- doc/spot.bib | 30 +++++++++++++++++++++++------- spot/twaalgos/mealy_machine.hh | 28 +++++++++++++++------------- 2 files changed, 38 insertions(+), 20 deletions(-) diff --git a/doc/spot.bib b/doc/spot.bib index 9d5d6b235..2a58a3031 100644 --- a/doc/spot.bib +++ b/doc/spot.bib @@ -214,13 +214,13 @@ doi = {10.1109/DepCoS-RELCOMEX.2009.31} } -@InProceedings{ cimatti.06.fmcad, - author = {Cimatti, Alessandro and Roveri, Marco and Semprini, Simone and - Tonetta, Stefano}, - title = {From {PSL} to {NBA}: a Modular Symbolic Encoding}, - booktitle = {Proceedings of the 6th conference on Formal Methods in Computer - Aided Design (FMCAD'06)}, - pages = {125--133}, +@InProceedings{ cimatti.06.fmcad, + author = {Cimatti, Alessandro and Roveri, Marco and Semprini, Simone + and Tonetta, Stefano}, + title = {From {PSL} to {NBA}: a Modular Symbolic Encoding}, + booktitle = {Proceedings of the 6th conference on Formal Methods in + Computer Aided Design (FMCAD'06)}, + pages = {125--133}, year = {2006}, publisher = {IEEE Computer Society}, doi = {10.1109/FMCAD.2006.19} @@ -858,6 +858,22 @@ doi = {10.1007/978-3-030-59152-6_7} } +@InProceedings{ renkin.22.forte, + author = {Florian Renkin and Philipp Schlehuber-Caissier and + Alexandre Duret-Lutz and Adrien Pommellet}, + title = {Effective Reductions of {M}ealy Machines}, + year = 2022, + booktitle = {Proceedings of the 42nd International Conference on Formal + Techniques for Distributed Objects, Components, and Systems + (FORTE'22)}, + series = {Lecture Notes in Computer Science}, + volume = 13273, + pages = {170--187}, + month = jun, + publisher = {Springer}, + doi = {10.1007/978-3-031-08679-3_8} +} + @InProceedings{ rozier.07.spin, author = {Kristin Y. Rozier and Moshe Y. Vardi}, title = {LTL Satisfiability Checking}, diff --git a/spot/twaalgos/mealy_machine.hh b/spot/twaalgos/mealy_machine.hh index 7406cb61d..6da0f072a 100644 --- a/spot/twaalgos/mealy_machine.hh +++ b/spot/twaalgos/mealy_machine.hh @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2021 Laboratoire de Recherche et Développement +// Copyright (C) 2021-2022 Laboratoire de Recherche et Développement // de l'Epita (LRDE). // // This file is part of Spot, a model checking library. @@ -87,28 +87,30 @@ namespace spot unsplit_mealy(const const_twa_graph_ptr& m); /// \brief reduce an (in)completely specified mealy machine - /// Based on signature inclusion or equality. This is not guaranteed - /// to find the minimal number of states but is usually faster. - /// This also comes at another drawback: - /// All applicable sequences have to be infinite. Finite - /// traces are disregarded - /// \param mm The mealy machine to be minimized, has to be unsplit + /// + /// This is a bisimulation based reduction, that optionally use + /// inclusion between signatures to force some output when there is + /// a choice in order to favor more reductions. Only infinite + /// traces are considered. See \cite renkin.22.forte for details. + /// + /// \param mm The mealy machine to be minimized, has to be unsplit. /// \param output_assignment Whether or not to use output assignment /// \return A specialization of \c mm. Note that if mm is separated, /// the returned machine is separated as well. - /// \note See todo TACAS22 Effective reductions of mealy machines - /// @{ + /// @{ SPOT_API twa_graph_ptr reduce_mealy(const const_twa_graph_ptr& mm, - bool output_assignment = false); + bool output_assignment = true); SPOT_API void reduce_mealy_here(twa_graph_ptr& mm, - bool output_assignment = false); + bool output_assignment = true); /// @} /// \brief Minimizes an (in)completely specified mealy machine - /// The approach is described in \todo TACAS + /// + /// The approach is described in \cite renkin.22.forte. + /// /// \param premin Use reduce_mealy before applying the /// main algorithm if demanded AND /// the original machine has no finite trace. @@ -159,4 +161,4 @@ namespace spot SPOT_API void simplify_mealy_here(twa_graph_ptr& m, synthesis_info& si, bool split_out); -} \ No newline at end of file +} From faa8fe88734c61bdb55d480f9886808951b99fd0 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Sat, 6 Aug 2022 15:08:11 +0200 Subject: [PATCH 120/337] mealy: cleanup the doxygen documentation * spot/twaalgos/mealy_machine.hh: Create a new "Mealy" section for all these function, and fix some doc strings. --- spot/twaalgos/mealy_machine.hh | 143 +++++++++++++++++++++------------ 1 file changed, 93 insertions(+), 50 deletions(-) diff --git a/spot/twaalgos/mealy_machine.hh b/spot/twaalgos/mealy_machine.hh index 6da0f072a..d603d8000 100644 --- a/spot/twaalgos/mealy_machine.hh +++ b/spot/twaalgos/mealy_machine.hh @@ -21,59 +21,84 @@ #include +/// \addtogroup mealy Functions related to Mealy machines +/// \ingroup twa_algorithms + namespace spot { // Forward decl struct synthesis_info; - /// todo - /// Comment je faire au mieux pour expliquer mealy dans les doc - - /// \brief Checks whether or not the automaton is a mealy machine + /// \ingroup mealy + /// \brief Checks whether the automaton is a mealy machine + /// + /// A mealy machine is an automaton with the named property + /// `"synthesis-outputs"` and and that has a "true" as acceptance + /// condition. /// /// \param m The automaton to be verified - /// \note A mealy machine must have the named property \"synthesis-outputs\" - /// and have a \"true\" as acceptance condition + /// \see is_separated_mealy + /// \see is_split_mealy + /// \see is_input_deterministic_mealy SPOT_API bool is_mealy(const const_twa_graph_ptr& m); - /// \brief Checks whether or not the automaton is a separated mealy machine + /// \ingroup mealy + /// \brief Checks whether the automaton is a separated mealy machine + /// + /// A separated mealy machine is a mealy machine with + /// all transitions having the form `(in)&(out)` where `in` and + /// `out` are BDDs over the input and output propositions. /// /// \param m The automaton to be verified - /// \note A separated mealy machine is a mealy machine machine with all - /// transitions having the form (in)&(out) where in[out] is a bdd over - /// input[output] propositions of m + /// + /// \see is_mealy + /// \see is_split_mealy SPOT_API bool is_separated_mealy(const const_twa_graph_ptr& m); + /// \ingroup mealy /// \brief Checks whether or not the automaton is a split mealy machine /// + /// A split mealy machine is a mealy machine machine that has + /// be converted into a game. It should have the named property + /// `"state-player"`, moreover the game should be alternating + /// between the two players. Transitions leaving states owned by + /// player 0 (the environment) should use only input propositions, + /// while transitions leaving states owned by player 1 (the + /// controller) should use only output propositions. + /// /// \param m The automaton to be verified - /// \note A split mealy machine is a mealy machine machine with the named - /// property \"state-player\". Moreover the underlying automaton - /// must be alternating between the player and the env. Transitions - /// leaving env[player] states can only be labeled by - /// input[output] propositions. + /// \see is_mealy + /// \see is_separated_mealy SPOT_API bool is_split_mealy(const const_twa_graph_ptr& m); - /// \brief Checks whether or not a mealy machine is input deterministic + /// \brief Checks whether a mealy machine is input deterministic + /// + /// A machine is input deterministic if none of the states has two + /// outgoing transitions that can agree on a common assignment of + /// the input propositions. In case the mealy machine is split, the + /// previous condition is tested only on states owned by player 0 + /// (the environment). /// /// \param m The automaton to be verified - /// \note works all mealy machines, no matter whether they are split - /// or separated or neither of neither of them. - /// \note A machine is input deterministic if none of the states - /// has two outgoing transitions that can agree on a assignement - /// of the input propositions. + /// \see is_mealy SPOT_API bool is_input_deterministic_mealy(const const_twa_graph_ptr& m); - /// \brief make each transition in a separated mealy machine a - /// 2-step transition. + /// \ingroup mealy + /// \brief split a separated mealy machine + /// + /// In a separated mealy machine, every transitions as a label of + /// the form `(in)&(out)`. This function will turn each transtion + /// into a pair of consecutive transitions labeled by `in` and + /// `out`, and turn the mealy machine into a game (what we call a + /// split mealy machine) /// /// \param m separated mealy machine to be split - /// \return returns the equivalent split mealy machine if not done inplace + /// \see is_split_mealy /// @{ SPOT_API twa_graph_ptr split_separated_mealy(const const_twa_graph_ptr& m); @@ -82,10 +107,18 @@ namespace spot split_separated_mealy_here(const twa_graph_ptr& m); /// @} + /// \ingroup mealy /// \brief the inverse of split_separated_mealy + /// + /// Take a split mealy machine \a m, and build a separated mealy machine. + /// + /// \see split_separated_mealy + /// \see is_split_mealy + /// \see is_separated_mealy SPOT_API twa_graph_ptr unsplit_mealy(const const_twa_graph_ptr& m); + /// \ingroup mealy /// \brief reduce an (in)completely specified mealy machine /// /// This is a bisimulation based reduction, that optionally use @@ -95,9 +128,11 @@ namespace spot /// /// \param mm The mealy machine to be minimized, has to be unsplit. /// \param output_assignment Whether or not to use output assignment - /// \return A specialization of \c mm. Note that if mm is separated, - /// the returned machine is separated as well. - /// @{ + /// \return A specialization of \c mm. + /// + /// \note If mm is separated, the returned machine is separated as + /// well. + /// @{ SPOT_API twa_graph_ptr reduce_mealy(const const_twa_graph_ptr& mm, bool output_assignment = true); @@ -107,53 +142,60 @@ namespace spot bool output_assignment = true); /// @} + /// \ingroup mealy /// \brief Minimizes an (in)completely specified mealy machine /// /// The approach is described in \cite renkin.22.forte. /// - /// \param premin Use reduce_mealy before applying the - /// main algorithm if demanded AND - /// the original machine has no finite trace. - /// -1 : Do not use; - /// 0 : Use without output assignment; - /// 1 : Use with output assignment - /// \return Returns a split mealy machines which is a minimal - /// speciliazation of the original machine + /// \param premin Whether to use reduce_mealy as a preprocessing: + /// - -1: Do not use; + /// - 0: Use without output assignment; + /// - 1: Use with output assignment. + /// \return A split mealy machines which is a minimal + /// specialization of the original machine. + /// + /// \note Enabling \a premin will remove finite traces. + /// \see is_split_mealy_specialization SPOT_API twa_graph_ptr minimize_mealy(const const_twa_graph_ptr& mm, int premin = -1); + /// \ingroup mealy /// \brief Test if the split mealy machine \a right is a specialization of /// the split mealy machine \a left. /// - /// That is all input sequences valid for left - /// must be applicable for right and the induced sequence of output signals - /// of right must imply the ones of left + /// That is, all input sequences valid for left must be applicable + /// for right and the induced sequence of output signals of right + /// must imply the ones of left SPOT_API bool is_split_mealy_specialization(const_twa_graph_ptr left, const_twa_graph_ptr right, bool verbose = false); + /// \ingroup mealy /// \brief Product between two mealy machines \a left and \a right. /// \pre The machines have to be both either split or unsplit, - /// input complete and compatible. All of this is check by assertion - /// \result The mealy machine representing the shared behaviour. - /// The resulting machine has the same class (mealy/separated/split) - /// as the input machines + /// input complete and compatible. All of this is checked by assertion. + /// \result A mealy machine representing the shared behaviour, + /// with the same tyoe (mealy/separated/split) as the input machines SPOT_API twa_graph_ptr mealy_product(const const_twa_graph_ptr& left, const const_twa_graph_ptr& right); + /// \ingroup mealy /// \brief Convenience function to call minimize_mealy or reduce_mealy. - /// Uses the same convention as ltlsynt for \a minimize_lvl: - /// 0: no reduction - /// 1: bisimulation based reduction - /// 2: bisimulation with output assignment - /// 3: SAT minimization - /// 4: 1 then 3 - /// 5: 2 then 3 + /// Uses the same convention as ltlsynt for \a minimize_lvl (or the + /// field `minimize_lvl` of \a si): + /// - 0: no reduction + /// - 1: bisimulation based reduction + /// - 2: bisimulation with output assignment + /// - 3: SAT minimization + /// - 4: 1 then 3 + /// - 5: 2 then 3 + /// /// Minimizes the given machine \a m inplace, the parameter - /// \a split_out defines whether it is split or not + /// \a split_out specifies if the result should be split. + /// @{ SPOT_API void simplify_mealy_here(twa_graph_ptr& m, int minimize_lvl, bool split_out); @@ -161,4 +203,5 @@ namespace spot SPOT_API void simplify_mealy_here(twa_graph_ptr& m, synthesis_info& si, bool split_out); + /// @} } From a7e87a1fc79332a81a9851f55ecd2479e426202c Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Wed, 10 Aug 2022 10:07:22 +0200 Subject: [PATCH 121/337] Mention the CAV'22 paper * doc/org/citing.org: Here. * doc/org/spot.css: Add support for "begin_note...end_note". --- doc/org/citing.org | 34 ++++++++++++++++++++++++---------- doc/org/spot.css | 2 ++ 2 files changed, 26 insertions(+), 10 deletions(-) diff --git a/doc/org/citing.org b/doc/org/citing.org index 0704eb886..8cc1f52ef 100644 --- a/doc/org/citing.org +++ b/doc/org/citing.org @@ -6,18 +6,22 @@ * Generic reference -If you need to cite the Spot project in some academic paper, please -use the following reference: +If you need to cite the Spot project, the latest tool paper about +it is the following reference: -- *Spot 2.0 — a framework for LTL and ω-automata manipulation*, - /Alexandre Duret-Lutz/, /Alexandre Lewkowicz/, /Amaury Fauchille/, - /Thibaud Michaud/, /Etienne Renault/, and /Laurent Xu/. In Proc. - of ATVA'16, LNCS 9938, pp. 122--129. Chiba, Japan, Oct. 2016. - ([[https://www.lrde.epita.fr/~adl/dl/adl_bib.html#duret.16.atva2][bib]] | [[https://www.lrde.epita.fr/~adl/dl/adl/duret.16.atva2.pdf][pdf]]) +- *From Spot 2.0 to Spot 2.10: What's new?*, /Alexandre Duret-Lutz/, + /Etienne Renault/, /Maximilien Colange/, /Florian Renkin/, + /Alexandre Gbaguidi Aisse/, /Philipp Schlehuber-Caissier/, /Thomas + Medioni/, /Antoine Martin/, /Jérôme Dubois/, /Clément Gillard/, and + Henrich Lauko/. In Proc. of CAV'22, LNCS 13372, pp. 174--187. + Haifa, Israel, Aug. 2022. + ([[https://www.lrde.epita.fr/~adl/dl/adl_bib.html#duret.22.cav][bib]] | [[https://www.lrde.epita.fr/~adl/dl/adl/duret.22.cav.pdf][pdf]]) - This provides a quick overview of the entire project (the features - of the library, [[file:tools.org][the tools]], the Python bindings), and provides many - references detailing more specific aspects. +#+begin_note + Tools evolve while published papers don't. Please always specify + the version of Spot (or any other tool) you are using when citing it + in a paper. Future versions might have different behaviors. +#+end_note * Other, more specific, references @@ -83,6 +87,16 @@ be more specific about a particular aspect of Spot. * Obsolete reference +- *Spot 2.0 — a framework for LTL and ω-automata manipulation*, + /Alexandre Duret-Lutz/, /Alexandre Lewkowicz/, /Amaury Fauchille/, + /Thibaud Michaud/, /Etienne Renault/, and /Laurent Xu/. In Proc. + of ATVA'16, LNCS 9938, pp. 122--129. Chiba, Japan, Oct. 2016. + ([[https://www.lrde.epita.fr/~adl/dl/adl_bib.html#duret.16.atva2][bib]] | [[https://www.lrde.epita.fr/~adl/dl/adl/duret.16.atva2.pdf][pdf]]) + + This provides a quick overview of the entire project (the features + of the library, [[file:tools.org][the tools]], the Python bindings), and provides many + references detailing more specific aspects. + - *Spot: an extensible model checking library using transition-based generalized Büchi automata*, /Alexandre Duret-Lutz/ and /Denis Poitrenaud/. In Proc. of MASCOTS'04, pp. 76--83. Volendam, The diff --git a/doc/org/spot.css b/doc/org/spot.css index 74cbab5bf..7bbd0ef39 100644 --- a/doc/org/spot.css +++ b/doc/org/spot.css @@ -77,6 +77,8 @@ thead tr{background:#ffe35e} .org-hoa-ap-number{color:#d70079} .implem{background:#fff0a6;padding:0.5ex 1ex 0.5ex 1ex;margin:1ex;border-color:#ffe35e;border-style:solid none} .implem::before{background:#ffe35e;content:"Implementation detail";padding:.5ex;position:relative;top:0;left:0;font-weight:bold} +.note{background:#fff0a6;padding:0.5ex 1ex 0.5ex 1ex;margin:1ex;border-color:#ffe35e;border-style:solid none} +.note::before{background:#ffe35e;content:"Note";padding:.5ex;position:relative;top:0;left:0;font-weight:bold} .caveat{background:#ef99c9;padding:0.5ex 1ex 0.5ex 1ex;margin:1ex;border-color:#d70079;border-style:solid none} .caveat::before{background:#d70079;content:"Caveat";padding:.5ex;position:relative;top:0;left:0;font-weight:bold} .spotlogo{transform-origin:50% 50%;animation-duration:2s;animation-name:animspotlogo} From 2848951965ec258c225075f1ad00a3a7a337f0cc Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Wed, 10 Aug 2022 10:26:30 +0200 Subject: [PATCH 122/337] * doc/spot.bib: Add entries for last two tool papers. --- doc/spot.bib | 50 ++++++++++++++++++++++++++++++++++++++++++++++++-- 1 file changed, 48 insertions(+), 2 deletions(-) diff --git a/doc/spot.bib b/doc/spot.bib index 2a58a3031..284bf226a 100644 --- a/doc/spot.bib +++ b/doc/spot.bib @@ -1,4 +1,3 @@ - @InProceedings{ babiak.12.tacas, author = {Tom{\'a}{\v{s}} Babiak and Mojm{\'i}r K{\v{r}}et{\'i}nsk{\'y} and Vojt{\v{e}}ch {\v{R}}eh{\'a}k @@ -353,6 +352,41 @@ doi = {10.1504/IJCCBS.2014.059594} } +@InProceedings{ duret.16.atva, + author = {Alexandre Duret-Lutz and Fabrice Kordon and Denis + Poitrenaud and Etienne Renault}, + title = {Heuristics for Checking Liveness Properties with Partial + Order Reductions}, + booktitle = {Proceedings of the 14th International Symposium on + Automated Technology for Verification and Analysis + (ATVA'16)}, + series = {Lecture Notes in Computer Science}, + publisher = {Springer}, + volume = {9938}, + pages = {340--356}, + year = {2016}, + month = oct, + doi = {10.1007/978-3-319-46520-3_22} +} + +@InProceedings{ duret.22.cav, + author = {Alexandre~Duret-Lutz and Etienne Renault and Maximilien + Colange and Florian Renkin and Alexandre Gbaguidi~Aisse and + Philipp Schlehuber-Caissier and Thomas Medioni and Antoine + Martin and J{\'e}r{\^o}me Dubois and Cl{\'e}ment Gillard + and Henrich Lauko}, + title = {From {S}pot 2.0 to {S}pot 2.10: What's New?}, + booktitle = {Proceedings of the 34th International Conference on + Computer Aided Verification (CAV'22)}, + year = 2022, + volume = {13372}, + series = {Lecture Notes in Computer Science}, + pages = {174--187}, + month = aug, + publisher = {Springer}, + doi = {10.1007/978-3-031-13188-2_9} +} + @InProceedings{ dwyer.98.fmsp, author = {Matthew B. Dwyer and George S. Avrunin and James C. Corbett}, @@ -1036,7 +1070,19 @@ publisher = {Elsevier}, editor = {Rance Cleaveland and Hubert Garavel}, year = {2002}, - month = jul, + month = jul, pdf = {adl/duret.16.atva.pdf}, + abstract = {Checking liveness properties with partial-order reductions + requires a cycle proviso to ensure that an action cannot be + postponed forever. The proviso forces each cycle to contain + at least one fully expanded state. We present new + heuristics to select which state to expand, hoping to + reduce the size of the resulting graph. The choice of the + state to expand is done when encountering a + \emph{dangerous} edge. Almost all existing provisos expand + the source of this edge, while this paper also explores the + expansion of the destination and the use of SCC-based + information.}, + address = {M{\'a}laga, Spain}, doi = {10.1016/S1571-0661(04)80409-2} } From d1b8495510c354ff635b171af3e9ecc8078bd6fc Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Fri, 12 Aug 2022 14:56:45 +0200 Subject: [PATCH 123/337] do not use a global variable to define the number of available threads * python/spot/impl.i: Make parallel_policy implicitly contractible. * spot/graph/graph.hh (sort_edges_srcfirst_): Pass a parallel_policy explicitly. * spot/twa/twagraph.hh, spot/twa/twagraph.cc (merge_states): Likewise. * spot/misc/common.cc: Remove file. * spot/misc/common.hh (set_nthreads, get_nthreads): Remove, and replace with... (parallel_policy): ... this. * spot/misc/Makefile.am, tests/python/mergedge.py, NEWS: Adjust. --- NEWS | 8 ++-- python/spot/impl.i | 1 + spot/graph/graph.hh | 47 +++++++++++----------- spot/misc/Makefile.am | 3 +- spot/misc/common.cc | 33 --------------- spot/misc/common.hh | 31 ++++++++++----- spot/twa/twagraph.cc | 86 ++++++++++++++++++++-------------------- spot/twa/twagraph.hh | 8 +++- tests/python/mergedge.py | 74 +++++++++++++++++----------------- 9 files changed, 137 insertions(+), 154 deletions(-) delete mode 100644 spot/misc/common.cc diff --git a/NEWS b/NEWS index 77c4e081f..5b55ebbdd 100644 --- a/NEWS +++ b/NEWS @@ -38,9 +38,6 @@ New in spot 2.10.6.dev (not yet released) Library: - - A global variable, together with its setters and getters to define the - maximal number of threads is added to common.hh/common.cc - - The new function suffix_operator_normal_form() implements transformation of formulas to Suffix Operator Normal Form, described in [cimatti.06.fmcad]. @@ -117,6 +114,11 @@ New in spot 2.10.6.dev (not yet released) to obtain a simple model checker (that returns true or false, without counterexample). + - spot::parallel_policy is an object that can be passed to some + algorithm to specify how many threads can be used if Spot has been + compiled with --enable-pthread. Currently, only + twa_graph::merge_states() support it. + Python bindings: - The to_str() method of automata can now export a parity game into diff --git a/python/spot/impl.i b/python/spot/impl.i index a07709005..23c07c4e8 100644 --- a/python/spot/impl.i +++ b/python/spot/impl.i @@ -486,6 +486,7 @@ static void handle_any_exception() } } +%implicitconv spot::parallel_policy; %include %include %include diff --git a/spot/graph/graph.hh b/spot/graph/graph.hh index 06ddf0997..531426244 100644 --- a/spot/graph/graph.hh +++ b/spot/graph/graph.hh @@ -1255,56 +1255,57 @@ namespace spot /// and make a temporary copy of the edges (needs more ram) /// \pre This needs the edge_vector to be in a coherent state when called template> - void sort_edges_srcfirst_(Predicate p = Predicate()) + void sort_edges_srcfirst_(Predicate p = Predicate(), + parallel_policy ppolicy = parallel_policy()) { - //std::cerr << "\nbefore\n"; - //dump_storage(std::cerr); - const auto N = num_states(); - - auto idx_list = std::vector(N+1); - auto new_edges = edge_vector_t(); + SPOT_ASSERT(!edges_.empty()); + const unsigned ns = num_states(); + std::vector idx_list(ns+1); + edge_vector_t new_edges; new_edges.reserve(edges_.size()); - if (SPOT_UNLIKELY(edges_.empty())) - throw std::runtime_error("Empty edge vector!"); new_edges.resize(1); // This causes edge 0 to be considered as dead. new_edges[0].next_succ = 0; - // Copy the edges such that they are sorted by src - for (auto s = 0u; s < N; ++s) + // Copy all edges so that they are sorted by src + for (unsigned s = 0; s < ns; ++s) { idx_list[s] = new_edges.size(); for (const auto& e : out(s)) new_edges.push_back(e); } - idx_list[N] = new_edges.size(); + idx_list[ns] = new_edges.size(); // New edge sorted by source // If we have few edge or only one threads // Benchmark few? auto bne = new_edges.begin(); -#ifdef SPOT_ENABLE_PTHREAD - const unsigned nthreads = get_nthreads(); - if (nthreads == 1 || edges_.size() < 1000) +#ifndef SPOT_ENABLE_PTHREAD + (void) ppolicy; +#else + unsigned nthreads = ppolicy.nthreads(); + if (nthreads <= 1) #endif { - for (auto s = 0u; s < N; ++s) + for (unsigned s = 0u; s < ns; ++s) std::stable_sort(bne + idx_list[s], - bne + idx_list[s+1], - p); + bne + idx_list[s+1], p); } #ifdef SPOT_ENABLE_PTHREAD else { - static auto tv = std::vector(); + static std::vector tv; SPOT_ASSERT(tv.empty()); tv.resize(nthreads); + // FIXME: Due to the way these thread advence into the sate + // vectors, they access very close memory location. It + // would seems more cache friendly to have thread work on + // blocks of continuous states. for (unsigned id = 0; id < nthreads; ++id) tv[id] = std::thread( - [bne, id, N, &idx_list, p, nthreads]() + [bne, id, ns, &idx_list, p, nthreads]() { - for (auto s = id; s < N; s+=nthreads) + for (unsigned s = id; s < ns; s += nthreads) std::stable_sort(bne + idx_list[s], - bne + idx_list[s+1], - p); + bne + idx_list[s+1], p); return; }); for (auto& t : tv) diff --git a/spot/misc/Makefile.am b/spot/misc/Makefile.am index 623a13c87..6b771dbb5 100644 --- a/spot/misc/Makefile.am +++ b/spot/misc/Makefile.am @@ -1,5 +1,5 @@ ## -*- coding: utf-8 -*- -## Copyright (C) 2011-2014, 2016-2018, 2020-2021 Laboratoire de +## Copyright (C) 2011-2014, 2016-2018, 2020-2022 Laboratoire de ## Recherche et Développement de l'Epita (LRDE). ## Copyright (C) 2003, 2004, 2005, 2006 Laboratoire d'Informatique de ## Paris 6 (LIP6), département Systèmes Répartis Coopératifs (SRC), @@ -63,7 +63,6 @@ libmisc_la_SOURCES = \ bareword.cc \ bitset.cc \ bitvect.cc \ - common.cc \ escape.cc \ formater.cc \ intvcomp.cc \ diff --git a/spot/misc/common.cc b/spot/misc/common.cc deleted file mode 100644 index adf9f2da0..000000000 --- a/spot/misc/common.cc +++ /dev/null @@ -1,33 +0,0 @@ -// -*- coding: utf-8 -*- -// Copyright (C) 2018 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 . - -#include "config.h" -#include - -static unsigned N_MAX_THREADS = 1; - -void set_nthreads(unsigned nthreads) -{ - N_MAX_THREADS = nthreads; -} - -unsigned get_nthreads() -{ - return N_MAX_THREADS; -} \ No newline at end of file diff --git a/spot/misc/common.hh b/spot/misc/common.hh index fc74a8ee7..8b066b0a5 100644 --- a/spot/misc/common.hh +++ b/spot/misc/common.hh @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2013-2021 Laboratoire de Recherche et Développement +// Copyright (C) 2013-2022 Laboratoire de Recherche et Développement // de l'Epita (LRDE). // // This file is part of Spot, a model checking library. @@ -145,6 +145,27 @@ namespace spot { } }; + + /// \brief This class is used to tell parallel algorithms what + /// resources they may use. + /// + /// Currently, this simply stores an integer indicating the number + /// of threads that the algorithm may create, but in the future it + /// will probably do more. + class SPOT_API parallel_policy + { + unsigned nthreads_; + public: + parallel_policy(unsigned nthreads = 1) : nthreads_(nthreads) + { + } + + unsigned nthreads() const + { + return nthreads_; + } + }; + } // This is a workaround for the issue described in GNU GCC bug 89303. @@ -169,11 +190,3 @@ namespace spot # define SPOT_make_shared_enabled__(TYPE, ...) \ std::make_shared(__VA_ARGS__) #endif - - -// Global variable to determine the maximal number of threads -SPOT_API void -set_nthreads(unsigned nthreads); - -SPOT_API unsigned -get_nthreads(); diff --git a/spot/twa/twagraph.cc b/spot/twa/twagraph.cc index 5b4da10a3..64f229b03 100644 --- a/spot/twa/twagraph.cc +++ b/spot/twa/twagraph.cc @@ -366,15 +366,16 @@ namespace spot g_.chain_edges_(); } - unsigned twa_graph::merge_states() + unsigned twa_graph::merge_states(parallel_policy ppolicy) { if (!is_existential()) throw std::runtime_error( "twa_graph::merge_states() does not work on alternating automata"); #ifdef ENABLE_PTHREAD - const unsigned nthreads = get_nthreads(); + const unsigned nthreads = ppolicy.nthreads(); #else + (void) ppolicy; constexpr unsigned nthreads = 1; #endif @@ -392,10 +393,10 @@ namespace spot if (rhs.cond != lhs.cond) return false; return lhs.dst < rhs.dst; - }); + }, nthreads); g_.chain_edges_(); - const auto n_states = num_states(); + const unsigned n_states = num_states(); // Edges are nicely chained and there are no erased edges // -> We can work with the edge_vector @@ -404,30 +405,28 @@ namespace spot // if so, the graph alternates between env and player vertices, // so there are, by definition, no self-loops auto sp = get_named_prop>("state-player"); - const auto spe = (bool) sp; // The hashing is a bit delicat: We may only use the dst // if it has no self-loop - auto use_for_hash = spe ? std::vector() - : std::vector(n_states); + auto use_for_hash = sp ? std::vector() + : std::vector(n_states); const auto& e_vec = edge_vector(); - const auto n_edges = e_vec.size(); + unsigned n_edges = e_vec.size(); // For each state we need 4 indices of the edge vector // [first, first_non_sfirst_selflooplfloop, first_selfloop, end] // The init value makes sure nothing is done for dead end states - auto e_idx = - std::vector>(n_states, {-1u, -1u, - -1u, -1u}); + std::vector> e_idx(n_states, {-1u, -1u, + -1u, -1u}); // Like a linked list holding the non-selfloop and selfloop transitions - auto e_chain = std::vector(e_vec.size(), -1u); + std::vector e_chain(n_edges, -1u); unsigned idx = 1; // Edges are sorted with repected to src first const unsigned n_high = e_vec.back().src; - if (spe) + if (sp) for (auto s = 0u; s < n_high; ++s) treat(e_idx, e_vec, e_chain, use_for_hash, idx, s, n_edges); @@ -436,7 +435,7 @@ namespace spot treat(e_idx, e_vec, e_chain, use_for_hash, idx, s, n_edges); // Last one - if (spe) + if (sp) treat(e_idx, e_vec, e_chain, use_for_hash, idx, n_high, n_edges); else @@ -445,7 +444,7 @@ namespace spot assert(idx == e_vec.size() && "Something went wrong during indexing"); - auto n_players = 0u; + unsigned n_players = 0u; if (sp) n_players = std::accumulate(sp->begin(), sp->end(), 0u); @@ -454,14 +453,12 @@ namespace spot // hash_linked_list is like a linked list structure // of fake pointers - auto hash_linked_list = std::vector(n_states, -1u); - auto s_to_hash = std::vector(n_states, 0); - auto env_map = - robin_hood::unordered_flat_map>(); - auto player_map = - robin_hood::unordered_flat_map>(); + std::vector hash_linked_list(n_states, -1u); + std::vector s_to_hash(n_states, 0); + robin_hood::unordered_flat_map> env_map; + robin_hood::unordered_flat_map> player_map; env_map.reserve(n_states - n_players); player_map.reserve(n_players); @@ -476,7 +473,7 @@ namespace spot else { // "tail" - auto idx = it->second.second; + unsigned idx = it->second.second; assert(idx < s && "Must be monotone"); hash_linked_list[idx] = s; it->second.second = s; @@ -484,19 +481,19 @@ namespace spot }; // Hash all states - constexpr auto SHIFT = sizeof(size_t)/2 * CHAR_BIT; + constexpr unsigned shift = sizeof(size_t)/2 * CHAR_BIT; for (auto s = 0u; s != n_states; ++s) { - auto h = fnv::init; - const auto e = e_idx[s][3]; - for (auto i = e_idx[s][0]; i != e; ++i) + size_t h = fnv::init; + const unsigned e = e_idx[s][3]; + for (unsigned i = e_idx[s][0]; i != e; ++i) { // If size_t has 8byte and unsigned has 4byte // then this works fine, otherwise there might be more collisions - size_t hh = spe || use_for_hash[e_vec[i].dst] + size_t hh = sp || use_for_hash[e_vec[i].dst] ? e_vec[i].dst : fnv::init; - hh <<= SHIFT; + hh <<= shift; hh += e_vec[i].cond.id(); h ^= hh; h *= fnv::prime; @@ -504,7 +501,7 @@ namespace spot h *= fnv::prime; } s_to_hash[s] = h; - if (spe && (*sp)[s]) + if (sp && (*sp)[s]) emplace(player_map, h, s); else emplace(env_map, h, s); @@ -538,20 +535,20 @@ namespace spot auto [i1, nsl1, sl1, e1] = e_idx[s1]; auto [i2, nsl2, sl2, e2] = e_idx[s2]; - if ((e2-i2) != (e1-i1)) + if ((e2 - i2) != (e1 - i1)) return false; // Different number of outgoing trans // checked1/2 is one element larger than necessary // the last element is always false // and acts like a nulltermination - checked1.resize(e1-i1+1); + checked1.resize(e1 - i1 + 1); std::fill(checked1.begin(), checked1.end(), false); - checked2.resize(e2-i2+1); + checked2.resize(e2 - i2 + 1); std::fill(checked2.begin(), checked2.end(), false); // Try to match self-loops // Not entirely sure when this helps exactly - while ((sl1 < e1) & (sl2 < e2)) + while ((sl1 < e1) && (sl2 < e2)) { // Like a search in ordered array if (e_vec[sl1].data() == e_vec[sl2].data()) @@ -576,12 +573,12 @@ namespace spot // Check if all have been correctly treated if ((nsl1 > e1) && std::all_of(checked1.begin(), checked1.end(), - [](const auto& e){return e; })) + [](const auto& e){return e;})) return true; // The remaining edges need to match exactly - auto idx1 = i1; - auto idx2 = i2; + unsigned idx1 = i1; + unsigned idx2 = i2; while (((idx1 < e1) & (idx2 < e2))) { // More efficient version? @@ -600,7 +597,7 @@ namespace spot if ((e_vec[idx1].dst != e_vec[idx2].dst) - || !(e_vec[idx1].data() == e_vec[idx2].data())) + || !(e_vec[idx1].data() == e_vec[idx2].data())) return false; // Advance @@ -620,7 +617,7 @@ namespace spot std::vector& checked2) { v.clear(); - for (auto i = ix; i != -1U; i = hash_linked_list[i]) + for (unsigned i = ix; i != -1U; i = hash_linked_list[i]) v.push_back(i); const unsigned N = v.size(); @@ -699,8 +696,10 @@ namespace spot auto bege = env_map.begin(); auto ende = env_map.end(); -#ifdef ENABLE_PTHREAD - if ((nthreads == 1) & (num_states() > 1000)) // Bound? +#ifndef ENABLE_PTHREAD + (void) nthreads; +#else + if (nthreads <= 1) { #endif // ENABLE_PTHREAD worker(0, begp, endp, bege, ende); @@ -728,12 +727,11 @@ namespace spot for (auto& e: edges()) if (remap[e.dst] != -1U) { - assert((!spe || (sp->at(e.dst) == sp->at(remap[e.dst]))) + assert((!sp || (sp->at(e.dst) == sp->at(remap[e.dst]))) && "States do not have the same owner"); e.dst = remap[e.dst]; } - if (remap[get_init_state_number()] != -1U) set_init_state(remap[get_init_state_number()]); diff --git a/spot/twa/twagraph.hh b/spot/twa/twagraph.hh index c6222871e..742a4d69a 100644 --- a/spot/twa/twagraph.hh +++ b/spot/twa/twagraph.hh @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2014-2021 Laboratoire de Recherche et Développement +// Copyright (C) 2014-2022 Laboratoire de Recherche et Développement // de l'Epita. // // This file is part of Spot, a model checking library. @@ -589,8 +589,12 @@ namespace spot /// (1)-a->(1) and (1)-a->(1) if (1), (2) and (3) are merged into /// (1). /// + /// On large automaton, it might be worthwhile to use multiple + /// threads to find states that can be merged. This can be + /// requested with the \a ppolicy argument. + /// /// \return the number of states that have been merged and removed. - unsigned merge_states(); + unsigned merge_states(parallel_policy ppolicy = parallel_policy()); /// \brief Like merge states, but one can chose which states are /// candidates for merging. diff --git a/tests/python/mergedge.py b/tests/python/mergedge.py index 2be4d4984..b3e934946 100644 --- a/tests/python/mergedge.py +++ b/tests/python/mergedge.py @@ -23,42 +23,40 @@ import spot from unittest import TestCase tc = TestCase() +aut = spot.automaton("""HOA: v1 States: 1 Start: 0 AP: 1 "a" +Acceptance: 1 Inf(0) --BODY-- State: 0 [0] 0 [0] 0 {0} --END--""") +tc.assertEqual(aut.num_edges(), 2) +aut.merge_edges() +tc.assertEqual(aut.num_edges(), 1) + +aut = spot.automaton(""" +HOA: v1 +States: 2 +Start: 0 +AP: 2 "p0" "p1" +acc-name: Buchi +Acceptance: 1 Inf(0) +properties: trans-labels explicit-labels trans-acc complete +--BODY-- +State: 0 +[!0] 0 {0} +[0] 1 {0} +State: 1 +[!0&!1] 0 {0} +[0 | 1] 1 +[0&!1] 1 {0} +--END--""") +tc.assertEqual(aut.num_edges(), 5) +aut.merge_edges() +tc.assertEqual(aut.num_edges(), 5) +tc.assertFalse(spot.is_deterministic(aut)) +aut = spot.split_edges(aut) +tc.assertEqual(aut.num_edges(), 9) +aut.merge_edges() +tc.assertEqual(aut.num_edges(), 5) +tc.assertTrue(spot.is_deterministic(aut)) + for nthread in range(1, 16, 2): - spot.set_nthreads(nthread) - tc.assertEqual(spot.get_nthreads(), nthread) - aut = spot.automaton("""HOA: v1 States: 1 Start: 0 AP: 1 "a" - Acceptance: 1 Inf(0) --BODY-- State: 0 [0] 0 [0] 0 {0} --END--""") - tc.assertEqual(aut.num_edges(), 2) - aut.merge_edges() - tc.assertEqual(aut.num_edges(), 1) - - aut = spot.automaton(""" - HOA: v1 - States: 2 - Start: 0 - AP: 2 "p0" "p1" - acc-name: Buchi - Acceptance: 1 Inf(0) - properties: trans-labels explicit-labels trans-acc complete - --BODY-- - State: 0 - [!0] 0 {0} - [0] 1 {0} - State: 1 - [!0&!1] 0 {0} - [0 | 1] 1 - [0&!1] 1 {0} - --END--""") - tc.assertEqual(aut.num_edges(), 5) - aut.merge_edges() - tc.assertEqual(aut.num_edges(), 5) - tc.assertFalse(spot.is_deterministic(aut)) - aut = spot.split_edges(aut) - tc.assertEqual(aut.num_edges(), 9) - aut.merge_edges() - tc.assertEqual(aut.num_edges(), 5) - tc.assertTrue(spot.is_deterministic(aut)) - aut = spot.automaton(""" HOA: v1 States: 3 @@ -78,12 +76,12 @@ for nthread in range(1, 16, 2): [!0] 2 {0} [0] 1 --END--""") - aut.merge_states() + aut.merge_states(nthread) tc.assertEqual(aut.num_edges(), 4) tc.assertEqual(aut.num_states(), 2) tc.assertTrue(spot.is_deterministic(aut)) tc.assertTrue(aut.prop_complete()) - aut.merge_states() + aut.merge_states(nthread) tc.assertEqual(aut.num_edges(), 4) tc.assertEqual(aut.num_states(), 2) tc.assertTrue(spot.is_deterministic(aut)) @@ -168,6 +166,6 @@ for nthread in range(1, 16, 2): State: 40 [0&1&!2] 7 {3} [0&1&2] 8 {3} [0&!1&!2] 11 {1} [0&!1&2] 12 {1} [!0&1&!2] 31 {1} [!0&1&2] 32 {1} [!0&!1&2] 34 {1} [!0&!1&!2] 40 {1} --END--""") - aa.merge_states() + aa.merge_states(nthread) # This used to cause a segfault reported by Philipp. print(aa.to_str()) From cd21521bfe3bae5d8dfd65c6df2bb4bec61ff80b Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Fri, 12 Aug 2022 17:24:02 +0200 Subject: [PATCH 124/337] * spot/twa/twagraph.cc (merge_states): Some cleanup and simplifications. --- spot/twa/twagraph.cc | 194 +++++++++++++++++++++---------------------- 1 file changed, 95 insertions(+), 99 deletions(-) diff --git a/spot/twa/twagraph.cc b/spot/twa/twagraph.cc index 64f229b03..4d0009e93 100644 --- a/spot/twa/twagraph.cc +++ b/spot/twa/twagraph.cc @@ -36,11 +36,11 @@ namespace using namespace spot; // If LAST is false, // it is guaranteed that there will be another src state - template + template void treat(std::vector>& e_idx, const twa_graph::graph_t::edge_vector_t& e_vec, std::vector& e_chain, - std::vector& use_for_hash, + std::vector& hash_of_state, unsigned& idx, unsigned s, unsigned n_e) @@ -98,10 +98,10 @@ namespace } s_idx[3] = idx; - // Check if self-loops appeared - // If so -> do not use for hash - if constexpr (!SPE) - use_for_hash[s] = s_idx[2] == -1u; + // Check if self-loops appeared. We cannot hash + // states with self-loops. + if (s_idx[2] != -1u) + hash_of_state[s] = fnv::init; } } @@ -401,15 +401,19 @@ namespace spot // Edges are nicely chained and there are no erased edges // -> We can work with the edge_vector - // Check if it is a game <-> "state-player" is defined - // if so, the graph alternates between env and player vertices, - // so there are, by definition, no self-loops + // Check if it is a game <-> "state-player" is defined. If + // so, we can only merge states that belong to the same player. + // (We will use two hash maps in this case.) auto sp = get_named_prop>("state-player"); - // The hashing is a bit delicat: We may only use the dst - // if it has no self-loop - auto use_for_hash = sp ? std::vector() - : std::vector(n_states); + // The hashing is a bit delicat: We may only use the dst if it has + // no self-loop. HASH_OF_STATE stores the hash associated to each + // state (by default its own number) or some common value if the + // state contains self-loop. + std::vector hash_of_state; + hash_of_state.reserve(n_states); + for (unsigned i = 0; i < n_states; ++i) + hash_of_state.push_back(i); const auto& e_vec = edge_vector(); unsigned n_edges = e_vec.size(); @@ -424,29 +428,20 @@ namespace spot unsigned idx = 1; - // Edges are sorted with repected to src first + // Edges are sorted with respect to src first const unsigned n_high = e_vec.back().src; - if (sp) - for (auto s = 0u; s < n_high; ++s) - treat(e_idx, e_vec, e_chain, - use_for_hash, idx, s, n_edges); - else - for (auto s = 0u; s < n_high; ++s) - treat(e_idx, e_vec, e_chain, - use_for_hash, idx, s, n_edges); + for (auto s = 0u; s < n_high; ++s) + treat(e_idx, e_vec, e_chain, + hash_of_state, idx, s, n_edges); // Last one - if (sp) - treat(e_idx, e_vec, e_chain, - use_for_hash, idx, n_high, n_edges); - else - treat(e_idx, e_vec, e_chain, - use_for_hash, idx, n_high, n_edges); + treat(e_idx, e_vec, e_chain, + hash_of_state, idx, n_high, n_edges); assert(idx == e_vec.size() && "Something went wrong during indexing"); - unsigned n_players = 0u; + unsigned n_player1 = 0u; if (sp) - n_players = std::accumulate(sp->begin(), sp->end(), 0u); + n_player1 = std::accumulate(sp->begin(), sp->end(), 0u); // Represents which states share a hash // Head is in the unordered_map, @@ -454,26 +449,28 @@ namespace spot // of fake pointers std::vector hash_linked_list(n_states, -1u); - std::vector s_to_hash(n_states, 0); - robin_hood::unordered_flat_map> env_map; - robin_hood::unordered_flat_map> player_map; - env_map.reserve(n_states - n_players); - player_map.reserve(n_players); + typedef robin_hood::unordered_flat_map> player_map; + // If the automaton is not a game, everything is assumed to be + // owned by player 0. + player_map map0; // for player 0 + player_map map1; // for player 1 + + map0.reserve(n_states - n_player1); + map1.reserve(n_player1); // Sadly we need to loop the edges twice since we have // to check for self-loops before hashing auto emplace = [&hash_linked_list](auto& m, auto h, auto s) { - auto it = m.find(h); - if (it == m.end()) - m.emplace(h, std::make_pair(s, s)); - else + auto [it, inserted] = m.try_emplace(h, std::make_pair(s, s)); + if (!inserted) { - // "tail" - unsigned idx = it->second.second; + // We already have an entry with hash "h". Link it + // to the new state. + unsigned idx = it->second.second; // tail of the list assert(idx < s && "Must be monotone"); hash_linked_list[idx] = s; it->second.second = s; @@ -490,9 +487,7 @@ namespace spot { // If size_t has 8byte and unsigned has 4byte // then this works fine, otherwise there might be more collisions - size_t hh = sp || use_for_hash[e_vec[i].dst] - ? e_vec[i].dst - : fnv::init; + size_t hh = hash_of_state[e_vec[i].dst]; hh <<= shift; hh += e_vec[i].cond.id(); h ^= hh; @@ -500,16 +495,15 @@ namespace spot h ^= e_vec[i].acc.hash(); h *= fnv::prime; } - s_to_hash[s] = h; if (sp && (*sp)[s]) - emplace(player_map, h, s); + emplace(map1, h, s); else - emplace(env_map, h, s); + emplace(map0, h, s); } // All states that might possible be merged share the same hash // Info hash coll //std::cout << "Hash collission rate pre merge: " - // << ((env_map.size()+player_map.size())/((float)n_states)) + // << ((map0.size()+map1.size())/((float)n_states)) // << '\n'; @@ -535,51 +529,50 @@ namespace spot auto [i1, nsl1, sl1, e1] = e_idx[s1]; auto [i2, nsl2, sl2, e2] = e_idx[s2]; - if ((e2 - i2) != (e1 - i1)) + unsigned n_trans = e1 - i1; + if ((e2 - i2) != n_trans) return false; // Different number of outgoing trans - // checked1/2 is one element larger than necessary - // the last element is always false - // and acts like a nulltermination - checked1.resize(e1 - i1 + 1); - std::fill(checked1.begin(), checked1.end(), false); - checked2.resize(e2 - i2 + 1); - std::fill(checked2.begin(), checked2.end(), false); + // checked1/2 is one element larger than necessary; + // the last element (false) serves as a sentinel. + checked1.clear(); + checked1.resize(n_trans + 1, false); + checked2.clear(); + checked2.resize(n_trans + 1, false); // Try to match self-loops - // Not entirely sure when this helps exactly + unsigned self_loops_matched = 0; while ((sl1 < e1) && (sl2 < e2)) { - // Like a search in ordered array - if (e_vec[sl1].data() == e_vec[sl2].data()) + auto& data1 = e_vec[sl1].data(); + auto& data2 = e_vec[sl2].data(); + if (data1 == data2) { // Matched + ++self_loops_matched; checked1[sl1 - i1] = true; //never touches last element checked2[sl2 - i2] = true; // Advance both sl1 = e_chain[sl1]; sl2 = e_chain[sl2]; } - else if (edge_data_comp(e_vec[sl1].data(), - e_vec[sl2].data())) - // sl1 needs to advance + // Since edges are ordered on each side, aadvance + // the smallest side in case there is no match. + else if (edge_data_comp(data1, data2)) sl1 = e_chain[sl1]; else - // sl2 needs to advance sl2 = e_chain[sl2]; } - // If there are no non-self-loops, in s1 - // Check if all have been correctly treated - if ((nsl1 > e1) - && std::all_of(checked1.begin(), checked1.end(), - [](const auto& e){return e;})) + // If the matched self-loops cover all transitions, we can + // stop here. + if (self_loops_matched == n_trans) return true; // The remaining edges need to match exactly unsigned idx1 = i1; unsigned idx2 = i2; - while (((idx1 < e1) & (idx2 < e2))) + while (((idx1 < e1) && (idx2 < e2))) { // More efficient version? // Skip checked edges @@ -595,7 +588,6 @@ namespace spot break; } - if ((e_vec[idx1].dst != e_vec[idx2].dst) || !(e_vec[idx1].data() == e_vec[idx2].data())) return false; @@ -611,32 +603,35 @@ namespace spot const unsigned nb_states = num_states(); std::vector remap(nb_states, -1U); - // Check each hash + // Check all pair of states with compatible hash auto check_ix = [&](unsigned ix, std::vector& v, std::vector& checked1, std::vector& checked2) { + if (hash_linked_list[ix] == -1U) // no compatible state + return; + v.clear(); for (unsigned i = ix; i != -1U; i = hash_linked_list[i]) v.push_back(i); - const unsigned N = v.size(); + const unsigned vs = v.size(); - for (unsigned idx = 0; idx < N; ++idx) + for (unsigned idx = 0; idx < vs; ++idx) { - auto i = v[idx]; + unsigned i = v[idx]; for (unsigned jdx = 0; jdx < idx; ++jdx) { - auto j = v[jdx]; + unsigned j = v[jdx]; if (state_equal(j, i, checked1, checked2)) { remap[i] = (remap[j] != -1U) ? remap[j] : j; - // Because of the special self-loop tests we use above, - // it's possible that i can be mapped to remap[j] even - // if j was last compatible states found. Consider the - // following cases, taken from an actual test case: - // 18 is equal to 5, 35 is equal to 18, but 35 is not - // equal to 5. + // Because of the special self-loop tests we use + // above, it's possible that i can be mapped to + // remap[j] even if j was the last compatible + // state found. Consider the following cases, + // taken from an actual test case: 18 is equal to + // 5, 35 is equal to 18, but 35 is not equal to 5. // // State: 5 // [0&1&2] 8 {3} @@ -673,8 +668,9 @@ namespace spot } }; - auto worker = [&upd, check_ix, nthreads](unsigned pid, auto begp, auto endp, - auto bege, auto ende) + auto worker = [&upd, check_ix, nthreads](unsigned pid, + auto beg1, auto end1, + auto beg0, auto end0) { // Temporary storage for list of edges to reduce cache misses std::vector v; @@ -682,19 +678,19 @@ namespace spot // that have been matched already. std::vector checked1; std::vector checked2; - upd(begp, endp, pid); - upd(bege, ende, pid); - for (; begp != endp; upd(begp, endp, nthreads)) - check_ix(begp->second.first, v, checked1, checked2); - for (; bege != ende; upd(bege, ende, nthreads)) - check_ix(bege->second.first, v, checked1, checked2); + upd(beg1, end1, pid); + upd(beg0, end0, pid); + for (; beg1 != end1; upd(beg1, end1, nthreads)) + check_ix(beg1->second.first, v, checked1, checked2); + for (; beg0 != end0; upd(beg0, end0, nthreads)) + check_ix(beg0->second.first, v, checked1, checked2); }; { - auto begp = player_map.begin(); - auto endp = player_map.end(); - auto bege = env_map.begin(); - auto ende = env_map.end(); + auto beg1 = map1.begin(); + auto end1 = map1.end(); + auto beg0 = map0.begin(); + auto end0 = map0.end(); #ifndef ENABLE_PTHREAD (void) nthreads; @@ -702,7 +698,7 @@ namespace spot if (nthreads <= 1) { #endif // ENABLE_PTHREAD - worker(0, begp, endp, bege, ende); + worker(0, beg1, end1, beg0, end0); #ifdef ENABLE_PTHREAD } else @@ -712,9 +708,9 @@ namespace spot tv.resize(nthreads); for (unsigned pid = 0; pid < nthreads; ++pid) tv[pid] = std::thread( - [worker, pid, begp, endp, bege, ende]() + [worker, pid, beg1, end1, beg0, end0]() { - worker(pid, begp, endp, bege, ende); + worker(pid, beg1, end1, beg0, end0); return; }); for (auto& t : tv) @@ -747,7 +743,7 @@ namespace spot defrag_states(remap, st); // Info hash coll 2 //std::cout << "Hash collission rate post merge: " - // << ((env_map.size()+player_map.size())/((float)num_states())) + // << ((map0.size()+map1.size())/((float)num_states())) // << '\n'; return merged; } From 925ac6bbe496d5ec6844f427c677b321c30fc458 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Tue, 30 Aug 2022 23:37:12 +0200 Subject: [PATCH 125/337] * .gitlab-ci.yml: Use gcovr and produce an XML report for gitlab. --- .gitlab-ci.yml | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 86d89f9d7..3950ea523 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -44,10 +44,8 @@ debian-unstable-gcc-coverage: - ./configure CXX='g++ --coverage' --enable-devel --disable-static --enable-doxygen - make - make check - - lcov --capture --directory . --no-external --output spot.info - - lcov --remove spot.info '*/bin/spot.cc' '*/bin/spot-x.cc' '*/spot/parse*/scan*.cc' '*/spot/parse*/parse*.cc' '*/utf8/*' '*/python/*' '*/buddy/*' '*/doc/org/tmp/*' --output spot2.info - - lcov --summary spot2.info - - genhtml --legend --demangle-cpp --output-directory coverage spot2.info + - gcovr --xml-pretty --exclude-unreachable-branches --print-summary -o coverage.xml --root $PWD -e 'bin/spot.cc' -e 'bin/spot-x.cc' -e 'spot/bricks/.*' -e 'spot/parse.*/scan.*.cc' -e 'spot/parse.*/parse.*.cc' -e 'utf8/.*' -e 'python/.*' -e 'buddy/.*' -e 'doc/org/tmp/.*' --html-details coverage.html --html-tab-size 8 + coverage: /^\s*lines:\s*\d+.\d+\%/ artifacts: when: always paths: @@ -55,9 +53,12 @@ debian-unstable-gcc-coverage: - ./*.log - doc/spot.html/ - doc/userdoc/ - - coverage/ + - coverage*.html - ./*.tar.gz - - spot2.info + reports: + coverage_report: + coverage_format: cobertura + path: coverage.xml debian-unstable-gcc-pypy: stage: build From 2e32793ed12b5fde9f745747d7106cee627cf0e5 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Thu, 1 Sep 2022 20:47:31 +0200 Subject: [PATCH 126/337] * .gitlab-ci.yml (debian-unstable-gcc-coverage): Export coverage.css. --- .gitlab-ci.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 3950ea523..5f5a9c662 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -54,6 +54,7 @@ debian-unstable-gcc-coverage: - doc/spot.html/ - doc/userdoc/ - coverage*.html + - coverage*.css - ./*.tar.gz reports: coverage_report: From 7cf580a9c53edffcff2c3a384117c39707ce80cf Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Thu, 1 Sep 2022 21:27:18 +0200 Subject: [PATCH 127/337] we want the tarball we distribute to be built on Debian unstable See issue #512. * .gitlab-ci.yml (make-dist): New build. (debian-unstable-gcc-pypy, arch-gcc-glibcxxdebug, mingw-shared, mingw-static, publish-stable): Depend upon make-dist. --- .gitlab-ci.yml | 86 ++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 73 insertions(+), 13 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 5f5a9c662..c381793b0 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -32,6 +32,30 @@ debian-stable-gcc: - ./*.log - ./*.tar.gz +# We build on Debian unstable because we want an up-to-date Automake. +# (See issue #512.) We do not run distcheck here to speedup this build +# that several other builds depend upon. Other builds will run distcheck. +make-dist: + stage: build + only: + - branches + except: + - /wip/ + image: gitlab-registry.lrde.epita.fr/spot/buildenv/debian + script: + - autoreconf -vfi + - ./configure --disable-static --enable-doxygen + - make + - make dist + - autoconf --trace='AC_INIT:$2' > VERSION + artifacts: + when: always + paths: + - spot-*/_build/sub/tests/*/*.log + - ./*.log + - ./*.tar.gz + - VERSION + debian-unstable-gcc-coverage: stage: build only: @@ -62,22 +86,29 @@ debian-unstable-gcc-coverage: path: coverage.xml debian-unstable-gcc-pypy: - stage: build + stage: build2 + needs: + - job: make-dist + artifacts: true + variables: + GIT_STRATEGY: none only: - branches except: - /wip/ image: gitlab-registry.lrde.epita.fr/spot/buildenv/debian script: - - autoreconf -vfi + - VERSION=`cat VERSION` + - tar xvf spot-$VERSION.tar.gz + - cd spot-$VERSION - ./configure PYTHON=/usr/bin/pypy3 --disable-static - make - make check TESTS='$(TESTS_python) $(TESTS_ipython)' artifacts: when: always paths: - - tests/*/*.log - - ./*.log + - spot-*/tests/*/*.log + - spot-*/*.log debian-gcc-snapshot: stage: build @@ -140,22 +171,30 @@ arch-clang: - ./*.log arch-gcc-glibcxxdebug: - stage: build + stage: build2 + needs: + - job: make-dist + artifacts: true + variables: + GIT_STRATEGY: none only: - branches except: - /wip/ image: gitlab-registry.lrde.epita.fr/spot/buildenv/arch script: - - autoreconf -vfi - - ./configure --enable-devel --enable-c++20 --enable-glibcxx-debug + - VERSION=`cat VERSION` + - tar xvf spot-$VERSION.tar.gz + - mkdir build-$VERSION + - cd build-$VERSION + - ../spot-$VERSION/configure --enable-devel --enable-c++20 --enable-glibcxx-debug - make - make distcheck DISTCHECK_CONFIGURE_FLAGS='--enable-devel --enable-c++20 --enable-glibcxx-debug' artifacts: when: on_failure paths: - - ./spot-*/_build/sub/tests/*/*.log - - ./*.log + - build-*/spot-*/_build/sub/tests/*/*.log + - build-*/*.log mingw-shared: stage: build2 @@ -163,15 +202,17 @@ mingw-shared: # We start from the tarball generated from a non-cross-compiling # job, so that all generated files are included, especially those # built from the executables. - - job: debian-stable-gcc + - job: make-dist artifacts: true + variables: + GIT_STRATEGY: none only: - branches except: - /wip/ image: gitlab-registry.lrde.epita.fr/spot/buildenv/debian script: - - VERSION=`autoconf --trace='AC_INIT:$2'` + - VERSION=`cat VERSION` - tar xvf spot-$VERSION.tar.gz - cd spot-$VERSION - ./configure CC=i686-w64-mingw32-gcc CXX=i686-w64-mingw32-g++-posix --host i686-w64-mingw32 --disable-python @@ -188,15 +229,17 @@ mingw-static: # We start from the tarball generated from a non-cross-compiling # job, so that all generated files are included, especially those # built from the executables. - - job: debian-stable-gcc + - job: make-dist artifacts: true + variables: + GIT_STRATEGY: none only: - branches except: - /wip/ image: gitlab-registry.lrde.epita.fr/spot/buildenv/debian script: - - VERSION=`autoconf --trace='AC_INIT:$2'` + - VERSION=`cat VERSION` - tar xvf spot-$VERSION.tar.gz - cd spot-$VERSION - mkdir install_dir @@ -216,6 +259,8 @@ mingw-static: debpkg-stable: stage: build + variables: + GIT_STRATEGY: none only: - /-deb$/ - master @@ -240,6 +285,8 @@ debpkg-stable: debpkg-stable-i386: stage: build2 + variables: + GIT_STRATEGY: none only: - /-deb$/ - master @@ -269,6 +316,8 @@ debpkg-stable-i386: debpkg-unstable: stage: build + variables: + GIT_STRATEGY: none only: - /-deb$/ - next @@ -291,6 +340,8 @@ debpkg-unstable: debpkg-unstable-i386: stage: build2 + variables: + GIT_STRATEGY: none only: - /-deb$/ - next @@ -342,6 +393,8 @@ rpm-pkg: publish-rpm: stage: publish + variables: + GIT_STRATEGY: none only: - /-rpm$/ - next @@ -359,12 +412,17 @@ publish-stable: tags: - dput stage: publish + variables: + GIT_STRATEGY: none dependencies: - debpkg-stable-i386 + - make-dist script: - cd _build_stable - ls -l - dput lrde *.changes + - cd .. + - ls -l - tgz=`ls spot-*.tar.* | head -n 1` - case $tgz in *[0-9].tar.*) scp $tgz doc@perso:/var/www/dload/spot/;; esac - curl -X POST -F ref=master -F token=$TRIGGER_SPOT_WEB -F "variables[spot_branch]=stable" https://gitlab.lrde.epita.fr/api/v4/projects/131/trigger/pipeline @@ -377,6 +435,8 @@ publish-unstable: tags: - dput stage: publish + variables: + GIT_STRATEGY: none dependencies: - debpkg-unstable-i386 script: From 0f131f2eee8e71113c6298016d94aba1e540f21b Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Tue, 6 Sep 2022 18:05:52 +0200 Subject: [PATCH 128/337] =?UTF-8?q?product:=20B=C3=BCchi|B=C3=BCchi=3DB?= =?UTF-8?q?=C3=BCchi,=20CoB=C3=BCchi&CoB=C3=BCchi=3DCoB=C3=BCchi?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Improve the construction of the above constructions, saving colors. * spot/twaalgos/product.cc: Here. * spot/twaalgos/product.hh, NEWS: Mention it. * tests/core/prodchain.test, tests/core/prodor.test, tests/python/_product_weak.ipynb: Adjust. --- NEWS | 6 +- spot/twaalgos/product.cc | 22 +- spot/twaalgos/product.hh | 43 +- tests/core/prodchain.test | 42 +- tests/core/prodor.test | 8 +- tests/python/_product_weak.ipynb | 8468 ++++++++++++++++++++++-------- 6 files changed, 6449 insertions(+), 2140 deletions(-) diff --git a/NEWS b/NEWS index 5b55ebbdd..66f56e75d 100644 --- a/NEWS +++ b/NEWS @@ -114,10 +114,14 @@ New in spot 2.10.6.dev (not yet released) to obtain a simple model checker (that returns true or false, without counterexample). + - product() learned that the product of two co-Büchi automata + is a co-Büchi automaton. And product_or() learned that the + "or"-product of two Büchi automata is a Büchi automaton. + - spot::parallel_policy is an object that can be passed to some algorithm to specify how many threads can be used if Spot has been compiled with --enable-pthread. Currently, only - twa_graph::merge_states() support it. + twa_graph::merge_states() supports it. Python bindings: diff --git a/spot/twaalgos/product.cc b/spot/twaalgos/product.cc index 7fb70ddd6..243f3768c 100644 --- a/spot/twaalgos/product.cc +++ b/spot/twaalgos/product.cc @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2014-2020 Laboratoire de Recherche et Développement +// Copyright (C) 2014-2020, 2022 Laboratoire de Recherche et Développement // de l'Epita (LRDE). // // This file is part of Spot, a model checking library. @@ -122,8 +122,23 @@ namespace spot res->copy_ap_of(left); res->copy_ap_of(right); + auto& lacc = left->acc(); + auto& racc = right->acc(); + bool leftweak = left->prop_weak().is_true(); bool rightweak = right->prop_weak().is_true(); + + // The conjunction of two co-Büchi automata is a co-Büchi automaton. + // The disjunction of two Büchi automata is a Büchi automaton. + // + // The code to handle this case is similar to the weak_weak case, + // except we do not set the weak property on the result. + if (!leftweak + && !rightweak + && ((aop == and_acc && lacc.is_co_buchi() && racc.is_co_buchi()) + || (aop == or_acc && lacc.is_buchi() && racc.is_buchi()))) + goto and_cobuchi_or_buchi; + // We have optimization to the standard product in case one // of the arguments is weak. if (leftweak || rightweak) @@ -132,14 +147,13 @@ namespace spot // t, f, Büchi or co-Büchi. We use co-Büchi only when // t and f cannot be used, and both acceptance conditions // are in {t,f,co-Büchi}. - if (leftweak && rightweak) + if ((leftweak && rightweak)) { weak_weak: res->prop_weak(true); + and_cobuchi_or_buchi: acc_cond::mark_t accmark = {0}; acc_cond::mark_t rejmark = {}; - auto& lacc = left->acc(); - auto& racc = right->acc(); if ((lacc.is_co_buchi() && (racc.is_co_buchi() || racc.num_sets() == 0)) || (lacc.num_sets() == 0 && racc.is_co_buchi())) diff --git a/spot/twaalgos/product.hh b/spot/twaalgos/product.hh index 49ee9acdf..784a3cb49 100644 --- a/spot/twaalgos/product.hh +++ b/spot/twaalgos/product.hh @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2014-2015, 2018-2020 Laboratoire de Recherche et +// Copyright (C) 2014-2015, 2018-2020, 2022 Laboratoire de Recherche et // Développement de l'Epita (LRDE). // // This file is part of Spot, a model checking library. @@ -37,10 +37,14 @@ namespace spot /// The resulting automaton will accept the intersection of both /// languages and have an acceptance condition that is the /// conjunction of the acceptance conditions of the two input - /// automata. In case one of the left or right automaton is weak, - /// the acceptance condition of the result is made simpler: it - /// usually is the acceptance condition of the other argument, + /// automata. + /// + /// As an optionmization, in case one of the left or right automaton + /// is weak, the acceptance condition of the result is made simpler: + /// it usually is the acceptance condition of the other argument, /// therefore avoiding the need to introduce new accepting sets. + /// Similarly, the product of two co-Büchi automata will be a + /// co-Büchi automaton. /// /// The algorithm also defines a named property called /// "product-states" with type spot::product_states. This stores @@ -64,10 +68,14 @@ namespace spot /// languages recognized by each input automaton (with its initial /// state changed) and have an acceptance condition that is the /// conjunction of the acceptance conditions of the two input - /// automata. In case one of the left or right automaton is weak, - /// the acceptance condition of the result is made simpler: it - /// usually is the acceptance condition of the other argument, + /// automata. + /// + /// As an optionmization, in case one of the left or right automaton + /// is weak, the acceptance condition of the result is made simpler: + /// it usually is the acceptance condition of the other argument, /// therefore avoiding the need to introduce new accepting sets. + /// Similarly, the product of two co-Büchi automata will be a + /// co-Büchi automaton. /// /// The algorithm also defines a named property called /// "product-states" with type spot::product_states. This stores @@ -89,10 +97,15 @@ namespace spot /// The resulting automaton will accept the union of both /// languages and have an acceptance condition that is the /// disjunction of the acceptance conditions of the two input - /// automata. In case one of the left or right automaton is weak, - /// the acceptance condition of the result is made simpler: it - /// usually is the acceptance condition of the other argument, + /// automata. + /// + /// As an optionmization, in case one of the left or right automaton + /// is weak, the acceptance condition of the result is made simpler: + /// it usually is the acceptance condition of the other argument, /// therefore avoiding the need to introduce new accepting sets. + /// Similarly, the product_pr of two Büchi automata will be a + /// Büchi automaton. + /// /// /// The algorithm also defines a named property called /// "product-states" with type spot::product_states. This stores @@ -112,10 +125,14 @@ namespace spot /// recognized by each input automaton (with its initial state /// changed) and have an acceptance condition that is the /// disjunction of the acceptance conditions of the two input - /// automata. In case one of the left or right automaton is weak, - /// the acceptance condition of the result is made simpler: it - /// usually is the acceptance condition of the other argument, + /// automata. + /// + /// As an optionmization, in case one of the left or right automaton + /// is weak, the acceptance condition of the result is made simpler: + /// it usually is the acceptance condition of the other argument, /// therefore avoiding the need to introduce new accepting sets. + /// Similarly, the product_pr of two Büchi automata will be a + /// Büchi automaton. /// /// The algorithm also defines a named property called /// "product-states" with type spot::product_states. This stores diff --git a/tests/core/prodchain.test b/tests/core/prodchain.test index e00422148..9a9c74648 100755 --- a/tests/core/prodchain.test +++ b/tests/core/prodchain.test @@ -32,12 +32,12 @@ for i in *.hoa; do shift done shift -if $MAX_ACCSETS -eq 32; then +if [ $MAX_ACCSETS -eq 32 ]; then autfilt "$@" 2> error && exit 1 grep 'Too many acceptance sets used' error fi -autfilt -B "$@" > result -test "127,253,508,1" = `autfilt --stats=%s,%e,%t,%a result` +autfilt -B --low "$@" > result +test "4,7,16,1" = `autfilt --stats=%s,%e,%t,%a result` set x shift @@ -46,9 +46,37 @@ for i in *.hoa; do shift done shift -if $MAX_ACCSETS -eq 32; then - autfilt "$@" 2> error && exit 1 +autfilt -B --low "$@" > result +test "45,89,180,1" = `autfilt --stats=%s,%e,%t,%a result` + + +set x +shift +for i in 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 \ + 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42; do + ltl2tgba -D --cobuchi -S "{a[*$i]}<>->FGb" > $i.hoa +done +for i in *.hoa; do + set x "$@" --product $i + shift +done +shift +autfilt -D --cobuchi --low -S "$@" > result +test "85,170,174,1" = `autfilt --stats=%s,%e,%t,%a result` + +set x +shift +for i in *.hoa; do + set x "$@" --product-or $i + shift +done +shift +if [ $MAX_ACCSETS -eq 32 ]; then + autfilt --cobuchi -S "$@" 2> error && exit 1 grep 'Too many acceptance sets used' error fi -autfilt -B "$@" > result -test "45,89,180,1" = `autfilt --stats=%s,%e,%t,%a result` +# FIXME: implement degeneralization for generalized-co-Büchi +# autfilt --cobuchi --low -S "$@" > result +# test "45,89,180,1" = `autfilt --stats=%s,%e,%t,%a result` + +true diff --git a/tests/core/prodor.test b/tests/core/prodor.test index 03127508b..03d8cd458 100755 --- a/tests/core/prodor.test +++ b/tests/core/prodor.test @@ -1,6 +1,6 @@ #!/bin/sh # -*- coding: utf-8 -*- -# Copyright (C) 2015, 2017-2018, 2021 Laboratoire de Recherche et +# Copyright (C) 2015, 2017-2018, 2021-2022 Laboratoire de Recherche et # Développement de l'Epita (LRDE). # # This file is part of Spot, a model checking library. @@ -154,8 +154,8 @@ diff por.hoa exp ltl2tgba -BDH 'GFa' > gfa.hoa ltl2tgba -x '!wdba-minimize' -DH 'Xb' > xb.hoa -autfilt --product-or gfa.hoa xb.hoa -H > por.hoa -cat por.hoa +autfilt --product-or gfa.hoa xb.hoa -H > por2.hoa +cat por2.hoa cat >exp <\n" ], "text/plain": [ - " *' at 0x7fd90c347ba0> >" + " *' at 0x7f26743d3720> >" ] }, "metadata": {}, @@ -299,11 +313,11 @@ "\n", "\n", - "\n", - "\n", - "\n", - "[Büchi]\n", + "\n", + "\n", + "\n", + "[Büchi]\n", "\n", "\n", "\n", @@ -379,12 +393,12 @@ "\n", "\n", - "\n", - "\n", + "\n", + "\n", "Fb\n", - "\n", - "[Büchi]\n", + "\n", + "[Büchi]\n", "\n", "\n", "\n", @@ -434,11 +448,11 @@ "\n", "\n", - "\n", - "\n", - "\n", - "[Büchi]\n", + "\n", + "\n", + "\n", + "[Büchi]\n", "\n", "\n", "\n", @@ -508,11 +522,11 @@ "\n", "\n", - "\n", - "\n", - "\n", - "[Büchi]\n", + "\n", + "\n", + "\n", + "[Büchi]\n", "\n", "\n", "\n", @@ -644,12 +658,12 @@ "\n", "\n", - "\n", - "\n", + "\n", + "\n", "Fb\n", - "\n", - "[co-Büchi]\n", + "\n", + "[co-Büchi]\n", "\n", "\n", "\n", @@ -699,11 +713,11 @@ "\n", "\n", - "\n", - "\n", - "\n", - "[co-Büchi]\n", + "\n", + "\n", + "\n", + "[co-Büchi]\n", "\n", "\n", "\n", @@ -774,11 +788,11 @@ "\n", "\n", - "\n", - "\n", - "\n", - "[Büchi]\n", + "\n", + "\n", + "\n", + "[Büchi]\n", "\n", "\n", "\n", @@ -1127,6 +1141,226 @@ "metadata": {}, "output_type": "display_data" }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "FGd\n", + "\n", + "Fin(\n", + "\n", + ")\n", + "[co-Büchi]\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "!c\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "c\n", + "\n", + "\n", + "\n", + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "Fin(\n", + "\n", + ")\n", + "[co-Büchi]\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "1,0\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "1\n", + "\n", + "0,0\n", + "\n", + "\n", + "\n", + "0->1\n", + "\n", + "\n", + "a & !c\n", + "\n", + "\n", + "\n", + "\n", + "0->1\n", + "\n", + "\n", + "a & c\n", + "\n", + "\n", + "\n", + "1->1\n", + "\n", + "\n", + "!c\n", + "\n", + "\n", + "\n", + "\n", + "1->1\n", + "\n", + "\n", + "c\n", + "\n", + "\n", + "\n", + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "Fin(\n", + "\n", + ")\n", + "[co-Büchi]\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "1,0\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "1\n", + "\n", + "0,0\n", + "\n", + "\n", + "\n", + "0->1\n", + "\n", + "\n", + "a & !c\n", + "\n", + "\n", + "\n", + "0->1\n", + "\n", + "\n", + "a & c\n", + "\n", + "\n", + "\n", + "2\n", + "\n", + "2,0\n", + "\n", + "\n", + "\n", + "0->2\n", + "\n", + "\n", + "!a & !c\n", + "\n", + "\n", + "\n", + "0->2\n", + "\n", + "\n", + "!a & c\n", + "\n", + "\n", + "\n", + "1->1\n", + "\n", + "\n", + "!c\n", + "\n", + "\n", + "\n", + "1->1\n", + "\n", + "\n", + "c\n", + "\n", + "\n", + "\n", + "2->2\n", + "\n", + "\n", + "!c\n", + "\n", + "\n", + "\n", + "\n", + "2->2\n", + "\n", + "\n", + "c\n", + "\n", + "\n", + "\n", + "
" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, { "data": { "text/html": [ @@ -1586,7 +1820,7 @@ "# In a previous version we used to iterate over all possible left automata with \"for left in auts:\"\n", "# however we had trouble with Jupyter on i386, where running the full loop abort with some low-level \n", "# exeptions from Jupyter client. Halving the loop helped for some times, but then the timeout\n", - "# came back. So we do one left automaton at at time.\n", + "# came back. So we do one left automaton at a time.\n", "left = auts[0]\n", "display(left)\n", "for right in auts:\n", @@ -1609,12 +1843,12 @@ "\n", "\n", - "\n", - "\n", + "\n", + "\n", "Fb\n", - "\n", - "[Büchi]\n", + "\n", + "[Büchi]\n", "\n", "\n", "\n", @@ -1660,7 +1894,7 @@ "\n" ], "text/plain": [ - " *' at 0x7fd90c347bd0> >" + " *' at 0x7f26743d36c0> >" ] }, "metadata": {}, @@ -1723,11 +1957,11 @@ "\n", "\n", - "\n", - "\n", - "\n", - "[Büchi]\n", + "\n", + "\n", + "\n", + "[Büchi]\n", "\n", "\n", "\n", @@ -1797,11 +2031,11 @@ "\n", "\n", - "\n", - "\n", - "\n", - "[Büchi]\n", + "\n", + "\n", + "\n", + "[Büchi]\n", "\n", "\n", "\n", @@ -1933,12 +2167,12 @@ "\n", "\n", - "\n", - "\n", + "\n", + "\n", "Fb\n", - "\n", - "[Büchi]\n", + "\n", + "[Büchi]\n", "\n", "\n", "\n", @@ -1988,11 +2222,11 @@ "\n", "\n", - "\n", - "\n", - "\n", - "[Büchi]\n", + "\n", + "\n", + "\n", + "[Büchi]\n", "\n", "\n", "\n", @@ -2042,11 +2276,11 @@ "\n", "\n", - "\n", - "\n", - "\n", - "[Büchi]\n", + "\n", + "\n", + "\n", + "[Büchi]\n", "\n", "\n", "\n", @@ -2108,12 +2342,12 @@ "\n", "\n", - "\n", - "\n", + "\n", + "\n", "Fb\n", - "\n", - "[co-Büchi]\n", + "\n", + "[co-Büchi]\n", "\n", "\n", "\n", @@ -2163,11 +2397,11 @@ "\n", "\n", - "\n", - "\n", - "\n", - "[Büchi]\n", + "\n", + "\n", + "\n", + "[Büchi]\n", "\n", "\n", "\n", @@ -2217,11 +2451,11 @@ "\n", "\n", - "\n", - "\n", - "\n", - "[Büchi]\n", + "\n", + "\n", + "\n", + "[Büchi]\n", "\n", "\n", "\n", @@ -2490,6 +2724,224 @@ "metadata": {}, "output_type": "display_data" }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "FGd\n", + "\n", + "Fin(\n", + "\n", + ")\n", + "[co-Büchi]\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "!c\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "c\n", + "\n", + "\n", + "\n", + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "Fin(\n", + "\n", + ")\n", + "[co-Büchi]\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "1,0\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "!b & !c\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "!b & c\n", + "\n", + "\n", + "\n", + "\n", + "1\n", + "\n", + "0,0\n", + "\n", + "\n", + "\n", + "0->1\n", + "\n", + "\n", + "b & !c\n", + "\n", + "\n", + "\n", + "\n", + "0->1\n", + "\n", + "\n", + "b & c\n", + "\n", + "\n", + "\n", + "\n", + "1->1\n", + "\n", + "\n", + "!c\n", + "\n", + "\n", + "\n", + "\n", + "1->1\n", + "\n", + "\n", + "c\n", + "\n", + "\n", + "\n", + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "Fin(\n", + "\n", + ")\n", + "[co-Büchi]\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "1,0\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "!b & !c\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "!b & c\n", + "\n", + "\n", + "\n", + "1\n", + "\n", + "0,0\n", + "\n", + "\n", + "\n", + "0->1\n", + "\n", + "\n", + "b & !c\n", + "\n", + "\n", + "\n", + "\n", + "0->1\n", + "\n", + "\n", + "b & c\n", + "\n", + "\n", + "\n", + "1->1\n", + "\n", + "\n", + "!c\n", + "\n", + "\n", + "\n", + "1->1\n", + "\n", + "\n", + "c\n", + "\n", + "\n", + "\n", + "
" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, { "data": { "text/html": [ @@ -2949,12 +3401,12 @@ "\n", "\n", - "\n", - "\n", + "\n", + "\n", "Fb\n", - "\n", - "[co-Büchi]\n", + "\n", + "[co-Büchi]\n", "\n", "\n", "\n", @@ -3000,7 +3452,7 @@ "\n" ], "text/plain": [ - " *' at 0x7fd90c347cf0> >" + " *' at 0x7f26743d37e0> >" ] }, "metadata": {}, @@ -3063,11 +3515,11 @@ "\n", "\n", - "\n", - "\n", - "\n", - "[co-Büchi]\n", + "\n", + "\n", + "\n", + "[co-Büchi]\n", "\n", "\n", "\n", @@ -3138,11 +3590,11 @@ "\n", "\n", - "\n", - "\n", - "\n", - "[Büchi]\n", + "\n", + "\n", + "\n", + "[Büchi]\n", "\n", "\n", "\n", @@ -3274,12 +3726,12 @@ "\n", "\n", - "\n", - "\n", + "\n", + "\n", "Fb\n", - "\n", - "[Büchi]\n", + "\n", + "[Büchi]\n", "\n", "\n", "\n", @@ -3329,11 +3781,11 @@ "\n", "\n", - "\n", - "\n", - "\n", - "[Büchi]\n", + "\n", + "\n", + "\n", + "[Büchi]\n", "\n", "\n", "\n", @@ -3383,11 +3835,11 @@ "\n", "\n", - "\n", - "\n", - "\n", - "[Büchi]\n", + "\n", + "\n", + "\n", + "[Büchi]\n", "\n", "\n", "\n", @@ -3449,12 +3901,12 @@ "\n", "\n", - "\n", - "\n", + "\n", + "\n", "Fb\n", - "\n", - "[co-Büchi]\n", + "\n", + "[co-Büchi]\n", "\n", "\n", "\n", @@ -3504,11 +3956,11 @@ "\n", "\n", - "\n", - "\n", - "\n", - "[co-Büchi]\n", + "\n", + "\n", + "\n", + "[co-Büchi]\n", "\n", "\n", "\n", @@ -3558,11 +4010,11 @@ "\n", "\n", - "\n", - "\n", - "\n", - "[co-Büchi]\n", + "\n", + "\n", + "\n", + "[co-Büchi]\n", "\n", "\n", "\n", @@ -3831,6 +4283,224 @@ "metadata": {}, "output_type": "display_data" }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "FGd\n", + "\n", + "Fin(\n", + "\n", + ")\n", + "[co-Büchi]\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "!c\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "c\n", + "\n", + "\n", + "\n", + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "Fin(\n", + "\n", + ")\n", + "[co-Büchi]\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "1,0\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "!b & !c\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "!b & c\n", + "\n", + "\n", + "\n", + "\n", + "1\n", + "\n", + "0,0\n", + "\n", + "\n", + "\n", + "0->1\n", + "\n", + "\n", + "b & !c\n", + "\n", + "\n", + "\n", + "\n", + "0->1\n", + "\n", + "\n", + "b & c\n", + "\n", + "\n", + "\n", + "\n", + "1->1\n", + "\n", + "\n", + "!c\n", + "\n", + "\n", + "\n", + "\n", + "1->1\n", + "\n", + "\n", + "c\n", + "\n", + "\n", + "\n", + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "Fin(\n", + "\n", + ")\n", + "[co-Büchi]\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "1,0\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "!b & !c\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "!b & c\n", + "\n", + "\n", + "\n", + "1\n", + "\n", + "0,0\n", + "\n", + "\n", + "\n", + "0->1\n", + "\n", + "\n", + "b & !c\n", + "\n", + "\n", + "\n", + "\n", + "0->1\n", + "\n", + "\n", + "b & c\n", + "\n", + "\n", + "\n", + "1->1\n", + "\n", + "\n", + "!c\n", + "\n", + "\n", + "\n", + "1->1\n", + "\n", + "\n", + "c\n", + "\n", + "\n", + "\n", + "
" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, { "data": { "text/html": [ @@ -4331,7 +5001,7 @@ "\n" ], "text/plain": [ - " *' at 0x7fd90c347d50> >" + " *' at 0x7f26743d3870> >" ] }, "metadata": {}, @@ -4575,12 +5245,12 @@ "\n", "\n", - "\n", - "\n", + "\n", + "\n", "Fb\n", - "\n", - "[Büchi]\n", + "\n", + "[Büchi]\n", "\n", "\n", "\n", @@ -4801,12 +5471,12 @@ "\n", "\n", - "\n", - "\n", + "\n", + "\n", "Fb\n", - "\n", - "[co-Büchi]\n", + "\n", + "[co-Büchi]\n", "\n", "\n", "\n", @@ -5119,44 +5789,192 @@ "\n", "\n", - "\n", - "\n", - "\n", - "Inf(\n", - "\n", - ") | Inf(\n", - "\n", - ")\n", - "[Fin-less 2]\n", + "\n", + "\n", + "\n", + "Inf(\n", + "\n", + ")\n", + "[Büchi]\n", "\n", "\n", "\n", "0\n", - "\n", - "0,0\n", + "\n", + "0,0\n", "\n", "\n", "\n", "I->0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "0->0\n", - "\n", - "\n", - "!c\n", + "\n", + "\n", + "!c\n", "\n", "\n", "\n", "0->0\n", - "\n", - "\n", - "c\n", - "\n", - "\n", + "\n", + "\n", + "c\n", + "\n", + "\n", + "\n", + "\n", + "
" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "FGd\n", + "\n", + "Fin(\n", + "\n", + ")\n", + "[co-Büchi]\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "!c\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "c\n", + "\n", + "\n", + "\n", + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "Inf(\n", + "\n", + ") & Fin(\n", + "\n", + ")\n", + "[Rabin-like 1]\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0,0\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "!c\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "c\n", + "\n", + "\n", + "\n", + "\n", + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "Inf(\n", + "\n", + ") | Fin(\n", + "\n", + ")\n", + "[gen. Streett 1]\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0,0\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "!c\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "c\n", + "\n", "\n", "\n", "\n", @@ -5559,6 +6377,1431 @@ "metadata": { "scrolled": false }, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "FGd\n", + "\n", + "Fin(\n", + "\n", + ")\n", + "[co-Büchi]\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "!c\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "c\n", + "\n", + "\n", + "\n" + ], + "text/plain": [ + " *' at 0x7f26743d3900> >" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "a\n", + "\n", + "t\n", + "[all]\n", + "\n", + "\n", + "\n", + "1\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "I->1\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0\n", + "\n", + "\n", + "\n", + "1->0\n", + "\n", + "\n", + "a\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "Fin(\n", + "\n", + ")\n", + "[co-Büchi]\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0,1\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "1\n", + "\n", + "0,0\n", + "\n", + "\n", + "\n", + "0->1\n", + "\n", + "\n", + "a & !c\n", + "\n", + "\n", + "\n", + "\n", + "0->1\n", + "\n", + "\n", + "a & c\n", + "\n", + "\n", + "\n", + "1->1\n", + "\n", + "\n", + "!c\n", + "\n", + "\n", + "\n", + "\n", + "1->1\n", + "\n", + "\n", + "c\n", + "\n", + "\n", + "\n", + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "Fin(\n", + "\n", + ")\n", + "[co-Büchi]\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0,1\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "1\n", + "\n", + "0,0\n", + "\n", + "\n", + "\n", + "0->1\n", + "\n", + "\n", + "a & !c\n", + "\n", + "\n", + "\n", + "0->1\n", + "\n", + "\n", + "a & c\n", + "\n", + "\n", + "\n", + "2\n", + "\n", + "0,2\n", + "\n", + "\n", + "\n", + "0->2\n", + "\n", + "\n", + "!a & !c\n", + "\n", + "\n", + "\n", + "0->2\n", + "\n", + "\n", + "!a & c\n", + "\n", + "\n", + "\n", + "1->1\n", + "\n", + "\n", + "!c\n", + "\n", + "\n", + "\n", + "1->1\n", + "\n", + "\n", + "c\n", + "\n", + "\n", + "\n", + "2->2\n", + "\n", + "\n", + "!c\n", + "\n", + "\n", + "\n", + "\n", + "2->2\n", + "\n", + "\n", + "c\n", + "\n", + "\n", + "\n", + "
" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "Fb\n", + "\n", + "[Büchi]\n", + "\n", + "\n", + "\n", + "1\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "I->1\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "1->1\n", + "\n", + "\n", + "!b\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "\n", + "0\n", + "\n", + "\n", + "\n", + "1->0\n", + "\n", + "\n", + "b\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "Fin(\n", + "\n", + ")\n", + "[co-Büchi]\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0,1\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "!b & !c\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "!b & c\n", + "\n", + "\n", + "\n", + "\n", + "1\n", + "\n", + "0,0\n", + "\n", + "\n", + "\n", + "0->1\n", + "\n", + "\n", + "b & !c\n", + "\n", + "\n", + "\n", + "\n", + "0->1\n", + "\n", + "\n", + "b & c\n", + "\n", + "\n", + "\n", + "\n", + "1->1\n", + "\n", + "\n", + "!c\n", + "\n", + "\n", + "\n", + "\n", + "1->1\n", + "\n", + "\n", + "c\n", + "\n", + "\n", + "\n", + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "Fin(\n", + "\n", + ")\n", + "[co-Büchi]\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0,1\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "!b & !c\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "!b & c\n", + "\n", + "\n", + "\n", + "1\n", + "\n", + "0,0\n", + "\n", + "\n", + "\n", + "0->1\n", + "\n", + "\n", + "b & !c\n", + "\n", + "\n", + "\n", + "\n", + "0->1\n", + "\n", + "\n", + "b & c\n", + "\n", + "\n", + "\n", + "1->1\n", + "\n", + "\n", + "!c\n", + "\n", + "\n", + "\n", + "1->1\n", + "\n", + "\n", + "c\n", + "\n", + "\n", + "\n", + "
" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "Fb\n", + "\n", + "[co-Büchi]\n", + "\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "I->1\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "1->1\n", + "\n", + "\n", + "!b\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0\n", + "\n", + "\n", + "\n", + "1->0\n", + "\n", + "\n", + "b\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "Fin(\n", + "\n", + ")\n", + "[co-Büchi]\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0,1\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "!b & !c\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "!b & c\n", + "\n", + "\n", + "\n", + "\n", + "1\n", + "\n", + "0,0\n", + "\n", + "\n", + "\n", + "0->1\n", + "\n", + "\n", + "b & !c\n", + "\n", + "\n", + "\n", + "\n", + "0->1\n", + "\n", + "\n", + "b & c\n", + "\n", + "\n", + "\n", + "\n", + "1->1\n", + "\n", + "\n", + "!c\n", + "\n", + "\n", + "\n", + "\n", + "1->1\n", + "\n", + "\n", + "c\n", + "\n", + "\n", + "\n", + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "Fin(\n", + "\n", + ")\n", + "[co-Büchi]\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0,1\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "!b & !c\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "!b & c\n", + "\n", + "\n", + "\n", + "1\n", + "\n", + "0,0\n", + "\n", + "\n", + "\n", + "0->1\n", + "\n", + "\n", + "b & !c\n", + "\n", + "\n", + "\n", + "\n", + "0->1\n", + "\n", + "\n", + "b & c\n", + "\n", + "\n", + "\n", + "1->1\n", + "\n", + "\n", + "!c\n", + "\n", + "\n", + "\n", + "1->1\n", + "\n", + "\n", + "c\n", + "\n", + "\n", + "\n", + "
" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "GFc\n", + "\n", + "Inf(\n", + "\n", + ")\n", + "[Büchi]\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "!c\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "c\n", + "\n", + "\n", + "\n", + "\n", + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "Fin(\n", + "\n", + ") & Inf(\n", + "\n", + ")\n", + "[Rabin 1]\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0,0\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "!c\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "c\n", + "\n", + "\n", + "\n", + "\n", + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "Fin(\n", + "\n", + ") | Inf(\n", + "\n", + ")\n", + "[Streett 1]\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0,0\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "!c\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "c\n", + "\n", + "\n", + "\n", + "\n", + "
" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "FGd\n", + "\n", + "Fin(\n", + "\n", + ")\n", + "[co-Büchi]\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "!c\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "c\n", + "\n", + "\n", + "\n", + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "Fin(\n", + "\n", + ")\n", + "[co-Büchi]\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0,0\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "!c\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "c\n", + "\n", + "\n", + "\n", + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "Fin(\n", + "\n", + ")|Fin(\n", + "\n", + ")\n", + "[gen. co-Büchi 2]\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0,0\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "!c\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "c\n", + "\n", + "\n", + "\n", + "
" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "Inf(\n", + "\n", + ") & Fin(\n", + "\n", + ")\n", + "[Rabin-like 1]\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "!d\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "d\n", + "\n", + "\n", + "\n", + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "Fin(\n", + "\n", + ") & Inf(\n", + "\n", + ") & Fin(\n", + "\n", + ")\n", + "[Streett-like 3]\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0,0\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "!c & !d\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "!c & d\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "c & !d\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "c & d\n", + "\n", + "\n", + "\n", + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "Fin(\n", + "\n", + ") | (Inf(\n", + "\n", + ") & Fin(\n", + "\n", + "))\n", + "[Rabin-like 2]\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0,0\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "!c & !d\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "!c & d\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "c & !d\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "c & d\n", + "\n", + "\n", + "\n", + "
" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "Inf(\n", + "\n", + ") | Fin(\n", + "\n", + ")\n", + "[Streett-like 1]\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "!d\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "d\n", + "\n", + "\n", + "\n", + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "Fin(\n", + "\n", + ") & (Inf(\n", + "\n", + ") | Fin(\n", + "\n", + "))\n", + "[Streett-like 2]\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0,0\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "!c & !d\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "!c & d\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "c & !d\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "c & d\n", + "\n", + "\n", + "\n", + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "(Fin(\n", + "\n", + ")|Fin(\n", + "\n", + ")) | Inf(\n", + "\n", + ")\n", + "[Rabin-like 3]\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0,0\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "!c & !d\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "!c & d\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "c & !d\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "c & d\n", + "\n", + "\n", + "\n", + "
" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "left = auts[4]\n", + "display(left)\n", + "for right in auts:\n", + " display_inline(right, spot.product(left, right), spot.product_or(left, right))" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "metadata": { + "scrolled": false + }, "outputs": [ { "data": { @@ -5611,7 +7854,7 @@ "\n" ], "text/plain": [ - " *' at 0x7fd90c347d80> >" + " *' at 0x7f26743d3990> >" ] }, "metadata": {}, @@ -5856,12 +8099,12 @@ "\n", "\n", - "\n", - "\n", + "\n", + "\n", "Fb\n", - "\n", - "[Büchi]\n", + "\n", + "[Büchi]\n", "\n", "\n", "\n", @@ -6082,12 +8325,12 @@ "\n", "\n", - "\n", - "\n", + "\n", + "\n", "Fb\n", - "\n", - "[co-Büchi]\n", + "\n", + "[co-Büchi]\n", "\n", "\n", "\n", @@ -6486,6 +8729,193 @@ "metadata": {}, "output_type": "display_data" }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "FGd\n", + "\n", + "Fin(\n", + "\n", + ")\n", + "[co-Büchi]\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "!c\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "c\n", + "\n", + "\n", + "\n", + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "Inf(\n", + "\n", + ") & Fin(\n", + "\n", + ") & Fin(\n", + "\n", + ")\n", + "[Streett-like 3]\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0,0\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "!c & !d\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "c & !d\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "!c & d\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "c & d\n", + "\n", + "\n", + "\n", + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "(Inf(\n", + "\n", + ") & Fin(\n", + "\n", + ")) | Fin(\n", + "\n", + ")\n", + "[Rabin-like 2]\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0,0\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "!c & !d\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "c & !d\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "!c & d\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "c & d\n", + "\n", + "\n", + "\n", + "
" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, { "data": { "text/html": [ @@ -6807,1266 +9237,6 @@ "output_type": "display_data" } ], - "source": [ - "left = auts[4]\n", - "display(left)\n", - "for right in auts:\n", - " display_inline(right, spot.product(left, right), spot.product_or(left, right))" - ] - }, - { - "cell_type": "code", - "execution_count": 9, - "metadata": { - "scrolled": false - }, - "outputs": [ - { - "data": { - "image/svg+xml": [ - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "Inf(\n", - "\n", - ") | Fin(\n", - "\n", - ")\n", - "[Streett-like 1]\n", - "\n", - "\n", - "\n", - "0\n", - "\n", - "0\n", - "\n", - "\n", - "\n", - "I->0\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "0->0\n", - "\n", - "\n", - "!d\n", - "\n", - "\n", - "\n", - "\n", - "0->0\n", - "\n", - "\n", - "d\n", - "\n", - "\n", - "\n" - ], - "text/plain": [ - " *' at 0x7fd90c347e40> >" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "a\n", - "\n", - "t\n", - "[all]\n", - "\n", - "\n", - "\n", - "1\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "I->1\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "0\n", - "\n", - "0\n", - "\n", - "\n", - "\n", - "1->0\n", - "\n", - "\n", - "a\n", - "\n", - "\n", - "\n", - "0->0\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "
\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "Inf(\n", - "\n", - ")\n", - "[Büchi]\n", - "\n", - "\n", - "\n", - "0\n", - "\n", - "0,1\n", - "\n", - "\n", - "\n", - "I->0\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "1\n", - "\n", - "0,0\n", - "\n", - "\n", - "\n", - "0->1\n", - "\n", - "\n", - "a & !d\n", - "\n", - "\n", - "\n", - "\n", - "0->1\n", - "\n", - "\n", - "a & d\n", - "\n", - "\n", - "\n", - "\n", - "1->1\n", - "\n", - "\n", - "!d\n", - "\n", - "\n", - "\n", - "\n", - "1->1\n", - "\n", - "\n", - "d\n", - "\n", - "\n", - "\n", - "\n", - "
\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "Inf(\n", - "\n", - ") | Fin(\n", - "\n", - ")\n", - "[Streett-like 1]\n", - "\n", - "\n", - "\n", - "0\n", - "\n", - "0,1\n", - "\n", - "\n", - "\n", - "I->0\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "1\n", - "\n", - "0,0\n", - "\n", - "\n", - "\n", - "0->1\n", - "\n", - "\n", - "a & !d\n", - "\n", - "\n", - "\n", - "0->1\n", - "\n", - "\n", - "a & d\n", - "\n", - "\n", - "\n", - "2\n", - "\n", - "0,2\n", - "\n", - "\n", - "\n", - "0->2\n", - "\n", - "\n", - "!a & !d\n", - "\n", - "\n", - "\n", - "0->2\n", - "\n", - "\n", - "!a & d\n", - "\n", - "\n", - "\n", - "1->1\n", - "\n", - "\n", - "!d\n", - "\n", - "\n", - "\n", - "1->1\n", - "\n", - "\n", - "d\n", - "\n", - "\n", - "\n", - "2->2\n", - "\n", - "\n", - "!d\n", - "\n", - "\n", - "\n", - "\n", - "2->2\n", - "\n", - "\n", - "d\n", - "\n", - "\n", - "\n", - "
" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "Fb\n", - "\n", - "[Büchi]\n", - "\n", - "\n", - "\n", - "1\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "I->1\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "1->1\n", - "\n", - "\n", - "!b\n", - "\n", - "\n", - "\n", - "0\n", - "\n", - "\n", - "0\n", - "\n", - "\n", - "\n", - "1->0\n", - "\n", - "\n", - "b\n", - "\n", - "\n", - "\n", - "0->0\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "
\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "Inf(\n", - "\n", - ")\n", - "[Büchi]\n", - "\n", - "\n", - "\n", - "0\n", - "\n", - "0,1\n", - "\n", - "\n", - "\n", - "I->0\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "0->0\n", - "\n", - "\n", - "!b & !d\n", - "\n", - "\n", - "\n", - "0->0\n", - "\n", - "\n", - "!b & d\n", - "\n", - "\n", - "\n", - "1\n", - "\n", - "0,0\n", - "\n", - "\n", - "\n", - "0->1\n", - "\n", - "\n", - "b & !d\n", - "\n", - "\n", - "\n", - "0->1\n", - "\n", - "\n", - "b & d\n", - "\n", - "\n", - "\n", - "1->1\n", - "\n", - "\n", - "!d\n", - "\n", - "\n", - "\n", - "\n", - "1->1\n", - "\n", - "\n", - "d\n", - "\n", - "\n", - "\n", - "\n", - "
\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "Inf(\n", - "\n", - ") | Fin(\n", - "\n", - ")\n", - "[Streett-like 1]\n", - "\n", - "\n", - "\n", - "0\n", - "\n", - "0,1\n", - "\n", - "\n", - "\n", - "I->0\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "0->0\n", - "\n", - "\n", - "!b & !d\n", - "\n", - "\n", - "\n", - "\n", - "0->0\n", - "\n", - "\n", - "!b & d\n", - "\n", - "\n", - "\n", - "1\n", - "\n", - "0,0\n", - "\n", - "\n", - "\n", - "0->1\n", - "\n", - "\n", - "b & !d\n", - "\n", - "\n", - "\n", - "\n", - "0->1\n", - "\n", - "\n", - "b & d\n", - "\n", - "\n", - "\n", - "1->1\n", - "\n", - "\n", - "!d\n", - "\n", - "\n", - "\n", - "1->1\n", - "\n", - "\n", - "d\n", - "\n", - "\n", - "\n", - "
" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "Fb\n", - "\n", - "[co-Büchi]\n", - "\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "I->1\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "1->1\n", - "\n", - "\n", - "!b\n", - "\n", - "\n", - "\n", - "0\n", - "\n", - "0\n", - "\n", - "\n", - "\n", - "1->0\n", - "\n", - "\n", - "b\n", - "\n", - "\n", - "\n", - "0->0\n", - "\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "
\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "Inf(\n", - "\n", - ")\n", - "[Büchi]\n", - "\n", - "\n", - "\n", - "0\n", - "\n", - "0,1\n", - "\n", - "\n", - "\n", - "I->0\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "0->0\n", - "\n", - "\n", - "!b & !d\n", - "\n", - "\n", - "\n", - "0->0\n", - "\n", - "\n", - "!b & d\n", - "\n", - "\n", - "\n", - "1\n", - "\n", - "0,0\n", - "\n", - "\n", - "\n", - "0->1\n", - "\n", - "\n", - "b & !d\n", - "\n", - "\n", - "\n", - "0->1\n", - "\n", - "\n", - "b & d\n", - "\n", - "\n", - "\n", - "1->1\n", - "\n", - "\n", - "!d\n", - "\n", - "\n", - "\n", - "\n", - "1->1\n", - "\n", - "\n", - "d\n", - "\n", - "\n", - "\n", - "\n", - "
\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "Inf(\n", - "\n", - ") | Fin(\n", - "\n", - ")\n", - "[Streett-like 1]\n", - "\n", - "\n", - "\n", - "0\n", - "\n", - "0,1\n", - "\n", - "\n", - "\n", - "I->0\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "0->0\n", - "\n", - "\n", - "!b & !d\n", - "\n", - "\n", - "\n", - "\n", - "0->0\n", - "\n", - "\n", - "!b & d\n", - "\n", - "\n", - "\n", - "1\n", - "\n", - "0,0\n", - "\n", - "\n", - "\n", - "0->1\n", - "\n", - "\n", - "b & !d\n", - "\n", - "\n", - "\n", - "\n", - "0->1\n", - "\n", - "\n", - "b & d\n", - "\n", - "\n", - "\n", - "1->1\n", - "\n", - "\n", - "!d\n", - "\n", - "\n", - "\n", - "1->1\n", - "\n", - "\n", - "d\n", - "\n", - "\n", - "\n", - "
" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "GFc\n", - "\n", - "Inf(\n", - "\n", - ")\n", - "[Büchi]\n", - "\n", - "\n", - "\n", - "0\n", - "\n", - "0\n", - "\n", - "\n", - "\n", - "I->0\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "0->0\n", - "\n", - "\n", - "!c\n", - "\n", - "\n", - "\n", - "0->0\n", - "\n", - "\n", - "c\n", - "\n", - "\n", - "\n", - "\n", - "
\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "(Inf(\n", - "\n", - ") | Fin(\n", - "\n", - ")) & Inf(\n", - "\n", - ")\n", - "[Streett-like 2]\n", - "\n", - "\n", - "\n", - "0\n", - "\n", - "0,0\n", - "\n", - "\n", - "\n", - "I->0\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "0->0\n", - "\n", - "\n", - "!c & !d\n", - "\n", - "\n", - "\n", - "\n", - "0->0\n", - "\n", - "\n", - "c & !d\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "0->0\n", - "\n", - "\n", - "!c & d\n", - "\n", - "\n", - "\n", - "0->0\n", - "\n", - "\n", - "c & d\n", - "\n", - "\n", - "\n", - "\n", - "
\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "Inf(\n", - "\n", - ") | Fin(\n", - "\n", - ") | Inf(\n", - "\n", - ")\n", - "[Rabin-like 3]\n", - "\n", - "\n", - "\n", - "0\n", - "\n", - "0,0\n", - "\n", - "\n", - "\n", - "I->0\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "0->0\n", - "\n", - "\n", - "!c & !d\n", - "\n", - "\n", - "\n", - "\n", - "0->0\n", - "\n", - "\n", - "c & !d\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "0->0\n", - "\n", - "\n", - "!c & d\n", - "\n", - "\n", - "\n", - "0->0\n", - "\n", - "\n", - "c & d\n", - "\n", - "\n", - "\n", - "\n", - "
" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "Inf(\n", - "\n", - ") & Fin(\n", - "\n", - ")\n", - "[Rabin-like 1]\n", - "\n", - "\n", - "\n", - "0\n", - "\n", - "0\n", - "\n", - "\n", - "\n", - "I->0\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "0->0\n", - "\n", - "\n", - "!d\n", - "\n", - "\n", - "\n", - "\n", - "0->0\n", - "\n", - "\n", - "d\n", - "\n", - "\n", - "\n", - "
\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "(Inf(\n", - "\n", - ") | Fin(\n", - "\n", - ")) & Inf(\n", - "\n", - ") & Fin(\n", - "\n", - ")\n", - "[Streett-like 3]\n", - "\n", - "\n", - "\n", - "0\n", - "\n", - "0,0\n", - "\n", - "\n", - "\n", - "I->0\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "0->0\n", - "\n", - "\n", - "!d\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "0->0\n", - "\n", - "\n", - "d\n", - "\n", - "\n", - "\n", - "
\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "Inf(\n", - "\n", - ") | Fin(\n", - "\n", - ") | (Inf(\n", - "\n", - ") & Fin(\n", - "\n", - "))\n", - "[Rabin-like 3]\n", - "\n", - "\n", - "\n", - "0\n", - "\n", - "0,0\n", - "\n", - "\n", - "\n", - "I->0\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "0->0\n", - "\n", - "\n", - "!d\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "0->0\n", - "\n", - "\n", - "d\n", - "\n", - "\n", - "\n", - "
" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "data": { - "text/html": [ - "
\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "Inf(\n", - "\n", - ") | Fin(\n", - "\n", - ")\n", - "[Streett-like 1]\n", - "\n", - "\n", - "\n", - "0\n", - "\n", - "0\n", - "\n", - "\n", - "\n", - "I->0\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "0->0\n", - "\n", - "\n", - "!d\n", - "\n", - "\n", - "\n", - "\n", - "0->0\n", - "\n", - "\n", - "d\n", - "\n", - "\n", - "\n", - "
\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "(Inf(\n", - "\n", - ") | Fin(\n", - "\n", - ")) & (Inf(\n", - "\n", - ") | Fin(\n", - "\n", - "))\n", - "[Streett-like 2]\n", - "\n", - "\n", - "\n", - "0\n", - "\n", - "0,0\n", - "\n", - "\n", - "\n", - "I->0\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "0->0\n", - "\n", - "\n", - "!d\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "0->0\n", - "\n", - "\n", - "d\n", - "\n", - "\n", - "\n", - "
\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "Inf(\n", - "\n", - ") | (Fin(\n", - "\n", - ")|Fin(\n", - "\n", - ")) | Inf(\n", - "\n", - ")\n", - "[Rabin-like 4]\n", - "\n", - "\n", - "\n", - "0\n", - "\n", - "0,0\n", - "\n", - "\n", - "\n", - "I->0\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "0->0\n", - "\n", - "\n", - "!d\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "0->0\n", - "\n", - "\n", - "d\n", - "\n", - "\n", - "\n", - "
" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - } - ], "source": [ "left = auts[5]\n", "display(left)\n", @@ -8134,7 +9304,7 @@ "\n" ], "text/plain": [ - " *' at 0x7fd90c347ba0> >" + " *' at 0x7f26743d3720> >" ] }, "metadata": {}, @@ -8197,11 +9367,11 @@ "\n", "\n", - "\n", - "\n", - "\n", - "[Büchi]\n", + "\n", + "\n", + "\n", + "[Büchi]\n", "\n", "\n", "\n", @@ -8263,11 +9433,11 @@ "\n", "\n", - "\n", - "\n", - "\n", - "[Büchi]\n", + "\n", + "\n", + "\n", + "[Büchi]\n", "\n", "\n", "\n", @@ -8344,12 +9514,12 @@ "\n", "\n", - "\n", - "\n", + "\n", + "\n", "Fb\n", - "\n", - "[Büchi]\n", + "\n", + "[Büchi]\n", "\n", "\n", "\n", @@ -8399,11 +9569,11 @@ "\n", "\n", - "\n", - "\n", - "\n", - "[Büchi]\n", + "\n", + "\n", + "\n", + "[Büchi]\n", "\n", "\n", "\n", @@ -8522,11 +9692,11 @@ "\n", "\n", - "\n", - "\n", - "\n", - "[Büchi]\n", + "\n", + "\n", + "\n", + "[Büchi]\n", "\n", "\n", "\n", @@ -8656,12 +9826,12 @@ "\n", "\n", - "\n", - "\n", + "\n", + "\n", "Fb\n", - "\n", - "[co-Büchi]\n", + "\n", + "[co-Büchi]\n", "\n", "\n", "\n", @@ -8711,11 +9881,11 @@ "\n", "\n", - "\n", - "\n", - "\n", - "[Büchi]\n", + "\n", + "\n", + "\n", + "[Büchi]\n", "\n", "\n", "\n", @@ -8834,11 +10004,11 @@ "\n", "\n", - "\n", - "\n", - "\n", - "[Büchi]\n", + "\n", + "\n", + "\n", + "[Büchi]\n", "\n", "\n", "\n", @@ -9241,6 +10411,288 @@ "metadata": {}, "output_type": "display_data" }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "FGd\n", + "\n", + "Fin(\n", + "\n", + ")\n", + "[co-Büchi]\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "!c\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "c\n", + "\n", + "\n", + "\n", + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "(Inf(\n", + "\n", + ")&Inf(\n", + "\n", + ")) | (Fin(\n", + "\n", + ") & Fin(\n", + "\n", + "))\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "1,0\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "1\n", + "\n", + "0,0\n", + "\n", + "\n", + "\n", + "0->1\n", + "\n", + "\n", + "a & !c\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "0->1\n", + "\n", + "\n", + "a & c\n", + "\n", + "\n", + "\n", + "\n", + "2\n", + "\n", + "2,0\n", + "\n", + "\n", + "\n", + "0->2\n", + "\n", + "\n", + "!a & !c\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "0->2\n", + "\n", + "\n", + "!a & c\n", + "\n", + "\n", + "\n", + "\n", + "1->1\n", + "\n", + "\n", + "!c\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "1->1\n", + "\n", + "\n", + "c\n", + "\n", + "\n", + "\n", + "\n", + "2->2\n", + "\n", + "\n", + "!c\n", + "\n", + "\n", + "\n", + "\n", + "2->2\n", + "\n", + "\n", + "c\n", + "\n", + "\n", + "\n", + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "(Inf(\n", + "\n", + ") & Fin(\n", + "\n", + ")) | (Fin(\n", + "\n", + ") & Inf(\n", + "\n", + "))\n", + "[Rabin-like 2]\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "1,0\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "1\n", + "\n", + "0,0\n", + "\n", + "\n", + "\n", + "0->1\n", + "\n", + "\n", + "a & !c\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "0->1\n", + "\n", + "\n", + "a & c\n", + "\n", + "\n", + "\n", + "\n", + "2\n", + "\n", + "2,0\n", + "\n", + "\n", + "\n", + "0->2\n", + "\n", + "\n", + "!a & !c\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "0->2\n", + "\n", + "\n", + "!a & c\n", + "\n", + "\n", + "\n", + "\n", + "1->1\n", + "\n", + "\n", + "!c\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "1->1\n", + "\n", + "\n", + "c\n", + "\n", + "\n", + "\n", + "\n", + "2->2\n", + "\n", + "\n", + "!c\n", + "\n", + "\n", + "\n", + "\n", + "2->2\n", + "\n", + "\n", + "c\n", + "\n", + "\n", + "\n", + "
" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, { "data": { "text/html": [ @@ -9785,12 +11237,12 @@ "\n", "\n", - "\n", - "\n", + "\n", + "\n", "Fb\n", - "\n", - "[Büchi]\n", + "\n", + "[Büchi]\n", "\n", "\n", "\n", @@ -9836,7 +11288,7 @@ "\n" ], "text/plain": [ - " *' at 0x7fd90c347bd0> >" + " *' at 0x7f26743d36c0> >" ] }, "metadata": {}, @@ -9899,11 +11351,11 @@ "\n", "\n", - "\n", - "\n", - "\n", - "[Büchi]\n", + "\n", + "\n", + "\n", + "[Büchi]\n", "\n", "\n", "\n", @@ -10022,11 +11474,11 @@ "\n", "\n", - "\n", - "\n", - "\n", - "[Büchi]\n", + "\n", + "\n", + "\n", + "[Büchi]\n", "\n", "\n", "\n", @@ -10156,12 +11608,12 @@ "\n", "\n", - "\n", - "\n", + "\n", + "\n", "Fb\n", - "\n", - "[Büchi]\n", + "\n", + "[Büchi]\n", "\n", "\n", "\n", @@ -10211,11 +11663,11 @@ "\n", "\n", - "\n", - "\n", - "\n", - "[Büchi]\n", + "\n", + "\n", + "\n", + "[Büchi]\n", "\n", "\n", "\n", @@ -10264,11 +11716,11 @@ "\n", "\n", - "\n", - "\n", - "\n", - "[Büchi]\n", + "\n", + "\n", + "\n", + "[Büchi]\n", "\n", "\n", "\n", @@ -10331,12 +11783,12 @@ "\n", "\n", - "\n", - "\n", + "\n", + "\n", "Fb\n", - "\n", - "[co-Büchi]\n", + "\n", + "[co-Büchi]\n", "\n", "\n", "\n", @@ -10386,11 +11838,11 @@ "\n", "\n", - "\n", - "\n", - "\n", - "[Büchi]\n", + "\n", + "\n", + "\n", + "[Büchi]\n", "\n", "\n", "\n", @@ -10439,11 +11891,11 @@ "\n", "\n", - "\n", - "\n", - "\n", - "[Büchi]\n", + "\n", + "\n", + "\n", + "[Büchi]\n", "\n", "\n", "\n", @@ -10729,6 +12181,238 @@ "metadata": {}, "output_type": "display_data" }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "FGd\n", + "\n", + "Fin(\n", + "\n", + ")\n", + "[co-Büchi]\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "!c\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "c\n", + "\n", + "\n", + "\n", + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "(Inf(\n", + "\n", + ")&Inf(\n", + "\n", + ")) | (Fin(\n", + "\n", + ") & Fin(\n", + "\n", + "))\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "1,0\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "!b & !c\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "!b & c\n", + "\n", + "\n", + "\n", + "1\n", + "\n", + "0,0\n", + "\n", + "\n", + "\n", + "0->1\n", + "\n", + "\n", + "b & !c\n", + "\n", + "\n", + "\n", + "\n", + "0->1\n", + "\n", + "\n", + "b & c\n", + "\n", + "\n", + "\n", + "1->1\n", + "\n", + "\n", + "!c\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "1->1\n", + "\n", + "\n", + "c\n", + "\n", + "\n", + "\n", + "\n", + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "(Inf(\n", + "\n", + ") & Fin(\n", + "\n", + ")) | (Fin(\n", + "\n", + ") & Inf(\n", + "\n", + "))\n", + "[Rabin-like 2]\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "1,0\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "!b & !c\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "!b & c\n", + "\n", + "\n", + "\n", + "1\n", + "\n", + "0,0\n", + "\n", + "\n", + "\n", + "0->1\n", + "\n", + "\n", + "b & !c\n", + "\n", + "\n", + "\n", + "\n", + "0->1\n", + "\n", + "\n", + "b & c\n", + "\n", + "\n", + "\n", + "1->1\n", + "\n", + "\n", + "!c\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "1->1\n", + "\n", + "\n", + "c\n", + "\n", + "\n", + "\n", + "\n", + "
" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, { "data": { "text/html": [ @@ -11189,12 +12873,12 @@ "\n", "\n", - "\n", - "\n", + "\n", + "\n", "Fb\n", - "\n", - "[co-Büchi]\n", + "\n", + "[co-Büchi]\n", "\n", "\n", "\n", @@ -11240,7 +12924,7 @@ "\n" ], "text/plain": [ - " *' at 0x7fd90c347cf0> >" + " *' at 0x7f26743d37e0> >" ] }, "metadata": {}, @@ -11303,11 +12987,11 @@ "\n", "\n", - "\n", - "\n", - "\n", - "[Büchi]\n", + "\n", + "\n", + "\n", + "[Büchi]\n", "\n", "\n", "\n", @@ -11426,11 +13110,11 @@ "\n", "\n", - "\n", - "\n", - "\n", - "[Büchi]\n", + "\n", + "\n", + "\n", + "[Büchi]\n", "\n", "\n", "\n", @@ -11560,12 +13244,12 @@ "\n", "\n", - "\n", - "\n", + "\n", + "\n", "Fb\n", - "\n", - "[Büchi]\n", + "\n", + "[Büchi]\n", "\n", "\n", "\n", @@ -11615,11 +13299,11 @@ "\n", "\n", - "\n", - "\n", - "\n", - "[Büchi]\n", + "\n", + "\n", + "\n", + "[Büchi]\n", "\n", "\n", "\n", @@ -11668,11 +13352,11 @@ "\n", "\n", - "\n", - "\n", - "\n", - "[Büchi]\n", + "\n", + "\n", + "\n", + "[Büchi]\n", "\n", "\n", "\n", @@ -11735,12 +13419,12 @@ "\n", "\n", - "\n", - "\n", + "\n", + "\n", "Fb\n", - "\n", - "[co-Büchi]\n", + "\n", + "[co-Büchi]\n", "\n", "\n", "\n", @@ -11790,11 +13474,11 @@ "\n", "\n", - "\n", - "\n", - "\n", - "[co-Büchi]\n", + "\n", + "\n", + "\n", + "[co-Büchi]\n", "\n", "\n", "\n", @@ -11845,11 +13529,11 @@ "\n", "\n", - "\n", - "\n", - "\n", - "[co-Büchi]\n", + "\n", + "\n", + "\n", + "[co-Büchi]\n", "\n", "\n", "\n", @@ -12137,6 +13821,242 @@ "metadata": {}, "output_type": "display_data" }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "FGd\n", + "\n", + "Fin(\n", + "\n", + ")\n", + "[co-Büchi]\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "!c\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "c\n", + "\n", + "\n", + "\n", + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "(Fin(\n", + "\n", + ") & Inf(\n", + "\n", + ")) | (Inf(\n", + "\n", + ") & Fin(\n", + "\n", + "))\n", + "[Rabin-like 2]\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "1,0\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "!b & !c\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "!b & c\n", + "\n", + "\n", + "\n", + "\n", + "1\n", + "\n", + "0,0\n", + "\n", + "\n", + "\n", + "0->1\n", + "\n", + "\n", + "b & !c\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "0->1\n", + "\n", + "\n", + "b & c\n", + "\n", + "\n", + "\n", + "\n", + "1->1\n", + "\n", + "\n", + "!c\n", + "\n", + "\n", + "\n", + "\n", + "1->1\n", + "\n", + "\n", + "c\n", + "\n", + "\n", + "\n", + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "(Fin(\n", + "\n", + ") & Fin(\n", + "\n", + ")) | (Inf(\n", + "\n", + ")&Inf(\n", + "\n", + "))\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "1,0\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "!b & !c\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "!b & c\n", + "\n", + "\n", + "\n", + "\n", + "1\n", + "\n", + "0,0\n", + "\n", + "\n", + "\n", + "0->1\n", + "\n", + "\n", + "b & !c\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "0->1\n", + "\n", + "\n", + "b & c\n", + "\n", + "\n", + "\n", + "\n", + "1->1\n", + "\n", + "\n", + "!c\n", + "\n", + "\n", + "\n", + "\n", + "1->1\n", + "\n", + "\n", + "c\n", + "\n", + "\n", + "\n", + "
" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, { "data": { "text/html": [ @@ -12638,7 +14558,7 @@ "\n" ], "text/plain": [ - " *' at 0x7fd90c347d50> >" + " *' at 0x7f26743d3870> >" ] }, "metadata": {}, @@ -12938,12 +14858,12 @@ "\n", "\n", - "\n", - "\n", + "\n", + "\n", "Fb\n", - "\n", - "[Büchi]\n", + "\n", + "[Büchi]\n", "\n", "\n", "\n", @@ -13180,12 +15100,12 @@ "\n", "\n", - "\n", - "\n", + "\n", + "\n", "Fb\n", - "\n", - "[co-Büchi]\n", + "\n", + "[co-Büchi]\n", "\n", "\n", "\n", @@ -13575,6 +15495,164 @@ "metadata": {}, "output_type": "display_data" }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "FGd\n", + "\n", + "Fin(\n", + "\n", + ")\n", + "[co-Büchi]\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "!c\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "c\n", + "\n", + "\n", + "\n", + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "(Inf(\n", + "\n", + ")&Inf(\n", + "\n", + ")) | (Fin(\n", + "\n", + ") & Fin(\n", + "\n", + "))\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0,0\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "!c\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "c\n", + "\n", + "\n", + "\n", + "\n", + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "(Inf(\n", + "\n", + ") & Fin(\n", + "\n", + ")) | (Fin(\n", + "\n", + ") & Inf(\n", + "\n", + "))\n", + "[Rabin-like 2]\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0,0\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "!c\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "c\n", + "\n", + "\n", + "\n", + "\n", + "
" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, { "data": { "text/html": [ @@ -13985,6 +16063,1562 @@ "metadata": { "scrolled": false }, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "FGd\n", + "\n", + "Fin(\n", + "\n", + ")\n", + "[co-Büchi]\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "!c\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "c\n", + "\n", + "\n", + "\n" + ], + "text/plain": [ + " *' at 0x7f26743d3900> >" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "a\n", + "\n", + "t\n", + "[all]\n", + "\n", + "\n", + "\n", + "1\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "I->1\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0\n", + "\n", + "\n", + "\n", + "1->0\n", + "\n", + "\n", + "a\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "(Fin(\n", + "\n", + ") & Fin(\n", + "\n", + ")) | (Inf(\n", + "\n", + ")&Inf(\n", + "\n", + "))\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0,1\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "1\n", + "\n", + "0,0\n", + "\n", + "\n", + "\n", + "0->1\n", + "\n", + "\n", + "a & !c\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "0->1\n", + "\n", + "\n", + "a & c\n", + "\n", + "\n", + "\n", + "\n", + "2\n", + "\n", + "0,2\n", + "\n", + "\n", + "\n", + "0->2\n", + "\n", + "\n", + "!a & !c\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "0->2\n", + "\n", + "\n", + "!a & c\n", + "\n", + "\n", + "\n", + "\n", + "1->1\n", + "\n", + "\n", + "!c\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "1->1\n", + "\n", + "\n", + "c\n", + "\n", + "\n", + "\n", + "\n", + "2->2\n", + "\n", + "\n", + "!c\n", + "\n", + "\n", + "\n", + "\n", + "2->2\n", + "\n", + "\n", + "c\n", + "\n", + "\n", + "\n", + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "(Fin(\n", + "\n", + ") & Inf(\n", + "\n", + ")) | (Inf(\n", + "\n", + ") & Fin(\n", + "\n", + "))\n", + "[Rabin-like 2]\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0,1\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "1\n", + "\n", + "0,0\n", + "\n", + "\n", + "\n", + "0->1\n", + "\n", + "\n", + "a & !c\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "0->1\n", + "\n", + "\n", + "a & c\n", + "\n", + "\n", + "\n", + "\n", + "2\n", + "\n", + "0,2\n", + "\n", + "\n", + "\n", + "0->2\n", + "\n", + "\n", + "!a & !c\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "0->2\n", + "\n", + "\n", + "!a & c\n", + "\n", + "\n", + "\n", + "\n", + "1->1\n", + "\n", + "\n", + "!c\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "1->1\n", + "\n", + "\n", + "c\n", + "\n", + "\n", + "\n", + "\n", + "2->2\n", + "\n", + "\n", + "!c\n", + "\n", + "\n", + "\n", + "\n", + "2->2\n", + "\n", + "\n", + "c\n", + "\n", + "\n", + "\n", + "
" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "Fb\n", + "\n", + "[Büchi]\n", + "\n", + "\n", + "\n", + "1\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "I->1\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "1->1\n", + "\n", + "\n", + "!b\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "\n", + "0\n", + "\n", + "\n", + "\n", + "1->0\n", + "\n", + "\n", + "b\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "(Fin(\n", + "\n", + ") & Fin(\n", + "\n", + ")) | (Inf(\n", + "\n", + ")&Inf(\n", + "\n", + "))\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0,1\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "!b & !c\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "!b & c\n", + "\n", + "\n", + "\n", + "1\n", + "\n", + "0,0\n", + "\n", + "\n", + "\n", + "0->1\n", + "\n", + "\n", + "b & !c\n", + "\n", + "\n", + "\n", + "\n", + "0->1\n", + "\n", + "\n", + "b & c\n", + "\n", + "\n", + "\n", + "1->1\n", + "\n", + "\n", + "!c\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "1->1\n", + "\n", + "\n", + "c\n", + "\n", + "\n", + "\n", + "\n", + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "(Fin(\n", + "\n", + ") & Inf(\n", + "\n", + ")) | (Inf(\n", + "\n", + ") & Fin(\n", + "\n", + "))\n", + "[Rabin-like 2]\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0,1\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "!b & !c\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "!b & c\n", + "\n", + "\n", + "\n", + "1\n", + "\n", + "0,0\n", + "\n", + "\n", + "\n", + "0->1\n", + "\n", + "\n", + "b & !c\n", + "\n", + "\n", + "\n", + "\n", + "0->1\n", + "\n", + "\n", + "b & c\n", + "\n", + "\n", + "\n", + "1->1\n", + "\n", + "\n", + "!c\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "1->1\n", + "\n", + "\n", + "c\n", + "\n", + "\n", + "\n", + "\n", + "
" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "Fb\n", + "\n", + "[co-Büchi]\n", + "\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "I->1\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "1->1\n", + "\n", + "\n", + "!b\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0\n", + "\n", + "\n", + "\n", + "1->0\n", + "\n", + "\n", + "b\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "(Fin(\n", + "\n", + ") & Inf(\n", + "\n", + ")) | (Inf(\n", + "\n", + ") & Fin(\n", + "\n", + "))\n", + "[Rabin-like 2]\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0,1\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "!b & !c\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "!b & c\n", + "\n", + "\n", + "\n", + "\n", + "1\n", + "\n", + "0,0\n", + "\n", + "\n", + "\n", + "0->1\n", + "\n", + "\n", + "b & !c\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "0->1\n", + "\n", + "\n", + "b & c\n", + "\n", + "\n", + "\n", + "\n", + "1->1\n", + "\n", + "\n", + "!c\n", + "\n", + "\n", + "\n", + "\n", + "1->1\n", + "\n", + "\n", + "c\n", + "\n", + "\n", + "\n", + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "(Fin(\n", + "\n", + ") & Fin(\n", + "\n", + ")) | (Inf(\n", + "\n", + ")&Inf(\n", + "\n", + "))\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0,1\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "!b & !c\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "!b & c\n", + "\n", + "\n", + "\n", + "\n", + "1\n", + "\n", + "0,0\n", + "\n", + "\n", + "\n", + "0->1\n", + "\n", + "\n", + "b & !c\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "0->1\n", + "\n", + "\n", + "b & c\n", + "\n", + "\n", + "\n", + "\n", + "1->1\n", + "\n", + "\n", + "!c\n", + "\n", + "\n", + "\n", + "\n", + "1->1\n", + "\n", + "\n", + "c\n", + "\n", + "\n", + "\n", + "
" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "GFc\n", + "\n", + "Inf(\n", + "\n", + ")\n", + "[Büchi]\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "!c\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "c\n", + "\n", + "\n", + "\n", + "\n", + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "(Fin(\n", + "\n", + ") & Fin(\n", + "\n", + ")) | (Inf(\n", + "\n", + ")&Inf(\n", + "\n", + "))\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0,0\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "!c\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "c\n", + "\n", + "\n", + "\n", + "\n", + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "(Fin(\n", + "\n", + ") & Inf(\n", + "\n", + ")) | (Inf(\n", + "\n", + ") & Fin(\n", + "\n", + "))\n", + "[Rabin-like 2]\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0,0\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "!c\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "c\n", + "\n", + "\n", + "\n", + "\n", + "
" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "FGd\n", + "\n", + "Fin(\n", + "\n", + ")\n", + "[co-Büchi]\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "!c\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "c\n", + "\n", + "\n", + "\n", + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "(Fin(\n", + "\n", + ") & Inf(\n", + "\n", + ")) | (Inf(\n", + "\n", + ") & Fin(\n", + "\n", + "))\n", + "[Rabin-like 2]\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0,0\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "!c\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "c\n", + "\n", + "\n", + "\n", + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "(Fin(\n", + "\n", + ") & Fin(\n", + "\n", + ")) | (Inf(\n", + "\n", + ")&Inf(\n", + "\n", + "))\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0,0\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "!c\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "c\n", + "\n", + "\n", + "\n", + "
" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "Inf(\n", + "\n", + ") & Fin(\n", + "\n", + ")\n", + "[Rabin-like 1]\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "!d\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "d\n", + "\n", + "\n", + "\n", + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "(Fin(\n", + "\n", + ") & (Fin(\n", + "\n", + ") | Inf(\n", + "\n", + "))) | ((Inf(\n", + "\n", + ")&Inf(\n", + "\n", + ")) & Fin(\n", + "\n", + "))\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0,0\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "!c & !d\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "!c & d\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "c & !d\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "c & d\n", + "\n", + "\n", + "\n", + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "(Fin(\n", + "\n", + ") & Inf(\n", + "\n", + ") & Fin(\n", + "\n", + ")) | (Inf(\n", + "\n", + ") & (Fin(\n", + "\n", + ") | Inf(\n", + "\n", + ")))\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0,0\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "!c & !d\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "!c & d\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "c & !d\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "c & d\n", + "\n", + "\n", + "\n", + "
" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "Inf(\n", + "\n", + ") | Fin(\n", + "\n", + ")\n", + "[Streett-like 1]\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "!d\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "d\n", + "\n", + "\n", + "\n", + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "(Fin(\n", + "\n", + ") & Fin(\n", + "\n", + ") & Inf(\n", + "\n", + ")) | (Inf(\n", + "\n", + ") & (Inf(\n", + "\n", + ") | Fin(\n", + "\n", + ")))\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0,0\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "!c & !d\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "!c & d\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "c & !d\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "c & d\n", + "\n", + "\n", + "\n", + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "(Fin(\n", + "\n", + ") & (Inf(\n", + "\n", + ") | Fin(\n", + "\n", + "))) | ((Inf(\n", + "\n", + ")&Inf(\n", + "\n", + ")) & Fin(\n", + "\n", + "))\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0,0\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "!c & !d\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "!c & d\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "c & !d\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "c & d\n", + "\n", + "\n", + "\n", + "
" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "left = auts[4]\n", + "display(left)\n", + "for right in auts:\n", + " display_inline(right, spot.product_xor(left, right), spot.product_xnor(left, right))" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": { + "scrolled": false + }, "outputs": [ { "data": { @@ -14037,7 +17671,7 @@ "\n" ], "text/plain": [ - " *' at 0x7fd90c347d80> >" + " *' at 0x7f26743d3990> >" ] }, "metadata": {}, @@ -14314,12 +17948,12 @@ "\n", "\n", - "\n", - "\n", + "\n", + "\n", "Fb\n", - "\n", - "[Büchi]\n", + "\n", + "[Büchi]\n", "\n", "\n", "\n", @@ -14541,12 +18175,12 @@ "\n", "\n", - "\n", - "\n", + "\n", + "\n", "Fb\n", - "\n", - "[co-Büchi]\n", + "\n", + "[co-Büchi]\n", "\n", "\n", "\n", @@ -14956,6 +18590,203 @@ "metadata": {}, "output_type": "display_data" }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "FGd\n", + "\n", + "Fin(\n", + "\n", + ")\n", + "[co-Büchi]\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "!c\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "c\n", + "\n", + "\n", + "\n", + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "((Inf(\n", + "\n", + ")&Inf(\n", + "\n", + ")) & Fin(\n", + "\n", + ")) | ((Fin(\n", + "\n", + ") | Inf(\n", + "\n", + ")) & Fin(\n", + "\n", + "))\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0,0\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "!c & !d\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "c & !d\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "!c & d\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "c & d\n", + "\n", + "\n", + "\n", + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "(Inf(\n", + "\n", + ") & Fin(\n", + "\n", + ") & Fin(\n", + "\n", + ")) | ((Fin(\n", + "\n", + ") | Inf(\n", + "\n", + ")) & Inf(\n", + "\n", + "))\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0,0\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "!c & !d\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "c & !d\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "!c & d\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "c & d\n", + "\n", + "\n", + "\n", + "
" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, { "data": { "text/html": [ @@ -15306,7 +19137,7 @@ } ], "source": [ - "left = auts[4]\n", + "left = auts[5]\n", "display(left)\n", "for right in auts:\n", " display_inline(right, spot.product_xor(left, right), spot.product_xnor(left, right))" @@ -15314,7 +19145,7 @@ }, { "cell_type": "code", - "execution_count": 15, + "execution_count": 16, "metadata": { "scrolled": false }, @@ -15370,7 +19201,7 @@ "\n" ], "text/plain": [ - " *' at 0x7fd90c347e40> >" + " *' at 0x7f26743d3a20> >" ] }, "metadata": {}, @@ -15433,46 +19264,115 @@ "\n", "\n", - "\n", - "\n", - "\n", - "Inf(\n", - "\n", - ")\n", - "[Büchi]\n", + "\n", + "\n", + "\n", + "Inf(\n", + "\n", + ")\n", + "[Büchi]\n", "\n", "\n", "\n", "0\n", - "\n", - "0,1\n", + "\n", + "0,1\n", "\n", "\n", "\n", "I->0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "1\n", - "\n", - "0,0\n", + "\n", + "0,0\n", "\n", "\n", "\n", "0->1\n", - "\n", - "\n", - "a & !d\n", + "\n", + "\n", + "a & !d\n", + "\n", + "\n", + "\n", + "\n", + "0->1\n", + "\n", + "\n", + "a & d\n", + "\n", + "\n", + "\n", + "\n", + "1->1\n", + "\n", + "\n", + "!d\n", + "\n", + "\n", + "\n", + "\n", + "1->1\n", + "\n", + "\n", + "d\n", + "\n", + "\n", + "\n", + "\n", + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "Inf(\n", + "\n", + ") | Fin(\n", + "\n", + ")\n", + "[Streett-like 1]\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0,1\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "1\n", + "\n", + "0,0\n", + "\n", + "\n", + "\n", + "0->1\n", + "\n", + "\n", + "a & !d\n", "\n", "\n", "\n", "0->1\n", - "\n", - "\n", - "a & d\n", + "\n", + "\n", + "a & d\n", "\n", "\n", "\n", @@ -15483,30 +19383,30 @@ "\n", "\n", "0->2\n", - "\n", - "\n", - "!a & !d\n", + "\n", + "\n", + "!a & !d\n", "\n", "\n", "\n", "0->2\n", - "\n", - "\n", - "!a & d\n", + "\n", + "\n", + "!a & d\n", "\n", "\n", "\n", "1->1\n", - "\n", - "\n", - "!d\n", + "\n", + "\n", + "!d\n", "\n", "\n", "\n", "1->1\n", - "\n", - "\n", - "d\n", + "\n", + "\n", + "d\n", "\n", "\n", "\n", @@ -15521,111 +19421,7 @@ "2->2\n", "\n", "\n", - "d\n", - "\n", - "\n", - "\n", - "\n", - "
\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "Inf(\n", - "\n", - ")\n", - "[Büchi]\n", - "\n", - "\n", - "\n", - "0\n", - "\n", - "0,1\n", - "\n", - "\n", - "\n", - "I->0\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "1\n", - "\n", - "0,0\n", - "\n", - "\n", - "\n", - "0->1\n", - "\n", - "\n", - "a & !d\n", - "\n", - "\n", - "\n", - "\n", - "0->1\n", - "\n", - "\n", - "a & d\n", - "\n", - "\n", - "\n", - "\n", - "2\n", - "\n", - "0,2\n", - "\n", - "\n", - "\n", - "0->2\n", - "\n", - "\n", - "!a & !d\n", - "\n", - "\n", - "\n", - "\n", - "0->2\n", - "\n", - "\n", - "!a & d\n", - "\n", - "\n", - "\n", - "\n", - "1->1\n", - "\n", - "\n", - "!d\n", - "\n", - "\n", - "\n", - "\n", - "1->1\n", - "\n", - "\n", - "d\n", - "\n", - "\n", - "\n", - "\n", - "2->2\n", - "\n", - "\n", - "!d\n", - "\n", - "\n", - "\n", - "2->2\n", - "\n", - "\n", - "d\n", + "d\n", "\n", "\n", "\n", @@ -15647,12 +19443,12 @@ "\n", "\n", - "\n", - "\n", + "\n", + "\n", "Fb\n", - "\n", - "[Büchi]\n", + "\n", + "[Büchi]\n", "\n", "\n", "\n", @@ -15702,87 +19498,6 @@ "\n", "\n", - "\n", - "\n", - "\n", - "Inf(\n", - "\n", - ")\n", - "[Büchi]\n", - "\n", - "\n", - "\n", - "0\n", - "\n", - "0,1\n", - "\n", - "\n", - "\n", - "I->0\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "0->0\n", - "\n", - "\n", - "!b & !d\n", - "\n", - "\n", - "\n", - "\n", - "0->0\n", - "\n", - "\n", - "!b & d\n", - "\n", - "\n", - "\n", - "\n", - "1\n", - "\n", - "0,0\n", - "\n", - "\n", - "\n", - "0->1\n", - "\n", - "\n", - "b & !d\n", - "\n", - "\n", - "\n", - "\n", - "0->1\n", - "\n", - "\n", - "b & d\n", - "\n", - "\n", - "\n", - "\n", - "1->1\n", - "\n", - "\n", - "!d\n", - "\n", - "\n", - "\n", - "1->1\n", - "\n", - "\n", - "d\n", - "\n", - "\n", - "\n", - "
\n", - "\n", - "\n", - "\n", "\n", "\n", @@ -15856,6 +19571,87 @@ "\n", "\n", "\n", + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "Inf(\n", + "\n", + ") | Fin(\n", + "\n", + ")\n", + "[Streett-like 1]\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0,1\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "!b & !d\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "!b & d\n", + "\n", + "\n", + "\n", + "1\n", + "\n", + "0,0\n", + "\n", + "\n", + "\n", + "0->1\n", + "\n", + "\n", + "b & !d\n", + "\n", + "\n", + "\n", + "\n", + "0->1\n", + "\n", + "\n", + "b & d\n", + "\n", + "\n", + "\n", + "1->1\n", + "\n", + "\n", + "!d\n", + "\n", + "\n", + "\n", + "1->1\n", + "\n", + "\n", + "d\n", + "\n", + "\n", + "\n", "
" ], "text/plain": [ @@ -15874,12 +19670,12 @@ "\n", "\n", - "\n", - "\n", + "\n", + "\n", "Fb\n", - "\n", - "[co-Büchi]\n", + "\n", + "[co-Büchi]\n", "\n", "\n", "\n", @@ -15929,87 +19725,6 @@ "\n", "\n", - "\n", - "\n", - "\n", - "Inf(\n", - "\n", - ")\n", - "[Büchi]\n", - "\n", - "\n", - "\n", - "0\n", - "\n", - "0,1\n", - "\n", - "\n", - "\n", - "I->0\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "0->0\n", - "\n", - "\n", - "!b & !d\n", - "\n", - "\n", - "\n", - "\n", - "0->0\n", - "\n", - "\n", - "!b & d\n", - "\n", - "\n", - "\n", - "\n", - "1\n", - "\n", - "0,0\n", - "\n", - "\n", - "\n", - "0->1\n", - "\n", - "\n", - "b & !d\n", - "\n", - "\n", - "\n", - "\n", - "0->1\n", - "\n", - "\n", - "b & d\n", - "\n", - "\n", - "\n", - "\n", - "1->1\n", - "\n", - "\n", - "!d\n", - "\n", - "\n", - "\n", - "1->1\n", - "\n", - "\n", - "d\n", - "\n", - "\n", - "\n", - "
\n", - "\n", - "\n", - "\n", "\n", "\n", @@ -16083,6 +19798,87 @@ "\n", "\n", "\n", + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "Inf(\n", + "\n", + ") | Fin(\n", + "\n", + ")\n", + "[Streett-like 1]\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0,1\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "!b & !d\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "!b & d\n", + "\n", + "\n", + "\n", + "1\n", + "\n", + "0,0\n", + "\n", + "\n", + "\n", + "0->1\n", + "\n", + "\n", + "b & !d\n", + "\n", + "\n", + "\n", + "\n", + "0->1\n", + "\n", + "\n", + "b & d\n", + "\n", + "\n", + "\n", + "1->1\n", + "\n", + "\n", + "!d\n", + "\n", + "\n", + "\n", + "1->1\n", + "\n", + "\n", + "d\n", + "\n", + "\n", + "\n", "
" ], "text/plain": [ @@ -16146,67 +19942,62 @@ "\n", "\n", - "\n", - "\n", - "\n", - "((Inf(\n", - "\n", - ") | Fin(\n", - "\n", - ")) & Fin(\n", - "\n", - ")) | (Fin(\n", - "\n", - ") & (Inf(\n", - "\n", - ")&Inf(\n", - "\n", - ")))\n", + "\n", + "\n", + "\n", + "(Inf(\n", + "\n", + ") | Fin(\n", + "\n", + ")) & Inf(\n", + "\n", + ")\n", + "[Streett-like 2]\n", "\n", "\n", "\n", "0\n", - "\n", - "0,0\n", + "\n", + "0,0\n", "\n", "\n", "\n", "I->0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "0->0\n", - "\n", - "\n", - "!c & !d\n", - "\n", + "\n", + "\n", + "!c & !d\n", + "\n", "\n", "\n", "\n", "0->0\n", - "\n", - "\n", - "c & !d\n", - "\n", - "\n", + "\n", + "\n", + "c & !d\n", + "\n", + "\n", "\n", "\n", "\n", "0->0\n", - "\n", - "\n", - "!c & d\n", + "\n", + "\n", + "!c & d\n", "\n", "\n", "\n", "0->0\n", - "\n", - "\n", - "c & d\n", - "\n", + "\n", + "\n", + "c & d\n", + "\n", "\n", "\n", "\n", @@ -16216,67 +20007,249 @@ "\n", "\n", - "\n", - "\n", - "\n", - "((Inf(\n", - "\n", - ") | Fin(\n", - "\n", - ")) & Inf(\n", - "\n", - ")) | (Fin(\n", - "\n", - ") & Inf(\n", - "\n", - ") & Fin(\n", - "\n", - "))\n", + "\n", + "\n", + "\n", + "Inf(\n", + "\n", + ") | Fin(\n", + "\n", + ") | Inf(\n", + "\n", + ")\n", + "[Rabin-like 3]\n", "\n", "\n", "\n", "0\n", - "\n", - "0,0\n", + "\n", + "0,0\n", "\n", "\n", "\n", "I->0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "0->0\n", - "\n", - "\n", - "!c & !d\n", - "\n", + "\n", + "\n", + "!c & !d\n", + "\n", "\n", "\n", "\n", "0->0\n", - "\n", - "\n", - "c & !d\n", - "\n", - "\n", + "\n", + "\n", + "c & !d\n", + "\n", + "\n", "\n", "\n", "\n", "0->0\n", - "\n", - "\n", - "!c & d\n", + "\n", + "\n", + "!c & d\n", "\n", "\n", "\n", "0->0\n", - "\n", - "\n", - "c & d\n", - "\n", + "\n", + "\n", + "c & d\n", + "\n", + "\n", + "\n", + "\n", + "
" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "FGd\n", + "\n", + "Fin(\n", + "\n", + ")\n", + "[co-Büchi]\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "!c\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "c\n", + "\n", + "\n", + "\n", + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "(Inf(\n", + "\n", + ") | Fin(\n", + "\n", + ")) & Fin(\n", + "\n", + ")\n", + "[Streett-like 2]\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0,0\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "!c & !d\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "c & !d\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "!c & d\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "c & d\n", + "\n", + "\n", + "\n", + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "Inf(\n", + "\n", + ") | (Fin(\n", + "\n", + ")|Fin(\n", + "\n", + "))\n", + "[Rabin-like 3]\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0,0\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "!c & !d\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "c & !d\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "!c & d\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "c & d\n", "\n", "\n", "\n", @@ -16344,55 +20317,48 @@ "\n", "\n", - "\n", - "\n", - "\n", - "((Inf(\n", - "\n", - ") | Fin(\n", - "\n", - ")) & (Fin(\n", - "\n", - ") | Inf(\n", - "\n", - "))) | (Fin(\n", - "\n", - ") & (Inf(\n", - "\n", - ")&Inf(\n", - "\n", - ")) & Fin(\n", - "\n", - "))\n", + "\n", + "\n", + "\n", + "(Inf(\n", + "\n", + ") | Fin(\n", + "\n", + ")) & Inf(\n", + "\n", + ") & Fin(\n", + "\n", + ")\n", + "[Streett-like 3]\n", "\n", "\n", "\n", "0\n", - "\n", - "0,0\n", + "\n", + "0,0\n", "\n", "\n", "\n", "I->0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "0->0\n", - "\n", - "\n", - "!d\n", - "\n", - "\n", + "\n", + "\n", + "!d\n", + "\n", + "\n", "\n", "\n", "\n", "0->0\n", - "\n", - "\n", - "d\n", + "\n", + "\n", + "d\n", "\n", "\n", "\n", @@ -16402,55 +20368,48 @@ "\n", "\n", - "\n", - "\n", - "\n", - "((Inf(\n", - "\n", - ") | Fin(\n", - "\n", - ")) & Inf(\n", - "\n", - ") & Fin(\n", - "\n", - ")) | (Fin(\n", - "\n", - ") & Inf(\n", - "\n", - ") & (Fin(\n", - "\n", - ") | Inf(\n", - "\n", - ")))\n", + "\n", + "\n", + "\n", + "Inf(\n", + "\n", + ") | Fin(\n", + "\n", + ") | (Inf(\n", + "\n", + ") & Fin(\n", + "\n", + "))\n", + "[Rabin-like 3]\n", "\n", "\n", "\n", "0\n", - "\n", - "0,0\n", + "\n", + "0,0\n", "\n", "\n", "\n", "I->0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "0->0\n", - "\n", - "\n", - "!d\n", - "\n", - "\n", + "\n", + "\n", + "!d\n", + "\n", + "\n", "\n", "\n", "\n", "0->0\n", - "\n", - "\n", - "d\n", + "\n", + "\n", + "d\n", "\n", "\n", "\n", @@ -16518,55 +20477,48 @@ "\n", "\n", - "\n", - "\n", - "\n", - "((Inf(\n", - "\n", - ") | Fin(\n", - "\n", - ")) & Fin(\n", - "\n", - ") & Inf(\n", - "\n", - ")) | (Fin(\n", - "\n", - ") & Inf(\n", - "\n", - ") & (Inf(\n", - "\n", - ") | Fin(\n", - "\n", - ")))\n", + "\n", + "\n", + "\n", + "(Inf(\n", + "\n", + ") | Fin(\n", + "\n", + ")) & (Inf(\n", + "\n", + ") | Fin(\n", + "\n", + "))\n", + "[Streett-like 2]\n", "\n", "\n", "\n", "0\n", - "\n", - "0,0\n", + "\n", + "0,0\n", "\n", "\n", "\n", "I->0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "0->0\n", - "\n", - "\n", - "!d\n", - "\n", - "\n", + "\n", + "\n", + "!d\n", + "\n", + "\n", "\n", "\n", "\n", "0->0\n", - "\n", - "\n", - "d\n", + "\n", + "\n", + "d\n", "\n", "\n", "\n", @@ -16576,55 +20528,48 @@ "\n", "\n", - "\n", - "\n", - "\n", - "((Inf(\n", - "\n", - ") | Fin(\n", - "\n", - ")) & (Inf(\n", - "\n", - ") | Fin(\n", - "\n", - "))) | (Fin(\n", - "\n", - ") & (Inf(\n", - "\n", - ")&Inf(\n", - "\n", - ")) & Fin(\n", - "\n", - "))\n", + "\n", + "\n", + "\n", + "Inf(\n", + "\n", + ") | (Fin(\n", + "\n", + ")|Fin(\n", + "\n", + ")) | Inf(\n", + "\n", + ")\n", + "[Rabin-like 4]\n", "\n", "\n", "\n", "0\n", - "\n", - "0,0\n", + "\n", + "0,0\n", "\n", "\n", "\n", "I->0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "\n", "0->0\n", - "\n", - "\n", - "!d\n", - "\n", - "\n", + "\n", + "\n", + "!d\n", + "\n", + "\n", "\n", "\n", "\n", "0->0\n", - "\n", - "\n", - "d\n", + "\n", + "\n", + "d\n", "\n", "\n", "\n", @@ -16639,16 +20584,317 @@ } ], "source": [ - "left = auts[5]\n", + "left = auts[6]\n", "display(left)\n", "for right in auts:\n", - " display_inline(right, spot.product_xor(left, right), spot.product_xnor(left, right))" + " display_inline(right, spot.product(left, right), spot.product_or(left, right))" ] + }, + { + "cell_type": "code", + "execution_count": 17, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "Inf(\n", + "\n", + ")&Inf(\n", + "\n", + ")\n", + "[gen. Büchi 2]\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0,0\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "!a & !b\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "!a & b\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "a & !b\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "a & b\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "Inf(\n", + "\n", + ")\n", + "[Büchi]\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0,0\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "!a & !b\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "!a & b\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "a & !b\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "a & b\n", + "\n", + "\n", + "\n", + "\n", + "
" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "a1 = spot.translate('GFa')\n", + "a2 = spot.translate('GFb')\n", + "display_inline(spot.product(a1,a2), spot.product_or(a1, a2))" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "Fin(\n", + "\n", + ")\n", + "[co-Büchi]\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0,0\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "!a & !b\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "!a & b\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "a & !b\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "a & b\n", + "\n", + "\n", + "\n", + "\n", + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "Fin(\n", + "\n", + ")|Fin(\n", + "\n", + ")\n", + "[gen. co-Büchi 2]\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0,0\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "!a & !b\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "!a & b\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "a & !b\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "a & b\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "
" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "a1 = spot.dualize(a1)\n", + "a2 = spot.dualize(a2)\n", + "display_inline(spot.product(a1,a2), spot.product_or(a1, a2))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] } ], "metadata": { "kernelspec": { - "display_name": "Python 3", + "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, @@ -16662,7 +20908,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.3rc1" + "version": "3.10.6" } }, "nbformat": 4, From fe3ebd370b5fae9cf3ae07dc3154497348a5c707 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Wed, 7 Sep 2022 09:59:31 +0200 Subject: [PATCH 129/337] add the TACAS'22 reference * doc/org/citing.org, doc/spot.bib: There. --- doc/org/citing.org | 10 ++++++++-- doc/spot.bib | 16 ++++++++++++++++ 2 files changed, 24 insertions(+), 2 deletions(-) diff --git a/doc/org/citing.org b/doc/org/citing.org index 8cc1f52ef..8d669ae69 100644 --- a/doc/org/citing.org +++ b/doc/org/citing.org @@ -80,12 +80,18 @@ be more specific about a particular aspect of Spot. - *Generic Emptiness Check for Fun and Profit*, /Christel Baier/, /František Blahoudek/, /Alexandre Duret-Lutz/, /Joachim Klein/, /David Müller/, and /Jan Strejček/. - In. Proc. of ATVA'19, LNCS 11781, pp. 11781, Oct 2019. ([[https://www.lrde.epita.fr/~adl/dl/adl_bib.html#baier.19.atva][bib]] | [[https://www.lrde.epita.fr/~adl/dl/adl/baier.19.atva.pdf][pdf]] | + In. Proc. of ATVA'19, LNCS 11781, pp. 445--461, Oct 2019. ([[https://www.lrde.epita.fr/~adl/dl/adl_bib.html#baier.19.atva][bib]] | [[https://www.lrde.epita.fr/~adl/dl/adl/baier.19.atva.pdf][pdf]] | [[https://www.lrde.epita.fr/~adl/dl/adl/baier.19.atva.slides.mefosyloma.pdf][slides1]] | [[https://www.lrde.epita.fr/~adl/dl/adl/baier.19.atva.slides.pdf][slides2]]) Presents the generic emptiness-check implemented in Spot. -* Obsolete reference +- *Practical Applications of the Alternating Cycle Decomposition*, + /Antonio Casares/, /Alexandre Duret-Lutz/, /Klara J. Meyer/, /Florian Renkin/, + and /Salomon Sickert/. + In. Proc. of TACAS'22, LNCS 13244, pp. 99--117, Apr 2022. ([[https://www.lrde.epita.fr/~adl/dl/adl_bib.html#casares.22.tacas][bib]] | [[https://www.lrde.epita.fr/~adl/dl/adl/casares.22.tacas.pdf][pdf]] | + [[https://www.lrde.epita.fr/~adl/dl/adl/casares.22.tacas.slides.pdf][slides1]] | [[https://www.lrde.epita.fr/~adl/dl/adl/casares.22.tacas.slides2.pdf][slides2]]) + +* Obsolete references - *Spot 2.0 — a framework for LTL and ω-automata manipulation*, /Alexandre Duret-Lutz/, /Alexandre Lewkowicz/, /Amaury Fauchille/, diff --git a/doc/spot.bib b/doc/spot.bib index 284bf226a..6193cb1a2 100644 --- a/doc/spot.bib +++ b/doc/spot.bib @@ -172,6 +172,22 @@ doi = {10.4230/LIPIcs.ICALP.2021.123} } +@InProceedings{ casares.22.tacas, + author = {Antonio Casares and Alexandre Duret-Lutz and Klara J. + Meyer and Florian Renkin and Salomon Sickert}, + title = {Practical Applications of the {A}lternating {C}ycle + {D}ecomposition}, + booktitle = {Proceedings of the 28th International Conference on Tools + and Algorithms for the Construction and Analysis of + Systems}, + year = {2022}, + series = {Lecture Notes in Computer Science}, + month = apr, + volume = {13244}, + pages = {99--117}, + doi = {10.1007/978-3-030-99527-0_6}, +} + @InProceedings{ cerna.03.mfcs, author = {Ivana {\v{C}}ern{\'a} and Radek Pel{\'a}nek}, title = {Relating Hierarchy of Temporal Properties to Model From bdac53511addb0890f7425d37e7e50854af45e96 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Wed, 7 Sep 2022 14:36:23 +0200 Subject: [PATCH 130/337] =?UTF-8?q?degen:=20learn=20to=20work=20on=20gener?= =?UTF-8?q?alized-Co-B=C3=BCchi=20as=20well?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * spot/twaalgos/degen.hh, spot/twaalgos/degen.cc: Adjust degeneralize() and degeneralize_tba() to work on generalized-co-Büchi. * NEWS: Mention this. * spot/twaalgos/cobuchi.hh, spot/twaalgos/cobuchi.cc (to_nca): Use degeneralization on generalized-co-Büchi. * spot/twaalgos/postproc.cc: Use degeneralization for generalized co-Büchi as well. * bin/autfilt.cc: Improve chain products of co-Büchi automata by using generalization if too many colors are needed. * tests/core/prodchain.test, tests/python/pdegen.py: Add test cases. --- NEWS | 3 +++ bin/autfilt.cc | 32 +++++++++++++--------- spot/twaalgos/cobuchi.cc | 16 +++++++---- spot/twaalgos/cobuchi.hh | 9 ++++--- spot/twaalgos/degen.cc | 56 ++++++++++++++++++++++++++++++--------- spot/twaalgos/degen.hh | 55 +++++++++++++++++++++++--------------- spot/twaalgos/postproc.cc | 29 +++++++++++++++----- tests/core/prodchain.test | 35 ++++++++++++------------ tests/python/pdegen.py | 15 ++++++++++- 9 files changed, 169 insertions(+), 81 deletions(-) diff --git a/NEWS b/NEWS index 66f56e75d..38beaa062 100644 --- a/NEWS +++ b/NEWS @@ -114,6 +114,9 @@ New in spot 2.10.6.dev (not yet released) to obtain a simple model checker (that returns true or false, without counterexample). + - degeneralize() and degeneralize_tba() learned to work on + generalized-co-Büchi as well. + - product() learned that the product of two co-Büchi automata is a co-Büchi automaton. And product_or() learned that the "or"-product of two Büchi automata is a Büchi automaton. diff --git a/bin/autfilt.cc b/bin/autfilt.cc index 74fe44220..49543e596 100644 --- a/bin/autfilt.cc +++ b/bin/autfilt.cc @@ -713,10 +713,12 @@ ensure_deterministic(const spot::twa_graph_ptr& aut, bool nonalt = false) return p.run(aut); } -static spot::twa_graph_ptr ensure_tba(spot::twa_graph_ptr aut) +static spot::twa_graph_ptr +ensure_tba(spot::twa_graph_ptr aut, + spot::postprocessor::output_type type = spot::postprocessor::Buchi) { spot::postprocessor p; - p.set_type(spot::postprocessor::Buchi); + p.set_type(type); p.set_pref(spot::postprocessor::Any); p.set_level(spot::postprocessor::Low); return p.run(aut); @@ -726,12 +728,14 @@ static spot::twa_graph_ptr ensure_tba(spot::twa_graph_ptr aut) static spot::twa_graph_ptr product(spot::twa_graph_ptr left, spot::twa_graph_ptr right) { - if ((type == spot::postprocessor::Buchi) - && (left->num_sets() + right->num_sets() > - spot::acc_cond::mark_t::max_accsets())) + // Are we likely to fail because of too many colors? + if ((left->num_sets() + right->num_sets() > + spot::acc_cond::mark_t::max_accsets()) + && (type == spot::postprocessor::Buchi + || type == spot::postprocessor::CoBuchi)) { - left = ensure_tba(left); - right = ensure_tba(right); + left = ensure_tba(left, type); + right = ensure_tba(right, type); } return spot::product(left, right); } @@ -739,12 +743,14 @@ product(spot::twa_graph_ptr left, spot::twa_graph_ptr right) static spot::twa_graph_ptr product_or(spot::twa_graph_ptr left, spot::twa_graph_ptr right) { - if ((type == spot::postprocessor::Buchi) - && (left->num_sets() + right->num_sets() > - spot::acc_cond::mark_t::max_accsets())) + // Are we likely to fail because of too many colors? + if ((left->num_sets() + right->num_sets() > + spot::acc_cond::mark_t::max_accsets()) + && (type == spot::postprocessor::Buchi + || type == spot::postprocessor::CoBuchi)) { - left = ensure_tba(left); - right = ensure_tba(right); + left = ensure_tba(left, type); + right = ensure_tba(right, type); } return spot::product_or(left, right); } @@ -988,7 +994,7 @@ parse_opt(int key, char* arg, struct argp_state*) if (!opt->included_in) opt->included_in = aut; else - opt->included_in = spot::product_or(opt->included_in, aut); + opt->included_in = ::product_or(opt->included_in, aut); } break; case OPT_INHERENTLY_WEAK_SCCS: diff --git a/spot/twaalgos/cobuchi.cc b/spot/twaalgos/cobuchi.cc index 783cd0903..23d4871a0 100644 --- a/spot/twaalgos/cobuchi.cc +++ b/spot/twaalgos/cobuchi.cc @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2017-2018, 2021 Laboratoire de Recherche et Développement +// Copyright (C) 2017-2018, 2021, 2022 Laboratoire de Recherche et Développement // de l'Epita (LRDE). // // This file is part of Spot, a model checking library. @@ -26,6 +26,7 @@ #include #include #include +#include #include #include #include @@ -338,23 +339,26 @@ namespace spot twa_graph_ptr to_nca(const_twa_graph_ptr aut, bool named_states) { - if (aut->acc().is_co_buchi()) + const acc_cond& acc = aut->acc(); + if (acc.is_co_buchi()) return make_twa_graph(aut, twa::prop_set::all()); if (auto weak = weak_to_cobuchi(aut)) return weak; + if (acc.is_generalized_co_buchi()) + return degeneralize_tba(aut); + const acc_cond::acc_code& code = aut->get_acceptance(); std::vector pairs; - if (aut->acc().is_streett_like(pairs) || aut->acc().is_parity()) + if (acc.is_streett_like(pairs) || acc.is_parity()) return nsa_to_nca(aut, named_states); else if (code.is_dnf()) return dnf_to_nca(aut, named_states); auto tmp = make_twa_graph(aut, twa::prop_set::all()); - tmp->set_acceptance(aut->acc().num_sets(), - aut->get_acceptance().to_dnf()); + tmp->set_acceptance(acc.num_sets(), code.to_dnf()); return to_nca(tmp, named_states); } @@ -683,6 +687,8 @@ namespace spot return make_twa_graph(aut, twa::prop_set::all()); if (auto weak = weak_to_cobuchi(aut)) return weak; + if (aut->acc().is_generalized_co_buchi()) + return degeneralize_tba(aut); } const acc_cond::acc_code& code = aut->get_acceptance(); diff --git a/spot/twaalgos/cobuchi.hh b/spot/twaalgos/cobuchi.hh index 5c8d85e59..b02c0535d 100644 --- a/spot/twaalgos/cobuchi.hh +++ b/spot/twaalgos/cobuchi.hh @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2017-2019 Laboratoire de Recherche et Développement +// Copyright (C) 2017-2019, 2022 Laboratoire de Recherche et Développement // de l'Epita (LRDE). // // This file is part of Spot, a model checking library. @@ -92,8 +92,8 @@ namespace spot /// original language, and is a superset iff the original language /// can not be expressed using a co-Büchi acceptance condition. /// - /// The implementation dispatches between dnf_to_nca, nsa_to_nca, - /// and a trivial implementation for weak automata. + /// The implementation dispatches between dnf_to_nca(), nsa_to_nca(), + /// degeneralize_tba(), and a trivial implementation for weak automata. SPOT_API twa_graph_ptr to_nca(const_twa_graph_ptr aut, bool named_states = false); @@ -126,7 +126,8 @@ namespace spot /// can not be expressed using a co-Büchi acceptance condition. /// /// The implementation dispatches between dnf_to_dca, nsa_to_dca, - /// and a trivial implementation for deterministic weak automata. + /// degeneralize(), and a trivial implementation for deterministic + /// weak automata. SPOT_API twa_graph_ptr to_dca(const_twa_graph_ptr aut, bool named_states = false); } diff --git a/spot/twaalgos/degen.cc b/spot/twaalgos/degen.cc index 333efe6e6..d79844b84 100644 --- a/spot/twaalgos/degen.cc +++ b/spot/twaalgos/degen.cc @@ -80,7 +80,8 @@ namespace spot void fill_cache(unsigned s) { unsigned s1 = scc_of(s); - acc_cond::mark_t common = a_->acc().all_sets(); + acc_cond::mark_t all_colors = a_->acc().all_sets(); + acc_cond::mark_t common = all_colors; acc_cond::mark_t union_ = {}; bool has_acc_self_loop = false; bool is_true_state = false; @@ -97,7 +98,7 @@ namespace spot std::get<2>(cache_[d]) &= t.acc; // an accepting self-loop? - if ((t.dst == s) && a_->acc().accepting(t.acc)) + if ((t.dst == s) && t.acc == all_colors) { has_acc_self_loop = true; if (t.cond == bddtrue) @@ -330,9 +331,10 @@ namespace spot bool skip_levels, bool ignaccsl, bool remove_extra_scc) { - if (!a->acc().is_generalized_buchi()) + bool input_is_gba = a->acc().is_generalized_buchi(); + if (!(input_is_gba || a->acc().is_generalized_co_buchi())) throw std::runtime_error - ("degeneralize() only works with generalized Büchi acceptance"); + ("degeneralize() only works with generalized (co)Büchi acceptance"); if (!a->is_existential()) throw std::runtime_error ("degeneralize() does not support alternation"); @@ -347,7 +349,11 @@ namespace spot // The result automaton is an SBA. auto res = make_twa_graph(dict); res->copy_ap_of(a); - res->set_buchi(); + if (input_is_gba) + res->set_buchi(); + else + res->set_co_buchi(); + acc_cond::mark_t all_colors = a->get_acceptance().used_sets(); if (want_sba) res->prop_state_acc(true); // Preserve determinism, weakness, and stutter-invariance @@ -396,9 +402,32 @@ namespace spot std::vector> lvl_cache(a->num_states()); // Compute SCCs in order to use any optimization. - std::unique_ptr m = use_scc - ? std::make_unique(a, scc_info_options::NONE) - : nullptr; + std::unique_ptr m = nullptr; + if (use_scc) + { + if (!input_is_gba) + { + // If the input is gen-co-Büchi, temporary pretend its + // generalized Büchi. + unsigned n = a->num_sets(); + twa_graph_ptr amut = std::const_pointer_cast(a); + amut->set_generalized_buchi(n); + try + { + m = std::make_unique(a, scc_info_options::NONE); + } + catch (...) + { + amut->set_generalized_co_buchi(n); + throw; + } + amut->set_generalized_co_buchi(n); + } + else + { + m = std::make_unique(a, scc_info_options::NONE); + } + } // Initialize scc_orders std::unique_ptr orders = use_cust_acc_orders @@ -674,7 +703,7 @@ namespace spot { d.second = 0; // Make it go to the first level. // Skip as many levels as possible. - if (!a->acc().accepting(acc) && skip_levels) + if (acc != all_colors && skip_levels) { if (use_cust_acc_orders) { @@ -723,9 +752,10 @@ namespace spot int use_lvl_cache, bool skip_levels, bool ignaccsl, bool remove_extra_scc) { - // If this already a degeneralized digraph, there is nothing we + // If this already a degeneralized twa, there is nothing we // can improve. - if (a->is_sba()) + if (const acc_cond& acc = a->acc(); + a->prop_state_acc() && (acc.is_buchi() || acc.is_co_buchi())) return std::const_pointer_cast(a); return degeneralize_aux(a, use_z_lvl, use_cust_acc_orders, @@ -739,9 +769,9 @@ namespace spot int use_lvl_cache, bool skip_levels, bool ignaccsl, bool remove_extra_scc) { - // If this already a degeneralized digraph, there is nothing we + // If this already a degeneralized twa, there is nothing we // can improve. - if (a->acc().is_buchi()) + if (a->acc().is_buchi() || a->acc().is_co_buchi()) return std::const_pointer_cast(a); return degeneralize_aux(a, use_z_lvl, use_cust_acc_orders, diff --git a/spot/twaalgos/degen.hh b/spot/twaalgos/degen.hh index 281ba2ef5..e9ae13021 100644 --- a/spot/twaalgos/degen.hh +++ b/spot/twaalgos/degen.hh @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2012-2015, 2017-2020 Laboratoire de +// Copyright (C) 2012-2015, 2017-2020, 2022 Laboratoire de // Recherche et Développement de l'Epita. // // This file is part of Spot, a model checking library. @@ -26,33 +26,36 @@ namespace spot class scc_info; /// \ingroup twa_acc_transform - /// \brief Degeneralize a spot::tgba into an equivalent sba with - /// only one acceptance condition. + /// \brief Degeneralize a generalized (co)Büchi automaton into an + /// equivalent (co)Büchi automaton. /// - /// This algorithm will build a new explicit automaton that has - /// at most (N+1) times the number of states of the original automaton. + /// There are two variants of the function. If the generalizd + /// (co)Büchi acceptance uses N colors, degeneralize() algorithm + /// will builds a state-based (co)Büchi automaton that has at most + /// (N+1) times the number of states of the original automaton. + /// degeneralize_tba() builds a transition-based (co)Büchi automaton + /// that has at most N times the number of states of the original + /// automaton. /// - /// When \a use_z_lvl is set, the level of the degeneralized - /// automaton is reset everytime an SCC is exited. If \a - /// use_cust_acc_orders is set, the degeneralization will compute a - /// custom acceptance order for each SCC (this option is disabled by - /// default because our benchmarks show that it usually does more - /// harm than good). If \a use_lvl_cache is set, everytime an SCC - /// is entered on a state that as already been associated to some - /// level elsewhere, reuse that level (set it to 2 to keep the - /// smallest number, 3 to keep the largest level, and 1 to keep the - /// first level found). If \a ignaccsl is set, we do not directly - /// jump to the accepting level if the entering state has an - /// accepting self-loop. If \a remove_extra_scc is set (the default) - /// we ensure that the output automaton has as many SCCs as the input - /// by removing superfluous SCCs. + /// Additional options control optimizations described in + /// \cite babiak.13.spin . When \a use_z_lvl is set, the level of + /// the degeneralized automaton is reset everytime an SCC is exited. + /// If \a use_cust_acc_orders is set, the degeneralization will + /// compute a custom acceptance order for each SCC (this option is + /// disabled by default because our benchmarks show that it usually + /// does more harm than good). If \a use_lvl_cache is set, + /// everytime an SCC is entered on a state that as already been + /// associated to some level elsewhere, reuse that level (set it to + /// 2 to keep the smallest number, 3 to keep the largest level, and + /// 1 to keep the first level found). If \a ignaccsl is set, we do + /// not directly jump to the accepting level if the entering state + /// has an accepting self-loop. If \a remove_extra_scc is set (the + /// default) we ensure that the output automaton has as many SCCs as + /// the input by removing superfluous SCCs. /// /// Any of these three options will cause the SCCs of the automaton /// \a a to be computed prior to its actual degeneralization. /// - /// The degeneralize_tba() variant produce a degeneralized automaton - /// with transition-based acceptance. - /// /// The mapping between each state of the resulting automaton /// and the original state of the input automaton is stored in the /// "original-states" named property of the produced automaton. Call @@ -70,6 +73,14 @@ namespace spot /// Similarly, the property "degen-levels" keeps track of the degeneralization /// levels. To retrieve it, call /// `aut->get_named_prop>("degen-levels")`. + /// + /// As an alternative method to degeneralization, one may also + /// consider ACD transform. acd_transform() will never produce + /// larger automata than degenaralize_tba(), and + /// acd_transform_sbacc() produce smaller automata than + /// degeneralize() on the average. See \cite casares.22.tacas for + /// some comparisons. + /// /// \@{ SPOT_API twa_graph_ptr degeneralize(const const_twa_graph_ptr& a, bool use_z_lvl = true, diff --git a/spot/twaalgos/postproc.cc b/spot/twaalgos/postproc.cc index a44ac3d52..39a6c0926 100644 --- a/spot/twaalgos/postproc.cc +++ b/spot/twaalgos/postproc.cc @@ -236,7 +236,8 @@ namespace spot if (COMP_) tmp = complete(tmp); bool want_parity = type_ & Parity; - if (want_parity && tmp->acc().is_generalized_buchi()) + if (want_parity && (tmp->acc().is_generalized_buchi() + || tmp->acc().is_generalized_co_buchi())) tmp = choose_degen(tmp); assert(!!SBACC_ == state_based_); if (state_based_) @@ -402,10 +403,19 @@ namespace spot if (PREF_ == Any) { - if (type_ == Buchi) - a = choose_degen(a); + if (type_ == Buchi + || (type_ == CoBuchi && a->acc().is_generalized_co_buchi())) + { + a = choose_degen(a); + } else if (type_ == CoBuchi) - a = to_nca(a); + { + a = to_nca(a); + if (state_based_ && a->prop_state_acc().is_true()) + a = do_sba_simul(a, simul_); + else + a = do_simul(a, simul_); + } return finalize(a); } @@ -699,6 +709,8 @@ namespace spot if (type_ == CoBuchi) { unsigned ns = sim->num_states(); + bool weak = sim->prop_weak().is_true(); + if (PREF_ == Deterministic) sim = to_dca(sim); else @@ -706,8 +718,13 @@ namespace spot // if the input of to_dca/to_nca was weak, the number of // states has not changed, and running simulation is useless. - if (level_ != Low && ns < sim->num_states()) - sim = do_simul(sim, simul_); + if (!weak || (level_ != Low && ns < sim->num_states())) + { + if (state_based_ && sim->prop_state_acc().is_true()) + sim = do_sba_simul(sim, simul_); + else + sim = do_simul(sim, simul_); + } } return finalize(sim); diff --git a/tests/core/prodchain.test b/tests/core/prodchain.test index 9a9c74648..c2d6091c7 100755 --- a/tests/core/prodchain.test +++ b/tests/core/prodchain.test @@ -26,9 +26,7 @@ shift for i in 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 \ 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42; do ltl2tgba "{a[*$i]}[]->GFb" > $i.hoa -done -for i in *.hoa; do - set x "$@" --product $i + set x "$@" --product $i.hoa shift done shift @@ -41,8 +39,9 @@ test "4,7,16,1" = `autfilt --stats=%s,%e,%t,%a result` set x shift -for i in *.hoa; do - set x "$@" --product-or $i +for i in 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 \ + 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42; do + set x "$@" --product-or $i.hoa shift done shift @@ -55,28 +54,30 @@ shift for i in 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 \ 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42; do ltl2tgba -D --cobuchi -S "{a[*$i]}<>->FGb" > $i.hoa -done -for i in *.hoa; do - set x "$@" --product $i + set x "$@" --product $i.hoa shift done shift -autfilt -D --cobuchi --low -S "$@" > result -test "85,170,174,1" = `autfilt --stats=%s,%e,%t,%a result` +autfilt --cobuchi --high -D -S "$@" > result +test "44,47,92,1" = `autfilt --stats=%s,%e,%t,%a result` +: > stats set x shift -for i in *.hoa; do - set x "$@" --product-or $i +for i in 01 02 03 04 05 06 07 08 09 10 11 12 13 14 15 16 17 18 19 20 21 \ + 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42; do + ltl2tgba -D --cobuchi "{a[*$i]}<>->FGb" > $i.hoa + set x "$@" --product-or $i.hoa shift + test $i -eq 1 && shift # remove the first --product + test 2,3,6,1 = `autfilt --high --small --cobuchi "$@" --stats=%s,%e,%t,%a` + test 3,5,10,1 = \ + `autfilt --high --small --cobuchi "$@" | autfilt -S --stats=%s,%e,%t,%a` done -shift + if [ $MAX_ACCSETS -eq 32 ]; then - autfilt --cobuchi -S "$@" 2> error && exit 1 + autfilt "$@" 2> error && exit 1 grep 'Too many acceptance sets used' error fi -# FIXME: implement degeneralization for generalized-co-Büchi -# autfilt --cobuchi --low -S "$@" > result -# test "45,89,180,1" = `autfilt --stats=%s,%e,%t,%a result` true diff --git a/tests/python/pdegen.py b/tests/python/pdegen.py index 7df9f0878..00f3df7e0 100644 --- a/tests/python/pdegen.py +++ b/tests/python/pdegen.py @@ -149,6 +149,9 @@ tc.assertEqual(spot.is_partially_degeneralizable(de), []) df = spot.partial_degeneralize(f, [0, 1]) df.equivalent_to(f) tc.assertEqual(str(df.acc()), '(1, Fin(0))') +df2 = spot.degeneralize(f) +df.equivalent_to(f) +tc.assertEqual(str(df2.acc()), '(1, Fin(0))') try: df = spot.partial_degeneralize(f, [0, 1, 2]) @@ -206,6 +209,16 @@ pdaut7 = spot.partial_degeneralize(aut7, sets) tc.assertTrue(pdaut7.equivalent_to(aut7)) tc.assertEqual(daut7.num_states(), 10) tc.assertEqual(pdaut7.num_states(), 10) +ddaut7 = spot.dualize(aut7) +ddaut7a = spot.scc_filter(spot.dualize(spot.degeneralize_tba(ddaut7))) +tc.assertTrue(ddaut7a.equivalent_to(aut7)) +tc.assertEqual(ddaut7a.num_states(), daut7.num_states()) +ddaut7b = spot.scc_filter(spot.dualize(spot.to_nca(ddaut7))) +tc.assertTrue(ddaut7b.equivalent_to(aut7)) +tc.assertEqual(ddaut7b.num_states(), daut7.num_states()) +ddaut7c = spot.scc_filter(spot.dualize(spot.to_dca(ddaut7))) +tc.assertTrue(ddaut7c.equivalent_to(aut7)) +tc.assertEqual(ddaut7c.num_states(), daut7.num_states()) aut8 = spot.automaton("""HOA: v1 States: 8 Start: 0 AP: 3 "p0" "p1" "p2" acc-name: generalized-Buchi 5 Acceptance: 5 Inf(0)&Inf(1)&Inf(2)&Inf(3)&Inf(4) @@ -482,4 +495,4 @@ State: 1 [0] 0 {0} State: 2 [!0] 0 ---END--""") \ No newline at end of file +--END--""") From d9248e2e9725cd459139c1bde54a797f1fa54463 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Fri, 9 Sep 2022 16:41:44 +0200 Subject: [PATCH 131/337] * doc/org/concepts.org (T-based vs. S-based acceptance): Adjust example. --- doc/org/concepts.org | 25 ++++++------------------- 1 file changed, 6 insertions(+), 19 deletions(-) diff --git a/doc/org/concepts.org b/doc/org/concepts.org index d5641e42f..a8fab8b65 100644 --- a/doc/org/concepts.org +++ b/doc/org/concepts.org @@ -381,13 +381,14 @@ When /transition-based acceptance/ is used, acceptance sets are now sets of /edges/ (or set of /transitions/ if you prefer), and runs are accepting if the edges they visit satisfy the acceptance condition. -Here is an example of Transition-based Generalized Büchi Automaton -(TGBA). +Here is an example of Transition-based Büchi Automaton +(TBA). #+NAME: tgba-example1 #+BEGIN_SRC sh ltl2tgba 'GF(a & X(a U b))' -d #+END_SRC + #+BEGIN_SRC dot :file concept-tgba1.svg :var txt=tgba-example1 :exports results $txt #+END_SRC @@ -399,27 +400,13 @@ This automaton accept all ω-words that infinitely often match the pattern $a^+;b$ (that is: a positive number of letters where $a$ is true are followed by one letter where $b$ is true). -Using transition-based acceptance allows for more compact automata. -The typical example is the LTL formula =GFa= (infinitely often $a$) -that can be represented using a one-state transition-based Büchi -automaton: -#+NAME: tgba-example2 -#+BEGIN_SRC sh -ltl2tgba 'GFa' -d -#+END_SRC -#+BEGIN_SRC dot :file concept-tgba2.svg :var txt=tgba-example2 :exports results -$txt -#+END_SRC - -#+RESULTS: -[[file:concept-tgba2.svg]] - -While the same property require a 2-state Büchi automaton using +Using transition-based acceptance often allows for more compact automata. +For instance the above automaton would need at least 3 states with state-based acceptance: #+NAME: tgba-example3 #+BEGIN_SRC sh -ltl2tgba 'GFa' -B -d +ltl2tgba 'GF(a & X(a U b))' -B -d #+END_SRC #+BEGIN_SRC dot :file concept-tba-vs-ba.svg :var txt=tgba-example3 :exports results $txt From b3b22388c917880703010868732ddda516230988 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Tue, 13 Sep 2022 13:53:59 +0200 Subject: [PATCH 132/337] postproc: introduce -x merge-states-min * spot/twaalgos/postproc.cc, spot/twaalgos/postproc.hh: Introduce a merge-states-min option. * bin/spot-x.cc: Document it. * spot/gen/automata.cc, spot/gen/automata.hh, bin/genaut.cc: Add option to generate cyclist test cases. * NEWS: Document the above. * tests/core/included.test: Add test cases that used to be too slow. --- NEWS | 11 ++++++++++ bin/genaut.cc | 7 +++++- bin/spot-x.cc | 6 +++++- spot/gen/automata.cc | 45 +++++++++++++++++++++++++++++++++++++-- spot/gen/automata.hh | 20 ++++++++++++++++- spot/twaalgos/postproc.cc | 4 ++++ spot/twaalgos/postproc.hh | 3 ++- tests/core/included.test | 11 ++++++++-- 8 files changed, 99 insertions(+), 8 deletions(-) diff --git a/NEWS b/NEWS index 38beaa062..93708c0d0 100644 --- a/NEWS +++ b/NEWS @@ -36,6 +36,11 @@ New in spot 2.10.6.dev (not yet released) - ltlsynt has a new option --from-pgame that takes a parity game in extended HOA format, as used in the Synthesis Competition. + - genaut learned the --cyclist-trace-nba and --cyclist-proof-dba + options. Those are used to generate pairs of automata that should + include each other, and are used to show a regression (in speed) + present in Spot 2.10.x and fixed in 2.11. + Library: - The new function suffix_operator_normal_form() implements @@ -121,6 +126,12 @@ New in spot 2.10.6.dev (not yet released) is a co-Büchi automaton. And product_or() learned that the "or"-product of two Büchi automata is a Büchi automaton. + - spot::postprocessor has a new extra option merge-states-min that + indicate above how many states twa_graph::merge_states(), which + perform a very cheap pass to fuse states with identicall + succesors, should be called before running simulation-based + reductions. + - spot::parallel_policy is an object that can be passed to some algorithm to specify how many threads can be used if Spot has been compiled with --enable-pthread. Currently, only diff --git a/bin/genaut.cc b/bin/genaut.cc index eb2163cab..d7db04d98 100644 --- a/bin/genaut.cc +++ b/bin/genaut.cc @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2017-2019 Laboratoire de Recherche et Développement +// Copyright (C) 2017-2019, 2022 Laboratoire de Recherche et Développement // de l'Epita (LRDE). // // This file is part of Spot, a model checking library. @@ -62,6 +62,11 @@ static const argp_option options[] = { "m-nba", gen::AUT_M_NBA, "RANGE", 0, "An NBA with N+1 states whose determinization needs at least " "N! states", 0}, + { "cyclist-trace-nba", gen::AUT_CYCLIST_TRACE_NBA, "RANGE", 0, + "An NBA with N+2 states that should include cyclist-proof-dba=B.", 0}, + { "cyclist-proof-dba", gen::AUT_CYCLIST_PROOF_DBA, "RANGE", 0, + "A DBA with N+2 states that should be included " + "in cyclist-trace-nba=B.", 0}, RANGE_DOC, /**************************************************/ { nullptr, 0, nullptr, 0, "Miscellaneous options:", -1 }, diff --git a/bin/spot-x.cc b/bin/spot-x.cc index c4905c2e9..a653fc926 100644 --- a/bin/spot-x.cc +++ b/bin/spot-x.cc @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2013-2021 Laboratoire de Recherche et Développement +// Copyright (C) 2013-2022 Laboratoire de Recherche et Développement // de l'Epita (LRDE). // // This file is part of Spot, a model checking library. @@ -164,6 +164,10 @@ Set to 1 to use only direct simulation. Set to 2 to use only reverse \ simulation. Set to 3 to iterate both direct and reverse simulations. \ The default is the value of parameter \"simul\" in --high mode, and 0 \ therwise.") }, + { DOC("merge-states-min", "Number of states above which states are \ +merged using a cheap approximation of a bisimulation quotient before \ +attempting simulation-based reductions. Defaults to 128. Set to 0 to \ +never merge states.") }, { DOC("simul-max", "Number of states above which simulation-based \ reductions are skipped. Defaults to 4096. Set to 0 to disable. This \ applies to all simulation-based optimization, including thoses of the \ diff --git a/spot/gen/automata.cc b/spot/gen/automata.cc index 165ab8c98..73c057a00 100644 --- a/spot/gen/automata.cc +++ b/spot/gen/automata.cc @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2017-2019, 2021 Laboratoire de Recherche et +// Copyright (C) 2017-2019, 2021-2022 Laboratoire de Recherche et // Developpement de l'EPITA (LRDE). // // This file is part of Spot, a model checking library. @@ -220,13 +220,48 @@ namespace spot return aut; } + static twa_graph_ptr + cyclist_trace_or_proof(unsigned n, bool trace, bdd_dict_ptr dict) + { + auto aut = make_twa_graph(dict); + acc_cond::mark_t m = aut->set_buchi(); + aut->new_states(n + 2); + aut->set_init_state(0); + if (trace) + m = {}; + aut->prop_state_acc(true); + + // How many AP to we need to represent n letters + unsigned nap = ulog2(n + 1); + std::vector apvars(nap); + for (unsigned a = 0; a < nap; ++a) + apvars[a] = aut->register_ap("p" + std::to_string(a)); + + if (trace) + aut->new_edge(0, 0, bddtrue); // the only non-deterministic edge + else + aut->prop_universal(true); + + bdd zero = bdd_ibuildcube(0, nap, apvars.data()); + aut->new_edge(0, 1, zero, m); + for (unsigned letter = 1; letter <= n; ++letter) + { + bdd cond = bdd_ibuildcube(letter, nap, apvars.data()); + aut->new_acc_edge(1, letter + 1, cond); + aut->new_edge(letter + 1, 1, zero, m); + } + + return aut; + } + + twa_graph_ptr aut_pattern(aut_pattern_id pattern, int n, bdd_dict_ptr dict) { if (n < 0) { std::ostringstream err; err << "pattern argument for " << aut_pattern_name(pattern) - << " should be positive"; + << " should be non-negative"; throw std::runtime_error(err.str()); } @@ -241,6 +276,10 @@ namespace spot return l_dsa(n, dict); case AUT_M_NBA: return m_nba(n, dict); + case AUT_CYCLIST_TRACE_NBA: + return cyclist_trace_or_proof(n, true, dict); + case AUT_CYCLIST_PROOF_DBA: + return cyclist_trace_or_proof(n, false, dict); case AUT_END: break; } @@ -255,6 +294,8 @@ namespace spot "l-nba", "l-dsa", "m-nba", + "cyclist-trace-nba", + "cyclist-proof-dba", }; // Make sure we do not forget to update the above table every // time a new pattern is added. diff --git a/spot/gen/automata.hh b/spot/gen/automata.hh index d0c43d5f5..a54f75ac1 100644 --- a/spot/gen/automata.hh +++ b/spot/gen/automata.hh @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2017, 2019 Laboratoire de Recherche et Developpement de +// Copyright (C) 2017, 2019, 2022 Laboratoire de Recherche et Developpement de // l'EPITA (LRDE). // // This file is part of Spot, a model checking library. @@ -79,6 +79,24 @@ namespace spot /// propositions to encode the $n+1$ letters used in the /// original alphabet. AUT_M_NBA, + /// \brief An NBA with (n+2) states derived from a Cyclic test + /// case. + /// + /// This familly of automata is derived from a couple of + /// examples supplied by Reuben Rowe. The task is to + /// check that the automaton generated with AUT_CYCLIST_TRACE_NBA + /// for a given n contain the automaton generated with + /// AUT_CYCLIST_PROOF_DBA for the same n. + AUT_CYCLIST_TRACE_NBA, + /// \brief A DBA with (n+2) states derived from a Cyclic test + /// case. + /// + /// This familly of automata is derived from a couple of + /// examples supplied by Reuben Rowe. The task is to + /// check that the automaton generated with AUT_CYCLIST_TRACE_NBA + /// for a given n contain the automaton generated with + /// AUT_CYCLIST_PROOF_DBA for the same n. + AUT_CYCLIST_PROOF_DBA, AUT_END }; diff --git a/spot/twaalgos/postproc.cc b/spot/twaalgos/postproc.cc index 39a6c0926..55feeb295 100644 --- a/spot/twaalgos/postproc.cc +++ b/spot/twaalgos/postproc.cc @@ -89,6 +89,7 @@ namespace spot wdba_minimize_ = opt->get("wdba-minimize", -1); gen_reduce_parity_ = opt->get("gen-reduce-parity", 1); simul_max_ = opt->get("simul-max", 4096); + merge_states_min_ = opt->get("merge-states-min", 128); wdba_det_max_ = opt->get("wdba-det-max", 4096); simul_trans_pruning_ = opt->get("simul-trans-pruning", 512); @@ -118,6 +119,9 @@ namespace spot { if (opt == 0) return a; + if (merge_states_min_ > 0 + && static_cast(merge_states_min_) < a->num_states()) + a->merge_states(); if (simul_max_ > 0 && static_cast(simul_max_) < a->num_states()) return a; diff --git a/spot/twaalgos/postproc.hh b/spot/twaalgos/postproc.hh index 080cb831f..96128c531 100644 --- a/spot/twaalgos/postproc.hh +++ b/spot/twaalgos/postproc.hh @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2012-2021 Laboratoire de Recherche et Développement +// Copyright (C) 2012-2022 Laboratoire de Recherche et Développement // de l'Epita (LRDE). // // This file is part of Spot, a model checking library. @@ -268,6 +268,7 @@ namespace spot bool state_based_ = false; int wdba_minimize_ = -1; int simul_max_ = 4096; + int merge_states_min_ = 128; int wdba_det_max_ = 4096; }; /// @} diff --git a/tests/core/included.test b/tests/core/included.test index 9f39fef20..3574af9e3 100755 --- a/tests/core/included.test +++ b/tests/core/included.test @@ -1,6 +1,6 @@ #! /bin/sh # -*- coding: utf-8 -*- -# Copyright (C) 2016 Laboratoire de Recherche et Développement +# Copyright (C) 2016, 2022 Laboratoire de Recherche et Développement # de l'Epita (LRDE). # # This file is part of Spot, a model checking library. @@ -60,5 +60,12 @@ ltl2tgba true | autfilt out.hoa --equivalent-to - ltl2tgba '!(a U c)' | autfilt --product-or a1.hoa > out.hoa ltl2tgba true | autfilt out.hoa --equivalent-to - && exit 1 -: +# In Spot 2.10, the following was very slow. +for n in 1 2 4 8 16 512 1024 2048 4096 8192; do + genaut --cyclist-trace-nba=$n > trace.hoa + genaut --cyclist-proof-dba=$n > proof.hoa + autfilt -q --included-in=trace.hoa proof.hoa || exit 1 + autfilt -q --included-in=proof.hoa trace.hoa && exit 1 +done +: From ef0aeed22844bd58041c6a1e9e4c3ba9422eb7eb Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Wed, 14 Sep 2022 11:29:18 +0200 Subject: [PATCH 133/337] ltlsynt: fix documentation of --aiger option * bin/ltlsynt.cc: Here. --- bin/ltlsynt.cc | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/bin/ltlsynt.cc b/bin/ltlsynt.cc index 06c29db88..1779211ef 100644 --- a/bin/ltlsynt.cc +++ b/bin/ltlsynt.cc @@ -114,13 +114,13 @@ static const argp_option options[] = "realizability only, do not compute a winning strategy", 0}, { "aiger", OPT_PRINT_AIGER, "ite|isop|both[+ud][+dc]" "[+sub0|sub1|sub2]", OPTION_ARG_OPTIONAL, - "prints a winning strategy as an AIGER circuit. The first, and only " - "mandatory option defines the method to be used. \"ite\" for " - "If-then-else normal form; " + "prints a winning strategy as an AIGER circuit. The first word " + "indicates the encoding to used: \"ite\" for " + "If-Then-Else normal form; " "\"isop\" for irreducible sum of producs; " - "\"both\" tries both encodings and keeps the smaller one. " + "\"both\" tries both and keeps the smaller one. " "The other options further " - "refine the encoding, see aiger::encode_bdd.", 0}, + "refine the encoding, see aiger::encode_bdd. Defaults to \"ite\".", 0}, { "verbose", OPT_VERBOSE, nullptr, 0, "verbose mode", -1 }, { "verify", OPT_VERIFY, nullptr, 0, From c1c874b1a511640facedfa9ec0f4c4acecb77de6 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Wed, 14 Sep 2022 15:33:46 +0200 Subject: [PATCH 134/337] ltlsynt: add options --dot and --hide-status * bin/ltlsynt.cc: Implement these options. * bin/common_aoutput.hh, bin/common_aoutput.cc (automaton_format_opt): Make extern. * NEWS: Mention the new options. * doc/org/ltlsynt.org: Use dot output in documentation. * tests/core/ltlsynt.test: Quick test of the new options. --- NEWS | 10 +++ bin/common_aoutput.cc | 4 +- bin/common_aoutput.hh | 3 +- bin/ltlsynt.cc | 97 ++++++++++++++++++---------- doc/org/ltlsynt.org | 139 +++++++++++++++++++++++++++++++--------- tests/core/ltlsynt.test | 19 ++++++ 6 files changed, 205 insertions(+), 67 deletions(-) diff --git a/NEWS b/NEWS index 93708c0d0..535dee5fa 100644 --- a/NEWS +++ b/NEWS @@ -36,6 +36,16 @@ New in spot 2.10.6.dev (not yet released) - ltlsynt has a new option --from-pgame that takes a parity game in extended HOA format, as used in the Synthesis Competition. + - ltlsynt has a new option --hide-status to hide the REALIZABLE or + UNREALIZABLE output expected by SYNTCOMP. (This line is + superfluous, because the exit status of ltlsynt already indicate + whether the formula is realizable or not.) + + - ltlsynt has a new option --dot to request GraphViz output instead + of most output. This works for displaying Mealy machines, games, + or AIG circuits. See https://spot.lrde.epita.fr/ltlsynt.html for + examples. + - genaut learned the --cyclist-trace-nba and --cyclist-proof-dba options. Those are used to generate pairs of automata that should include each other, and are used to show a regression (in speed) diff --git a/bin/common_aoutput.cc b/bin/common_aoutput.cc index 665fafc67..f2c8691ec 100644 --- a/bin/common_aoutput.cc +++ b/bin/common_aoutput.cc @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2012-2021 Laboratoire de Recherche et Développement +// Copyright (C) 2012-2022 Laboratoire de Recherche et Développement // de l'Epita (LRDE). // // This file is part of Spot, a model checking library. @@ -41,7 +41,7 @@ #include automaton_format_t automaton_format = Hoa; -static const char* automaton_format_opt = nullptr; +const char* automaton_format_opt = nullptr; const char* opt_name = nullptr; static const char* opt_output = nullptr; static const char* stats = ""; diff --git a/bin/common_aoutput.hh b/bin/common_aoutput.hh index 1b2e7ae41..0fb2e8d7c 100644 --- a/bin/common_aoutput.hh +++ b/bin/common_aoutput.hh @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2014-2018, 2020 Laboratoire de Recherche et +// Copyright (C) 2014-2018, 2020, 2022 Laboratoire de Recherche et // Développement de l'Epita (LRDE). // // This file is part of Spot, a model checking library. @@ -47,6 +47,7 @@ enum automaton_format_t { // The format to use in output_automaton() extern automaton_format_t automaton_format; +extern const char* automaton_format_opt; // Set to the argument of --name, else nullptr. extern const char* opt_name; // Output options diff --git a/bin/ltlsynt.cc b/bin/ltlsynt.cc index 1779211ef..e0cf78c47 100644 --- a/bin/ltlsynt.cc +++ b/bin/ltlsynt.cc @@ -37,6 +37,7 @@ #include #include #include +#include #include #include #include @@ -49,7 +50,9 @@ enum OPT_BYPASS, OPT_CSV, OPT_DECOMPOSE, + OPT_DOT, OPT_FROM_PGAME, + OPT_HIDE, OPT_INPUT, OPT_OUTPUT, OPT_PRINT, @@ -107,32 +110,41 @@ static const argp_option options[] = /**************************************************/ { nullptr, 0, nullptr, 0, "Output options:", 20 }, { "print-pg", OPT_PRINT, nullptr, 0, - "print the parity game in the pgsolver format, do not solve it", 0}, + "print the parity game in the pgsolver format, do not solve it", 0 }, { "print-game-hoa", OPT_PRINT_HOA, "options", OPTION_ARG_OPTIONAL, - "print the parity game in the HOA format, do not solve it", 0}, + "print the parity game in the HOA format, do not solve it", 0 }, { "realizability", OPT_REAL, nullptr, 0, - "realizability only, do not compute a winning strategy", 0}, + "realizability only, do not compute a winning strategy", 0 }, { "aiger", OPT_PRINT_AIGER, "ite|isop|both[+ud][+dc]" "[+sub0|sub1|sub2]", OPTION_ARG_OPTIONAL, - "prints a winning strategy as an AIGER circuit. The first word " - "indicates the encoding to used: \"ite\" for " + "encode the winning strategy as an AIG circuit and print it in AIGER" + " format. The first word indicates the encoding to used: \"ite\" for " "If-Then-Else normal form; " - "\"isop\" for irreducible sum of producs; " + "\"isop\" for irreducible sum of products; " "\"both\" tries both and keeps the smaller one. " - "The other options further " - "refine the encoding, see aiger::encode_bdd. Defaults to \"ite\".", 0}, - { "verbose", OPT_VERBOSE, nullptr, 0, - "verbose mode", -1 }, - { "verify", OPT_VERIFY, nullptr, 0, - "verifies the strategy or (if demanded) aiger against the spec.", -1 }, + "Other options further " + "refine the encoding, see aiger::encode_bdd. Defaults to \"ite\".", 0 }, + { "dot", OPT_DOT, "options", OPTION_ARG_OPTIONAL, + "Use dot format when printing the result (game, strategy, or " + "AIG circuit, depending on other options). The options that " + "may be passed to --dot depend on the nature of what is printed. " + "For games and strategies, standard automata rendering " + "options are supported (e.g., see ltl2tgba --dot). For AIG circuit, " + "use (h) for horizontal and (v) for vertical layouts.", 0 }, { "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 }, + { "hide-status", OPT_HIDE, nullptr, 0, + "Hide the REALIZABLE or UNREALIZABLE line. (Hint: exit status " + "is enough of an indication.)", 0 }, /**************************************************/ { nullptr, 0, nullptr, 0, "Miscellaneous options:", -1 }, { "extra-options", 'x', "OPTS", 0, "fine-tuning options (see spot-x (7))", 0 }, + { "verbose", OPT_VERBOSE, nullptr, 0, "verbose mode", 0 }, + { "verify", OPT_VERIFY, nullptr, 0, + "verify the strategy or (if demanded) AIG against the formula", 0 }, { nullptr, 0, nullptr, 0, nullptr, 0 }, }; @@ -162,8 +174,10 @@ static const char* opt_print_hoa_args = nullptr; static bool opt_real = false; static bool opt_do_verify = false; static const char* opt_print_aiger = nullptr; - +static const char* opt_dot_arg = nullptr; +static bool opt_dot = false; static spot::synthesis_info* gi; +static bool show_status = true; static char const *const algo_names[] = { @@ -254,6 +268,17 @@ namespace return s; }; + static void + dispatch_print_hoa(const spot::const_twa_graph_ptr& game) + { + if (opt_dot) + spot::print_dot(std::cout, game, opt_print_hoa_args); + else if (opt_print_pg) + spot::print_pg(std::cout, game); + else + spot::print_hoa(std::cout, game, opt_print_hoa_args) << '\n'; + } + static void print_csv(const spot::formula& f, const char* filename = nullptr) { @@ -326,7 +351,7 @@ namespace outf.close(opt_csv); } - int + static int solve_formula(const spot::formula& f, const std::vector& input_aps, const std::vector& output_aps) @@ -397,15 +422,8 @@ namespace std::vector mealy_machines; auto print_game = want_game ? - [](const spot::twa_graph_ptr& game)->void - { - if (opt_print_pg) - spot::print_pg(std::cout, game); - else - spot::print_hoa(std::cout, game, opt_print_hoa_args) << '\n'; - } - : - [](const spot::twa_graph_ptr&)->void{}; + [](const spot::twa_graph_ptr& game)->void { dispatch_print_hoa(game); } + : [](const spot::twa_graph_ptr&)->void{}; for (; sub_f != sub_form.end(); ++sub_f, ++sub_o) { @@ -425,7 +443,8 @@ namespace { case spot::mealy_like::realizability_code::UNREALIZABLE: { - std::cout << "UNREALIZABLE" << std::endl; + if (show_status) + std::cout << "UNREALIZABLE" << std::endl; safe_tot_time(); return 1; } @@ -448,7 +467,8 @@ namespace continue; if (!spot::solve_game(arena, *gi)) { - std::cout << "UNREALIZABLE" << std::endl; + if (show_status) + std::cout << "UNREALIZABLE" << std::endl; safe_tot_time(); return 1; } @@ -506,7 +526,8 @@ namespace return 0; } - std::cout << "REALIZABLE" << std::endl; + if (show_status) + std::cout << "REALIZABLE" << std::endl; if (opt_real) { safe_tot_time(); @@ -545,7 +566,10 @@ namespace << " latches and " << saig->num_gates() << " gates\n"; } - spot::print_aiger(std::cout, saig) << '\n'; + if (opt_dot) + spot::print_dot(std::cout, saig, opt_dot_arg); + else + spot::print_aiger(std::cout, saig) << '\n'; } else { @@ -784,10 +808,7 @@ namespace } if (opt_print_pg || opt_print_hoa) { - if (opt_print_pg) - spot::print_pg(std::cout, arena); - else - spot::print_hoa(std::cout, arena, opt_print_hoa_args) << '\n'; + dispatch_print_hoa(arena); return 0; } auto safe_tot_time = [&]() { @@ -796,13 +817,15 @@ namespace }; if (!spot::solve_game(arena, *gi)) { - std::cout << "UNREALIZABLE" << std::endl; + if (show_status) + std::cout << "UNREALIZABLE" << std::endl; safe_tot_time(); return 1; } if (gi->bv) gi->bv->realizable = true; - std::cout << "REALIZABLE" << std::endl; + if (show_status) + std::cout << "REALIZABLE" << std::endl; if (opt_real) { safe_tot_time(); @@ -905,9 +928,17 @@ parse_opt(int key, char *arg, struct argp_state *) opt_decompose_ltl = XARGMATCH("--decompose", arg, decompose_args, decompose_values); break; + case OPT_DOT: + opt_dot = true; + automaton_format_opt = opt_dot_arg = arg; + automaton_format = Dot; + break; case OPT_FROM_PGAME: jobs.emplace_back(arg, job_type::AUT_FILENAME); break; + case OPT_HIDE: + show_status = false; + break; case OPT_INPUT: { all_input_aps.emplace(std::vector{}); diff --git a/doc/org/ltlsynt.org b/doc/org/ltlsynt.org index f05d58309..e4fbc66e4 100644 --- a/doc/org/ltlsynt.org +++ b/doc/org/ltlsynt.org @@ -7,19 +7,19 @@ * Basic usage -This tool synthesizes controllers from LTL/PSL formulas. +This tool synthesizes reactive controllers from LTL/PSL formulas. Consider a set $I$ of /input/ atomic propositions, a set $O$ of output atomic propositions, and a PSL formula \phi over the propositions in $I \cup O$. A -=controller= realizing \phi is a function $c: (2^{I})^\star \times 2^I \mapsto +*reactive controller* realizing \phi is a function $c: (2^{I})^\star \times 2^I \mapsto 2^O$ such that, for every \omega-word $(u_i)_{i \in N} \in (2^I)^\omega$ over the input propositions, the word $(u_i \cup c(u_0 \dots u_{i-1}, u_i))_{i \in N}$ satisfies \phi. -If a controller exists, then one with finite memory exists. Such controllers -are easily represented as automata (or more specifically as I/O automata or -transducers). In the automaton representing the controller, the acceptance -condition is irrelevant and trivially true. +If a reactive controller exists, then one with finite memory +exists. Such controllers are easily represented as automata (or more +specifically as Mealy machines). In the automaton representing the +controller, the acceptance condition is irrelevant and trivially true. =ltlsynt= has three mandatory options: - =--ins=: a comma-separated list of input atomic propositions; @@ -27,45 +27,52 @@ condition is irrelevant and trivially true. - =--formula= or =--file=: a specification in LTL or PSL. One of =--ins= or =--outs= may be omitted, as any atomic proposition not listed -as input can be assumed to be an output and vice-versa. +as input can be assumed to be output and vice-versa. -The following example illustrates the synthesis of a controller acting as an -=AND= gate. We have two inputs =a= and =b= and one output =c=, and we want =c= -to always be the =AND= of the two inputs: +The following example illustrates the synthesis of a controller +ensuring that input =i1= and =i2= are both true initially if and only +if eventually output =o1= will go from true to false at some point. +Note that this is an equivalence, not an implication. #+NAME: example #+BEGIN_SRC sh :exports both -ltlsynt --ins=a,b -f 'G (a & b <=> c)' +ltlsynt --ins=i1,i2 -f '(i1 & i2) <-> F(o1 & X(!o1))' #+END_SRC #+RESULTS: example #+begin_example REALIZABLE HOA: v1 -States: 1 +States: 3 Start: 0 -AP: 3 "a" "b" "c" +AP: 3 "i1" "i2" "o1" acc-name: all Acceptance: 0 t properties: trans-labels explicit-labels state-acc deterministic +controllable-AP: 2 --BODY-- State: 0 -[!0&!2 | !1&!2] 0 -[0&1&2] 0 +[0&1&2] 1 +[!0&2 | !1&2] 2 +State: 1 +[!2] 0 +State: 2 +[2] 2 --END-- #+end_example The output is composed of two parts: -- the first one is a single line =REALIZABLE= or =UNREALIZABLE;= -- the second one, only present in the =REALIZABLE= case is an automaton describing the controller. - In this example, the controller has a single - state, with two loops labeled by =a & b & c= and =(!a | !b) & !c=. +- The first one is a single line =REALIZABLE= or =UNREALIZABLE=; the presence of this + line, required by the [[http://http://www.syntcomp.org/][SyntComp competition]], can be disabled with option =--hide-status=. +- The second one, only present in the =REALIZABLE= case, is an automaton describing the controller. + +The controller contains the line =controllable-AP: 2=, which means that this automaton +should be interpreted as a Mealy machine where =o0= is part of the output. +Using the =--dot= option, makes it easier to visualize this machine. #+NAME: exampledot -#+BEGIN_SRC sh :exports none :noweb yes -sed 1d <> -EOF +#+BEGIN_SRC sh :exports code +ltlsynt --ins=i1,i2 -f '(i1 & i2) <-> F(o1 & X(!o1))' --hide-status --dot #+END_SRC #+BEGIN_SRC dot :file ltlsyntex.svg :var txt=exampledot :exports results @@ -75,9 +82,6 @@ EOF #+RESULTS: [[file:ltlsyntex.svg]] -The label =a & b & c= should be understood as: "if the input is =a&b=, -the output should be =c=". - The following example illustrates the case of an unrealizable specification. As =a= is an input proposition, there is no way to guarantee that it will eventually hold. @@ -90,11 +94,68 @@ ltlsynt --ins=a -f 'F a' : UNREALIZABLE By default, the controller is output in HOA format, but it can be -output as an [[http://fmv.jku.at/aiger/][AIGER]] circuit thanks to the =--aiger= flag. This is the -output format required for the [[http://syntcomp.org/][SYNTCOMP]] competition. +output as an And-Inverter-Graph in [[http://fmv.jku.at/aiger/][AIGER format]] using the =--aiger= +flag. This is the output format required for the [[http://syntcomp.org/][SYNTCOMP]] competition. -The generation of a controller can be disabled with the flag =--realizability=. -In this case, =ltlsynt= output is limited to =REALIZABLE= or =UNREALIZABLE=. +#+NAME: exampleaig +#+BEGIN_SRC sh :exports both +ltlsynt --ins=i1,i2 -f '(i1 & i2) <-> F(o1 & X(!o1))' --aiger +#+END_SRC + +#+RESULTS: exampleaig +#+begin_example +REALIZABLE +aag 14 2 2 1 10 +2 +4 +6 14 +8 29 +7 +10 7 9 +12 4 10 +14 2 12 +16 7 8 +18 4 16 +20 5 7 +22 21 19 +24 2 23 +26 3 7 +28 27 25 +i0 i1 +i1 i2 +o0 o1 +#+end_example + +The above format is not very human friendly. Again, by passing both +=--aiger= and =--dot=, one can display the And-Inverter-Graph representing +the controller: + +#+NAME: exampleaigdot +#+BEGIN_SRC sh :exports code +ltlsynt --ins=i1,i2 -f '(i1 & i2) <-> F(o1 & X(!o1))' --hide-status --aiger --dot +#+END_SRC + +#+BEGIN_SRC dot :file ltlsyntexaig.svg :var txt=exampleaigdot :exports results + $txt +#+END_SRC + +#+RESULTS: +[[file:ltlsyntexaig.svg]] + +In the above diagram, round nodes represent AND gates. Small black +circles represent inversions (or negations), colored triangles are +used to represent input signals (at the bottom) and output signals (at +the top), and finally rectangles represent latches. A latch is a one +bit register that delays the signal by one step. Initially, all +latches are assumed to contain =false=, and them emit their value from +the =L0_out= and =L1_out= rectangles at the bottom. Their input value, +to be emitted at the next step, is received via the =L0_in= and =L1_in= +boxes at the top. In =ltlsynt='s encoding, the set of latches is used +to keep track of the current state of the Mealy machine. + +The generation of a controller can be disabled with the flag +=--realizability=. In this case, =ltlsynt='s output is limited to +=REALIZABLE= or =UNREALIZABLE=. * TLSF @@ -177,7 +238,18 @@ be tried by separating them using commas. For instance You can also ask =ltlsynt= to print to obtained parity game into [[https://github.com/tcsprojects/pgsolver][PGSolver]] format, with the flag =--print-pg=, or in the HOA format, using =--print-game-hoa=. These flag deactivate the resolution of the -parity game. +parity game. Note that if any of those flag is used with =--dot=, the game +will be printed in the Dot format instead: + +#+NAME: examplegamedot +#+BEGIN_SRC sh :exports code +ltlsynt --ins=i1,i2 -f '(i1 & i2) <-> F(o1 & X(!o1))' --print-game-hoa --dot +#+END_SRC +#+BEGIN_SRC dot :file ltlsyntexgame.svg :var txt=examplegamedot :exports results + $txt +#+END_SRC +#+RESULTS: +[[file:ltlsyntexgame.svg]] For benchmarking purpose, the =--csv= option can be used to record intermediate statistics about the resolution. @@ -200,6 +272,11 @@ Further improvements are described in the following paper: /Alexandre Duret-Lutz/, and /Adrien Pommellet/. Presented at the SYNT'21 workshop. ([[https://www.lrde.epita.fr/~adl/dl/adl/renkin.21.synt.pdf][pdf]] | [[https://www.lrde.epita.fr/~adl/dl/adl_bib.html#renkin.21.synt][bib]]) +Simplification of Mealy machines is discussed in: + +- *Effective reductions of Mealy machines*, /Florian Renkin/, + /Philipp Schlehuber-Caissier/, /Alexandre Duret-Lutz/, and /Adrien Pommellet/. + Presented at FORTE'22. ([[https://www.lrde.epita.fr/~adl/dl/adl/renkin.22.forte.pdf][pdf]] | [[https://www.lrde.epita.fr/~adl/dl/adl_bib.html#renkin.22.forte][bib]]) # LocalWords: utf ltlsynt AIGER html args mapsto SRC acc aiger TLSF # LocalWords: UNREALIZABLE unrealizable SYNTCOMP realizability Proc diff --git a/tests/core/ltlsynt.test b/tests/core/ltlsynt.test index 33369dcde..b9dfac204 100644 --- a/tests/core/ltlsynt.test +++ b/tests/core/ltlsynt.test @@ -985,3 +985,22 @@ ltlsynt -f "G(o1|o2) & (GFi <-> GFo1)" --outs="o1,o2" --verbose\ --bypass=yes 2> out sed 's/ [0-9.e-]* seconds/ X seconds/g' out > outx diff outx exp + +# Test --dot and --hide-status +ltlsynt -f 'i <-> Fo' --ins=i --aiger --dot | grep arrowhead=dot +ltlsynt -f 'i <-> Fo' --ins=i --print-game-hoa --dot | grep 'shape="diamond"' +ltlsynt -f 'i <-> Fo' --ins=i --dot --hide-status > res +cat >exp < 0 + 0 [label="0"] + 0 -> 0 [label="i / o"] + 0 -> 1 [label="!i / !o"] + 1 [label="1"] + 1 -> 1 [label="1 / !o"] +} +EOF +diff res exp From c63c1796b992ab9e49b7eb53bd0f3919f7ebb482 Mon Sep 17 00:00:00 2001 From: Philipp Schlehuber-Caissier Date: Wed, 21 Sep 2022 17:27:58 +0200 Subject: [PATCH 135/337] Improve aiger INF encoding the encoding cna be simplified to produce less gates when high or low is True. * spot/twaalgos/aiger.cc: Here * tests/python/_synthesis.ipynb: Test --- spot/twaalgos/aiger.cc | 33 ++- tests/python/_synthesis.ipynb | 446 ++++++++++++++++++++++++++++++++-- 2 files changed, 454 insertions(+), 25 deletions(-) diff --git a/spot/twaalgos/aiger.cc b/spot/twaalgos/aiger.cc index e3c3bb6c5..af255a167 100644 --- a/spot/twaalgos/aiger.cc +++ b/spot/twaalgos/aiger.cc @@ -637,19 +637,44 @@ namespace spot // De-morgan // !(!v&low | v&high) = !(!v&low) & !(v&high) // !v&low | v&high = !(!(!v&low) & !(v&high)) + // note that if low or high are T + // we can simplify the formula + // given that low / high is T + // then !(!v&low) & !(v&high) can be simplified to + // !v&low | v&high = !v | high / low | v + // = !(v & !high) / !(!low & !v) + // The case when low / high is ⊥ is automatically treated auto b_it = bdd2var_.find(b.id()); if (b_it != bdd2var_.end()) return b_it->second; - // todo -// unsigned v_var = bdd2var_.at(bdd_var(b)); unsigned v_var = bdd2var_.at(bdd_ithvar(bdd_var(b)).id()); unsigned b_branch_var[2] = {bdd2INFvar(bdd_low(b)), bdd2INFvar(bdd_high(b))}; - unsigned r = aig_not(aig_and(v_var, b_branch_var[1])); - unsigned l = aig_not(aig_and(aig_not(v_var), b_branch_var[0])); + unsigned l; + unsigned r; + + if (b_branch_var[0] == aig_true()) + { + // low == T + l = v_var; + r = aig_not(b_branch_var[1]); + } + else if (b_branch_var[1] == aig_true()) + { + // high == T + l = aig_not(b_branch_var[0]); + r = aig_not(v_var); + } + else + { + // General case + r = aig_not(aig_and(v_var, b_branch_var[1])); + l = aig_not(aig_and(aig_not(v_var), b_branch_var[0])); + } + return aig_not(aig_and(l, r)); } diff --git a/tests/python/_synthesis.ipynb b/tests/python/_synthesis.ipynb index 6952eadd3..4c203a86e 100644 --- a/tests/python/_synthesis.ipynb +++ b/tests/python/_synthesis.ipynb @@ -738,7 +738,7 @@ "\n" ], "text/plain": [ - " *' at 0x7fcc883a7720> >" + " *' at 0x7fbccc33a0f0> >" ] }, "execution_count": 8, @@ -821,7 +821,7 @@ "\n" ], "text/plain": [ - " *' at 0x7fcc883a7720> >" + " *' at 0x7fbccc33a0f0> >" ] }, "execution_count": 9, @@ -945,7 +945,7 @@ "\n" ], "text/plain": [ - " *' at 0x7fcc8833aa80> >" + " *' at 0x7fbccc345660> >" ] }, "execution_count": 10, @@ -1043,7 +1043,7 @@ "\n" ], "text/plain": [ - " *' at 0x7fcc880c2ab0> >" + " *' at 0x7fbccc3486c0> >" ] }, "execution_count": 11, @@ -1211,7 +1211,7 @@ "\n" ], "text/plain": [ - " *' at 0x7fcc8833ae70> >" + " *' at 0x7fbccc345ae0> >" ] }, "execution_count": 12, @@ -1420,7 +1420,7 @@ "\n" ], "text/plain": [ - " *' at 0x7fcc880c2240> >" + " *' at 0x7fbccc345e40> >" ] }, "execution_count": 13, @@ -1578,7 +1578,7 @@ "\n" ], "text/plain": [ - " *' at 0x7fcc880bdc30> >" + " *' at 0x7fbccc364a20> >" ] }, "execution_count": 14, @@ -1722,7 +1722,7 @@ "\n" ], "text/plain": [ - " *' at 0x7fcc880bdc30> >" + " *' at 0x7fbccc364a20> >" ] }, "execution_count": 15, @@ -1869,7 +1869,7 @@ "\n" ], "text/plain": [ - " *' at 0x7fcc880c5210> >" + " *' at 0x7fbccc35a2d0> >" ] }, "execution_count": 16, @@ -2014,7 +2014,7 @@ "\n" ], "text/plain": [ - " *' at 0x7fcc880c5210> >" + " *' at 0x7fbccc35a2d0> >" ] }, "execution_count": 17, @@ -2561,7 +2561,7 @@ "\n" ], "text/plain": [ - " *' at 0x7fcc880c5d50> >" + " *' at 0x7fbccc35af00> >" ] }, "execution_count": 18, @@ -2715,7 +2715,7 @@ "\n" ], "text/plain": [ - " *' at 0x7fcc880c5d50> >" + " *' at 0x7fbccc35af00> >" ] }, "execution_count": 19, @@ -2873,7 +2873,7 @@ "\n" ], "text/plain": [ - " *' at 0x7fcc880cd090> >" + " *' at 0x7fbccc36d240> >" ] }, "metadata": {}, @@ -4215,7 +4215,7 @@ "\n" ], "text/plain": [ - " *' at 0x7fcc880bdc00> >" + " *' at 0x7fbccc3910f0> >" ] }, "execution_count": 20, @@ -4698,7 +4698,7 @@ "\n" ], "text/plain": [ - " *' at 0x7fcc880bdc00> >" + " *' at 0x7fbccc3910f0> >" ] }, "execution_count": 21, @@ -4831,7 +4831,7 @@ "\n" ], "text/plain": [ - " *' at 0x7fcc880cd2a0> >" + " *' at 0x7fbccc364570> >" ] }, "metadata": {}, @@ -5509,7 +5509,7 @@ "\n" ], "text/plain": [ - " *' at 0x7fcc880c55a0> >" + " *' at 0x7fbccc35a9c0> >" ] }, "execution_count": 22, @@ -5748,7 +5748,7 @@ "\n" ], "text/plain": [ - " *' at 0x7fcc880c55a0> >" + " *' at 0x7fbccc35a9c0> >" ] }, "execution_count": 23, @@ -5838,7 +5838,7 @@ "\n" ], "text/plain": [ - " *' at 0x7fcc880c5a50> >" + " *' at 0x7fbccc3646c0> >" ] }, "metadata": {}, @@ -5932,7 +5932,7 @@ "\n" ], "text/plain": [ - " *' at 0x7fcc880c5a50> >" + " *' at 0x7fbccc3646c0> >" ] }, "metadata": {}, @@ -5955,10 +5955,414 @@ "display(aut)" ] }, + { + "cell_type": "markdown", + "id": "7efe7450", + "metadata": {}, + "source": [ + "# Test improved aiger INF encoding" + ] + }, + { + "cell_type": "code", + "execution_count": 25, + "id": "31872ccc", + "metadata": {}, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "t\n", + "[all]\n", + "\n", + "\n", + "\n", + "1\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "I->1\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "2\n", + "\n", + "2\n", + "\n", + "\n", + "\n", + "1->2\n", + "\n", + "\n", + "!a & !b & !c & !d\n", + "\n", + "\n", + "\n", + "3\n", + "\n", + "3\n", + "\n", + "\n", + "\n", + "1->3\n", + "\n", + "\n", + "a | b | c | d\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0\n", + "\n", + "\n", + "\n", + "0->2\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "2->0\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "3->0\n", + "\n", + "\n", + "x\n", + "\n", + "\n", + "\n" + ], + "text/plain": [ + " *' at 0x7fbccc3911e0> >" + ] + }, + "execution_count": 25, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "si = spot.synthesis_info()\n", + "\n", + "aut = spot.ltl_to_game(\"(a|b|c|d)->x\", [\"x\"], si)\n", + "aut" + ] + }, + { + "cell_type": "code", + "execution_count": 26, + "id": "9064bc60", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "True\n" + ] + } + ], + "source": [ + "print(spot.solve_game(aut))" + ] + }, + { + "cell_type": "code", + "execution_count": 27, + "id": "303ada1e", + "metadata": {}, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "t\n", + "[all]\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "2\n", + "\n", + "2\n", + "\n", + "\n", + "\n", + "0->2\n", + "\n", + "\n", + "!a & !b & !c & !d\n", + "\n", + "\n", + "\n", + "3\n", + "\n", + "3\n", + "\n", + "\n", + "\n", + "0->3\n", + "\n", + "\n", + "a | b | c | d\n", + "\n", + "\n", + "\n", + "1\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "2->1\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "3->1\n", + "\n", + "\n", + "x\n", + "\n", + "\n", + "\n", + "1->2\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n" + ], + "text/plain": [ + " *' at 0x7fbcd407ca20> >" + ] + }, + "execution_count": 27, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "ctrl = spot.solved_game_to_split_mealy(aut)\n", + "ctrl" + ] + }, + { + "cell_type": "code", + "execution_count": 28, + "id": "9874a530", + "metadata": {}, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "10\n", + "\n", + "L0_out\n", + "\n", + "\n", + "\n", + "18\n", + "\n", + "18\n", + "\n", + "\n", + "\n", + "10->18\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "12\n", + "\n", + "12\n", + "\n", + "\n", + "\n", + "14\n", + "\n", + "14\n", + "\n", + "\n", + "\n", + "12->14\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "16\n", + "\n", + "16\n", + "\n", + "\n", + "\n", + "14->16\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "16->18\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "o0\n", + "\n", + "x\n", + "\n", + "\n", + "\n", + "18->o0:s\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "L0\n", + "\n", + "L0_in\n", + "\n", + "\n", + "\n", + "2\n", + "\n", + "a\n", + "\n", + "\n", + "\n", + "2->16\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "4\n", + "\n", + "b\n", + "\n", + "\n", + "\n", + "4->14\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "6\n", + "\n", + "c\n", + "\n", + "\n", + "\n", + "6->12\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "8\n", + "\n", + "d\n", + "\n", + "\n", + "\n", + "8->12\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "False\n", + "\n", + "\n", + "\n", + "0->L0\n", + "\n", + "\n", + "\n", + "\n", + "\n" + ], + "text/plain": [ + " >" + ] + }, + "execution_count": 28, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "aig = spot.mealy_machine_to_aig(ctrl, \"ite\")\n", + "aig" + ] + }, { "cell_type": "code", "execution_count": null, - "id": "f3b2d981", + "id": "eb81b7d3", "metadata": {}, "outputs": [], "source": [] From 4a24739c3f38c42551e3b2f322f0882b4b2e3ba2 Mon Sep 17 00:00:00 2001 From: Philipp Schlehuber-Caissier Date: Wed, 14 Sep 2022 16:44:12 +0200 Subject: [PATCH 136/337] Improving minimize_mealy benchmarking * python/spot/__init__.py: Adding helper function for inline plot of csv *spot/twaalgos/mealy_machine.cc, spot/twaalgos/mealy_machine.hh: Main changes * tests/python/_mealy.ipynb: Update * tests/python/ipnbdoctest.py: Ignore timing table * tests/python/synthesis.ipynb: Update --- python/spot/__init__.py | 30 + spot/twaalgos/mealy_machine.cc | 290 +++++-- spot/twaalgos/mealy_machine.hh | 21 + tests/python/_mealy.ipynb | 1291 +++++++++++++++++++++++++++++++- tests/python/ipnbdoctest.py | 4 + tests/python/synthesis.ipynb | 36 +- 6 files changed, 1609 insertions(+), 63 deletions(-) diff --git a/python/spot/__init__.py b/python/spot/__init__.py index 340eba00a..01210c824 100644 --- a/python/spot/__init__.py +++ b/python/spot/__init__.py @@ -1298,6 +1298,36 @@ def sat_minimize(aut, acc=None, colored=False, else: return sm(aut, args, state_based) +# Adding the inline csv-display option +def minimize_mealy(mm, opt = -1, display_log = False, return_log = False): + from spot.impl import minimize_mealy as minmealy + + try: + lvl = int(opt) + opt = synthesis_info() + opt.minimize_lvl = lvl + 4 + except (ValueError, TypeError) as _: + pass + + if display_log or return_log: + import pandas as pd + with tempfile.NamedTemporaryFile(dir='.', suffix='.minlog') as t: + opt.opt.set_str("satlogcsv", t.name) + resmm = minmealy(mm, opt) + + dfrm = pd.read_csv(t.name, dtype=object) + if display_log: + from IPython.display import display + del dfrm['instance'] + display(dfrm) + if return_log: + return resmm, dfrm + else: + return resmm + else: + return minmealy(mm, opt) + + def parse_word(word, dic=_bdd_dict): from spot.impl import parse_word as pw diff --git a/spot/twaalgos/mealy_machine.cc b/spot/twaalgos/mealy_machine.cc index 6bbb9c4f7..3635e6334 100644 --- a/spot/twaalgos/mealy_machine.cc +++ b/spot/twaalgos/mealy_machine.cc @@ -27,6 +27,7 @@ #include #include #include +#include #include #include @@ -88,6 +89,33 @@ namespace #endif } +namespace +{ + static std::unique_ptr sat_csv_file; + struct fwrapper{ + std::string fname; + std::FILE* f; + fwrapper(const std::string& name) + : fname{name} + , f{std::fopen(name.c_str(), "a")} + { + if (!f) + throw std::runtime_error("File could not be oppened for writing."); + } + ~fwrapper() + { + std::fclose(f); + f = nullptr; + } + fwrapper& operator=(const fwrapper&) = delete; + fwrapper& operator=(fwrapper&&) = delete; + fwrapper(const fwrapper&) = delete; + fwrapper(fwrapper&&) = delete; + }; + static std::unique_ptr sat_dimacs_file; + static std::string sat_instance_name = ""; +} + namespace spot { @@ -817,6 +845,119 @@ namespace #else void trace_clause(const std::vector&){} #endif + struct satprob_info + { + stopwatch sw; + + double premin_time, reorg_time, partsol_time, player_incomp_time, + incomp_time, split_all_let_time, split_min_let_time, + split_cstr_time, prob_init_build_time, sat_time, + build_time, refine_time, total_time; + long long n_classes, n_refinement, n_lit, n_clauses, + n_iteration, n_bisim_let, n_min_states, done; + std::string task; + const std::string instance; + + satprob_info(const std::string& instance) + : premin_time{-1} + , reorg_time{-1} + , partsol_time{-1} + , player_incomp_time{-1} + , incomp_time{-1} + , split_all_let_time{-1} + , split_min_let_time{-1} + , split_cstr_time{-1} + , prob_init_build_time{-1} + , sat_time{-1} + , build_time{-1} + , refine_time{-1} + , total_time{-1} + , n_classes{-1} + , n_refinement{-1} + , n_lit{-1} + , n_clauses{-1} + , n_iteration{-1} + , n_bisim_let{-1} + , n_min_states{-1} + , done{-1} + , task{} + , instance{instance+","} + { + } + + void start() + { + sw.start(); + } + double stop() + { + return sw.stop(); + } + double restart() + { + double res = sw.stop(); + sw.start(); + return res; + } + // Writing also "flushes" + void write() + { + auto f = [](std::ostream& o, auto& v, bool sep = true) + { + if (v >= 0) + o << v; + if (sep) + o.put(','); + v = -1; + }; + if (!sat_csv_file) + return; + + auto& out = *sat_csv_file; + if (out.tellp() == 0) + { + out << "instance,task,premin_time,reorg_time,partsol_time," + << "player_incomp_time,incomp_time,split_all_let_time," + << "split_min_let_time,split_cstr_time,prob_init_build_time," + << "sat_time,build_time,refine_time,total_time,n_classes," + << "n_refinement,n_lit,n_clauses,n_iteration,n_bisim_let," + << "n_min_states,done\n"; + } + + assert(!task.empty()); + out << instance; + out << task; + task = ""; + out.put(','); + + std::stringstream ss; + + f(ss, premin_time); + f(ss, reorg_time); + f(ss, partsol_time); + f(ss, player_incomp_time); + f(ss, incomp_time); + f(ss, split_all_let_time); + f(ss, split_min_let_time); + f(ss, split_cstr_time); + f(ss, prob_init_build_time); + f(ss, sat_time); + f(ss, build_time); + f(ss, refine_time); + f(ss, total_time); + f(ss, n_classes); + f(ss, n_refinement); + f(ss, n_lit); + f(ss, n_clauses); + f(ss, n_iteration); + f(ss, n_bisim_let); + f(ss, n_min_states); + f(ss, done, false); + out << ss.str(); + out.put('\n'); + } + }; + template bool all_of(const CONT& c) @@ -1125,7 +1266,7 @@ namespace square_matrix compute_incomp(const_twa_graph_ptr mm, const unsigned n_env, - stopwatch& sw) + satprob_info& si) { const unsigned n_tot = mm->num_states(); @@ -1201,7 +1342,7 @@ namespace return inc_player.get(ps2c[s1].second, ps2c[s2].second); }; - dotimeprint << "Done computing player incomp " << sw.stop() << '\n'; + si.player_incomp_time = si.restart(); #ifdef TRACE trace << "player cond id incomp\n"; @@ -1284,7 +1425,7 @@ namespace trace << "Env state incomp\n"; inc_env.print(std::cerr); #endif - + si.incomp_time = si.restart(); return inc_env; } @@ -1912,26 +2053,22 @@ namespace std::pair reduce_and_split(const_twa_graph_ptr mmw, const unsigned n_env, const square_matrix& incompmat, - stopwatch& sw) + satprob_info& si) { reduced_alphabet_t red; + si.start(); + std::tie(red.n_groups, red.which_group) = trans_comp_classes(incompmat); - dotimeprint << "Done trans comp " << red.n_groups - << " - " << sw.stop() << '\n'; compute_all_letters(red, mmw, n_env); - dotimeprint << "Done comp all letters " << " - " << sw.stop() << '\n'; + si.split_all_let_time = si.restart(); compute_minimal_letters(red, mmw, n_env); -#ifdef MINTIMINGS - dotimeprint << "Done comp all min sim letters "; - for (const auto& al : red.bisim_letters) - dotimeprint << al.size() << ' '; - dotimeprint << " - " << sw.stop() << '\n'; -#endif + si.split_min_let_time = si.restart(); + si.n_bisim_let = red.n_red_sigma; twa_graph_ptr split_mmw = split_on_minimal(red, mmw, n_env); - dotimeprint << "Done splitting " << sw.stop() << '\n'; + si.split_cstr_time = si.restart(); trace << std::endl; return std::make_pair(split_mmw, red); @@ -2234,9 +2371,10 @@ namespace struct mm_sat_prob_t { mm_sat_prob_t(unsigned n_classes, unsigned n_env, - unsigned n_sigma_red) + unsigned n_sigma_red, satprob_info& si) : lm(n_classes, n_env, n_sigma_red) , n_classes{lm.n_classes_} + , si{si} { state_cover_clauses.reserve(n_classes); trans_cover_clauses.reserve(n_classes*n_sigma_red); @@ -2288,6 +2426,13 @@ namespace // res[i] == -1 : i not used in lit mapper // res[i] == 0 : i is assigned false // res[i] == 1 : i is assigned true + if (sat_dimacs_file) + { + fprintf(sat_dimacs_file->f, + "c ### Next Instance %lld %lld ###\n", + this->si.n_classes, this->si.n_refinement); + picosat_print(lm.psat_, sat_dimacs_file->f); + } switch (picosat_sat(lm.psat_, -1)) { case PICOSAT_UNSATISFIABLE: @@ -2353,6 +2498,8 @@ namespace std::unordered_map> cube_map; // A map that indicates if two cubes are compatible or not via their id std::unordered_map, bool, pair_hash> cube_incomp_map; + // Piggy-back a struct for performance measure + satprob_info& si; }; template<> @@ -2431,14 +2578,15 @@ namespace const square_matrix& incompmat, const reduced_alphabet_t& red, const part_sol_t& psol, - const unsigned n_env) + const unsigned n_env, + satprob_info& si) { const auto& psolv = psol.psol; const unsigned n_classes = psolv.size(); const unsigned n_red = red.n_red_sigma; const unsigned n_groups = red.n_groups; - mm_sat_prob_t mm_pb(n_classes, n_env, n_red); + mm_sat_prob_t mm_pb(n_classes, n_env, n_red, si); auto& lm = mm_pb.lm; @@ -3372,7 +3520,7 @@ namespace const reduced_alphabet_t& red, const part_sol_t& psol, const unsigned n_env, - stopwatch& sw) + satprob_info& si) { const auto& psolv = psol.psol; const unsigned n_psol = psolv.size(); @@ -3393,15 +3541,16 @@ namespace mm_pb.lm.print(std::cerr); #endif mm_pb.set_variable_clauses(); - dotimeprint << "Done constructing SAT " << sw.stop() << '\n'; - dotimeprint << "n literals " << mm_pb.n_lits() - << " n clauses " << mm_pb.n_clauses() << '\n'; + si.n_lit = mm_pb.n_lits(); + si.n_clauses = mm_pb.n_clauses(); + si.start(); auto sol = mm_pb.get_sol(); - dotimeprint << "Done solving SAT " << sw.stop() << '\n'; + si.sat_time = si.restart(); if (sol.empty()) { mm_pb.unset_variable_clauses(); + si.write(); return nullptr; } #ifdef TRACE @@ -3606,20 +3755,25 @@ namespace for (const auto& el : used_ziaj_map) if (el.second == bddfalse) infeasible_classes.emplace_back(el.first.i, el.first.a); + si.build_time = si.restart(); + if (!infeasible_classes.empty()) { // Remove the variable clauses // This is suboptimal but the contexts form a stack so... - dotimeprint << "Refining constraints for " - << infeasible_classes.size() << " classses.\n"; + auto oldrefine = si.n_refinement; + si.write(); + si.task = "refinement"; + si.n_classes = n_classes; + si.n_refinement = oldrefine + infeasible_classes.size(); mm_pb.unset_variable_clauses(); add_bdd_cond_constr(mm_pb, mmw, red, n_env, infeasible_classes, x_in_class); + si.refine_time = si.restart(); continue; //retry } cstr_split_mealy(minmach, red, x_in_class, used_ziaj_map); - // todo: What is the impact of chosing one of the possibilities minmach->set_init_state(init_class_v.front()); return minmach; @@ -3634,8 +3788,10 @@ namespace spot { assert(is_mealy(mm)); - stopwatch sw; - sw.start(); + satprob_info si(sat_instance_name); + si.task = "presat"; + stopwatch sglob; + sglob.start(); if ((premin < -1) || (premin > 1)) throw std::runtime_error("premin has to be -1, 0 or 1"); @@ -3672,7 +3828,9 @@ namespace spot const_twa_graph_ptr mmw = do_premin(); assert(is_split_mealy(mmw)); - dotimeprint << "Done premin " << sw.stop() << '\n'; + si.premin_time = si.restart(); + + // 0 -> "Env" next is input props // 1 -> "Player" next is output prop @@ -3690,24 +3848,24 @@ namespace spot print_hoa(std::cerr, mmw); #endif assert(n_env != -1u); - dotimeprint << "Done reorganise " << n_env << " - " - << sw.stop() << '\n'; + si.reorg_time = si.restart(); // Compute incompatibility based on bdd - auto incompmat = compute_incomp(mmw, n_env, sw); - dotimeprint << "Done incompatibility " << sw.stop() << '\n'; + auto incompmat = compute_incomp(mmw, n_env, si); #ifdef TRACE incompmat.print(std::cerr); #endif // Get a partial solution auto partsol = get_part_sol(incompmat); - dotimeprint << "Done partial solution " << partsol.psol.size() - << " - " << sw.stop() << '\n'; + si.partsol_time = si.restart(); auto early_exit = [&]() { + si.done = 1; + si.total_time = sglob.stop(); + si.write(); // Always keep machines split if (mm->get_named_prop("state-player")) assert(is_split_mealy_specialization(mm, mmw)); @@ -3721,58 +3879,78 @@ namespace spot // states as the original automaton -> we are done if (partsol.psol.size() == n_env) { - dotimeprint << "Done trans comp " << 1 << " - " << sw.stop() << '\n'; - dotimeprint << "Done comp all letters " << " - " - << sw.stop() << '\n'; -#ifdef MINTIMINGS - dotimeprint << "Done comp all min sim letters 0 - " - << sw.stop() << '\n'; -#endif - dotimeprint << "Done splitting " << sw.stop() << '\n'; - dotimeprint << "Done split and reduce " << sw.stop() << '\n'; - dotimeprint << "Done build init prob " << sw.stop() << '\n'; - dotimeprint << "Done minimizing - " << mmw->num_states() - << " - " << sw.stop() << '\n'; return early_exit(); } // Get the reduced alphabet auto [split_mmw, reduced_alphabet] = - reduce_and_split(mmw, n_env, incompmat, sw); - dotimeprint << "Done split and reduce " << sw.stop() << '\n'; + reduce_and_split(mmw, n_env, incompmat, si); auto mm_pb = build_init_prob(split_mmw, incompmat, - reduced_alphabet, partsol, n_env); - dotimeprint << "Done build init prob " << sw.stop() << '\n'; + reduced_alphabet, partsol, n_env, si); + si.prob_init_build_time = si.restart(); + si.write(); twa_graph_ptr minmachine = nullptr; for (size_t n_classes = partsol.psol.size(); n_classes < n_env; ++n_classes) { + if (si.task.empty()) + si.task = "sat"; + si.n_iteration = (n_classes-partsol.psol.size()); + si.n_refinement = 0; + si.n_classes = n_classes; + minmachine = try_build_min_machine(mm_pb, mmw, reduced_alphabet, partsol, n_env, - sw); - dotimeprint << "Done try_build " << n_classes - << " - " << sw.stop() << '\n'; + si); if (minmachine) break; increment_classes(split_mmw, incompmat, reduced_alphabet, partsol, mm_pb); - dotimeprint << "Done incrementing " << sw.stop() << '\n'; + } // Is already minimal -> Return a copy // Set state players! if (!minmachine) return early_exit(); set_synthesis_outputs(minmachine, get_synthesis_outputs(mm)); - dotimeprint << "Done minimizing - " << minmachine->num_states() - << " - " << sw.stop() << '\n'; + + si.done=1; + si.n_min_states = minmachine->num_states(); + si.total_time = sglob.stop(); + si.write(); assert(is_split_mealy_specialization(mm, minmachine)); return minmachine; } + + twa_graph_ptr + minimize_mealy(const const_twa_graph_ptr& mm, + synthesis_info& si) + { + if ((si.minimize_lvl < 3) || (5 < si.minimize_lvl)) + throw std::runtime_error("Invalid option"); + + std::string csvfile = si.opt.get_str("satlogcsv"); + std::string dimacsfile = si.opt.get_str("satlogdimacs"); + + if (!csvfile.empty()) + sat_csv_file + = std::make_unique(csvfile, + std::ios_base::ate + | std::ios_base::app); + if (!dimacsfile.empty()) + sat_dimacs_file + = std::make_unique(dimacsfile); + sat_instance_name = si.opt.get_str("satinstancename"); + auto res = minimize_mealy(mm, si.minimize_lvl-4); + sat_csv_file.reset(); + sat_dimacs_file.reset(); + return res; + } } namespace spot diff --git a/spot/twaalgos/mealy_machine.hh b/spot/twaalgos/mealy_machine.hh index d603d8000..3bdb71b73 100644 --- a/spot/twaalgos/mealy_machine.hh +++ b/spot/twaalgos/mealy_machine.hh @@ -159,6 +159,27 @@ namespace spot SPOT_API twa_graph_ptr minimize_mealy(const const_twa_graph_ptr& mm, int premin = -1); + /// \ingroup mealy + /// \brief Minimizes an (in)completely specified mealy machine + /// + /// The approach is described in \cite renkin.22.forte. + /// + /// \param si synthesis_info structure used to store data for benchmarking + /// and indicates which premin level to use + /// + /// \return A split mealy machines which is a minimal + /// specialization of the original machine. + /// + /// \note Enabling \a premin will remove finite traces. + /// \note If si.opt contains an option "satlogcsv" detailed results will be + /// stored in this file. If it contains "satlogdimacs" all sat problems will + /// stored. + /// \see is_split_mealy_specialization + + SPOT_API twa_graph_ptr + minimize_mealy(const const_twa_graph_ptr& mm, + synthesis_info& si); + /// \ingroup mealy /// \brief Test if the split mealy machine \a right is a specialization of diff --git a/tests/python/_mealy.ipynb b/tests/python/_mealy.ipynb index c2aeb125c..9d7fe7d96 100644 --- a/tests/python/_mealy.ipynb +++ b/tests/python/_mealy.ipynb @@ -7,7 +7,8 @@ "metadata": {}, "outputs": [], "source": [ - "import spot\n", + "import spot, buddy\n", + "import pandas as pd\n", "spot.setup()" ] }, @@ -128,7 +129,7 @@ "\n" ], "text/plain": [ - " *' at 0x7fc1244a3d50> >" + " *' at 0x7fcc35aaa030> >" ] }, "execution_count": 4, @@ -208,7 +209,7 @@ "\n" ], "text/plain": [ - " *' at 0x7fc124439570> >" + " *' at 0x7fcc35aaa900> >" ] }, "execution_count": 6, @@ -282,7 +283,7 @@ "\n" ], "text/plain": [ - " *' at 0x7fc124439570> >" + " *' at 0x7fcc35aaa900> >" ] }, "execution_count": 8, @@ -296,9 +297,1284 @@ }, { "cell_type": "code", - "execution_count": null, + "execution_count": 9, "id": "923a59d6", "metadata": {}, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "1\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "0->1\n", + "\n", + "\n", + "\n", + "!i\n", + "/\n", + "\n", + "!o\n", + "\n", + "\n", + "\n", + "2\n", + "\n", + "2\n", + "\n", + "\n", + "\n", + "0->2\n", + "\n", + "\n", + "\n", + "i\n", + "/\n", + "\n", + "o\n", + "\n", + "\n", + "\n", + "1->1\n", + "\n", + "\n", + "\n", + "1\n", + "/\n", + "\n", + "!o\n", + "\n", + "\n", + "\n", + "2->2\n", + "\n", + "\n", + "\n", + "1\n", + "/\n", + "\n", + "1\n", + "\n", + "\n", + "\n" + ], + "text/plain": [ + " *' at 0x7fcc35ac20c0> >" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "aut = spot.make_twa_graph()\n", + "i = buddy.bdd_ithvar(aut.register_ap(\"i\"))\n", + "o = buddy.bdd_ithvar(aut.register_ap(\"o\"))\n", + "spot.set_synthesis_outputs(aut, o)\n", + "aut.new_states(3)\n", + "aut.new_edge(0,1,buddy.bdd_not(i)&buddy.bdd_not(o))\n", + "aut.new_edge(0,2,i&o)\n", + "aut.new_edge(1,1,buddy.bdd_not(o))\n", + "aut.new_edge(2,2,buddy.bddtrue)\n", + "aut" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "f06d6df4", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "('o',)\n" + ] + }, + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "t\n", + "[all]\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "3\n", + "\n", + "3\n", + "\n", + "\n", + "\n", + "0->3\n", + "\n", + "\n", + "!i\n", + "\n", + "\n", + "\n", + "4\n", + "\n", + "4\n", + "\n", + "\n", + "\n", + "0->4\n", + "\n", + "\n", + "i\n", + "\n", + "\n", + "\n", + "1\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "3->1\n", + "\n", + "\n", + "!o\n", + "\n", + "\n", + "\n", + "2\n", + "\n", + "2\n", + "\n", + "\n", + "\n", + "4->2\n", + "\n", + "\n", + "o\n", + "\n", + "\n", + "\n", + "1->3\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "5\n", + "\n", + "5\n", + "\n", + "\n", + "\n", + "2->5\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "5->2\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n" + ], + "text/plain": [ + " *' at 0x7fcc35ac2720> >" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "aut_s = spot.split_2step(aut)\n", + "print(spot.get_synthesis_output_aps(aut_s))\n", + "aut_s" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "3cc4d320", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
taskpremin_timereorg_timepartsol_timeplayer_incomp_timeincomp_timesplit_all_let_timesplit_min_let_timesplit_cstr_timeprob_init_build_time...refine_timetotal_timen_classesn_refinementn_litn_clausesn_iterationn_bisim_letn_min_statesdone
0presat25643.31.112e-064.588e-069.888e-064.549e-061.5929e-059.338e-065.901e-066.7276e-05...NaNNaNNaNNaNNaNNaNNaN2NaNNaN
1satNaNNaNNaNNaNNaNNaNNaNNaNNaN...NaN0.000282709207120NaN41
\n", + "

2 rows × 22 columns

\n", + "
" + ], + "text/plain": [ + " task premin_time reorg_time partsol_time player_incomp_time incomp_time \\\n", + "0 presat 25643.3 1.112e-06 4.588e-06 9.888e-06 4.549e-06 \n", + "1 sat NaN NaN NaN NaN NaN \n", + "\n", + " split_all_let_time split_min_let_time split_cstr_time prob_init_build_time \\\n", + "0 1.5929e-05 9.338e-06 5.901e-06 6.7276e-05 \n", + "1 NaN NaN NaN NaN \n", + "\n", + " ... refine_time total_time n_classes n_refinement n_lit n_clauses \\\n", + "0 ... NaN NaN NaN NaN NaN NaN \n", + "1 ... NaN 0.000282709 2 0 7 12 \n", + "\n", + " n_iteration n_bisim_let n_min_states done \n", + "0 NaN 2 NaN NaN \n", + "1 0 NaN 4 1 \n", + "\n", + "[2 rows x 22 columns]" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "t\n", + "[all]\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "2\n", + "\n", + "2\n", + "\n", + "\n", + "\n", + "0->2\n", + "\n", + "\n", + "i\n", + "\n", + "\n", + "\n", + "3\n", + "\n", + "3\n", + "\n", + "\n", + "\n", + "0->3\n", + "\n", + "\n", + "!i\n", + "\n", + "\n", + "\n", + "1\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "2->1\n", + "\n", + "\n", + "o\n", + "\n", + "\n", + "\n", + "3->1\n", + "\n", + "\n", + "!o\n", + "\n", + "\n", + "\n", + "1->3\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n" + ], + "text/plain": [ + " *' at 0x7fcc88735f00> >" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "min_lvl = 0\n", + "aut_ms, table = spot.minimize_mealy(aut_s, min_lvl, display_log=True, return_log=True)\n", + "aut_ms" + ] + }, + { + "cell_type": "markdown", + "id": "bc844797", + "metadata": {}, + "source": [ + "## A more involved example" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "893bc90e", + "metadata": {}, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "1\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "0->1\n", + "\n", + "\n", + "\n", + "1\n", + "/\n", + "\n", + "o1\n", + "\n", + "\n", + "\n", + "2\n", + "\n", + "2\n", + "\n", + "\n", + "\n", + "1->2\n", + "\n", + "\n", + "\n", + "1\n", + "/\n", + "\n", + "o0\n", + "\n", + "\n", + "\n", + "2->2\n", + "\n", + "\n", + "\n", + "1\n", + "/\n", + "\n", + "(!o0 & o1) | (o0 & !o1)\n", + "\n", + "\n", + "\n" + ], + "text/plain": [ + " *' at 0x7fcc6157fe40> >" + ] + }, + "execution_count": 12, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "aut = spot.make_twa_graph()\n", + "i = buddy.bdd_ithvar(aut.register_ap(\"i\"))\n", + "o0 = buddy.bdd_ithvar(aut.register_ap(\"o0\"))\n", + "no0 = buddy.bdd_not(o0)\n", + "o1 = buddy.bdd_ithvar(aut.register_ap(\"o1\"))\n", + "no1 = buddy.bdd_not(o1)\n", + "spot.set_synthesis_outputs(aut, o0&o1)\n", + "\n", + "vo1 = o0&o1\n", + "vo2 = no0&o1\n", + "vo3 = o0&no1\n", + "\n", + "aut.new_states(3)\n", + "\n", + "aut.new_edge(0,1,vo1|vo2)\n", + "aut.new_edge(1,2,vo1|vo3)\n", + "aut.new_edge(2,2,vo2|vo3)\n", + "aut" + ] + }, + { + "cell_type": "code", + "execution_count": 13, + "id": "23edb107", + "metadata": {}, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "t\n", + "[all]\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "3\n", + "\n", + "3\n", + "\n", + "\n", + "\n", + "0->3\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "1\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "3->1\n", + "\n", + "\n", + "o1\n", + "\n", + "\n", + "\n", + "4\n", + "\n", + "4\n", + "\n", + "\n", + "\n", + "1->4\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "2\n", + "\n", + "2\n", + "\n", + "\n", + "\n", + "4->2\n", + "\n", + "\n", + "o0\n", + "\n", + "\n", + "\n", + "5\n", + "\n", + "5\n", + "\n", + "\n", + "\n", + "2->5\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "5->2\n", + "\n", + "\n", + "(!o0 & o1) | (o0 & !o1)\n", + "\n", + "\n", + "\n" + ], + "text/plain": [ + " *' at 0x7fcc6157f210> >" + ] + }, + "execution_count": 13, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "aut_s = spot.split_2step(aut)\n", + "aut_s" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "id": "837aab84", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
taskpremin_timereorg_timepartsol_timeplayer_incomp_timeincomp_timesplit_all_let_timesplit_min_let_timesplit_cstr_timeprob_init_build_time...refine_timetotal_timen_classesn_refinementn_litn_clausesn_iterationn_bisim_letn_min_statesdone
0presat25643.41.683e-065.611e-062.66e-051.2e-073.647e-068.365e-063.747e-062.5538e-05...NaNNaNNaNNaNNaNNaNNaN1NaNNaN
1satNaNNaNNaNNaNNaNNaNNaNNaNNaN...NaNNaN10360NaNNaNNaN
2refinementNaNNaNNaNNaNNaNNaNNaNNaNNaN...4.4884e-05NaN111016NaNNaNNaNNaN
3satNaNNaNNaNNaNNaNNaNNaNNaNNaN...NaN0.0002003442017291NaN41
\n", + "

4 rows × 22 columns

\n", + "
" + ], + "text/plain": [ + " task premin_time reorg_time partsol_time player_incomp_time \\\n", + "0 presat 25643.4 1.683e-06 5.611e-06 2.66e-05 \n", + "1 sat NaN NaN NaN NaN \n", + "2 refinement NaN NaN NaN NaN \n", + "3 sat NaN NaN NaN NaN \n", + "\n", + " incomp_time split_all_let_time split_min_let_time split_cstr_time \\\n", + "0 1.2e-07 3.647e-06 8.365e-06 3.747e-06 \n", + "1 NaN NaN NaN NaN \n", + "2 NaN NaN NaN NaN \n", + "3 NaN NaN NaN NaN \n", + "\n", + " prob_init_build_time ... refine_time total_time n_classes n_refinement \\\n", + "0 2.5538e-05 ... NaN NaN NaN NaN \n", + "1 NaN ... NaN NaN 1 0 \n", + "2 NaN ... 4.4884e-05 NaN 1 1 \n", + "3 NaN ... NaN 0.000200344 2 0 \n", + "\n", + " n_lit n_clauses n_iteration n_bisim_let n_min_states done \n", + "0 NaN NaN NaN 1 NaN NaN \n", + "1 3 6 0 NaN NaN NaN \n", + "2 10 16 NaN NaN NaN NaN \n", + "3 17 29 1 NaN 4 1 \n", + "\n", + "[4 rows x 22 columns]" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "0 NaN\n", + "1 3\n", + "2 10\n", + "3 17\n", + "Name: n_lit, dtype: object\n" + ] + }, + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "t\n", + "[all]\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "2\n", + "\n", + "2\n", + "\n", + "\n", + "\n", + "0->2\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "1\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "2->1\n", + "\n", + "\n", + "o0 & o1\n", + "\n", + "\n", + "\n", + "3\n", + "\n", + "3\n", + "\n", + "\n", + "\n", + "1->3\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "3->1\n", + "\n", + "\n", + "o0 & !o1\n", + "\n", + "\n", + "\n" + ], + "text/plain": [ + " *' at 0x7fcc35ac22a0> >" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "si = spot.synthesis_info()\n", + "si.minimize_lvl = 3\n", + "aut_ms, table = spot.minimize_mealy(aut_s, si, display_log=True, return_log=True)\n", + "print(table[\"n_lit\"])\n", + "aut_ms" + ] + }, + { + "cell_type": "markdown", + "id": "0fea0269", + "metadata": {}, + "source": [ + "## Testing dimacs output" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "id": "d14324e8", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + " \n", + "
taskpremin_timereorg_timepartsol_timeplayer_incomp_timeincomp_timesplit_all_let_timesplit_min_let_timesplit_cstr_timeprob_init_build_time...refine_timetotal_timen_classesn_refinementn_litn_clausesn_iterationn_bisim_letn_min_statesdone
0presat25643.51.563e-065.4e-062.0519e-051.3e-073.968e-069.698e-067.624e-063.211e-05...NaNNaNNaNNaNNaNNaNNaN1NaNNaN
1satNaNNaNNaNNaNNaNNaNNaNNaNNaN...NaNNaN10360NaNNaNNaN
2refinementNaNNaNNaNNaNNaNNaNNaNNaNNaN...4.4633e-05NaN111016NaNNaNNaNNaN
3satNaNNaNNaNNaNNaNNaNNaNNaNNaN...NaN0.0002806752017291NaN41
\n", + "

4 rows × 22 columns

\n", + "
" + ], + "text/plain": [ + " task premin_time reorg_time partsol_time player_incomp_time \\\n", + "0 presat 25643.5 1.563e-06 5.4e-06 2.0519e-05 \n", + "1 sat NaN NaN NaN NaN \n", + "2 refinement NaN NaN NaN NaN \n", + "3 sat NaN NaN NaN NaN \n", + "\n", + " incomp_time split_all_let_time split_min_let_time split_cstr_time \\\n", + "0 1.3e-07 3.968e-06 9.698e-06 7.624e-06 \n", + "1 NaN NaN NaN NaN \n", + "2 NaN NaN NaN NaN \n", + "3 NaN NaN NaN NaN \n", + "\n", + " prob_init_build_time ... refine_time total_time n_classes n_refinement \\\n", + "0 3.211e-05 ... NaN NaN NaN NaN \n", + "1 NaN ... NaN NaN 1 0 \n", + "2 NaN ... 4.4633e-05 NaN 1 1 \n", + "3 NaN ... NaN 0.000280675 2 0 \n", + "\n", + " n_lit n_clauses n_iteration n_bisim_let n_min_states done \n", + "0 NaN NaN NaN 1 NaN NaN \n", + "1 3 6 0 NaN NaN NaN \n", + "2 10 16 NaN NaN NaN NaN \n", + "3 17 29 1 NaN 4 1 \n", + "\n", + "[4 rows x 22 columns]" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "c ### Next Instance 1 0 ###\n", + "p cnf 5 5\n", + "-1 2 -3 0\n", + "1 -3 0\n", + "1 -5 0\n", + "2 -5 0\n", + "3 -5 0\n", + "c ### Next Instance 1 1 ###\n", + "p cnf 12 15\n", + "-1 2 -3 0\n", + "4 0\n", + "6 0\n", + "-9 0\n", + "-1 -2 10 0\n", + "-10 0\n", + "1 -3 0\n", + "1 -5 0\n", + "1 -12 0\n", + "2 -5 0\n", + "2 -12 0\n", + "-2 9 0\n", + "3 -5 0\n", + "3 -12 0\n", + "7 8 0\n", + "c ### Next Instance 2 0 ###\n", + "p cnf 19 29\n", + "-3 -1 2 0\n", + "4 0\n", + "6 0\n", + "-9 0\n", + "-1 -2 10 0\n", + "-10 0\n", + "11 -16 -17 0\n", + "1 -15 -17 0\n", + "-1 13 -14 0\n", + "-11 13 -16 0\n", + "-11 -15 2 0\n", + "-13 -15 2 0\n", + "1 11 -19 0\n", + "13 -19 2 0\n", + "15 16 -19 0\n", + "3 14 -19 0\n", + "-2 0\n", + "-12 0\n", + "-5 0\n", + "1 -3 0\n", + "1 -5 0\n", + "1 -12 0\n", + "2 -5 0\n", + "2 -12 0\n", + "-2 9 0\n", + "3 -5 0\n", + "3 -12 0\n", + "7 8 0\n", + "11 -14 0\n", + "\n" + ] + } + ], + "source": [ + "import tempfile\n", + "\n", + "si = spot.synthesis_info()\n", + "si.minimize_lvl = 3\n", + "\n", + "with tempfile.NamedTemporaryFile(dir='.', suffix='.dimacslog') as t:\n", + " si.opt.set_str(\"satlogdimacs\", t.name)\n", + " aut_ms, table = spot.minimize_mealy(aut_s, si, display_log=True, return_log=True)\n", + " with open(t.name, \"r\") as f:\n", + " print(\"\".join(f.readlines()))\n", + " \n", + " " + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "5c9fe115", + "metadata": {}, "outputs": [], "source": [] } @@ -320,6 +1596,11 @@ "nbconvert_exporter": "python", "pygments_lexer": "ipython3", "version": "3.8.10" + }, + "vscode": { + "interpreter": { + "hash": "916dbcbb3f70747c44a77c7bcd40155683ae19c65e1c03b4aa3499c5328201f1" + } } }, "nbformat": 4, diff --git a/tests/python/ipnbdoctest.py b/tests/python/ipnbdoctest.py index 18da81cf8..c6bfcf134 100755 --- a/tests/python/ipnbdoctest.py +++ b/tests/python/ipnbdoctest.py @@ -143,6 +143,10 @@ def canonicalize(s, type, ignores): # timing result we cannot compare between runs. s = re.sub(r'', '
', s, flags=re.DOTALL) + # Table that contains premin_time are log from the mealy minimization. + # They contain timing result so we cannot compare between runs. + s = re.sub(r'', '
', + s, flags=re.DOTALL) for n, p in enumerate(ignores): s = re.sub(p, 'IGN{}'.format(n), s) diff --git a/tests/python/synthesis.ipynb b/tests/python/synthesis.ipynb index 654d22873..54da20ef7 100644 --- a/tests/python/synthesis.ipynb +++ b/tests/python/synthesis.ipynb @@ -3,6 +3,7 @@ { "cell_type": "code", "execution_count": 1, + "id": "4f84fa79", "metadata": {}, "outputs": [], "source": [ @@ -13,6 +14,7 @@ }, { "cell_type": "markdown", + "id": "4ad017a0", "metadata": {}, "source": [ "This notebook presents functions that can be used to solve the Reactive Synthesis problem using games.\n", @@ -37,6 +39,7 @@ { "cell_type": "code", "execution_count": 2, + "id": "e333be09", "metadata": {}, "outputs": [ { @@ -655,6 +658,7 @@ }, { "cell_type": "markdown", + "id": "4d030586", "metadata": {}, "source": [ "Solving the game, is done with `solve_game()` as with any game. There is also a version that takes a `synthesis_info` as second argument in case the time it takes has to be recorded. Here passing `si` or not makes no difference." @@ -663,6 +667,7 @@ { "cell_type": "code", "execution_count": 3, + "id": "f13ac820", "metadata": {}, "outputs": [ { @@ -1222,6 +1227,7 @@ }, { "cell_type": "markdown", + "id": "98aa1402", "metadata": {}, "source": [ "Once a strategy has been found, it can be extracted as an automaton and simplified using 6 different levels (the default is 2). The output should be interpreted as a Mealy automaton, where transition have the form `(ins)&(outs)` where `ins` and `outs` are Boolean formulas representing possible inputs and outputs (they could be more than just conjunctions of atomic proposition). Mealy machines with this type of labels are called \"separated\" in Spot." @@ -1230,6 +1236,7 @@ { "cell_type": "code", "execution_count": 4, + "id": "4c93add7", "metadata": {}, "outputs": [ { @@ -2228,6 +2235,7 @@ }, { "cell_type": "markdown", + "id": "9d8d52f6", "metadata": {}, "source": [ "If needed, a separated Mealy machine can be turned into game shape using `split_sepearated_mealy()`, which is more efficient than `split_2step()`." @@ -2236,6 +2244,7 @@ { "cell_type": "code", "execution_count": 5, + "id": "707f4cf6", "metadata": {}, "outputs": [ { @@ -2517,6 +2526,7 @@ }, { "cell_type": "markdown", + "id": "b9e4412e", "metadata": {}, "source": [ "# Converting the separated Mealy machine to AIG\n", @@ -2529,6 +2539,7 @@ { "cell_type": "code", "execution_count": 6, + "id": "9f344931", "metadata": {}, "outputs": [ { @@ -2604,6 +2615,7 @@ }, { "cell_type": "markdown", + "id": "92bbe8d0", "metadata": {}, "source": [ "While we are at it, let us mention that you can render those circuits horizontally as follows:" @@ -2612,6 +2624,7 @@ { "cell_type": "code", "execution_count": 7, + "id": "3ae7ce32", "metadata": {}, "outputs": [ { @@ -2687,6 +2700,7 @@ }, { "cell_type": "markdown", + "id": "44fbc0ac", "metadata": {}, "source": [ "To encode the circuit in the AIGER format (ASCII version) use:" @@ -2695,6 +2709,7 @@ { "cell_type": "code", "execution_count": 8, + "id": "566715d5", "metadata": {}, "outputs": [ { @@ -2718,6 +2733,7 @@ }, { "cell_type": "markdown", + "id": "ef304f36", "metadata": {}, "source": [ "# Adding more inputs and outputs by force" @@ -2725,6 +2741,7 @@ }, { "cell_type": "markdown", + "id": "5c2b0b78", "metadata": {}, "source": [ "It can happen that propositions declared as output are ommited in the aig circuit (either because they are not part of the specification, or because they do not appear in the winning strategy). In that case those \n", @@ -2736,6 +2753,7 @@ { "cell_type": "code", "execution_count": 9, + "id": "874c7df1", "metadata": {}, "outputs": [ { @@ -3260,6 +3278,7 @@ }, { "cell_type": "markdown", + "id": "c564dba3", "metadata": {}, "source": [ "To force the presence of extra variables in the circuit, they can be passed to `mealy_machine_to_aig()`." @@ -3268,6 +3287,7 @@ { "cell_type": "code", "execution_count": 10, + "id": "c31a3b38", "metadata": {}, "outputs": [ { @@ -3378,6 +3398,7 @@ }, { "cell_type": "markdown", + "id": "3323fc84", "metadata": {}, "source": [ "# Combining Mealy machines\n", @@ -3397,6 +3418,7 @@ { "cell_type": "code", "execution_count": 11, + "id": "5d8e4cdb", "metadata": {}, "outputs": [ { @@ -3991,6 +4013,7 @@ }, { "cell_type": "markdown", + "id": "c7a1986f", "metadata": {}, "source": [ "# Reading an AIGER-file\n", @@ -4005,6 +4028,7 @@ { "cell_type": "code", "execution_count": 12, + "id": "a10d7e3b", "metadata": {}, "outputs": [], "source": [ @@ -4025,6 +4049,7 @@ { "cell_type": "code", "execution_count": 13, + "id": "2c40e19b", "metadata": {}, "outputs": [ { @@ -4149,6 +4174,7 @@ { "cell_type": "code", "execution_count": 14, + "id": "0ad6c566", "metadata": {}, "outputs": [ { @@ -4177,6 +4203,7 @@ { "cell_type": "code", "execution_count": 15, + "id": "2e1996c1", "metadata": {}, "outputs": [ { @@ -4193,6 +4220,7 @@ }, { "cell_type": "markdown", + "id": "41a8e042", "metadata": {}, "source": [ "An AIG circuit can be transformed into a monitor/Mealy machine. This can be used for instance to check that it does not intersect the negation of the specification." @@ -4201,6 +4229,7 @@ { "cell_type": "code", "execution_count": 16, + "id": "7399ea38", "metadata": {}, "outputs": [ { @@ -4268,6 +4297,7 @@ }, { "cell_type": "markdown", + "id": "7ac06afc", "metadata": {}, "source": [ "Note that the generation of aiger circuits from Mealy machines is flexible and accepts separated Mealy machines\n", @@ -4277,6 +4307,7 @@ { "cell_type": "code", "execution_count": 17, + "id": "bac68923", "metadata": {}, "outputs": [ { @@ -4424,6 +4455,7 @@ { "cell_type": "code", "execution_count": 18, + "id": "03ceb2a8", "metadata": {}, "outputs": [ { @@ -4626,7 +4658,7 @@ ], "metadata": { "kernelspec": { - "display_name": "Python 3", + "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, @@ -4640,7 +4672,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.7.3" + "version": "3.8.10" } }, "nbformat": 4, From c45ff0c94ce4a53595d25423590c8d915148e30b Mon Sep 17 00:00:00 2001 From: Philipp Schlehuber-Caissier Date: Wed, 21 Sep 2022 16:02:49 +0200 Subject: [PATCH 137/337] fix: ltlsynt --tlsf does not propagate name to csv * bin/ltlsynt.cc: Here --- bin/ltlsynt.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/ltlsynt.cc b/bin/ltlsynt.cc index e0cf78c47..630ccd629 100644 --- a/bin/ltlsynt.cc +++ b/bin/ltlsynt.cc @@ -705,7 +705,7 @@ namespace } if (opt_csv) - print_csv(f); + print_csv(f, filename); return res; } From aa7992c65f19204d10d25d313d2c89057d15de72 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Wed, 21 Sep 2022 14:04:18 +0200 Subject: [PATCH 138/337] simplify some uses of minato_isop Typically intead of doing minato_isop isop(rel & letter); while (bdd cube = isop.next()) { bdd res = bdd_exists(cube, ap) ... } do minato_isop isop(bdd_relprod(rel, letter, ap); while (bdd res = isop.next()) { ... } this way the existential quantification is done once at the same time of the conjunction, and isop has fewer variable to work with. * spot/twaalgos/alternation.cc, spot/twaalgos/dualize.cc, spot/twaalgos/simulation.cc, spot/twaalgos/toweak.cc: Here. --- spot/twaalgos/alternation.cc | 41 ++++++++++++++++++------------------ spot/twaalgos/dualize.cc | 15 ++++++------- spot/twaalgos/simulation.cc | 13 ++++-------- spot/twaalgos/toweak.cc | 15 ++++++------- 4 files changed, 36 insertions(+), 48 deletions(-) diff --git a/spot/twaalgos/alternation.cc b/spot/twaalgos/alternation.cc index a3762f9b0..8370f395b 100644 --- a/spot/twaalgos/alternation.cc +++ b/spot/twaalgos/alternation.cc @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2016-2019, 2021 Laboratoire de Recherche et +// Copyright (C) 2016-2019, 2021, 2022 Laboratoire de Recherche et // Développement de l'Epita (LRDE). // // This file is part of Spot, a model checking library. @@ -457,12 +457,10 @@ namespace spot // First loop over all possible valuations atomic properties. for (bdd oneletter: minterms_of(all_letters, ap)) { - minato_isop isop(bs & oneletter); - bdd cube; - while ((cube = isop.next()) != bddfalse) + minato_isop isop(bdd_relprod(bs, oneletter, ap)); + bdd dest; + while ((dest = isop.next()) != bddfalse) { - bdd cond = bdd_exist(cube, all_vars_); - bdd dest = bdd_existcomp(cube, all_vars_); v.clear(); acc_cond::mark_t m = bdd_to_state(dest, v); @@ -491,7 +489,7 @@ namespace spot unsigned d = new_state(v, has_mark); if (has_mark) m.set(0); - res->new_edge(s, d, cond, all_marks - m); + res->new_edge(s, d, oneletter, all_marks - m); } } } @@ -576,7 +574,8 @@ namespace spot bdd all_states_; bdd ap_; bdd all_letters_; - bdd transition_; + bdd dest_; + bdd cond_; minato_isop isop_; const std::map& var_to_state_; univ_remover_state* dst_; @@ -587,8 +586,8 @@ namespace spot const std::vector& state_to_var, const std::map& var_to_state, bdd all_states) - : transitions_(bddtrue), all_states_(all_states), transition_(bddfalse), - isop_(bddfalse), var_to_state_(var_to_state) + : transitions_(bddtrue), all_states_(all_states), dest_(bddfalse), + cond_(bddfalse), isop_(bddfalse), var_to_state_(var_to_state) { // Build the bdd transitions_, from which we extract the successors. for (unsigned s : state->states()) @@ -627,20 +626,20 @@ namespace spot void one_transition() { - transition_ = isop_.next(); - if (transition_ != bddfalse || all_letters_ != bddfalse) + dest_ = isop_.next(); + if (dest_ != bddfalse || all_letters_ != bddfalse) { // If it was the last transition, try the next letter. - if (transition_ == bddfalse) + if (dest_ == bddfalse) { bdd oneletter = bdd_satoneset(all_letters_, ap_, bddfalse); + cond_ = oneletter; all_letters_ -= oneletter; // Get a sum of possible transitions matching this letter. - isop_ = minato_isop(oneletter & transitions_); - transition_ = isop_.next(); + isop_ = minato_isop(bdd_relprod(transitions_, oneletter, ap_)); + dest_ = isop_.next(); } - bdd dest_bdd = bdd_exist(transition_, ap_); - std::set dest = bdd_to_state(dest_bdd); + std::set dest = bdd_to_state(dest_); dst_ = new univ_remover_state(dest); } } @@ -648,18 +647,18 @@ namespace spot virtual bool first() override { one_transition(); - return transition_ != bddfalse; + return dest_ != bddfalse; } virtual bool next() override { one_transition(); - return transition_ != bddfalse; + return dest_ != bddfalse; } virtual bool done() const override { - return transition_ == bddfalse && all_letters_ == bddfalse; + return dest_ == bddfalse && all_letters_ == bddfalse; } virtual const state* dst() const override @@ -669,7 +668,7 @@ namespace spot virtual bdd cond() const override { - return bdd_exist(transition_, all_states_); + return cond_; } virtual acc_cond::mark_t acc() const override diff --git a/spot/twaalgos/dualize.cc b/spot/twaalgos/dualize.cc index e42822740..91498ce8d 100644 --- a/spot/twaalgos/dualize.cc +++ b/spot/twaalgos/dualize.cc @@ -1,6 +1,6 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2017-2019, 2021 Laboratoire de Recherche et Développement -// de l'Epita (LRDE). +// Copyright (C) 2017-2019, 2021-2022 Laboratoire de Recherche et +// Développement de l'Epita (LRDE). // // This file is part of Spot, a model checking library. // @@ -155,14 +155,11 @@ namespace spot for (bdd oneletter: minterms_of(letters, ap)) { - minato_isop isop(delta & oneletter); - bdd cube; + minato_isop isop(bdd_relprod(delta, oneletter, ap)); + bdd dest; - while ((cube = isop.next()) != bddfalse) + while ((dest = isop.next()) != bddfalse) { - bdd cond = bdd_exist(cube, all_vars_); - bdd dest = bdd_existcomp(cube, all_vars_); - st.clear(); acc_cond::mark_t m = bdd_to_state(dest, st); if (st.empty()) @@ -171,7 +168,7 @@ namespace spot if (aut_->prop_state_acc()) m = aut_->state_acc_sets(i); } - res->new_univ_edge(i, st.begin(), st.end(), cond, m); + res->new_univ_edge(i, st.begin(), st.end(), oneletter, m); } } } diff --git a/spot/twaalgos/simulation.cc b/spot/twaalgos/simulation.cc index e62762489..58ebfd79d 100644 --- a/spot/twaalgos/simulation.cc +++ b/spot/twaalgos/simulation.cc @@ -590,7 +590,7 @@ namespace spot // C1 then (!C1)C2, instead of C1 then C2. // With minatop_isop, we ensure that the no negative // class variable will be seen (likewise for promises). - minato_isop isop(sig & one); + minato_isop isop(bdd_relprod(sig, one, sup_all_atomic_prop)); ++nb_minterms; @@ -603,17 +603,12 @@ namespace spot // Take the edge, and keep only the variable which // are used to represent the class. - bdd dst = bdd_existcomp(cond_acc_dest, - all_class_var_); + bdd dst = bdd_existcomp(cond_acc_dest, all_class_var_); // Keep only ones who are acceptance condition. auto acc = bdd_to_mark(bdd_existcomp(cond_acc_dest, all_proms_)); - // Keep the other! - bdd cond = bdd_existcomp(cond_acc_dest, - sup_all_atomic_prop); - // Because we have complemented all the Inf // acceptance conditions on the input automaton, // we must revert them to create a new edge. @@ -630,11 +625,11 @@ namespace spot accst[srcst] = acc; acc = {}; } - gb->new_edge(dst.id(), src.id(), cond, acc); + gb->new_edge(dst.id(), src.id(), one, acc); } else { - gb->new_edge(src.id(), dst.id(), cond, acc); + gb->new_edge(src.id(), dst.id(), one, acc); } } } diff --git a/spot/twaalgos/toweak.cc b/spot/twaalgos/toweak.cc index 8f62477a4..543c7c9a1 100644 --- a/spot/twaalgos/toweak.cc +++ b/spot/twaalgos/toweak.cc @@ -1,6 +1,6 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2017, 2018, 2021 Laboratoire de Recherche et Développement -// de l'Epita (LRDE). +// Copyright (C) 2017, 2018, 2021, 2022 Laboratoire de Recherche et +// Développement de l'Epita (LRDE). // // This file is part of Spot, a model checking library. // @@ -179,14 +179,11 @@ namespace spot for (bdd oneletter: minterms_of(letters, ap)) { - minato_isop isop(delta & oneletter); - bdd cube; + minato_isop isop(bdd_relprod(delta, oneletter, ap)); + bdd dest; - while ((cube = isop.next()) != bddfalse) + while ((dest = isop.next()) != bddfalse) { - bdd cond = bdd_exist(cube, all_states_); - bdd dest = bdd_existcomp(cube, all_states_); - states.clear(); while (dest != bddtrue) { @@ -199,7 +196,7 @@ namespace spot } res_->new_univ_edge(new_state(st.id, st.rank, st.mark), states.begin(), states.end(), - cond, mark); + oneletter, mark); } } todo_.pop(); From 3efab05cf22214b74c8235a1d7dd8c1701c532e5 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Wed, 21 Sep 2022 15:40:12 +0200 Subject: [PATCH 139/337] introduce delay_branching_here This is motivated by an example sent by Edmond Irani Liu, that will be tested in next patch. * spot/twaalgos/dbranch.cc, spot/twaalgos/dbranch.hh: New files. * python/spot/impl.i, spot/twaalgos/Makefile.am: Add them. * spot/twaalgos/translate.cc: Call delay_branching_here unconditionally. * spot/twa/twagraph.cc (defrag_states): Do not assume that games are alternating. * tests/core/genltl.test: Adjust expected numbers. * tests/python/dbranch.py: New file. * tests/Makefile.am: Add it. --- NEWS | 9 ++ python/spot/impl.i | 16 ++-- spot/twa/twagraph.cc | 12 --- spot/twaalgos/Makefile.am | 4 +- spot/twaalgos/dbranch.cc | 163 +++++++++++++++++++++++++++++++++++++ spot/twaalgos/dbranch.hh | 36 ++++++++ spot/twaalgos/translate.cc | 8 +- tests/Makefile.am | 1 + tests/core/genltl.test | 6 +- tests/python/dbranch.py | 147 +++++++++++++++++++++++++++++++++ 10 files changed, 378 insertions(+), 24 deletions(-) create mode 100644 spot/twaalgos/dbranch.cc create mode 100644 spot/twaalgos/dbranch.hh create mode 100644 tests/python/dbranch.py diff --git a/NEWS b/NEWS index 535dee5fa..f6ec9ad15 100644 --- a/NEWS +++ b/NEWS @@ -142,6 +142,15 @@ New in spot 2.10.6.dev (not yet released) succesors, should be called before running simulation-based reductions. + - A new function delay_branching_here(aut) can be used to simplify + some non-deterministic branching. If two transitions (q₁,ℓ,M,q₂) + and (q₁,ℓ,M,q₃) differ only by their destination state, and are + the only incoming transitions of their destination states, then q₂ + and q₃ can be merged (taking the union of their outgoing + transitions). This is cheap function is automatically called by + spot::translate() after translation of a formula to GBA, before + further simplification. + - spot::parallel_policy is an object that can be passed to some algorithm to specify how many threads can be used if Spot has been compiled with --enable-pthread. Currently, only diff --git a/python/spot/impl.i b/python/spot/impl.i index 23c07c4e8..88bdcf5c4 100644 --- a/python/spot/impl.i +++ b/python/spot/impl.i @@ -114,13 +114,14 @@ #include #include #include -#include -#include -#include #include #include #include #include +#include +#include +#include +#include #include #include #include @@ -678,11 +679,14 @@ def state_is_accepting(self, src) -> "bool": %include %include %include -%include -%include %include %include %include +%include +%include +%include +%include +%include %feature("flatnested") spot::twa_run::step; %include %template(list_step) std::list; @@ -694,8 +698,6 @@ def state_is_accepting(self, src) -> "bool": %include %include %include -%include -%include %include %include %include diff --git a/spot/twa/twagraph.cc b/spot/twa/twagraph.cc index 4d0009e93..2a72702f3 100644 --- a/spot/twa/twagraph.cc +++ b/spot/twa/twagraph.cc @@ -1323,18 +1323,6 @@ namespace spot } init_number_ = newst[init_number_]; g_.defrag_states(newst, used_states); - // Make sure we did not mess up the structure - assert([&]() - { - if (auto sp = get_named_prop>("state-player")) - { - for (const auto& e : edges()) - if (sp->at(e.src) == sp->at(e.dst)) - return false; - return true; - } - return true; - }() && "Game not alternating!"); } void twa_graph::remove_unused_ap() diff --git a/spot/twaalgos/Makefile.am b/spot/twaalgos/Makefile.am index ff71982b5..57ae8ce9f 100644 --- a/spot/twaalgos/Makefile.am +++ b/spot/twaalgos/Makefile.am @@ -1,5 +1,5 @@ ## -*- coding: utf-8 -*- -## Copyright (C) 2008-2018, 2020-2021 Laboratoire de Recherche et +## Copyright (C) 2008-2018, 2020-2022 Laboratoire de Recherche et ## Développement de l'Epita (LRDE). ## Copyright (C) 2003-2005 Laboratoire d'Informatique de Paris 6 ## (LIP6), département Systèmes Répartis Coopératifs (SRC), Université @@ -42,6 +42,7 @@ twaalgos_HEADERS = \ contains.hh \ copy.hh \ cycles.hh \ + dbranch.hh \ degen.hh \ determinize.hh \ dot.hh \ @@ -115,6 +116,7 @@ libtwaalgos_la_SOURCES = \ compsusp.cc \ contains.cc \ cycles.cc \ + dbranch.cc \ degen.cc \ determinize.cc \ dot.cc \ diff --git a/spot/twaalgos/dbranch.cc b/spot/twaalgos/dbranch.cc new file mode 100644 index 000000000..465f8326e --- /dev/null +++ b/spot/twaalgos/dbranch.cc @@ -0,0 +1,163 @@ +// -*- coding: utf-8 -*- +// Copyright (C) 2022 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 . + +#include "config.h" + +#include +#include +#include +#include +#include +#include + +namespace spot +{ + namespace + { + typedef std::pair bdd_color; + + struct bdd_color_hash + { + size_t + operator()(const bdd_color& bc) const noexcept + { + return bc.first.id() ^ bc.second.hash(); + } + }; + + template + bool delay_branching_aux(const twa_graph_ptr& aut, std::vector* owner) + { + unsigned ns = aut->num_states(); + // number of predecessors of each state + std::vector pred_count(ns, 0); + unsigned init = aut->get_init_state_number(); + pred_count[init] = 2; // pretend the initial state has too many + // predecessors, so it does not get fused. + // for each state, number of successors that have a single predecessors + std::vector succ_cand(ns, 0); + for (auto& e: aut->edges()) + for (unsigned d: aut->univ_dests(e)) + { + // Note that e.dst might be a destination group in + // alternating automata. + unsigned pc = ++pred_count[d]; + succ_cand[e.src] += (pc == 1) - (pc == 2); + } + bool changed = false; + typedef robin_hood::unordered_map hashmap_t; + hashmap_t first_dest[1 + is_game]; + auto& g = aut->get_graph(); + + // setup a DFS + std::vector seen(ns); + std::stack todo; + auto push_state = [&](unsigned state) + { + todo.push(state); + seen[state] = true; + }; + push_state(init); + + while (!todo.empty()) + { + unsigned src = todo.top(); + todo.pop(); + if (succ_cand[src] < 2) // nothing to merge + { + for (auto& e: aut->out(src)) + for (unsigned d: aut->univ_dests(e)) + if (!seen[d]) + push_state(d); + continue; + } + first_dest[0].clear(); + if constexpr (is_game) + first_dest[1].clear(); + auto it = g.out_iteraser(src); + while (it) + { + unsigned canddst = it->dst; + for (unsigned d: aut->univ_dests(canddst)) + if (!seen[d]) + push_state(d); + if (aut->is_univ_dest(canddst) || pred_count[canddst] != 1) + { + ++it; + continue; + } + if (it->cond == bddfalse) + { + it.erase(); + continue; + } + unsigned mapidx = is_game ? (*owner)[canddst] : 0; + auto [it2, inserted] = + first_dest[mapidx].emplace(bdd_color{it->cond, it->acc}, + canddst); + if (inserted) + { + ++it; + continue; + } + unsigned mergedst = it2->second; + // we have to merge canddst into mergedst. This is as + // simple as: + // 1) connecting their list of transitions + unsigned& mergedfirst = g.state_storage(mergedst).succ; + unsigned& mergedlast = g.state_storage(mergedst).succ_tail; + unsigned& candfirst = g.state_storage(canddst).succ; + unsigned& candlast = g.state_storage(canddst).succ_tail; + if (mergedlast) + aut->edge_storage(mergedlast).next_succ = candfirst; + else // mergedst had now successor + mergedfirst = candfirst; + mergedlast = candlast; + // 2) updating the source of the merged transitions + for (unsigned e2 = candfirst; e2 != 0;) + { + auto& edge = aut->edge_storage(e2); + edge.src = mergedst; + e2 = edge.next_succ; + } + // 3) deleting the edge to canddst. + candfirst = candlast = 0; + it.erase(); + // 4) updating succ_cand + succ_cand[mergedst] += succ_cand[canddst]; + succ_cand[canddst] = 0; + changed = true; + } + } + return changed; + } + } + + bool delay_branching_here(const twa_graph_ptr& aut) + { + if (aut->prop_universal()) + return false; + auto owner = aut->get_named_prop>("state-player"); + if (SPOT_UNLIKELY(owner)) + return delay_branching_aux(aut, owner); + else + return delay_branching_aux(aut, nullptr); + } +} diff --git a/spot/twaalgos/dbranch.hh b/spot/twaalgos/dbranch.hh new file mode 100644 index 000000000..9cd0efa5e --- /dev/null +++ b/spot/twaalgos/dbranch.hh @@ -0,0 +1,36 @@ +// -*- coding: utf-8 -*- +// Copyright (C) 2022 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 . + +#pragma once + +#include + +namespace spot +{ + /// \ingroup twa_algorithms + /// \brief Merge states to delay + /// + /// If a state (x) has two outgoing transitions (x,l,m,y) and + /// (x,l,m,z) going to states (x) and (y) that have no other + /// incoming edges, then (y) and (z) can be merged (keeping the + /// union of their outgoing destinations). + /// + /// \return true iff the automaton was modified. + SPOT_API bool delay_branching_here(const twa_graph_ptr& aut); +} diff --git a/spot/twaalgos/translate.cc b/spot/twaalgos/translate.cc index 4db8643f9..a5a84a10b 100644 --- a/spot/twaalgos/translate.cc +++ b/spot/twaalgos/translate.cc @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2013-2018, 2020-2021 Laboratoire de Recherche et +// Copyright (C) 2013-2018, 2020-2022 Laboratoire de Recherche et // Développement de l'Epita (LRDE). // // This file is part of Spot, a model checking library. @@ -29,6 +29,7 @@ #include #include #include +#include namespace spot { @@ -401,6 +402,11 @@ namespace spot aut = ltl_to_tgba_fm(r, simpl_->get_dict(), exprop, true, false, false, nullptr, nullptr, unambiguous); + if (delay_branching_here(aut)) + { + aut->purge_unreachable_states(); + aut->merge_edges(); + } } aut = this->postprocessor::run(aut, r); diff --git a/tests/Makefile.am b/tests/Makefile.am index 91d3f10ea..9570f7dcd 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -405,6 +405,7 @@ TESTS_python = \ python/bddnqueen.py \ python/bugdet.py \ python/complement_semidet.py \ + python/dbranch.py \ python/declenv.py \ python/decompose_scc.py \ python/det.py \ diff --git a/tests/core/genltl.test b/tests/core/genltl.test index d5efb0236..d943c4cae 100755 --- a/tests/core/genltl.test +++ b/tests/core/genltl.test @@ -1,6 +1,6 @@ #!/bin/sh # -*- coding: utf-8 -*- -# Copyright (C) 2016-2021 Laboratoire de Recherche et Développement +# Copyright (C) 2016-2022 Laboratoire de Recherche et Développement # de l'Epita (LRDE). # # This file is part of Spot, a model checking library. @@ -134,8 +134,8 @@ genltl --kr-n2=1..2 --kr-nlogn=1..2 --kr-n=1..2 --gxf-and=0..3 --fxg-or=0..3 \ --pps-arbiter-standard=2..3 --pps-arbiter-strict=2..3 --format=%F=%L,%f | ltl2tgba --low --det -F-/2 --stats='%<,%s' > out cat >exp<. + +# Test that the spot.gen package works, in particular, we want +# to make sure that the objects created from spot.gen methods +# are usable with methods from the spot package. + + +import spot +from unittest import TestCase +tc = TestCase() + +aut5 = spot.automaton("""HOA: v1 States: 28 Start: 0 AP: 4 "alive" "b" +"a" "c" acc-name: Buchi Acceptance: 1 Inf(0) properties: trans-labels +explicit-labels state-acc very-weak --BODY-- State: 0 [0] 1 [0] 2 [0] +3 [0] 4 [0] 5 [0&!1] 6 [0] 7 State: 1 [0] 8 State: 2 [!0] 9 [0] 10 +State: 3 [!0] 9 [0] 11 State: 4 [!0] 9 [0] 12 State: 5 [!0] 9 [0] 13 +State: 6 [!0] 9 [0&!1] 14 State: 7 [!0] 9 [0&!1&!2] 14 State: 8 [0] 15 +State: 9 {0} [!0] 9 State: 10 [!0] 9 [0] 16 State: 11 [!0] 9 [0] 17 +State: 12 [!0] 9 [0] 18 State: 13 [!0] 9 [0&!1&!2] 19 State: 14 [!0] 9 +[0&!1] 19 State: 15 [0] 20 State: 16 [!0] 9 [0] 21 State: 17 [!0] 9 +[0] 22 State: 18 [!0] 9 [0&!1&!2] 23 State: 19 [!0] 9 [0&!1] 23 State: +20 [0] 24 State: 21 [!0] 9 [0] 25 State: 22 [!0] 9 [0&!1&!2] 26 State: +23 [!0] 9 [0&!1] 26 State: 24 [0&3] 27 State: 25 [!0] 9 [0&!1&!2] 27 +State: 26 [!0] 9 [0&!1] 27 State: 27 [!0] 9 [0] 27 --END--""") + +copy = spot.make_twa_graph(aut5, spot.twa_prop_set.all()) + +tc.assertFalse(spot.is_deterministic(aut5)) +if spot.delay_branching_here(aut5): + aut5.purge_unreachable_states() + aut5.merge_edges() +tc.assertEqual(aut5.num_states(), 13) +tc.assertEqual(aut5.num_edges(), 29) +tc.assertTrue(spot.are_equivalent(copy, aut5)) + +a = spot.automaton("""HOA: v1 States: 8 Start: 0 AP: 3 "a" "b" "c" +Acceptance: 0 t --BODY-- State: 0 [0] 1 [0] 2 [0] 3 State: 1 [!1] 4&5 +[1] 5&6 State: 2 [0] 4&6 State: 3 [0] 3&6 State: 4 [!0] 7 State: 5 +[!0] 7 State: 6 [!0] 6 State: 7 [0] 7 --END--""") + +copy = spot.make_twa_graph(a, spot.twa_prop_set.all()) +if spot.delay_branching_here(a): + a.purge_unreachable_states() + a.merge_edges() +tc.assertEqual(a.to_str(), """HOA: v1 +States: 7 +Start: 0 +AP: 3 "b" "a" "c" +acc-name: all +Acceptance: 0 t +properties: trans-labels explicit-labels state-acc univ-branch +--BODY-- +State: 0 +[1] 1 +[1] 2 +State: 1 +[1] 3&5 +[0] 4&5 +[!0] 3&4 +State: 2 +[1] 2&5 +State: 3 +[!1] 6 +State: 4 +[!1] 6 +State: 5 +[!1] 5 +State: 6 +[1] 6 +--END--""") + +a = spot.automaton("""HOA: v1 +States: 9 +Start: 0 AP: 2 "a" "b" +spot.state-player: 0 1 1 0 0 0 0 1 1 +Acceptance: 0 t +--BODY-- +State: 0 +[0] 1 +[0] 2 +[0] 3 +[0] 4 +State: 1 +[1] 5 +State: 2 +[!1] 6 +State: 3 +[1] 7 +State: 4 +[!1] 8 +State: 5 +[t] 5 +State: 6 +[t] 6 +State: 7 +[t] 7 +State: 8 +[t] 8 +--END--""") +copy = spot.make_twa_graph(a, spot.twa_prop_set.all()) +if spot.delay_branching_here(a): + a.purge_unreachable_states() +tc.assertTrue(spot.are_equivalent(a, copy)) +tc.assertEqual(a.to_str(), """HOA: v1 +States: 7 +Start: 0 +AP: 2 "b" "a" +acc-name: all +Acceptance: 0 t +properties: trans-labels explicit-labels state-acc very-weak +spot-state-player: 0 1 0 0 0 1 1 +--BODY-- +State: 0 +[1] 1 +[1] 2 +State: 1 +[0] 3 +[!0] 4 +State: 2 +[0] 5 +[!0] 6 +State: 3 +[t] 3 +State: 4 +[t] 4 +State: 5 +[t] 5 +State: 6 +[t] 6 +--END--""") From 7ed62f7eedd89133cdd052b0b11c912bdd69b823 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Wed, 21 Sep 2022 16:43:28 +0200 Subject: [PATCH 140/337] genltl: introduce --eil-gsi Based on a mail from Edmond Irani Liu. The test case also serves for the previous patch. * bin/genltl.cc, spot/gen/formulas.cc, spot/gen/formulas.hh: Add it. * NEWS: Mention it. * tests/core/genltl.test: Test it. --- NEWS | 10 +++++++++- bin/genltl.cc | 4 +++- spot/gen/formulas.cc | 20 ++++++++++++++++++++ spot/gen/formulas.hh | 6 ++++-- tests/core/genltl.test | 40 ++++++++++++++++++++++++++++++++++++++++ 5 files changed, 76 insertions(+), 4 deletions(-) diff --git a/NEWS b/NEWS index f6ec9ad15..2fbe8d2bf 100644 --- a/NEWS +++ b/NEWS @@ -51,6 +51,13 @@ New in spot 2.10.6.dev (not yet released) include each other, and are used to show a regression (in speed) present in Spot 2.10.x and fixed in 2.11. + - genltl learned --eil-gsi to generate a familly a function whose + translation and simplification used to be very slow. In particular + + genltl --eil-gsi=23 | ltlfilt --from-ltlf | ltl2tgba + + was reported as taking 9 days. This is now instantaneous. + Library: - The new function suffix_operator_normal_form() implements @@ -149,7 +156,8 @@ New in spot 2.10.6.dev (not yet released) and q₃ can be merged (taking the union of their outgoing transitions). This is cheap function is automatically called by spot::translate() after translation of a formula to GBA, before - further simplification. + further simplification. This was introduced to help with automata + produced from formulas output by "genltl --eil-gsi" (see above). - spot::parallel_policy is an object that can be passed to some algorithm to specify how many threads can be used if Spot has been diff --git a/bin/genltl.cc b/bin/genltl.cc index 6c632de7a..6393024c2 100644 --- a/bin/genltl.cc +++ b/bin/genltl.cc @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2012, 2013, 2015-2019 Laboratoire de Recherche et +// Copyright (C) 2012, 2013, 2015-2019, 2022 Laboratoire de Recherche et // Développement de l'Epita (LRDE). // // This file is part of Spot, a model checking library. @@ -84,6 +84,8 @@ static const argp_option options[] = { "eh-patterns", gen::LTL_EH_PATTERNS, "RANGE", OPTION_ARG_OPTIONAL, "Etessami and Holzmann [Concur'00] patterns " "(range should be included in 1..12)", 0 }, + { "eil-gsi", gen::LTL_EIL_GSI, "RANGE", 0, + "G[0..n]((a S b) -> c) rewritten using future operators", 0 }, { "fxg-or", gen::LTL_FXG_OR, "RANGE", 0, "F(p0 | XG(p1 | XG(p2 | ... XG(pn))))", 0}, { "gf-equiv", gen::LTL_GF_EQUIV, "RANGE", 0, diff --git a/spot/gen/formulas.cc b/spot/gen/formulas.cc index 3f63b07e7..10841e820 100644 --- a/spot/gen/formulas.cc +++ b/spot/gen/formulas.cc @@ -1281,6 +1281,21 @@ namespace spot } } + // G[0..n]((a S b) -> c) rewritten using future operators, + // from Edmond Irani Liu (EIL). GSI stands for "Globally Since Implies." + static formula eil_gsi(int n, std::string a, std::string b, std::string c) + { + formula fa = formula::ap(a); + formula fb = formula::ap(b); + formula res = fb; + for (int i = 1; i <= n; ++i) + { + formula tmp = formula::And({formula::strong_X(i, fa), res}); + res = formula::Or({formula::strong_X(i, fb), tmp}); + } + return formula::Implies(res, formula::strong_X(n, formula::ap(c))); + } + formula ltl_pattern(ltl_pattern_id pattern, int n, int m) { if (n < 0) @@ -1317,6 +1332,8 @@ namespace spot return dac_pattern(n); case LTL_EH_PATTERNS: return eh_pattern(n); + case LTL_EIL_GSI: + return eil_gsi(n, "a", "b", "c"); case LTL_FXG_OR: return FXG_or_n("p", n); case LTL_GF_EQUIV: @@ -1418,6 +1435,7 @@ namespace spot "ccj-beta-prime", "dac-patterns", "eh-patterns", + "eil-gsi", "fxg-or", "gf-equiv", "gf-equiv-xn", @@ -1485,6 +1503,7 @@ namespace spot return 55; case LTL_EH_PATTERNS: return 12; + case LTL_EIL_GSI: case LTL_FXG_OR: case LTL_GF_EQUIV: case LTL_GF_EQUIV_XN: @@ -1554,6 +1573,7 @@ namespace spot case LTL_CCJ_BETA_PRIME: case LTL_DAC_PATTERNS: case LTL_EH_PATTERNS: + case LTL_EIL_GSI: case LTL_FXG_OR: case LTL_GF_EQUIV: case LTL_GF_EQUIV_XN: diff --git a/spot/gen/formulas.hh b/spot/gen/formulas.hh index ac5974e48..ef5a0d850 100644 --- a/spot/gen/formulas.hh +++ b/spot/gen/formulas.hh @@ -1,6 +1,6 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2017, 2018, 2019 Laboratoire de Recherche et Developpement de -// l'EPITA (LRDE). +// Copyright (C) 2017-2019, 2022 Laboratoire de Recherche et +// Developpement de l'EPITA (LRDE). // // This file is part of Spot, a model checking library. // @@ -56,6 +56,8 @@ namespace spot /// 12 formulas from Etessami and Holzmann. /// \cite etessami.00.concur LTL_EH_PATTERNS, + /// Familly sent by Edmond Irani Liu + LTL_EIL_GSI, /// `F(p0 | XG(p1 | XG(p2 | ... XG(pn))))` LTL_FXG_OR, /// `(GFa1 & GFa2 & ... & GFan) <-> GFz` diff --git a/tests/core/genltl.test b/tests/core/genltl.test index d943c4cae..622950f65 100755 --- a/tests/core/genltl.test +++ b/tests/core/genltl.test @@ -269,3 +269,43 @@ diff range1.ltl range2.ltl genltl --sb-patterns=1..20 | ltlfilt -v --nth 10..20 > range1.ltl genltl --sb-patterns=1..9 > range2.ltl diff range1.ltl range2.ltl + + +# Edmond Irani Liu sent a bug report where formula 23 in this series +# took 9 days to produce, despite the small size of the resulting +# automaton. I (ADL) later found this to be caused by simulation +# applied on a non-deterministic automaton with many non-deterministic +# choices going to state that simulate one another, which in turn lead +# to massive slowdown of the minato_isop algorithm. As a workaround, +# I introduced delay_branching_here(), a cheap function that is called +# before simplification. In this case, this is enough to determinize +# the automaton, simplifying simulation-based reduction greatly. +genltl --eil-gsi=1..25 | ltlfilt --from-ltlf | ltl2tgba --stats=%s,%e >output +cat >expected < Date: Thu, 22 Sep 2022 15:44:18 +0200 Subject: [PATCH 141/337] translate: add a branch-post option * spot/twaalgos/translate.cc, spot/twaalgos/translate.hh: Here. * NEWS, bin/spot-x.cc: Mention it. * tests/core/genltl.test: Test it. --- NEWS | 5 +++++ bin/spot-x.cc | 5 +++++ spot/twaalgos/translate.cc | 13 +++++++++++-- spot/twaalgos/translate.hh | 3 ++- tests/core/genltl.test | 7 ++++++- 5 files changed, 29 insertions(+), 4 deletions(-) diff --git a/NEWS b/NEWS index 2fbe8d2bf..4268ab81a 100644 --- a/NEWS +++ b/NEWS @@ -159,6 +159,11 @@ New in spot 2.10.6.dev (not yet released) further simplification. This was introduced to help with automata produced from formulas output by "genltl --eil-gsi" (see above). + - spot::postproc has new configuration variable branch-post that + can be used to control the use of branching-postponement (diabled + by default) or delayed-branching (see above, enabled by default). + See the spot-x(7) man page for details. + - spot::parallel_policy is an object that can be passed to some algorithm to specify how many threads can be used if Spot has been compiled with --enable-pthread. Currently, only diff --git a/bin/spot-x.cc b/bin/spot-x.cc index a653fc926..908cbb98a 100644 --- a/bin/spot-x.cc +++ b/bin/spot-x.cc @@ -50,6 +50,11 @@ implication checks for formula simplifications. Defaults to 64.") }, { nullptr, 0, nullptr, 0, "Translation options:", 0 }, { DOC("ltl-split", "Set to 0 to disable the translation of automata \ as product or sum of subformulas.") }, + { DOC("branch-prop", "Set to 0 to disable branching-postponement \ +(done during translation, may create more states) and delayed-branching \ +(almost similar, but done after translation to only remove states). \ +Set to 1 to force branching-postponement, and to 2 \ +to force delayed-branching. By default delayed-branching is used.") }, { DOC("comp-susp", "Set to 1 to enable compositional suspension, \ as described in our SPIN'13 paper (see Bibliography below). Set to 2, \ to build only the skeleton TGBA without composing it. Set to 0 (the \ diff --git a/spot/twaalgos/translate.cc b/spot/twaalgos/translate.cc index a5a84a10b..cd1e2aa63 100644 --- a/spot/twaalgos/translate.cc +++ b/spot/twaalgos/translate.cc @@ -65,6 +65,7 @@ namespace spot int tls_max_states = opt->get("tls-max-states", 64); tls_max_states_ = std::max(0, tls_max_states); exprop_ = opt->get("exprop", -1); + branchpost_ = opt->get("branch-post", -1); } void translator::build_simplifier(const bdd_dict_ptr& dict) @@ -399,10 +400,18 @@ namespace spot bool exprop = unambiguous || (level_ == postprocessor::High && exprop_ != 0) || exprop_ > 0; + // branch-post: 1 == force branching postponement + // 0 == disable branching post. and delay_branching + // 2 == force delay_branching + // -1 == auto (delay_branching) + // Some quick experiments suggests that branching postponement + // can produce larger automata on non-obligations formulas, and + // that even on obligation formulas, delay_branching is faster. + bool bpost = branchpost_ == 1; aut = ltl_to_tgba_fm(r, simpl_->get_dict(), exprop, - true, false, false, nullptr, nullptr, + true, bpost, false, nullptr, nullptr, unambiguous); - if (delay_branching_here(aut)) + if (!bpost && branchpost_ != 0 && delay_branching_here(aut)) { aut->purge_unreachable_states(); aut->merge_edges(); diff --git a/spot/twaalgos/translate.hh b/spot/twaalgos/translate.hh index 9dc6b12d2..d17c917b2 100644 --- a/spot/twaalgos/translate.hh +++ b/spot/twaalgos/translate.hh @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2013-2018, 2020 Laboratoire de Recherche et Développement +// Copyright (C) 2013-2018, 2020, 2022 Laboratoire de Recherche et Développement // de l'Epita (LRDE). // // This file is part of Spot, a model checking library. @@ -154,6 +154,7 @@ namespace spot bool gf_guarantee_ = true; bool gf_guarantee_set_ = false; bool ltl_split_; + int branchpost_ = -1; unsigned tls_max_states_ = 0; int exprop_; const option_map* opt_; diff --git a/tests/core/genltl.test b/tests/core/genltl.test index 622950f65..71b1ddf77 100755 --- a/tests/core/genltl.test +++ b/tests/core/genltl.test @@ -280,7 +280,8 @@ diff range1.ltl range2.ltl # I introduced delay_branching_here(), a cheap function that is called # before simplification. In this case, this is enough to determinize # the automaton, simplifying simulation-based reduction greatly. -genltl --eil-gsi=1..25 | ltlfilt --from-ltlf | ltl2tgba --stats=%s,%e >output +genltl --eil-gsi=1..25 | ltlfilt --from-ltlf > formulas.ltl +ltl2tgba -F formulas.ltl --stats=%s,%e >output cat >expected <expected <output +diff expected output From 383128d9835cc672b2638ceec9dcd6e65fbd2a7d Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Thu, 22 Sep 2022 16:19:07 +0200 Subject: [PATCH 142/337] * doc/tl/tl.tex: Fix a couple of typos detected by ispell. --- doc/tl/tl.tex | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/doc/tl/tl.tex b/doc/tl/tl.tex index 2c0599f82..b6268d9cd 100644 --- a/doc/tl/tl.tex +++ b/doc/tl/tl.tex @@ -395,7 +395,7 @@ following Boolean operators: (allowing better compatibility with Wring and VIS) may only used in temporal formulas. Boolean expressions that occur inside SERE (see Section~\ref{sec:sere}) may not use this form because the $\STARALT$ - symbol is used as the Kleen star.} + symbol is used as the Kleene star.} Additionally, an atomic proposition $a$ can be negated using the syntax \samp{$a$=0}, which is equivalent to \samp{$\NOT a$}. Also @@ -600,7 +600,7 @@ the source. It can mean either ``\textit{Sequential Extended Regular ``\textit{Semi-Extended Regular Expression}''~\citep{eisner.08.hvc}. In any case, the intent is the same: regular expressions with traditional operations (union `$\OR$', concatenation `$\CONCAT$', -Kleen star `$\STAR{}$') are extended with operators such as +Kleene star `$\STAR{}$') are extended with operators such as intersection `$\ANDALT$', and fusion `$\FUSION$'. Any Boolean formula (section~\ref{def:boolform}) is a SERE. SERE can @@ -638,7 +638,7 @@ denote arbitrary SERE. \end{tabular} \end{center} -\footnotetext{\emph{Non-Length-Matching} interesction.} +\footnotetext{\emph{Non-Length-Matching} intersection.} The character \samp{\$} or the string \samp{inf} can also be used as value for $\mvar{j}$ in the above operators to denote an unbounded @@ -1069,7 +1069,7 @@ psl2ba, Modella, and NuSMV all have $\U$ and $\R$ as left-associative, while Goal (hence Büchi store), LTL2AUT, and LTL2Büchi (from JavaPathFinder) have $\U$ and $\R$ as right-associative. Vis and LBTT have these two operators as non-associative (parentheses required). -Similarly the tools do not aggree on the associativity of $\IMPLIES$ +Similarly the tools do not agree on the associativity of $\IMPLIES$ and $\EQUIV$: some tools handle both operators as left-associative, or both right-associative, other have only $\IMPLIES$ as right-associative. @@ -1429,7 +1429,7 @@ $\NOT$ operator. \end{align*} Note that the above rules include the ``unabbreviation'' of operators -``$\EQUIV$'', ``$\IMPLIES$'', and ``$\XOR$'', correspondings to the +``$\EQUIV$'', ``$\IMPLIES$'', and ``$\XOR$'', corresponding to the rules \texttt{"ei\^"} of function `\verb=unabbreviate()= as described in Section~\ref{sec:unabbrev}. Therefore it is never necessary to apply these abbreviations before or after @@ -2097,3 +2097,14 @@ $f_1\AND f_2$ & \bor{f_1}{g}{f_2}{g} & & & %%% TeX-master: t %%% coding: utf-8 %%% End: + +% LocalWords: tabu Alexandre Duret Lutz toc subsequence Kripke unary +% LocalWords: LTL GFa INISHED ZX FX cccccrl UTF syntaxes disjunction +% LocalWords: VIS Kleene overline overbar ary cccrl EF sep FB LTLf +% LocalWords: rewritings TSLF NLM iter un SVA PSL SEREs DFA ccccc ba +% LocalWords: SystemVerilog clc ltl psl Modella NuSMV Büchi AUT Vis +% LocalWords: JavaPathFinder LBTT AST subtrees boolean nenoform lbt +% LocalWords: eword nn LBT's automata subformulas ottom unabbreviate +% LocalWords: Unabbreviations ei GRW RW WR unabbreviator simplifier +% LocalWords: tl unabbreviation indeterminism dnf cnf SNF rl iff BDD +% LocalWords: subformula From 51caa5588e986ed860df1527be1a8a2c8b6eec8a Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Thu, 22 Sep 2022 17:48:56 +0200 Subject: [PATCH 143/337] update gitlab references As LRDE is being renamed LRE, gitlab is one of the first URL to migrate. The old URL is still supported, but we want to only use the new one eventually. * .dir-locals.el, .gitlab-ci.yml, HACKING, NEWS, doc/org/concepts.org, doc/org/install.org, doc/org/setup.org, elisp/Makefile.am, elisp/hoa-mode.el, tests/ltsmin/README: Update to the new gitlab URL. --- .dir-locals.el | 2 +- .gitlab-ci.yml | 46 ++++++++++++++++++++++---------------------- HACKING | 4 ++-- NEWS | 2 +- doc/org/concepts.org | 6 +++--- doc/org/install.org | 6 +++--- doc/org/setup.org | 2 +- elisp/Makefile.am | 6 +++--- elisp/hoa-mode.el | 4 ++-- tests/ltsmin/README | 2 +- 10 files changed, 40 insertions(+), 40 deletions(-) diff --git a/.dir-locals.el b/.dir-locals.el index 7bc423371..91c287367 100644 --- a/.dir-locals.el +++ b/.dir-locals.el @@ -5,7 +5,7 @@ (bug-reference-bug-regexp . "\\(?:[Ff]ix\\(es\\)? \\|[Ii]ssue \\)#\\(?2:[0-9]+\\)") (bug-reference-url-format - . "https://gitlab.lrde.epita.fr/spot/spot/issues/%s") + . "https://gitlab.lre.epita.fr/spot/spot/issues/%s") (mode . bug-reference) (magit-branch-adjust-remote-upstream-alist ("origin/next" . "/")))) (c++-mode . ((c-default-style . "gnu") diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index c381793b0..4a94ebfce 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -19,7 +19,7 @@ debian-stable-gcc: - branches except: - /wip/ - image: gitlab-registry.lrde.epita.fr/spot/buildenv/debian:stable + image: gitlab-registry.lre.epita.fr/spot/buildenv/debian:stable script: - autoreconf -vfi - ./configure --enable-max-accsets=256 --enable-pthread @@ -41,7 +41,7 @@ make-dist: - branches except: - /wip/ - image: gitlab-registry.lrde.epita.fr/spot/buildenv/debian + image: gitlab-registry.lre.epita.fr/spot/buildenv/debian script: - autoreconf -vfi - ./configure --disable-static --enable-doxygen @@ -62,7 +62,7 @@ debian-unstable-gcc-coverage: - branches except: - /wip/ - image: gitlab-registry.lrde.epita.fr/spot/buildenv/debian + image: gitlab-registry.lre.epita.fr/spot/buildenv/debian script: - autoreconf -vfi - ./configure CXX='g++ --coverage' --enable-devel --disable-static --enable-doxygen @@ -96,7 +96,7 @@ debian-unstable-gcc-pypy: - branches except: - /wip/ - image: gitlab-registry.lrde.epita.fr/spot/buildenv/debian + image: gitlab-registry.lre.epita.fr/spot/buildenv/debian script: - VERSION=`cat VERSION` - tar xvf spot-$VERSION.tar.gz @@ -116,7 +116,7 @@ debian-gcc-snapshot: - branches except: - /wip/ - image: gitlab-registry.lrde.epita.fr/spot/buildenv/debian + image: gitlab-registry.lre.epita.fr/spot/buildenv/debian script: - export PATH="/usr/lib/gcc-snapshot/bin:$PATH" LD_LIBRARY_PATH="/usr/lib/gcc-snapshot/lib:$LD_LIBRARY_PATH" - autoreconf -vfi @@ -139,7 +139,7 @@ alpine-gcc: - branches except: - /wip/ - image: gitlab-registry.lrde.epita.fr/spot/buildenv/alpine + image: gitlab-registry.lre.epita.fr/spot/buildenv/alpine script: - autoreconf -vfi - ./configure @@ -158,7 +158,7 @@ arch-clang: - branches except: - /wip/ - image: gitlab-registry.lrde.epita.fr/spot/buildenv/arch + image: gitlab-registry.lre.epita.fr/spot/buildenv/arch script: - autoreconf -vfi - ./configure --prefix ~/install_dir CC='clang -Qunused-arguments' CXX='clang++ -Qunused-arguments' --enable-devel --enable-c++20 --enable-doxygen @@ -181,7 +181,7 @@ arch-gcc-glibcxxdebug: - branches except: - /wip/ - image: gitlab-registry.lrde.epita.fr/spot/buildenv/arch + image: gitlab-registry.lre.epita.fr/spot/buildenv/arch script: - VERSION=`cat VERSION` - tar xvf spot-$VERSION.tar.gz @@ -210,7 +210,7 @@ mingw-shared: - branches except: - /wip/ - image: gitlab-registry.lrde.epita.fr/spot/buildenv/debian + image: gitlab-registry.lre.epita.fr/spot/buildenv/debian script: - VERSION=`cat VERSION` - tar xvf spot-$VERSION.tar.gz @@ -237,7 +237,7 @@ mingw-static: - branches except: - /wip/ - image: gitlab-registry.lrde.epita.fr/spot/buildenv/debian + image: gitlab-registry.lre.epita.fr/spot/buildenv/debian script: - VERSION=`cat VERSION` - tar xvf spot-$VERSION.tar.gz @@ -267,11 +267,11 @@ debpkg-stable: - next - stable script: - - docker pull gitlab-registry.lrde.epita.fr/spot/buildenv/debian:stable + - docker pull gitlab-registry.lre.epita.fr/spot/buildenv/debian:stable - vol=spot-stable-$CI_COMMIT_SHA - docker volume create $vol - exitcode=0 - - docker run -v $vol:/build/result --name helper-$vol gitlab-registry.lrde.epita.fr/spot/buildenv/debian:stable ./build-spot.sh $CI_COMMIT_REF_NAME -j${NBPROC-1} || exitcode=$? + - docker run -v $vol:/build/result --name helper-$vol gitlab-registry.lre.epita.fr/spot/buildenv/debian:stable ./build-spot.sh $CI_COMMIT_REF_NAME -j${NBPROC-1} || exitcode=$? - docker cp helper-$vol:/build/result _build_stable || exitcode=$? - docker rm helper-$vol || exitcode=$? - docker volume rm $vol || exitcode=$? @@ -295,11 +295,11 @@ debpkg-stable-i386: tags: ["x86"] needs: ["debpkg-stable"] script: - - docker pull gitlab-registry.lrde.epita.fr/spot/buildenv/debian-i386:stable + - docker pull gitlab-registry.lre.epita.fr/spot/buildenv/debian-i386:stable - vol=spot-stable-$CI_COMMIT_SHA - docker volume create $vol - exitcode=0 - - docker create -v $vol:/build/result --name helper-$vol gitlab-registry.lrde.epita.fr/spot/buildenv/debian-i386:stable ./bin-spot.sh -j${NBPROC-1} || exitcode=$? + - docker create -v $vol:/build/result --name helper-$vol gitlab-registry.lre.epita.fr/spot/buildenv/debian-i386:stable ./bin-spot.sh -j${NBPROC-1} || exitcode=$? - docker cp _build_stable/. helper-$vol:/build/result || exitcode=$? - rm -rf _build_stable - docker start -a helper-$vol || exitcode=$? @@ -322,11 +322,11 @@ debpkg-unstable: - /-deb$/ - next script: - - docker pull gitlab-registry.lrde.epita.fr/spot/buildenv/debian + - docker pull gitlab-registry.lre.epita.fr/spot/buildenv/debian - vol=spot-unstable-$CI_COMMIT_SHA - docker volume create $vol - exitcode=0 - - docker run -v $vol:/build/result --name helper-$vol gitlab-registry.lrde.epita.fr/spot/buildenv/debian ./build-spot.sh $CI_COMMIT_REF_NAME -j${NBPROC-1} || exitcode=$? + - docker run -v $vol:/build/result --name helper-$vol gitlab-registry.lre.epita.fr/spot/buildenv/debian ./build-spot.sh $CI_COMMIT_REF_NAME -j${NBPROC-1} || exitcode=$? - docker cp helper-$vol:/build/result _build_unstable || exitcode=$? - docker rm helper-$vol || exitcode=$? - docker volume rm $vol || exitcode=$? @@ -348,11 +348,11 @@ debpkg-unstable-i386: tags: ["x86"] needs: ["debpkg-unstable"] script: - - docker pull gitlab-registry.lrde.epita.fr/spot/buildenv/debian-i386 + - docker pull gitlab-registry.lre.epita.fr/spot/buildenv/debian-i386 - vol=spot-unstable-$CI_COMMIT_SHA - docker volume create $vol - exitcode=0 - - docker create -v $vol:/build/result --name helper-$vol gitlab-registry.lrde.epita.fr/spot/buildenv/debian-i386 ./bin-spot.sh -j${NBPROC-1} || exitcode=$? + - docker create -v $vol:/build/result --name helper-$vol gitlab-registry.lre.epita.fr/spot/buildenv/debian-i386 ./bin-spot.sh -j${NBPROC-1} || exitcode=$? - docker cp _build_unstable/. helper-$vol:/build/result || exitcode=$? - rm -rf _build_unstable - docker start -a helper-$vol || exitcode=$? @@ -374,7 +374,7 @@ rpm-pkg: - master - next - stable - image: gitlab-registry.lrde.epita.fr/spot/buildenv/fedora + image: gitlab-registry.lre.epita.fr/spot/buildenv/fedora script: - autoreconf -vfi - ./configure @@ -425,8 +425,8 @@ publish-stable: - ls -l - tgz=`ls spot-*.tar.* | head -n 1` - case $tgz in *[0-9].tar.*) scp $tgz doc@perso:/var/www/dload/spot/;; esac - - curl -X POST -F ref=master -F token=$TRIGGER_SPOT_WEB -F "variables[spot_branch]=stable" https://gitlab.lrde.epita.fr/api/v4/projects/131/trigger/pipeline - - curl -X POST "https://archive.softwareheritage.org/api/1/origin/save/git/url/https://gitlab.lrde.epita.fr/spot/spot/" + - curl -X POST -F ref=master -F token=$TRIGGER_SPOT_WEB -F "variables[spot_branch]=stable" https://gitlab.lre.epita.fr/api/v4/projects/131/trigger/pipeline + - curl -X POST "https://archive.softwareheritage.org/api/1/origin/save/git/url/https://gitlab.lre.epita.fr/spot/spot/" - curl "https://web.archive.org/save/https://www.lrde.epita.fr/dload/spot/$tgz" publish-unstable: @@ -443,8 +443,8 @@ publish-unstable: - cd _build_unstable - ls -l - dput lrde *.changes - - curl -X POST -F ref=master -F token=$TRIGGER_SPOT_WEB -F "variables[spot_branch]=next" https://gitlab.lrde.epita.fr/api/v4/projects/131/trigger/pipeline - - curl -X POST -F ref=master -F token=$TRIGGER_SANDBOX https://gitlab.lrde.epita.fr/api/v4/projects/181/trigger/pipeline + - curl -X POST -F ref=master -F token=$TRIGGER_SPOT_WEB -F "variables[spot_branch]=next" https://gitlab.lre.epita.fr/api/v4/projects/131/trigger/pipeline + - curl -X POST -F ref=master -F token=$TRIGGER_SANDBOX https://gitlab.lre.epita.fr/api/v4/projects/181/trigger/pipeline raspbian: stage: build diff --git a/HACKING b/HACKING index de461376b..f2cf27e8c 100644 --- a/HACKING +++ b/HACKING @@ -5,11 +5,11 @@ Bootstraping from the GIT repository Spot's gitlab page is at - https://gitlab.lrde.epita.fr/spot/spot + https://gitlab.lre.epita.fr/spot/spot The GIT repository can be cloned with - git clone https://gitlab.lrde.epita.fr/spot/spot.git + git clone https://gitlab.lre.epita.fr/spot/spot.git Some files in SPOT's source tree are generated. They are distributed so that users do not need to install tools to rebuild them, but we diff --git a/NEWS b/NEWS index 4268ab81a..262414d67 100644 --- a/NEWS +++ b/NEWS @@ -1602,7 +1602,7 @@ New in spot 2.6.2 (2018-09-28) - We no longer distribute the Python-based CGI script + javascript code for the online translator. Its replacement has its own - repository: https://gitlab.lrde.epita.fr/spot/spot-web-app/ + repository: https://gitlab.lre.epita.fr/spot/spot-web-app/ Library: diff --git a/doc/org/concepts.org b/doc/org/concepts.org index a8fab8b65..64f982eb8 100644 --- a/doc/org/concepts.org +++ b/doc/org/concepts.org @@ -1022,7 +1022,7 @@ layers. dynamic libraries that [[http://fmt.cs.utwente.nl/tools/ltsmin/][LTSmin]] uses to represent state-spaces. It currently supports libraries generated from Promela models using SpinS or a patched version of DiVinE, but you have to install - those third-party tools first. See [[https://gitlab.lrde.epita.fr/spot/spot/blob/next/tests/ltsmin/README][=tests/ltsmin/README=]] + those third-party tools first. See [[https://gitlab.lre.epita.fr/spot/spot/blob/next/tests/ltsmin/README][=tests/ltsmin/README=]] for details. - In addition to the C++17 API, we also provide Python bindings for =libspotgen=, =libspotltsmin=, =libbddx=, and most of =libspot=. @@ -1034,8 +1034,8 @@ layers. distributed with the rest of Spot, their source-code is publicly available (in case you want to contribute or run a local version). The [[https://spot-sandbox.lrde.epita.fr/][=spot-sandbox=]] website runs from a Docker container whose - configuration can be found in [[https://gitlab.lrde.epita.fr/spot/sandbox/tree/master=][this repository]]. The client and - server parts of the [[https://spot.lrde.epita.fr/app/][online LTL translator]] can be found in [[https://gitlab.lrde.epita.fr/spot/spot-web-app/][this + configuration can be found in [[https://gitlab.lre.epita.fr/spot/sandbox/tree/master=][this repository]]. The client and + server parts of the [[https://spot.lrde.epita.fr/app/][online LTL translator]] can be found in [[https://gitlab.lre.epita.fr/spot/spot-web-app/][this repository]]. * Automaton property flags diff --git a/doc/org/install.org b/doc/org/install.org index a5759da17..a24134e42 100644 --- a/doc/org/install.org +++ b/doc/org/install.org @@ -14,7 +14,7 @@ The latest release of Spot is version {{{LASTRELEASE}}}: - {{{LASTTARBALL}}} (see also the {{{LASTNEWS}}}) Past releases can be found [[https://www.lrde.epita.fr/dload/spot/][in the same directory]]. If you are -interested in /future/ releases, you can always peek at the [[https://gitlab.lrde.epita.fr/spot/spot/-/jobs/artifacts/next/browse?job=debian-stable-gcc][last +interested in /future/ releases, you can always peek at the [[https://gitlab.lre.epita.fr/spot/spot/-/jobs/artifacts/next/browse?job=make-dist][last successful development build]]. ** Requirements @@ -162,11 +162,11 @@ the (working) code that should be part of the next major release. To clone the git repository, use #+BEGIN_SRC sh -git clone https://gitlab.lrde.epita.fr/spot/spot.git +git clone https://gitlab.lre.epita.fr/spot/spot.git #+END_SRC This should put you on the =next= branch by default. From there, read -the [[https://gitlab.lrde.epita.fr/spot/spot/blob/next/HACKING][HACKING]] file that should be at the top of your cloned repository: +the [[https://gitlab.lre.epita.fr/spot/spot/blob/next/HACKING][HACKING]] file that should be at the top of your cloned repository: it lists all the tools you should install before attempting to compile the source tree. diff --git a/doc/org/setup.org b/doc/org/setup.org index 52aa02639..d68521b7b 100644 --- a/doc/org/setup.org +++ b/doc/org/setup.org @@ -4,7 +4,7 @@ #+MACRO: SPOTVERSION 2.10.6 #+MACRO: LASTRELEASE 2.10.6 #+MACRO: LASTTARBALL [[http://www.lrde.epita.fr/dload/spot/spot-2.10.6.tar.gz][=spot-2.10.6.tar.gz=]] -#+MACRO: LASTNEWS [[https://gitlab.lrde.epita.fr/spot/spot/blob/spot-2-10-6/NEWS][summary of the changes]] +#+MACRO: LASTNEWS [[https://gitlab.lre.epita.fr/spot/spot/blob/spot-2-10-6/NEWS][summary of the changes]] #+MACRO: LASTDATE 2022-05-18 #+ATTR_HTML: :id spotlogo diff --git a/elisp/Makefile.am b/elisp/Makefile.am index efdc604f5..c67a969e8 100644 --- a/elisp/Makefile.am +++ b/elisp/Makefile.am @@ -1,6 +1,6 @@ ## -*- coding: utf-8 -*- -## Copyright (C) 2015, 2016, 2017, 2018 Laboratoire de Recherche et Développement -## de l'Epita (LRDE). +## Copyright (C) 2015-2018, 2022 Laboratoire de Recherche et +## Développement de l'Epita (LRDE). ## ## This file is part of Spot, a model checking library. ## @@ -19,7 +19,7 @@ EXTRA_DIST = hoa-mode.el -GIT = https://gitlab.lrde.epita.fr/spot/emacs-modes/raw/master/ +GIT = https://gitlab.lre.epita.fr/spot/emacs-modes/raw/master/ .PHONY: update-el update-el: diff --git a/elisp/hoa-mode.el b/elisp/hoa-mode.el index 9083b529d..58730b971 100644 --- a/elisp/hoa-mode.el +++ b/elisp/hoa-mode.el @@ -1,10 +1,10 @@ ;;; hoa-mode.el --- Major mode for the HOA format -*- lexical-binding: t -*- -;; Copyright (C) 2015, 2017, 2019 Alexandre Duret-Lutz +;; Copyright (C) 2015, 2017, 2019, 2022 Alexandre Duret-Lutz ;; Author: Alexandre Duret-Lutz ;; Maintainer: Alexandre Duret-Lutz -;; URL: https://gitlab.lrde.epita.fr/spot/emacs-modes +;; URL: https://gitlab.lre.epita.fr/spot/emacs-modes ;; Keywords: major-mode, automata, convenience ;; Created: 2015-11-13 diff --git a/tests/ltsmin/README b/tests/ltsmin/README index 6aaf5bba4..c3f2696d6 100644 --- a/tests/ltsmin/README +++ b/tests/ltsmin/README @@ -31,7 +31,7 @@ Installation of DiVinE Use the following commands to compile and install the patched version of DiVinE. - git clone https://gitlab.lrde.epita.fr/spot/divine-ltsmin-deb + git clone https://gitlab.lre.epita.fr/spot/divine-ltsmin-deb cd divine-ltsmin-deb mkdir _build && cd _build cmake .. -DMURPHI=OFF -DHOARD=OFF -DGUI=OFF -DRX_PATH= -DCMAKE_INSTALL_PREFIX=$HOME/usr From 0521901e9db006ceef1c896cb7abe2506a6a288c Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Fri, 23 Sep 2022 09:42:15 +0200 Subject: [PATCH 144/337] revert c45ff0c94 and add test case showing why * bin/ltlsynt.cc: Revert c45ff0c94. Also fix documentation of exit status. * tests/core/ltlsynt2.test: New file. * tests/Makefile.am: Add it. --- bin/ltlsynt.cc | 10 ++++-- tests/Makefile.am | 1 + tests/core/ltlsynt2.test | 77 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 85 insertions(+), 3 deletions(-) create mode 100755 tests/core/ltlsynt2.test diff --git a/bin/ltlsynt.cc b/bin/ltlsynt.cc index 630ccd629..44c55ef54 100644 --- a/bin/ltlsynt.cc +++ b/bin/ltlsynt.cc @@ -160,8 +160,8 @@ static const struct argp_child children[] = const char argp_program_doc[] = "\ Synthesize a controller from its LTL specification.\v\ Exit status:\n\ - 0 if the input problem is realizable\n\ - 1 if the input problem is not realizable\n\ + 0 if all input problems were realizable\n\ + 1 if at least one input problem was not realizable\n\ 2 if any error has been reported"; static std::optional> all_output_aps; @@ -279,6 +279,10 @@ namespace spot::print_hoa(std::cout, game, opt_print_hoa_args) << '\n'; } + // If filename is passed, it is printed instead of the formula. We + // use that when processing games since we have no formula to print. + // It would be cleaner to have two columns: one for location (that's + // filename + line number if known), and one for formula (if known). static void print_csv(const spot::formula& f, const char* filename = nullptr) { @@ -705,7 +709,7 @@ namespace } if (opt_csv) - print_csv(f, filename); + print_csv(f); return res; } diff --git a/tests/Makefile.am b/tests/Makefile.am index 9570f7dcd..2384f115e 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -343,6 +343,7 @@ TESTS_twa = \ core/parity2.test \ core/pgsolver.test \ core/ltlsynt.test \ + core/ltlsynt2.test \ core/ltlsynt-pgame.test \ core/syfco.test \ core/rabin2parity.test \ diff --git a/tests/core/ltlsynt2.test b/tests/core/ltlsynt2.test new file mode 100755 index 000000000..dbb754d92 --- /dev/null +++ b/tests/core/ltlsynt2.test @@ -0,0 +1,77 @@ +#! /bin/sh +# -*- coding: utf-8 -*- +# Copyright (C) 2022 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 . + + +# More checks for ltlfilt + +. ./defs || exit 1 + +set -e + +cat >formulas.ltl < Xo1) +F(i1 xor i2) <-> F(o1) +i1 <-> F(o1 xor o2) +F(i1) <-> G(o2) +EOF + +ltlsynt --ins=i1,i2 -F formulas.ltl -f 'o1 & F(i1 <-> o2)' -q --csv=out.csv &&\ + exit 2 +test $? -eq 1 || exit 2 + +cat >test.py <expected < Xo1),lar,1,3 +F(i1 xor i2) <-> Fo1,lar,1,2 +i1 <-> F(o1 xor o2),lar,1,3 +Fi1 <-> Go2,lar,0,0 +o1 & F(i1 <-> o2),lar,1,4 +EOF + +diff filtered.csv expected + +# ltlfilt should be able to read the first columns +mv filtered.csv input.csv +ltlsynt --ins=i1,i2 -F input.csv/-1 --csv=out.csv -q && exit 2 +test $? -eq 1 +$PYTHON test.py +diff filtered.csv expected + +grep -v 0,0 filtered.csv >input.csv +ltlsynt --ins=i1,i2 -F input.csv/-1 --csv=out.csv -q || exit 2 +$PYTHON test.py +diff filtered.csv input.csv From 3cd43f618c6cabab2762bf8893238f249dc88500 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Fri, 30 Sep 2022 13:36:41 +0200 Subject: [PATCH 145/337] test: fix running on python test in OpenBSD * tests/run.in: Add LD_LIBRARY_PATH. --- tests/run.in | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/tests/run.in b/tests/run.in index 3b9470bef..7502b88f8 100755 --- a/tests/run.in +++ b/tests/run.in @@ -46,7 +46,9 @@ PATH="@abs_top_builddir@/bin:$PATH" export PATH test -z "$1" && - PYTHONPATH=$pypath DYLD_LIBRARY_PATH=$modpath:$DYLD_LIBRARY_PATH \ + PYTHONPATH=$pypath \ + DYLD_LIBRARY_PATH=$modpath:$DYLD_LIBRARY_PATH \ + LD_LIBRARY_PATH=$modpath:$LD_LIBRARY_PATH \ exec $PREFIXCMD @PYTHON@ srcdir="@srcdir@" @@ -109,10 +111,12 @@ case $1 in *.ipynb) PYTHONPATH=$pypath:$PYTHONPATH \ DYLD_LIBRARY_PATH=$modpath:$DYLD_LIBRARY_PATH \ + LD_LIBRARY_PATH=$modpath:$LD_LIBRARY_PATH \ PYTHONIOENCODING=utf-8:surrogateescape \ exec $PREFIXCMD @PYTHON@ @abs_srcdir@/python/ipnbdoctest.py "$@";; *.py) PYTHONPATH=$pypath:$PYTHONPATH \ + LD_LIBRARY_PATH=$modpath:$LD_LIBRARY_PATH \ DYLD_LIBRARY_PATH=$modpath:$DYLD_LIBRARY_PATH \ exec $PREFIXCMD @PYTHON@ "$@";; *.test) @@ -121,6 +125,7 @@ case $1 in exec $PERL "$@";; *python*|*jupyter*|*pypy*) PYTHONPATH=$pypath:$PYTHONPATH \ + LD_LIBRARY_PATH=$modpath:$LD_LIBRARY_PATH \ DYLD_LIBRARY_PATH=$modpath:$DYLD_LIBRARY_PATH \ exec $PREFIXCMD "$@";; *) From 27816ea4385cc9b3b33c50a8e5402b8a9f40d58e Mon Sep 17 00:00:00 2001 From: Florian Renkin Date: Sat, 3 Sep 2022 14:04:41 +0200 Subject: [PATCH 146/337] synthesis: Fix for implication decomposition * spot/twaalgos/synthesis.cc: here --- spot/twaalgos/synthesis.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/spot/twaalgos/synthesis.cc b/spot/twaalgos/synthesis.cc index 6fb126ff8..e1e4e1780 100644 --- a/spot/twaalgos/synthesis.cc +++ b/spot/twaalgos/synthesis.cc @@ -1737,7 +1737,7 @@ namespace // anonymous for subsformula std::vector children; for (auto fi : f) children.push_back( - extract_and(fi, outs, can_extract_impl, form2props)); + extract_and(fi, outs, false, form2props)); return formula::And(children); } if (f.is(op::Not)) From 4d2c096ec0ce9fee9701ad26af1ed66d1e552341 Mon Sep 17 00:00:00 2001 From: Florian Renkin Date: Sat, 3 Sep 2022 14:13:23 +0200 Subject: [PATCH 147/337] dot: fix 'g' with a Mealy machine * spot/twaalgos/dot.cc: here * tests/python/mealy.py: add test --- spot/twaalgos/dot.cc | 9 ++++++--- tests/python/mealy.py | 15 +++++++++++++++ 2 files changed, 21 insertions(+), 3 deletions(-) diff --git a/spot/twaalgos/dot.cc b/spot/twaalgos/dot.cc index 70b707edc..19a638b9e 100644 --- a/spot/twaalgos/dot.cc +++ b/spot/twaalgos/dot.cc @@ -1084,9 +1084,12 @@ namespace spot { if (aut->acc().is_t()) opt_show_acc_ = false; - bdd out = *p; - opt_mealy_output_ = out; - opt_mealy_ = true; + if (opt_showlabel_) + { + bdd out = *p; + opt_mealy_output_ = out; + opt_mealy_ = true; + } } incomplete_ = aut->get_named_prop>("incomplete-states"); diff --git a/tests/python/mealy.py b/tests/python/mealy.py index 71c7739f9..7a884235e 100644 --- a/tests/python/mealy.py +++ b/tests/python/mealy.py @@ -611,3 +611,18 @@ State: 1 res = spot.reduce_mealy(aut, True) tc.assertEqual(res.to_str(), exp) + +exp = """digraph "" { + rankdir=LR + node [shape="circle"] + I [label="", style=invis, width=0] + I -> 0 + 0 [label="0"] + 0 -> 1 [label=""] + 0 -> 1 [label=""] + 0 -> 1 [label=""] + 1 [label="1"] + 1 -> 1 [label=""] +} +""" +tc.assertEqual(res.to_str("dot", "g"), exp) From 74b752eb79d4846df37fef618ee2de335f4a4423 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Mon, 3 Oct 2022 09:15:55 +0200 Subject: [PATCH 148/337] * .gitlab-ci.yml (debian-gcc-snapshot): Build from tarball. --- .gitlab-ci.yml | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 4a94ebfce..eeb07acf7 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -110,8 +110,14 @@ debian-unstable-gcc-pypy: - spot-*/tests/*/*.log - spot-*/*.log +# With emacs now using gcc for on-the-fly compilation, +# we cannot rebuild the documentation using gcc-snapshot. So we start +# from the tarball instead. debian-gcc-snapshot: - stage: build + stage: build2 + needs: + - job: make-dist + artifacts: true only: - branches except: @@ -119,7 +125,9 @@ debian-gcc-snapshot: image: gitlab-registry.lre.epita.fr/spot/buildenv/debian script: - export PATH="/usr/lib/gcc-snapshot/bin:$PATH" LD_LIBRARY_PATH="/usr/lib/gcc-snapshot/lib:$LD_LIBRARY_PATH" - - autoreconf -vfi + - VERSION=`cat VERSION` + - tar xvf spot-$VERSION.tar.gz + - cd spot-$VERSION - ./configure --with-included-ltdl CXX='g++' - make - make distcheck DISTCHECK_CONFIGURE_FLAGS='--with-included-ltdl' @@ -127,11 +135,8 @@ debian-gcc-snapshot: artifacts: when: always paths: - - ./spot-*/_build/sub/tests/*/*.log - - ./*.log - - doc/spot.html/ - - doc/userdoc/ - - ./*.tar.gz + - spot-*/tests/*/*.log + - spot-*/*.log alpine-gcc: stage: build From fa4500a8d3b6330a129e712fd66ba1ce31c69c9b Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Mon, 3 Oct 2022 11:17:51 +0200 Subject: [PATCH 149/337] * tests/python/ipnbdoctest.py: Also retry if Kernel does not respond. --- tests/python/ipnbdoctest.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tests/python/ipnbdoctest.py b/tests/python/ipnbdoctest.py index c6bfcf134..f6ce3562e 100755 --- a/tests/python/ipnbdoctest.py +++ b/tests/python/ipnbdoctest.py @@ -364,7 +364,8 @@ if __name__ == '__main__': except RuntimeError as e: # If the Kernel dies, try again. It seems we have spurious # failures when multiple instances of jupyter start in parallel. - if 'Kernel died' in str(e): + stre = str(e) + if 'Kernel died' in stre or "Kernel didn't respond" in stre: tries -= 1 if tries: s = random.randint(1, 5) From 35b4cb89fc9a483944813e5eddaa7a5370638b43 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Mon, 3 Oct 2022 16:26:33 +0200 Subject: [PATCH 150/337] add test for previous decomposition patch * tests/core/ltlsynt.test: Here. --- tests/core/ltlsynt.test | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/tests/core/ltlsynt.test b/tests/core/ltlsynt.test index b9dfac204..4a7595539 100644 --- a/tests/core/ltlsynt.test +++ b/tests/core/ltlsynt.test @@ -1004,3 +1004,24 @@ digraph "" { } EOF diff res exp + + +# The following formula, generated from SPIReadManag.tlsf exhibited a bug +# in the decomposition. +s1="G(!((!o15 & !((!o14 & o16) <-> (o14 & !o16))) <-> (o15 & !(o14 | o16)))" +s2=" & !((!o12 & !((!o11 & o13) <-> (o11 & !o13))) <-> (o12 & !(o11 | o13)))" +s3=" & !((o09 & !o10) <-> (!o09 & o10)) & !((o07 & !o08) <-> (!o07 & o08))" +s4=" & !((!o05 & !((!o04 & o06) <-> (o04 & !o06))) <-> (o05 & !(o04 | o06)))" +s5=" & !((!o02 & !((!o01 & o03) <-> (o01 & !o03))) <-> (o02 & !(o01 | o03))))" +s6=" & ((G!(i2 & i7) & G(o15 -> Fi3)) -> (Go09 & G(o14 <-> (i6 & !i7)) & " +s7="G(o07 <-> (i7 & i8)) & G((i7 & i8) -> (o11 U i3)) & GFo12 & G(o04 <-> " +s8="(i4 & i6)) & G(o05 <-> !(i4 & i6)) & G(o15 <-> (i7 & i8)) & G(i7 -> o02) & " +s9="G((!i7 & !(i1 & i2 & !i5 & i6)) -> o03) & G(o01 <-> (i1 & i2 & !i5 & i6))))" +s=$s1$s2$s3$s4$s5$s6$s7$s8$s9 +ltlsynt --decomp=yes -f "$s" --ins=i1,i2,i3,i4,i5,i6,i7,i8 --realizability >out +ltlsynt --decomp=no -f "$s" --ins=i1,i2,i3,i4,i5,i6,i7,i8 --realizability >>out +cat >expected < Date: Mon, 3 Oct 2022 16:48:03 +0200 Subject: [PATCH 151/337] * .dir-locals.el (bug-reference-bug-regexp): Fix first group. --- .dir-locals.el | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.dir-locals.el b/.dir-locals.el index 91c287367..5de24fdfc 100644 --- a/.dir-locals.el +++ b/.dir-locals.el @@ -3,7 +3,7 @@ (require-final-newline . t) (mode . global-whitespace) (bug-reference-bug-regexp - . "\\(?:[Ff]ix\\(es\\)? \\|[Ii]ssue \\)#\\(?2:[0-9]+\\)") + . "\\(?1:\\(?:[Ff]ix\\(?:es\\)? \\|[Ii]ssue \\)#\\(?2:[0-9]+\\)\\)") (bug-reference-url-format . "https://gitlab.lre.epita.fr/spot/spot/issues/%s") (mode . bug-reference) From e907f114885faf36282dc90bddfd1a99e0fe4ed3 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Mon, 3 Oct 2022 17:00:15 +0200 Subject: [PATCH 152/337] emptinesscheck: improve coverage of CVWY90 and SE05 * tests/core/randtgba.cc: Test the ar:form_stack variants. --- tests/core/randtgba.cc | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/tests/core/randtgba.cc b/tests/core/randtgba.cc index 7462f2c80..460bf9cd9 100644 --- a/tests/core/randtgba.cc +++ b/tests/core/randtgba.cc @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2008-2012, 2014-2019 Laboratoire de Recherche et +// Copyright (C) 2008-2012, 2014-2019, 2022 Laboratoire de Recherche et // Développement de l'Epita (LRDE). // Copyright (C) 2004, 2005 Laboratoire d'Informatique de Paris // 6 (LIP6), département Systèmes Répartis Coopératifs (SRC), @@ -69,9 +69,11 @@ const char* default_algos[] = { "Cou99abs", "CVWY90", "CVWY90(bsh=4K)", + "CVWY90(ar:from_stack)", "GV04", "SE05", "SE05(bsh=4K)", + "SE05(ar:from_stack)", "Tau03", "Tau03_opt", "Tau03_opt(condstack)", From 4ab51e1c88529e6869d863d9701efdf36992ed3a Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Mon, 3 Oct 2022 17:40:45 +0200 Subject: [PATCH 153/337] toparity: cover more options * tests/python/toparity.py: Augment test cases. --- tests/python/toparity.py | 136 ++++++++++++++++++++++++++++----------- 1 file changed, 98 insertions(+), 38 deletions(-) diff --git a/tests/python/toparity.py b/tests/python/toparity.py index ad9bc6e0b..ab5fbf314 100644 --- a/tests/python/toparity.py +++ b/tests/python/toparity.py @@ -49,26 +49,26 @@ no_option.propagate_col = False no_option.use_generalized_rabin = False acc_clean_search_opt = spot.to_parity_options() -no_option.search_ex = False -no_option.use_last = False -no_option.use_last_post_process = False -no_option.force_order = False -no_option.partial_degen = False -no_option.acc_clean = True -no_option.parity_equiv = False -no_option.tar = False -no_option.iar = True -no_option.lar_dfs = True -no_option.bscc = True -no_option.parity_prefix = False -no_option.parity_prefix_general = False -no_option.generic_emptiness = False -no_option.rabin_to_buchi = False -no_option.buchi_type_to_buchi = False -no_option.parity_type_to_parity = False -no_option.reduce_col_deg = False -no_option.propagate_col = False -no_option.use_generalized_rabin = False +acc_clean_search_opt.search_ex = False +acc_clean_search_opt.use_last = False +acc_clean_search_opt.use_last_post_process = False +acc_clean_search_opt.force_order = False +acc_clean_search_opt.partial_degen = False +acc_clean_search_opt.acc_clean = True +acc_clean_search_opt.parity_equiv = False +acc_clean_search_opt.tar = False +acc_clean_search_opt.iar = True +acc_clean_search_opt.lar_dfs = True +acc_clean_search_opt.bscc = True +acc_clean_search_opt.parity_prefix = False +acc_clean_search_opt.parity_prefix_general = False +acc_clean_search_opt.generic_emptiness = False +acc_clean_search_opt.rabin_to_buchi = False +acc_clean_search_opt.buchi_type_to_buchi = False +acc_clean_search_opt.parity_type_to_parity = False +acc_clean_search_opt.reduce_col_deg = False +acc_clean_search_opt.propagate_col = False +acc_clean_search_opt.use_generalized_rabin = False partial_degen_opt = spot.to_parity_options() partial_degen_opt.search_ex = False @@ -156,9 +156,29 @@ use_car_opt.reduce_col_deg = False use_car_opt.propagate_col = False use_car_opt.use_generalized_rabin = False -all_opt = spot.to_parity_options() -all_opt.pretty_print = True +default_opt = spot.to_parity_options() +all_opt = spot.to_parity_options() +all_opt.search_ex = True +all_opt.use_last = True +all_opt.use_last_post_process = True +all_opt.partial_degen = True +all_opt.acc_clean = True +all_opt.parity_equiv = True +all_opt.tar = True +all_opt.iar = True +all_opt.lar_dfs = True +all_opt.bscc = True +all_opt.parity_prefix = True +all_opt.parity_prefix_general = True +all_opt.generic_emptiness = True +all_opt.rabin_to_buchi = True +all_opt.buchi_type_to_buchi = True +all_opt.parity_type_to_parity = True +all_opt.reduce_col_deg = True +all_opt.propagate_col = True +all_opt.use_generalized_rabin = True +all_opt.pretty_print = True options = [ no_option, @@ -167,6 +187,7 @@ options = [ parity_equiv_opt, rab_to_buchi_opt, use_car_opt, + default_opt, all_opt, None, # acd_transform ] @@ -174,6 +195,8 @@ options = [ def test(aut, expected_num_states=[], full=True): for (opt, expected_num) in zip_longest(options, expected_num_states): + if type(expected_num) is str and expected_num == 'skip': + continue if opt is not None: p1 = spot.to_parity(aut, search_ex = opt.search_ex, @@ -296,7 +319,7 @@ State: 13 [0&1] 5 [!0&!1] 10 {0 1 3 5} [0&!1] 13 {1 3} ---END--"""), [32, 22, 23, 30, 33, 45, 22, 21]) +--END--"""), [30, 32, 23, 30, 33, 45, 22, 22, 21]) test(spot.automaton(""" HOA: v1 @@ -314,7 +337,7 @@ State: 1 [0&!1] 1 {4} [!0&1] 1 {0 1 2 3} [!0&!1] 1 {0 3} ---END--"""), [6, 3, 3, 5, 5, 26, 3, 3]) +--END--"""), [5, 6, 3, 5, 5, 26, 3, 3, 3]) test(spot.automaton("""HOA: v1 States: 2 @@ -330,14 +353,7 @@ State: 0 State: 1 [0&1] 1 {2 3 4} [!0&!1] 0 {1 2} ---END--"""), [3, 2, 2, 9, 9, 10, 2, 2]) - -for i,f in enumerate(spot.randltl(10, 200)): - test(spot.translate(f, "det", "G"), full=(i<50)) - -for f in spot.randltl(5, 500): - test(spot.translate(f), full=False) - +--END--"""), [9, 3, 2, 9, 9, 10, 2, 2, 2]) test(spot.automaton(""" HOA: v1 @@ -370,7 +386,7 @@ State: 3 [!0&1] 2 {1 4} [0&1] 3 {0} --END-- -"""), [104, 6, 80, 23, 27, 17, 6, 5]) +"""), [23, 104, 80, 23, 27, 17, "skip", "skip", 5]) test(spot.automaton(""" HOA: v1 @@ -404,7 +420,7 @@ State: 4 [0&!1] 4 [0&1] 4 {1 2 4} --END-- -"""), [6, 6, 7, 9, 9, 10, 6, 6]) +"""), [9, 6, 7, 9, 9, 10, 6, 6, 6]) test(spot.automaton(""" HOA: v1 @@ -426,7 +442,7 @@ State: 1 [0&!1] 1 {2 3} [0&1] 1 {1 2 4} --END-- -"""), [3, 2, 2, 6, 6, 6, 2, 2]) +"""), [6, 3, 2, 6, 6, 6, 2, 2, 2]) # Tests both the old and new version of to_parity @@ -457,7 +473,7 @@ explicit-labels trans-acc --BODY-- State: 0 [0&1] 2 {4 5} [0&1] 4 {0 4} p = spot.to_parity_old(a, True) tc.assertEqual(p.num_states(), 22) tc.assertTrue(spot.are_equivalent(a, p)) -test(a, [6, 6, 7, 8, 6, 7, 6, 6]) +test(a, [8, 6, 7, 8, 6, 7, 6, 6, 6]) # Force a few edges to false, to make sure to_parity() is OK with that. for e in a.out(2): @@ -471,7 +487,7 @@ for e in a.out(3): p = spot.to_parity_old(a, True) tc.assertEqual(p.num_states(), 22) tc.assertTrue(spot.are_equivalent(a, p)) -test(a, [6, 6, 7, 8, 6, 7, 6, 6]) +test(a, [8, 6, 7, 8, 6, 7, 6, 6, 6]) for f in spot.randltl(4, 400): d = spot.translate(f, "det", "G") @@ -483,8 +499,52 @@ for f in spot.randltl(5, 2000): p = spot.to_parity_old(n, True) tc.assertTrue(spot.are_equivalent(n, p)) +for i,f in enumerate(spot.randltl(10, 200)): + test(spot.translate(f, "det", "G"), full=(i<50)) + +for f in spot.randltl(5, 500): + test(spot.translate(f), full=False) + # Issue #390. a = spot.translate('!(GFa -> (GFb & GF(!b & !Xb)))', 'gen', 'det') b = spot.to_parity_old(a, True) tc.assertTrue(a.equivalent_to(b)) -test(a, [7, 3, 3, 8, 8, 7, 3, 3]) +test(a, [8, 7, 3, 8, 8, 7, 3, 3, 3]) + +# owl-21.0 ltl2dra -f '(GFa -> GFb) & GF(c <-> Xc)' | autfilt -Hi | fmt +a = spot.automaton(""" +HOA: v1 name: "Automaton for ((((F(G(!a))) | (G(F(b))))) & (G(F(((c) <-> +(X(c)))))))" States: 11 Start: 0 AP: 3 "a" "b" "c" acc-name: Rabin 3 +Acceptance: 6 (Fin(0) & Inf(1)) | (Fin(2) & Inf(3)) | (Fin(4) & Inf(5)) +properties: implicit-labels trans-acc complete deterministic --BODY-- +State: 0 0 {3} 0 {2 4} 3 {3} 3 {2 4} 1 0 {2 4} 2 4 {2 4} State: 1 0 0 {2 +4} 3 3 {2 4} 1 {5} 0 {2 4} 2 {5} 4 {2 4} State: 2 3 3 {2 4} 3 3 {2 4} +6 {1 5} 5 {1 2 4} 2 {1 5} 4 {1 2 4} State: 3 7 {1 3} 7 {1 2 4} 3 {1 3} +3 {1 2 4} 2 4 {2 4} 2 4 {2 4} State: 4 3 {3} 3 {2 4} 3 {3} 3 {2 4} 6 {1} +5 {1 2 4} 2 {1} 4 {1 2 4} State: 5 8 {3} 8 {2 4} 3 {3} 3 {2 4} 6 5 {2 +4} 2 4 {2 4} State: 6 8 8 {2 4} 3 3 {2 4} 6 {5} 5 {2 4} 2 {5} 4 {2 4} +State: 7 7 {3} 7 {2 4} 3 {3} 3 {2 4} 9 10 {2 4} 2 4 {2 4} State: 8 0 {1 +3} 0 {1 2 4} 3 {1 3} 3 {1 2 4} 6 5 {2 4} 2 4 {2 4} State: 9 7 7 {2 4} +3 3 {2 4} 1 {1 5} 0 {1 2 4} 2 {1 5} 4 {1 2 4} State: 10 7 {3} 7 {2 4} +3 {3} 3 {2 4} 1 {1} 0 {1 2 4} 2 {1} 4 {1 2 4} --END-- +""") +b = spot.iar_maybe(a) +tc.assertEqual(b.num_states(), 11) +tc.assertTrue(a.equivalent_to(b)) +test(a, [11, 11, 11, 11, 11, 11, 11, 11, 11]) + +a = spot.automaton(""" +HOA: v1 States: 10 Start: 0 AP: 2 "p0" "p1" acc-name: Rabin 4 Acceptance: +8 (Fin(0) & Inf(1)) | (Fin(2) & Inf(3)) | (Fin(4) & Inf(5)) | (Fin(6) +& Inf(7)) properties: implicit-labels trans-acc complete deterministic +--BODY-- State: 0 2 {7} 7 {3} 2 {7} 3 State: 1 5 {0 3} 9 {3 4} 5 {0 3} +9 {3 4} State: 2 9 {1 6} 9 {1 6} 9 {1 6} 9 {1 6} State: 3 3 {4} 9 {0} +1 {4} 4 {5 6} State: 4 7 8 {1 5 7} 9 {3 7} 8 {1 5 7} State: 5 6 {4} 9 +{1 2 6} 6 {4} 9 {1 2 6} State: 6 1 {3 7} 1 {3 7} 1 {3 7} 1 {3 7} State: +7 1 {3 6} 8 {2} 1 {3 6} 8 {2} State: 8 8 {3 4 7} 3 {2} 8 {3 4 7} 3 {2} +State: 9 3 {4} 2 3 {4} 6 --END-- +""") +b = spot.iar_maybe(a) +tc.assertEqual(b.num_states(), 87) +tc.assertTrue(a.equivalent_to(b)) +test(a, [87, 91, 91, 87, 87, 87, 51, 51, 21]) From e867242cf619900f07ccc450389c247438e9d4e6 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Tue, 4 Oct 2022 11:15:07 +0200 Subject: [PATCH 154/337] Update troubleshouting instruction for Python bindings For issue #512 * README: Update instructions. * configure.ac: Add some code to warn if Python files will be installed in a place that is not searched up by default. Add --with-pythondir support. * NEWS: Mention --with-pythondir. --- NEWS | 5 +++++ README | 40 +++++++++++++++++++++++++++------------- configure.ac | 25 +++++++++++++++++++++++++ 3 files changed, 57 insertions(+), 13 deletions(-) diff --git a/NEWS b/NEWS index 262414d67..88c689bfc 100644 --- a/NEWS +++ b/NEWS @@ -2,6 +2,11 @@ New in spot 2.10.6.dev (not yet released) Build: + - configure will now diagnose situation where Python bindings will + be installed in a directory that is not part of Python's search + path. A new configure option --with-pythondir can be used to + modify this installation path. + - A new configure option --enable-pthread enable the compilation of Spot with -pthread, and activate the parallel version of some algorithms. If Spot is compiled with -pthread enabled, any user diff --git a/README b/README index a0b7c9579..458da2d99 100644 --- a/README +++ b/README @@ -110,16 +110,16 @@ Spot follows the traditional `./configure && make && make check && make install' process. People unfamiliar with the GNU Build System should read the file INSTALL for generic instructions. -If you plan to use the Python binding, we recommend you use one -of the following --prefix options when calling configure: +If you plan to use the Python bindings, we recommend you use the +following --prefix options when calling configure: - --prefix /usr - --prefix /usr/local (the default) - --prefix ~/.local (if you do not have root permissions) + --prefix ~/.local -The reason is that all these locations are usually automatically -searched by Python. If you use a different prefix directory, you may -have to tune the PYTHONPATH environment variable. +The reason is that ~/.local/lib/python3.X/site-packages, where Spot's +Python bindings will be installed, is automatically searched by +Python. If you use a different prefix directory, you may have to tune +the PYTHONPATH environment variable, or use the --with-pythondir +option to specify different installation paths. In addition to its usual options, ./configure will accept some flags specific to Spot: @@ -250,17 +250,31 @@ To test the Python bindings, try running >>> import spot >>> print(spot.version()) -If you installed Spot with a prefix that is not one of those suggested -in the "Building and installing" section, it is likely that the above -import statement will fail to locate the spot package. You can show -the list of directories that are searched by Python using: +If you installed Spot with a prefix that is not searched by Python by +default it is likely that the above import statement will fail to +locate the spot package. You can show the list of directories that +are searched by Python using: % python3 >>> import sys >>> print(sys.path) And you can modify that list of searched directories using the -PYTHONPATH environment variable. +PYTHONPATH environment variable. Alternatively, you can instruct Spot +to install its Python files in one of those directory using the +--with-pythondir configure option. As an example, an issue in +distributions derived from Debian is that if you run + + ./configure && make && make install + +Python files get installed in /usr/local/lib/python3.X/site-packages +while Debian's version of Python only looks for them into +/usr/local/lib/python3.X/dist-packages instead. You can fix that by +instructing configure that you want packages installed into the right +directory instead: + + ./configure --with-pythondir=/usr/local/lib/python3.X/dist-packages \ + && make && make install To test if man pages can be found, simply try: diff --git a/configure.ac b/configure.ac index 2d6b4be1f..e2e16d63a 100644 --- a/configure.ac +++ b/configure.ac @@ -189,9 +189,14 @@ if test "x${enable_python:-yes}" = xyes; then AC_MSG_NOTICE([You may configure with --disable-python ]dnl [if you do not need Python bindings.]) adl_CHECK_PYTHON + + AC_ARG_WITH([pythondir], + [AS_HELP_STRING([--with-pythondir], [override the computed pythondir])], + [pythondir=$withval], []) fi + adl_ENABLE_DEBUG ad_GCC_OPTIM adl_NDEBUG @@ -290,3 +295,23 @@ case $VERSION:$enable_devel in echo '===================================================================' ;; esac + +case $enable_python in + yes) + pd=$pythondir + eval pd=$pd + eval pd=$pd + $PYTHON -c " +import sys +if '$pd' in sys.path: + exit() +else: + print('\nWARNING: Python bindings will be installed in $pd') + print(' however this path is not searched by default by $PYTHON.') + print('\n$PYTHON\'s sys.path contains the following paths:\n', + '\n'.join(sys.path)) + print('\nUse --with-pythondir=... if you wish ' + 'to change this installation path.') +" + ;; +esac From 344e01d4e2450bf0e7f1d891544a330d62786f46 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Wed, 5 Oct 2022 11:08:19 +0200 Subject: [PATCH 155/337] translate, postproc: improve parity output * spot/twaalgos/translate.cc: When producing Parity output, split LTL as we do in the Generic case. * spot/twaalgos/postproc.hh, spot/twaalgos/postproc.cc: Use acd_transform() and add an "acd" option to disable this. * bin/spot-x.cc, NEWS: Document this. * tests/core/genltl.test, tests/core/minusx.test, tests/core/parity2.test: Adjust test cases for improved outputs. --- NEWS | 11 +- bin/spot-x.cc | 3 + spot/twaalgos/postproc.cc | 31 ++-- spot/twaalgos/postproc.hh | 2 + spot/twaalgos/translate.cc | 20 +-- tests/core/genltl.test | 32 ++-- tests/core/minusx.test | 10 +- tests/core/parity2.test | 321 ++++++++++++++----------------------- 8 files changed, 189 insertions(+), 241 deletions(-) diff --git a/NEWS b/NEWS index 88c689bfc..3a2b8316b 100644 --- a/NEWS +++ b/NEWS @@ -164,11 +164,20 @@ New in spot 2.10.6.dev (not yet released) further simplification. This was introduced to help with automata produced from formulas output by "genltl --eil-gsi" (see above). - - spot::postproc has new configuration variable branch-post that + - spot::postprocessor has new configuration variable branch-post that can be used to control the use of branching-postponement (diabled by default) or delayed-branching (see above, enabled by default). See the spot-x(7) man page for details. + - spot::postprocessor is now using acd_transform() by default when + building parity automata. Setting option "acd=0" will revert + to using "to_parity()" instead. + + - When asked to build parity automata, spot::translator is now more + aggressively using LTL decomposition, as done in the Generic + acceptance case before paritizing the result. This results in + much smaller automata in many cases. + - spot::parallel_policy is an object that can be passed to some algorithm to specify how many threads can be used if Spot has been compiled with --enable-pthread. Currently, only diff --git a/bin/spot-x.cc b/bin/spot-x.cc index 908cbb98a..d1a8f96f6 100644 --- a/bin/spot-x.cc +++ b/bin/spot-x.cc @@ -80,6 +80,9 @@ only if it is smaller than the original skeleton. This option is only \ used when comp-susp=1 and default to 1 or 2 depending on whether --small \ or --deterministic is specified.") }, { nullptr, 0, nullptr, 0, "Postprocessing options:", 0 }, + { DOC("acd", "Set to 1 (the default) to use paritize automata using \ +the alternatinc cycle decomposition. Set to 0 to use paritization based \ +on latest appearance record variants.") }, { DOC("scc-filter", "Set to 1 (the default) to enable \ SCC-pruning and acceptance simplification at the beginning of \ post-processing. Transitions that are outside of accepting SCC are \ diff --git a/spot/twaalgos/postproc.cc b/spot/twaalgos/postproc.cc index 55feeb295..19e3d2b6c 100644 --- a/spot/twaalgos/postproc.cc +++ b/spot/twaalgos/postproc.cc @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2012-2021 Laboratoire de Recherche et Développement +// Copyright (C) 2012-2022 Laboratoire de Recherche et Développement // de l'Epita (LRDE). // // This file is part of Spot, a model checking library. @@ -40,6 +40,7 @@ #include #include #include +#include namespace spot { @@ -92,6 +93,7 @@ namespace spot merge_states_min_ = opt->get("merge-states-min", 128); wdba_det_max_ = opt->get("wdba-det-max", 4096); simul_trans_pruning_ = opt->get("simul-trans-pruning", 512); + acd_ = opt->get("acd", 1); if (sat_acc_ && sat_minimize_ == 0) sat_minimize_ = 1; // Dicho. @@ -250,7 +252,8 @@ namespace spot tmp = ensure_ba(tmp); if (want_parity) { - reduce_parity_here(tmp, COLORED_); + if (!acd_was_used_) + reduce_parity_here(tmp, COLORED_); parity_kind kind = parity_kind_any; parity_style style = parity_style_any; if ((type_ & ParityMin) == ParityMin) @@ -295,6 +298,8 @@ namespace spot bool via_gba = (type_ == Buchi) || (type_ == GeneralizedBuchi) || (type_ == Monitor); bool want_parity = type_ & Parity; + acd_was_used_ = false; + if (COLORED_ && !want_parity) throw std::runtime_error("postprocessor: the Colored setting only works " "for parity acceptance"); @@ -340,18 +345,26 @@ namespace spot !(type_ == Generic && PREF_ == Any && level_ == Low)) a = remove_alternation(a); + // If we do want a parity automaton, we can use to_parity(). + // However (1) degeneralization is faster if the input is + // GBA, and (2) if we want a deterministic parity automaton and the + // input is not deterministic, that is useless here. We need + // to determinize it first, and our deterministization + // function only deal with TGBA as input. if ((via_gba || (want_parity && !a->acc().is_parity())) && !a->acc().is_generalized_buchi()) { - // If we do want a parity automaton, we can use to_parity(). - // However (1) degeneralization is better if the input is - // GBA, and (2) if we want a deterministic parity automaton and the - // input is not deterministic, that is useless here. We need - // to determinize it first, and our deterministization - // function only deal with TGBA as input. if (want_parity && (PREF_ != Deterministic || is_deterministic(a))) { - a = to_parity(a); + if (acd_) + { + a = acd_transform(a, COLORED_); + acd_was_used_ = true; + } + else + { + a = to_parity(a); + } } else { diff --git a/spot/twaalgos/postproc.hh b/spot/twaalgos/postproc.hh index 96128c531..f470dcf5b 100644 --- a/spot/twaalgos/postproc.hh +++ b/spot/twaalgos/postproc.hh @@ -270,6 +270,8 @@ namespace spot int simul_max_ = 4096; int merge_states_min_ = 128; int wdba_det_max_ = 4096; + bool acd_ = false; + bool acd_was_used_; }; /// @} } diff --git a/spot/twaalgos/translate.cc b/spot/twaalgos/translate.cc index cd1e2aa63..0f5e86cde 100644 --- a/spot/twaalgos/translate.cc +++ b/spot/twaalgos/translate.cc @@ -137,6 +137,9 @@ namespace spot twa_graph_ptr aut; twa_graph_ptr aut2 = nullptr; + bool split_hard = + type_ == Generic || (type_ & Parity) || type_ == GeneralizedBuchi; + if (ltl_split_ && !r.is_syntactic_obligation()) { formula r2 = r; @@ -146,11 +149,11 @@ namespace spot r2 = r2[0]; ++leading_x; } - if (type_ == Generic || type_ == GeneralizedBuchi) + if (split_hard) { - // F(q|u|f) = q|F(u)|F(f) only for generic acceptance + // F(q|u|f) = q|F(u)|F(f) disabled for GeneralizedBuchi // G(q&e&f) = q&G(e)&G(f) - bool want_u = r2.is({op::F, op::Or}) && (type_ == Generic); + bool want_u = r2.is({op::F, op::Or}) && (type_ != GeneralizedBuchi); if (want_u || r2.is({op::G, op::And})) { std::vector susp; @@ -213,20 +216,19 @@ namespace spot oblg.erase(i, oblg.end()); } + // The only cases where we accept susp and rest to be both + // non-empty is when doing Generic/Parity/TGBA if (!susp.empty()) { - // The only cases where we accept susp and rest to be both - // non-empty is when doing Generic acceptance or TGBA. - if (!rest.empty() - && !(type_ == Generic || type_ == GeneralizedBuchi)) + if (!rest.empty() && !split_hard) { rest.insert(rest.end(), susp.begin(), susp.end()); susp.clear(); } // For Parity, we want to translate all suspendable // formulas at once. - if (rest.empty() && type_ & Parity) - susp = { formula::multop(r2.kind(), susp) }; + //if (rest.empty() && type_ & Parity) + // susp = { formula::multop(r2.kind(), susp) }; } // For TGBA and BA, we only split if there is something to // suspend. diff --git a/tests/core/genltl.test b/tests/core/genltl.test index 71b1ddf77..ce5584a21 100755 --- a/tests/core/genltl.test +++ b/tests/core/genltl.test @@ -190,29 +190,29 @@ cat >exp< Date: Wed, 5 Oct 2022 16:29:47 +0200 Subject: [PATCH 156/337] fix previous patch this patch failed on i386 and on Raspberry. * spot/twaalgos/translate.cc: Clear. * spot/twaalgos/postproc.cc: Call reduce_parity_here in more cases. --- spot/twaalgos/postproc.cc | 10 ++++++---- spot/twaalgos/translate.cc | 1 + 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/spot/twaalgos/postproc.cc b/spot/twaalgos/postproc.cc index 19e3d2b6c..1a2915dda 100644 --- a/spot/twaalgos/postproc.cc +++ b/spot/twaalgos/postproc.cc @@ -239,11 +239,13 @@ namespace spot { if (PREF_ != Any && level_ != Low) tmp->remove_unused_ap(); - if (COMP_) + bool was_complete = tmp->prop_complete().is_true(); + if (COMP_ && !was_complete) tmp = complete(tmp); bool want_parity = type_ & Parity; - if (want_parity && (tmp->acc().is_generalized_buchi() - || tmp->acc().is_generalized_co_buchi())) + if (want_parity && tmp->num_sets() > 1 + && (tmp->acc().is_generalized_buchi() + || tmp->acc().is_generalized_co_buchi())) tmp = choose_degen(tmp); assert(!!SBACC_ == state_based_); if (state_based_) @@ -252,7 +254,7 @@ namespace spot tmp = ensure_ba(tmp); if (want_parity) { - if (!acd_was_used_) + if (!acd_was_used_ || (COMP_ && !was_complete)) reduce_parity_here(tmp, COLORED_); parity_kind kind = parity_kind_any; parity_style style = parity_style_any; diff --git a/spot/twaalgos/translate.cc b/spot/twaalgos/translate.cc index 0f5e86cde..8a99313a3 100644 --- a/spot/twaalgos/translate.cc +++ b/spot/twaalgos/translate.cc @@ -387,6 +387,7 @@ namespace spot || type_ == GeneralizedBuchi) aut2 = gf_guarantee_to_ba_maybe(r, simpl_->get_dict(), det, state_based_); + acd_was_used_ = false; if (aut2 && (pref_ & Deterministic)) return finalize(aut2); if (!aut2 && (type_ == Generic From 1a4121c6c2826b73c8ec4c65c9d438674918653f Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Fri, 7 Oct 2022 16:37:02 +0200 Subject: [PATCH 157/337] * tests/Makefile.am (.ipynb.html): Use classic template. --- tests/Makefile.am | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/Makefile.am b/tests/Makefile.am index 2384f115e..71d6a852f 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -476,7 +476,11 @@ endif CLEANFILES = python/test1.dve python/test1.dve2C python/test1.dve.cpp SUFFIXES = .ipynb .html +# Use the classic template when available because it loads +# jquery and we need it in zlktree.html; however the --template +# option does not exist with nbconvert 5.6.1 (in Debian stable). .ipynb.html: + $(JUPYTER) nbconvert $< --to html --template classic --stdout >$@ || \ $(JUPYTER) nbconvert $< --to html --stdout >$@ .PHONY: nb-html From 9fc48daf281c706e9b61d792a8b035edb98b4ec1 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Sat, 8 Oct 2022 10:34:30 +0200 Subject: [PATCH 158/337] CI: work around GIT_STRATEGY=none not cleaning the build dir * .gitlab-ci.yml (publish-rpm, publish-stable, publish-unstable): Use the latest files and clean things up after publication. --- .gitlab-ci.yml | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index eeb07acf7..11be46f71 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -410,6 +410,7 @@ publish-rpm: - rpm-pkg script: - case $CI_COMMIT_REF_NAME in stable) rput fedora stable *.rpm;; next) rput fedora unstable *.rpm;; esac + - rm -rf ./* publish-stable: only: @@ -425,11 +426,12 @@ publish-stable: script: - cd _build_stable - ls -l - - dput lrde *.changes + - dput lrde `ls -t *.changes | head -1` - cd .. - ls -l - tgz=`ls spot-*.tar.* | head -n 1` - case $tgz in *[0-9].tar.*) scp $tgz doc@perso:/var/www/dload/spot/;; esac + - rm -rf ./* - curl -X POST -F ref=master -F token=$TRIGGER_SPOT_WEB -F "variables[spot_branch]=stable" https://gitlab.lre.epita.fr/api/v4/projects/131/trigger/pipeline - curl -X POST "https://archive.softwareheritage.org/api/1/origin/save/git/url/https://gitlab.lre.epita.fr/spot/spot/" - curl "https://web.archive.org/save/https://www.lrde.epita.fr/dload/spot/$tgz" @@ -447,7 +449,9 @@ publish-unstable: script: - cd _build_unstable - ls -l - - dput lrde *.changes + - dput lrde `ls -t *.changes | head -1` + - cd .. + - rm -rf _build_unstable - curl -X POST -F ref=master -F token=$TRIGGER_SPOT_WEB -F "variables[spot_branch]=next" https://gitlab.lre.epita.fr/api/v4/projects/131/trigger/pipeline - curl -X POST -F ref=master -F token=$TRIGGER_SANDBOX https://gitlab.lre.epita.fr/api/v4/projects/181/trigger/pipeline From 1a5b5602db2909d7e61b1bda4dbcae132d231393 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Sat, 8 Oct 2022 15:28:15 +0200 Subject: [PATCH 159/337] * .gitlab-ci.yml (publish-unstable): Publish both amd64 and i386. --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 11be46f71..b7cd7fd1d 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -449,7 +449,7 @@ publish-unstable: script: - cd _build_unstable - ls -l - - dput lrde `ls -t *.changes | head -1` + - dput lrde `ls -t *amd64.changes | head -1` `ls -t *i386.changes | head -1` - cd .. - rm -rf _build_unstable - curl -X POST -F ref=master -F token=$TRIGGER_SPOT_WEB -F "variables[spot_branch]=next" https://gitlab.lre.epita.fr/api/v4/projects/131/trigger/pipeline From 8131fae1a6fefc3f6c8a768ba35879c5f4d29f7e Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Sat, 8 Oct 2022 20:59:27 +0200 Subject: [PATCH 160/337] Release Spot 2.11 * NEWS, configure.ac, doc/org/setup.org: Update version. --- NEWS | 47 +++++++++++++++++++++++++---------------------- configure.ac | 2 +- doc/org/setup.org | 10 +++++----- 3 files changed, 31 insertions(+), 28 deletions(-) diff --git a/NEWS b/NEWS index 3a2b8316b..c01a17e7e 100644 --- a/NEWS +++ b/NEWS @@ -1,26 +1,26 @@ -New in spot 2.10.6.dev (not yet released) +New in spot 2.11 (2022-10-08) Build: - - configure will now diagnose situation where Python bindings will + - configure will now diagnose situations where Python bindings will be installed in a directory that is not part of Python's search path. A new configure option --with-pythondir can be used to modify this installation path. - - A new configure option --enable-pthread enable the compilation of - Spot with -pthread, and activate the parallel version of some - algorithms. If Spot is compiled with -pthread enabled, any user - linking with Spot should also link with the pthread library. In - order to not break existing build setups using Spot, this option - is currently disabled by default in this release. We plan to turn - it on by default in some future release. Third-party project - using Spot may want to start linking with -pthread in prevision - for this change. + - A new configure option --enable-pthread enables the compilation of + Spot with -pthread, and render available the parallel version of + some algorithms. If Spot is compiled with -pthread enabled, any + user linking with Spot should also link with the pthread library. + In order to not break existing build setups using Spot, this + option is currently disabled by default in this release. We plan + to turn it on by default in some future release. Third-party + project using Spot may want to start linking with -pthread in + prevision for this change. Command-line tools: - autfilt has a new options --aliases=drop|keep to specify - if the output code should attempt to preserve aliases + if the HOA printer should attempt to preserve aliases present in the HOA input. This defaults to "keep". - autfilt has a new --to-finite option, illustrated on @@ -79,11 +79,11 @@ New in spot 2.10.6.dev (not yet released) - tgba_determinize() learned to fill the "original-classes" property. States of the determinized automaton that correspond to the same subset of states of the original automaton belong to the same - class. Filling this property is only done on demand has it inccurs - on small overhead. + class. Filling this property is only done on demand as it inccurs + a small overhead. - sbacc() learned to take the "original-classes" property into - account and preserve it. + account and to preserve it. - The HOA parser and printer learned to map the synthesis-outputs property of Spot to the controllable-AP header for the Extended @@ -148,10 +148,10 @@ New in spot 2.10.6.dev (not yet released) is a co-Büchi automaton. And product_or() learned that the "or"-product of two Büchi automata is a Büchi automaton. - - spot::postprocessor has a new extra option merge-states-min that - indicate above how many states twa_graph::merge_states(), which + - spot::postprocessor has a new extra option "merge-states-min" that + indicates above how many states twa_graph::merge_states() (which perform a very cheap pass to fuse states with identicall - succesors, should be called before running simulation-based + succesors) should be called before running simulation-based reductions. - A new function delay_branching_here(aut) can be used to simplify @@ -164,15 +164,18 @@ New in spot 2.10.6.dev (not yet released) further simplification. This was introduced to help with automata produced from formulas output by "genltl --eil-gsi" (see above). - - spot::postprocessor has new configuration variable branch-post that - can be used to control the use of branching-postponement (diabled - by default) or delayed-branching (see above, enabled by default). - See the spot-x(7) man page for details. + - spot::postprocessor has new configuration variable branch-post + that can be used to control the use of branching-postponement + (disabled by default) or delayed-branching (see above, enabled by + default). See the spot-x(7) man page for details. - spot::postprocessor is now using acd_transform() by default when building parity automata. Setting option "acd=0" will revert to using "to_parity()" instead. + - to_parity() has been almost entirely rewritten and is a bit + faster. + - When asked to build parity automata, spot::translator is now more aggressively using LTL decomposition, as done in the Generic acceptance case before paritizing the result. This results in diff --git a/configure.ac b/configure.ac index e2e16d63a..65ee20594 100644 --- a/configure.ac +++ b/configure.ac @@ -21,7 +21,7 @@ # along with this program. If not, see . AC_PREREQ([2.69]) -AC_INIT([spot], [2.10.6.dev], [spot@lrde.epita.fr]) +AC_INIT([spot], [2.11], [spot@lrde.epita.fr]) AC_CONFIG_AUX_DIR([tools]) AC_CONFIG_MACRO_DIR([m4]) AM_INIT_AUTOMAKE([1.11 gnu tar-ustar color-tests parallel-tests]) diff --git a/doc/org/setup.org b/doc/org/setup.org index d68521b7b..1df1e07e2 100644 --- a/doc/org/setup.org +++ b/doc/org/setup.org @@ -1,11 +1,11 @@ #+OPTIONS: H:2 num:nil toc:t html-postamble:nil ^:nil #+EMAIL: spot@lrde.epita.fr #+HTML_LINK_HOME: index.html -#+MACRO: SPOTVERSION 2.10.6 -#+MACRO: LASTRELEASE 2.10.6 -#+MACRO: LASTTARBALL [[http://www.lrde.epita.fr/dload/spot/spot-2.10.6.tar.gz][=spot-2.10.6.tar.gz=]] -#+MACRO: LASTNEWS [[https://gitlab.lre.epita.fr/spot/spot/blob/spot-2-10-6/NEWS][summary of the changes]] -#+MACRO: LASTDATE 2022-05-18 +#+MACRO: SPOTVERSION 2.11 +#+MACRO: LASTRELEASE 2.11 +#+MACRO: LASTTARBALL [[http://www.lrde.epita.fr/dload/spot/spot-2.11.tar.gz][=spot-2.11.tar.gz=]] +#+MACRO: LASTNEWS [[https://gitlab.lre.epita.fr/spot/spot/blob/spot-2-11/NEWS][summary of the changes]] +#+MACRO: LASTDATE 2022-10-08 #+ATTR_HTML: :id spotlogo [[file:spot2.svg]] From db79d5a79e41cdbe81544988e740649269a7781f Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Sat, 8 Oct 2022 21:05:04 +0200 Subject: [PATCH 161/337] * NEWS, configure.ac: Bump version to 2.11.0.dev. --- NEWS | 4 ++++ configure.ac | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index c01a17e7e..19c1408bb 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,7 @@ +New in spot 2.11.0.dev (not yet released) + + Nothing yet. + New in spot 2.11 (2022-10-08) Build: diff --git a/configure.ac b/configure.ac index 65ee20594..3d07d9d0e 100644 --- a/configure.ac +++ b/configure.ac @@ -21,7 +21,7 @@ # along with this program. If not, see . AC_PREREQ([2.69]) -AC_INIT([spot], [2.11], [spot@lrde.epita.fr]) +AC_INIT([spot], [2.11.0.dev], [spot@lrde.epita.fr]) AC_CONFIG_AUX_DIR([tools]) AC_CONFIG_MACRO_DIR([m4]) AM_INIT_AUTOMAKE([1.11 gnu tar-ustar color-tests parallel-tests]) From 2c13b299b8c4627a528507c0dc7976a5d71285c9 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Mon, 10 Oct 2022 10:00:38 +0200 Subject: [PATCH 162/337] hoa: add missing include Fixes #515, reported by Yuri Victorovich. * spot/twaalgos/hoa.hh: Include . --- spot/twaalgos/hoa.hh | 1 + 1 file changed, 1 insertion(+) diff --git a/spot/twaalgos/hoa.hh b/spot/twaalgos/hoa.hh index 8c2da4e43..74e97b567 100644 --- a/spot/twaalgos/hoa.hh +++ b/spot/twaalgos/hoa.hh @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include From d0c296e1cf599c00814d95cddbe14972d4c9a4ac Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Mon, 10 Oct 2022 10:06:25 +0200 Subject: [PATCH 163/337] org: mention "make check" and the new GPG key Fixes #515. * doc/org/install.org: Here. --- doc/org/install.org | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/doc/org/install.org b/doc/org/install.org index a24134e42..dc492af57 100644 --- a/doc/org/install.org +++ b/doc/org/install.org @@ -52,10 +52,13 @@ make make install #+END_SRC +Before running =make install=, you might want to run =make check= to +run our test-suite. + Files =INSTALL= and =README= included in the tarball contains more -explanations about the various options you can use during this -process. Also note that =README= has a section about troubleshooting -installations. +explanations about the various options you can use during the +compilation process. Also note that =README= has a section about +troubleshooting installations. * Installing the Debian packages :PROPERTIES: @@ -88,7 +91,9 @@ apt-get install spot libspot-dev spot-doc python3-spot # Or a subset of those Note that our Debian repository is signed since that is the new Debian policy, and both of the above command blocks start with a download of our [[https://www.lrde.epita.fr/repo/debian.gpg][GPG key]]. Its fingerprint is =209B 7362 CFD6 FECF B41D 717F 03D9 -9E74 44F2 A84A=, if you want to verify it. +9E74 44F2 A84A=, if you want to verify it. If you have an old copy of +the GPG key that expired, please download it again: the current +version should be valid until 2032. The package =spot= contains the [[file:tools.org][command-line tools]]. =libspot-dev= contains the header files if you plan to use Spot in a C++17 From 55e4d340fe622755a767ea1d4ebb1f3d8779bdd4 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Mon, 10 Oct 2022 10:42:40 +0200 Subject: [PATCH 164/337] CI: fix upload of stable Debian packages for amd64 This prevented the Spot website to regenerate. Should fix #516 once we release 2.11.1. * .gitlab-ci.yml (publish-stable): Upload changes for amd64 and i386, not just the later. --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index b7cd7fd1d..dd6e49c16 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -426,7 +426,7 @@ publish-stable: script: - cd _build_stable - ls -l - - dput lrde `ls -t *.changes | head -1` + - dput lrde `ls -t *amd64.changes | head -1` `ls -t *i386.changes | head -1` - cd .. - ls -l - tgz=`ls spot-*.tar.* | head -n 1` From c2bbb3fd00d39c5e4b145220f06ed912f52bacd0 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Mon, 10 Oct 2022 14:13:42 +0200 Subject: [PATCH 165/337] Release Spot 2.11.1 * NEWS, configure.ac, doc/org/setup.org: Update. --- NEWS | 7 +++++-- configure.ac | 2 +- doc/org/setup.org | 10 +++++----- 3 files changed, 11 insertions(+), 8 deletions(-) diff --git a/NEWS b/NEWS index 19c1408bb..ea9fc533d 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,9 @@ -New in spot 2.11.0.dev (not yet released) +New in spot 2.11.1 (2022-10-10) - Nothing yet. + Bugs fixed: + + - Fix a build issue preventing the update of website (issue #516). + - Fix a compilation error with clang-14 on FreeBSD (issue #515). New in spot 2.11 (2022-10-08) diff --git a/configure.ac b/configure.ac index 3d07d9d0e..382bba644 100644 --- a/configure.ac +++ b/configure.ac @@ -21,7 +21,7 @@ # along with this program. If not, see . AC_PREREQ([2.69]) -AC_INIT([spot], [2.11.0.dev], [spot@lrde.epita.fr]) +AC_INIT([spot], [2.11.1], [spot@lrde.epita.fr]) AC_CONFIG_AUX_DIR([tools]) AC_CONFIG_MACRO_DIR([m4]) AM_INIT_AUTOMAKE([1.11 gnu tar-ustar color-tests parallel-tests]) diff --git a/doc/org/setup.org b/doc/org/setup.org index 1df1e07e2..0be5b364b 100644 --- a/doc/org/setup.org +++ b/doc/org/setup.org @@ -1,11 +1,11 @@ #+OPTIONS: H:2 num:nil toc:t html-postamble:nil ^:nil #+EMAIL: spot@lrde.epita.fr #+HTML_LINK_HOME: index.html -#+MACRO: SPOTVERSION 2.11 -#+MACRO: LASTRELEASE 2.11 -#+MACRO: LASTTARBALL [[http://www.lrde.epita.fr/dload/spot/spot-2.11.tar.gz][=spot-2.11.tar.gz=]] -#+MACRO: LASTNEWS [[https://gitlab.lre.epita.fr/spot/spot/blob/spot-2-11/NEWS][summary of the changes]] -#+MACRO: LASTDATE 2022-10-08 +#+MACRO: SPOTVERSION 2.11.1 +#+MACRO: LASTRELEASE 2.11.1 +#+MACRO: LASTTARBALL [[http://www.lrde.epita.fr/dload/spot/spot-2.11.1.tar.gz][=spot-2.11.1.tar.gz=]] +#+MACRO: LASTNEWS [[https://gitlab.lre.epita.fr/spot/spot/blob/spot-2-11-1/NEWS][summary of the changes]] +#+MACRO: LASTDATE 2022-10-10 #+ATTR_HTML: :id spotlogo [[file:spot2.svg]] From 548f3d766342ad9a62655d77aa80bd7fc3a59c79 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Mon, 10 Oct 2022 14:15:23 +0200 Subject: [PATCH 166/337] * NEWS, configure.ac: Bump version to 2.11.1.dev. --- NEWS | 4 ++++ configure.ac | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index ea9fc533d..9aee79188 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,7 @@ +New in spot 2.11.1.dev (not yet released) + + Nothing yet. + New in spot 2.11.1 (2022-10-10) Bugs fixed: diff --git a/configure.ac b/configure.ac index 382bba644..5c160e6cd 100644 --- a/configure.ac +++ b/configure.ac @@ -21,7 +21,7 @@ # along with this program. If not, see . AC_PREREQ([2.69]) -AC_INIT([spot], [2.11.1], [spot@lrde.epita.fr]) +AC_INIT([spot], [2.11.1.dev], [spot@lrde.epita.fr]) AC_CONFIG_AUX_DIR([tools]) AC_CONFIG_MACRO_DIR([m4]) AM_INIT_AUTOMAKE([1.11 gnu tar-ustar color-tests parallel-tests]) From 583ca38d91c92fc5201ddaf3b26d46cc41f42736 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Tue, 11 Oct 2022 10:43:27 +0200 Subject: [PATCH 167/337] replace bdd_relprod by bdd_restrict * spot/twaalgos/alternation.cc, spot/twaalgos/dualize.cc, spot/twaalgos/simulation.cc, spot/twaalgos/toweak.cc: Here. --- spot/twaalgos/alternation.cc | 4 ++-- spot/twaalgos/dualize.cc | 2 +- spot/twaalgos/simulation.cc | 4 ++-- spot/twaalgos/toweak.cc | 2 +- 4 files changed, 6 insertions(+), 6 deletions(-) diff --git a/spot/twaalgos/alternation.cc b/spot/twaalgos/alternation.cc index 8370f395b..bdbe07982 100644 --- a/spot/twaalgos/alternation.cc +++ b/spot/twaalgos/alternation.cc @@ -457,7 +457,7 @@ namespace spot // First loop over all possible valuations atomic properties. for (bdd oneletter: minterms_of(all_letters, ap)) { - minato_isop isop(bdd_relprod(bs, oneletter, ap)); + minato_isop isop(bdd_restrict(bs, oneletter)); bdd dest; while ((dest = isop.next()) != bddfalse) { @@ -636,7 +636,7 @@ namespace spot cond_ = oneletter; all_letters_ -= oneletter; // Get a sum of possible transitions matching this letter. - isop_ = minato_isop(bdd_relprod(transitions_, oneletter, ap_)); + isop_ = minato_isop(bdd_restrict(transitions_, oneletter)); dest_ = isop_.next(); } std::set dest = bdd_to_state(dest_); diff --git a/spot/twaalgos/dualize.cc b/spot/twaalgos/dualize.cc index 91498ce8d..1b60a0d17 100644 --- a/spot/twaalgos/dualize.cc +++ b/spot/twaalgos/dualize.cc @@ -155,7 +155,7 @@ namespace spot for (bdd oneletter: minterms_of(letters, ap)) { - minato_isop isop(bdd_relprod(delta, oneletter, ap)); + minato_isop isop(bdd_restrict(delta, oneletter)); bdd dest; while ((dest = isop.next()) != bddfalse) diff --git a/spot/twaalgos/simulation.cc b/spot/twaalgos/simulation.cc index 58ebfd79d..ca8928888 100644 --- a/spot/twaalgos/simulation.cc +++ b/spot/twaalgos/simulation.cc @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2012-2021 Laboratoire de Recherche et Développement +// Copyright (C) 2012-2022 Laboratoire de Recherche et Développement // de l'Epita (LRDE). // // This file is part of Spot, a model checking library. @@ -590,7 +590,7 @@ namespace spot // C1 then (!C1)C2, instead of C1 then C2. // With minatop_isop, we ensure that the no negative // class variable will be seen (likewise for promises). - minato_isop isop(bdd_relprod(sig, one, sup_all_atomic_prop)); + minato_isop isop(bdd_restrict(sig, one)); ++nb_minterms; diff --git a/spot/twaalgos/toweak.cc b/spot/twaalgos/toweak.cc index 543c7c9a1..ae7a0f58a 100644 --- a/spot/twaalgos/toweak.cc +++ b/spot/twaalgos/toweak.cc @@ -179,7 +179,7 @@ namespace spot for (bdd oneletter: minterms_of(letters, ap)) { - minato_isop isop(bdd_relprod(delta, oneletter, ap)); + minato_isop isop(bdd_restrict(delta, oneletter)); bdd dest; while ((dest = isop.next()) != bddfalse) From 9de545555235fa07536ac9a85c60d8a46e772202 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Tue, 11 Oct 2022 13:28:15 +0200 Subject: [PATCH 168/337] fix some typos * spot/graph/graph.hh, spot/ltsmin/spins_kripke.hxx, spot/mc/bloemen.hh, spot/mc/lpar13.hh, spot/twaalgos/determinize.cc: Here. --- spot/graph/graph.hh | 8 ++++---- spot/ltsmin/spins_kripke.hxx | 12 ++++++------ spot/mc/bloemen.hh | 4 ++-- spot/mc/lpar13.hh | 16 ++++++++-------- spot/twaalgos/determinize.cc | 10 +++++----- 5 files changed, 25 insertions(+), 25 deletions(-) diff --git a/spot/graph/graph.hh b/spot/graph/graph.hh index 531426244..04c21fec9 100644 --- a/spot/graph/graph.hh +++ b/spot/graph/graph.hh @@ -1295,10 +1295,10 @@ namespace spot static std::vector tv; SPOT_ASSERT(tv.empty()); tv.resize(nthreads); - // FIXME: Due to the way these thread advence into the sate - // vectors, they access very close memory location. It - // would seems more cache friendly to have thread work on - // blocks of continuous states. + // FIXME: Due to the way these thread advance into the state + // vector, they access very close memory location. It would + // seems more cache friendly to have threads work on blocks + // of continuous states. for (unsigned id = 0; id < nthreads; ++id) tv[id] = std::thread( [bne, id, ns, &idx_list, p, nthreads]() diff --git a/spot/ltsmin/spins_kripke.hxx b/spot/ltsmin/spins_kripke.hxx index bafb6f641..bdf47fbb6 100644 --- a/spot/ltsmin/spins_kripke.hxx +++ b/spot/ltsmin/spins_kripke.hxx @@ -1,6 +1,6 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2017, 2018, 2020 Laboratoire de Recherche et Développement de -// l'Epita (LRDE) +// Copyright (C) 2017, 2018, 2020, 2022 Laboratoire de Recherche et +// Développement de l'Epita (LRDE) // // This file is part of Spot, a model checking library. // @@ -400,10 +400,10 @@ namespace spot } } - // FIXME I think we only need visbles aps, i.e. if the system has - // following variables, i.e. P_0.var1 and P_0.var2 but the property - // automaton only mention P_0.var2, we do not need to capture (in - // the resulting cube) any atomic proposition for P_0.var1 + // FIXME: I think we only need visible aps. E.g., if the system has + // variables P_0.var1 and P_0.var2 but the property automaton only + // mentions P_0.var2, we do not need to capture (in the resulting + // cube) any atomic proposition for P_0.var1 void kripkecube::match_aps(std::vector& aps, diff --git a/spot/mc/bloemen.hh b/spot/mc/bloemen.hh index 1a37a71be..432badb76 100644 --- a/spot/mc/bloemen.hh +++ b/spot/mc/bloemen.hh @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2015, 2016, 2017, 2018, 2019, 2020 Laboratoire de Recherche et +// Copyright (C) 2015-2020, 2022 Laboratoire de Recherche et // Developpement de l'Epita // // This file is part of Spot, a model checking library. @@ -127,7 +127,7 @@ namespace spot bool b = it.isnew(); // Insertion failed, delete element - // FIXME Should we add a local cache to avoid useless allocations? + // FIXME: Should we add a local cache to avoid useless allocations? if (!b) p_.deallocate(v); else diff --git a/spot/mc/lpar13.hh b/spot/mc/lpar13.hh index 28b71aa4b..77396fb9d 100644 --- a/spot/mc/lpar13.hh +++ b/spot/mc/lpar13.hh @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2015-2016, 2018-2021 Laboratoire de Recherche et +// Copyright (C) 2015-2016, 2018-, 20222022 Laboratoire de Recherche et // Developpement de l'Epita // // This file is part of Spot, a model checking library. @@ -32,9 +32,9 @@ namespace spot { /// \brief This class implements the sequential emptiness check as /// presented in "Three SCC-based Emptiness Checks for Generalized - /// B\¨uchi Automata" (Renault et al, LPAR 2013). Among the three - /// emptiness check that has been proposed we opted to implement - /// the Gabow's one. + /// Büchi Automata" (Renault et al, LPAR 2013). Among the three + /// emptiness checks that have been proposed, we opted to implement + /// yGabow's one. template class SPOT_API lpar13 @@ -62,8 +62,8 @@ namespace spot size_t operator()(const product_state that) const noexcept { - // FIXME! wang32_hash(that.st_prop) could have - // been pre-calculated! + // FIXME: wang32_hash(that.st_prop) could have been + // pre-calculated! StateHash hasher; return wang32_hash(that.st_prop) ^ hasher(that.st_kripke); } @@ -135,7 +135,7 @@ namespace spot map[newtop]))) { sys_.recycle(todo.back().it_kripke, tid_); - // FIXME a local storage for twacube iterator? + // FIXME: a local storage for twacube iterator? todo.pop_back(); if (SPOT_UNLIKELY(found_)) { @@ -346,7 +346,7 @@ namespace spot ctrx_element* current = front; while (current != nullptr) { - // FIXME also display acc? + // FIXME: also display acc? res = res + " " + std::to_string(current->prod_st->st_prop) + + "*" + diff --git a/spot/twaalgos/determinize.cc b/spot/twaalgos/determinize.cc index 82305f564..ba4fb3ded 100644 --- a/spot/twaalgos/determinize.cc +++ b/spot/twaalgos/determinize.cc @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2015-2021 Laboratoire de Recherche et +// Copyright (C) 2015-2022 Laboratoire de Recherche et // Développement de l'Epita. // // This file is part of Spot, a model checking library. @@ -476,7 +476,7 @@ namespace spot std::vector tmp; while (brace >= 0) { - // FIXME is not there a smarter way? + // FIXME: is there a smarter way? tmp.insert(tmp.begin(), brace); brace = s.braces_[brace]; } @@ -781,7 +781,7 @@ namespace spot bool safra_state::operator<(const safra_state& other) const { - // FIXME what is the right, if any, comparison to perform? + // FIXME: what is the right, if any, comparison to perform? return braces_ == other.braces_ ? nodes_ < other.nodes_ : braces_ < other.braces_; } @@ -887,7 +887,7 @@ namespace spot // NB spot::simulation() does not remove unreachable states, as it // would invalidate the contents of 'implications'. // so we need to explicitly test for unreachable states - // FIXME based on the scc_info, we could remove the unreachable + // FIXME: based on the scc_info, we could remove the unreachable // states, both in the input automaton and in 'implications' // to reduce the size of 'implies'. if (!scc.reachable_state(i)) @@ -922,7 +922,7 @@ namespace spot std::vector support(aut->num_states()); if (use_stutter && aut->prop_stutter_invariant()) { - // FIXME this could be improved + // FIXME: this could be improved // supports of states should account for possible stuttering if we plan // to use stuttering invariance for (unsigned c = 0; c != scc.scc_count(); ++c) From dae46567e7a78230b5616061b937a75339c73112 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Tue, 11 Oct 2022 14:54:24 +0200 Subject: [PATCH 169/337] org: work around newer org-mode not displaying SVG as * doc/org/init.el.in (spot-svg-output-as-object): New function. --- doc/org/init.el.in | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) diff --git a/doc/org/init.el.in b/doc/org/init.el.in index e16d097cc..543e39423 100644 --- a/doc/org/init.el.in +++ b/doc/org/init.el.in @@ -184,6 +184,49 @@ up.html points to index.html, then the result is: :publishing-function org-publish-attachment) ("spot-all" :components ("spot-html" "spot-static")))) + + + +;;; Org-mode 9.4.6 is now using to render SVG images. +;;; Unfortunately, this breaks SVG images that use external style +;;; sheets as are expected to be self-contained. +;;; +;;; Since we do use such external style-sheets and never had +;;; any issue with , we revert +;;; to the previous behavior. +;;; +;;; The following function is based on org-html--svg-image from +;;; Org-mode 9.4.5, with the addition of the SVG extension test. +(defun spot-svg-output-as-object (source attributes info) + "If source is an SVG file, return an \"object\" embedding svg file +SOURCE with given ATTRIBUTES. +INFO is a plist used as a communication channel. Otherwise return nil. + +The special attribute \"fallback\" can be used to specify a +fallback image file to use if the object embedding is not +supported. CSS class \"org-svg\" is assigned as the class of the +object unless a different class is specified with an attribute." + (when (string= "svg" (file-name-extension source)) + (let ((fallback (plist-get attributes :fallback)) + (attrs (org-html--make-attribute-string + (org-combine-plists + ;; Remove fallback attribute, which is not meant to + ;; appear directly in the attributes string, and + ;; provide a default class if none is set. + '(:class "org-svg") attributes '(:fallback nil))))) + (format "\n%s" + source + attrs + (if fallback + (org-html-close-tag + "img" (format "src=\"%s\" %s" fallback attrs) info) + "Sorry, your browser does not support SVG."))))) +;;; Hack org-html--format-image to call the above first. +;;; (The org-html--svg-image function was removed when the formater code +;;; switched to for SVG.) +(unless (fboundp 'org-html--svg-image) + (advice-add 'org-html--format-image :before-until 'spot-svg-output-as-object)) + (org-publish-all t) ;;; org-babel-remove-temporary-directory does not correctly remove ;;; nested directories and we have some files in tmp/.libs/ because of From bfb8f0a0784cb981c000629a7a566a90162b6dea Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Tue, 11 Oct 2022 15:06:54 +0200 Subject: [PATCH 170/337] * .gitlab-ci.yml: Fail if coverage goes below 90.7%. --- .gitlab-ci.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index dd6e49c16..3bad252ae 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -68,7 +68,7 @@ debian-unstable-gcc-coverage: - ./configure CXX='g++ --coverage' --enable-devel --disable-static --enable-doxygen - make - make check - - gcovr --xml-pretty --exclude-unreachable-branches --print-summary -o coverage.xml --root $PWD -e 'bin/spot.cc' -e 'bin/spot-x.cc' -e 'spot/bricks/.*' -e 'spot/parse.*/scan.*.cc' -e 'spot/parse.*/parse.*.cc' -e 'utf8/.*' -e 'python/.*' -e 'buddy/.*' -e 'doc/org/tmp/.*' --html-details coverage.html --html-tab-size 8 + - gcovr --xml-pretty --exclude-unreachable-branches --print-summary -o coverage.xml --root $PWD -e 'bin/spot.cc' -e 'bin/spot-x.cc' -e 'spot/bricks/.*' -e 'spot/parse.*/scan.*.cc' -e 'spot/parse.*/parse.*.cc' -e 'utf8/.*' -e 'python/.*' -e 'buddy/.*' -e 'doc/org/tmp/.*' --html-details coverage.html --html-tab-size 8 --fail-under-line 90.7 coverage: /^\s*lines:\s*\d+.\d+\%/ artifacts: when: always From da356f114297c6f1f5c40fe112ff0f8cd27ab75e Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Tue, 11 Oct 2022 15:34:09 +0200 Subject: [PATCH 171/337] substitute @LIBSPOT_PTHREAD@ in spot/libspot.pc Fixes #520, reported by Fangyi Zhou. * spot/Makefile.am (libspot.pc): Substitute @LIBSPOT_PTHREAD@. * THANKS: Add Fangyi Zhou. --- THANKS | 1 + spot/Makefile.am | 1 + 2 files changed, 2 insertions(+) diff --git a/THANKS b/THANKS index c53a0aafb..db74e14b8 100644 --- a/THANKS +++ b/THANKS @@ -15,6 +15,7 @@ Edmond Irani Liu Ernesto Posse Étienne Renault Fabrice Kordon +Fangyi Zhou Felix Klaedtke Florian Perlié-Long František Blahoudek diff --git a/spot/Makefile.am b/spot/Makefile.am index 72cbef22d..806b299ad 100644 --- a/spot/Makefile.am +++ b/spot/Makefile.am @@ -68,6 +68,7 @@ libspot.pc: $(srcdir)/libspot.pc.in Makefile -e 's![@]includedir[@]!$(includedir)!g' \ -e 's![@]libdir[@]!$(libdir)!g' \ -e 's![@]PACKAGE_VERSION[@]!$(PACKAGE_VERSION)!g' \ + -e 's![@]LIBSPOT_PTHREAD[@]!$(LIBSPOT_PTHREAD)!g' \ $(srcdir)/libspot.pc.in > $@.tmp && mv $@.tmp $@ CLEANFILES = libspot.pc From 7f6e3c2bf8e537a496febafcaad14e614918c9e7 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Thu, 13 Oct 2022 11:21:50 +0200 Subject: [PATCH 172/337] * NEWS: Add news entry for previous fix. --- NEWS | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index 9aee79188..805325fe6 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,8 @@ New in spot 2.11.1.dev (not yet released) - Nothing yet. + Bugs fixed: + + - Fix pkg-config files containing @LIBSPOT_PTHREAD@ (issue #520) New in spot 2.11.1 (2022-10-10) From 666d78d4999d1e2130cd5cec52649b2b09afd277 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Thu, 13 Oct 2022 11:22:14 +0200 Subject: [PATCH 173/337] * doc/org/init.el.in: Typo in comment. --- doc/org/init.el.in | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/org/init.el.in b/doc/org/init.el.in index 543e39423..9f589bb35 100644 --- a/doc/org/init.el.in +++ b/doc/org/init.el.in @@ -187,7 +187,7 @@ up.html points to index.html, then the result is: -;;; Org-mode 9.4.6 is now using to render SVG images. +;;; Org-mode 9.5 is now using to render SVG images. ;;; Unfortunately, this breaks SVG images that use external style ;;; sheets as are expected to be self-contained. ;;; From 179672fe3b920134d71287cec9cabb1b2ea0a30d Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Thu, 13 Oct 2022 11:34:38 +0200 Subject: [PATCH 174/337] relabel: fix handling of concat and fusion * spot/tl/relabel.cc (formula_to_fgraph): Do not assume that n-ary operators are Boolean operators. * tests/python/relabel.py: Add a test case found while discussing some expression with Antoine Martin. * NEWS: Mention it. --- NEWS | 3 +++ spot/tl/relabel.cc | 14 +++++++++++--- tests/python/relabel.py | 4 ++++ 3 files changed, 18 insertions(+), 3 deletions(-) diff --git a/NEWS b/NEWS index 805325fe6..3ad57d31d 100644 --- a/NEWS +++ b/NEWS @@ -3,6 +3,9 @@ New in spot 2.11.1.dev (not yet released) Bugs fixed: - Fix pkg-config files containing @LIBSPOT_PTHREAD@ (issue #520) + - spot::relabel_bse() was incorrectly relabeling some dependent + Boolean subexpressions in SERE. (Note that this had no + consequence on automata translated from those SERE.) New in spot 2.11.1 (2022-10-10) diff --git a/spot/tl/relabel.cc b/spot/tl/relabel.cc index 44d6577cb..26c7564c1 100644 --- a/spot/tl/relabel.cc +++ b/spot/tl/relabel.cc @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2012-2016, 2018-2020 Laboratoire de Recherche et +// Copyright (C) 2012-2016, 2018-2020, 2022 Laboratoire de Recherche et // Développement de l'Epita (LRDE). // // This file is part of Spot, a model checking library. @@ -363,7 +363,7 @@ namespace spot goto done; } } - if (sz > 2 && !f.is_boolean()) + if (sz > 2 && !f.is_boolean() && f.is(op::And, op::Or)) { /// If we have a formula like (a & b & Xc), consider /// it as ((a & b) & Xc) in the graph to isolate the @@ -384,7 +384,7 @@ namespace spot for (i = 1; i < sz; ++i) { formula next = f[i]; - // Note that we only add an edge in both directions, + // Note that we add an edge in both directions, // as the cut point algorithm really need undirected // graphs. (We used to do only one direction, and // that turned out to be a bug.) @@ -581,6 +581,14 @@ namespace spot conv.visit(f); } + //// Uncomment to print the graph. + // for (auto& [f, sv]: g) + // { + // std::cerr << f << ":\n"; + // for (auto& s: sv) + // std::cerr << " " << s << '\n'; + // } + // Compute its cut-points fset c; cut_points(g, c, f); diff --git a/tests/python/relabel.py b/tests/python/relabel.py index 0de668b12..b32ebd752 100644 --- a/tests/python/relabel.py +++ b/tests/python/relabel.py @@ -55,3 +55,7 @@ try: spot.relabel_here(autg, m) except RuntimeError as e: tc.assertIn("old labels", str(e)) + +f = spot.parse_infix_sere("(p9;p21|p22):(p1&p2;p11&p22;p1&p2)").f +g = spot.relabel_bse(f, spot.Abc) +tc.assertEqual(str(g), "(a;(b | c)):(d;(c & e);d)") From eb2616efaa9a68f27393b0c5904cc11a3e48e039 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Fri, 14 Oct 2022 09:44:17 +0200 Subject: [PATCH 175/337] * .gitlab-ci.yml (debian-unstable-gcc-coverage): Disable devel mode. --- .gitlab-ci.yml | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index 3bad252ae..a2006ee7f 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -56,6 +56,9 @@ make-dist: - ./*.tar.gz - VERSION +# We --disable-devel for coverage, because debug mode replaces +# SPOT_UNREACHABLE by an assertion wich is never reachable, lowering +# our coverage. debian-unstable-gcc-coverage: stage: build only: @@ -65,7 +68,7 @@ debian-unstable-gcc-coverage: image: gitlab-registry.lre.epita.fr/spot/buildenv/debian script: - autoreconf -vfi - - ./configure CXX='g++ --coverage' --enable-devel --disable-static --enable-doxygen + - ./configure CXX='g++ --coverage' --disable-devel --enable-warnings --disable-static --enable-doxygen - make - make check - gcovr --xml-pretty --exclude-unreachable-branches --print-summary -o coverage.xml --root $PWD -e 'bin/spot.cc' -e 'bin/spot-x.cc' -e 'spot/bricks/.*' -e 'spot/parse.*/scan.*.cc' -e 'spot/parse.*/parse.*.cc' -e 'utf8/.*' -e 'python/.*' -e 'buddy/.*' -e 'doc/org/tmp/.*' --html-details coverage.html --html-tab-size 8 --fail-under-line 90.7 From b0c299b9e95114421cf2821f4ad9281cebf0984d Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Fri, 14 Oct 2022 16:41:26 +0200 Subject: [PATCH 176/337] reduce_parity: add layered option * spot/twaalgos/parity.cc: Implement it. * spot/twaalgos/parity.hh, NEWS: Document it. * tests/python/parity.ipynb: Demonstrate it. This is the only test so far, but more uses are coming. --- NEWS | 6 + spot/twaalgos/parity.cc | 39 ++-- spot/twaalgos/parity.hh | 47 ++++- tests/python/parity.ipynb | 430 +++++++++++++++++++++++++------------- 4 files changed, 357 insertions(+), 165 deletions(-) diff --git a/NEWS b/NEWS index 3ad57d31d..d8fd0ab70 100644 --- a/NEWS +++ b/NEWS @@ -1,5 +1,11 @@ New in spot 2.11.1.dev (not yet released) + Library: + + - spot::reduce_parity() now has a "layered" option to force all + transition in the same parity layer to receive the same color; + like acd_transform() would do. + Bugs fixed: - Fix pkg-config files containing @LIBSPOT_PTHREAD@ (issue #520) diff --git a/spot/twaalgos/parity.cc b/spot/twaalgos/parity.cc index 94c7bd922..9428d7bb5 100644 --- a/spot/twaalgos/parity.cc +++ b/spot/twaalgos/parity.cc @@ -388,14 +388,14 @@ namespace spot } twa_graph_ptr - reduce_parity(const const_twa_graph_ptr& aut, bool colored) + reduce_parity(const const_twa_graph_ptr& aut, bool colored, bool layered) { return reduce_parity_here(make_twa_graph(aut, twa::prop_set::all()), - colored); + colored, layered); } twa_graph_ptr - reduce_parity_here(twa_graph_ptr aut, bool colored) + reduce_parity_here(twa_graph_ptr aut, bool colored, bool layered) { unsigned num_sets = aut->num_sets(); if (!colored && num_sets == 0) @@ -507,15 +507,30 @@ namespace spot m.first += (piri - m.first) & 1; m.second += (piri - m.second) & 1; } - for (unsigned state: si.states_of(scc)) - for (auto& e: aut->out(state)) - if ((sba || si.scc_of(e.dst) == scc) && - ((piri >= 0 && e.acc.has(color)) || (piri < 0 && !e.acc))) - { - unsigned en = aut->edge_number(e); - piprime1[en] = m.first; - piprime2[en] = m.second; - } + // Recolor edges. Depending on LAYERED we want to + // either recolor all edges for which piprime1 is -2 + // (uncolored), or only the edges that we were removed + // by the previous filter. + auto coloredge = [&](auto& e) { + unsigned en = aut->edge_number(e); + bool recolor = layered + ? piprime1[en] == -2 + : (piri >= 0 && e.acc.has(color)) || (piri < 0 && !e.acc); + if (recolor) + { + piprime1[en] = m.first; + piprime2[en] = m.second; + } + }; + if (sba) + // si.edges_of(scc) would be wrong as it can ignore + // outgoing edges removed from a previous level. + for (unsigned s: si.states_of(scc)) + for (auto& e: aut->out(s)) + coloredge(e); + else + for (auto& e: si.inner_edges_of(scc)) + coloredge(e); res.first = std::max(res.first, m.first); res.second = std::max(res.second, m.second); } diff --git a/spot/twaalgos/parity.hh b/spot/twaalgos/parity.hh index 167c6b2d8..44d7cca7e 100644 --- a/spot/twaalgos/parity.hh +++ b/spot/twaalgos/parity.hh @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2016-2019 Laboratoire de Recherche et Développement +// Copyright (C) 2016-2019, 2022 Laboratoire de Recherche et Développement // de l'Epita (LRDE). // // This file is part of Spot, a model checking library. @@ -134,7 +134,6 @@ namespace spot colorize_parity_here(twa_graph_ptr aut, bool keep_style = false); /// @} - /// \brief Reduce the parity acceptance condition to use a minimal /// number of colors. /// @@ -149,11 +148,51 @@ namespace spot /// the above paper assumes). Otherwise, the smallest or highest /// colors (depending on the parity kind) is removed to simplify the /// acceptance condition. + /// + /// If the input uses state-based acceptance, the output will use + /// state-based acceptance as well. + /// + /// A parity automaton, sometimes called a chain automaton, can be + /// seen as a stack of layers that are alternatively rejecting and + /// accepting. For instance imagine a parity max automaton that is + /// strongly connected. Removing the transitions with the maximal + /// color might leave a few transitions that were not labeled by + /// this maximal color, but that are part of any cycle anymore: + /// those transition could have been colored with the maximal color, + /// since any cycle going through them would have seen the maximal + /// color. (Once your remove this maximal layer, + /// your can define the next layer similarly.) + /// + /// When \a layered is true all transition that belong to the same + /// layer receive the same color. When layer is `false`, only the + /// transition that where used initially to define the layers (i.e, + /// the transition with the maximal color in the previous exemple), + /// get their color adjusted. The other will receive either no + /// color (if \a colored is false), or a useless color (if \a colored + /// is true). Here "useless color" means the smallest color + /// for parity max, and the largest color for parity min. + /// + /// When \a layered is true, the output of this function is + /// comparable to what acd_transform() would produce. The + /// difference is that this function preserve the kind (min/max) of + /// parity input, while acd_transform() always output a parity min + /// automaton. Additionally, this function needs fewer resources + /// than acd_transform() because it is already known that the input + /// is a parity automaton. In some (historically inaccurate) way, + /// reduce_parity() can be seen as a specialized version of + /// acd_transform(). + /// + /// The reason layered is false by default, is that not introducing + /// colors in place where there where none occasionally help with + /// simulation-based reductions. + /// /// @{ SPOT_API twa_graph_ptr - reduce_parity(const const_twa_graph_ptr& aut, bool colored = false); + reduce_parity(const const_twa_graph_ptr& aut, + bool colored = false, bool layered = false); SPOT_API twa_graph_ptr - reduce_parity_here(twa_graph_ptr aut, bool colored = false); + reduce_parity_here(twa_graph_ptr aut, + bool colored = false, bool layered = false); /// @} } diff --git a/tests/python/parity.ipynb b/tests/python/parity.ipynb index 7323717da..56d6af350 100644 --- a/tests/python/parity.ipynb +++ b/tests/python/parity.ipynb @@ -72,9 +72,9 @@ "cell_type": "markdown", "metadata": {}, "source": [ - "Of course the case of parity automata with a single color is a bit degenerate, as the same formula correspond to two parity conditions with different kinds. \n", + "Of course the case of parity automata with a single color is a bit degenerate, as the same formula corresponds to two parity conditions of different kinds. \n", "\n", - "In addition the the above, an automaton is said to be **colored** if each of its edges (or states) has exactly one color. Automata that people usually call *parity automata* correspond in Spot to *colored* automata with *parity acceptance*. For this reason try to use the term *automata with parity acceptance* rather than *parity automata* for automata that are not *colored*." + "In addition to the above, an automaton is said to be **colored** if each of its edges (or states) has exactly one color. Automata that people usually call *parity automata* correspond in Spot to *colored* automata with *parity acceptance*. For this reason try to use the term *automata with parity acceptance* rather than *parity automata* for automata that are not *colored*." ] }, { @@ -3009,11 +3009,11 @@ "\n", "\n", - "\n", - "\n", - "\n", - "[co-Büchi]\n", + "\n", + "\n", + "\n", + "[co-Büchi]\n", "\n", "\n", "\n", @@ -3237,11 +3237,11 @@ "\n", "\n", - "\n", - "\n", - "\n", - "[Büchi]\n", + "\n", + "\n", + "\n", + "[Büchi]\n", "\n", "\n", "\n", @@ -4223,14 +4223,15 @@ "\n", "# Reduce parity\n", "\n", - "The `reduce_parity()` function is a more elaborate version of `cleanup_parity()`. It implements an algorithm by Carton and Maceiras (*Computing the Rabin index of a parity automaton*, Informatique théorique et applications, 1999), to obtain the minimal parity acceptance condition for a given automaton. Why the original algorithm assume *max odd* parity, this version with work with the four types of parity acceptance. It will only try to preserve the kind (max/min) and may change the style if it allows saving one color. Furthermore, it can colorize (or uncolorize) automata at the same time,\n", + "The `reduce_parity()` function is a more elaborate version of `cleanup_parity()`. It implements an algorithm by Carton and Maceiras (*Computing the Rabin index of a parity automaton*, Informatique théorique et applications, 1999), to obtain the minimal parity acceptance condition for a given automaton. While the original algorithm assumes *max odd* parity, this version works with the four types of parity acceptance. It will only try to preserve the kind (max/min) and may change the style if it allows saving one color. Furthermore, it can colorize (or uncolorize) automata at the same time,\n", "making it a very nice replacement for both `cleanup_parity()` and `colorize_parity()`.\n", "\n", - "It takes two arguments:\n", + "It takes three arguments:\n", "1. the automaton whose parity acceptance condition should be reduced\n", "2. a Boolean indicating whether the output should be colored (`True`), or if transition with no color can be used (`False`).\n", + "3. a Boolean indicating whether the output should be layered, i.e., in a max parity automaton, that means the color of a transition should be the maximal color visited by all cycles going through it.\n", "\n", - "By default, the second argument is `False`, because acceptance sets is a scarse ressource in Spot." + "By default, the second argument is `False`, because acceptance sets is a scarse ressource in Spot. The third argument also defaults to `False`, but for empircal reason: adding more colors like this tends to hinder simulation-based reductions." ] }, { @@ -4715,8 +4716,8 @@ "outputs": [ { "data": { - "text/html": [ - "
\n", + "image/svg+xml": [ + "\n", "\n", "\n", - "\n", - "\n", - "\n", - "\n", - "Fin(\n", - "\n", - ") & (Inf(\n", - "\n", - ") | (Fin(\n", - "\n", - ") & Inf(\n", - "\n", - ")))\n", - "[parity max even 4]\n", - "\n", - "\n", - "\n", - "0\n", - "\n", - "0\n", - "\n", - "\n", - "\n", - "I->0\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "2\n", - "\n", - "2\n", - "\n", - "\n", - "\n", - "0->2\n", - "\n", - "\n", - "!p0 & !p1\n", - "\n", - "\n", - "\n", - "3\n", - "\n", - "3\n", - "\n", - "\n", - "\n", - "0->3\n", - "\n", - "\n", - "!p0 & !p1\n", - "\n", - "\n", - "\n", - "1\n", - "\n", - "1\n", - "\n", - "\n", - "\n", - "2->1\n", - "\n", - "\n", - "!p0 & p1\n", - "\n", - "\n", - "\n", - "3->0\n", - "\n", - "\n", - "p0 & !p1\n", - "\n", - "\n", - "\n", - "\n", - "3->2\n", - "\n", - "\n", - "!p0 & !p1\n", - "\n", - "\n", - "\n", - "\n", - "3->3\n", - "\n", - "\n", - "!p0 & p1\n", - "\n", - "\n", - "\n", - "\n", - "1->0\n", - "\n", - "\n", - "!p0 & p1\n", - "\n", - "\n", - "\n", - "\n", - "\n", - "1->2\n", - "\n", - "\n", - "!p0 & !p1\n", - "\n", - "\n", - "\n", "
\n", "\n", @@ -5188,17 +5074,263 @@ }, "metadata": {}, "output_type": "display_data" + }, + { + "data": { + "text/html": [ + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "Fin(\n", + "\n", + ") & Inf(\n", + "\n", + ")\n", + "[parity max even 2]\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "2\n", + "\n", + "2\n", + "\n", + "\n", + "\n", + "0->2\n", + "\n", + "\n", + "!p0 & !p1\n", + "\n", + "\n", + "\n", + "\n", + "3\n", + "\n", + "3\n", + "\n", + "\n", + "\n", + "0->3\n", + "\n", + "\n", + "!p0 & !p1\n", + "\n", + "\n", + "\n", + "\n", + "1\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "2->1\n", + "\n", + "\n", + "!p0 & p1\n", + "\n", + "\n", + "\n", + "3->0\n", + "\n", + "\n", + "p0 & !p1\n", + "\n", + "\n", + "\n", + "\n", + "3->2\n", + "\n", + "\n", + "!p0 & !p1\n", + "\n", + "\n", + "\n", + "\n", + "3->3\n", + "\n", + "\n", + "!p0 & p1\n", + "\n", + "\n", + "\n", + "\n", + "1->0\n", + "\n", + "\n", + "!p0 & p1\n", + "\n", + "\n", + "\n", + "\n", + "1->2\n", + "\n", + "\n", + "!p0 & !p1\n", + "\n", + "\n", + "\n", + "
\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "Fin(\n", + "\n", + ") & (Inf(\n", + "\n", + ") | Fin(\n", + "\n", + "))\n", + "[parity max odd 3]\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "2\n", + "\n", + "2\n", + "\n", + "\n", + "\n", + "0->2\n", + "\n", + "\n", + "!p0 & !p1\n", + "\n", + "\n", + "\n", + "\n", + "3\n", + "\n", + "3\n", + "\n", + "\n", + "\n", + "0->3\n", + "\n", + "\n", + "!p0 & !p1\n", + "\n", + "\n", + "\n", + "\n", + "1\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "2->1\n", + "\n", + "\n", + "!p0 & p1\n", + "\n", + "\n", + "\n", + "\n", + "3->0\n", + "\n", + "\n", + "p0 & !p1\n", + "\n", + "\n", + "\n", + "\n", + "3->2\n", + "\n", + "\n", + "!p0 & !p1\n", + "\n", + "\n", + "\n", + "\n", + "3->3\n", + "\n", + "\n", + "!p0 & p1\n", + "\n", + "\n", + "\n", + "\n", + "1->0\n", + "\n", + "\n", + "!p0 & p1\n", + "\n", + "\n", + "\n", + "\n", + "1->2\n", + "\n", + "\n", + "!p0 & !p1\n", + "\n", + "\n", + "\n", + "\n", + "
" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" } ], "source": [ - "display2(maxeven4, spot.reduce_parity(maxeven4))\n", - "display2(maxeven4, spot.reduce_parity(maxeven4, True))" + "display(maxeven4)\n", + "display2(spot.reduce_parity(maxeven4), spot.reduce_parity(maxeven4, True))\n", + "display2(spot.reduce_parity(maxeven4, False, True), spot.reduce_parity(maxeven4, True, True))" ] + }, + { + "cell_type": "code", + "execution_count": null, + "metadata": {}, + "outputs": [], + "source": [] } ], "metadata": { "kernelspec": { - "display_name": "Python 3", + "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, @@ -5212,7 +5344,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.2" + "version": "3.10.7" } }, "nbformat": 4, From 67722db78f64ed332abba8ad7042d4657b2db89d Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Fri, 14 Oct 2022 17:55:36 +0200 Subject: [PATCH 177/337] reduce_parity: expose the internal vectors of colors * spot/twaalgos/parity.cc, spot/twaalgos/parity.hh: Add a reduce_parity_data class for access to the vectors of colors computed by reduce_parity. * python/spot/impl.i: Add bindings for std::vector. --- python/spot/impl.i | 1 + spot/twaalgos/parity.cc | 75 ++++++++++++++++++++++------------------- spot/twaalgos/parity.hh | 23 +++++++++++++ 3 files changed, 64 insertions(+), 35 deletions(-) diff --git a/python/spot/impl.i b/python/spot/impl.i index 88bdcf5c4..502770fcb 100644 --- a/python/spot/impl.i +++ b/python/spot/impl.i @@ -520,6 +520,7 @@ namespace std { %template(vectorbdd) vector; %template(aliasvector) vector>; %template(vectorstring) vector; + %template(vectorint) vector; %template(pair_formula_vectorstring) pair>; %template(atomic_prop_set) set; %template(relabeling_map) map; diff --git a/spot/twaalgos/parity.cc b/spot/twaalgos/parity.cc index 9428d7bb5..c8507ac53 100644 --- a/spot/twaalgos/parity.cc +++ b/spot/twaalgos/parity.cc @@ -387,27 +387,15 @@ namespace spot return aut; } - twa_graph_ptr - reduce_parity(const const_twa_graph_ptr& aut, bool colored, bool layered) + reduce_parity_data::reduce_parity_data(const const_twa_graph_ptr& aut, + bool layered) { - return reduce_parity_here(make_twa_graph(aut, twa::prop_set::all()), - colored, layered); - } - - twa_graph_ptr - reduce_parity_here(twa_graph_ptr aut, bool colored, bool layered) - { - unsigned num_sets = aut->num_sets(); - if (!colored && num_sets == 0) - return aut; - - bool current_max; - bool current_odd; - if (!aut->acc().is_parity(current_max, current_odd, true)) - input_is_not_parity("reduce_parity"); + if (!aut->acc().is_parity(parity_max, parity_odd, true)) + input_is_not_parity("reduce_parity_data"); if (!aut->is_existential()) throw std::runtime_error - ("reduce_parity_here() does not support alternation"); + ("reduce_parity_data() does not support alternation"); + unsigned num_sets = aut->num_sets(); // The algorithm assumes "max odd" or "max even" parity. "min" // parity is handled by converting it to "max" while the algorithm @@ -466,8 +454,8 @@ namespace spot // // -2 means the edge was never assigned a color. unsigned evs = aut->edge_vector().size(); - std::vector piprime1(evs, -2); // k=1 - std::vector piprime2(evs, -2); // k=0 + piprime1.resize(evs, -2); // k=1 + piprime2.resize(evs, -2); // k=0 bool sba = aut->prop_state_acc().is_true(); auto rec = @@ -481,7 +469,7 @@ namespace spot { int piri; // π(Rᵢ) int color; // corresponding color, to deal with "min" kind - if (current_max) + if (parity_max) { piri = color = si.acc_sets_of(scc).max_set() - 1; } @@ -538,11 +526,28 @@ namespace spot }; scc_and_mark_filter filter1(aut, {}); rec(filter1, rec); + } + + twa_graph_ptr + reduce_parity(const const_twa_graph_ptr& aut, bool colored, bool layered) + { + return reduce_parity_here(make_twa_graph(aut, twa::prop_set::all()), + colored, layered); + } + + twa_graph_ptr + reduce_parity_here(twa_graph_ptr aut, bool colored, bool layered) + { + unsigned num_sets = aut->num_sets(); + if (!colored && num_sets == 0) + return aut; + + reduce_parity_data pd(aut, layered); // compute the used range for each vector. int min1 = num_sets; int max1 = -2; - for (int m : piprime1) + for (int m : pd.piprime1) { if (m <= -2) continue; @@ -559,7 +564,7 @@ namespace spot } int min2 = num_sets; int max2 = -2; - for (int m : piprime2) + for (int m : pd.piprime2) { if (m <= -2) continue; @@ -575,13 +580,13 @@ namespace spot { std::swap(size1, size2); std::swap(min1, min2); - std::swap(piprime1, piprime2); + std::swap(pd.piprime1, pd.piprime2); } unsigned new_num_sets = size1; - if (current_max) + if (pd.parity_max) { - for (int& m : piprime1) + for (int& m : pd.piprime1) if (m > -2) m -= min1; else @@ -589,7 +594,7 @@ namespace spot } else { - for (int& m : piprime1) + for (int& m : pd.piprime1) if (m > -2) m = new_num_sets - (m - min1) - 1; else @@ -597,8 +602,8 @@ namespace spot } // The parity style changes if we shift colors by an odd number. - bool new_odd = current_odd ^ (min1 & 1); - if (!current_max) + bool new_odd = pd.parity_odd ^ (min1 & 1); + if (!pd.parity_max) // Switching from min<->max changes the parity style every time // the number of colors is even. If the input was "min", we // switched once to "max" to apply the reduction and once again @@ -607,7 +612,7 @@ namespace spot new_odd ^= !(num_sets & 1) ^ !(new_num_sets & 1); if (!colored) { - new_odd ^= current_max; + new_odd ^= pd.parity_max; new_num_sets -= 1; // It seems we have nothing to win by changing automata with a @@ -617,18 +622,18 @@ namespace spot } aut->set_acceptance(new_num_sets, - acc_cond::acc_code::parity(current_max, new_odd, + acc_cond::acc_code::parity(pd.parity_max, new_odd, new_num_sets)); if (colored) for (auto& e: aut->edges()) { unsigned n = aut->edge_number(e); - e.acc = acc_cond::mark_t({unsigned(piprime1[n])}); + e.acc = acc_cond::mark_t({unsigned(pd.piprime1[n])}); } - else if (current_max) + else if (pd.parity_max) for (auto& e: aut->edges()) { - unsigned n = piprime1[aut->edge_number(e)]; + unsigned n = pd.piprime1[aut->edge_number(e)]; if (n == 0) e.acc = acc_cond::mark_t({}); else @@ -637,7 +642,7 @@ namespace spot else for (auto& e: aut->edges()) { - unsigned n = piprime1[aut->edge_number(e)]; + unsigned n = pd.piprime1[aut->edge_number(e)]; if (n >= new_num_sets) e.acc = acc_cond::mark_t({}); else diff --git a/spot/twaalgos/parity.hh b/spot/twaalgos/parity.hh index 44d7cca7e..188e92483 100644 --- a/spot/twaalgos/parity.hh +++ b/spot/twaalgos/parity.hh @@ -21,6 +21,7 @@ #include #include +#include namespace spot { @@ -194,5 +195,27 @@ namespace spot SPOT_API twa_graph_ptr reduce_parity_here(twa_graph_ptr aut, bool colored = false, bool layered = false); + + /// @} + + /// \brief Internal data computed by the reduce_parity function + /// + /// `piprime1` and `piprime2` have the size of `aut`'s edge vector, + /// represent two possible colorations of the edges. piprime1 assumes + /// that terminal cases of the recursion are odd, and piprime2 assumes + /// they are even. + /// + /// reduce_parity() actually compare the range of values in these + /// two vectors to limit the number of colors. + struct SPOT_API reduce_parity_data + { + bool parity_max; ///< Whether the input automaton is parity max + bool parity_odd; ///< Whether the input automaton is parity odd + std::vector piprime1; + std::vector piprime2; + + reduce_parity_data(const const_twa_graph_ptr& aut, bool layered = false); + }; + /// @} } From c4a33d34573f7c1881aead7ed2c4756583072be3 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Tue, 18 Oct 2022 17:34:13 +0200 Subject: [PATCH 178/337] add a .mailmap for git * .mailmap: New file, to fix email inconsistencies. --- .mailmap | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 .mailmap diff --git a/.mailmap b/.mailmap new file mode 100644 index 000000000..41bc60980 --- /dev/null +++ b/.mailmap @@ -0,0 +1,20 @@ +Ala-Eddine Ben-Salem +Ala-Eddine Ben-Salem +Ala-Eddine Ben-Salem +Antoine Martin +Arthur Remaud +Arthur Remaud +Damien Lefortier +Felix Abecassis +Felix Abecassis +Felix Abecassis +Guillaume Sadegh +Guillaume Sadegh +Henrich Lauko +Henrich Lauko +Jerome Dubois Jérôme Dubois +Philipp Schlehuber-Caissier +Thibaud Michaud +Thomas Badie +Rachid Rebiha +Thomas Martinez From 52ed3d1e8fa60044baecd1fbfb113596da59dc78 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Wed, 19 Oct 2022 14:54:34 +0200 Subject: [PATCH 179/337] * bin/common_aoutput.cc: Missing space in doc string. --- bin/common_aoutput.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/common_aoutput.cc b/bin/common_aoutput.cc index f2c8691ec..e8c2e5401 100644 --- a/bin/common_aoutput.cc +++ b/bin/common_aoutput.cc @@ -221,7 +221,7 @@ static const argp_option io_options[] = "(iw) inherently weak. Use uppercase letters to negate them.", 0 }, { "%R, %[LETTERS]R", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE, - "CPU time (excluding parsing), in seconds; Add LETTERS to restrict to" + "CPU time (excluding parsing), in seconds; Add LETTERS to restrict to " "(u) user time, (s) system time, (p) parent process, " "or (c) children processes.", 0 }, { "%N, %n", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE, From de29ba9e4cb21b0e56ede4ecb9cfc2cebe4c6e5e Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Wed, 19 Oct 2022 16:30:00 +0200 Subject: [PATCH 180/337] stats: add options to count unreachable states and transitions Based on a request from Pierre Ganty. * spot/twaalgos/stats.cc, spot/twaalgos/stats.hh, bin/common_aoutput.cc, bin/common_aoutput.hh: Implement those options. * tests/core/format.test: Add test case. * doc/org/autfilt.org: Update doc. * NEWS: Mention them. --- NEWS | 7 ++++ bin/common_aoutput.cc | 43 ++++++++++++-------- bin/common_aoutput.hh | 6 +-- doc/org/autfilt.org | 10 +++-- spot/twaalgos/stats.cc | 90 ++++++++++++++++++++++++++++++++++++++---- spot/twaalgos/stats.hh | 43 +++++++++++++++++--- tests/core/format.test | 50 +++++++++++++++-------- 7 files changed, 197 insertions(+), 52 deletions(-) diff --git a/NEWS b/NEWS index d8fd0ab70..bf9e1079e 100644 --- a/NEWS +++ b/NEWS @@ -1,5 +1,12 @@ New in spot 2.11.1.dev (not yet released) + Command-line tools: + + - The --stats specifications %s, %e, %t for printing the number of + (reachable) states, edges, and transitions, learned to support + options [r], [u], [a] to indicate if only reachable, unreachable, + or all elements should be counted. + Library: - spot::reduce_parity() now has a "layered" option to force all diff --git a/bin/common_aoutput.cc b/bin/common_aoutput.cc index e8c2e5401..fcc79fc3c 100644 --- a/bin/common_aoutput.cc +++ b/bin/common_aoutput.cc @@ -203,12 +203,18 @@ static const argp_option io_options[] = "to specify additional options as in --hoa=opt)", 0 }, { "%M, %m", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE, "name of the automaton", 0 }, - { "%S, %s", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE, - "number of reachable states", 0 }, - { "%E, %e", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE, - "number of reachable edges", 0 }, - { "%T, %t", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE, - "number of reachable transitions", 0 }, + { "%S, %s, %[LETTER]S, %[LETTER]s", + 0, nullptr, OPTION_DOC | OPTION_NO_USAGE, + "number of states (add one LETTER to select (r) reachable [default], " + "(u) unreachable, (a) all).", 0 }, + { "%E, %e, %[LETTER]E, %[LETTER]e", + 0, nullptr, OPTION_DOC | OPTION_NO_USAGE, + "number of edges (add one LETTER to select (r) reachable [default], " + "(u) unreachable, (a) all).", 0 }, + { "%T, %t, %[LETTER]E, %[LETTER]e", + 0, nullptr, OPTION_DOC | OPTION_NO_USAGE, + "number of transitions (add one LETTER to select (r) reachable " + "[default], (u) unreachable, (a) all).", 0 }, { "%A, %a", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE, "number of acceptance sets", 0 }, { "%G, %g, %[LETTERS]G, %[LETTERS]g", 0, nullptr, @@ -268,12 +274,15 @@ static const argp_option o_options[] = "to specify additional options as in --hoa=opt)", 0 }, { "%m", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE, "name of the automaton", 0 }, - { "%s", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE, - "number of reachable states", 0 }, - { "%e", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE, - "number of reachable edges", 0 }, - { "%t", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE, - "number of reachable transitions", 0 }, + { "%s, %[LETTER]s", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE, + "number of states (add one LETTER to select (r) reachable [default], " + "(u) unreachable, (a) all).", 0 }, + { "%e, %[LETTER]e", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE, + "number of edges (add one LETTER to select (r) reachable [default], " + "(u) unreachable, (a) all).", 0 }, + { "%t, %[LETTER]t", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE, + "number of transitions (add one LETTER to select (r) reachable " + "[default], (u) unreachable, (a) all).", 0 }, { "%a", 0, nullptr, OPTION_DOC | OPTION_NO_USAGE, "number of acceptance sets", 0 }, { "%g, %[LETTERS]g", 0, nullptr, @@ -472,15 +481,15 @@ hoa_stat_printer::print(const spot::const_parsed_aut_ptr& haut, if (has('T')) { spot::twa_sub_statistics s = sub_stats_reachable(haut->aut); - haut_states_ = s.states; - haut_edges_ = s.edges; - haut_trans_ = s.transitions; + haut_states_.set(s.states, haut->aut->num_states()); + haut_edges_.set(s.edges, haut->aut->num_edges()); + haut_trans_.set(s.transitions, count_all_transitions(haut->aut)); } else if (has('E') || has('S')) { spot::twa_statistics s = stats_reachable(haut->aut); - haut_states_ = s.states; - haut_edges_ = s.edges; + haut_states_.set(s.states, haut->aut->num_states()); + haut_edges_.set(s.edges, haut->aut->num_edges()); } if (has('M')) { diff --git a/bin/common_aoutput.hh b/bin/common_aoutput.hh index 0fb2e8d7c..d33b687d2 100644 --- a/bin/common_aoutput.hh +++ b/bin/common_aoutput.hh @@ -166,9 +166,9 @@ private: spot::printable_value aut_word_; spot::printable_value haut_word_; spot::printable_acc_cond haut_gen_acc_; - spot::printable_value haut_states_; - spot::printable_value haut_edges_; - spot::printable_value haut_trans_; + spot::printable_size haut_states_; + spot::printable_size haut_edges_; + spot::printable_long_size haut_trans_; spot::printable_value haut_acc_; printable_varset haut_ap_; printable_varset aut_ap_; diff --git a/doc/org/autfilt.org b/doc/org/autfilt.org index 4ccf09f07..5c8a8f1e5 100644 --- a/doc/org/autfilt.org +++ b/doc/org/autfilt.org @@ -145,7 +145,8 @@ ltl2tgba --help | sed -n '/ sequences:/,/^$/p' | sed '1d;$d' (iw) inherently weak. Use uppercase letters to negate them. %d 1 if the output is deterministic, 0 otherwise - %e number of reachable edges + %e, %[LETTER]e number of edges (add one LETTER to select (r) + reachable [default], (u) unreachable, (a) all). %f the formula, in Spot's syntax %F name of the input file %g, %[LETTERS]g acceptance condition (in HOA syntax); add brackets @@ -170,8 +171,11 @@ ltl2tgba --help | sed -n '/ sequences:/,/^$/p' | sed '1d;$d' LETTERS to restrict to(u) user time, (s) system time, (p) parent process, or (c) children processes. - %s number of reachable states - %t number of reachable transitions + %s, %[LETTER]s number of states (add one LETTER to select (r) + reachable [default], (u) unreachable, (a) all). + %t, %[LETTER]t number of transitions (add one LETTER to select + (r) reachable [default], (u) unreachable, (a) + all). %u, %[e]u number of states (or [e]dges) with universal branching %u, %[LETTER]u 1 if the automaton contains some universal diff --git a/spot/twaalgos/stats.cc b/spot/twaalgos/stats.cc index ddccba5db..4a905e542 100644 --- a/spot/twaalgos/stats.cc +++ b/spot/twaalgos/stats.cc @@ -1,6 +1,6 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2008, 2011-2018, 2020 Laboratoire de Recherche et -// Développement de l'Epita (LRDE). +// Copyright (C) 2008, 2011-2018, 2020, 2022 Laboratoire de Recherche +// et Développement de l'Epita (LRDE). // Copyright (C) 2004 Laboratoire d'Informatique de Paris 6 (LIP6), // département Systèmes Répartis Coopératifs (SRC), Université Pierre // et Marie Curie. @@ -33,6 +33,16 @@ namespace spot { + unsigned long long + count_all_transitions(const const_twa_graph_ptr& g) + { + unsigned long long tr = 0; + bdd v = g->ap_vars(); + for (auto& e: g->edges()) + tr += bdd_satcountset(e.cond, v); + return tr; + } + namespace { class stats_bfs: public twa_reachable_iterator_breadth_first @@ -82,6 +92,7 @@ namespace spot }; + template void dfs(const const_twa_graph_ptr& ge, SU state_update, EU edge_update) { @@ -344,10 +355,73 @@ namespace spot << std::string(beg, end + 2) << ", "; tmp << e.what(); throw std::runtime_error(tmp.str()); - } } + void printable_size::print(std::ostream& os, const char* pos) const + { + char p = 'r'; + if (*pos == '[') + { + p = pos[1]; + if (pos[2] != ']' || !(p == 'r' || p == 'u' || p == 'a')) + { + const char* end = strchr(pos + 1, ']'); + std::ostringstream tmp; + tmp << "while processing %" + << std::string(pos, end + 2) << ", " + << "only [a], [r], or [u] is supported."; + throw std::runtime_error(tmp.str()); + } + } + switch (p) + { + case 'r': + os << reachable_; + return; + case 'a': + os << all_; + return; + case 'u': + os << all_ - reachable_; + return; + } + SPOT_UNREACHABLE(); + return; + } + + void printable_long_size::print(std::ostream& os, const char* pos) const + { + char p = 'r'; + if (*pos == '[') + { + p = pos[1]; + if (pos[2] != ']' || !(p == 'r' || p == 'u' || p == 'a')) + { + const char* end = strchr(pos + 1, ']'); + std::ostringstream tmp; + tmp << "while processing %" + << std::string(pos, end + 2) << ", " + << "only [a], [r], or [u] is supported."; + throw std::runtime_error(tmp.str()); + } + } + switch (p) + { + case 'r': + os << reachable_; + return; + case 'a': + os << all_; + return; + case 'u': + os << all_ - reachable_; + return; + } + SPOT_UNREACHABLE(); + return; + } + stat_printer::stat_printer(std::ostream& os, const char* format) : format_(format) @@ -376,15 +450,15 @@ namespace spot if (has('t')) { twa_sub_statistics s = sub_stats_reachable(aut); - states_ = s.states; - edges_ = s.edges; - trans_ = s.transitions; + states_.set(s.states, aut->num_states()); + edges_.set(s.edges, aut->num_edges()); + trans_.set(s.transitions, count_all_transitions(aut)); } else if (has('s') || has('e')) { twa_statistics s = stats_reachable(aut); - states_ = s.states; - edges_ = s.edges; + states_.set(s.states, aut->num_states()); + edges_.set(s.edges, aut->num_edges()); } if (has('a')) diff --git a/spot/twaalgos/stats.hh b/spot/twaalgos/stats.hh index 1caa8324b..24353fc31 100644 --- a/spot/twaalgos/stats.hh +++ b/spot/twaalgos/stats.hh @@ -1,6 +1,6 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2008, 2011-2017, 2020 Laboratoire de Recherche et -// Développement de l'Epita (LRDE). +// Copyright (C) 2008, 2011-2017, 2020, 2022 Laboratoire de Recherche +// et Développement de l'Epita (LRDE). // Copyright (C) 2004 Laboratoire d'Informatique de Paris 6 (LIP6), // département Systèmes Répartis Coopératifs (SRC), Université Pierre // et Marie Curie. @@ -55,6 +55,9 @@ namespace spot /// \brief Compute sub statistics for an automaton. SPOT_API twa_sub_statistics sub_stats_reachable(const const_twa_ptr& g); + /// \brief Count all transtitions, even unreachable ones. + SPOT_API unsigned long long + count_all_transitions(const const_twa_graph_ptr& g); class SPOT_API printable_formula: public printable_value { @@ -102,6 +105,36 @@ namespace spot void print(std::ostream& os, const char* pos) const override; }; + class SPOT_API printable_size final: + public spot::printable + { + unsigned reachable_ = 0; + unsigned all_ = 0; + public: + void set(unsigned reachable, unsigned all) + { + reachable_ = reachable; + all_ = all; + } + + void print(std::ostream& os, const char* pos) const override; + }; + + class SPOT_API printable_long_size final: + public spot::printable + { + unsigned long long reachable_ = 0; + unsigned long long all_ = 0; + public: + void set(unsigned long long reachable, unsigned long long all) + { + reachable_ = reachable; + all_ = all; + } + + void print(std::ostream& os, const char* pos) const override; + }; + /// \brief prints various statistics about a TGBA /// /// This object can be configured to display various statistics @@ -123,9 +156,9 @@ namespace spot const char* format_; printable_formula form_; - printable_value states_; - printable_value edges_; - printable_value trans_; + printable_size states_; + printable_size edges_; + printable_long_size trans_; printable_value acc_; printable_scc_info scc_; printable_value nondetstates_; diff --git a/tests/core/format.test b/tests/core/format.test index 4e6f4a4d5..da78e3e7e 100644 --- a/tests/core/format.test +++ b/tests/core/format.test @@ -1,7 +1,7 @@ #!/bin/sh # -*- coding: utf-8 -*- -# Copyright (C) 2016, 2017 Laboratoire de Recherche et Développement de -# l'Epita (LRDE). +# Copyright (C) 2016, 2017, 2022 Laboratoire de Recherche et +# Développement de l'Epita (LRDE). # # This file is part of Spot, a model checking library. # @@ -139,18 +139,36 @@ test 3,5 = `ltl2tgba --low --any --stats=%s,%e "$f"` test 3,4 = `ltl2tgba --stats=%s,%e "$f"` cat >foo < stats + +cat >expected <err && exit 1 +grep 'only \[a\], \[r\], or \[u\] is supported' err From 0ba6949f7dd5d0cff778672ec5eb15de2416a5b5 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Thu, 20 Oct 2022 10:48:01 +0200 Subject: [PATCH 181/337] use bdd_restrict more Doing so reduced the number of GC passes tested in bdd.test, which is good. * spot/twaalgos/ltl2tgba_fm.cc: Simplify minato loops with bdd_restrict. * spot/twaalgos/synthesis.cc (split_2step): Use bdd_restrict instead of bdd_appex. * tests/core/bdd.test, tests/core/ltlf.test: Adjust test cases. --- spot/twaalgos/ltl2tgba_fm.cc | 35 +++++++++++++---------------------- spot/twaalgos/synthesis.cc | 4 +--- tests/core/bdd.test | 14 +++++++------- tests/core/ltlf.test | 12 ++++++------ 4 files changed, 27 insertions(+), 38 deletions(-) diff --git a/spot/twaalgos/ltl2tgba_fm.cc b/spot/twaalgos/ltl2tgba_fm.cc index 3566abc97..a354aaddd 100644 --- a/spot/twaalgos/ltl2tgba_fm.cc +++ b/spot/twaalgos/ltl2tgba_fm.cc @@ -900,8 +900,7 @@ namespace spot for (bdd label: minterms_of(all_props, var_set)) { formula dest = - dict_.bdd_to_sere(bdd_appex(res_ndet, label, bddop_and, - dict_.var_set)); + dict_.bdd_to_sere(bdd_restrict(res_ndet, label)); dest = formula::first_match(dest); if (to_concat_) dest = formula::Concat({dest, to_concat_}); @@ -995,9 +994,7 @@ namespace spot bdd all_props = bdd_existcomp(res, dict_.var_set); for (bdd label: minterms_of(all_props, var_set)) { - formula dest = - dict_.bdd_to_sere(bdd_appex(res, label, bddop_and, - dict_.var_set)); + formula dest = dict_.bdd_to_sere(bdd_restrict(res, label)); f2a_t::const_iterator i = f2a_.find(dest); if (i != f2a_.end() && i->second.first == nullptr) continue; @@ -1471,9 +1468,7 @@ namespace spot for (bdd label: minterms_of(all_props, var_set)) { formula dest = - dict_.bdd_to_sere(bdd_appex(f1, label, bddop_and, - dict_.var_set)); - + dict_.bdd_to_sere(bdd_restrict(f1, label)); formula dest2 = formula::binop(o, dest, node[1]); bool unamb = dict_.unambiguous; if (!dest2.is_ff()) @@ -1552,9 +1547,7 @@ namespace spot for (bdd label: minterms_of(all_props, var_set)) { formula dest = - dict_.bdd_to_sere(bdd_appex(f1, label, bddop_and, - dict_.var_set)); - + dict_.bdd_to_sere(bdd_restrict(f1, label)); formula dest2 = formula::binop(o, dest, node[1]); bdd udest = @@ -1787,16 +1780,15 @@ namespace spot var_set = bdd_existcomp(bdd_support(t.symbolic), d_.var_set); all_props = bdd_existcomp(t.symbolic, d_.var_set); } - for (bdd one_prop_set: minterms_of(all_props, var_set)) + for (bdd label: minterms_of(all_props, var_set)) { - minato_isop isop(t.symbolic & one_prop_set); + minato_isop isop(t.symbolic & label); bdd cube; while ((cube = isop.next()) != bddfalse) { bdd label = bdd_exist(cube, d_.next_set); bdd dest_bdd = bdd_existcomp(cube, d_.next_set); - formula dest = - d_.conj_bdd_to_formula(dest_bdd); + formula dest = d_.conj_bdd_to_formula(dest_bdd); // Handle a Miyano-Hayashi style unrolling for // rational operators. Marked nodes correspond to @@ -1818,8 +1810,7 @@ namespace spot dest = d_.mt.mark_concat_ops(dest); } // Note that simplify_mark may have changed dest. - dest_bdd = bdd_ithvar(d_.register_next_variable(dest)); - res |= label & dest_bdd; + res |= label & bdd_ithvar(d_.register_next_variable(dest)); } } t.symbolic = res; @@ -2120,16 +2111,15 @@ namespace spot // // FIXME: minato_isop is quite expensive, and I (=adl) // don't think we really care that much about getting the - // smalled sum of products that minato_isop strives to + // smallest sum of products that minato_isop strives to // compute. Given that Next and Acc variables should // always be positive, maybe there is a faster way to // compute the successors? E.g. using bdd_satone() and // ignoring negated Next and Acc variables. - minato_isop isop(res & one_prop_set); + minato_isop isop(bdd_restrict(res, one_prop_set)); bdd cube; while ((cube = isop.next()) != bddfalse) { - bdd label = bdd_exist(cube, d.next_set); bdd dest_bdd = bdd_existcomp(cube, d.next_set); formula dest = d.conj_bdd_to_formula(dest_bdd); @@ -2147,8 +2137,9 @@ namespace spot if (symb_merge) dest = fc.canonicalize(dest); - bdd conds = bdd_existcomp(label, d.var_set); - bdd promises = bdd_existcomp(label, d.a_set); + bdd conds = + exprop ? one_prop_set : bdd_existcomp(cube, d.var_set); + bdd promises = bdd_existcomp(cube, d.a_set); dests.emplace_back(transition(dest, conds, promises)); } } diff --git a/spot/twaalgos/synthesis.cc b/spot/twaalgos/synthesis.cc index e1e4e1780..88e22ff04 100644 --- a/spot/twaalgos/synthesis.cc +++ b/spot/twaalgos/synthesis.cc @@ -574,9 +574,7 @@ namespace spot // implies is faster than and if (bdd_implies(one_letter, e_info.einsup.first)) { - e_info.econdout = - bdd_appex(e_info.econd, one_letter, - bddop_and, input_bdd); + e_info.econdout = bdd_restrict(e_info.econd, one_letter); dests.push_back(&e_info); assert(e_info.econdout != bddfalse); } diff --git a/tests/core/bdd.test b/tests/core/bdd.test index ba2e11232..db03dbad5 100755 --- a/tests/core/bdd.test +++ b/tests/core/bdd.test @@ -1,7 +1,7 @@ #!/bin/sh # -*- coding: utf-8 -*- -# Copyright (C) 2017, 2018, 2020 Laboratoire de Recherche et Développement de -# l'Epita (LRDE). +# Copyright (C) 2017, 2018, 2020, 2022 Laboratoire de Recherche et +# Développement de l'Epita (LRDE). # # This file is part of Spot, a model checking library. # @@ -23,7 +23,7 @@ set -e # Make sure that setting the SPOT_BDD_TRACE envvar actually does # something. -genltl --kr-nlogn=2 | +genltl --kr-n=3 | SPOT_BDD_TRACE=1 ltl2tgba -x tls-max-states=0 -D >out 2>err cat err grep spot: out && exit 1 @@ -31,14 +31,14 @@ grep 'spot: BDD package initialized' err # This value below, which is the number of time we need to garbage # collect might change if we improve the tool or change the way BuDDy # is initialized. -test 11 = `grep -c 'spot: BDD GC' err` +test 2 = `grep -c 'spot: BDD GC' err` # Minimal size for this automaton. # See also https://www.lrde.epita.fr/dload/spot/mochart10-fixes.pdf -test "147,207" = `autfilt --stats=%s,%e out` +test "2240,4214" = `autfilt --stats=%s,%e out` # With the default value of tls-max-states, no GC is needed -genltl --kr-nlogn=2 | SPOT_BDD_TRACE=1 ltl2tgba -D --stats=%s,%e >out 2>err +genltl --kr-n=3 | SPOT_BDD_TRACE=1 ltl2tgba -D --stats=%s,%e >out 2>err cat err grep 'spot: BDD package initialized' err test 0 = `grep -c 'spot: BDD GC' err` -test "147,207" = `cat out` +test "2240,4214" = `cat out` diff --git a/tests/core/ltlf.test b/tests/core/ltlf.test index a1979bc8d..11f2132ac 100755 --- a/tests/core/ltlf.test +++ b/tests/core/ltlf.test @@ -57,7 +57,7 @@ State: 3 HOA: v1 name: "a & X(A & a) & (A U G!A)" States: 4 -Start: 2 +Start: 3 AP: 2 "A" "a" acc-name: Buchi Acceptance: 1 Inf(0) @@ -70,9 +70,9 @@ State: 1 [!0] 0 [0] 1 State: 2 -[0&1] 3 -State: 3 [0&1] 1 +State: 3 +[0&1] 2 --END-- HOA: v1 name: "(a U (A & b)) & (A U G!A) & F((A & c) | (A & d & X!A))" @@ -124,7 +124,7 @@ State: 2 {0} --END-- HOA: v1 States: 3 -Start: 1 +Start: 2 AP: 1 "a" acc-name: Buchi Acceptance: 1 Inf(0) @@ -133,9 +133,9 @@ properties: trans-labels explicit-labels state-acc deterministic State: 0 {0} [t] 0 State: 1 -[0] 2 -State: 2 [0] 0 +State: 2 +[0] 1 --END-- HOA: v1 States: 5 From 0ecc870a0eabaf7f845bcab2acbdc1e1c0945072 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Tue, 25 Oct 2022 11:52:03 +0200 Subject: [PATCH 182/337] [buddy] Add a default_deleter for bddPair * src/bddx.h (std::default_deleter): Here. --- buddy/src/bddx.h | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/buddy/src/bddx.h b/buddy/src/bddx.h index 0efd3a0a9..b3cb377a1 100644 --- a/buddy/src/bddx.h +++ b/buddy/src/bddx.h @@ -501,6 +501,7 @@ BUDDY_API_VAR const BDD bddtrue; *************************************************************************/ #ifdef CPLUSPLUS #include +#include /*=== User BDD class ===================================================*/ @@ -1092,6 +1093,11 @@ inline bddxfalse bdd_false(void) { return bddxfalse(); } +template<> +struct std::default_delete { + void operator()(bddPair *p) const { bdd_freepair(p); }; +}; + /*=== Iostream printing ================================================*/ class BUDDY_API bdd_ioformat From 65bc67f300ecaa07fa027dfada96c026b34d9945 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Tue, 25 Oct 2022 11:53:05 +0200 Subject: [PATCH 183/337] relabel_here: make sure free_bddpair is called * spot/twaalgos/relabel.cc (relabel_here): This function has multiple exit paths, and none of them were calling bdd_freepair. Use a unique_ptr to ensure that. --- spot/twaalgos/relabel.cc | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/spot/twaalgos/relabel.cc b/spot/twaalgos/relabel.cc index 22eddd893..ac1556aec 100644 --- a/spot/twaalgos/relabel.cc +++ b/spot/twaalgos/relabel.cc @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2015-2018, 2020 Laboratoire de Recherche et +// Copyright (C) 2015-2018, 2020, 2022 Laboratoire de Recherche et // Développement de l'Epita (LRDE). // // This file is part of Spot, a model checking library. @@ -26,7 +26,7 @@ namespace spot void relabel_here(twa_graph_ptr& aut, relabeling_map* relmap) { - bddPair* pairs = bdd_newpair(); + std::unique_ptr pairs(bdd_newpair()); auto d = aut->get_dict(); std::vector vars; std::set newvars; @@ -53,7 +53,7 @@ namespace spot { int newv = aut->register_ap(p.second); newvars.insert(newv); - bdd_setpair(pairs, oldv, newv); + bdd_setpair(pairs.get(), oldv, newv); } else { @@ -64,7 +64,7 @@ namespace spot return false; }); bdd newb = formula_to_bdd(p.second, d, aut); - bdd_setbddpair(pairs, oldv, newb); + bdd_setbddpair(pairs.get(), oldv, newb); bool_subst = true; } } @@ -75,7 +75,7 @@ namespace spot static_cast(bdd_veccompose) : static_cast(bdd_replace); for (auto& t: aut->edges()) { - bdd c = (*op)(t.cond, pairs); + bdd c = (*op)(t.cond, pairs.get()); t.cond = c; if (c == bddfalse) need_cleanup = true; From 0a710eb99594983bfc559a607fe702804c3e17ce Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Tue, 25 Oct 2022 16:31:35 +0200 Subject: [PATCH 184/337] declare all argp_program_doc as static * bench/stutter/stutter_invariance_formulas.cc, bin/autcross.cc, bin/autfilt.cc, bin/dstar2tgba.cc, bin/genaut.cc, bin/genltl.cc, bin/ltl2tgba.cc, bin/ltl2tgta.cc, bin/ltlcross.cc, bin/ltldo.cc, bin/ltlfilt.cc, bin/ltlsynt.cc, bin/randaut.cc, bin/randltl.cc, bin/spot-x.cc, bin/spot.cc, tests/ltsmin/modelcheck.cc: Here. --- bench/stutter/stutter_invariance_formulas.cc | 6 +++--- bin/autcross.cc | 6 +++--- bin/autfilt.cc | 2 +- bin/dstar2tgba.cc | 2 +- bin/genaut.cc | 7 ++++--- bin/genltl.cc | 2 +- bin/ltl2tgba.cc | 2 +- bin/ltl2tgta.cc | 2 +- bin/ltlcross.cc | 6 +++--- bin/ltldo.cc | 2 +- bin/ltlfilt.cc | 2 +- bin/ltlsynt.cc | 2 +- bin/randaut.cc | 6 +++--- bin/randltl.cc | 4 ++-- bin/spot-x.cc | 2 +- bin/spot.cc | 7 ++++--- tests/ltsmin/modelcheck.cc | 6 +++--- 17 files changed, 34 insertions(+), 32 deletions(-) diff --git a/bench/stutter/stutter_invariance_formulas.cc b/bench/stutter/stutter_invariance_formulas.cc index 2007891af..32bc45083 100644 --- a/bench/stutter/stutter_invariance_formulas.cc +++ b/bench/stutter/stutter_invariance_formulas.cc @@ -1,6 +1,6 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2014, 2015, 2016, 2017 Laboratoire de Recherche et -// Développement de l'Epita (LRDE). +// Copyright (C) 2014, 2015, 2016, 2017, 2022 Laboratoire de Recherche +// et Développement de l'Epita (LRDE). // // This file is part of Spot, a model checking library. // @@ -28,7 +28,7 @@ #include #include -const char argp_program_doc[] =""; +static const char argp_program_doc[] = ""; const struct argp_child children[] = { diff --git a/bin/autcross.cc b/bin/autcross.cc index 21d21f2c7..24cd9bcd4 100644 --- a/bin/autcross.cc +++ b/bin/autcross.cc @@ -1,6 +1,6 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2017-2020, 2022 Laboratoire de Recherche et Développement de -// l'Epita (LRDE). +// Copyright (C) 2017-2020, 2022 Laboratoire de Recherche et +// Développement de l'Epita (LRDE). // // This file is part of Spot, a model checking library. // @@ -51,7 +51,7 @@ #include #include -const char argp_program_doc[] ="\ +static 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 option.\v\ diff --git a/bin/autfilt.cc b/bin/autfilt.cc index 49543e596..e16ef770a 100644 --- a/bin/autfilt.cc +++ b/bin/autfilt.cc @@ -75,7 +75,7 @@ #include #include -static const char argp_program_doc[] ="\ +static const char argp_program_doc[] = "\ Convert, transform, and filter omega-automata.\v\ Exit status:\n\ 0 if some automata were output\n\ diff --git a/bin/dstar2tgba.cc b/bin/dstar2tgba.cc index 1d5cf8762..5b60a0ecc 100644 --- a/bin/dstar2tgba.cc +++ b/bin/dstar2tgba.cc @@ -48,7 +48,7 @@ #include #include -static const char argp_program_doc[] ="\ +static const char argp_program_doc[] = "\ Convert automata with any acceptance condition into variants of \ Büchi automata.\n\nThis reads automata into any supported format \ (HOA, LBTT, ltl2dstar, never claim) and outputs a \ diff --git a/bin/genaut.cc b/bin/genaut.cc index d7db04d98..26678c588 100644 --- a/bin/genaut.cc +++ b/bin/genaut.cc @@ -1,6 +1,6 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2017-2019, 2022 Laboratoire de Recherche et Développement -// de l'Epita (LRDE). +// Copyright (C) 2017-2019, 2022 Laboratoire de Recherche et +// Développement de l'Epita (LRDE). // // This file is part of Spot, a model checking library. // @@ -43,7 +43,8 @@ using namespace spot; -const char argp_program_doc[] ="Generate ω-automata from predefined patterns."; +static const char argp_program_doc[] = + "Generate ω-automata from predefined patterns."; static const argp_option options[] = { diff --git a/bin/genltl.cc b/bin/genltl.cc index 6393024c2..96d8bd7d3 100644 --- a/bin/genltl.cc +++ b/bin/genltl.cc @@ -45,7 +45,7 @@ using namespace spot; -const char argp_program_doc[] ="\ +static const char argp_program_doc[] = "\ Generate temporal logic formulas from predefined patterns."; // We reuse the values from gen::ltl_pattern_id as option keys. diff --git a/bin/ltl2tgba.cc b/bin/ltl2tgba.cc index ee3d9f777..d4fb2fc17 100644 --- a/bin/ltl2tgba.cc +++ b/bin/ltl2tgba.cc @@ -39,7 +39,7 @@ #include #include -static const char argp_program_doc[] ="\ +static const char argp_program_doc[] = "\ Translate linear-time formulas (LTL/PSL) into various types of automata.\n\n\ By default it will apply all available optimizations to output \ the smallest Transition-based Generalized Büchi Automata, \ diff --git a/bin/ltl2tgta.cc b/bin/ltl2tgta.cc index e3f241385..ab925c7ac 100644 --- a/bin/ltl2tgta.cc +++ b/bin/ltl2tgta.cc @@ -46,7 +46,7 @@ #include #include -const char argp_program_doc[] ="\ +static const char argp_program_doc[] = "\ Translate linear-time formulas (LTL/PSL) into Testing Automata.\n\n\ By default it outputs a transition-based generalized Testing Automaton \ the smallest Transition-based Generalized Büchi Automata, \ diff --git a/bin/ltlcross.cc b/bin/ltlcross.cc index 8e1005db6..0dfa09985 100644 --- a/bin/ltlcross.cc +++ b/bin/ltlcross.cc @@ -1,6 +1,6 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2012-2020, 2022 Laboratoire de Recherche et Développement -// de l'Epita (LRDE). +// Copyright (C) 2012-2020, 2022 Laboratoire de Recherche et +// Développement de l'Epita (LRDE). // // This file is part of Spot, a model checking library. // @@ -69,7 +69,7 @@ #include #include -const char argp_program_doc[] ="\ +static const char argp_program_doc[] = "\ Call several LTL/PSL translators and cross-compare their output to detect \ bugs, or to gather statistics. The list of formulas to use should be \ supplied on standard input, or using the -f or -F options.\v\ diff --git a/bin/ltldo.cc b/bin/ltldo.cc index 705e71105..ffbd4873e 100644 --- a/bin/ltldo.cc +++ b/bin/ltldo.cc @@ -47,7 +47,7 @@ #include #include -const char argp_program_doc[] ="\ +static const char argp_program_doc[] = "\ Run LTL/PSL formulas through another program, performing conversion\n\ of input and output as required."; diff --git a/bin/ltlfilt.cc b/bin/ltlfilt.cc index b74f7bc0c..c9064368d 100644 --- a/bin/ltlfilt.cc +++ b/bin/ltlfilt.cc @@ -59,7 +59,7 @@ #include #include -const char argp_program_doc[] ="\ +static const char argp_program_doc[] = "\ Read a list of formulas and output them back after some optional processing.\v\ Exit status:\n\ 0 if some formulas were output (skipped syntax errors do not count)\n\ diff --git a/bin/ltlsynt.cc b/bin/ltlsynt.cc index 44c55ef54..aaea855a4 100644 --- a/bin/ltlsynt.cc +++ b/bin/ltlsynt.cc @@ -157,7 +157,7 @@ static const struct argp_child children[] = { nullptr, 0, nullptr, 0 } }; -const char argp_program_doc[] = "\ +static const char argp_program_doc[] = "\ Synthesize a controller from its LTL specification.\v\ Exit status:\n\ 0 if all input problems were realizable\n\ diff --git a/bin/randaut.cc b/bin/randaut.cc index 27512c9ce..1ceb82ee0 100644 --- a/bin/randaut.cc +++ b/bin/randaut.cc @@ -1,6 +1,6 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2012-2016, 2018-2020 Laboratoire de Recherche et -// Développement de l'Epita (LRDE). +// Copyright (C) 2012-2016, 2018-2020, 2022 Laboratoire de Recherche +// et Développement de l'Epita (LRDE). // // This file is part of Spot, a model checking library. // @@ -42,7 +42,7 @@ #include -const char argp_program_doc[] = "\ +static const char argp_program_doc[] = "\ Generate random connected automata.\n\n\ The automata are built over the atomic propositions named by PROPS...\n\ or, if N is a nonnegative number, using N arbitrary names.\n\ diff --git a/bin/randltl.cc b/bin/randltl.cc index cded77171..986c437c1 100644 --- a/bin/randltl.cc +++ b/bin/randltl.cc @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2012-2016, 2018-2019 Laboratoire de Recherche +// Copyright (C) 2012-2016, 2018-2019, 2022 Laboratoire de Recherche // et Développement de l'Epita (LRDE). // // This file is part of Spot, a model checking library. @@ -40,7 +40,7 @@ #include #include -const char argp_program_doc[] ="\ +static const char argp_program_doc[] = "\ Generate random temporal logic formulas.\n\n\ The formulas are built over the atomic propositions named by PROPS...\n\ or, if N is a nonnegative number, using N arbitrary names.\v\ diff --git a/bin/spot-x.cc b/bin/spot-x.cc index d1a8f96f6..35f971fd6 100644 --- a/bin/spot-x.cc +++ b/bin/spot-x.cc @@ -24,7 +24,7 @@ #include #include "common_setup.hh" -const char argp_program_doc[] ="\ +static const char argp_program_doc[] = "\ Common fine-tuning options for programs installed with Spot.\n\ \n\ The argument of -x or --extra-options is a comma-separated list of KEY=INT \ diff --git a/bin/spot.cc b/bin/spot.cc index 95ce7063a..c6bad3c70 100644 --- a/bin/spot.cc +++ b/bin/spot.cc @@ -1,6 +1,6 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2013-2018 Laboratoire de Recherche et Développement -// de l'Epita (LRDE). +// Copyright (C) 2013-2018, 2022 Laboratoire de Recherche et +// Développement de l'Epita (LRDE). // // This file is part of Spot, a model checking library. // @@ -24,7 +24,8 @@ #include #include "common_setup.hh" -const char argp_program_doc[] ="Command-line tools installed by Spot."; +static const char argp_program_doc[] = + "Command-line tools installed by Spot."; #define DOC(NAME, TXT) NAME, 0, nullptr, OPTION_DOC | OPTION_NO_USAGE, TXT, 0 diff --git a/tests/ltsmin/modelcheck.cc b/tests/ltsmin/modelcheck.cc index 6e17e8ba2..9c529f0e8 100644 --- a/tests/ltsmin/modelcheck.cc +++ b/tests/ltsmin/modelcheck.cc @@ -1,6 +1,6 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2011-2020 Laboratoire de Recherche et Developpement -// de l'Epita (LRDE) +// Copyright (C) 2011-2020, 2022 Laboratoire de Recherche et +// Developpement de l'Epita (LRDE) // // This file is part of Spot, a model checking library. // @@ -45,7 +45,7 @@ #include #include -const char argp_program_doc[] = +static const char argp_program_doc[] = "Process model and formula to check wether a " "model meets a specification.\v\ Exit status:\n\ From c312a05bbd5b034b23d860fbc7e9b12fabb663d1 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Wed, 26 Oct 2022 10:03:29 +0200 Subject: [PATCH 185/337] do not use id for animating the logo because we remove ids using svgo... * doc/org/spot2.svg, doc/org/spot.css: Animate the verison using a class. --- doc/org/spot.css | 2 +- doc/org/spot2.svg | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/org/spot.css b/doc/org/spot.css index 7bbd0ef39..ca8b12395 100644 --- a/doc/org/spot.css +++ b/doc/org/spot.css @@ -83,7 +83,7 @@ thead tr{background:#ffe35e} .caveat::before{background:#d70079;content:"Caveat";padding:.5ex;position:relative;top:0;left:0;font-weight:bold} .spotlogo{transform-origin:50% 50%;animation-duration:2s;animation-name:animspotlogo} g.spotlogobg{transform-origin:50% 50%;animation-duration:2s;animation-name:animspotlogobg} -g#version{transform-origin:50% 50%;animation-duration:3s;animation-name:animspotlogover} +g.spotlogover{transform-origin:50% 50%;animation-duration:3s;animation-name:animspotlogover} @keyframes animspotlogo{ 0%{transform:rotateY(90deg)} 80%{transform:rotateY(0deg)} diff --git a/doc/org/spot2.svg b/doc/org/spot2.svg index 76b76525f..8d68ba9d3 100644 --- a/doc/org/spot2.svg +++ b/doc/org/spot2.svg @@ -14,7 +14,7 @@ - + From 66aaa115801f668fbcccdc8ba22ab395c563c48b Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Wed, 26 Oct 2022 11:15:39 +0200 Subject: [PATCH 186/337] Release Spot 2.11.2 * NEWS, configure.ac, doc/org/setup.org: Bump version to 2.11.2. --- NEWS | 2 +- configure.ac | 2 +- doc/org/setup.org | 10 +++++----- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/NEWS b/NEWS index bf9e1079e..a6e370af4 100644 --- a/NEWS +++ b/NEWS @@ -1,4 +1,4 @@ -New in spot 2.11.1.dev (not yet released) +New in spot 2.11.2 (2022-10-26) Command-line tools: diff --git a/configure.ac b/configure.ac index 5c160e6cd..8ec99065c 100644 --- a/configure.ac +++ b/configure.ac @@ -21,7 +21,7 @@ # along with this program. If not, see . AC_PREREQ([2.69]) -AC_INIT([spot], [2.11.1.dev], [spot@lrde.epita.fr]) +AC_INIT([spot], [2.11.2], [spot@lrde.epita.fr]) AC_CONFIG_AUX_DIR([tools]) AC_CONFIG_MACRO_DIR([m4]) AM_INIT_AUTOMAKE([1.11 gnu tar-ustar color-tests parallel-tests]) diff --git a/doc/org/setup.org b/doc/org/setup.org index 0be5b364b..bb568e5f1 100644 --- a/doc/org/setup.org +++ b/doc/org/setup.org @@ -1,11 +1,11 @@ #+OPTIONS: H:2 num:nil toc:t html-postamble:nil ^:nil #+EMAIL: spot@lrde.epita.fr #+HTML_LINK_HOME: index.html -#+MACRO: SPOTVERSION 2.11.1 -#+MACRO: LASTRELEASE 2.11.1 -#+MACRO: LASTTARBALL [[http://www.lrde.epita.fr/dload/spot/spot-2.11.1.tar.gz][=spot-2.11.1.tar.gz=]] -#+MACRO: LASTNEWS [[https://gitlab.lre.epita.fr/spot/spot/blob/spot-2-11-1/NEWS][summary of the changes]] -#+MACRO: LASTDATE 2022-10-10 +#+MACRO: SPOTVERSION 2.11.2 +#+MACRO: LASTRELEASE 2.11.2 +#+MACRO: LASTTARBALL [[http://www.lrde.epita.fr/dload/spot/spot-2.11.2.tar.gz][=spot-2.11.2.tar.gz=]] +#+MACRO: LASTNEWS [[https://gitlab.lre.epita.fr/spot/spot/blob/spot-2-11-2/NEWS][summary of the changes]] +#+MACRO: LASTDATE 2022-10-26 #+ATTR_HTML: :id spotlogo [[file:spot2.svg]] From 17a959aa291e33ee62af754c38524c365a9d0e11 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Wed, 26 Oct 2022 11:24:20 +0200 Subject: [PATCH 187/337] Bump version to 2.11.2.dev * NEWS, configure.ac: Here. --- NEWS | 4 ++++ configure.ac | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index a6e370af4..f6cba13cc 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,7 @@ +New in spot 2.11.2.dev (not yet released) + + Nothing yet. + New in spot 2.11.2 (2022-10-26) Command-line tools: diff --git a/configure.ac b/configure.ac index 8ec99065c..236063093 100644 --- a/configure.ac +++ b/configure.ac @@ -21,7 +21,7 @@ # along with this program. If not, see . AC_PREREQ([2.69]) -AC_INIT([spot], [2.11.2], [spot@lrde.epita.fr]) +AC_INIT([spot], [2.11.2.dev], [spot@lrde.epita.fr]) AC_CONFIG_AUX_DIR([tools]) AC_CONFIG_MACRO_DIR([m4]) AM_INIT_AUTOMAKE([1.11 gnu tar-ustar color-tests parallel-tests]) From fafe40c530d26aa10cfd5c5a768a7f2fb0a560ee Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Fri, 4 Nov 2022 17:11:51 +0100 Subject: [PATCH 188/337] fix namespace for exception errors * spot/priv/satcommon.cc, spot/twaalgos/dtbasat.cc, spot/twaalgos/dtwasat.cc: When setting exception on std::ofstream, use ofstream::failbit and ofstream::badbit instead of ifstream::failbit and ifstream::badbit. --- spot/priv/satcommon.cc | 4 ++-- spot/twaalgos/dtbasat.cc | 4 ++-- spot/twaalgos/dtwasat.cc | 4 ++-- 3 files changed, 6 insertions(+), 6 deletions(-) diff --git a/spot/priv/satcommon.cc b/spot/priv/satcommon.cc index 21d75eee1..aec73d104 100644 --- a/spot/priv/satcommon.cc +++ b/spot/priv/satcommon.cc @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2013-2019 Laboratoire de Recherche et Développement +// Copyright (C) 2013-2019, 2022 Laboratoire de Recherche et Développement // de l'Epita (LRDE). // // This file is part of Spot, a model checking library. @@ -167,7 +167,7 @@ namespace spot return; std::ofstream out(log, std::ios_base::ate | std::ios_base::app); - out.exceptions(std::ifstream::failbit | std::ifstream::badbit); + out.exceptions(std::ofstream::failbit | std::ofstream::badbit); if (out.tellp() == 0) out << ("input.states,target.states,reachable.states,edges,transitions," diff --git a/spot/twaalgos/dtbasat.cc b/spot/twaalgos/dtbasat.cc index eb39a69b6..b2147ebb4 100644 --- a/spot/twaalgos/dtbasat.cc +++ b/spot/twaalgos/dtbasat.cc @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2013-2018, 2021 Laboratoire de Recherche et +// Copyright (C) 2013-2018, 2021, 2022 Laboratoire de Recherche et // Développement de l'Epita. // // This file is part of Spot, a model checking library. @@ -585,7 +585,7 @@ namespace spot #if TRACE std::fstream out("dtba-sat.dbg", std::ios_base::trunc | std::ios_base::out); - out.exceptions(std::ifstream::failbit | std::ifstream::badbit); + out.exceptions(std::ofstream::failbit | std::ofstream::badbit); #endif std::set acc_states; std::set seen_trans; diff --git a/spot/twaalgos/dtwasat.cc b/spot/twaalgos/dtwasat.cc index 670a9ffc8..25a299154 100644 --- a/spot/twaalgos/dtwasat.cc +++ b/spot/twaalgos/dtwasat.cc @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2013-2021 Laboratoire de Recherche +// Copyright (C) 2013-2022 Laboratoire de Recherche // et Développement de l'Epita. // // This file is part of Spot, a model checking library. @@ -864,7 +864,7 @@ namespace spot #if TRACE std::fstream out("dtwa-sat.dbg", std::ios_base::trunc | std::ios_base::out); - out.exceptions(std::ifstream::failbit | std::ifstream::badbit); + out.exceptions(std::ofstream::failbit | std::ofstream::badbit); #endif std::map state_acc; std::set seen_trans; From 5c5133348eaf774b7be88f01327406d68406ecba Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Fri, 4 Nov 2022 18:20:29 +0100 Subject: [PATCH 189/337] mealy: improve error reporting * spot/twaalgos/mealy_machine.cc: Add more exceptions. * tests/python/except.py: Test them. --- spot/twaalgos/mealy_machine.cc | 76 +++++++++++++++++++++------------- tests/python/except.py | 38 +++++++++++++++++ 2 files changed, 86 insertions(+), 28 deletions(-) diff --git a/spot/twaalgos/mealy_machine.cc b/spot/twaalgos/mealy_machine.cc index 3635e6334..9cdae279e 100644 --- a/spot/twaalgos/mealy_machine.cc +++ b/spot/twaalgos/mealy_machine.cc @@ -100,7 +100,8 @@ namespace , f{std::fopen(name.c_str(), "a")} { if (!f) - throw std::runtime_error("File could not be oppened for writing."); + throw std::runtime_error("`" + name + + "' could not be oppened for writing."); } ~fwrapper() { @@ -120,21 +121,36 @@ namespace namespace spot { + static bdd + ensure_mealy(const char* function_name, + const const_twa_graph_ptr& m) + { + if (SPOT_UNLIKELY(!m->acc().is_t())) + throw std::runtime_error(std::string(function_name) + + "(): Mealy machines must have " + "true acceptance condition"); + bdd* out = m->get_named_prop("synthesis-outputs"); + if (SPOT_UNLIKELY(!out)) + throw std::runtime_error(std::string(function_name) + + "(): \"synthesis-outputs\" not defined"); + return *out; + } + bool is_mealy(const const_twa_graph_ptr& m) { if (!m->acc().is_t()) - { - trace << "is_mealy(): Mealy machines must have " - "true acceptance condition.\n"; - return false; - } + { + trace << "is_mealy(): Mealy machines must have " + "true acceptance condition.\n"; + return false; + } if (!m->get_named_prop("synthesis-outputs")) - { - trace << "is_mealy(): \"synthesis-outputs\" not found!\n"; - return false; - } + { + trace << "is_mealy(): \"synthesis-outputs\" not found!\n"; + return false; + } return true; } @@ -252,9 +268,7 @@ namespace spot void split_separated_mealy_here(const twa_graph_ptr& m) { - assert(is_mealy(m)); - - auto output_bdd = get_synthesis_outputs(m); + bdd output_bdd = ensure_mealy("split_separated_mealy_here", m); struct dst_cond_color_t { @@ -323,10 +337,10 @@ namespace spot twa_graph_ptr split_separated_mealy(const const_twa_graph_ptr& m) { - assert(is_mealy((m))); + bdd outputs = ensure_mealy("split_separated_mealy", m); auto m2 = make_twa_graph(m, twa::prop_set::all()); m2->copy_acceptance_of(m); - set_synthesis_outputs(m2, get_synthesis_outputs(m)); + set_synthesis_outputs(m2, outputs); split_separated_mealy_here(m2); return m2; } @@ -767,7 +781,7 @@ namespace spot twa_graph_ptr reduce_mealy(const const_twa_graph_ptr& mm, bool output_assignment) { - assert(is_mealy(mm)); + bdd outputs = ensure_mealy("reduce_mealy", mm); if (mm->get_named_prop>("state-player")) throw std::runtime_error("reduce_mealy(): " "Only works on unsplit machines.\n"); @@ -775,7 +789,7 @@ namespace spot auto mmc = make_twa_graph(mm, twa::prop_set::all()); mmc->copy_ap_of(mm); mmc->copy_acceptance_of(mm); - set_synthesis_outputs(mmc, get_synthesis_outputs(mm)); + set_synthesis_outputs(mmc, outputs); reduce_mealy_here(mmc, output_assignment); @@ -785,7 +799,7 @@ namespace spot void reduce_mealy_here(twa_graph_ptr& mm, bool output_assignment) { - assert(is_mealy(mm)); + ensure_mealy("reduce_mealy_here", mm); // Only consider infinite runs mm->purge_dead_states(); @@ -902,6 +916,8 @@ namespace // Writing also "flushes" void write() { + if (!sat_csv_file) + return; auto f = [](std::ostream& o, auto& v, bool sep = true) { if (v >= 0) @@ -910,8 +926,6 @@ namespace o.put(','); v = -1; }; - if (!sat_csv_file) - return; auto& out = *sat_csv_file; if (out.tellp() == 0) @@ -3786,7 +3800,7 @@ namespace spot twa_graph_ptr minimize_mealy(const const_twa_graph_ptr& mm, int premin) { - assert(is_mealy(mm)); + bdd outputs = ensure_mealy("minimize_mealy", mm); satprob_info si(sat_instance_name); si.task = "presat"; @@ -3916,7 +3930,7 @@ namespace spot // Set state players! if (!minmachine) return early_exit(); - set_synthesis_outputs(minmachine, get_synthesis_outputs(mm)); + set_synthesis_outputs(minmachine, outputs); si.done=1; si.n_min_states = minmachine->num_states(); @@ -3931,17 +3945,23 @@ namespace spot minimize_mealy(const const_twa_graph_ptr& mm, synthesis_info& si) { - if ((si.minimize_lvl < 3) || (5 < si.minimize_lvl)) - throw std::runtime_error("Invalid option"); + if ((si.minimize_lvl < 3) || (si.minimize_lvl > 5)) + throw std::runtime_error("minimize_mealy(): " + "minimize_lvl should be between 3 and 5."); std::string csvfile = si.opt.get_str("satlogcsv"); std::string dimacsfile = si.opt.get_str("satlogdimacs"); if (!csvfile.empty()) - sat_csv_file - = std::make_unique(csvfile, - std::ios_base::ate - | std::ios_base::app); + { + sat_csv_file = std::make_unique + (csvfile, std::ios_base::ate | std::ios_base::app); + if (!*sat_csv_file) + throw std::runtime_error("could not open `" + csvfile + + "' for writing"); + sat_csv_file->exceptions(std::ofstream::failbit + | std::ofstream::badbit); + } if (!dimacsfile.empty()) sat_dimacs_file = std::make_unique(dimacsfile); diff --git a/tests/python/except.py b/tests/python/except.py index 508ffd7f9..34aa61ad2 100644 --- a/tests/python/except.py +++ b/tests/python/except.py @@ -321,3 +321,41 @@ except RuntimeError as e: tc.assertIn("already registered", se) else: report_missing_exception() + + +try: + spot.minimize_mealy(a, 100) +except RuntimeError as e: + se = str(e) + tc.assertIn("minimize_mealy", se) + tc.assertIn("minimize_lvl", se) +else: + report_missing_exception() + +opt = spot.synthesis_info() +opt.minimize_lvl = 3 +try: + spot.minimize_mealy(a, opt) +except RuntimeError as e: + se = str(e) + tc.assertIn("minimize_mealy", se) + tc.assertIn("synthesis-output", se) + +spot.set_synthesis_outputs(a, buddy.bdd_ithvar(a.register_ap("b"))) +filename = "/THIS-FILE/SHOULD/NOT/EXIST" +opt.opt.set_str("satlogdimacs", filename) +try: + spot.minimize_mealy(a, opt) +except RuntimeError as e: + tc.assertIn(filename, str(e)) +else: + report_missing_exception() + +opt.opt.set_str("satlogdimacs", "") +opt.opt.set_str("satlogcsv", filename) +try: + spot.minimize_mealy(a, opt) +except RuntimeError as e: + tc.assertIn(filename, str(e)) +else: + report_missing_exception() From 6dc740184c154bde0f4065c6c9a0d1322931ebf3 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Mon, 7 Nov 2022 09:37:26 +0100 Subject: [PATCH 190/337] * tests/sanity/style.test: Fix recent grep warnings. --- tests/sanity/style.test | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/sanity/style.test b/tests/sanity/style.test index 85ef359b0..325ebe78d 100755 --- a/tests/sanity/style.test +++ b/tests/sanity/style.test @@ -40,7 +40,7 @@ GREP=grep # Get some help from GNU grep. if (grep --color=auto -n --version)>/dev/null 2>&1; then GREP="$GREP --color=auto -n" - GREP_COLOR='1;31' + GREP_COLOR='mt=1;31' export GREP_COLOR fi @@ -295,7 +295,7 @@ for dir in "$TOP/spot" "$TOP/bin" "$TOP/tests"; do fi # we want catch (const reftype&) or catch (...) - $GREP 'catch *([^.]' $tmp | $GREP -v 'const.*\&' && + $GREP 'catch *([^.]' $tmp | $GREP -v 'const.*&' && diag 'Always capture exceptions by const reference.' case $file in From b36cee06a1bb8630aa6d9b2070d2e18514e1ef83 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Mon, 7 Nov 2022 16:24:33 +0100 Subject: [PATCH 191/337] adjust to Swig 4.1.0 * python/spot/__init__.py: Add flatnested versions of some static methods. * spot/twa/acc.hh: Hide && version of & and |, causing trouble to swig. * tests/python/_synthesis.ipynb, tests/python/synthesis.ipynb: Upgrade expected type names. * tests/python/ipnbdoctest.py: Adjust for difference between 4.0 and 4.1. --- python/spot/__init__.py | 19 +++++++++++-------- spot/twa/acc.hh | 4 ++++ tests/python/_synthesis.ipynb | 2 +- tests/python/ipnbdoctest.py | 5 +++++ tests/python/synthesis.ipynb | 12 ++++++------ 5 files changed, 27 insertions(+), 15 deletions(-) diff --git a/python/spot/__init__.py b/python/spot/__init__.py index 01210c824..edbf4a4e6 100644 --- a/python/spot/__init__.py +++ b/python/spot/__init__.py @@ -104,19 +104,22 @@ def setup(**kwargs): os.environ['SPOT_DOTDEFAULT'] = d -# In version 3.0.2, Swig puts strongly typed enum in the main -# namespace without prefixing them. Latter versions fix this. So we -# can remove for following hack once 3.0.2 is no longer used in our -# build farm. -if 'op_ff' not in globals(): +# Swig versions prior to 4.1.0 export formula.X as formula_X as well, +# for all operators. Swig 4.1.0 stops doing this, breaking some +# existing code. +if 'formula_ff' not in globals(): for i in ('ff', 'tt', 'eword', 'ap', 'Not', 'X', 'F', 'G', 'Closure', 'NegClosure', 'NegClosureMarked', 'Xor', 'Implies', 'Equiv', 'U', 'R', 'W', 'M', 'EConcat', 'EConcatMarked', 'UConcat', 'Or', 'OrRat', 'And', 'AndRat', 'AndNLM', 'Concat', - 'Fusion', 'Star', 'FStar'): - globals()['op_' + i] = globals()[i] - del globals()[i] + 'Fusion', 'Star', 'FStar', 'nested_unop_range', + 'sugar_goto', 'sugar_equal', 'sugar_delay', 'unop', + 'binop', 'bunop', 'multop', 'first_match', 'unbounded'): + globals()['formula_' + i] = formula.__dict__[i].__func__ +if 'trival_maybe' not in globals(): + for i in ('maybe',): + globals()['trival_' + i] = trival.__dict__[i].__func__ # Global BDD dict so that we do not have to create one in user code. diff --git a/spot/twa/acc.hh b/spot/twa/acc.hh index 905f5c40a..1c460cfc4 100644 --- a/spot/twa/acc.hh +++ b/spot/twa/acc.hh @@ -1011,6 +1011,7 @@ namespace spot return res; } +#ifndef SWIG /// \brief Conjunct the current condition with \a r. acc_code operator&(acc_code&& r) const { @@ -1018,6 +1019,7 @@ namespace spot res &= r; return res; } +#endif // SWIG /// \brief Disjunct the current condition in place with \a r. acc_code& operator|=(const acc_code& r) @@ -1106,6 +1108,7 @@ namespace spot return *this; } +#ifndef SWIG /// \brief Disjunct the current condition with \a r. acc_code operator|(acc_code&& r) const { @@ -1113,6 +1116,7 @@ namespace spot res |= r; return res; } +#endif // SWIG /// \brief Disjunct the current condition with \a r. acc_code operator|(const acc_code& r) const diff --git a/tests/python/_synthesis.ipynb b/tests/python/_synthesis.ipynb index 4c203a86e..2d92236b7 100644 --- a/tests/python/_synthesis.ipynb +++ b/tests/python/_synthesis.ipynb @@ -6346,7 +6346,7 @@ "\n" ], "text/plain": [ - " >" + " *' at 0x7fbccc33adb0> >" ] }, "execution_count": 28, diff --git a/tests/python/ipnbdoctest.py b/tests/python/ipnbdoctest.py index f6ce3562e..47b73f901 100755 --- a/tests/python/ipnbdoctest.py +++ b/tests/python/ipnbdoctest.py @@ -101,6 +101,11 @@ def canonicalize(s, type, ignores): # %%file writes `Writing`, or `Overwriting` if the file exists. s = re.sub(r'^Overwriting ', 'Writing ', s) + # Swig 4.1.0 fixed an ordering issue with how types are printed. + # aig_ptr is expected to be printed as shared_ptr, but prior + # Swig version did not do that. + s = re.sub(r'spot::aig_ptr ', 'std::shared_ptr< spot::aig > ', s) + # SVG generated by graphviz may put note at different positions # depending on the graphviz build. Let's just strip anything that # look like a position. diff --git a/tests/python/synthesis.ipynb b/tests/python/synthesis.ipynb index 54da20ef7..ba1b562cc 100644 --- a/tests/python/synthesis.ipynb +++ b/tests/python/synthesis.ipynb @@ -2601,7 +2601,7 @@ "\n" ], "text/plain": [ - " >" + " *' at 0x7f0e584deae0> >" ] }, "metadata": {}, @@ -3256,7 +3256,7 @@ "\n" ], "text/plain": [ - " >" + " *' at 0x7f0e5855c9f0> >" ] }, "metadata": {}, @@ -3385,7 +3385,7 @@ "\n" ], "text/plain": [ - " >" + " *' at 0x7f0e5855c900> >" ] }, "metadata": {}, @@ -3801,7 +3801,7 @@ "\n" ], "text/plain": [ - " >" + " *' at 0x7f0e584def00> >" ] }, "metadata": {}, @@ -3979,7 +3979,7 @@ "\n" ], "text/plain": [ - " >" + " *' at 0x7f0e5855c930> >" ] }, "metadata": {}, @@ -4159,7 +4159,7 @@ "\n" ], "text/plain": [ - " >" + " *' at 0x7f0e584def90> >" ] }, "metadata": {}, From 0f4f7ec287dc5d0e7b93bf1171aa5c7214bc51b1 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Tue, 8 Nov 2022 15:51:27 +0100 Subject: [PATCH 192/337] * debian/copyright: Fix download URL. --- debian/copyright | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/debian/copyright b/debian/copyright index 9c4653c28..792afcec1 100644 --- a/debian/copyright +++ b/debian/copyright @@ -1,6 +1,6 @@ Format: http://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ Upstream-Name: spot -Source: http://spot.lrde.epita.fr/dload/spot/ +Source: http://www.lrde.epita.fr/dload/spot/ Files: * Copyright: 2003-2007 Laboratoire d'Informatique de Paris 6 (LIP6) From a6c65dff8d026bdacf373fb738b9adb9a1b71fd5 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Tue, 8 Nov 2022 15:52:02 +0100 Subject: [PATCH 193/337] misc Doxygen fixes * spot/misc/satsolver.hh, spot/tl/formula.hh, spot/twaalgos/hoa.hh, spot/twaalgos/synthesis.hh, spot/twaalgos/zlktree.hh, spot/twacube_algos/convert.hh: Typos in Doxygen comments. --- spot/misc/satsolver.hh | 6 +++--- spot/tl/formula.hh | 16 ++++++++-------- spot/twaalgos/hoa.hh | 4 ++-- spot/twaalgos/synthesis.hh | 6 +++--- spot/twaalgos/zlktree.hh | 2 +- spot/twacube_algos/convert.hh | 6 +++--- 6 files changed, 20 insertions(+), 20 deletions(-) diff --git a/spot/misc/satsolver.hh b/spot/misc/satsolver.hh index 03a75fa02..3b5bedccd 100644 --- a/spot/misc/satsolver.hh +++ b/spot/misc/satsolver.hh @@ -1,6 +1,6 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2013, 2017-2018, 2020 Laboratoire de Recherche et -// Développement de l'Epita. +// Copyright (C) 2013, 2017-2018, 2020, 2022 Laboratoire de Recherche +// et Développement de l'Epita. // // This file is part of Spot, a model checking library. // @@ -88,7 +88,7 @@ namespace spot /// \brief Add a single lit. to the current clause. void add(int v); - /// \breif Get the current number of clauses. + /// \brief Get the current number of clauses. int get_nb_clauses() const; /// \brief Get the current number of variables. diff --git a/spot/tl/formula.hh b/spot/tl/formula.hh index c52ed3e39..d01b8379c 100644 --- a/spot/tl/formula.hh +++ b/spot/tl/formula.hh @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2015-2021 Laboratoire de Recherche et Développement +// Copyright (C) 2015-2022 Laboratoire de Recherche et Développement // de l'Epita (LRDE). // // This file is part of Spot, a model checking library. @@ -1227,12 +1227,12 @@ namespace spot return bunop(op::Name, std::move(f), min, max); \ } #endif - /// \brief Create SERE for f[*min..max] + /// \brief Create SERE for `f[*min..max]` /// @{ SPOT_DEF_BUNOP(Star); /// @} - /// \brief Create SERE for f[:*min..max] + /// \brief Create SERE for `f[:*min..max]` /// /// This operator is a generalization of the (+) operator /// defined by Dax et al. \cite dax.09.atva @@ -1259,24 +1259,24 @@ namespace spot f.ptr_->clone())); } - /// \brief Create a SERE equivalent to b[->min..max] + /// \brief Create a SERE equivalent to `b[->min..max]` /// /// The operator does not exist: it is handled as syntactic sugar /// by the parser and the printer. This function is used by the /// parser to create the equivalent SERE. static formula sugar_goto(const formula& b, unsigned min, unsigned max); - /// Create the SERE b[=min..max] + /// \brief Create the SERE `b[=min..max]` /// /// The operator does not exist: it is handled as syntactic sugar /// by the parser and the printer. This function is used by the /// parser to create the equivalent SERE. static formula sugar_equal(const formula& b, unsigned min, unsigned max); - /// Create the SERE a ##[n:m] b + /// \brief Create the SERE `a ##[n:m] b` /// - /// This ##[n:m] operator comes from SVA. When n=m, it is simply - /// written ##n. + /// This `##[n:m]` operator comes from SVA. When n=m, it is simply + /// written `##n`. /// /// The operator does not exist in Spot it is handled as syntactic /// sugar by the parser. This function is used by the parser to diff --git a/spot/twaalgos/hoa.hh b/spot/twaalgos/hoa.hh index 74e97b567..441b9ed16 100644 --- a/spot/twaalgos/hoa.hh +++ b/spot/twaalgos/hoa.hh @@ -95,7 +95,7 @@ namespace spot /// registered in the automaton is not only ignored, but also /// removed from the alias list stored in the automaton. /// - /// The \a or_str, \a and_str, and \ap_printer arguments are + /// The \a or_str, \a and_str, and \a ap_printer arguments are /// used to print operators OR, AND, and to print atomic propositions /// that are not aliases. \a lpar_str and \a rpar_str are used /// to group conjuncts that appear in a disjunction. @@ -119,7 +119,7 @@ namespace spot /// /// - If an alias A exists for \a label, `"@A"` is returned. /// - /// - If an alias A exists for the negation of \a label, `"!@A` + /// - If an alias A exists for the negation of \a label, `"!@A"` /// is returned. /// /// - If \a label is true or false, `true_str` or `false_str` diff --git a/spot/twaalgos/synthesis.hh b/spot/twaalgos/synthesis.hh index 115b8097c..b1b7fdf1d 100644 --- a/spot/twaalgos/synthesis.hh +++ b/spot/twaalgos/synthesis.hh @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2020-2021 Laboratoire de Recherche et +// Copyright (C) 2020-2022 Laboratoire de Recherche et // Développement de l'Epita (LRDE). // // This file is part of Spot, a model checking library. @@ -36,8 +36,8 @@ namespace spot /// p -- cond --> q cond in 2^2^AP /// into a set of transitions of the form /// p -- {a} --> (p,a) -- o --> q - /// for each a in cond \cap 2^2^I - /// and where o = (cond & a) \cap 2^2^(O) + /// for each a in cond ∪ 2^2^I + /// and where o = (cond & a) ∪ 2^2^O. /// /// By definition, the states p are deterministic, /// only the states of the form diff --git a/spot/twaalgos/zlktree.hh b/spot/twaalgos/zlktree.hh index d210033e3..6d8b3270c 100644 --- a/spot/twaalgos/zlktree.hh +++ b/spot/twaalgos/zlktree.hh @@ -481,7 +481,7 @@ namespace spot /// /// If \a colored is set, each output transition will have exactly /// one color, and the output automaton will use at most n+1 colors - /// if the input has n colors. If \colored is unsed (the default), + /// if the input has n colors. If \a colored is unsed (the default), /// output transitions will use at most one color, and output /// automaton will use at most n colors. /// diff --git a/spot/twacube_algos/convert.hh b/spot/twacube_algos/convert.hh index f21aaec3f..ba739f470 100644 --- a/spot/twacube_algos/convert.hh +++ b/spot/twacube_algos/convert.hh @@ -1,6 +1,6 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2015, 2016, 2020 Laboratoire de Recherche et Developpement de -// l'Epita (LRDE). +// Copyright (C) 2015, 2016, 2020, 2022 Laboratoire de Recherche et +// Developpement de l'Epita (LRDE). // // This file is part of Spot, a model checking library. // @@ -51,7 +51,7 @@ namespace spot twa_to_twacube(spot::const_twa_graph_ptr aut); /// \brief Convert a twacube into a twa. - /// When \d is specified, the BDD_dict in parameter is used rather than + /// When \a d is specified, the BDD_dict in parameter is used rather than /// creating a new one. SPOT_API spot::twa_graph_ptr twacube_to_twa(spot::twacube_ptr twacube, From f2c65ea5579c456e508110274550794e3caf0390 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Tue, 15 Nov 2022 16:59:21 +0100 Subject: [PATCH 194/337] simplify: set exprop=false during containment checks For issue #521, reported by Jacopo Binchi. * spot/tl/simplify.cc: Here. * tests/core/521.test: New test case. * tests/Makefile.am: Add it. * NEWS: Mention it. * THANKS: Add Jacopo Binchi. --- NEWS | 6 ++++- THANKS | 1 + spot/tl/simplify.cc | 6 ++--- tests/Makefile.am | 1 + tests/core/521.test | 64 +++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 74 insertions(+), 4 deletions(-) create mode 100755 tests/core/521.test diff --git a/NEWS b/NEWS index f6cba13cc..1af76aed4 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,10 @@ New in spot 2.11.2.dev (not yet released) - Nothing yet. + Bug fixes: + + - Automata-based implication checks, used to simplify formulas where + slower than necessary because the translator was configured to + favor determinism unnecessarily. (Issue #521.) New in spot 2.11.2 (2022-10-26) diff --git a/THANKS b/THANKS index db74e14b8..2b054666c 100644 --- a/THANKS +++ b/THANKS @@ -23,6 +23,7 @@ Gerard J. Holzmann Hashim Ali Heikki Tauriainen Henrich Lauko +Jacopo Binchi Jan Strejček Jean-Michel Couvreur Jean-Michel Ilié diff --git a/spot/tl/simplify.cc b/spot/tl/simplify.cc index 3a2433197..cbc5857c5 100644 --- a/spot/tl/simplify.cc +++ b/spot/tl/simplify.cc @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2011-2021 Laboratoire de Recherche et Developpement +// Copyright (C) 2011-2022 Laboratoire de Recherche et Developpement // de l'Epita (LRDE). // // This file is part of Spot, a model checking library. @@ -64,14 +64,14 @@ namespace spot } tl_simplifier_cache(const bdd_dict_ptr& d) - : dict(d), lcc(d, true, true, false, false) + : dict(d), lcc(d, false, true, false, false) { } tl_simplifier_cache(const bdd_dict_ptr& d, const tl_simplifier_options& opt) : dict(d), options(opt), - lcc(d, true, true, false, false, opt.containment_max_states) + lcc(d, false, true, false, false, opt.containment_max_states) { options.containment_checks |= options.containment_checks_stronger; options.event_univ |= options.favor_event_univ; diff --git a/tests/Makefile.am b/tests/Makefile.am index 71d6a852f..4c2fe830c 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -222,6 +222,7 @@ TESTS_misc = \ TESTS_twa = \ core/385.test \ + core/521.test \ core/acc.test \ core/acc2.test \ core/bdddict.test \ diff --git a/tests/core/521.test b/tests/core/521.test new file mode 100755 index 000000000..002ab1ca2 --- /dev/null +++ b/tests/core/521.test @@ -0,0 +1,64 @@ +#!/bin/sh +# -*- coding: utf-8 -*- +# Copyright (C) 2022 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 . + +. ./defs + +set -e + +# For issue #521. + +# The following formula used to take hours or days to translate with +# default settings (nobody was patient enough to wait) because +# automata-based containment checks were run to exprop=1. + +cat >formula.ltl <<'EOF' +!a & !b & !c & !d & e & f & G(g & h & i & j & ((!c & !d) | (!c & d) | +(c & !d) | (c & d)) & ((!a & !b) | (!a & b) | (a & !b) | (a & b)) & +(k -> !l) & (f -> k) & (l -> !k) & (f -> !l) & (l -> !f) & (m -> !n) & +(m -> o) & (p -> !q) & (m -> !r) & (p -> !m) & (s -> !e) & (r -> !s) & +(e -> n) & (m -> !t) & (t -> !s) & (q -> u) & (o -> !t) & (m -> !p) & +(u -> o) & (p -> !v) & (q -> v) & (n -> w) & (x -> !s) & (u -> !t) & +(p -> w) & (u -> !p) & (t -> n) & (m -> !x) & (q -> !e) & (p -> !u) & +(s -> !n) & (s -> o) & (s -> m) & (v -> !e) & (x -> n) & (s -> !r) & +(e -> x) & (e -> !q) & (n -> r) & (w -> !s) & (m -> q) & (s -> !t) & +(u -> !x) & (e -> p) & (e -> !m) & (s -> !p) & (p -> r) & (e -> !o) & +(e -> !v) & (t -> x) & (q -> o) & (q -> !n) & (t -> !q) & (r -> !m) & +(t -> p) & (t -> !m) & (s -> !x) & (v -> o) & (e -> w) & (n -> !s) & +(q -> !t) & (t -> !o) & (x -> !q) & (e -> !u) & (q -> !p) & (t -> !v) & +(p -> !s) & (m -> u) & (x -> !m) & (v -> !t) & (s -> q) & (v -> !p) & +(m -> v) & (r -> w) & (t -> w) & (e -> t) & (e -> r) & (q -> !x) & +(t -> !u) & (p -> n) & (m -> !e) & (u -> v) & (x -> w) & (o -> !e) & +(x -> !u) & (s -> !w) & (u -> !e) & (t -> r) & (s -> u) & (e -> !s) & +(s -> v) & (n -> !q) & (x -> r) & (n -> !m) & (p -> x) & ((!a & !b & +!c & !d) | (!a & b & !c & d) | (a & !b & c & !d) | (a & b & c & d)) & +((!c & !d & k & o) -> X(!c & d)) & ((!c & !d & l & v & !(k & o)) -> +X(!c & d)) & ((!c & !d) -> ((!(k & o) & !(l & v)) -> X(!c & !d))) & +((!c & d & k & t) -> X(!c & !d)) & ((!c & d & l & p & !(k & t)) -> X(!c +& !d)) & ((!c & d & k & u & !(l & p & !(k & t))) -> X(c & !d)) & ((!c & +d & l & q & !(k & u & !(l & p & !(k & t)))) -> X(c & !d)) & ((!c & d) -> +((!(k & t) & !(l & p) & !(k & u) & !(l & q)) -> X(!c & d))) & ((c & !d +& k & x) -> X(!c & d)) & ((c & !d & l & n & !(k & x)) -> X(!c & d)) & +((c & !d & k & m & !(l & n & !(k & x))) -> X(c & d)) & ((c & !d & l & +s & !(k & m & !(l & n & !(k & x)))) -> X(c & d)) & ((c & !d) -> ((!(k & +x) & !(l & n) & !(k & m) & !(l & s)) -> X(c & !d))) & ((c & d & k & r) +-> X(c & !d)) & ((c & d & l & w & !(k & r)) -> X(c & !d)) & ((c & d) -> +((!(k & r) & !(l & w)) -> X(c & d)))) +EOF +test 5 = `tr -d "\r\n" < formula.ltl | ltl2tgba --stats=%s` From 843c4cdb91685e108d0d7e945512e4ccf9a8aea2 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Tue, 15 Nov 2022 17:27:10 +0100 Subject: [PATCH 195/337] translate, simplify: limit containment checks of n-ary operators Fixes #521. * spot/tl/simplify.cc, spot/tl/simplify.hh, spot/twaalgos/translate.cc, spot/twaalgos/translate.hh: Add an option to limit automata-based implication checks of n-ary operators when too many operands are used. Defaults to 16. * bin/spot-x.cc, NEWS, doc/tl/tl.tex: Document it. * tests/core/bdd.test: Disable the limit for this test. --- NEWS | 5 +++++ bin/spot-x.cc | 3 +++ doc/tl/tl.tex | 6 ++++++ spot/tl/simplify.cc | 10 ++++++---- spot/tl/simplify.hh | 5 ++++- spot/twaalgos/translate.cc | 5 +++-- spot/twaalgos/translate.hh | 3 ++- tests/core/bdd.test | 7 ++++--- 8 files changed, 33 insertions(+), 11 deletions(-) diff --git a/NEWS b/NEWS index 1af76aed4..f19987c2c 100644 --- a/NEWS +++ b/NEWS @@ -6,6 +6,11 @@ New in spot 2.11.2.dev (not yet released) slower than necessary because the translator was configured to favor determinism unnecessarily. (Issue #521.) + - Automata-based implication checks for f&g and f|g could be + very slow when those n-ary operator had two many arguments. + They have been limited to 16 operands, but this value can be changed + with option -x tls-max-ops=N. (Issue #521 too.) + New in spot 2.11.2 (2022-10-26) Command-line tools: diff --git a/bin/spot-x.cc b/bin/spot-x.cc index 35f971fd6..1edb3f54e 100644 --- a/bin/spot-x.cc +++ b/bin/spot-x.cc @@ -47,6 +47,9 @@ depends on the --low, --medium, or --high settings.") }, { DOC("tls-max-states", "Maximum number of states of automata involved in automata-based \ implication checks for formula simplifications. Defaults to 64.") }, + { DOC("tls-max-ops", + "Maximum number of operands in n-ary opertors (or, and) on which \ +implication-based simplifications are attempted. Defaults to 16.") }, { nullptr, 0, nullptr, 0, "Translation options:", 0 }, { DOC("ltl-split", "Set to 0 to disable the translation of automata \ as product or sum of subformulas.") }, diff --git a/doc/tl/tl.tex b/doc/tl/tl.tex index b6268d9cd..62a35635f 100644 --- a/doc/tl/tl.tex +++ b/doc/tl/tl.tex @@ -1926,6 +1926,12 @@ Many of the above rules were collected from the literature~\cite{somenzi.00.cav,tauriainen.03.tr,babiak.12.tacas} and sometimes generalized to support operators such as $\M$ and $\W$. +The first six rules, about n-ary operators $\AND$ and $\OR$, are +implemented for $n$ operands by testing each operand against all +other. To prevent the complexity to escalate, this is only performed +with up to 16 operands. That value can be changed in +``\verb|tl_simplifier_options::containment_max_ops|''. + The following rules mix implication-based checks with formulas that are pure eventualities ($e$) or that are purely universal ($u$). diff --git a/spot/tl/simplify.cc b/spot/tl/simplify.cc index cbc5857c5..4eac97282 100644 --- a/spot/tl/simplify.cc +++ b/spot/tl/simplify.cc @@ -2507,8 +2507,11 @@ namespace spot unsigned mos = mo.size(); if ((opt_.synt_impl | opt_.containment_checks) - && mo.is(op::Or, op::And)) + && mo.is(op::Or, op::And) + && (opt_.containment_max_ops == 0 + || opt_.containment_max_ops >= mos)) { + bool is_and = mo.is(op::And); // Do not merge these two loops, as rewritings from the // second loop could prevent rewritings from the first one // to trigger. @@ -2520,7 +2523,6 @@ namespace spot // if fo => !fi, then fi & fo = false // if !fi => fo, then fi | fo = true // if !fo => fi, then fi | fo = true - bool is_and = mo.is(op::And); if (c_->implication_neg(fi, fo, is_and) || c_->implication_neg(fo, fi, is_and)) return recurse(is_and ? formula::ff() : formula::tt()); @@ -2531,8 +2533,8 @@ namespace spot formula fo = mo.all_but(i); // if fi => fo, then fi | fo = fo // if fo => fi, then fi & fo = fo - if ((mo.is(op::Or) && c_->implication(fi, fo)) - || (mo.is(op::And) && c_->implication(fo, fi))) + if (((!is_and) && c_->implication(fi, fo)) + || (is_and && c_->implication(fo, fi))) { // We are about to pick fo, but hold on! // Maybe we actually have fi <=> fo, in diff --git a/spot/tl/simplify.hh b/spot/tl/simplify.hh index e5838544d..ec102a205 100644 --- a/spot/tl/simplify.hh +++ b/spot/tl/simplify.hh @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2011-2017, 2019, 2020 Laboratoire de Recherche et Developpement +// Copyright (C) 2011-2022 Laboratoire de Recherche et Developpement // de l'Epita (LRDE). // // This file is part of Spot, a model checking library. @@ -96,6 +96,9 @@ namespace spot // If greater than 0, bound the number of states used by automata // in containment checks. unsigned containment_max_states = 0; + // If greater than 0, maximal number of terms in a multop to perform + // containment checks on this multop. + unsigned containment_max_ops = 16; }; // fwd declaration to hide technical details. diff --git a/spot/twaalgos/translate.cc b/spot/twaalgos/translate.cc index 8a99313a3..d5b1aacd0 100644 --- a/spot/twaalgos/translate.cc +++ b/spot/twaalgos/translate.cc @@ -62,8 +62,8 @@ namespace spot gf_guarantee_set_ = true; } ltl_split_ = opt->get("ltl-split", 1); - int tls_max_states = opt->get("tls-max-states", 64); - tls_max_states_ = std::max(0, tls_max_states); + tls_max_states_ = std::max(0, opt->get("tls-max-states", 64)); + tls_max_ops_ = std::max(0, opt->get("tls-max-ops", 16)); exprop_ = opt->get("exprop", -1); branchpost_ = opt->get("branch-post", -1); } @@ -72,6 +72,7 @@ namespace spot { tl_simplifier_options options(false, false, false); options.containment_max_states = tls_max_states_; + options.containment_max_ops = tls_max_ops_; switch (level_) { case High: diff --git a/spot/twaalgos/translate.hh b/spot/twaalgos/translate.hh index d17c917b2..8428a2f22 100644 --- a/spot/twaalgos/translate.hh +++ b/spot/twaalgos/translate.hh @@ -155,7 +155,8 @@ namespace spot bool gf_guarantee_set_ = false; bool ltl_split_; int branchpost_ = -1; - unsigned tls_max_states_ = 0; + unsigned tls_max_states_ = 64; + unsigned tls_max_ops_ = 16; int exprop_; const option_map* opt_; }; diff --git a/tests/core/bdd.test b/tests/core/bdd.test index db03dbad5..85d410f8d 100755 --- a/tests/core/bdd.test +++ b/tests/core/bdd.test @@ -24,20 +24,21 @@ set -e # Make sure that setting the SPOT_BDD_TRACE envvar actually does # something. genltl --kr-n=3 | - SPOT_BDD_TRACE=1 ltl2tgba -x tls-max-states=0 -D >out 2>err + SPOT_BDD_TRACE=1 ltl2tgba -x tls-max-states=0,tls-max-ops=0 -D >out 2>err cat err grep spot: out && exit 1 grep 'spot: BDD package initialized' err # This value below, which is the number of time we need to garbage # collect might change if we improve the tool or change the way BuDDy # is initialized. -test 2 = `grep -c 'spot: BDD GC' err` +test 15 = `grep -c 'spot: BDD GC' err` # Minimal size for this automaton. # See also https://www.lrde.epita.fr/dload/spot/mochart10-fixes.pdf test "2240,4214" = `autfilt --stats=%s,%e out` # With the default value of tls-max-states, no GC is needed -genltl --kr-n=3 | SPOT_BDD_TRACE=1 ltl2tgba -D --stats=%s,%e >out 2>err +genltl --kr-n=3 | + SPOT_BDD_TRACE=1 ltl2tgba -D -x tls-max-ops=0 --stats=%s,%e >out 2>err cat err grep 'spot: BDD package initialized' err test 0 = `grep -c 'spot: BDD GC' err` From c2a3f2941d3c14b0ea1ce6794bbaa3cc5b709901 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Tue, 15 Nov 2022 17:50:45 +0100 Subject: [PATCH 196/337] ltl_to_tgba_fm: fix a memory leak on abort This issue surfaced in twacube.test after the previous patches. * spot/twaalgos/ltl2tgba_fm.cc: Release the formula namer on abort. * NEWS: Mention the bug. --- NEWS | 4 ++++ spot/twaalgos/ltl2tgba_fm.cc | 1 + 2 files changed, 5 insertions(+) diff --git a/NEWS b/NEWS index f19987c2c..a1f6267bb 100644 --- a/NEWS +++ b/NEWS @@ -11,6 +11,10 @@ New in spot 2.11.2.dev (not yet released) They have been limited to 16 operands, but this value can be changed with option -x tls-max-ops=N. (Issue #521 too.) + - Running ltl_to_tgba_fm() with an output_aborter (which is done + during automata-based implication checks) would leak memory on + abort. + New in spot 2.11.2 (2022-10-26) Command-line tools: diff --git a/spot/twaalgos/ltl2tgba_fm.cc b/spot/twaalgos/ltl2tgba_fm.cc index a354aaddd..42571f00f 100644 --- a/spot/twaalgos/ltl2tgba_fm.cc +++ b/spot/twaalgos/ltl2tgba_fm.cc @@ -2031,6 +2031,7 @@ namespace spot { if (aborter && aborter->too_large(a)) { + a->release_formula_namer(namer, false); if (!simplifier) delete s; return nullptr; From cfe1b0b70d54cfd56ecf6f26c9f190a88b3550d6 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Thu, 17 Nov 2022 11:14:32 +0100 Subject: [PATCH 197/337] configure: --with-pythondir should also override pyexecdir Fixes #512. * configure.ac: Here. * NEWS: Mention the bug. --- NEWS | 4 ++++ configure.ac | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index a1f6267bb..feef2c0d8 100644 --- a/NEWS +++ b/NEWS @@ -15,6 +15,10 @@ New in spot 2.11.2.dev (not yet released) during automata-based implication checks) would leak memory on abort. + - configure --with-pythondir should also redefine pyexecdir, + otherwise, libraries get installed in the wrong place on Debian. + (Issue #512.) + New in spot 2.11.2 (2022-10-26) Command-line tools: diff --git a/configure.ac b/configure.ac index 236063093..469ff0df4 100644 --- a/configure.ac +++ b/configure.ac @@ -192,7 +192,7 @@ if test "x${enable_python:-yes}" = xyes; then AC_ARG_WITH([pythondir], [AS_HELP_STRING([--with-pythondir], [override the computed pythondir])], - [pythondir=$withval], []) + [pythondir=$withval pyexecdir=$withval], []) fi From a032abf0c54482bc6003ea84c7d0911d1a507862 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Fri, 2 Dec 2022 14:44:03 +0100 Subject: [PATCH 198/337] parseaut: diagnose states that are unused and undefined Reported by Pierre Ganty. * spot/parseaut/parseaut.yy: Add diagnostics. * tests/core/parseaut.test: Adjust expected output, and add a test case. * NEWS: Mention the bug. --- NEWS | 4 ++++ spot/parseaut/parseaut.yy | 43 +++++++++++++++++++++++++++++++-------- tests/core/parseaut.test | 16 +++++++++++++-- 3 files changed, 53 insertions(+), 10 deletions(-) diff --git a/NEWS b/NEWS index feef2c0d8..96e3d3431 100644 --- a/NEWS +++ b/NEWS @@ -19,6 +19,10 @@ New in spot 2.11.2.dev (not yet released) otherwise, libraries get installed in the wrong place on Debian. (Issue #512.) + - The HOA parser used to silently declare unused and undefined states + (e.g., when the State: header declare many more states than the body + of the file). It now warns about those. + New in spot 2.11.2 (2022-10-26) Command-line tools: diff --git a/spot/parseaut/parseaut.yy b/spot/parseaut/parseaut.yy index 52d448c16..4d96b8c1c 100644 --- a/spot/parseaut/parseaut.yy +++ b/spot/parseaut/parseaut.yy @@ -1231,6 +1231,7 @@ body: states // diagnostic, so let not add another one. if (res.states >= 0) n = res.states; + std::vector unused_undeclared; for (unsigned i = 0; i < n; ++i) { auto& p = res.info_states[i]; @@ -1239,17 +1240,43 @@ body: states if (p.used) error(p.used_loc, "state " + std::to_string(i) + " has no definition"); - if (!p.used && res.complete) - if (auto p = res.prop_is_true("complete")) - { - error(res.states_loc, - "state " + std::to_string(i) + - " has no definition..."); - error(p.loc, "... despite 'properties: complete'"); - } + if (!p.used) + unused_undeclared.push_back(i); res.complete = false; } } + if (!unused_undeclared.empty()) + { + std::ostringstream out; + unsigned uus = unused_undeclared.size(); + int rangestart = -2; + int rangecur = -2; + const char* sep = uus > 1 ? "states " : "state "; + auto print_range = [&]() { + if (rangecur < 0) + return; + out << sep << rangestart; + if (rangecur != rangestart) + out << '-' << rangecur; + sep = ","; + }; + for (unsigned s: unused_undeclared) + { + if ((int)s != rangecur + 1) + { + print_range(); + rangestart = s; + } + rangecur = s; + } + print_range(); + out << (uus > 1 ? " are" : " is") << " unused and undefined"; + error(res.states_loc, out.str()); + + if (auto p = res.prop_is_true("complete")) + error(p.loc, "automaton is incomplete because it has " + "undefined states"); + } if (res.complete) if (auto p = res.prop_is_false("complete")) { diff --git a/tests/core/parseaut.test b/tests/core/parseaut.test index 56f2d54eb..7dabd563d 100755 --- a/tests/core/parseaut.test +++ b/tests/core/parseaut.test @@ -230,6 +230,7 @@ input:3.1-8: initial state number is larger than state count... input:4.1-9: ... declared here. input:1.1-4.9: missing 'Acceptance:' header input:3.1-8: initial state 1 has no definition +input:4.1-9: state 0 is unused and undefined EOF cat >input < Date: Fri, 2 Dec 2022 15:22:32 +0100 Subject: [PATCH 199/337] autfilt: print match count even on parse errors * bin/autfilt.cc: If -c is used, print the match_count even in present of parse errors. * tests/core/readsave.test: Adjust. * NEWS: Mention the bug. --- NEWS | 3 +++ bin/autfilt.cc | 12 +++++++----- tests/core/readsave.test | 13 +++++++------ 3 files changed, 17 insertions(+), 11 deletions(-) diff --git a/NEWS b/NEWS index 96e3d3431..ec6cff1bf 100644 --- a/NEWS +++ b/NEWS @@ -23,6 +23,9 @@ New in spot 2.11.2.dev (not yet released) (e.g., when the State: header declare many more states than the body of the file). It now warns about those. + - 'autfilt -c ...' should display a match count even in present of + parse errors. + New in spot 2.11.2 (2022-10-26) Command-line tools: diff --git a/bin/autfilt.cc b/bin/autfilt.cc index e16ef770a..7cff60e8b 100644 --- a/bin/autfilt.cc +++ b/bin/autfilt.cc @@ -1763,15 +1763,17 @@ main(int argc, char** argv) post.set_level(level); autfilt_processor processor(post, o.dict); - if (processor.run()) - return 2; - - // Diagnose unused -x options - extra_options.report_unused_options(); + int err = processor.run(); if (automaton_format == Count) std::cout << match_count << std::endl; + // Diagnose unused -x options + if (!err) + extra_options.report_unused_options(); + else + return 2; + check_cout(); return match_count ? 0 : 1; }); diff --git a/tests/core/readsave.test b/tests/core/readsave.test index dd4e2efaf..3780b4766 100755 --- a/tests/core/readsave.test +++ b/tests/core/readsave.test @@ -963,7 +963,8 @@ EOF test `autfilt -c --is-inherently-weak input7` = 1 test `autfilt -c --is-weak input7` = 0 test `autfilt -c --is-stutter-invariant input7` = 1 -autfilt --check input7 -H >output7 +autfilt --check input7 -H >output7 && exit 0 +test $? -eq 2 cat >expected7 <oneline.hoa -autfilt input8 --stats='%h' >oneline2.hoa -autfilt input8 --stats='%H' >oneline3.hoa -autfilt input8 --randomize --stats='%h' >oneline4.hoa -autfilt input8 --randomize --stats='%H' >oneline5.hoa +autfilt input8 -Hl >oneline.hoa && exit 1 +autfilt input8 --stats='%h' >oneline2.hoa && exit 1 +autfilt input8 --stats='%H' >oneline3.hoa && exit 1 +autfilt input8 --randomize --stats='%h' >oneline4.hoa && exit 1 +autfilt input8 --randomize --stats='%H' >oneline5.hoa && exit 1 diff oneline.hoa oneline2.hoa diff oneline.hoa oneline3.hoa diff oneline.hoa oneline4.hoa && exit 1 From 6b70edabf0029b7c4349b178f7b85c3ece1ba1d1 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Fri, 2 Dec 2022 17:30:29 +0100 Subject: [PATCH 200/337] getopt: do not include sys/cdefs.h to please Alpine Linux * m4/getopt.m4: Pretend sys/cdefs.h is missing, so that Alpine linux does not output a warning which we would turn into an error. --- m4/getopt.m4 | 12 ++++-------- 1 file changed, 4 insertions(+), 8 deletions(-) diff --git a/m4/getopt.m4 b/m4/getopt.m4 index 595483d58..e291e0c66 100644 --- a/m4/getopt.m4 +++ b/m4/getopt.m4 @@ -1,5 +1,5 @@ # getopt.m4 serial 47 -dnl Copyright (C) 2002-2006, 2008-2020 Free Software Foundation, Inc. +dnl Copyright (C) 2002-2006, 2008-2020, 2022 Free Software Foundation, Inc. dnl This file is free software; the Free Software Foundation dnl gives unlimited permission to copy and/or distribute it, dnl with or without modifications, as long as this notice is preserved. @@ -363,13 +363,9 @@ dnl is ambiguous with environment values that contain newlines. AC_DEFUN([gl_GETOPT_SUBSTITUTE_HEADER], [ - AC_CHECK_HEADERS_ONCE([sys/cdefs.h]) - if test $ac_cv_header_sys_cdefs_h = yes; then - HAVE_SYS_CDEFS_H=1 - else - HAVE_SYS_CDEFS_H=0 - fi - AC_SUBST([HAVE_SYS_CDEFS_H]) + # pretend HAVE_SYS_CDEFS_H is always 0 including isn't + # really necessary and causes warning on Alpine Linux. + AC_SUBST([HAVE_SYS_CDEFS_H], [0]) AC_DEFINE([__GETOPT_PREFIX], [[rpl_]], [Define to rpl_ if the getopt replacement functions and variables From 86c433cf808cf77dee0a23b2eda76a32a393c98d Mon Sep 17 00:00:00 2001 From: Philipp Schlehuber-Caissier Date: Thu, 1 Dec 2022 13:26:53 +0100 Subject: [PATCH 201/337] mealy: fix incorrect assertion * spot/twaalgos/mealy_machine.cc (minimize_mealy): Do not compare result to the original unsplit machine without splitting it first. * tests/python/mealy.py: Add a test case. --- spot/twaalgos/mealy_machine.cc | 6 +++- tests/python/mealy.py | 52 ++++++++++++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 1 deletion(-) diff --git a/spot/twaalgos/mealy_machine.cc b/spot/twaalgos/mealy_machine.cc index 9cdae279e..1126ad8e0 100644 --- a/spot/twaalgos/mealy_machine.cc +++ b/spot/twaalgos/mealy_machine.cc @@ -185,6 +185,7 @@ namespace spot { trace << "is_split_mealy(): Split mealy machine must define the named " "property \"state-player\"!\n"; + return false; } auto sp = get_state_players(m); @@ -3937,7 +3938,10 @@ namespace spot si.total_time = sglob.stop(); si.write(); - assert(is_split_mealy_specialization(mm, minmachine)); + assert(is_split_mealy_specialization( + mm->get_named_prop("state-player") ? mm + :split_2step(mm, false), + minmachine)); return minmachine; } diff --git a/tests/python/mealy.py b/tests/python/mealy.py index 7a884235e..7f6070146 100644 --- a/tests/python/mealy.py +++ b/tests/python/mealy.py @@ -626,3 +626,55 @@ exp = """digraph "" { } """ tc.assertEqual(res.to_str("dot", "g"), exp) + +# assertion bug: original machine is not always +# correctly split before testing inside minimize_mealy +aut = spot.automaton("""HOA: v1 +States: 2 +Start: 0 +AP: 11 "u0accel0accel" "u0accel0f1dcon23p81b" "u0accel0f1dcon231b" + "u0gear0f1dmin0f1dcon61b0f1dadd0gear0f1dcon241b1b1b" + "u0gear0gear" "u0gear0f1dmax0f1dcon241b0f1dsub0gear0f1dcon241b1b1b" + "u0steer0f1dsteering0angle0trackpos1b" "u0steer0steer" + "p0p0gt0rpm0f1dcon5523231b" "p0p0lt0rpm0f1dcon32323231b" + "p0p0lt0speed0f1dsub0target2speed0f1dmultp0f1dabs0steer1b0f1dcon248881b1b1b" +acc-name: all +Acceptance: 0 t +properties: trans-labels explicit-labels state-acc deterministic +controllable-AP: 0 1 2 3 4 5 6 7 +--BODY-- +State: 0 +[!0&!1&2&!3&4&!5&6&!7&!8&!9&!10] 0 +[!0&1&!2&!3&4&!5&6&!7&!8&!9&10] 0 +[!0&!1&2&!3&!4&5&6&!7&!8&9&!10] 0 +[!0&1&!2&!3&!4&5&6&!7&!8&9&10] 0 +[!0&!1&2&3&!4&!5&6&!7&8&!9&!10] 0 +[!0&1&!2&3&!4&!5&6&!7&8&!9&10] 0 +[!0&!1&2&!3&!4&5&!6&7&8&9 | !0&!1&2&!3&!4&5&6&!7&8&9 | !0&!1&2&!3&4&!5&!6&7&8&9 + | !0&!1&2&!3&4&!5&6&!7&8&9 | !0&!1&2&3&!4&!5&!6&7&8&9 + | !0&!1&2&3&!4&!5&6&!7&8&9 | !0&1&!2&!3&!4&5&!6&7&8&9 + | !0&1&!2&!3&!4&5&6&!7&8&9 | !0&1&!2&!3&4&!5&!6&7&8&9 + | !0&1&!2&!3&4&!5&6&!7&8&9 | !0&1&!2&3&!4&!5&!6&7&8&9 + | !0&1&!2&3&!4&!5&6&!7&8&9 | 0&!1&!2&!3&!4&5&!6&7&8&9 + | 0&!1&!2&!3&!4&5&6&!7&8&9 | 0&!1&!2&!3&4&!5&!6&7&8&9 + | 0&!1&!2&!3&4&!5&6&!7&8&9 | 0&!1&!2&3&!4&!5&!6&7&8&9 + | 0&!1&!2&3&!4&!5&6&!7&8&9] 1 +State: 1 +[!0&!1&2&!3&!4&5&!6&7 | !0&!1&2&!3&!4&5&6&!7 | !0&!1&2&!3&4&!5&!6&7 + | !0&!1&2&!3&4&!5&6&!7 | !0&!1&2&3&!4&!5&!6&7 + | !0&!1&2&3&!4&!5&6&!7 | !0&1&!2&!3&!4&5&!6&7 + | !0&1&!2&!3&!4&5&6&!7 | !0&1&!2&!3&4&!5&!6&7 + | !0&1&!2&!3&4&!5&6&!7 | !0&1&!2&3&!4&!5&!6&7 + | !0&1&!2&3&!4&!5&6&!7 | 0&!1&!2&!3&!4&5&!6&7 + | 0&!1&!2&!3&!4&5&6&!7 | 0&!1&!2&!3&4&!5&!6&7 + | 0&!1&!2&!3&4&!5&6&!7 | 0&!1&!2&3&!4&!5&!6&7 + | 0&!1&!2&3&!4&!5&6&!7] 1 +--END--""") + +spot.minimize_mealy(aut, -1) +spot.minimize_mealy(aut, 0) +spot.minimize_mealy(aut, 1) +auts = spot.split_2step(aut) +spot.minimize_mealy(auts, -1) +spot.minimize_mealy(auts, 0) +spot.minimize_mealy(auts, 1) \ No newline at end of file From 37d4e513d95776f0144e4d8f8581f62d4e6a83f3 Mon Sep 17 00:00:00 2001 From: Philipp Schlehuber-Caissier Date: Sun, 27 Nov 2022 01:16:46 +0100 Subject: [PATCH 202/337] game: fix appending strategies bug MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When calling solve_parity_game() multiple times on the same automaton the strategies are appended one after the other. Reported by Dávid Smolka. * NEWS: Mention the bug. * spot/twaalgos/game.cc: Fix it. * tests/python/game.py: Test it. * THANKS: Add Dávid. --- NEWS | 4 ++++ THANKS | 1 + spot/twaalgos/game.cc | 1 + tests/python/game.py | 14 ++++++++++++++ 4 files changed, 20 insertions(+) diff --git a/NEWS b/NEWS index ec6cff1bf..b3f9b2c63 100644 --- a/NEWS +++ b/NEWS @@ -26,6 +26,10 @@ New in spot 2.11.2.dev (not yet released) - 'autfilt -c ...' should display a match count even in present of parse errors. + - Calling solve_parity_game() multiple times on the same automaton + used to append the new strategy to the existing one instead of + overwriting it. + New in spot 2.11.2 (2022-10-26) Command-line tools: diff --git a/THANKS b/THANKS index 2b054666c..356d187a1 100644 --- a/THANKS +++ b/THANKS @@ -11,6 +11,7 @@ Christian Dax Christopher Ziegler Clément Tamines David Müller +Dávid Smolka Edmond Irani Liu Ernesto Posse Étienne Renault diff --git a/spot/twaalgos/game.cc b/spot/twaalgos/game.cc index ccb3b818e..f5699bf49 100644 --- a/spot/twaalgos/game.cc +++ b/spot/twaalgos/game.cc @@ -227,6 +227,7 @@ namespace spot region_t &w = *arena->get_or_set_named_prop("state-winner"); strategy_t &s = *arena->get_or_set_named_prop("strategy"); w.swap(w_.winner_); + s.clear(); s.reserve(s_.size()); for (auto as : s_) s.push_back(as == no_strat_mark ? 0 : (unsigned) as); diff --git a/tests/python/game.py b/tests/python/game.py index 647c8d347..a7080b696 100644 --- a/tests/python/game.py +++ b/tests/python/game.py @@ -371,3 +371,17 @@ for kind in [spot.parity_kind_min, spot.parity_kind_max]: tc.assertTrue(spot.solve_parity_game(g_test_split1)) c_strat1 = spot.get_strategy(g_test_split1) tc.assertTrue(c_strat == c_strat1) + +# Test that strategies are not appended +# if solve is called multiple times +aut = spot.make_twa_graph() +aut.set_buchi() +aut.new_states(2) +aut.new_edge(0,1,buddy.bddtrue, [0]) +aut.new_edge(1,0,buddy.bddtrue, []) +spot.set_state_players(aut, [False, True]) +spot.solve_game(aut) +S1 = list(spot.get_strategy(aut)) +spot.solve_game(aut) +S2 = list(spot.get_strategy(aut)) +tc.assertEqual(S1, S2) \ No newline at end of file From 5dbf601afb9c28db8002f6b60593f0acbad011e0 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Tue, 6 Dec 2022 16:07:21 +0100 Subject: [PATCH 203/337] * NEWS: Typos. --- NEWS | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/NEWS b/NEWS index b3f9b2c63..068be3a4d 100644 --- a/NEWS +++ b/NEWS @@ -2,7 +2,7 @@ New in spot 2.11.2.dev (not yet released) Bug fixes: - - Automata-based implication checks, used to simplify formulas where + - Automata-based implication checks, used to simplify formulas were slower than necessary because the translator was configured to favor determinism unnecessarily. (Issue #521.) @@ -23,7 +23,7 @@ New in spot 2.11.2.dev (not yet released) (e.g., when the State: header declare many more states than the body of the file). It now warns about those. - - 'autfilt -c ...' should display a match count even in present of + - 'autfilt -c ...' should display a match count even in presence of parse errors. - Calling solve_parity_game() multiple times on the same automaton From 4629d074ab0b1374c9c055b8b1285a8e371fd843 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Wed, 7 Dec 2022 11:26:51 +0100 Subject: [PATCH 204/337] Fix semantics of [*i..j] and [:*i..j] * doc/tl/tl.tex: After a discussion with Antoin, it appears that the semantics previously given for f[*0..j] was not considering that f[*0] should accept any sequence of one letter. --- doc/tl/tl.tex | 24 +++++++++--------------- 1 file changed, 9 insertions(+), 15 deletions(-) diff --git a/doc/tl/tl.tex b/doc/tl/tl.tex index 62a35635f..f9205cced 100644 --- a/doc/tl/tl.tex +++ b/doc/tl/tl.tex @@ -668,20 +668,17 @@ $a$ is an atomic proposition. \sigma\VDash f\FUSION g&\iff \exists k\in\N,\,(\sigma^{0..k} \VDash f)\land(\sigma^{k..} \VDash g)\\ \sigma\VDash f\STAR{\mvar{i}..\mvar{j}}& \iff \begin{cases} - \text{either} & \mvar{i}=0 \land \sigma=\varepsilon \\ - \text{or} & \mvar{i}=0 \land \mvar{j}>0 \land (\exists k\in\N,\, - (\sigma^{0..k-1}\VDash f) \land (\sigma^{k..} - \VDash f\STAR{\mvar{0}..\mvar{j-1}}))\\ + \text{either} & \mvar{i}=0 \land\mvar{j}=0\land \sigma=\varepsilon \\ + \text{or} & \mvar{i}=0 \land \mvar{j}>0 \land \bigl((\sigma = \varepsilon) \lor (\sigma + \VDash f\STAR{\mvar{1}..\mvar{j}})\bigr)\\ \text{or} & \mvar{i}>0 \land \mvar{j}>0 \land (\exists k\in\N,\, (\sigma^{0..k-1}\VDash f) \land (\sigma^{k..} \VDash f\STAR{\mvar{i-1}..\mvar{j-1}}))\\ \end{cases}\\ \sigma\VDash f\STAR{\mvar{i}..} & \iff \begin{cases} - \text{either} & \mvar{i}=0 \land \sigma=\varepsilon \\ - \text{or} & \mvar{i}=0 \land (\exists k\in\N,\, - (\sigma^{0..k-1}\VDash f) \land (\sigma^{k..} - \VDash f\STAR{\mvar{0}..}))\\ + \text{either} & \mvar{i}=0 \land \bigl((\sigma=\varepsilon)\lor(\sigma + \VDash f\STAR{\mvar{1}..})\bigr)\\ \text{or} & \mvar{i}>0 \land (\exists k\in\N,\, (\sigma^{0..k-1}\VDash f) \land (\sigma^{k..} \VDash f\STAR{\mvar{i-1}..}))\\ @@ -689,19 +686,16 @@ $a$ is an atomic proposition. \sigma\VDash f\FSTAR{\mvar{i}..\mvar{j}}& \iff \begin{cases} \text{either} & \mvar{i}=0 \land \mvar{j}=0 \land \sigma\VDash\1 \\ - \text{or} & \mvar{i}=0 \land \mvar{j}>0 \land (\exists k\in\N,\, - (\sigma^{0..k}\VDash f) \land (\sigma^{k..} - \VDash f\FSTAR{\mvar{0}..\mvar{j-1}}))\\ + \text{or} & \mvar{i}=0 \land \mvar{j}>0 \land \bigl((\sigma\VDash\1)\lor(\sigma + \VDash f\FSTAR{\mvar{1}..\mvar{j}})\bigr)\\ \text{or} & \mvar{i}>0 \land \mvar{j}>0 \land (\exists k\in\N,\, (\sigma^{0..k}\VDash f) \land (\sigma^{k..} \VDash f\FSTAR{\mvar{i-1}..\mvar{j-1}}))\\ \end{cases}\\ \sigma\VDash f\FSTAR{\mvar{i}..} & \iff \begin{cases} - \text{either} & \mvar{i}=0 \land \sigma\VDash\1 \\ - \text{or} & \mvar{i}=0 \land (\exists k\in\N,\, - (\sigma^{0..k}\VDash f) \land (\sigma^{k..} - \VDash f\FSTAR{\mvar{0}..}))\\ + \text{either} & \mvar{i}=0 \land \bigl((\sigma\VDash\1) + \lor(\sigma \VDash f\FSTAR{\mvar{1}..})\bigr)\\ \text{or} & \mvar{i}>0 \land (\exists k\in\N,\, (\sigma^{0..k}\VDash f) \land (\sigma^{k..} \VDash f\FSTAR{\mvar{i-1}..}))\\ From 8ed9e3381f9720bc9580ea3ce5e7cc911a1affa5 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Thu, 8 Dec 2022 11:51:07 +0100 Subject: [PATCH 205/337] formula: introduce one_plus(), and saturate predefined formulas * spot/tl/formula.hh, spot/tl/formula.cc (one_plus): New. (fnode): Add a saturated argument. (tt_, ff_, eword_, one_plus, one_star): Create saturated node. (destroy): Do not check for id() < 3. --- spot/tl/formula.cc | 21 ++++++++++++++++----- spot/tl/formula.hh | 37 ++++++++++++++++++++++++++++--------- 2 files changed, 44 insertions(+), 14 deletions(-) diff --git a/spot/tl/formula.cc b/spot/tl/formula.cc index fb4ab0d49..a3145884d 100644 --- a/spot/tl/formula.cc +++ b/spot/tl/formula.cc @@ -660,6 +660,16 @@ namespace spot switch (o) { case op::Star: + if (max == unbounded() && child == tt_) + { + // bypass normal construction: 1[*] and 1[+] are + // frequently used, so they are not reference counted. + if (min == 0) + return one_star(); + if (min == 1) + return one_plus(); + } + neutral = eword(); break; case op::FStar: @@ -810,7 +820,7 @@ namespace spot return tt(); // ![*0] = 1[+] if (f->is_eword()) - return bunop(op::Star, tt(), 1); + return one_plus(); auto fop = f->kind(); // "Not" is an involution. @@ -1138,10 +1148,11 @@ namespace spot return id; } - const fnode* fnode::ff_ = new fnode(op::ff, {}); - const fnode* fnode::tt_ = new fnode(op::tt, {}); - const fnode* fnode::ew_ = new fnode(op::eword, {}); + const fnode* fnode::ff_ = new fnode(op::ff, {}, true); + const fnode* fnode::tt_ = new fnode(op::tt, {}, true); + const fnode* fnode::ew_ = new fnode(op::eword, {}, true); const fnode* fnode::one_star_ = nullptr; // Only built when necessary. + const fnode* fnode::one_plus_ = nullptr; // Only built when necessary. void fnode::setup_props(op o) { @@ -1817,7 +1828,7 @@ namespace spot { unsigned cnt = 0; for (auto i: m.uniq) - if (i->id() > 3 && i != one_star_) + if (!i->saturated_) { if (!cnt++) std::cerr << "*** m.uniq is not empty ***\n"; diff --git a/spot/tl/formula.hh b/spot/tl/formula.hh index d01b8379c..9239e1a68 100644 --- a/spot/tl/formula.hh +++ b/spot/tl/formula.hh @@ -149,7 +149,7 @@ namespace spot { if (SPOT_LIKELY(refs_)) --refs_; - else if (SPOT_LIKELY(id_ > 2) && SPOT_LIKELY(!saturated_)) + else if (SPOT_LIKELY(!saturated_)) // last reference to a node that is not a constant destroy_aux(); } @@ -351,10 +351,18 @@ namespace spot static const fnode* one_star() { if (!one_star_) - one_star_ = bunop(op::Star, tt(), 0); + one_star_ = new fnode(op::Star, tt_, 0, unbounded(), true); return one_star_; } + /// \see formula::one_plus + static const fnode* one_plus() + { + if (!one_plus_) + one_plus_ = new fnode(op::Star, tt_, 1, unbounded(), true); + return one_plus_; + } + /// \see formula::ap_name const std::string& ap_name() const; @@ -536,7 +544,7 @@ namespace spot template - fnode(op o, iter begin, iter end) + fnode(op o, iter begin, iter end, bool saturated = false) // Clang has some optimization where is it able to combine the // 4 movb initializing op_,min_,max_,saturated_ into a single // movl. Also it can optimize the three byte-comparisons of @@ -551,7 +559,7 @@ namespace spot #if __llvm__ min_(0), max_(0), #endif - saturated_(0) + saturated_(saturated) { size_t s = std::distance(begin, end); if (SPOT_UNLIKELY(s > (size_t) UINT16_MAX)) @@ -563,13 +571,15 @@ namespace spot setup_props(o); } - fnode(op o, std::initializer_list l) - : fnode(o, l.begin(), l.end()) + fnode(op o, std::initializer_list l, + bool saturated = false) + : fnode(o, l.begin(), l.end(), saturated) { } - fnode(op o, const fnode* f, uint8_t min, uint8_t max) - : op_(o), min_(min), max_(max), saturated_(0), size_(1) + fnode(op o, const fnode* f, uint8_t min, uint8_t max, + bool saturated = false) + : op_(o), min_(min), max_(max), saturated_(saturated), size_(1) { children[0] = f; setup_props(o); @@ -579,6 +589,7 @@ namespace spot static const fnode* tt_; static const fnode* ew_; static const fnode* one_star_; + static const fnode* one_plus_; op op_; // operator uint8_t min_; // range minimum (for star-like operators) @@ -1552,7 +1563,15 @@ namespace spot /// \brief Return a copy of the formula 1[*]. static formula one_star() { - return formula(fnode::one_star()->clone()); + // no need to clone, 1[*] is not reference counted + return formula(fnode::one_star()); + } + + /// \brief Return a copy of the formula 1[+]. + static formula one_plus() + { + // no need to clone, 1[+] is not reference counted + return formula(fnode::one_plus()); } /// \brief Whether the formula is an atomic proposition or its From 720c380412e4c70d134d227b9bd7e78cf6e4d13e Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Thu, 8 Dec 2022 13:54:19 +0100 Subject: [PATCH 206/337] formula: new trivial simplifications Add the following rules: - f|[+] = [+] if f rejects [*0] - f|[*] = [*] if f accepts [*0] - f&&[+] = f if f rejects [*0] - b:b[*i..j] = b[*max(i,1)..j] - b[*i..j]:b[*k..l] = b[*max(i,1)+max(k,1)-1,1), j+l-1] * spot/tl/formula.cc: Implement the new rules. * doc/tl/tl.tex: Document them. * tests/core/equals.test: Test them. * NEWS: Add them --- NEWS | 9 +++ doc/tl/tl.tex | 18 ++++-- spot/tl/formula.cc | 126 ++++++++++++++++++++++++++++++++++++++--- tests/core/equals.test | 8 ++- 4 files changed, 147 insertions(+), 14 deletions(-) diff --git a/NEWS b/NEWS index 068be3a4d..775bb710c 100644 --- a/NEWS +++ b/NEWS @@ -1,5 +1,14 @@ New in spot 2.11.2.dev (not yet released) + Library: + + - The following new trivial simplifications have been implemented for SEREs: + - f|[+] = [+] if f rejects [*0] + - f|[*] = [*] if f accepts [*0] + - f&&[+] = f if f rejects [*0] + - b:b[*i..j] = b[*max(i,1)..j] + - b[*i..j]:b[*k..l] = b[*max(i,1)+max(k,1)-1,1), j+l-1] + Bug fixes: - Automata-based implication checks, used to simplify formulas were diff --git a/doc/tl/tl.tex b/doc/tl/tl.tex index f9205cced..288a5da0c 100644 --- a/doc/tl/tl.tex +++ b/doc/tl/tl.tex @@ -853,10 +853,18 @@ The following rules are all valid with the two arguments swapped. \1\OR b &\equiv \1 & \1 \FUSION f & \equiv f\mathrlap{\text{~if~}\varepsilon\nVDash f}\\ && - \STAR{} \AND f &\equiv f & - \STAR{} \OR f &\equiv \1\mathrlap{\STAR{}} & + \STAR{} \ANDALT f &\equiv f & + \STAR{} \OR f &\equiv \mathrlap{\STAR{}} & && - \STAR{} \CONCAT f &\equiv \STAR{}\mathrlap{\text{~if~}\varepsilon\VDash f}& \\ + \STAR{} \CONCAT f &\equiv \STAR{}\text{~if~}\varepsilon\VDash f& \\ + && + \PLUS{} \ANDALT f &\equiv f \text{~if~}\varepsilon\nVDash f& + \PLUS{} \OR f &\equiv \begin{cases} + \mathrlap{\STAR{}\text{~if~} \varepsilon\VDash f} \\ + \mathrlap{\PLUS{}\text{~if~} \varepsilon\nVDash f} \\ + \end{cases} & + && + && \\ \eword\AND f &\equiv f & \eword\ANDALT f &\equiv \begin{cases} @@ -880,7 +888,9 @@ The following rules are all valid with the two arguments swapped. f\STAR{\mvar{i}..\mvar{j}}\CONCAT f&\equiv f\STAR{\mvar{i+1}..\mvar{j+1}} & f\STAR{\mvar{i}..\mvar{j}}\CONCAT f\STAR{\mvar{k}..\mvar{l}}&\equiv f\STAR{\mvar{i+k}..\mvar{j+l}}\\ f\FSTAR{\mvar{i}..\mvar{j}}\FUSION f&\equiv f\FSTAR{\mvar{i+1}..\mvar{j+1}} & -f\FSTAR{\mvar{i}..\mvar{j}}\FUSION f\FSTAR{\mvar{k}..\mvar{l}}&\equiv f\FSTAR{\mvar{i+k}..\mvar{j+l}} +f\FSTAR{\mvar{i}..\mvar{j}}\FUSION f\FSTAR{\mvar{k}..\mvar{l}}&\equiv f\FSTAR{\mvar{i+k}..\mvar{j+l}}\\ +b\STAR{\mvar{i}..\mvar{j}}\FUSION b &\equiv b\STAR{\mvar{\max(i,1)}..\mvar{j}} & +b\STAR{\mvar{i}..\mvar{j}}\FUSION b\STAR{\mvar{k}..\mvar{l}} &\equiv b\mathrlap{\STAR{\mvar{\max(i,1)+\max(k,1)-1}..\mvar{j+l-1}}} \end{align*} \section{SERE-LTL Binding Operators} diff --git a/spot/tl/formula.cc b/spot/tl/formula.cc index a3145884d..370a50e8f 100644 --- a/spot/tl/formula.cc +++ b/spot/tl/formula.cc @@ -307,11 +307,14 @@ namespace spot unsigned orig_size = v.size(); - const fnode* neutral; - const fnode* neutral2; - const fnode* abs; - const fnode* abs2; - const fnode* weak_abs; + const fnode* neutral; // neutral element + const fnode* neutral2; // second neutral element (if any) + const fnode* abs; // absorbent element + const fnode* abs2; // second absorbent element (if any) + const fnode* weak_abs; // almost absorbent element (if any) + // The notion of "almost absorbent" captures situation where the + // present of the element can be simplified in itself or another + // element depending on a condition on the rest of the formula. switch (o) { case op::And: @@ -323,7 +326,17 @@ namespace spot break; case op::AndRat: neutral = one_star(); - neutral2 = nullptr; + { + // If this AndRat contains an operand that does not accept + // the empty word, and that is not [+], then any [+] can be + // removed. + bool one_non_eword_non_plus = + std::find_if(v.begin(), v.end(), + [o = one_plus()](const fnode* f) { + return !f->accepts_eword() && f != o; + }) != v.end(); + neutral2 = one_non_eword_non_plus ? one_plus() : nullptr; + } abs = ff(); abs2 = nullptr; weak_abs = eword(); @@ -349,7 +362,7 @@ namespace spot neutral2 = nullptr; abs = one_star(); abs2 = nullptr; - weak_abs = nullptr; + weak_abs = one_plus(); gather_bool(v, op::Or); break; case op::Concat: @@ -506,11 +519,10 @@ namespace spot else return abs; } - else + else if (o == op::AndNLM) { // Similarly, a* & 1 & (c;d) = c;d // a* & 1 & c* = 1 - assert(o == op::AndNLM); vec tmp; for (auto i: v) { @@ -527,6 +539,27 @@ namespace spot tmp.emplace_back(weak_abs); v.swap(tmp); } + else if (o == op::OrRat) + { + // We have a[*] | [+] | c = [*] + // and a | [+] | c = [+] + // So if [+] has been seen, check if some term + // recognize the empty word. + bool acc_eword = false; + for (i = v.begin(); i != v.end(); ++i) + { + acc_eword |= (*i)->accepts_eword(); + (*i)->destroy(); + } + if (acc_eword) + return abs; + else + return weak_abs; + } + else + { + SPOT_UNREACHABLE(); + } } else if (o == op::Concat || o == op::Fusion) { @@ -613,6 +646,81 @@ namespace spot *fpos = newfs; } } + // also + // b[*i..j]:b -> b[*max(1,i),j] + // b:b[*i..j] -> b[*max(1,i),j] + // b[*i..j]:b[*k..l] -> b[*max(i,1)+max(j,1)-1,j+l-1] + if (o == op::Fusion && v.size() > 1) + { + i = v.begin(); + while (i != v.end()) + { + if (!(((*i)->is(op::Star) && (*i)->nth(0)->is_boolean()) + || (*i)->is_boolean())) + { + ++i; + continue; + } + const fnode *b; + unsigned min; + unsigned max; + if ((*i)->is_boolean()) + { + min = max = 1; + b = *i; + } + else + { + b = (*i)->nth(0); + min = (*i)->min(); + max = (*i)->max(); + } + vec::iterator prev = i; + ++i; + bool changed = false; + while (i != v.end()) + { + unsigned min2; + unsigned max2; + if ((*i)->is_boolean()) + { + if (*i != b) + break; + min2 = max2 = 1; + } + else if ((*i)->is(op::Star) && (*i)->nth(0)->is_boolean()) + { + if ((*i)->nth(0) != b) + break; + min2 = (*i)->min(); + max2 = (*i)->max(); + } + else + { + break; + } + // Now we can merge prev and i. + min = min + (min == 0) + min2 + (min2 == 0) - 1; + assert(max != 0 && max2 != 0); + if (max2 == unbounded() || max == unbounded()) + max = unbounded(); + else if (max + max2 < unbounded()) + max = max + max2 - 1; + else + break; + changed = true; + (*i)->destroy(); + i = v.erase(i); + } + if (changed) + { + const fnode* newf = + fnode::bunop(op::Star, b->clone(), min, max); + (*prev)->destroy(); + *prev = newf; + } + } + } } } diff --git a/tests/core/equals.test b/tests/core/equals.test index f00216347..a67c4b1ef 100755 --- a/tests/core/equals.test +++ b/tests/core/equals.test @@ -1,6 +1,6 @@ #! /bin/sh # -*- coding: utf-8 -*- -# Copyright (C) 2009-2012, 2014-2015, 2021 Laboratoire de Recherche et +# Copyright (C) 2009-2012, 2014-2015, 2021, 2022 Laboratoire de Recherche et # Développement de l'Epita (LRDE). # Copyright (C) 2003, 2004 Laboratoire d'Informatique de Paris 6 (LIP6), # département Systèmes Répartis Coopératifs (SRC), Université Pierre @@ -196,6 +196,12 @@ G({1}<>->1), 1 {(a*;b|c)[:*0]}, 1 {(a*;b|c)[:*1]}, {(a*;b|c)} {(a;b):(a;b):(a;b)[:*2]:(a;b):b*:b*:(c;d)[:*1]}, {(a;b)[:*5]:b*[:*2]:(c;d)} +{((a;b)|[+]|(c;d[*]));a}, {[+];a} +{((a;b)|[+]|(d[*]));a}, {[*];a} +{((a;b)&&[+]&&(d[*]));a}, {((a;b)&&(d[*]));a} +{((a;b|[*0])&&[+]&&(d[*]));a}, {((a;b|[*0])&&[+]&&(d[*]));a} +{(a;c):b[*3..5]:b[*10]:(a;c)}, {(a;c):b[*12..14]:(a;c)} +{(a;c):b:b[*3..5]:b:b[*0..4]:(a;c)}, {(a;c):b[*3..8]:(a;c)} EOF From 1248d326aaa03debb539db2ae34693f01553233b Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Thu, 8 Dec 2022 17:27:32 +0100 Subject: [PATCH 207/337] Work around spurious g++-12 warnings * spot/twaalgos/ltl2tgba_fm.cc, spot/tl/formula.hh, spot/twaalgos/translate.cc: Add SPOT_ASSUME in various places to help g++. --- spot/tl/formula.hh | 4 +++- spot/twaalgos/ltl2tgba_fm.cc | 15 ++++++++++----- spot/twaalgos/translate.cc | 9 ++++++--- 3 files changed, 19 insertions(+), 9 deletions(-) diff --git a/spot/tl/formula.hh b/spot/tl/formula.hh index 9239e1a68..074ec8b02 100644 --- a/spot/tl/formula.hh +++ b/spot/tl/formula.hh @@ -294,7 +294,9 @@ namespace spot { if (SPOT_UNLIKELY(i >= size())) report_non_existing_child(); - return children[i]; + const fnode* c = children[i]; + SPOT_ASSUME(c != nullptr); + return c; } /// \see formula::ff diff --git a/spot/twaalgos/ltl2tgba_fm.cc b/spot/twaalgos/ltl2tgba_fm.cc index 42571f00f..9768dfbfd 100644 --- a/spot/twaalgos/ltl2tgba_fm.cc +++ b/spot/twaalgos/ltl2tgba_fm.cc @@ -1026,11 +1026,16 @@ namespace spot bool coacc = false; auto& st = sm->states_of(n); for (auto l: st) - if (namer->get_name(l).accepts_eword()) - { - coacc = true; - break; - } + { + formula lf = namer->get_name(l); + // Somehow gcc 12.2.0 thinks lf can be nullptr. + SPOT_ASSUME(lf != nullptr); + if (lf.accepts_eword()) + { + coacc = true; + break; + } + } if (!coacc) { // ... or if any of its successors is coaccessible. diff --git a/spot/twaalgos/translate.cc b/spot/twaalgos/translate.cc index d5b1aacd0..339463426 100644 --- a/spot/twaalgos/translate.cc +++ b/spot/twaalgos/translate.cc @@ -209,9 +209,12 @@ namespace spot if (!rest.empty() && !oblg.empty()) { auto safety = [](formula f) - { - return f.is_syntactic_safety(); - }; + { + // Prevent gcc 12.2.0 from warning us that f could be a + // nullptr formula. + SPOT_ASSUME(f != nullptr); + return f.is_syntactic_safety(); + }; auto i = std::remove_if(oblg.begin(), oblg.end(), safety); rest.insert(rest.end(), i, oblg.end()); oblg.erase(i, oblg.end()); From d7feeca13e2ede1948d90dfcdfe39b125fdd8338 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Fri, 9 Dec 2022 09:40:27 +0100 Subject: [PATCH 208/337] Release Spot 2.11.3 * NEWS, configure.ac, doc/org/setup.org: Bump version to 2.11.3. --- NEWS | 2 +- configure.ac | 2 +- doc/org/setup.org | 10 +++++----- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/NEWS b/NEWS index 068be3a4d..37555a50e 100644 --- a/NEWS +++ b/NEWS @@ -1,4 +1,4 @@ -New in spot 2.11.2.dev (not yet released) +New in spot 2.11.3 (2022-12-09) Bug fixes: diff --git a/configure.ac b/configure.ac index 469ff0df4..3478f41b9 100644 --- a/configure.ac +++ b/configure.ac @@ -21,7 +21,7 @@ # along with this program. If not, see . AC_PREREQ([2.69]) -AC_INIT([spot], [2.11.2.dev], [spot@lrde.epita.fr]) +AC_INIT([spot], [2.11.3], [spot@lrde.epita.fr]) AC_CONFIG_AUX_DIR([tools]) AC_CONFIG_MACRO_DIR([m4]) AM_INIT_AUTOMAKE([1.11 gnu tar-ustar color-tests parallel-tests]) diff --git a/doc/org/setup.org b/doc/org/setup.org index bb568e5f1..78091ea45 100644 --- a/doc/org/setup.org +++ b/doc/org/setup.org @@ -1,11 +1,11 @@ #+OPTIONS: H:2 num:nil toc:t html-postamble:nil ^:nil #+EMAIL: spot@lrde.epita.fr #+HTML_LINK_HOME: index.html -#+MACRO: SPOTVERSION 2.11.2 -#+MACRO: LASTRELEASE 2.11.2 -#+MACRO: LASTTARBALL [[http://www.lrde.epita.fr/dload/spot/spot-2.11.2.tar.gz][=spot-2.11.2.tar.gz=]] -#+MACRO: LASTNEWS [[https://gitlab.lre.epita.fr/spot/spot/blob/spot-2-11-2/NEWS][summary of the changes]] -#+MACRO: LASTDATE 2022-10-26 +#+MACRO: SPOTVERSION 2.11.3 +#+MACRO: LASTRELEASE 2.11.3 +#+MACRO: LASTTARBALL [[http://www.lrde.epita.fr/dload/spot/spot-2.11.3.tar.gz][=spot-2.11.3.tar.gz=]] +#+MACRO: LASTNEWS [[https://gitlab.lre.epita.fr/spot/spot/blob/spot-2-11-3/NEWS][summary of the changes]] +#+MACRO: LASTDATE 2022-12-09 #+ATTR_HTML: :id spotlogo [[file:spot2.svg]] From 09e147ee4b4949e0ebc7ec172e4ba7de144d448f Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Fri, 9 Dec 2022 09:43:18 +0100 Subject: [PATCH 209/337] * NEWS, configure.ac: Bump version to 2.11.3.dev. --- NEWS | 4 ++++ configure.ac | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index 37555a50e..b1f7c2d79 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,7 @@ +New in spot 2.11.3.dev (not yet released) + + Nothing yet. + New in spot 2.11.3 (2022-12-09) Bug fixes: diff --git a/configure.ac b/configure.ac index 3478f41b9..68fe4cab7 100644 --- a/configure.ac +++ b/configure.ac @@ -21,7 +21,7 @@ # along with this program. If not, see . AC_PREREQ([2.69]) -AC_INIT([spot], [2.11.3], [spot@lrde.epita.fr]) +AC_INIT([spot], [2.11.3.dev], [spot@lrde.epita.fr]) AC_CONFIG_AUX_DIR([tools]) AC_CONFIG_MACRO_DIR([m4]) AM_INIT_AUTOMAKE([1.11 gnu tar-ustar color-tests parallel-tests]) From fb63dfc309683d1ac443158605e22c94a11775e6 Mon Sep 17 00:00:00 2001 From: Philipp Schlehuber-Caissier Date: Tue, 29 Nov 2022 14:01:45 +0100 Subject: [PATCH 210/337] introduce partitioned_relabel_here Function taking an automaton and trying to relabel it by partitioning the old conditions and encode the different subsets of the partition with new variables * spot/priv/Makefile.am: Add * spot/priv/partitioned_relabel.hh , spot/priv/partitioned_relabel.cc: try_partition_me, computes the partition of a given vector of bdds * spot/twaalgos/relabel.hh , spot/twaalgos/relabel.cc: Here. Adapt also relabel() to cope with the different type of relabeling_maps * tests/python/_partitioned_relabel.ipynb , tests/python/except.py: Test and Usage * tests/Makefile.am: Add test --- spot/priv/Makefile.am | 2 + spot/priv/partitioned_relabel.cc | 147 +++ spot/priv/partitioned_relabel.hh | 81 ++ spot/twaalgos/relabel.cc | 461 ++++++++- spot/twaalgos/relabel.hh | 33 + tests/Makefile.am | 1 + tests/python/_partitioned_relabel.ipynb | 1224 +++++++++++++++++++++++ tests/python/except.py | 15 + 8 files changed, 1920 insertions(+), 44 deletions(-) create mode 100644 spot/priv/partitioned_relabel.cc create mode 100644 spot/priv/partitioned_relabel.hh create mode 100644 tests/python/_partitioned_relabel.ipynb diff --git a/spot/priv/Makefile.am b/spot/priv/Makefile.am index d4e9cc77c..317292bd3 100644 --- a/spot/priv/Makefile.am +++ b/spot/priv/Makefile.am @@ -29,6 +29,8 @@ libpriv_la_SOURCES = \ bddalloc.hh \ freelist.cc \ freelist.hh \ + partitioned_relabel.cc \ + partitioned_relabel.hh \ robin_hood.hh \ satcommon.hh\ satcommon.cc\ diff --git a/spot/priv/partitioned_relabel.cc b/spot/priv/partitioned_relabel.cc new file mode 100644 index 000000000..f28ea5554 --- /dev/null +++ b/spot/priv/partitioned_relabel.cc @@ -0,0 +1,147 @@ +// -*- coding: utf-8 -*- +// Copyright (C) 2022 Laboratoire de Recherche +// de l'Epita (LRE). +// +// 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 . + +#include "config.h" + +#include + + +relabeling_map +bdd_partition::to_relabeling_map(twa_graph& for_me) const +{ + relabeling_map res; + // Change to unordered_map? + bdd_dict_ptr bdddict = for_me.get_dict(); + + bool use_inner = ig->state_storage(0).new_label != bddfalse; + std::vector doskip + = use_inner ? std::vector(ig->num_states(), false) + : std::vector(); + + auto bdd2form = [&bdddict](const bdd& cond) + { + return bdd_to_formula(cond, bdddict); + }; + + for (const auto& [old_letter, s] : treated) + { + formula new_letter_form = bdd2form(ig->state_storage(s).new_label); + assert(res.count(new_letter_form) == 0); + if (use_inner) + doskip[s] = true; + res[new_letter_form] = bdd2form(old_letter); + } + + if (use_inner) + { + // This implies that the split option was false, + // so we can store further info + auto& all_cond = *all_cond_ptr; + const unsigned Norig = all_cond.size(); + + for (unsigned i = 0; i < Norig; ++i) + { + // Internal node -> new ? + if (doskip[i]) + continue; + // Leave node -> already exists + if (ig->state_storage(i).succ == 0) + continue; + doskip[i] = true; + formula new_letter_form + = bdd2form(ig->state_storage(i).new_label); +#ifdef NDEBUG + res[new_letter_form] = bdd2form(all_cond[i]); +#else + // Check if they are the same + formula old_form = bdd2form(all_cond[i]); + if (res.count(new_letter_form) == 0) + res[new_letter_form] = old_form; + else + assert(res[new_letter_form] == old_form); +#endif + } + } + return res; +} + +/// \brief Tries to partition the given condition vector \a all_cond +/// abandons at \a max_letter. +/// \return The corresponding bdd_partition +/// \note A pointer to all_cond is captured internally, it needs +/// to outlive the returned bdd_partition +bdd_partition +try_partition_me(const std::vector& all_cond, unsigned max_letter) +{ + // We create vector that will be succesively filled. + // Each entry corresponds to a "letter", of the partition + const size_t Norig = all_cond.size(); + + bdd_partition result(all_cond); + + auto& treated = result.treated; + auto& ig = *result.ig; + + for (unsigned io = 0; io < Norig; ++io) + { + bdd cond = all_cond[io]; + const auto Nt = treated.size(); + for (size_t in = 0; in < Nt; ++in) + { + if (cond == bddfalse) + break; + if (treated[in].first == cond) + { + // Found this very condition -> make transition + ig.new_edge(io, treated[in].second); + cond = bddfalse; + break; + } + if (bdd_have_common_assignment(treated[in].first, cond)) + { + bdd inter = treated[in].first & cond; + // Create two new states + unsigned ssplit = ig.new_states(2); + // ssplit becomes the state without the intersection + // ssplit + 1 becomes the intersection + // Both of them are implied by the original node, + // Only inter is implied by the current letter + ig.new_edge(treated[in].second, ssplit); + ig.new_edge(treated[in].second, ssplit+1); + ig.new_edge(io, ssplit+1); + treated.emplace_back(inter, ssplit+1); + // Update + cond -= inter; + treated[in].first -= inter; + treated[in].second = ssplit; + if (treated.size() > max_letter) + return bdd_partition{}; + } + } + if (cond != bddfalse) + { + unsigned sc = ig.new_state(); + treated.emplace_back(cond, sc); + ig.new_edge(io, sc); + } + } + + result.relabel_succ = true; + return result; +} \ No newline at end of file diff --git a/spot/priv/partitioned_relabel.hh b/spot/priv/partitioned_relabel.hh new file mode 100644 index 000000000..cd19ffaea --- /dev/null +++ b/spot/priv/partitioned_relabel.hh @@ -0,0 +1,81 @@ +// -*- coding: utf-8 -*- +// Copyright (C) 2022 Laboratoire de Recherche +// de l'Epita (LRE). +// +// 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 . + +#pragma once + +#include +#include +#include +#include +#include +#include +#include + + +using namespace spot; + +struct bdd_partition +{ + struct S + { + bdd new_label = bddfalse; + }; + struct T + { + }; + using implication_graph = digraph; + + // A pointer to the conditions to be partitioned + const std::vector* all_cond_ptr; + // Graph with the invariant that + // children imply parents + // Leaves from the partition + // original conditions are "root" nodes + std::unique_ptr ig; + // todo: technically there are at most two successors, so a graph + // is "too" generic + // All conditions currently part of the partition + // unsigned corresponds to the associated node + std::vector> treated; + std::vector new_aps; + bool relabel_succ = false; + + bdd_partition() = default; + bdd_partition(const std::vector& all_cond) + : all_cond_ptr(&all_cond) + , ig{std::make_unique(2*all_cond.size(), + 2*all_cond.size())} + { + // Create the roots of all old conditions + // Each condition is associated to the state with + // the same index + const unsigned Norig = all_cond.size(); + ig->new_states(Norig); + } + + // Facilitate conversion + // This can only be called when letters have already + // been computed + relabeling_map + to_relabeling_map(twa_graph& for_me) const; +}; // bdd_partition + + +bdd_partition +try_partition_me(const std::vector& all_cond, unsigned max_letter); \ No newline at end of file diff --git a/spot/twaalgos/relabel.cc b/spot/twaalgos/relabel.cc index ac1556aec..ba5e4ed14 100644 --- a/spot/twaalgos/relabel.cc +++ b/spot/twaalgos/relabel.cc @@ -20,60 +20,275 @@ #include "config.h" #include #include +#include + +#include + +#include +#include +#include + namespace spot { - void - relabel_here(twa_graph_ptr& aut, relabeling_map* relmap) + namespace { - std::unique_ptr pairs(bdd_newpair()); - auto d = aut->get_dict(); - std::vector vars; - std::set newvars; - vars.reserve(relmap->size()); - bool bool_subst = false; - auto aplist = aut->ap(); - for (auto& p: *relmap) - { - if (!p.first.is(op::ap)) - throw std::runtime_error - ("relabel_here: old labels should be atomic propositions"); - if (!p.second.is_boolean()) - throw std::runtime_error - ("relabel_here: new labels should be Boolean formulas"); + void + comp_new_letters(bdd_partition& part, + twa_graph& aut, + const std::string& var_prefix, + bool split) + { + auto& ig = *part.ig; + const auto& treated = part.treated; + auto& new_aps = part.new_aps; + // Get the new variables and their negations + const unsigned Nnl = treated.size(); + const unsigned Nnv = std::ceil(std::log2(Nnl)); + std::vector> Nv_vec(Nnv); - // Don't attempt to rename APs that are not used. - if (std::find(aplist.begin(), aplist.end(), p.first) == aplist.end()) - continue; + new_aps.reserve(Nnv); + for (unsigned i = 0; i < Nnv; ++i) + { + // todo check if it does not exist / use anonymous? + new_aps.push_back(formula::ap(var_prefix+std::to_string(i))); + int v = aut.register_ap(new_aps.back()); + Nv_vec[i] = {bdd_nithvar(v), bdd_ithvar(v)}; + } - int oldv = aut->register_ap(p.first); - vars.emplace_back(oldv); - if (p.second.is(op::ap)) - { - int newv = aut->register_ap(p.second); - newvars.insert(newv); - bdd_setpair(pairs.get(), oldv, newv); - } - else - { - p.second.traverse([&](const formula& f) - { - if (f.is(op::ap)) - newvars.insert(aut->register_ap(f)); - return false; - }); - bdd newb = formula_to_bdd(p.second, d, aut); - bdd_setbddpair(pairs.get(), oldv, newb); - bool_subst = true; - } + auto leaveidx2label = [&](unsigned idx) + { + unsigned c = 0; + unsigned rem = idx; + bdd thisbdd = bddtrue; + while (rem) + { + thisbdd &= Nv_vec[c][rem & 1]; + ++c; + rem >>= 1; + } + for (; c < Nnv; ++c) + thisbdd &= Nv_vec[c][0]; + return thisbdd; + }; + + // Compute only labels of leaves + for (unsigned idx = 0; idx < Nnl; ++idx) + ig.state_storage(treated[idx].second).new_label = leaveidx2label(idx); + + // We will label the implication graph with the new letters + auto relabel_impl = [&](unsigned s, auto&& relabel_impl_rec) + { + auto& ss = ig.state_storage(s); + if (ss.new_label != bddfalse) + return ss.new_label; + else + { + assert((ss.succ != 0) && "Should not be a leave"); + bdd thisbdd = bddfalse; + for (const auto& e : ig.out(s)) + thisbdd |= relabel_impl_rec(e.dst, relabel_impl_rec); + ss.new_label = thisbdd; + return thisbdd; + } + }; + + if (!split) + { + // For split only leaves is ok, + // disjunction is done via transitions + // This will compute the new_label for all states in the ig + const unsigned Norig = part.all_cond_ptr->size(); + for (unsigned i = 0; i < Norig; ++i) + relabel_impl(i, relabel_impl); + } + } // comp_new_letters + + // Recursive traversal of implication graph + void replace_label_(unsigned si, + unsigned esrc, unsigned edst, + bdd& econd, + const bdd_partition::implication_graph& ig, + twa_graph& aut) + { + auto& sstore = ig.state_storage(si); + if (sstore.succ == 0) + { + if (econd == bddfalse) + econd = sstore.new_label; + else + aut.new_edge(esrc, edst, sstore.new_label); + } + else + { + for (const auto& e_ig : ig.out(si)) + replace_label_(e_ig.dst, esrc, edst, econd, ig, aut); + } + } + + relabeling_map + partitioned_relabel_here_(twa_graph& aut, bool split, + unsigned max_letter, + unsigned max_letter_mult, + const bdd& concerned_ap, + bool treat_all, + const std::string& var_prefix) + { + auto abandon = []() + { + return relabeling_map{}; + }; + + + // When split we need to distiguish effectively new and old edges + if (split) + { + aut.get_graph().remove_dead_edges_(); + aut.get_graph().sort_edges_(); + aut.get_graph().chain_edges_(); + } + + // Get all conditions present in the automaton + std::vector all_cond; + bdd ignoredcon = bddtrue; + std::unordered_map all_cond_id2idx; + + all_cond.reserve(0.1*aut.num_edges()); + all_cond_id2idx.reserve(0.1*aut.num_edges()); + + // Map for all supports + // and whether or not they are to be relabeled + std::unordered_map, bdd_hash> all_supports; + + for (const auto& e : aut.edges()) + { + auto it = all_supports.find(e.cond); + if (it != all_supports.end()) + continue; // Already treated + bdd se = bddtrue; + bool is_concerned = true; + if (!treat_all) + { + se = bdd_support(e.cond); + is_concerned = bdd_implies(concerned_ap, se); + } + + all_supports.emplace(e.cond, + std::make_pair(is_concerned, se)); + + if (!is_concerned) + { + assert(bdd_existcomp(se, concerned_ap) == bddtrue + && "APs are not partitioned"); + continue; + } + + auto [_, ins] = + all_cond_id2idx.try_emplace(e.cond.id(), all_cond.size()); + if (ins) + { + all_cond.push_back(e.cond); + if (all_cond.size() > max_letter) + return abandon(); + } + } + + unsigned stop = max_letter; + if (max_letter_mult != -1u) + { + // Make sure it does not overflow + if (max_letter_mult <= (-1u / ((unsigned) all_cond.size()))) + stop = std::min(stop, + (unsigned) (max_letter_mult*all_cond.size())); + } + + auto this_partition = try_partition_me(all_cond, stop); + + if (!this_partition.relabel_succ) + return abandon(); + + comp_new_letters(this_partition, aut, var_prefix, split); + + // An original condition is represented by all leaves that imply it + auto& ig = *this_partition.ig; + const unsigned Ns = aut.num_states(); + const unsigned Nt = aut.num_edges(); + for (unsigned s = 0; s < Ns; ++s) + { + for (auto& e : aut.out(s)) + { + if (aut.edge_number(e) > Nt) + continue; + if (!all_supports.at(e.cond).first) + continue; // Edge not concerned + unsigned idx = all_cond_id2idx[e.cond.id()]; + + if (split) + { + // initial call + // We can not hold a ref to the edge + // as the edgevector might get reallocated + bdd econd = bddfalse; + unsigned eidx = aut.edge_number(e); + replace_label_(idx, e.src, e.dst, + econd, ig, aut); + aut.edge_storage(eidx).cond = econd; + } + else + e.cond = ig.state_storage(idx).new_label; + } // for edge + } // for state + return this_partition.to_relabeling_map(aut); + } + + void + relabel_here_ap_(twa_graph_ptr& aut_ptr, relabeling_map relmap) + { + assert(aut_ptr); + twa_graph& aut = *aut_ptr; + + std::unique_ptr pairs(bdd_newpair()); + auto d = aut.get_dict(); + std::vector vars; + std::set newvars; + vars.reserve(relmap.size()); + bool bool_subst = false; + auto aplist = aut.ap(); + + for (auto& p: relmap) + { + // Don't attempt to rename APs that are not used. + if (std::find(aplist.begin(), aplist.end(), p.first) == aplist.end()) + continue; + + int oldv = aut.register_ap(p.first); + vars.emplace_back(oldv); + if (p.second.is(op::ap)) + { + int newv = aut.register_ap(p.second); + newvars.insert(newv); + bdd_setpair(pairs.get(), oldv, newv); + } + else + { + p.second.traverse([&](const formula& f) + { + if (f.is(op::ap)) + newvars.insert(aut.register_ap(f)); + return false; + }); + bdd newb = formula_to_bdd(p.second, d, aut_ptr); + bdd_setbddpair(pairs.get(), oldv, newb); + bool_subst = true; + } } bool need_cleanup = false; typedef bdd (*op_t)(const bdd&, bddPair*); op_t op = bool_subst ? static_cast(bdd_veccompose) : static_cast(bdd_replace); - for (auto& t: aut->edges()) + for (auto& t: aut.edges()) { bdd c = (*op)(t.cond, pairs.get()); t.cond = c; @@ -86,14 +301,172 @@ namespace spot // p0) for (auto v: vars) if (newvars.find(v) == newvars.end()) - aut->unregister_ap(v); + aut.unregister_ap(v); // If some of the edges were relabeled false, we need to clean the // automaton. if (need_cleanup) { - aut->merge_edges(); // remove any edge labeled by 0 - aut->purge_dead_states(); // remove useless states + aut.merge_edges(); // remove any edge labeled by 0 + aut.purge_dead_states(); // remove useless states } + } + + void + relabel_here_gen_(twa_graph_ptr& aut_ptr, relabeling_map relmap) + { + assert(aut_ptr); + twa_graph& aut = *aut_ptr; + + auto form2bdd = [this_dict = aut.get_dict()](const formula& f) + { + return formula_to_bdd(f, this_dict, this_dict); + }; + + auto bdd2form = [bdddict = aut.get_dict()](const bdd& cond) + { + return bdd_to_formula(cond, bdddict); + }; + + + // translate formula -> bdd + std::unordered_map base_letters; + base_letters.reserve(relmap.size()); + + std::unordered_map comp_letters; + std::unordered_set ignored_letters; + + // Necessary to detect unused + bdd new_var_supp = bddtrue; + auto translate = [&](bdd& cond) + { + // Check if known + for (const auto& map : {base_letters, comp_letters}) + { + auto it = map.find(cond); + if (it != map.end()) + { + cond = it->second; + return; + } + } + + // Check if known to be ignored + if (auto it = ignored_letters.find(cond); + it != ignored_letters.end()) + return; + + // Check if ignored + bdd cond_supp = bdd_support(cond); + if (!bdd_implies(new_var_supp, cond_supp)) + { + ignored_letters.insert(cond); + assert(bdd_existcomp(cond_supp, new_var_supp) == bddtrue + && "APs are not partitioned"); + return; + } + + // Compute + // compose the given cond from a disjunction of base_letters + bdd old_cond = bddfalse; + for (const auto& [k, v] : base_letters) + { + if (bdd_implies(k, cond)) + old_cond |= v; + } + comp_letters[cond] = old_cond; + cond = old_cond; + return; + }; + + for (const auto& [new_f, old_f] : relmap) + { + bdd new_cond = form2bdd(new_f); + new_var_supp &= bdd_support(new_cond); + base_letters[new_cond] = form2bdd(old_f); + } + + + // Save the composed letters? With a special seperator like T/F? + // Is swapping between formula <-> bdd expensive + for (auto& e : aut.edges()) + translate(e.cond); + + // Remove the new auxilliary variables from the aut + bdd c_supp = new_var_supp; + while (c_supp != bddtrue) + { + aut.unregister_ap(bdd_var(c_supp)); + c_supp = bdd_high(c_supp); + } + + return; + } + + } // Namespace + + void + relabel_here(twa_graph_ptr& aut, relabeling_map* relmap) + { + if (!relmap || relmap->empty()) + return; + + // There are two different types of relabeling maps: + // 1) The "traditional": + // New atomic propositions (keys) correspond to general formulas over + // the original propositions (values) + // 2) The one resulting from partitioned_relabel_here + // Here general (boolean) formulas over new propositions (keys) + // are associated to general formulas over + // the original propositions (values) + + if (!std::all_of(relmap->begin(), relmap->end(), + [](const auto& it){return it.first.is_boolean() + && it.second.is_boolean(); })) + throw std::runtime_error + ("relabel_here: old labels and new labels " + "should be Boolean formulas"); + + bool only_ap = std::all_of(relmap->cbegin(), relmap->cend(), + [](const auto& p) + { + return p.first.is(op::ap); + }); + + if (only_ap) + relabel_here_ap_(aut, *relmap); + else + relabel_here_gen_(aut, *relmap); + } + + relabeling_map + partitioned_relabel_here(twa_graph_ptr& aut, + bool split, + unsigned max_letter, + unsigned max_letter_mult, + const bdd& concerned_ap, + std::string var_prefix) + { + if (!aut) + throw std::runtime_error("aut is null!"); + + if (std::find_if(aut->ap().cbegin(), aut->ap().cend(), + [var_prefix](const auto& ap) + { + return ap.ap_name().find(var_prefix) == 0; + }) != aut->ap().cend()) + throw std::runtime_error("partitioned_relabel_here(): " + "The given prefix for new variables may not appear as " + "a prefix of existing variables."); + + // If concerned_ap == bddtrue -> all aps are concerned + bool treat_all = concerned_ap == bddtrue; + bdd concerned_ap_ + = treat_all ? aut->ap_vars() : concerned_ap; + return partitioned_relabel_here_(*aut, split, + max_letter, max_letter_mult, + concerned_ap_, + treat_all, + var_prefix); } } diff --git a/spot/twaalgos/relabel.hh b/spot/twaalgos/relabel.hh index e10fe8903..34f7a0a41 100644 --- a/spot/twaalgos/relabel.hh +++ b/spot/twaalgos/relabel.hh @@ -21,6 +21,10 @@ #include #include +#include + +#include +#include namespace spot { @@ -33,4 +37,33 @@ namespace spot /// or relabel_bse(). SPOT_API void relabel_here(twa_graph_ptr& aut, relabeling_map* relmap); + + + /// \brief Replace conditions in \a aut with non-overlapping conditions + /// over fresh variables. + /// + /// Partitions the conditions in the automaton, then (binary) encodes + /// them using fresh propositions. + /// This can lead to an exponential explosion in the number of + /// conditions. The operations is aborted if either + /// the number of new letters (subsets of the partition) exceeds + /// \a max_letter OR \a max_letter_mult times the number of conditions + /// in the original automaton. + /// The argument \a concerned_ap can be used to filter out transitions. + /// If given, only the transitions whose support intersects the + /// concerned_ap (or whose condition is T) are taken into account. + /// The fresh aps will be enumerated and prefixed by \a var_prefix. + /// These variables need to be fresh, i.e. may not exist yet (not checked) + /// + /// \note If concerned_ap is given, then there may not be an edge + /// whose condition uses ap inside AND outside of concerned_ap. + /// Mostly used in a game setting to distinguish between + /// env and player transitions. + SPOT_API relabeling_map + partitioned_relabel_here(twa_graph_ptr& aut, bool split = false, + unsigned max_letter = -1u, + unsigned max_letter_mult = -1u, + const bdd& concerned_ap = bddtrue, + std::string var_prefix = "__nv"); + } diff --git a/tests/Makefile.am b/tests/Makefile.am index 4c2fe830c..0810df809 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -436,6 +436,7 @@ TESTS_python = \ python/origstate.py \ python/otfcrash.py \ python/parsetgba.py \ + python/_partitioned_relabel.ipynb \ python/parity.py \ python/pdegen.py \ python/prodexpt.py \ diff --git a/tests/python/_partitioned_relabel.ipynb b/tests/python/_partitioned_relabel.ipynb new file mode 100644 index 000000000..a9c1c7af7 --- /dev/null +++ b/tests/python/_partitioned_relabel.ipynb @@ -0,0 +1,1224 @@ +{ + "cells": [ + { + "cell_type": "code", + "execution_count": 1, + "id": "4d896402", + "metadata": {}, + "outputs": [], + "source": [ + "import spot, buddy" + ] + }, + { + "cell_type": "markdown", + "id": "94e87f9c", + "metadata": {}, + "source": [ + "# Partitioned relabeling\n", + "\n", + "Partitioned relabeling will:\n", + "First compute a partition over all conditions appearing in the automaton.\n", + "That is, the set of new conditions is such that (1) they do not overlap (2) all valuations that verify some condition in the original automaton also verify (exactly one) of the new conditions.\n", + "These new conditions can be thought of as letters in a \"classical\" sense.\n", + "Then we create new aps and encode the \"number\" of these letters using the fresh aps, resulting in new letters which are a single valuation over the fresh aps.\n", + "\n", + "This can be helpful if there are many aps, but few different conditions over them\n", + "\n", + "The algorithm comes in two flavours:\n", + "\n", + "We maintain the original number of edges. Therefore the new label correspond to a disjunction over new letters (split=False).\n", + "We split each edge into its letters, creating more edges (split=True)." + ] + }, + { + "cell_type": "code", + "execution_count": 2, + "id": "62123fa9", + "metadata": {}, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "t\n", + "[all]\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "1\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "0->1\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "2\n", + "\n", + "2\n", + "\n", + "\n", + "\n", + "0->2\n", + "\n", + "\n", + "a\n", + "\n", + "\n", + "\n", + "3\n", + "\n", + "3\n", + "\n", + "\n", + "\n", + "0->3\n", + "\n", + "\n", + "a & b0 & b1 & b2\n", + "\n", + "\n", + "\n", + "4\n", + "\n", + "4\n", + "\n", + "\n", + "\n", + "0->4\n", + "\n", + "\n", + "a & !b0 & !b1 & !b2\n", + "\n", + "\n", + "\n" + ], + "text/plain": [ + " *' at 0x7f65b0311a80> >" + ] + }, + "execution_count": 2, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "#Relabeling a graph\n", + "aut = spot.make_twa_graph()\n", + "aut.new_states(5)\n", + "\n", + "a = buddy.bdd_ithvar(aut.register_ap(\"a\"))\n", + "na = buddy.bdd_nithvar(aut.register_ap(\"a\"))\n", + "b0 = buddy.bdd_ithvar(aut.register_ap(\"b0\"))\n", + "nb0 = buddy.bdd_nithvar(aut.register_ap(\"b0\"))\n", + "b1 = buddy.bdd_ithvar(aut.register_ap(\"b1\"))\n", + "nb1 = buddy.bdd_nithvar(aut.register_ap(\"b1\"))\n", + "b2 = buddy.bdd_ithvar(aut.register_ap(\"b2\"))\n", + "nb2 = buddy.bdd_nithvar(aut.register_ap(\"b2\"))\n", + "\n", + "aut.new_edge(0,1,buddy.bddtrue)\n", + "aut.new_edge(0,2,a)\n", + "aut.new_edge(0,3,a&b0&b1&b2)\n", + "aut.new_edge(0,4,a&nb0&nb1&nb2)\n", + "\n", + "aut" + ] + }, + { + "cell_type": "code", + "execution_count": 3, + "id": "d4c8e977", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "6\n" + ] + }, + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "t\n", + "[all]\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "1\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "0->1\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "2\n", + "\n", + "2\n", + "\n", + "\n", + "\n", + "0->2\n", + "\n", + "\n", + "__nv0 | __nv1\n", + "\n", + "\n", + "\n", + "3\n", + "\n", + "3\n", + "\n", + "\n", + "\n", + "0->3\n", + "\n", + "\n", + "!__nv0 & __nv1\n", + "\n", + "\n", + "\n", + "4\n", + "\n", + "4\n", + "\n", + "\n", + "\n", + "0->4\n", + "\n", + "\n", + "__nv0 & __nv1\n", + "\n", + "\n", + "\n" + ], + "text/plain": [ + " *' at 0x7f65b0311a80> >" + ] + }, + "execution_count": 3, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "relabel_dict = spot.partitioned_relabel_here(aut)\n", + "\n", + "print(relabel_dict.size())\n", + "aut" + ] + }, + { + "cell_type": "code", + "execution_count": 4, + "id": "6f90a095", + "metadata": {}, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "t\n", + "[all]\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "1\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "0->1\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "2\n", + "\n", + "2\n", + "\n", + "\n", + "\n", + "0->2\n", + "\n", + "\n", + "a\n", + "\n", + "\n", + "\n", + "3\n", + "\n", + "3\n", + "\n", + "\n", + "\n", + "0->3\n", + "\n", + "\n", + "a & b0 & b1 & b2\n", + "\n", + "\n", + "\n", + "4\n", + "\n", + "4\n", + "\n", + "\n", + "\n", + "0->4\n", + "\n", + "\n", + "a & !b0 & !b1 & !b2\n", + "\n", + "\n", + "\n" + ], + "text/plain": [ + " *' at 0x7f65b0311a80> >" + ] + }, + "execution_count": 4, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Undo the relabeling\n", + "spot.relabel_here(aut, relabel_dict)\n", + "aut" + ] + }, + { + "cell_type": "code", + "execution_count": 5, + "id": "513067ab", + "metadata": {}, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "t\n", + "[all]\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "1\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "0->1\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "2\n", + "\n", + "2\n", + "\n", + "\n", + "\n", + "0->2\n", + "\n", + "\n", + "a\n", + "\n", + "\n", + "\n", + "3\n", + "\n", + "3\n", + "\n", + "\n", + "\n", + "0->3\n", + "\n", + "\n", + "a & b0 & b1 & b2\n", + "\n", + "\n", + "\n", + "4\n", + "\n", + "4\n", + "\n", + "\n", + "\n", + "0->4\n", + "\n", + "\n", + "a & !b0 & !b1 & !b2\n", + "\n", + "\n", + "\n" + ], + "text/plain": [ + " *' at 0x7f65b02c0d50> >" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "HOA: v1\n", + "States: 5\n", + "Start: 0\n", + "AP: 6 \"a\" \"b0\" \"b1\" \"b2\" \"__nv0\" \"__nv1\"\n", + "acc-name: all\n", + "Acceptance: 0 t\n", + "properties: trans-labels explicit-labels state-acc\n", + "--BODY--\n", + "State: 0\n", + "[!4&!5] 1\n", + "[4&!5] 2\n", + "[!4&5] 3\n", + "[4&5] 4\n", + "[4&!5] 1\n", + "[4&5] 1\n", + "[!4&5] 1\n", + "[4&5] 2\n", + "[!4&5] 2\n", + "State: 1\n", + "State: 2\n", + "State: 3\n", + "State: 4\n", + "--END--\n" + ] + }, + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "t\n", + "[all]\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "1\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "0->1\n", + "\n", + "\n", + "!__nv0 & !__nv1\n", + "\n", + "\n", + "\n", + "0->1\n", + "\n", + "\n", + "__nv0 & !__nv1\n", + "\n", + "\n", + "\n", + "0->1\n", + "\n", + "\n", + "__nv0 & __nv1\n", + "\n", + "\n", + "\n", + "0->1\n", + "\n", + "\n", + "!__nv0 & __nv1\n", + "\n", + "\n", + "\n", + "2\n", + "\n", + "2\n", + "\n", + "\n", + "\n", + "0->2\n", + "\n", + "\n", + "__nv0 & !__nv1\n", + "\n", + "\n", + "\n", + "0->2\n", + "\n", + "\n", + "__nv0 & __nv1\n", + "\n", + "\n", + "\n", + "0->2\n", + "\n", + "\n", + "!__nv0 & __nv1\n", + "\n", + "\n", + "\n", + "3\n", + "\n", + "3\n", + "\n", + "\n", + "\n", + "0->3\n", + "\n", + "\n", + "!__nv0 & __nv1\n", + "\n", + "\n", + "\n", + "4\n", + "\n", + "4\n", + "\n", + "\n", + "\n", + "0->4\n", + "\n", + "\n", + "__nv0 & __nv1\n", + "\n", + "\n", + "\n" + ], + "text/plain": [ + " *' at 0x7f65b02c0d50> >" + ] + }, + "execution_count": 5, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Relabeling the same graph using the split option\n", + "aut = spot.make_twa_graph()\n", + "aut.new_states(5)\n", + "\n", + "a = buddy.bdd_ithvar(aut.register_ap(\"a\"))\n", + "na = buddy.bdd_nithvar(aut.register_ap(\"a\"))\n", + "b0 = buddy.bdd_ithvar(aut.register_ap(\"b0\"))\n", + "nb0 = buddy.bdd_nithvar(aut.register_ap(\"b0\"))\n", + "b1 = buddy.bdd_ithvar(aut.register_ap(\"b1\"))\n", + "nb1 = buddy.bdd_nithvar(aut.register_ap(\"b1\"))\n", + "b2 = buddy.bdd_ithvar(aut.register_ap(\"b2\"))\n", + "nb2 = buddy.bdd_nithvar(aut.register_ap(\"b2\"))\n", + "\n", + "aut.new_edge(0,1,buddy.bddtrue)\n", + "aut.new_edge(0,2,a)\n", + "aut.new_edge(0,3,a&b0&b1&b2)\n", + "aut.new_edge(0,4,a&nb0&nb1&nb2)\n", + "\n", + "display(aut)\n", + "xx = spot.partitioned_relabel_here(aut, True)\n", + "print(aut.to_str(\"hoa\"))\n", + "aut" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "id": "50c6a08b", + "metadata": {}, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "t\n", + "[all]\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "1\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "0->1\n", + "\n", + "\n", + "!a\n", + "\n", + "\n", + "\n", + "0->1\n", + "\n", + "\n", + "(a & !b0 & b2) | (a & b0 & !b2) | (a & !b1 & b2) | (a & b1 & !b2)\n", + "\n", + "\n", + "\n", + "0->1\n", + "\n", + "\n", + "a & !b0 & !b1 & !b2\n", + "\n", + "\n", + "\n", + "0->1\n", + "\n", + "\n", + "a & b0 & b1 & b2\n", + "\n", + "\n", + "\n", + "2\n", + "\n", + "2\n", + "\n", + "\n", + "\n", + "0->2\n", + "\n", + "\n", + "(a & !b0 & b2) | (a & b0 & !b2) | (a & !b1 & b2) | (a & b1 & !b2)\n", + "\n", + "\n", + "\n", + "0->2\n", + "\n", + "\n", + "a & !b0 & !b1 & !b2\n", + "\n", + "\n", + "\n", + "0->2\n", + "\n", + "\n", + "a & b0 & b1 & b2\n", + "\n", + "\n", + "\n", + "3\n", + "\n", + "3\n", + "\n", + "\n", + "\n", + "0->3\n", + "\n", + "\n", + "a & b0 & b1 & b2\n", + "\n", + "\n", + "\n", + "4\n", + "\n", + "4\n", + "\n", + "\n", + "\n", + "0->4\n", + "\n", + "\n", + "a & !b0 & !b1 & !b2\n", + "\n", + "\n", + "\n" + ], + "text/plain": [ + " *' at 0x7f65b02c0d50> >" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Undo the relabeling -> disjoint conditions over the original ap\n", + "spot.relabel_here(aut, relabel_dict)\n", + "aut" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "id": "d2efd313", + "metadata": {}, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "t\n", + "[all]\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "1\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "0->1\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "2\n", + "\n", + "2\n", + "\n", + "\n", + "\n", + "0->2\n", + "\n", + "\n", + "a\n", + "\n", + "\n", + "\n", + "3\n", + "\n", + "3\n", + "\n", + "\n", + "\n", + "0->3\n", + "\n", + "\n", + "a & b\n", + "\n", + "\n", + "\n", + "4\n", + "\n", + "4\n", + "\n", + "\n", + "\n", + "0->4\n", + "\n", + "\n", + "a & !b\n", + "\n", + "\n", + "\n", + "5\n", + "\n", + "5\n", + "\n", + "\n", + "\n", + "0->5\n", + "\n", + "\n", + "c\n", + "\n", + "\n", + "\n" + ], + "text/plain": [ + " *' at 0x7f65b02c90f0> >" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "HOA: v1\n", + "States: 6\n", + "Start: 0\n", + "AP: 5 \"a\" \"__nv0\" \"__nv1\" \"b\" \"c\"\n", + "acc-name: all\n", + "Acceptance: 0 t\n", + "properties: trans-labels explicit-labels state-acc\n", + "--BODY--\n", + "State: 0\n", + "[!1 | !2] 1\n", + "[!1&2 | 1&!2] 2\n", + "[!1&2] 3\n", + "[1&!2] 4\n", + "[4] 5\n", + "State: 1\n", + "State: 2\n", + "State: 3\n", + "State: 4\n", + "State: 5\n", + "--END--\n" + ] + }, + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "t\n", + "[all]\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "1\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "0->1\n", + "\n", + "\n", + "!__nv0 | !__nv1\n", + "\n", + "\n", + "\n", + "2\n", + "\n", + "2\n", + "\n", + "\n", + "\n", + "0->2\n", + "\n", + "\n", + "(__nv0 & !__nv1) | (!__nv0 & __nv1)\n", + "\n", + "\n", + "\n", + "3\n", + "\n", + "3\n", + "\n", + "\n", + "\n", + "0->3\n", + "\n", + "\n", + "!__nv0 & __nv1\n", + "\n", + "\n", + "\n", + "4\n", + "\n", + "4\n", + "\n", + "\n", + "\n", + "0->4\n", + "\n", + "\n", + "__nv0 & !__nv1\n", + "\n", + "\n", + "\n", + "5\n", + "\n", + "5\n", + "\n", + "\n", + "\n", + "0->5\n", + "\n", + "\n", + "c\n", + "\n", + "\n", + "\n" + ], + "text/plain": [ + " *' at 0x7f65b02c90f0> >" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# Working only on a subset of the aps\n", + "# Note that True is always relabeled\n", + "\n", + "aut = spot.make_twa_graph()\n", + "aut.new_states(6)\n", + "\n", + "a = buddy.bdd_ithvar(aut.register_ap(\"a\"))\n", + "na = buddy.bdd_nithvar(aut.register_ap(\"a\"))\n", + "b = buddy.bdd_ithvar(aut.register_ap(\"b\"))\n", + "nb = buddy.bdd_nithvar(aut.register_ap(\"b\"))\n", + "c = buddy.bdd_ithvar(aut.register_ap(\"c\"))\n", + "nc = buddy.bdd_nithvar(aut.register_ap(\"c\"))\n", + "\n", + "aut.new_edge(0,1,buddy.bddtrue)\n", + "aut.new_edge(0,2,a)\n", + "aut.new_edge(0,3,a&b)\n", + "aut.new_edge(0,4,a&nb)\n", + "aut.new_edge(0,5,c)\n", + "\n", + "display(aut)\n", + "\n", + "concerned_aps = a & b # concerned aps are given as a conjunction of positive aps\n", + "# As partitioning can be exponentially costly,\n", + "# one can limit the number of new letters generated before abadoning\n", + "# This can be done either as a hard limit and/or as the number of current condition\n", + "# times a factor\n", + "relabel_dict = spot.partitioned_relabel_here(aut, False, 1000, 1000, concerned_aps)\n", + "print(aut.to_str(\"hoa\"))\n", + "aut" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "id": "1fbc8813", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "HOA: v1\n", + "States: 6\n", + "Start: 0\n", + "AP: 3 \"a\" \"b\" \"c\"\n", + "acc-name: all\n", + "Acceptance: 0 t\n", + "properties: trans-labels explicit-labels state-acc\n", + "--BODY--\n", + "State: 0\n", + "[t] 1\n", + "[0] 2\n", + "[0&1] 3\n", + "[0&!1] 4\n", + "[2] 5\n", + "State: 1\n", + "State: 2\n", + "State: 3\n", + "State: 4\n", + "State: 5\n", + "--END--\n" + ] + }, + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "t\n", + "[all]\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "1\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "0->1\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "2\n", + "\n", + "2\n", + "\n", + "\n", + "\n", + "0->2\n", + "\n", + "\n", + "a\n", + "\n", + "\n", + "\n", + "3\n", + "\n", + "3\n", + "\n", + "\n", + "\n", + "0->3\n", + "\n", + "\n", + "a & b\n", + "\n", + "\n", + "\n", + "4\n", + "\n", + "4\n", + "\n", + "\n", + "\n", + "0->4\n", + "\n", + "\n", + "a & !b\n", + "\n", + "\n", + "\n", + "5\n", + "\n", + "5\n", + "\n", + "\n", + "\n", + "0->5\n", + "\n", + "\n", + "c\n", + "\n", + "\n", + "\n" + ], + "text/plain": [ + " *' at 0x7f65b02c90f0> >" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "#undo partial relabeling\n", + "spot.relabel_here(aut, relabel_dict)\n", + "print(aut.to_str(\"hoa\"))\n", + "aut" + ] + } + ], + "metadata": { + "kernelspec": { + "display_name": "Python 3 (ipykernel)", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.8.10" + } + }, + "nbformat": 4, + "nbformat_minor": 5 +} diff --git a/tests/python/except.py b/tests/python/except.py index 34aa61ad2..e531882dd 100644 --- a/tests/python/except.py +++ b/tests/python/except.py @@ -359,3 +359,18 @@ except RuntimeError as e: tc.assertIn(filename, str(e)) else: report_missing_exception() + + +# Relabeling must use new variables +aut = spot.make_twa_graph() +aut.new_states(2) +ap = buddy.bdd_ithvar(aut.register_ap("__nv0")) +aut.new_edge(0,1,ap) + +try: + spot.partitioned_relabel_here(aut) +except RuntimeError as e: + tc.assertIn("The given prefix for new variables", + str(e)) +else: + report_missing_exception() \ No newline at end of file From 6e2e7c942e8fce377c5feb031b260120651c0288 Mon Sep 17 00:00:00 2001 From: Philipp Schlehuber-Caissier Date: Tue, 29 Nov 2022 14:58:26 +0100 Subject: [PATCH 211/337] Using partitioned_relabel_here Put the new function to use in order to speed up mealy machine minimization * spot/twaalgos/mealy_machine.cc: Here * spot/twaalgos/synthesis.cc , spot/twaalgos/synthesis.hh: Helper function to relabel games * tests/python/_mealy.ipynb , tests/python/except.py , tests/python/_partitioned_relabel.ipynb: Adapt/expand tests --- spot/twaalgos/mealy_machine.cc | 314 +++++-- spot/twaalgos/synthesis.cc | 95 ++ spot/twaalgos/synthesis.hh | 36 + tests/python/_mealy.ipynb | 292 ++++-- tests/python/_partitioned_relabel.ipynb | 1147 ++++++++++++++++++++++- tests/python/except.py | 20 +- 6 files changed, 1722 insertions(+), 182 deletions(-) diff --git a/spot/twaalgos/mealy_machine.cc b/spot/twaalgos/mealy_machine.cc index 1126ad8e0..df9ad6017 100644 --- a/spot/twaalgos/mealy_machine.cc +++ b/spot/twaalgos/mealy_machine.cc @@ -36,7 +36,9 @@ #include #include #include +#include #include +#include #include @@ -869,7 +871,7 @@ namespace split_cstr_time, prob_init_build_time, sat_time, build_time, refine_time, total_time; long long n_classes, n_refinement, n_lit, n_clauses, - n_iteration, n_bisim_let, n_min_states, done; + n_iteration, n_letters_part, n_bisim_let, n_min_states, done; std::string task; const std::string instance; @@ -892,6 +894,7 @@ namespace , n_lit{-1} , n_clauses{-1} , n_iteration{-1} + , n_letters_part{-1} , n_bisim_let{-1} , n_min_states{-1} , done{-1} @@ -935,8 +938,8 @@ namespace << "player_incomp_time,incomp_time,split_all_let_time," << "split_min_let_time,split_cstr_time,prob_init_build_time," << "sat_time,build_time,refine_time,total_time,n_classes," - << "n_refinement,n_lit,n_clauses,n_iteration,n_bisim_let," - << "n_min_states,done\n"; + << "n_refinement,n_lit,n_clauses,n_iteration,n_letters_part," + << "n_bisim_let,n_min_states,done\n"; } assert(!task.empty()); @@ -965,6 +968,7 @@ namespace f(ss, n_lit); f(ss, n_clauses); f(ss, n_iteration); + f(ss, n_letters_part); f(ss, n_bisim_let); f(ss, n_min_states); f(ss, done, false); @@ -1280,8 +1284,8 @@ namespace } square_matrix - compute_incomp(const_twa_graph_ptr mm, const unsigned n_env, - satprob_info& si) + compute_incomp_impl_(const_twa_graph_ptr mm, const unsigned n_env, + satprob_info& si, bool is_partitioned) { const unsigned n_tot = mm->num_states(); @@ -1292,20 +1296,6 @@ namespace // Have two states already been checked for common pred square_matrix checked_pred(n_env, false); - // We also need a transposed_graph - auto mm_t = make_twa_graph(mm->get_dict()); - mm_t->copy_ap_of(mm); - mm_t->new_states(n_env); - - for (unsigned s = 0; s < n_env; ++s) - { - for (const auto& e_env : mm->out(s)) - { - unsigned dst_env = mm->out(e_env.dst).begin()->dst; - mm_t->new_edge(dst_env, s, e_env.cond); - } - } - // Utility function auto get_cond = [&mm](unsigned s)->const bdd& {return mm->out(s).begin()->cond; }; @@ -1367,15 +1357,28 @@ namespace #endif // direct incomp: Two env states can reach incompatible player states // under the same input + // The original graph mm is not sorted, and most of the + // sorting is not rentable + // However, bdd_have_common_assignment simply becomes equality auto direct_incomp = [&](unsigned s1, unsigned s2) { for (const auto& e1 : mm->out(s1)) for (const auto& e2 : mm->out(s2)) { + if (is_partitioned && (e1.cond != e2.cond)) + continue; if (!is_p_incomp(e1.dst - n_env, e2.dst - n_env)) continue; //Compatible -> no prob // Reachable under same letter? - if (bdd_have_common_assignment(e1.cond, e2.cond)) + if (is_partitioned) // -> Yes + { + trace << s1 << " and " << s2 << " directly incomp " + "due to successors " << e1.dst << " and " << e2.dst + << '\n'; + return true; + } + else if (!is_partitioned + && bdd_have_common_assignment(e1.cond, e2.cond)) { trace << s1 << " and " << s2 << " directly incomp " "due to successors " << e1.dst << " and " << e2.dst @@ -1388,7 +1391,27 @@ namespace // If two states can reach an incompatible state // under the same input, then they are incompatible as well - auto tag_predec = [&](unsigned s1, unsigned s2) + + // Version if the input is not partitioned + // We also need a transposed_graph + twa_graph_ptr mm_t = nullptr; + if (!is_partitioned) + { + mm_t = make_twa_graph(mm->get_dict()); + mm_t->copy_ap_of(mm); + mm_t->new_states(n_env); + + for (unsigned s = 0; s < n_env; ++s) + { + for (const auto& e_env : mm->out(s)) + { + unsigned dst_env = mm->out(e_env.dst).begin()->dst; + mm_t->new_edge(dst_env, s, e_env.cond); + } + } + } + + auto tag_predec_unpart = [&](unsigned s1, unsigned s2) { static std::vector> todo_; assert(todo_.empty()); @@ -1422,17 +1445,98 @@ namespace // Done tagging all pred }; + // Version of taging taking advantaged of partitioned conditions + struct S + { + }; + struct T + { + int id; + }; + std::unique_ptr> mm_t_part; + if (is_partitioned) + { + mm_t_part = std::make_unique>(n_env, mm->num_edges()); + mm_t_part->new_states(n_env); + + for (unsigned s = 0; s < n_env; ++s) + { + for (const auto& e_env : mm->out(s)) + { + unsigned dst_env = mm->out(e_env.dst).begin()->dst; + mm_t_part->new_edge(dst_env, s, e_env.cond.id()); + } + } + + // Now we need to sort the edge to ensure that + // the next algo works correctly + mm_t_part->sort_edges_srcfirst_([](const auto& e1, const auto& e2) + {return e1.id < e2.id; }); + mm_t_part->chain_edges_(); + } + + auto tag_predec_part = [&](unsigned s1, unsigned s2) + { + static std::vector> todo_; + assert(todo_.empty()); + + todo_.emplace_back(s1, s2); + + while (!todo_.empty()) + { + auto [i, j] = todo_.back(); + todo_.pop_back(); + if (checked_pred.get(i, j)) + continue; + // If predecs are already marked incomp + auto e_it_i = mm_t_part->out(i); + auto e_it_j = mm_t_part->out(j); + + auto e_it_i_e = e_it_i.end(); + auto e_it_j_e = e_it_j.end(); + + auto e_i = e_it_i.begin(); + auto e_j = e_it_j.begin(); + + // Joint iteration over both edge groups + while ((e_i != e_it_i_e) && (e_j != e_it_j_e)) + { + if (e_i->id < e_j->id) + ++e_i; + else if (e_j->id < e_i->id) + ++e_j; + else + { + assert(e_j->id == e_i->id); + trace << e_i->dst << " and " << e_j->dst << " tagged incomp" + " due to " << e_i->id << '\n'; + inc_env.set(e_i->dst, e_j->dst, true); + todo_.emplace_back(e_i->dst, e_j->dst); + ++e_i; + ++e_j; + } + } + checked_pred.set(i, j, true); + } + // Done tagging all pred + }; + for (unsigned s1 = 0; s1 < n_env; ++s1) for (unsigned s2 = s1 + 1; s2 < n_env; ++s2) { if (inc_env.get(s1, s2)) continue; // Already done + // Check if they are incompatible for some letter // We have to check all pairs of edges if (direct_incomp(s1, s2)) { inc_env.set(s1, s2, true); - tag_predec(s1, s2); + if (is_partitioned) + tag_predec_part(s1, s2); + else + tag_predec_unpart(s1, s2); + } } @@ -1442,9 +1546,38 @@ namespace #endif si.incomp_time = si.restart(); return inc_env; + } // incomp no partition + + square_matrix + compute_incomp(const_twa_graph_ptr mm, const unsigned n_env, + satprob_info& si, int max_letter_mult) + { + // Try to generate a graph with partitioned env transitions + auto mm2 = make_twa_graph(mm, twa::prop_set::all()); + set_state_players(mm2, get_state_players(mm)); + set_synthesis_outputs(mm2, get_synthesis_outputs(mm)); + + // todo get a good value for cutoff + auto relabel_maps + = partitioned_game_relabel_here(mm2, true, false, true, + false, -1u, max_letter_mult); + bool succ = !relabel_maps.env_map.empty(); + + si.n_letters_part = relabel_maps.env_map.size(); + +#ifdef TRACE + if (succ) + std::cout << "Relabeling succesfull with " << relabel_maps.env_map.size() + << " letters\n"; + else + std::cout << "Relabeling aborted\n"; +#endif + + return compute_incomp_impl_(succ ? const_twa_graph_ptr(mm2) : mm, + n_env, si, succ); } - struct part_sol_t + struct part_sol_t { std::vector psol; std::vector is_psol; @@ -1602,6 +1735,11 @@ namespace return std::make_pair(n_group, which_group); } + // Helper function + // Computes the set of all original letters implied by the leaves + // This avoids transposing the graph + + // Computes the letters of each group // Letters here means bdds such that for all valid // assignments of the bdd we go to the same dst from the same source @@ -1611,7 +1749,9 @@ namespace { //To avoid recalc std::set all_bdd; - std::set treated_bdd; + std::vector all_bdd_v; + std::unordered_map node2idx; + std::unordered_multimap>> sigma_map; @@ -1649,6 +1789,11 @@ namespace continue; else { + // Store bdds as vector for compatibility + all_bdd_v.clear(); // Note: sorted automatically by id + std::transform(all_bdd.begin(), all_bdd.end(), + std::back_inserter(all_bdd_v), + [](int i){return bdd_from_int(i); }); // Insert it already into the sigma_map trace << "Group " << groupidx << " generates a new alphabet\n"; sigma_map.emplace(std::piecewise_construct, @@ -1658,62 +1803,60 @@ namespace } } + // Result red.share_sigma_with.push_back(groupidx); red.all_letters.emplace_back(); auto& group_letters = red.all_letters.back(); - treated_bdd.clear(); + // Compute it + auto this_part = try_partition_me(all_bdd_v, -1u); + assert(this_part.relabel_succ); - for (unsigned s = 0; s < n_env; ++s) + // Transform it + // group_letters is pair + // There are as many new_letters as treated bdds in the partition + group_letters.clear(); + group_letters.reserve(this_part.treated.size()); + node2idx.clear(); + node2idx.reserve(this_part.treated.size()); + + for (const auto& [label, node] : this_part.treated) { - if (red.which_group[s] != groupidx) - continue; - for (const auto& e : mmw->out(s)) - { - bdd rcond = e.cond; - const int econd_id = rcond.id(); - trace << rcond << " - " << econd_id << std::endl; - if (treated_bdd.count(econd_id)) - { - trace << "Already treated" << std::endl; - continue; - } - treated_bdd.insert(econd_id); - - assert(rcond != bddfalse && "Deactivated edges are forbiden"); - // Check against all currently used "letters" - const size_t osize = group_letters.size(); - for (size_t i = 0; i < osize; ++i) - { - if (group_letters[i].first == rcond) - { - rcond = bddfalse; - group_letters[i].second.insert(econd_id); - break; - } - bdd inter = group_letters[i].first & rcond; - if (inter == bddfalse) - continue; // No intersection - if (group_letters[i].first == inter) - group_letters[i].second.insert(econd_id); - else - { - group_letters[i].first -= inter; - group_letters.emplace_back(inter, - group_letters[i].second); - group_letters.back().second.insert(econd_id); - } - - rcond -= inter; - // Early exit? - if (rcond == bddfalse) - break; - } - // Leftovers? - if (rcond != bddfalse) - group_letters.emplace_back(rcond, std::set{econd_id}); - } + node2idx[node] = group_letters.size(); + group_letters.emplace_back(std::piecewise_construct, + std::forward_as_tuple(label), + std::forward_as_tuple()); } + + // Go through the graph for each original letter + auto search_leaves + = [&ig = *this_part.ig, &group_letters, &node2idx] + (int orig_letter_id, unsigned s, auto&& search_leaves_) -> void + { + if (ig.state_storage(s).succ == 0) + { + // Leaf + unsigned idx = node2idx[s]; + auto& setidx = group_letters[idx].second; + setidx.emplace_hint(setidx.end(), orig_letter_id); + } + else + { + // Traverse + for (const auto& e : ig.out(s)) + search_leaves_(orig_letter_id, e.dst, search_leaves_); + } + }; + + const unsigned Norig = all_bdd_v.size(); + for (unsigned s = 0; s < Norig; ++s) + search_leaves(all_bdd_v[s].id(), s, search_leaves); + + // Verify that all letters imply at least one original letter + assert(std::all_of(group_letters.begin(), group_letters.end(), + [](const auto& l){return !l.second.empty(); })); + + #ifdef TRACE trace << "this group letters" << std::endl; auto sp = [&](const auto& c) @@ -3467,6 +3610,7 @@ namespace for (unsigned letter_idx = 0; letter_idx < n_ml; ++letter_idx) { const auto& ml_list = group_map[letter_idx]; + assert(ml_list.begin() != ml_list.end()); // Incompatibility is commutative // new / new constraints const auto it_end = ml_list.end(); @@ -3794,12 +3938,9 @@ namespace return minmach; } // while loop } // try_build_machine -} // namespace -namespace spot -{ - twa_graph_ptr minimize_mealy(const const_twa_graph_ptr& mm, - int premin) + twa_graph_ptr minimize_mealy_(const const_twa_graph_ptr& mm, + int premin, int max_letter_mult) { bdd outputs = ensure_mealy("minimize_mealy", mm); @@ -3866,8 +4007,9 @@ namespace spot si.reorg_time = si.restart(); // Compute incompatibility based on bdd - auto incompmat = compute_incomp(mmw, n_env, si); + auto incompmat = compute_incomp(mmw, n_env, si, max_letter_mult); #ifdef TRACE + std::cerr << "Final incomp mat\n"; incompmat.print(std::cerr); #endif @@ -3944,6 +4086,15 @@ namespace spot minmachine)); return minmachine; } +} // namespace + +namespace spot +{ + twa_graph_ptr minimize_mealy(const const_twa_graph_ptr& mm, + int premin) + { + return minimize_mealy_(mm, premin, 10); + } twa_graph_ptr minimize_mealy(const const_twa_graph_ptr& mm, @@ -3970,7 +4121,8 @@ namespace spot sat_dimacs_file = std::make_unique(dimacsfile); sat_instance_name = si.opt.get_str("satinstancename"); - auto res = minimize_mealy(mm, si.minimize_lvl-4); + auto res = minimize_mealy_(mm, si.minimize_lvl-4, + si.opt.get("max_letter_mult", 10)); sat_csv_file.reset(); sat_dimacs_file.reset(); return res; @@ -4161,7 +4313,7 @@ namespace spot reduce_mealy_here(m, minimize_lvl == 2); } else if (3 <= minimize_lvl) - m = minimize_mealy(m, minimize_lvl - 4); + m = minimize_mealy(m, si); // Convert to demanded output format bool is_split = m->get_named_prop("state-player"); diff --git a/spot/twaalgos/synthesis.cc b/spot/twaalgos/synthesis.cc index 88e22ff04..4e38efd5b 100644 --- a/spot/twaalgos/synthesis.cc +++ b/spot/twaalgos/synthesis.cc @@ -37,6 +37,7 @@ #include +#include // Helper function/structures for split_2step namespace{ @@ -1935,4 +1936,98 @@ namespace spot return res; } + namespace + { + const std::string in_mark_s("__AP_IN__"); + const std::string out_mark_s("__AP_OUT__"); + } + + game_relabeling_map + partitioned_game_relabel_here(twa_graph_ptr& arena, + bool relabel_env, + bool relabel_play, + bool split_env, + bool split_play, + unsigned max_letter, + unsigned max_letter_mult) + { + if (!arena) + throw std::runtime_error("arena is null."); + auto& arena_r = *arena; + + const auto& sp = get_state_players(arena); + bdd all_ap = arena->ap_vars(); + + if (std::find_if(arena->ap().cbegin(), arena->ap().cend(), + [](const auto& ap) + { + return ap.ap_name() == out_mark_s + || ap.ap_name() == in_mark_s; + }) != arena->ap().cend()) + throw std::runtime_error("partitioned_game_relabel_here(): " + "You can not use " + + in_mark_s + " or " + out_mark_s + + " as propositions if relabeling."); + + bdd out_mark = bdd_ithvar(arena_r.register_ap(out_mark_s)); + bdd in_mark = bdd_ithvar(arena_r.register_ap(in_mark_s)); + + bdd outs = get_synthesis_outputs(arena) & out_mark; + bdd ins = bdd_exist(all_ap, outs) & in_mark; + + for (auto& e : arena_r.edges()) + e.cond = e.cond & (sp[e.src] ? out_mark : in_mark); + + game_relabeling_map res; + + if (relabel_env) + res.env_map + = partitioned_relabel_here(arena, split_env, max_letter, + max_letter_mult, ins, "__nv_in"); + if (relabel_play) + res.player_map + = partitioned_relabel_here(arena, split_play, max_letter, + max_letter_mult, outs, "__nv_out"); + return res; + } + + void + relabel_game_here(twa_graph_ptr& arena, + game_relabeling_map& rel_maps) + { + if (!arena) + throw std::runtime_error("arena is null."); + auto& arena_r = *arena; + + // Check that it was partitioned_game_relabel_here + if (!((std::find_if(arena->ap().cbegin(), arena->ap().cend(), + [](const auto& ap) + { return ap.ap_name() == out_mark_s; }) + != arena->ap().cend()) + && (std::find_if(arena->ap().cbegin(), arena->ap().cend(), + [](const auto& ap) + { return ap.ap_name() == in_mark_s; })) + != arena->ap().cend())) + throw std::runtime_error("relabel_game_here(): " + + in_mark_s + " or " + out_mark_s + + " not registered with arena. " + "Not relabeled?"); + + if (!rel_maps.env_map.empty()) + relabel_here(arena, &rel_maps.env_map); + if (!rel_maps.player_map.empty()) + relabel_here(arena, &rel_maps.player_map); + + bdd dummy_ap = bdd_ithvar(arena_r.register_ap(in_mark_s)) + & bdd_ithvar(arena_r.register_ap(out_mark_s)); + + for (auto& e : arena_r.edges()) + e.cond = bdd_exist(e.cond, dummy_ap); + + arena_r.unregister_ap(arena_r.register_ap(in_mark_s)); + arena_r.unregister_ap(arena_r.register_ap(out_mark_s)); + + return; + } + } // spot diff --git a/spot/twaalgos/synthesis.hh b/spot/twaalgos/synthesis.hh index b1b7fdf1d..2d9c0600a 100644 --- a/spot/twaalgos/synthesis.hh +++ b/spot/twaalgos/synthesis.hh @@ -21,6 +21,7 @@ #include #include +#include #include namespace spot @@ -256,4 +257,39 @@ namespace spot SPOT_API bool solve_game(twa_graph_ptr arena, synthesis_info& gi); + struct SPOT_API game_relabeling_map + { + relabeling_map env_map; + relabeling_map player_map; + }; + + /// \ingroup synthesis + /// \brief Tries to relabel a SPLIT game \a arena using fresh propositions. + /// Can be applied to env or player depending on \a relabel_env + /// and \a relabel_play. The arguments \a split_env and \a split_play + /// determine whether or not env and player edges are to + /// be split into several transitions labelled by letters not conditions. + /// + /// \return pair of relabeling_map, first is for env, second is for player. + /// The maps are empty if no relabeling was performed + /// \note Can also be applied to split mealy machine. + /// \note partitioned_relabel_here can not be used directly if there are + /// T (true conditions) + SPOT_API game_relabeling_map + partitioned_game_relabel_here(twa_graph_ptr& arena, + bool relabel_env, + bool relabel_play, + bool split_env = false, + bool split_play = false, + unsigned max_letter = -1u, + unsigned max_letter_mult = -1u); + + /// \ingroup synthesis + /// \brief Undoes a relabeling done by partitioned_game_relabel_here. + /// A dedicated function is necessary in order to remove the + /// variables tagging env and player conditions + SPOT_API void + relabel_game_here(twa_graph_ptr& arena, + game_relabeling_map& rel_maps); + } diff --git a/tests/python/_mealy.ipynb b/tests/python/_mealy.ipynb index 9d7fe7d96..ebeeaacb7 100644 --- a/tests/python/_mealy.ipynb +++ b/tests/python/_mealy.ipynb @@ -129,7 +129,7 @@ "\n" ], "text/plain": [ - " *' at 0x7fcc35aaa030> >" + " *' at 0x7f86481a2690> >" ] }, "execution_count": 4, @@ -209,7 +209,7 @@ "\n" ], "text/plain": [ - " *' at 0x7fcc35aaa900> >" + " *' at 0x7f85f45cbb70> >" ] }, "execution_count": 6, @@ -283,7 +283,7 @@ "\n" ], "text/plain": [ - " *' at 0x7fcc35aaa900> >" + " *' at 0x7f85f45cbb70> >" ] }, "execution_count": 8, @@ -387,7 +387,7 @@ "\n" ], "text/plain": [ - " *' at 0x7fcc35ac20c0> >" + " *' at 0x7f861bfc8ae0> >" ] }, "execution_count": 9, @@ -532,7 +532,7 @@ "\n" ], "text/plain": [ - " *' at 0x7fcc35ac2720> >" + " *' at 0x7f85f45efde0> >" ] }, "execution_count": 10, @@ -584,13 +584,13 @@ " split_cstr_time\n", " prob_init_build_time\n", " ...\n", - " refine_time\n", " total_time\n", " n_classes\n", " n_refinement\n", " n_lit\n", " n_clauses\n", " n_iteration\n", + " n_letters_part\n", " n_bisim_let\n", " n_min_states\n", " done\n", @@ -600,15 +600,15 @@ " \n", " 0\n", " presat\n", - " 25643.3\n", - " 1.112e-06\n", - " 4.588e-06\n", - " 9.888e-06\n", - " 4.549e-06\n", - " 1.5929e-05\n", - " 9.338e-06\n", - " 5.901e-06\n", - " 6.7276e-05\n", + " 3868.95\n", + " 3.282e-06\n", + " 1.4388e-05\n", + " 0.000129765\n", + " 1.3759e-05\n", + " 9.499e-06\n", + " 8.73e-06\n", + " 9.01e-06\n", + " 6.6209e-05\n", " ...\n", " NaN\n", " NaN\n", @@ -616,7 +616,7 @@ " NaN\n", " NaN\n", " NaN\n", - " NaN\n", + " 3\n", " 2\n", " NaN\n", " NaN\n", @@ -634,40 +634,40 @@ " NaN\n", " NaN\n", " ...\n", - " NaN\n", - " 0.000282709\n", + " 0.000743251\n", " 2\n", " 0\n", " 7\n", " 12\n", " 0\n", " NaN\n", + " NaN\n", " 4\n", " 1\n", " \n", " \n", "\n", - "

2 rows × 22 columns

\n", + "

2 rows × 23 columns

\n", "
" ], "text/plain": [ " task premin_time reorg_time partsol_time player_incomp_time incomp_time \\\n", - "0 presat 25643.3 1.112e-06 4.588e-06 9.888e-06 4.549e-06 \n", + "0 presat 3868.95 3.282e-06 1.4388e-05 0.000129765 1.3759e-05 \n", "1 sat NaN NaN NaN NaN NaN \n", "\n", " split_all_let_time split_min_let_time split_cstr_time prob_init_build_time \\\n", - "0 1.5929e-05 9.338e-06 5.901e-06 6.7276e-05 \n", + "0 9.499e-06 8.73e-06 9.01e-06 6.6209e-05 \n", "1 NaN NaN NaN NaN \n", "\n", - " ... refine_time total_time n_classes n_refinement n_lit n_clauses \\\n", - "0 ... NaN NaN NaN NaN NaN NaN \n", - "1 ... NaN 0.000282709 2 0 7 12 \n", + " ... total_time n_classes n_refinement n_lit n_clauses n_iteration \\\n", + "0 ... NaN NaN NaN NaN NaN NaN \n", + "1 ... 0.000743251 2 0 7 12 0 \n", "\n", - " n_iteration n_bisim_let n_min_states done \n", - "0 NaN 2 NaN NaN \n", - "1 0 NaN 4 1 \n", + " n_letters_part n_bisim_let n_min_states done \n", + "0 3 2 NaN NaN \n", + "1 NaN NaN 4 1 \n", "\n", - "[2 rows x 22 columns]" + "[2 rows x 23 columns]" ] }, "metadata": {}, @@ -758,7 +758,7 @@ "\n" ], "text/plain": [ - " *' at 0x7fcc88735f00> >" + " *' at 0x7f861bfc8630> >" ] }, "execution_count": 11, @@ -861,7 +861,7 @@ "\n" ], "text/plain": [ - " *' at 0x7fcc6157fe40> >" + " *' at 0x7f861bf9fb40> >" ] }, "execution_count": 12, @@ -1000,7 +1000,7 @@ "\n" ], "text/plain": [ - " *' at 0x7fcc6157f210> >" + " *' at 0x7f861bf9f210> >" ] }, "execution_count": 13, @@ -1051,13 +1051,13 @@ " split_cstr_time\n", " prob_init_build_time\n", " ...\n", - " refine_time\n", " total_time\n", " n_classes\n", " n_refinement\n", " n_lit\n", " n_clauses\n", " n_iteration\n", + " n_letters_part\n", " n_bisim_let\n", " n_min_states\n", " done\n", @@ -1067,15 +1067,15 @@ " \n", " 0\n", " presat\n", - " 25643.4\n", - " 1.683e-06\n", - " 5.611e-06\n", - " 2.66e-05\n", - " 1.2e-07\n", - " 3.647e-06\n", - " 8.365e-06\n", - " 3.747e-06\n", - " 2.5538e-05\n", + " 3869.08\n", + " 3.213e-06\n", + " 9.079e-06\n", + " 9.5752e-05\n", + " 5.168e-06\n", + " 5.727e-06\n", + " 7.543e-06\n", + " 1.5784e-05\n", + " 4.0507e-05\n", " ...\n", " NaN\n", " NaN\n", @@ -1083,7 +1083,7 @@ " NaN\n", " NaN\n", " NaN\n", - " NaN\n", + " 1\n", " 1\n", " NaN\n", " NaN\n", @@ -1102,7 +1102,6 @@ " NaN\n", " ...\n", " NaN\n", - " NaN\n", " 1\n", " 0\n", " 3\n", @@ -1111,6 +1110,7 @@ " NaN\n", " NaN\n", " NaN\n", + " NaN\n", " \n", " \n", " 2\n", @@ -1125,7 +1125,6 @@ " NaN\n", " NaN\n", " ...\n", - " 4.4884e-05\n", " NaN\n", " 1\n", " 1\n", @@ -1135,6 +1134,7 @@ " NaN\n", " NaN\n", " NaN\n", + " NaN\n", " \n", " \n", " 3\n", @@ -1149,48 +1149,48 @@ " NaN\n", " NaN\n", " ...\n", - " NaN\n", - " 0.000200344\n", + " 0.000399073\n", " 2\n", " 0\n", " 17\n", " 29\n", " 1\n", " NaN\n", + " NaN\n", " 4\n", " 1\n", " \n", " \n", "\n", - "

4 rows × 22 columns

\n", + "

4 rows × 23 columns

\n", "" ], "text/plain": [ " task premin_time reorg_time partsol_time player_incomp_time \\\n", - "0 presat 25643.4 1.683e-06 5.611e-06 2.66e-05 \n", + "0 presat 3869.08 3.213e-06 9.079e-06 9.5752e-05 \n", "1 sat NaN NaN NaN NaN \n", "2 refinement NaN NaN NaN NaN \n", "3 sat NaN NaN NaN NaN \n", "\n", " incomp_time split_all_let_time split_min_let_time split_cstr_time \\\n", - "0 1.2e-07 3.647e-06 8.365e-06 3.747e-06 \n", + "0 5.168e-06 5.727e-06 7.543e-06 1.5784e-05 \n", "1 NaN NaN NaN NaN \n", "2 NaN NaN NaN NaN \n", "3 NaN NaN NaN NaN \n", "\n", - " prob_init_build_time ... refine_time total_time n_classes n_refinement \\\n", - "0 2.5538e-05 ... NaN NaN NaN NaN \n", - "1 NaN ... NaN NaN 1 0 \n", - "2 NaN ... 4.4884e-05 NaN 1 1 \n", - "3 NaN ... NaN 0.000200344 2 0 \n", + " prob_init_build_time ... total_time n_classes n_refinement n_lit \\\n", + "0 4.0507e-05 ... NaN NaN NaN NaN \n", + "1 NaN ... NaN 1 0 3 \n", + "2 NaN ... NaN 1 1 10 \n", + "3 NaN ... 0.000399073 2 0 17 \n", "\n", - " n_lit n_clauses n_iteration n_bisim_let n_min_states done \n", - "0 NaN NaN NaN 1 NaN NaN \n", - "1 3 6 0 NaN NaN NaN \n", - "2 10 16 NaN NaN NaN NaN \n", - "3 17 29 1 NaN 4 1 \n", + " n_clauses n_iteration n_letters_part n_bisim_let n_min_states done \n", + "0 NaN NaN 1 1 NaN NaN \n", + "1 6 0 NaN NaN NaN NaN \n", + "2 16 NaN NaN NaN NaN NaN \n", + "3 29 1 NaN NaN 4 1 \n", "\n", - "[4 rows x 22 columns]" + "[4 rows x 23 columns]" ] }, "metadata": {}, @@ -1200,6 +1200,7 @@ "name": "stdout", "output_type": "stream", "text": [ + "Number of variables\n", "0 NaN\n", "1 3\n", "2 10\n", @@ -1285,7 +1286,7 @@ "\n" ], "text/plain": [ - " *' at 0x7fcc35ac22a0> >" + " *' at 0x7f861bfcdc00> >" ] }, "execution_count": 14, @@ -1297,6 +1298,7 @@ "si = spot.synthesis_info()\n", "si.minimize_lvl = 3\n", "aut_ms, table = spot.minimize_mealy(aut_s, si, display_log=True, return_log=True)\n", + "print(\"Number of variables\")\n", "print(table[\"n_lit\"])\n", "aut_ms" ] @@ -1347,13 +1349,13 @@ " split_cstr_time\n", " prob_init_build_time\n", " ...\n", - " refine_time\n", " total_time\n", " n_classes\n", " n_refinement\n", " n_lit\n", " n_clauses\n", " n_iteration\n", + " n_letters_part\n", " n_bisim_let\n", " n_min_states\n", " done\n", @@ -1363,15 +1365,15 @@ " \n", " 0\n", " presat\n", - " 25643.5\n", - " 1.563e-06\n", - " 5.4e-06\n", - " 2.0519e-05\n", - " 1.3e-07\n", - " 3.968e-06\n", - " 9.698e-06\n", - " 7.624e-06\n", - " 3.211e-05\n", + " 3869.14\n", + " 2.863e-06\n", + " 9.08e-06\n", + " 6.0622e-05\n", + " 4.679e-06\n", + " 5.308e-06\n", + " 8.59e-06\n", + " 7.962e-06\n", + " 4.0159e-05\n", " ...\n", " NaN\n", " NaN\n", @@ -1379,7 +1381,7 @@ " NaN\n", " NaN\n", " NaN\n", - " NaN\n", + " 1\n", " 1\n", " NaN\n", " NaN\n", @@ -1398,7 +1400,6 @@ " NaN\n", " ...\n", " NaN\n", - " NaN\n", " 1\n", " 0\n", " 3\n", @@ -1407,6 +1408,7 @@ " NaN\n", " NaN\n", " NaN\n", + " NaN\n", " \n", " \n", " 2\n", @@ -1421,7 +1423,6 @@ " NaN\n", " NaN\n", " ...\n", - " 4.4633e-05\n", " NaN\n", " 1\n", " 1\n", @@ -1431,6 +1432,7 @@ " NaN\n", " NaN\n", " NaN\n", + " NaN\n", " \n", " \n", " 3\n", @@ -1445,48 +1447,48 @@ " NaN\n", " NaN\n", " ...\n", - " NaN\n", - " 0.000280675\n", + " 0.000416464\n", " 2\n", " 0\n", " 17\n", " 29\n", " 1\n", " NaN\n", + " NaN\n", " 4\n", " 1\n", " \n", " \n", "\n", - "

4 rows × 22 columns

\n", + "

4 rows × 23 columns

\n", "" ], "text/plain": [ " task premin_time reorg_time partsol_time player_incomp_time \\\n", - "0 presat 25643.5 1.563e-06 5.4e-06 2.0519e-05 \n", + "0 presat 3869.14 2.863e-06 9.08e-06 6.0622e-05 \n", "1 sat NaN NaN NaN NaN \n", "2 refinement NaN NaN NaN NaN \n", "3 sat NaN NaN NaN NaN \n", "\n", " incomp_time split_all_let_time split_min_let_time split_cstr_time \\\n", - "0 1.3e-07 3.968e-06 9.698e-06 7.624e-06 \n", + "0 4.679e-06 5.308e-06 8.59e-06 7.962e-06 \n", "1 NaN NaN NaN NaN \n", "2 NaN NaN NaN NaN \n", "3 NaN NaN NaN NaN \n", "\n", - " prob_init_build_time ... refine_time total_time n_classes n_refinement \\\n", - "0 3.211e-05 ... NaN NaN NaN NaN \n", - "1 NaN ... NaN NaN 1 0 \n", - "2 NaN ... 4.4633e-05 NaN 1 1 \n", - "3 NaN ... NaN 0.000280675 2 0 \n", + " prob_init_build_time ... total_time n_classes n_refinement n_lit \\\n", + "0 4.0159e-05 ... NaN NaN NaN NaN \n", + "1 NaN ... NaN 1 0 3 \n", + "2 NaN ... NaN 1 1 10 \n", + "3 NaN ... 0.000416464 2 0 17 \n", "\n", - " n_lit n_clauses n_iteration n_bisim_let n_min_states done \n", - "0 NaN NaN NaN 1 NaN NaN \n", - "1 3 6 0 NaN NaN NaN \n", - "2 10 16 NaN NaN NaN NaN \n", - "3 17 29 1 NaN 4 1 \n", + " n_clauses n_iteration n_letters_part n_bisim_let n_min_states done \n", + "0 NaN NaN 1 1 NaN NaN \n", + "1 6 0 NaN NaN NaN NaN \n", + "2 16 NaN NaN NaN NaN NaN \n", + "3 29 1 NaN NaN 4 1 \n", "\n", - "[4 rows x 22 columns]" + "[4 rows x 23 columns]" ] }, "metadata": {}, @@ -1570,10 +1572,118 @@ " " ] }, + { + "cell_type": "markdown", + "id": "b10213b8", + "metadata": {}, + "source": [ + "# Testing partitioned relabeling" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "id": "fd5ca506", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Conditions in orig machine: 8\n", + "Conditions in relabeled machine: 13\n" + ] + } + ], + "source": [ + "def get_mealy():\n", + " return spot.split_2step(spot.automaton(\"\"\"HOA: v1\n", + "States: 2\n", + "Start: 0\n", + "AP: 11 \"u0accel0accel\" \"u0accel0f1dcon23p81b\" \"u0accel0f1dcon231b\" \"u0gear0f1dmin0f1dcon61b0f1dadd0gear0f1dcon241b1b1b\" \"u0gear0gear\" \"u0gear0f1dmax0f1dcon241b0f1dsub0gear0f1dcon241b1b1b\" \"u0steer0f1dsteering0angle0trackpos1b\" \"u0steer0steer\" \"p0p0gt0rpm0f1dcon5523231b\" \"p0p0lt0rpm0f1dcon32323231b\" \"p0p0lt0speed0f1dsub0target2speed0f1dmultp0f1dabs0steer1b0f1dcon248881b1b1b\"\n", + "acc-name: all\n", + "Acceptance: 0 t\n", + "properties: trans-labels explicit-labels state-acc deterministic\n", + "controllable-AP: 0 1 2 3 4 5 6 7\n", + "--BODY--\n", + "State: 0\n", + "[!0&!1&2&!3&4&!5&6&!7&!8&!9&!10] 0\n", + "[!0&1&!2&!3&4&!5&6&!7&!8&!9&10] 0\n", + "[!0&!1&2&!3&!4&5&6&!7&!8&9&!10] 0\n", + "[!0&1&!2&!3&!4&5&6&!7&!8&9&10] 0\n", + "[!0&!1&2&3&!4&!5&6&!7&8&!9&!10] 0\n", + "[!0&1&!2&3&!4&!5&6&!7&8&!9&10] 0\n", + "[!0&!1&2&!3&!4&5&!6&7&8&9 | !0&!1&2&!3&!4&5&6&!7&8&9 | !0&!1&2&!3&4&!5&!6&7&8&9 | !0&!1&2&!3&4&!5&6&!7&8&9 | !0&!1&2&3&!4&!5&!6&7&8&9 | !0&!1&2&3&!4&!5&6&!7&8&9 | !0&1&!2&!3&!4&5&!6&7&8&9 | !0&1&!2&!3&!4&5&6&!7&8&9 | !0&1&!2&!3&4&!5&!6&7&8&9 | !0&1&!2&!3&4&!5&6&!7&8&9 | !0&1&!2&3&!4&!5&!6&7&8&9 | !0&1&!2&3&!4&!5&6&!7&8&9 | 0&!1&!2&!3&!4&5&!6&7&8&9 | 0&!1&!2&!3&!4&5&6&!7&8&9 | 0&!1&!2&!3&4&!5&!6&7&8&9 | 0&!1&!2&!3&4&!5&6&!7&8&9 | 0&!1&!2&3&!4&!5&!6&7&8&9 | 0&!1&!2&3&!4&!5&6&!7&8&9] 1\n", + "State: 1\n", + "[!0&!1&2&!3&!4&5&!6&7 | !0&!1&2&!3&!4&5&6&!7 | !0&!1&2&!3&4&!5&!6&7 | !0&!1&2&!3&4&!5&6&!7 | !0&!1&2&3&!4&!5&!6&7 | !0&!1&2&3&!4&!5&6&!7 | !0&1&!2&!3&!4&5&!6&7 | !0&1&!2&!3&!4&5&6&!7 | !0&1&!2&!3&4&!5&!6&7 | !0&1&!2&!3&4&!5&6&!7 | !0&1&!2&3&!4&!5&!6&7 | !0&1&!2&3&!4&!5&6&!7 | 0&!1&!2&!3&!4&5&!6&7 | 0&!1&!2&!3&!4&5&6&!7 | 0&!1&!2&!3&4&!5&!6&7 | 0&!1&!2&!3&4&!5&6&!7 | 0&!1&!2&3&!4&!5&!6&7 | 0&!1&!2&3&!4&!5&6&!7] 1\n", + "--END--\"\"\"))\n", + "\n", + "def env_conditions(m):\n", + " sp = spot.get_state_players(m)\n", + " conds = []\n", + " for e in m.edges():\n", + " if sp[e.src]:\n", + " continue\n", + " if not e.cond in conds:\n", + " conds.append(e.cond)\n", + " return conds\n", + "print(\"Conditions in orig machine: \", len(env_conditions(get_mealy())))\n", + "ms = get_mealy()\n", + "# Relabel only env\n", + "spot.partitioned_game_relabel_here(ms, True, False, True, False)\n", + "print(\"Conditions in relabeled machine: \", len(env_conditions(ms)))" + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "ee29da67", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Partitioned env letters: 13\n" + ] + } + ], + "source": [ + "si = spot.synthesis_info()\n", + "si.minimize_lvl = 3\n", + "# Turn on relabeling\n", + "si.opt.set(\"max_letter_mult\", 100000)\n", + "\n", + "mm, log = spot.minimize_mealy(get_mealy(), si, return_log=True)\n", + "print(\"Partitioned env letters:\", log[\"n_letters_part\"][0])" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "0aec8019", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "Partitioned env letters: 0\n" + ] + } + ], + "source": [ + "# Turn off relabeling\n", + "si.opt.set(\"max_letter_mult\", 0)\n", + "\n", + "mm, log = spot.minimize_mealy(get_mealy(), si, return_log=True)\n", + "print(\"Partitioned env letters:\", log[\"n_letters_part\"][0])" + ] + }, { "cell_type": "code", "execution_count": null, - "id": "5c9fe115", + "id": "a92f4f43", "metadata": {}, "outputs": [], "source": [] diff --git a/tests/python/_partitioned_relabel.ipynb b/tests/python/_partitioned_relabel.ipynb index a9c1c7af7..b7f1c4380 100644 --- a/tests/python/_partitioned_relabel.ipynb +++ b/tests/python/_partitioned_relabel.ipynb @@ -121,7 +121,7 @@ "\n" ], "text/plain": [ - " *' at 0x7f65b0311a80> >" + " *' at 0x7f936415fbd0> >" ] }, "execution_count": 2, @@ -248,7 +248,7 @@ "\n" ], "text/plain": [ - " *' at 0x7f65b0311a80> >" + " *' at 0x7f936415fbd0> >" ] }, "execution_count": 3, @@ -353,7 +353,7 @@ "\n" ], "text/plain": [ - " *' at 0x7f65b0311a80> >" + " *' at 0x7f936415fbd0> >" ] }, "execution_count": 4, @@ -457,7 +457,7 @@ "\n" ], "text/plain": [ - " *' at 0x7f65b02c0d50> >" + " *' at 0x7f936415bf30> >" ] }, "metadata": {}, @@ -611,7 +611,7 @@ "\n" ], "text/plain": [ - " *' at 0x7f65b02c0d50> >" + " *' at 0x7f936415bf30> >" ] }, "execution_count": 5, @@ -769,7 +769,7 @@ "\n" ], "text/plain": [ - " *' at 0x7f65b02c0d50> >" + " *' at 0x7f936415bf30> >" ] }, "execution_count": 6, @@ -886,7 +886,7 @@ "\n" ], "text/plain": [ - " *' at 0x7f65b02c90f0> >" + " *' at 0x7f936c3c6090> >" ] }, "metadata": {}, @@ -1015,7 +1015,7 @@ "\n" ], "text/plain": [ - " *' at 0x7f65b02c90f0> >" + " *' at 0x7f936c3c6090> >" ] }, "execution_count": 7, @@ -1184,7 +1184,7 @@ "\n" ], "text/plain": [ - " *' at 0x7f65b02c90f0> >" + " *' at 0x7f936c3c6090> >" ] }, "execution_count": 8, @@ -1198,6 +1198,1135 @@ "print(aut.to_str(\"hoa\"))\n", "aut" ] + }, + { + "cell_type": "markdown", + "id": "ef77c2ee", + "metadata": {}, + "source": [ + "# Concerning games and Mealy machines\n", + "\n", + "Games and split mealy machines have both: defined outputs and states that either belong to player or env.\n", + "Relabeling is done separately for env and player transitions (over inputs and outputs respectively).\n", + "\n", + "The problem is that T (bddtrue) is ambiguous, as it may be over the inputs or outputs.\n", + "\n", + "We therefore introduce a dedicated function for this matter." + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "id": "296a93d3", + "metadata": {}, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "!p0p0gt0rpm0f1dcon5523231b & !p0p0lt0rpm0f1dcon32323231b & !p0p0lt0speed0f1dsub0target2speed0f1dmultp0f1dabs0steer1b0f1dcon248881b1b1b / !u0accel0accel & !u0accel0f1dcon23p81b & u0accel0f1dcon231b & !u0gear0f1dmax0f1dcon241b0f1dsub0gear0f1dcon241b1b1b & !u0gear0f1dmin0f1dcon61b0f1dadd0gear0f1dcon241b1b1b & u0gear0gear & u0steer0f1dsteering0angle0trackpos1b & !u0steer0steer\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "!p0p0gt0rpm0f1dcon5523231b & !p0p0lt0rpm0f1dcon32323231b & p0p0lt0speed0f1dsub0target2speed0f1dmultp0f1dabs0steer1b0f1dcon248881b1b1b / !u0accel0accel & u0accel0f1dcon23p81b & !u0accel0f1dcon231b & !u0gear0f1dmax0f1dcon241b0f1dsub0gear0f1dcon241b1b1b & !u0gear0f1dmin0f1dcon61b0f1dadd0gear0f1dcon241b1b1b & u0gear0gear & u0steer0f1dsteering0angle0trackpos1b & !u0steer0steer\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "!p0p0gt0rpm0f1dcon5523231b & p0p0lt0rpm0f1dcon32323231b & !p0p0lt0speed0f1dsub0target2speed0f1dmultp0f1dabs0steer1b0f1dcon248881b1b1b / !u0accel0accel & !u0accel0f1dcon23p81b & u0accel0f1dcon231b & u0gear0f1dmax0f1dcon241b0f1dsub0gear0f1dcon241b1b1b & !u0gear0f1dmin0f1dcon61b0f1dadd0gear0f1dcon241b1b1b & !u0gear0gear & u0steer0f1dsteering0angle0trackpos1b & !u0steer0steer\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "!p0p0gt0rpm0f1dcon5523231b & p0p0lt0rpm0f1dcon32323231b & p0p0lt0speed0f1dsub0target2speed0f1dmultp0f1dabs0steer1b0f1dcon248881b1b1b / !u0accel0accel & u0accel0f1dcon23p81b & !u0accel0f1dcon231b & u0gear0f1dmax0f1dcon241b0f1dsub0gear0f1dcon241b1b1b & !u0gear0f1dmin0f1dcon61b0f1dadd0gear0f1dcon241b1b1b & !u0gear0gear & u0steer0f1dsteering0angle0trackpos1b & !u0steer0steer\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "p0p0gt0rpm0f1dcon5523231b & !p0p0lt0rpm0f1dcon32323231b & !p0p0lt0speed0f1dsub0target2speed0f1dmultp0f1dabs0steer1b0f1dcon248881b1b1b / !u0accel0accel & !u0accel0f1dcon23p81b & u0accel0f1dcon231b & !u0gear0f1dmax0f1dcon241b0f1dsub0gear0f1dcon241b1b1b & u0gear0f1dmin0f1dcon61b0f1dadd0gear0f1dcon241b1b1b & !u0gear0gear & u0steer0f1dsteering0angle0trackpos1b & !u0steer0steer\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "p0p0gt0rpm0f1dcon5523231b & !p0p0lt0rpm0f1dcon32323231b & p0p0lt0speed0f1dsub0target2speed0f1dmultp0f1dabs0steer1b0f1dcon248881b1b1b / !u0accel0accel & u0accel0f1dcon23p81b & !u0accel0f1dcon231b & !u0gear0f1dmax0f1dcon241b0f1dsub0gear0f1dcon241b1b1b & u0gear0f1dmin0f1dcon61b0f1dadd0gear0f1dcon241b1b1b & !u0gear0gear & u0steer0f1dsteering0angle0trackpos1b & !u0steer0steer\n", + "\n", + "\n", + "\n", + "1\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "0->1\n", + "\n", + "\n", + "p0p0gt0rpm0f1dcon5523231b & p0p0lt0rpm0f1dcon32323231b / (label too long)\n", + "\n", + "\n", + "\n", + "1->1\n", + "\n", + "\n", + "1 / (label too long)\n", + "\n", + "\n", + "\n" + ], + "text/plain": [ + " *' at 0x7f936415f510> >" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "HOA: v1\n", + "States: 9\n", + "Start: 0\n", + "AP: 11 \"u0accel0accel\" \"u0accel0f1dcon23p81b\" \"u0accel0f1dcon231b\" \"u0gear0f1dmin0f1dcon61b0f1dadd0gear0f1dcon241b1b1b\" \"u0gear0gear\" \"u0gear0f1dmax0f1dcon241b0f1dsub0gear0f1dcon241b1b1b\" \"u0steer0f1dsteering0angle0trackpos1b\" \"u0steer0steer\" \"p0p0gt0rpm0f1dcon5523231b\" \"p0p0lt0rpm0f1dcon32323231b\" \"p0p0lt0speed0f1dsub0target2speed0f1dmultp0f1dabs0steer1b0f1dcon248881b1b1b\"\n", + "acc-name: all\n", + "Acceptance: 0 t\n", + "properties: trans-labels explicit-labels state-acc deterministic\n", + "spot-state-player: 0 0 1 1 1 1 1 1 1\n", + "controllable-AP: 0 1 2 3 4 5 6 7\n", + "--BODY--\n", + "State: 0\n", + "[!8&!9&!10] 2\n", + "[!8&!9&10] 3\n", + "[!8&9&!10] 4\n", + "[!8&9&10] 5\n", + "[8&!9&!10] 6\n", + "[8&!9&10] 7\n", + "[8&9] 8\n", + "State: 1\n", + "[t] 8\n", + "State: 2\n", + "[!0&!1&2&!3&4&!5&6&!7] 0\n", + "State: 3\n", + "[!0&1&!2&!3&4&!5&6&!7] 0\n", + "State: 4\n", + "[!0&!1&2&!3&!4&5&6&!7] 0\n", + "State: 5\n", + "[!0&1&!2&!3&!4&5&6&!7] 0\n", + "State: 6\n", + "[!0&!1&2&3&!4&!5&6&!7] 0\n", + "State: 7\n", + "[!0&1&!2&3&!4&!5&6&!7] 0\n", + "State: 8\n", + "[!0&!1&2&!3&!4&5&!6&7 | !0&!1&2&!3&!4&5&6&!7 | !0&!1&2&!3&4&!5&!6&7 | !0&!1&2&!3&4&!5&6&!7 | !0&!1&2&3&!4&!5&!6&7 | !0&!1&2&3&!4&!5&6&!7 | !0&1&!2&!3&!4&5&!6&7 | !0&1&!2&!3&!4&5&6&!7 | !0&1&!2&!3&4&!5&!6&7 | !0&1&!2&!3&4&!5&6&!7 | !0&1&!2&3&!4&!5&!6&7 | !0&1&!2&3&!4&!5&6&!7 | 0&!1&!2&!3&!4&5&!6&7 | 0&!1&!2&!3&!4&5&6&!7 | 0&!1&!2&!3&4&!5&!6&7 | 0&!1&!2&!3&4&!5&6&!7 | 0&!1&!2&3&!4&!5&!6&7 | 0&!1&!2&3&!4&!5&6&!7] 1\n", + "--END--\n" + ] + }, + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "t\n", + "[all]\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "2\n", + "\n", + "2\n", + "\n", + "\n", + "\n", + "0->2\n", + "\n", + "\n", + "!p0p0gt0rpm0f1dcon5523231b & !p0p0lt0rpm0f1dcon32323231b & !p0p0lt0speed0f1dsub0target2speed0f1dmultp0f1dabs0steer1b0f1dcon248881b1b1b\n", + "\n", + "\n", + "\n", + "3\n", + "\n", + "3\n", + "\n", + "\n", + "\n", + "0->3\n", + "\n", + "\n", + "!p0p0gt0rpm0f1dcon5523231b & !p0p0lt0rpm0f1dcon32323231b & p0p0lt0speed0f1dsub0target2speed0f1dmultp0f1dabs0steer1b0f1dcon248881b1b1b\n", + "\n", + "\n", + "\n", + "4\n", + "\n", + "4\n", + "\n", + "\n", + "\n", + "0->4\n", + "\n", + "\n", + "!p0p0gt0rpm0f1dcon5523231b & p0p0lt0rpm0f1dcon32323231b & !p0p0lt0speed0f1dsub0target2speed0f1dmultp0f1dabs0steer1b0f1dcon248881b1b1b\n", + "\n", + "\n", + "\n", + "5\n", + "\n", + "5\n", + "\n", + "\n", + "\n", + "0->5\n", + "\n", + "\n", + "!p0p0gt0rpm0f1dcon5523231b & p0p0lt0rpm0f1dcon32323231b & p0p0lt0speed0f1dsub0target2speed0f1dmultp0f1dabs0steer1b0f1dcon248881b1b1b\n", + "\n", + "\n", + "\n", + "6\n", + "\n", + "6\n", + "\n", + "\n", + "\n", + "0->6\n", + "\n", + "\n", + "p0p0gt0rpm0f1dcon5523231b & !p0p0lt0rpm0f1dcon32323231b & !p0p0lt0speed0f1dsub0target2speed0f1dmultp0f1dabs0steer1b0f1dcon248881b1b1b\n", + "\n", + "\n", + "\n", + "7\n", + "\n", + "7\n", + "\n", + "\n", + "\n", + "0->7\n", + "\n", + "\n", + "p0p0gt0rpm0f1dcon5523231b & !p0p0lt0rpm0f1dcon32323231b & p0p0lt0speed0f1dsub0target2speed0f1dmultp0f1dabs0steer1b0f1dcon248881b1b1b\n", + "\n", + "\n", + "\n", + "8\n", + "\n", + "8\n", + "\n", + "\n", + "\n", + "0->8\n", + "\n", + "\n", + "p0p0gt0rpm0f1dcon5523231b & p0p0lt0rpm0f1dcon32323231b\n", + "\n", + "\n", + "\n", + "2->0\n", + "\n", + "\n", + "!u0accel0accel & !u0accel0f1dcon23p81b & u0accel0f1dcon231b & !u0gear0f1dmax0f1dcon241b0f1dsub0gear0f1dcon241b1b1b & !u0gear0f1dmin0f1dcon61b0f1dadd0gear0f1dcon241b1b1b & u0gear0gear & u0steer0f1dsteering0angle0trackpos1b & !u0steer0steer\n", + "\n", + "\n", + "\n", + "3->0\n", + "\n", + "\n", + "!u0accel0accel & u0accel0f1dcon23p81b & !u0accel0f1dcon231b & !u0gear0f1dmax0f1dcon241b0f1dsub0gear0f1dcon241b1b1b & !u0gear0f1dmin0f1dcon61b0f1dadd0gear0f1dcon241b1b1b & u0gear0gear & u0steer0f1dsteering0angle0trackpos1b & !u0steer0steer\n", + "\n", + "\n", + "\n", + "4->0\n", + "\n", + "\n", + "!u0accel0accel & !u0accel0f1dcon23p81b & u0accel0f1dcon231b & u0gear0f1dmax0f1dcon241b0f1dsub0gear0f1dcon241b1b1b & !u0gear0f1dmin0f1dcon61b0f1dadd0gear0f1dcon241b1b1b & !u0gear0gear & u0steer0f1dsteering0angle0trackpos1b & !u0steer0steer\n", + "\n", + "\n", + "\n", + "5->0\n", + "\n", + "\n", + "!u0accel0accel & u0accel0f1dcon23p81b & !u0accel0f1dcon231b & u0gear0f1dmax0f1dcon241b0f1dsub0gear0f1dcon241b1b1b & !u0gear0f1dmin0f1dcon61b0f1dadd0gear0f1dcon241b1b1b & !u0gear0gear & u0steer0f1dsteering0angle0trackpos1b & !u0steer0steer\n", + "\n", + "\n", + "\n", + "6->0\n", + "\n", + "\n", + "!u0accel0accel & !u0accel0f1dcon23p81b & u0accel0f1dcon231b & !u0gear0f1dmax0f1dcon241b0f1dsub0gear0f1dcon241b1b1b & u0gear0f1dmin0f1dcon61b0f1dadd0gear0f1dcon241b1b1b & !u0gear0gear & u0steer0f1dsteering0angle0trackpos1b & !u0steer0steer\n", + "\n", + "\n", + "\n", + "7->0\n", + "\n", + "\n", + "!u0accel0accel & u0accel0f1dcon23p81b & !u0accel0f1dcon231b & !u0gear0f1dmax0f1dcon241b0f1dsub0gear0f1dcon241b1b1b & u0gear0f1dmin0f1dcon61b0f1dadd0gear0f1dcon241b1b1b & !u0gear0gear & u0steer0f1dsteering0angle0trackpos1b & !u0steer0steer\n", + "\n", + "\n", + "\n", + "1\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "8->1\n", + "\n", + "\n", + "(label too long)\n", + "\n", + "\n", + "\n", + "1->8\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n" + ], + "text/plain": [ + " *' at 0x7f936415f990> >" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "HOA: v1\n", + "States: 9\n", + "Start: 0\n", + "AP: 21 \"u0accel0accel\" \"u0accel0f1dcon23p81b\" \"u0accel0f1dcon231b\" \"u0gear0f1dmin0f1dcon61b0f1dadd0gear0f1dcon241b1b1b\" \"u0gear0gear\" \"u0gear0f1dmax0f1dcon241b0f1dsub0gear0f1dcon241b1b1b\" \"u0steer0f1dsteering0angle0trackpos1b\" \"u0steer0steer\" \"p0p0gt0rpm0f1dcon5523231b\" \"p0p0lt0rpm0f1dcon32323231b\" \"p0p0lt0speed0f1dsub0target2speed0f1dmultp0f1dabs0steer1b0f1dcon248881b1b1b\" \"__AP_OUT__\" \"__AP_IN__\" \"__nv_in0\" \"__nv_in1\" \"__nv_in2\" \"__nv_in3\" \"__nv_out0\" \"__nv_out1\" \"__nv_out2\" \"__nv_out3\"\n", + "acc-name: all\n", + "Acceptance: 0 t\n", + "properties: trans-labels explicit-labels state-acc deterministic\n", + "spot-state-player: 0 0 1 1 1 1 1 1 1\n", + "controllable-AP: 0 1 2 3 4 5 6 7\n", + "--BODY--\n", + "State: 0\n", + "[!13&!14&!15&!16] 2\n", + "[13&!14&!15&!16] 3\n", + "[!13&14&!15&!16] 4\n", + "[13&14&!15&!16] 5\n", + "[!13&!14&15&!16] 6\n", + "[13&!14&15&!16] 7\n", + "[!13&14&15&!16] 8\n", + "[13&14&15&!16] 2\n", + "[!13&!14&!15&16] 3\n", + "[13&!14&!15&16] 4\n", + "[!13&14&!15&16] 5\n", + "[13&14&!15&16] 6\n", + "[!13&!14&15&16] 7\n", + "State: 1\n", + "[13&14&15&!16] 8\n", + "[!13&!14&!15&16] 8\n", + "[13&!14&!15&16] 8\n", + "[!13&14&!15&16] 8\n", + "[13&14&!15&16] 8\n", + "[!13&!14&15&16] 8\n", + "[!13&14&15&!16] 8\n", + "State: 2\n", + "[!17&!18&!19&!20 | !17&18&19&!20] 0\n", + "State: 3\n", + "[17&!18&!19&!20 | 17&18&19&!20] 0\n", + "State: 4\n", + "[!17&!18&!19&20 | !17&18&!19&!20] 0\n", + "State: 5\n", + "[17&!18&!19&20 | 17&18&!19&!20] 0\n", + "State: 6\n", + "[!17&!18&19&!20 | !17&18&!19&20] 0\n", + "State: 7\n", + "[17&!18&19&!20 | 17&18&!19&20] 0\n", + "State: 8\n", + "[!17&!18&20 | 18&19&!20 | !19&20] 1\n", + "--END--\n" + ] + }, + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "t\n", + "[all]\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "2\n", + "\n", + "2\n", + "\n", + "\n", + "\n", + "0->2\n", + "\n", + "\n", + "!__nv_in0 & !__nv_in1 & !__nv_in2 & !__nv_in3\n", + "\n", + "\n", + "\n", + "0->2\n", + "\n", + "\n", + "__nv_in0 & __nv_in1 & __nv_in2 & !__nv_in3\n", + "\n", + "\n", + "\n", + "3\n", + "\n", + "3\n", + "\n", + "\n", + "\n", + "0->3\n", + "\n", + "\n", + "__nv_in0 & !__nv_in1 & !__nv_in2 & !__nv_in3\n", + "\n", + "\n", + "\n", + "0->3\n", + "\n", + "\n", + "!__nv_in0 & !__nv_in1 & !__nv_in2 & __nv_in3\n", + "\n", + "\n", + "\n", + "4\n", + "\n", + "4\n", + "\n", + "\n", + "\n", + "0->4\n", + "\n", + "\n", + "!__nv_in0 & __nv_in1 & !__nv_in2 & !__nv_in3\n", + "\n", + "\n", + "\n", + "0->4\n", + "\n", + "\n", + "__nv_in0 & !__nv_in1 & !__nv_in2 & __nv_in3\n", + "\n", + "\n", + "\n", + "5\n", + "\n", + "5\n", + "\n", + "\n", + "\n", + "0->5\n", + "\n", + "\n", + "__nv_in0 & __nv_in1 & !__nv_in2 & !__nv_in3\n", + "\n", + "\n", + "\n", + "0->5\n", + "\n", + "\n", + "!__nv_in0 & __nv_in1 & !__nv_in2 & __nv_in3\n", + "\n", + "\n", + "\n", + "6\n", + "\n", + "6\n", + "\n", + "\n", + "\n", + "0->6\n", + "\n", + "\n", + "!__nv_in0 & !__nv_in1 & __nv_in2 & !__nv_in3\n", + "\n", + "\n", + "\n", + "0->6\n", + "\n", + "\n", + "__nv_in0 & __nv_in1 & !__nv_in2 & __nv_in3\n", + "\n", + "\n", + "\n", + "7\n", + "\n", + "7\n", + "\n", + "\n", + "\n", + "0->7\n", + "\n", + "\n", + "__nv_in0 & !__nv_in1 & __nv_in2 & !__nv_in3\n", + "\n", + "\n", + "\n", + "0->7\n", + "\n", + "\n", + "!__nv_in0 & !__nv_in1 & __nv_in2 & __nv_in3\n", + "\n", + "\n", + "\n", + "8\n", + "\n", + "8\n", + "\n", + "\n", + "\n", + "0->8\n", + "\n", + "\n", + "!__nv_in0 & __nv_in1 & __nv_in2 & !__nv_in3\n", + "\n", + "\n", + "\n", + "2->0\n", + "\n", + "\n", + "(!__nv_out0 & !__nv_out1 & !__nv_out2 & !__nv_out3) | (!__nv_out0 & __nv_out1 & __nv_out2 & !__nv_out3)\n", + "\n", + "\n", + "\n", + "3->0\n", + "\n", + "\n", + "(__nv_out0 & !__nv_out1 & !__nv_out2 & !__nv_out3) | (__nv_out0 & __nv_out1 & __nv_out2 & !__nv_out3)\n", + "\n", + "\n", + "\n", + "4->0\n", + "\n", + "\n", + "(!__nv_out0 & __nv_out1 & !__nv_out2 & !__nv_out3) | (!__nv_out0 & !__nv_out1 & !__nv_out2 & __nv_out3)\n", + "\n", + "\n", + "\n", + "5->0\n", + "\n", + "\n", + "(__nv_out0 & __nv_out1 & !__nv_out2 & !__nv_out3) | (__nv_out0 & !__nv_out1 & !__nv_out2 & __nv_out3)\n", + "\n", + "\n", + "\n", + "6->0\n", + "\n", + "\n", + "(!__nv_out0 & !__nv_out1 & __nv_out2 & !__nv_out3) | (!__nv_out0 & __nv_out1 & !__nv_out2 & __nv_out3)\n", + "\n", + "\n", + "\n", + "7->0\n", + "\n", + "\n", + "(__nv_out0 & !__nv_out1 & __nv_out2 & !__nv_out3) | (__nv_out0 & __nv_out1 & !__nv_out2 & __nv_out3)\n", + "\n", + "\n", + "\n", + "1\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "8->1\n", + "\n", + "\n", + "(!__nv_out0 & !__nv_out1 & __nv_out3) | (__nv_out1 & __nv_out2 & !__nv_out3) | (!__nv_out2 & __nv_out3)\n", + "\n", + "\n", + "\n", + "1->8\n", + "\n", + "\n", + "__nv_in0 & __nv_in1 & __nv_in2 & !__nv_in3\n", + "\n", + "\n", + "\n", + "1->8\n", + "\n", + "\n", + "!__nv_in0 & !__nv_in1 & !__nv_in2 & __nv_in3\n", + "\n", + "\n", + "\n", + "1->8\n", + "\n", + "\n", + "__nv_in0 & !__nv_in1 & !__nv_in2 & __nv_in3\n", + "\n", + "\n", + "\n", + "1->8\n", + "\n", + "\n", + "!__nv_in0 & __nv_in1 & !__nv_in2 & __nv_in3\n", + "\n", + "\n", + "\n", + "1->8\n", + "\n", + "\n", + "__nv_in0 & __nv_in1 & !__nv_in2 & __nv_in3\n", + "\n", + "\n", + "\n", + "1->8\n", + "\n", + "\n", + "!__nv_in0 & !__nv_in1 & __nv_in2 & __nv_in3\n", + "\n", + "\n", + "\n", + "1->8\n", + "\n", + "\n", + "!__nv_in0 & __nv_in1 & __nv_in2 & !__nv_in3\n", + "\n", + "\n", + "\n" + ], + "text/plain": [ + " *' at 0x7f936415f990> >" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# Strategy torquesimple_acd as mealy machine\n", + "\n", + "aut = spot.automaton(\"\"\"HOA: v1\n", + "States: 2\n", + "Start: 0\n", + "AP: 11 \"u0accel0accel\" \"u0accel0f1dcon23p81b\" \"u0accel0f1dcon231b\" \"u0gear0f1dmin0f1dcon61b0f1dadd0gear0f1dcon241b1b1b\" \"u0gear0gear\" \"u0gear0f1dmax0f1dcon241b0f1dsub0gear0f1dcon241b1b1b\" \"u0steer0f1dsteering0angle0trackpos1b\" \"u0steer0steer\" \"p0p0gt0rpm0f1dcon5523231b\" \"p0p0lt0rpm0f1dcon32323231b\" \"p0p0lt0speed0f1dsub0target2speed0f1dmultp0f1dabs0steer1b0f1dcon248881b1b1b\"\n", + "acc-name: all\n", + "Acceptance: 0 t\n", + "properties: trans-labels explicit-labels state-acc deterministic\n", + "controllable-AP: 0 1 2 3 4 5 6 7\n", + "--BODY--\n", + "State: 0\n", + "[!0&!1&2&!3&4&!5&6&!7&!8&!9&!10] 0\n", + "[!0&1&!2&!3&4&!5&6&!7&!8&!9&10] 0\n", + "[!0&!1&2&!3&!4&5&6&!7&!8&9&!10] 0\n", + "[!0&1&!2&!3&!4&5&6&!7&!8&9&10] 0\n", + "[!0&!1&2&3&!4&!5&6&!7&8&!9&!10] 0\n", + "[!0&1&!2&3&!4&!5&6&!7&8&!9&10] 0\n", + "[!0&!1&2&!3&!4&5&!6&7&8&9 | !0&!1&2&!3&!4&5&6&!7&8&9 | !0&!1&2&!3&4&!5&!6&7&8&9 | !0&!1&2&!3&4&!5&6&!7&8&9 | !0&!1&2&3&!4&!5&!6&7&8&9 | !0&!1&2&3&!4&!5&6&!7&8&9 | !0&1&!2&!3&!4&5&!6&7&8&9 | !0&1&!2&!3&!4&5&6&!7&8&9 | !0&1&!2&!3&4&!5&!6&7&8&9 | !0&1&!2&!3&4&!5&6&!7&8&9 | !0&1&!2&3&!4&!5&!6&7&8&9 | !0&1&!2&3&!4&!5&6&!7&8&9 | 0&!1&!2&!3&!4&5&!6&7&8&9 | 0&!1&!2&!3&!4&5&6&!7&8&9 | 0&!1&!2&!3&4&!5&!6&7&8&9 | 0&!1&!2&!3&4&!5&6&!7&8&9 | 0&!1&!2&3&!4&!5&!6&7&8&9 | 0&!1&!2&3&!4&!5&6&!7&8&9] 1\n", + "State: 1\n", + "[!0&!1&2&!3&!4&5&!6&7 | !0&!1&2&!3&!4&5&6&!7 | !0&!1&2&!3&4&!5&!6&7 | !0&!1&2&!3&4&!5&6&!7 | !0&!1&2&3&!4&!5&!6&7 | !0&!1&2&3&!4&!5&6&!7 | !0&1&!2&!3&!4&5&!6&7 | !0&1&!2&!3&!4&5&6&!7 | !0&1&!2&!3&4&!5&!6&7 | !0&1&!2&!3&4&!5&6&!7 | !0&1&!2&3&!4&!5&!6&7 | !0&1&!2&3&!4&!5&6&!7 | 0&!1&!2&!3&!4&5&!6&7 | 0&!1&!2&!3&!4&5&6&!7 | 0&!1&!2&!3&4&!5&!6&7 | 0&!1&!2&!3&4&!5&6&!7 | 0&!1&!2&3&!4&!5&!6&7 | 0&!1&!2&3&!4&!5&6&!7] 1\n", + "--END--\"\"\")\n", + "\n", + "display(aut)\n", + "\n", + "# Convert to split mealy machine\n", + "auts = spot.split_2step(aut)\n", + "print(auts.to_str(\"hoa\"))\n", + "display(auts)\n", + "\n", + "# Relabel both, inputs and outputs\n", + "# You can choose the split option and stopping criteria as before\n", + "rel_dicts = spot.partitioned_game_relabel_here(auts, True, True, True, False, 10000, 10000)\n", + "print(auts.to_str(\"hoa\"))\n", + "display(auts)" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "id": "7ec02ff5", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "HOA: v1\n", + "States: 9\n", + "Start: 0\n", + "AP: 11 \"u0accel0accel\" \"u0accel0f1dcon23p81b\" \"u0accel0f1dcon231b\" \"u0gear0f1dmin0f1dcon61b0f1dadd0gear0f1dcon241b1b1b\" \"u0gear0gear\" \"u0gear0f1dmax0f1dcon241b0f1dsub0gear0f1dcon241b1b1b\" \"u0steer0f1dsteering0angle0trackpos1b\" \"u0steer0steer\" \"p0p0gt0rpm0f1dcon5523231b\" \"p0p0lt0rpm0f1dcon32323231b\" \"p0p0lt0speed0f1dsub0target2speed0f1dmultp0f1dabs0steer1b0f1dcon248881b1b1b\"\n", + "acc-name: all\n", + "Acceptance: 0 t\n", + "properties: trans-labels explicit-labels state-acc deterministic\n", + "spot-state-player: 0 0 1 1 1 1 1 1 1\n", + "controllable-AP: 0 1 2 3 4 5 6 7\n", + "--BODY--\n", + "State: 0\n", + "[f] 2\n", + "[f] 3\n", + "[f] 4\n", + "[f] 5\n", + "[f] 6\n", + "[f] 7\n", + "[8&9] 8\n", + "[!8&!9&!10] 2\n", + "[!8&!9&10] 3\n", + "[!8&9&!10] 4\n", + "[!8&9&10] 5\n", + "[8&!9&!10] 6\n", + "[8&!9&10] 7\n", + "State: 1\n", + "[!8&!9&!10] 8\n", + "[!8&!9&10] 8\n", + "[!8&9&!10] 8\n", + "[!8&9&10] 8\n", + "[8&!9&!10] 8\n", + "[8&!9&10] 8\n", + "[8&9] 8\n", + "State: 2\n", + "[!0&!1&2&!3&4&!5&6&!7] 0\n", + "State: 3\n", + "[!0&1&!2&!3&4&!5&6&!7] 0\n", + "State: 4\n", + "[!0&!1&2&!3&!4&5&6&!7] 0\n", + "State: 5\n", + "[!0&1&!2&!3&!4&5&6&!7] 0\n", + "State: 6\n", + "[!0&!1&2&3&!4&!5&6&!7] 0\n", + "State: 7\n", + "[!0&1&!2&3&!4&!5&6&!7] 0\n", + "State: 8\n", + "[!0&!1&2&!3&!4&5&!6&7 | !0&!1&2&!3&!4&5&6&!7 | !0&!1&2&!3&4&!5&!6&7 | !0&!1&2&!3&4&!5&6&!7 | !0&!1&2&3&!4&!5&!6&7 | !0&!1&2&3&!4&!5&6&!7 | !0&1&!2&!3&!4&5&!6&7 | !0&1&!2&!3&!4&5&6&!7 | !0&1&!2&!3&4&!5&!6&7 | !0&1&!2&!3&4&!5&6&!7 | !0&1&!2&3&!4&!5&!6&7 | !0&1&!2&3&!4&!5&6&!7 | 0&!1&!2&!3&!4&5&!6&7 | 0&!1&!2&!3&!4&5&6&!7 | 0&!1&!2&!3&4&!5&!6&7 | 0&!1&!2&!3&4&!5&6&!7 | 0&!1&!2&3&!4&!5&!6&7 | 0&!1&!2&3&!4&!5&6&!7] 1\n", + "--END--\n" + ] + }, + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "t\n", + "[all]\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "2\n", + "\n", + "2\n", + "\n", + "\n", + "\n", + "0->2\n", + "\n", + "\n", + "0\n", + "\n", + "\n", + "\n", + "0->2\n", + "\n", + "\n", + "!p0p0gt0rpm0f1dcon5523231b & !p0p0lt0rpm0f1dcon32323231b & !p0p0lt0speed0f1dsub0target2speed0f1dmultp0f1dabs0steer1b0f1dcon248881b1b1b\n", + "\n", + "\n", + "\n", + "3\n", + "\n", + "3\n", + "\n", + "\n", + "\n", + "0->3\n", + "\n", + "\n", + "0\n", + "\n", + "\n", + "\n", + "0->3\n", + "\n", + "\n", + "!p0p0gt0rpm0f1dcon5523231b & !p0p0lt0rpm0f1dcon32323231b & p0p0lt0speed0f1dsub0target2speed0f1dmultp0f1dabs0steer1b0f1dcon248881b1b1b\n", + "\n", + "\n", + "\n", + "4\n", + "\n", + "4\n", + "\n", + "\n", + "\n", + "0->4\n", + "\n", + "\n", + "0\n", + "\n", + "\n", + "\n", + "0->4\n", + "\n", + "\n", + "!p0p0gt0rpm0f1dcon5523231b & p0p0lt0rpm0f1dcon32323231b & !p0p0lt0speed0f1dsub0target2speed0f1dmultp0f1dabs0steer1b0f1dcon248881b1b1b\n", + "\n", + "\n", + "\n", + "5\n", + "\n", + "5\n", + "\n", + "\n", + "\n", + "0->5\n", + "\n", + "\n", + "0\n", + "\n", + "\n", + "\n", + "0->5\n", + "\n", + "\n", + "!p0p0gt0rpm0f1dcon5523231b & p0p0lt0rpm0f1dcon32323231b & p0p0lt0speed0f1dsub0target2speed0f1dmultp0f1dabs0steer1b0f1dcon248881b1b1b\n", + "\n", + "\n", + "\n", + "6\n", + "\n", + "6\n", + "\n", + "\n", + "\n", + "0->6\n", + "\n", + "\n", + "0\n", + "\n", + "\n", + "\n", + "0->6\n", + "\n", + "\n", + "p0p0gt0rpm0f1dcon5523231b & !p0p0lt0rpm0f1dcon32323231b & !p0p0lt0speed0f1dsub0target2speed0f1dmultp0f1dabs0steer1b0f1dcon248881b1b1b\n", + "\n", + "\n", + "\n", + "7\n", + "\n", + "7\n", + "\n", + "\n", + "\n", + "0->7\n", + "\n", + "\n", + "0\n", + "\n", + "\n", + "\n", + "0->7\n", + "\n", + "\n", + "p0p0gt0rpm0f1dcon5523231b & !p0p0lt0rpm0f1dcon32323231b & p0p0lt0speed0f1dsub0target2speed0f1dmultp0f1dabs0steer1b0f1dcon248881b1b1b\n", + "\n", + "\n", + "\n", + "8\n", + "\n", + "8\n", + "\n", + "\n", + "\n", + "0->8\n", + "\n", + "\n", + "p0p0gt0rpm0f1dcon5523231b & p0p0lt0rpm0f1dcon32323231b\n", + "\n", + "\n", + "\n", + "2->0\n", + "\n", + "\n", + "!u0accel0accel & !u0accel0f1dcon23p81b & u0accel0f1dcon231b & !u0gear0f1dmax0f1dcon241b0f1dsub0gear0f1dcon241b1b1b & !u0gear0f1dmin0f1dcon61b0f1dadd0gear0f1dcon241b1b1b & u0gear0gear & u0steer0f1dsteering0angle0trackpos1b & !u0steer0steer\n", + "\n", + "\n", + "\n", + "3->0\n", + "\n", + "\n", + "!u0accel0accel & u0accel0f1dcon23p81b & !u0accel0f1dcon231b & !u0gear0f1dmax0f1dcon241b0f1dsub0gear0f1dcon241b1b1b & !u0gear0f1dmin0f1dcon61b0f1dadd0gear0f1dcon241b1b1b & u0gear0gear & u0steer0f1dsteering0angle0trackpos1b & !u0steer0steer\n", + "\n", + "\n", + "\n", + "4->0\n", + "\n", + "\n", + "!u0accel0accel & !u0accel0f1dcon23p81b & u0accel0f1dcon231b & u0gear0f1dmax0f1dcon241b0f1dsub0gear0f1dcon241b1b1b & !u0gear0f1dmin0f1dcon61b0f1dadd0gear0f1dcon241b1b1b & !u0gear0gear & u0steer0f1dsteering0angle0trackpos1b & !u0steer0steer\n", + "\n", + "\n", + "\n", + "5->0\n", + "\n", + "\n", + "!u0accel0accel & u0accel0f1dcon23p81b & !u0accel0f1dcon231b & u0gear0f1dmax0f1dcon241b0f1dsub0gear0f1dcon241b1b1b & !u0gear0f1dmin0f1dcon61b0f1dadd0gear0f1dcon241b1b1b & !u0gear0gear & u0steer0f1dsteering0angle0trackpos1b & !u0steer0steer\n", + "\n", + "\n", + "\n", + "6->0\n", + "\n", + "\n", + "!u0accel0accel & !u0accel0f1dcon23p81b & u0accel0f1dcon231b & !u0gear0f1dmax0f1dcon241b0f1dsub0gear0f1dcon241b1b1b & u0gear0f1dmin0f1dcon61b0f1dadd0gear0f1dcon241b1b1b & !u0gear0gear & u0steer0f1dsteering0angle0trackpos1b & !u0steer0steer\n", + "\n", + "\n", + "\n", + "7->0\n", + "\n", + "\n", + "!u0accel0accel & u0accel0f1dcon23p81b & !u0accel0f1dcon231b & !u0gear0f1dmax0f1dcon241b0f1dsub0gear0f1dcon241b1b1b & u0gear0f1dmin0f1dcon61b0f1dadd0gear0f1dcon241b1b1b & !u0gear0gear & u0steer0f1dsteering0angle0trackpos1b & !u0steer0steer\n", + "\n", + "\n", + "\n", + "1\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "8->1\n", + "\n", + "\n", + "(label too long)\n", + "\n", + "\n", + "\n", + "1->8\n", + "\n", + "\n", + "!p0p0gt0rpm0f1dcon5523231b & !p0p0lt0rpm0f1dcon32323231b & !p0p0lt0speed0f1dsub0target2speed0f1dmultp0f1dabs0steer1b0f1dcon248881b1b1b\n", + "\n", + "\n", + "\n", + "1->8\n", + "\n", + "\n", + "!p0p0gt0rpm0f1dcon5523231b & !p0p0lt0rpm0f1dcon32323231b & p0p0lt0speed0f1dsub0target2speed0f1dmultp0f1dabs0steer1b0f1dcon248881b1b1b\n", + "\n", + "\n", + "\n", + "1->8\n", + "\n", + "\n", + "!p0p0gt0rpm0f1dcon5523231b & p0p0lt0rpm0f1dcon32323231b & !p0p0lt0speed0f1dsub0target2speed0f1dmultp0f1dabs0steer1b0f1dcon248881b1b1b\n", + "\n", + "\n", + "\n", + "1->8\n", + "\n", + "\n", + "!p0p0gt0rpm0f1dcon5523231b & p0p0lt0rpm0f1dcon32323231b & p0p0lt0speed0f1dsub0target2speed0f1dmultp0f1dabs0steer1b0f1dcon248881b1b1b\n", + "\n", + "\n", + "\n", + "1->8\n", + "\n", + "\n", + "p0p0gt0rpm0f1dcon5523231b & !p0p0lt0rpm0f1dcon32323231b & !p0p0lt0speed0f1dsub0target2speed0f1dmultp0f1dabs0steer1b0f1dcon248881b1b1b\n", + "\n", + "\n", + "\n", + "1->8\n", + "\n", + "\n", + "p0p0gt0rpm0f1dcon5523231b & !p0p0lt0rpm0f1dcon32323231b & p0p0lt0speed0f1dsub0target2speed0f1dmultp0f1dabs0steer1b0f1dcon248881b1b1b\n", + "\n", + "\n", + "\n", + "1->8\n", + "\n", + "\n", + "p0p0gt0rpm0f1dcon5523231b & p0p0lt0rpm0f1dcon32323231b\n", + "\n", + "\n", + "\n" + ], + "text/plain": [ + " *' at 0x7f936415f990> >" + ] + }, + "metadata": {}, + "output_type": "display_data" + } + ], + "source": [ + "# Undo relabel\n", + "spot.relabel_game_here(auts, rel_dicts)\n", + "print(auts.to_str(\"hoa\"))\n", + "display(auts)" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "id": "48c2283b", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "True\n" + ] + } + ], + "source": [ + "# Check if we do actually obtain the same automaton\n", + "\n", + "print(spot.are_equivalent(aut, spot.unsplit_2step(auts)))" + ] + }, + { + "cell_type": "code", + "execution_count": 12, + "id": "2b8d907e", + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "True\n", + "True\n", + "True\n", + "True\n", + "True\n", + "True\n", + "True\n", + "True\n", + "True\n", + "True\n", + "True\n", + "True\n", + "True\n", + "True\n", + "True\n", + "True\n" + ] + } + ], + "source": [ + "# Test all options for equivalence\n", + "for relabel_env in [True, False]:\n", + " for relabel_player in [True, False]:\n", + " for split_env in [True, False]:\n", + " for split_player in [True, False]:\n", + " auts = spot.split_2step(aut)\n", + " rel_dicts = spot.partitioned_game_relabel_here(auts, relabel_env, relabel_player, split_env, split_player, 10000, 10000)\n", + " spot.relabel_game_here(auts, rel_dicts)\n", + " print(spot.are_equivalent(aut, spot.unsplit_2step(auts)))" + ] + }, + { + "cell_type": "code", + "execution_count": null, + "id": "17a32a72", + "metadata": {}, + "outputs": [], + "source": [] } ], "metadata": { diff --git a/tests/python/except.py b/tests/python/except.py index e531882dd..03076c01b 100644 --- a/tests/python/except.py +++ b/tests/python/except.py @@ -373,4 +373,22 @@ except RuntimeError as e: tc.assertIn("The given prefix for new variables", str(e)) else: - report_missing_exception() \ No newline at end of file + report_missing_exception() + +# Relabeling games must not use the +# globally reserved aps +aut = spot.make_twa_graph() +aut.new_states(2) +apin = buddy.bdd_ithvar(aut.register_ap("__AP_IN__")) +apout = buddy.bdd_ithvar(aut.register_ap("__AP_OUT__")) +aut.new_edge(0,1,apin & apout) +aut.new_edge(1,0,buddy.bdd_not(apin & apout)) +spot.set_state_players(aut, [False, True]) + +try: + spot.partitioned_game_relabel_here(aut, True, True) +except RuntimeError as e: + tc.assertIn("You can not use __AP_IN__ or __AP_OUT__", + str(e)) +else: + report_missing_exception() From 427f667f9f0db1131d0adf3b72da6710a4eabc9c Mon Sep 17 00:00:00 2001 From: Philipp Schlehuber-Caissier Date: Thu, 6 Oct 2022 22:03:28 +0200 Subject: [PATCH 212/337] lazy eval for sat mealy minimization Evaluate incomp of player conditions only if necessary * spot/twaalgos/mealy_machine.cc: Here --- spot/twaalgos/mealy_machine.cc | 33 +++++++++++++++++++-------------- 1 file changed, 19 insertions(+), 14 deletions(-) diff --git a/spot/twaalgos/mealy_machine.cc b/spot/twaalgos/mealy_machine.cc index df9ad6017..e2b1523de 100644 --- a/spot/twaalgos/mealy_machine.cc +++ b/spot/twaalgos/mealy_machine.cc @@ -1312,7 +1312,9 @@ namespace // Associated condition and id of each player state std::vector> ps2c; ps2c.reserve(n_tot - n_env); + // bdd id -> internal index std::unordered_map all_out_cond; + for (unsigned s1 = n_env; s1 < n_tot; ++s1) { const bdd &c1 = get_cond(s1); @@ -1327,24 +1329,26 @@ namespace #endif } // Are two player condition ids states incompatible + // Matrix for incompatibility square_matrix inc_player(all_out_cond.size(), false); + // Matrix whether computed or not + square_matrix inc_player_comp(all_out_cond.size(), false); // Compute. First is id of bdd - for (const auto& p1 : all_out_cond) - for (const auto& p2 : all_out_cond) - { - if (p1.second > p2.second) - continue; - inc_player.set(p1.second, p2.second, - !bdd_have_common_assignment( - bdd_from_int((int) p1.first), - bdd_from_int((int) p2.first))); - assert(inc_player.get(p1.second, p2.second) - == ((bdd_from_int((int) p1.first) - & bdd_from_int((int) p2.first)) == bddfalse)); - } + // Lazy eval: Compute incompatibility between out conditions + // only if demanded + auto is_p_incomp = [&](unsigned s1, unsigned s2) { - return inc_player.get(ps2c[s1].second, ps2c[s2].second); + const auto& [s1bdd, s1idx] = ps2c[s1]; + const auto& [s2bdd, s2idx] = ps2c[s2]; + + if (!inc_player_comp.get(s1idx, s2idx)) + { + inc_player_comp.set(s1idx, s2idx, true); + inc_player.set(s1idx, s2idx, + !bdd_have_common_assignment(s1bdd, s2bdd)); + } + return inc_player.get(s1idx, s2idx); }; si.player_incomp_time = si.restart(); @@ -3948,6 +3952,7 @@ namespace si.task = "presat"; stopwatch sglob; sglob.start(); + si.start(); if ((premin < -1) || (premin > 1)) throw std::runtime_error("premin has to be -1, 0 or 1"); From d0b15088318fc6a87999b28b31c90f3bbe4dd36b Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Fri, 9 Dec 2022 12:04:15 +0100 Subject: [PATCH 213/337] acd: rewrite Python wrapper without jQuery * python/spot/__init__.py (acd): Rewrite javascript so that it does not use jQuery, to make it easier to use in jupyterlab, or with nbconvert. * tests/python/zlktree.ipynb: Adjust. * NEWS: Mention this. --- NEWS | 4 + python/spot/__init__.py | 58 ++-- tests/python/zlktree.ipynb | 683 ++++++++++++++++++++++++++++--------- 3 files changed, 564 insertions(+), 181 deletions(-) diff --git a/NEWS b/NEWS index c45876408..384ddc8bc 100644 --- a/NEWS +++ b/NEWS @@ -9,6 +9,10 @@ New in spot 2.11.3.dev (not yet released) - b:b[*i..j] = b[*max(i,1)..j] - b[*i..j]:b[*k..l] = b[*max(i,1)+max(k,1)-1, j+l-1] + Python: + + - spot.acd() no longer depends on jQuery for interactivity. + New in spot 2.11.3 (2022-12-09) Bug fixes: diff --git a/python/spot/__init__.py b/python/spot/__init__.py index edbf4a4e6..ef4cd772e 100644 --- a/python/spot/__init__.py +++ b/python/spot/__init__.py @@ -502,51 +502,57 @@ class acd: .acdacc polygon{fill:green;} ''' js = ''' -function acd{num}_clear(){{ - $("#acd{num} .node,#acdaut{num} .node,#acdaut{num} .edge") - .removeClass("acdhigh acdbold acdacc acdrej"); +function acdremclasses(sel, classes) {{ +document.querySelectorAll(sel).forEach(n=>{{n.classList.remove(...classes)}});}} +function acdaddclasses(sel, classes) {{ +document.querySelectorAll(sel).forEach(n=>{{n.classList.add(...classes)}});}} +function acdonclick(sel, fn) {{ + document.querySelectorAll(sel).forEach(n=> + {{n.addEventListener("click", fn)}}); +}} +function acd{num}_clear() {{ + acdremclasses("#acd{num} .node,#acdaut{num} .node,#acdaut{num} .edge", + ["acdhigh", "acdbold", "acdacc", "acdrej"]); }}; function acd{num}_state(state){{ - acd{num}_clear(); - $("#acd{num} .acdS" + state).addClass("acdhigh acdbold"); - $("#acdaut{num} #S" + state).addClass("acdbold"); + acd{num}_clear(); + acdaddclasses("#acd{num} .acdS" + state, ["acdhigh", "acdbold"]); + acdaddclasses("#acdaut{num} #S" + state, ["acdbold"]); }}; function acd{num}_edge(edge){{ - acd{num}_clear(); - var theedge = $('#acdaut{num} #E' + edge) - var classList = theedge.attr('class').split(/\s+/); - $.each(classList, function(index, item) {{ - if (item.startsWith('acdN')) {{ - $("#acd{num} #" + item.substring(3)).addClass("acdhigh acdbold"); - }} - }}); - theedge.addClass("acdbold"); + acd{num}_clear(); + var theedge = document.querySelector('#acdaut{num} #E' + edge); + theedge.classList.forEach(function(item, index) {{ + if (item.startsWith('acdN')) {{ + acdaddclasses("#acd{num} #" + item.substring(3), ["acdhigh", "acdbold"]); + }} + }}); + theedge.classList.add("acdbold"); }}; function acd{num}_node(node, acc){{ acd{num}_clear(); - $("#acdaut{num} .acdN" + node).addClass(acc - ? "acdacc acdbold" - : "acdrej acdbold"); - $("#acd{num} #N" + node).addClass("acdbold acdhigh"); + acdaddclasses("#acdaut{num} .acdN" + node, + [acc ? "acdacc" : "acdrej", "acdbold"]); + acdaddclasses("#acd{num} #N" + node, ["acdbold", "acdhigh"]); }};'''.format(num=num) me = 0 for n in range(self.node_count()): for e in self.edges_of_node(n): me = max(e, me) - js += '$("#acdaut{num} #E{e}").addClass("acdN{n}");'\ + js += 'acdaddclasses("#acdaut{num} #E{e}", ["acdN{n}"]);\n'\ .format(num=num, e=e, n=n) for e in range(1, me + 1): - js += '$("#acdaut{num} #E{e}")'\ - '.click(function(){{acd{num}_edge({e});}});'\ + js += 'acdonclick("#acdaut{num} #E{e}",'\ + 'function(){{acd{num}_edge({e});}});\n'\ .format(num=num, e=e) for s in range(self.get_aut().num_states()): - js += '$("#acdaut{num} #S{s}")'\ - '.click(function(){{acd{num}_state({s});}});'\ + js += 'acdonclick("#acdaut{num} #S{s}",'\ + 'function(){{acd{num}_state({s});}});\n'\ .format(num=num, s=s) for n in range(self.node_count()): v = int(self.node_acceptance(n)) - js += '$("#acd{num} #N{n}")'\ - '.click(function(){{acd{num}_node({n}, {v});}});'\ + js += 'acdonclick("#acd{num} #N{n}",'\ + 'function(){{acd{num}_node({n}, {v});}});\n'\ .format(num=num, n=n, v=v) html = '
{}
{}
'\ .format(style, diff --git a/tests/python/zlktree.ipynb b/tests/python/zlktree.ipynb index ae44ad37d..c9eb3503d 100644 --- a/tests/python/zlktree.ipynb +++ b/tests/python/zlktree.ipynb @@ -216,7 +216,7 @@ "\n" ], "text/plain": [ - " >" + " >" ] }, "execution_count": 2, @@ -640,7 +640,7 @@ "\n" ], "text/plain": [ - " *' at 0x7f14701b7510> >" + " *' at 0x7f82c009d7a0> >" ] }, "execution_count": 10, @@ -1063,7 +1063,7 @@ "\n" ], "text/plain": [ - " *' at 0x7f1470220960> >" + " *' at 0x7f82c009c630> >" ] }, "execution_count": 11, @@ -1256,7 +1256,7 @@ "\n" ], "text/plain": [ - " *' at 0x7f14701b75d0> >" + " *' at 0x7f82c009c6c0> >" ] }, "execution_count": 13, @@ -1701,7 +1701,7 @@ "\n" ], "text/plain": [ - " *' at 0x7f1470142240> >" + " *' at 0x7f82c009c480> >" ] }, "execution_count": 14, @@ -2096,7 +2096,7 @@ "\n" ], "text/plain": [ - " >" + " >" ] }, "metadata": {}, @@ -2427,7 +2427,7 @@ "\n" ], "text/plain": [ - " >" + " >" ] }, "metadata": {}, @@ -2513,7 +2513,7 @@ "\n" ], "text/plain": [ - " >" + " >" ] }, "metadata": {}, @@ -2624,7 +2624,7 @@ "\n" ], "text/plain": [ - " >" + " >" ] }, "metadata": {}, @@ -2662,7 +2662,7 @@ "\n" ], "text/plain": [ - " >" + " >" ] }, "metadata": {}, @@ -2700,7 +2700,7 @@ "\n" ], "text/plain": [ - " >" + " >" ] }, "metadata": {}, @@ -2928,7 +2928,7 @@ "\n" ], "text/plain": [ - " >" + " >" ] }, "execution_count": 18, @@ -4064,36 +4064,159 @@ "\n", "\n", "" + " acdaddclasses(\"#acdaut0 .acdN\" + node,\n", + " [acc ? \"acdacc\" : \"acdrej\", \"acdbold\"]);\n", + " acdaddclasses(\"#acd0 #N\" + node, [\"acdbold\", \"acdhigh\"]);\n", + "};acdaddclasses(\"#acdaut0 #E9\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut0 #E10\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut0 #E11\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut0 #E12\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut0 #E13\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut0 #E14\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut0 #E15\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut0 #E16\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut0 #E21\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut0 #E22\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut0 #E23\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut0 #E24\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut0 #E25\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut0 #E26\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut0 #E27\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut0 #E28\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut0 #E33\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut0 #E34\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut0 #E35\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut0 #E36\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut0 #E31\", [\"acdN1\"]);\n", + "acdaddclasses(\"#acdaut0 #E32\", [\"acdN1\"]);\n", + "acdaddclasses(\"#acdaut0 #E39\", [\"acdN1\"]);\n", + "acdaddclasses(\"#acdaut0 #E40\", [\"acdN1\"]);\n", + "acdaddclasses(\"#acdaut0 #E5\", [\"acdN2\"]);\n", + "acdaddclasses(\"#acdaut0 #E7\", [\"acdN2\"]);\n", + "acdaddclasses(\"#acdaut0 #E17\", [\"acdN2\"]);\n", + "acdaddclasses(\"#acdaut0 #E1\", [\"acdN3\"]);\n", + "acdaddclasses(\"#acdaut0 #E10\", [\"acdN4\"]);\n", + "acdaddclasses(\"#acdaut0 #E12\", [\"acdN4\"]);\n", + "acdaddclasses(\"#acdaut0 #E13\", [\"acdN4\"]);\n", + "acdaddclasses(\"#acdaut0 #E15\", [\"acdN4\"]);\n", + "acdaddclasses(\"#acdaut0 #E21\", [\"acdN4\"]);\n", + "acdaddclasses(\"#acdaut0 #E22\", [\"acdN4\"]);\n", + "acdaddclasses(\"#acdaut0 #E23\", [\"acdN5\"]);\n", + "acdaddclasses(\"#acdaut0 #E24\", [\"acdN5\"]);\n", + "acdaddclasses(\"#acdaut0 #E34\", [\"acdN5\"]);\n", + "acdaddclasses(\"#acdaut0 #E36\", [\"acdN5\"]);\n", + "acdaddclasses(\"#acdaut0 #E14\", [\"acdN6\"]);\n", + "acdaddclasses(\"#acdaut0 #E15\", [\"acdN6\"]);\n", + "acdaddclasses(\"#acdaut0 #E22\", [\"acdN6\"]);\n", + "acdaddclasses(\"#acdaut0 #E23\", [\"acdN6\"]);\n", + "acdaddclasses(\"#acdaut0 #E14\", [\"acdN7\"]);\n", + "acdaddclasses(\"#acdaut0 #E16\", [\"acdN7\"]);\n", + "acdaddclasses(\"#acdaut0 #E26\", [\"acdN7\"]);\n", + "acdaddclasses(\"#acdaut0 #E9\", [\"acdN8\"]);\n", + "acdaddclasses(\"#acdaut0 #E40\", [\"acdN9\"]);\n", + "acdaddclasses(\"#acdaut0 #E5\", [\"acdN10\"]);\n", + "acdaddclasses(\"#acdaut0 #E23\", [\"acdN11\"]);\n", + "acdaddclasses(\"#acdaut0 #E14\", [\"acdN12\"]);\n", + "acdaddclasses(\"#acdaut0 #E23\", [\"acdN13\"]);\n", + "acdaddclasses(\"#acdaut0 #E14\", [\"acdN14\"]);\n", + "acdonclick(\"#acdaut0 #E1\",function(){acd0_edge(1);});\n", + "acdonclick(\"#acdaut0 #E2\",function(){acd0_edge(2);});\n", + "acdonclick(\"#acdaut0 #E3\",function(){acd0_edge(3);});\n", + "acdonclick(\"#acdaut0 #E4\",function(){acd0_edge(4);});\n", + "acdonclick(\"#acdaut0 #E5\",function(){acd0_edge(5);});\n", + "acdonclick(\"#acdaut0 #E6\",function(){acd0_edge(6);});\n", + "acdonclick(\"#acdaut0 #E7\",function(){acd0_edge(7);});\n", + "acdonclick(\"#acdaut0 #E8\",function(){acd0_edge(8);});\n", + "acdonclick(\"#acdaut0 #E9\",function(){acd0_edge(9);});\n", + "acdonclick(\"#acdaut0 #E10\",function(){acd0_edge(10);});\n", + "acdonclick(\"#acdaut0 #E11\",function(){acd0_edge(11);});\n", + "acdonclick(\"#acdaut0 #E12\",function(){acd0_edge(12);});\n", + "acdonclick(\"#acdaut0 #E13\",function(){acd0_edge(13);});\n", + "acdonclick(\"#acdaut0 #E14\",function(){acd0_edge(14);});\n", + "acdonclick(\"#acdaut0 #E15\",function(){acd0_edge(15);});\n", + "acdonclick(\"#acdaut0 #E16\",function(){acd0_edge(16);});\n", + "acdonclick(\"#acdaut0 #E17\",function(){acd0_edge(17);});\n", + "acdonclick(\"#acdaut0 #E18\",function(){acd0_edge(18);});\n", + "acdonclick(\"#acdaut0 #E19\",function(){acd0_edge(19);});\n", + "acdonclick(\"#acdaut0 #E20\",function(){acd0_edge(20);});\n", + "acdonclick(\"#acdaut0 #E21\",function(){acd0_edge(21);});\n", + "acdonclick(\"#acdaut0 #E22\",function(){acd0_edge(22);});\n", + "acdonclick(\"#acdaut0 #E23\",function(){acd0_edge(23);});\n", + "acdonclick(\"#acdaut0 #E24\",function(){acd0_edge(24);});\n", + "acdonclick(\"#acdaut0 #E25\",function(){acd0_edge(25);});\n", + "acdonclick(\"#acdaut0 #E26\",function(){acd0_edge(26);});\n", + "acdonclick(\"#acdaut0 #E27\",function(){acd0_edge(27);});\n", + "acdonclick(\"#acdaut0 #E28\",function(){acd0_edge(28);});\n", + "acdonclick(\"#acdaut0 #E29\",function(){acd0_edge(29);});\n", + "acdonclick(\"#acdaut0 #E30\",function(){acd0_edge(30);});\n", + "acdonclick(\"#acdaut0 #E31\",function(){acd0_edge(31);});\n", + "acdonclick(\"#acdaut0 #E32\",function(){acd0_edge(32);});\n", + "acdonclick(\"#acdaut0 #E33\",function(){acd0_edge(33);});\n", + "acdonclick(\"#acdaut0 #E34\",function(){acd0_edge(34);});\n", + "acdonclick(\"#acdaut0 #E35\",function(){acd0_edge(35);});\n", + "acdonclick(\"#acdaut0 #E36\",function(){acd0_edge(36);});\n", + "acdonclick(\"#acdaut0 #E37\",function(){acd0_edge(37);});\n", + "acdonclick(\"#acdaut0 #E38\",function(){acd0_edge(38);});\n", + "acdonclick(\"#acdaut0 #E39\",function(){acd0_edge(39);});\n", + "acdonclick(\"#acdaut0 #E40\",function(){acd0_edge(40);});\n", + "acdonclick(\"#acdaut0 #S0\",function(){acd0_state(0);});\n", + "acdonclick(\"#acdaut0 #S1\",function(){acd0_state(1);});\n", + "acdonclick(\"#acdaut0 #S2\",function(){acd0_state(2);});\n", + "acdonclick(\"#acdaut0 #S3\",function(){acd0_state(3);});\n", + "acdonclick(\"#acdaut0 #S4\",function(){acd0_state(4);});\n", + "acdonclick(\"#acdaut0 #S5\",function(){acd0_state(5);});\n", + "acdonclick(\"#acdaut0 #S6\",function(){acd0_state(6);});\n", + "acdonclick(\"#acdaut0 #S7\",function(){acd0_state(7);});\n", + "acdonclick(\"#acdaut0 #S8\",function(){acd0_state(8);});\n", + "acdonclick(\"#acdaut0 #S9\",function(){acd0_state(9);});\n", + "acdonclick(\"#acd0 #N0\",function(){acd0_node(0, 0);});\n", + "acdonclick(\"#acd0 #N1\",function(){acd0_node(1, 1);});\n", + "acdonclick(\"#acd0 #N2\",function(){acd0_node(2, 1);});\n", + "acdonclick(\"#acd0 #N3\",function(){acd0_node(3, 1);});\n", + "acdonclick(\"#acd0 #N4\",function(){acd0_node(4, 1);});\n", + "acdonclick(\"#acd0 #N5\",function(){acd0_node(5, 1);});\n", + "acdonclick(\"#acd0 #N6\",function(){acd0_node(6, 1);});\n", + "acdonclick(\"#acd0 #N7\",function(){acd0_node(7, 1);});\n", + "acdonclick(\"#acd0 #N8\",function(){acd0_node(8, 1);});\n", + "acdonclick(\"#acd0 #N9\",function(){acd0_node(9, 0);});\n", + "acdonclick(\"#acd0 #N10\",function(){acd0_node(10, 0);});\n", + "acdonclick(\"#acd0 #N11\",function(){acd0_node(11, 0);});\n", + "acdonclick(\"#acd0 #N12\",function(){acd0_node(12, 0);});\n", + "acdonclick(\"#acd0 #N13\",function(){acd0_node(13, 0);});\n", + "acdonclick(\"#acd0 #N14\",function(){acd0_node(14, 0);});\n", + "" ], "text/plain": [ - " >" + " >" ] }, "execution_count": 20, @@ -4968,7 +5091,7 @@ "\n" ], "text/plain": [ - " *' at 0x7f14701670f0> >" + " *' at 0x7f82c00bc870> >" ] }, "execution_count": 29, @@ -5607,7 +5730,7 @@ "\n" ], "text/plain": [ - " *' at 0x7f1470167210> >" + " *' at 0x7f82c00bc060> >" ] }, "execution_count": 31, @@ -5807,7 +5930,9 @@ "cell_type": "code", "execution_count": 40, "id": "813d15ed", - "metadata": {}, + "metadata": { + "scrolled": false + }, "outputs": [ { "data": { @@ -6875,36 +7000,159 @@ "\n", "\n", "" + " acdaddclasses(\"#acdaut1 .acdN\" + node,\n", + " [acc ? \"acdacc\" : \"acdrej\", \"acdbold\"]);\n", + " acdaddclasses(\"#acd1 #N\" + node, [\"acdbold\", \"acdhigh\"]);\n", + "};acdaddclasses(\"#acdaut1 #E9\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut1 #E10\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut1 #E11\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut1 #E12\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut1 #E13\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut1 #E14\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut1 #E15\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut1 #E16\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut1 #E21\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut1 #E22\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut1 #E23\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut1 #E24\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut1 #E25\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut1 #E26\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut1 #E27\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut1 #E28\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut1 #E33\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut1 #E34\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut1 #E35\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut1 #E36\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut1 #E31\", [\"acdN1\"]);\n", + "acdaddclasses(\"#acdaut1 #E32\", [\"acdN1\"]);\n", + "acdaddclasses(\"#acdaut1 #E39\", [\"acdN1\"]);\n", + "acdaddclasses(\"#acdaut1 #E40\", [\"acdN1\"]);\n", + "acdaddclasses(\"#acdaut1 #E5\", [\"acdN2\"]);\n", + "acdaddclasses(\"#acdaut1 #E7\", [\"acdN2\"]);\n", + "acdaddclasses(\"#acdaut1 #E17\", [\"acdN2\"]);\n", + "acdaddclasses(\"#acdaut1 #E1\", [\"acdN3\"]);\n", + "acdaddclasses(\"#acdaut1 #E10\", [\"acdN4\"]);\n", + "acdaddclasses(\"#acdaut1 #E12\", [\"acdN4\"]);\n", + "acdaddclasses(\"#acdaut1 #E13\", [\"acdN4\"]);\n", + "acdaddclasses(\"#acdaut1 #E15\", [\"acdN4\"]);\n", + "acdaddclasses(\"#acdaut1 #E21\", [\"acdN4\"]);\n", + "acdaddclasses(\"#acdaut1 #E22\", [\"acdN4\"]);\n", + "acdaddclasses(\"#acdaut1 #E14\", [\"acdN5\"]);\n", + "acdaddclasses(\"#acdaut1 #E15\", [\"acdN5\"]);\n", + "acdaddclasses(\"#acdaut1 #E22\", [\"acdN5\"]);\n", + "acdaddclasses(\"#acdaut1 #E23\", [\"acdN5\"]);\n", + "acdaddclasses(\"#acdaut1 #E23\", [\"acdN6\"]);\n", + "acdaddclasses(\"#acdaut1 #E24\", [\"acdN6\"]);\n", + "acdaddclasses(\"#acdaut1 #E34\", [\"acdN6\"]);\n", + "acdaddclasses(\"#acdaut1 #E36\", [\"acdN6\"]);\n", + "acdaddclasses(\"#acdaut1 #E14\", [\"acdN7\"]);\n", + "acdaddclasses(\"#acdaut1 #E16\", [\"acdN7\"]);\n", + "acdaddclasses(\"#acdaut1 #E26\", [\"acdN7\"]);\n", + "acdaddclasses(\"#acdaut1 #E9\", [\"acdN8\"]);\n", + "acdaddclasses(\"#acdaut1 #E40\", [\"acdN9\"]);\n", + "acdaddclasses(\"#acdaut1 #E5\", [\"acdN10\"]);\n", + "acdaddclasses(\"#acdaut1 #E14\", [\"acdN11\"]);\n", + "acdaddclasses(\"#acdaut1 #E23\", [\"acdN12\"]);\n", + "acdaddclasses(\"#acdaut1 #E23\", [\"acdN13\"]);\n", + "acdaddclasses(\"#acdaut1 #E14\", [\"acdN14\"]);\n", + "acdonclick(\"#acdaut1 #E1\",function(){acd1_edge(1);});\n", + "acdonclick(\"#acdaut1 #E2\",function(){acd1_edge(2);});\n", + "acdonclick(\"#acdaut1 #E3\",function(){acd1_edge(3);});\n", + "acdonclick(\"#acdaut1 #E4\",function(){acd1_edge(4);});\n", + "acdonclick(\"#acdaut1 #E5\",function(){acd1_edge(5);});\n", + "acdonclick(\"#acdaut1 #E6\",function(){acd1_edge(6);});\n", + "acdonclick(\"#acdaut1 #E7\",function(){acd1_edge(7);});\n", + "acdonclick(\"#acdaut1 #E8\",function(){acd1_edge(8);});\n", + "acdonclick(\"#acdaut1 #E9\",function(){acd1_edge(9);});\n", + "acdonclick(\"#acdaut1 #E10\",function(){acd1_edge(10);});\n", + "acdonclick(\"#acdaut1 #E11\",function(){acd1_edge(11);});\n", + "acdonclick(\"#acdaut1 #E12\",function(){acd1_edge(12);});\n", + "acdonclick(\"#acdaut1 #E13\",function(){acd1_edge(13);});\n", + "acdonclick(\"#acdaut1 #E14\",function(){acd1_edge(14);});\n", + "acdonclick(\"#acdaut1 #E15\",function(){acd1_edge(15);});\n", + "acdonclick(\"#acdaut1 #E16\",function(){acd1_edge(16);});\n", + "acdonclick(\"#acdaut1 #E17\",function(){acd1_edge(17);});\n", + "acdonclick(\"#acdaut1 #E18\",function(){acd1_edge(18);});\n", + "acdonclick(\"#acdaut1 #E19\",function(){acd1_edge(19);});\n", + "acdonclick(\"#acdaut1 #E20\",function(){acd1_edge(20);});\n", + "acdonclick(\"#acdaut1 #E21\",function(){acd1_edge(21);});\n", + "acdonclick(\"#acdaut1 #E22\",function(){acd1_edge(22);});\n", + "acdonclick(\"#acdaut1 #E23\",function(){acd1_edge(23);});\n", + "acdonclick(\"#acdaut1 #E24\",function(){acd1_edge(24);});\n", + "acdonclick(\"#acdaut1 #E25\",function(){acd1_edge(25);});\n", + "acdonclick(\"#acdaut1 #E26\",function(){acd1_edge(26);});\n", + "acdonclick(\"#acdaut1 #E27\",function(){acd1_edge(27);});\n", + "acdonclick(\"#acdaut1 #E28\",function(){acd1_edge(28);});\n", + "acdonclick(\"#acdaut1 #E29\",function(){acd1_edge(29);});\n", + "acdonclick(\"#acdaut1 #E30\",function(){acd1_edge(30);});\n", + "acdonclick(\"#acdaut1 #E31\",function(){acd1_edge(31);});\n", + "acdonclick(\"#acdaut1 #E32\",function(){acd1_edge(32);});\n", + "acdonclick(\"#acdaut1 #E33\",function(){acd1_edge(33);});\n", + "acdonclick(\"#acdaut1 #E34\",function(){acd1_edge(34);});\n", + "acdonclick(\"#acdaut1 #E35\",function(){acd1_edge(35);});\n", + "acdonclick(\"#acdaut1 #E36\",function(){acd1_edge(36);});\n", + "acdonclick(\"#acdaut1 #E37\",function(){acd1_edge(37);});\n", + "acdonclick(\"#acdaut1 #E38\",function(){acd1_edge(38);});\n", + "acdonclick(\"#acdaut1 #E39\",function(){acd1_edge(39);});\n", + "acdonclick(\"#acdaut1 #E40\",function(){acd1_edge(40);});\n", + "acdonclick(\"#acdaut1 #S0\",function(){acd1_state(0);});\n", + "acdonclick(\"#acdaut1 #S1\",function(){acd1_state(1);});\n", + "acdonclick(\"#acdaut1 #S2\",function(){acd1_state(2);});\n", + "acdonclick(\"#acdaut1 #S3\",function(){acd1_state(3);});\n", + "acdonclick(\"#acdaut1 #S4\",function(){acd1_state(4);});\n", + "acdonclick(\"#acdaut1 #S5\",function(){acd1_state(5);});\n", + "acdonclick(\"#acdaut1 #S6\",function(){acd1_state(6);});\n", + "acdonclick(\"#acdaut1 #S7\",function(){acd1_state(7);});\n", + "acdonclick(\"#acdaut1 #S8\",function(){acd1_state(8);});\n", + "acdonclick(\"#acdaut1 #S9\",function(){acd1_state(9);});\n", + "acdonclick(\"#acd1 #N0\",function(){acd1_node(0, 0);});\n", + "acdonclick(\"#acd1 #N1\",function(){acd1_node(1, 1);});\n", + "acdonclick(\"#acd1 #N2\",function(){acd1_node(2, 1);});\n", + "acdonclick(\"#acd1 #N3\",function(){acd1_node(3, 1);});\n", + "acdonclick(\"#acd1 #N4\",function(){acd1_node(4, 1);});\n", + "acdonclick(\"#acd1 #N5\",function(){acd1_node(5, 1);});\n", + "acdonclick(\"#acd1 #N6\",function(){acd1_node(6, 1);});\n", + "acdonclick(\"#acd1 #N7\",function(){acd1_node(7, 1);});\n", + "acdonclick(\"#acd1 #N8\",function(){acd1_node(8, 1);});\n", + "acdonclick(\"#acd1 #N9\",function(){acd1_node(9, 0);});\n", + "acdonclick(\"#acd1 #N10\",function(){acd1_node(10, 0);});\n", + "acdonclick(\"#acd1 #N11\",function(){acd1_node(11, 0);});\n", + "acdonclick(\"#acd1 #N12\",function(){acd1_node(12, 0);});\n", + "acdonclick(\"#acd1 #N13\",function(){acd1_node(13, 0);});\n", + "acdonclick(\"#acd1 #N14\",function(){acd1_node(14, 0);});\n", + "" ], "text/plain": [ - " >" + " >" ] }, "execution_count": 40, @@ -7817,7 +8065,7 @@ "\n" ], "text/plain": [ - " *' at 0x7f14700fe1e0> >" + " *' at 0x7f82c00be460> >" ] }, "execution_count": 45, @@ -8114,36 +8362,69 @@ "\n", "\n", "" + " acdaddclasses(\"#acdaut2 .acdN\" + node,\n", + " [acc ? \"acdacc\" : \"acdrej\", \"acdbold\"]);\n", + " acdaddclasses(\"#acd2 #N\" + node, [\"acdbold\", \"acdhigh\"]);\n", + "};acdaddclasses(\"#acdaut2 #E1\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut2 #E2\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut2 #E3\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut2 #E4\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut2 #E5\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut2 #E6\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut2 #E2\", [\"acdN1\"]);\n", + "acdaddclasses(\"#acdaut2 #E3\", [\"acdN1\"]);\n", + "acdaddclasses(\"#acdaut2 #E4\", [\"acdN1\"]);\n", + "acdaddclasses(\"#acdaut2 #E5\", [\"acdN1\"]);\n", + "acdaddclasses(\"#acdaut2 #E6\", [\"acdN1\"]);\n", + "acdaddclasses(\"#acdaut2 #E1\", [\"acdN2\"]);\n", + "acdaddclasses(\"#acdaut2 #E2\", [\"acdN2\"]);\n", + "acdaddclasses(\"#acdaut2 #E4\", [\"acdN2\"]);\n", + "acdaddclasses(\"#acdaut2 #E6\", [\"acdN2\"]);\n", + "acdonclick(\"#acdaut2 #E1\",function(){acd2_edge(1);});\n", + "acdonclick(\"#acdaut2 #E2\",function(){acd2_edge(2);});\n", + "acdonclick(\"#acdaut2 #E3\",function(){acd2_edge(3);});\n", + "acdonclick(\"#acdaut2 #E4\",function(){acd2_edge(4);});\n", + "acdonclick(\"#acdaut2 #E5\",function(){acd2_edge(5);});\n", + "acdonclick(\"#acdaut2 #E6\",function(){acd2_edge(6);});\n", + "acdonclick(\"#acdaut2 #S0\",function(){acd2_state(0);});\n", + "acdonclick(\"#acdaut2 #S1\",function(){acd2_state(1);});\n", + "acdonclick(\"#acdaut2 #S2\",function(){acd2_state(2);});\n", + "acdonclick(\"#acd2 #N0\",function(){acd2_node(0, 1);});\n", + "acdonclick(\"#acd2 #N1\",function(){acd2_node(1, 0);});\n", + "acdonclick(\"#acd2 #N2\",function(){acd2_node(2, 0);});\n", + "" ], "text/plain": [ - " >" + " >" ] }, "execution_count": 47, @@ -8353,7 +8634,7 @@ "\n" ], "text/plain": [ - " *' at 0x7f14700feb40> >" + " *' at 0x7f82c00bdd40> >" ] }, "execution_count": 48, @@ -8628,36 +8909,69 @@ "\n", "\n", "" + " acdaddclasses(\"#acdaut3 .acdN\" + node,\n", + " [acc ? \"acdacc\" : \"acdrej\", \"acdbold\"]);\n", + " acdaddclasses(\"#acd3 #N\" + node, [\"acdbold\", \"acdhigh\"]);\n", + "};acdaddclasses(\"#acdaut3 #E1\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut3 #E2\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut3 #E3\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut3 #E4\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut3 #E5\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut3 #E6\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut3 #E1\", [\"acdN1\"]);\n", + "acdaddclasses(\"#acdaut3 #E2\", [\"acdN1\"]);\n", + "acdaddclasses(\"#acdaut3 #E4\", [\"acdN1\"]);\n", + "acdaddclasses(\"#acdaut3 #E6\", [\"acdN1\"]);\n", + "acdaddclasses(\"#acdaut3 #E2\", [\"acdN2\"]);\n", + "acdaddclasses(\"#acdaut3 #E3\", [\"acdN2\"]);\n", + "acdaddclasses(\"#acdaut3 #E4\", [\"acdN2\"]);\n", + "acdaddclasses(\"#acdaut3 #E5\", [\"acdN2\"]);\n", + "acdaddclasses(\"#acdaut3 #E6\", [\"acdN2\"]);\n", + "acdonclick(\"#acdaut3 #E1\",function(){acd3_edge(1);});\n", + "acdonclick(\"#acdaut3 #E2\",function(){acd3_edge(2);});\n", + "acdonclick(\"#acdaut3 #E3\",function(){acd3_edge(3);});\n", + "acdonclick(\"#acdaut3 #E4\",function(){acd3_edge(4);});\n", + "acdonclick(\"#acdaut3 #E5\",function(){acd3_edge(5);});\n", + "acdonclick(\"#acdaut3 #E6\",function(){acd3_edge(6);});\n", + "acdonclick(\"#acdaut3 #S0\",function(){acd3_state(0);});\n", + "acdonclick(\"#acdaut3 #S1\",function(){acd3_state(1);});\n", + "acdonclick(\"#acdaut3 #S2\",function(){acd3_state(2);});\n", + "acdonclick(\"#acd3 #N0\",function(){acd3_node(0, 1);});\n", + "acdonclick(\"#acd3 #N1\",function(){acd3_node(1, 0);});\n", + "acdonclick(\"#acd3 #N2\",function(){acd3_node(2, 0);});\n", + "" ], "text/plain": [ - " >" + " >" ] }, "execution_count": 49, @@ -8841,7 +9155,7 @@ "\n" ], "text/plain": [ - " *' at 0x7f14700fea80> >" + " *' at 0x7f82c00bf300> >" ] }, "execution_count": 50, @@ -8993,7 +9307,7 @@ "\n" ], "text/plain": [ - " *' at 0x7f1470107240> >" + " *' at 0x7f82c00be5b0> >" ] }, "execution_count": 51, @@ -9105,7 +9419,7 @@ "\n" ], "text/plain": [ - " >" + " >" ] }, "execution_count": 52, @@ -9271,7 +9585,7 @@ "\n" ], "text/plain": [ - " *' at 0x7f1470107030> >" + " *' at 0x7f82c00bf5d0> >" ] }, "execution_count": 53, @@ -9535,36 +9849,63 @@ "\n", "\n", "" + " acdaddclasses(\"#acdaut4 .acdN\" + node,\n", + " [acc ? \"acdacc\" : \"acdrej\", \"acdbold\"]);\n", + " acdaddclasses(\"#acd4 #N\" + node, [\"acdbold\", \"acdhigh\"]);\n", + "};acdaddclasses(\"#acdaut4 #E1\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut4 #E2\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut4 #E3\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut4 #E4\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut4 #E5\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut4 #E6\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut4 #E7\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut4 #E8\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut4 #E6\", [\"acdN1\"]);\n", + "acdonclick(\"#acdaut4 #E1\",function(){acd4_edge(1);});\n", + "acdonclick(\"#acdaut4 #E2\",function(){acd4_edge(2);});\n", + "acdonclick(\"#acdaut4 #E3\",function(){acd4_edge(3);});\n", + "acdonclick(\"#acdaut4 #E4\",function(){acd4_edge(4);});\n", + "acdonclick(\"#acdaut4 #E5\",function(){acd4_edge(5);});\n", + "acdonclick(\"#acdaut4 #E6\",function(){acd4_edge(6);});\n", + "acdonclick(\"#acdaut4 #E7\",function(){acd4_edge(7);});\n", + "acdonclick(\"#acdaut4 #E8\",function(){acd4_edge(8);});\n", + "acdonclick(\"#acdaut4 #S0\",function(){acd4_state(0);});\n", + "acdonclick(\"#acdaut4 #S1\",function(){acd4_state(1);});\n", + "acdonclick(\"#acd4 #N0\",function(){acd4_node(0, 1);});\n", + "acdonclick(\"#acd4 #N1\",function(){acd4_node(1, 0);});\n", + "" ], "text/plain": [ - " >" + " >" ] }, "execution_count": 55, @@ -9708,7 +10049,7 @@ "\n" ], "text/plain": [ - " *' at 0x7f1470107b70> >" + " *' at 0x7f82c00f4240> >" ] }, "execution_count": 57, @@ -9855,7 +10196,7 @@ "\n" ], "text/plain": [ - " *' at 0x7f147010d240> >" + " *' at 0x7f82c00f4090> >" ] }, "execution_count": 58, @@ -10165,36 +10506,68 @@ "\n", "\n", "" + " acdaddclasses(\"#acdaut5 .acdN\" + node,\n", + " [acc ? \"acdacc\" : \"acdrej\", \"acdbold\"]);\n", + " acdaddclasses(\"#acd5 #N\" + node, [\"acdbold\", \"acdhigh\"]);\n", + "};acdaddclasses(\"#acdaut5 #E1\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut5 #E2\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut5 #E3\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut5 #E4\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut5 #E5\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut5 #E6\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut5 #E7\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut5 #E1\", [\"acdN1\"]);\n", + "acdaddclasses(\"#acdaut5 #E3\", [\"acdN1\"]);\n", + "acdaddclasses(\"#acdaut5 #E4\", [\"acdN1\"]);\n", + "acdaddclasses(\"#acdaut5 #E5\", [\"acdN1\"]);\n", + "acdaddclasses(\"#acdaut5 #E7\", [\"acdN2\"]);\n", + "acdonclick(\"#acdaut5 #E1\",function(){acd5_edge(1);});\n", + "acdonclick(\"#acdaut5 #E2\",function(){acd5_edge(2);});\n", + "acdonclick(\"#acdaut5 #E3\",function(){acd5_edge(3);});\n", + "acdonclick(\"#acdaut5 #E4\",function(){acd5_edge(4);});\n", + "acdonclick(\"#acdaut5 #E5\",function(){acd5_edge(5);});\n", + "acdonclick(\"#acdaut5 #E6\",function(){acd5_edge(6);});\n", + "acdonclick(\"#acdaut5 #E7\",function(){acd5_edge(7);});\n", + "acdonclick(\"#acdaut5 #S0\",function(){acd5_state(0);});\n", + "acdonclick(\"#acdaut5 #S1\",function(){acd5_state(1);});\n", + "acdonclick(\"#acdaut5 #S2\",function(){acd5_state(2);});\n", + "acdonclick(\"#acdaut5 #S3\",function(){acd5_state(3);});\n", + "acdonclick(\"#acd5 #N0\",function(){acd5_node(0, 1);});\n", + "acdonclick(\"#acd5 #N1\",function(){acd5_node(1, 0);});\n", + "acdonclick(\"#acd5 #N2\",function(){acd5_node(2, 0);});\n", + "" ], "text/plain": [ - " >" + " >" ] }, "execution_count": 60, @@ -10322,7 +10695,7 @@ "\n" ], "text/plain": [ - " *' at 0x7f147010d5a0> >" + " *' at 0x7f82c00f50b0> >" ] }, "execution_count": 61, @@ -10452,7 +10825,7 @@ "\n" ], "text/plain": [ - " *' at 0x7f147010d6f0> >" + " *' at 0x7f82c00f52c0> >" ] }, "execution_count": 62, @@ -10732,7 +11105,7 @@ "\n" ], "text/plain": [ - " *' at 0x7f1470116270> >" + " *' at 0x7f82c00f4960> >" ] }, "execution_count": 63, @@ -10826,7 +11199,7 @@ "\n" ], "text/plain": [ - " *' at 0x7f1470116630> >" + " *' at 0x7f82c00f5a10> >" ] }, "execution_count": 64, @@ -10937,7 +11310,7 @@ "\n" ], "text/plain": [ - " *' at 0x7f1470116450> >" + " *' at 0x7f82c00f5ce0> >" ] }, "execution_count": 66, @@ -10995,7 +11368,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.2" + "version": "3.10.7" } }, "nbformat": 4, From ba695194cd9161bc3fe4a5075b348684134989b6 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Fri, 9 Dec 2022 16:22:06 +0100 Subject: [PATCH 214/337] priv: remove unused allocator.hh * spot/priv/allocator.hh: Delete. * spot/priv/Makefile.am, tests/core/mempool.cc: Adjust. --- spot/priv/Makefile.am | 3 +- spot/priv/allocator.hh | 104 ----------------------------------------- tests/core/mempool.cc | 29 ------------ 3 files changed, 1 insertion(+), 135 deletions(-) delete mode 100644 spot/priv/allocator.hh diff --git a/spot/priv/Makefile.am b/spot/priv/Makefile.am index 317292bd3..b2c75ab7d 100644 --- a/spot/priv/Makefile.am +++ b/spot/priv/Makefile.am @@ -1,5 +1,5 @@ ## -*- coding: utf-8 -*- -## Copyright (C) 2013-2019, 2021 Laboratoire de Recherche et +## Copyright (C) 2013-2019, 2021-2022 Laboratoire de Recherche et ## Développement de l'Epita (LRDE). ## ## This file is part of Spot, a model checking library. @@ -24,7 +24,6 @@ AM_CXXFLAGS = $(WARNING_CXXFLAGS) noinst_LTLIBRARIES = libpriv.la libpriv_la_SOURCES = \ accmap.hh \ - allocator.hh \ bddalloc.cc \ bddalloc.hh \ freelist.cc \ diff --git a/spot/priv/allocator.hh b/spot/priv/allocator.hh deleted file mode 100644 index 9c3d50268..000000000 --- a/spot/priv/allocator.hh +++ /dev/null @@ -1,104 +0,0 @@ -// -*- coding: utf-8 -*- -// Copyright (C) 2011, 2015-2018 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 . - -#pragma once - -#include - -namespace spot -{ - /// An allocator to be used with STL containers. - /// It uses a spot::fixed_size_pool to handle memory. - /// It is intended to improve performance and locality of node-based - /// containers (std::{unordered}{multi}{set,map}). - /// It is geared towards efficiently allocating memory for one object at a - /// time (the nodes of the node-based containers). Larger allocations are - /// served by calling the global memory allocation mechanism (::operator new). - /// Using it for contiguous containers (such as std::vector or std::deque) - /// will be less efficient than using the default std::allocator. - /// - /// Short reminder on STL concept of Allocator: - /// allocate() may throw - /// deallocate() must not throw - /// equality testing (i.e. == and !=) must not throw - /// copying allocator (constructor and assignment) must not throw - /// moving allocator (constructor and assignment) must not throw - /// - /// WARNING this class is NOT thread-safe: the allocator relies on a static - /// fixed_size_pool (which is not thread-safe either). - template - class pool_allocator - { - static - fixed_size_pool& - pool() - { - static fixed_size_pool p = - fixed_size_pool(sizeof(T)); - return p; - } - - public: - using value_type = T; - using pointer = value_type*; - using const_pointer = const value_type*; - using size_type = size_t; - - constexpr pool_allocator() noexcept - {} - template - constexpr pool_allocator(const pool_allocator&) noexcept - {} - - template - struct rebind - { - using other = pool_allocator; - }; - - pointer - allocate(size_type n) - { - if (SPOT_LIKELY(n == 1)) - return static_cast(pool().allocate()); - else - return static_cast(::operator new(n*sizeof(T))); - } - - void - deallocate(pointer ptr, size_type n) noexcept - { - if (SPOT_LIKELY(n == 1)) - pool().deallocate(static_cast(ptr)); - else - ::operator delete(ptr); - } - - bool - operator==(const pool_allocator&) const noexcept - { - return true; - } - bool - operator!=(const pool_allocator& o) const noexcept - { - return !(this->operator==(o)); - } - }; -} diff --git a/tests/core/mempool.cc b/tests/core/mempool.cc index 9d3610df7..1431a24b2 100644 --- a/tests/core/mempool.cc +++ b/tests/core/mempool.cc @@ -23,9 +23,6 @@ #include #include -#include - -#include namespace { @@ -157,32 +154,6 @@ int main() c->incr(); // no delete: valgrind should find a leak } - { - std::set, spot::pool_allocator> s; - s.insert(1); - s.insert(2); - s.insert(1); - s.erase(1); - s.insert(3); - s.insert(4); - - s.clear(); - - auto t = s; - t.insert(5); - t.insert(6); - - std::swap(s, t); - - s.erase(5); - s.erase(6); - - if (s != t) - return 1; - else - return 0; - } - return 0; } From c9ba998200d6f57037360c67628bf24f6bf79e1b Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Fri, 9 Dec 2022 16:35:05 +0100 Subject: [PATCH 215/337] avoid a g++-12 warning about potential null pointer dereference * spot/twaalgos/determinize.cc (sorted_nodes): Rewrite to avoid reallocation of temporary vector. --- spot/twaalgos/determinize.cc | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/spot/twaalgos/determinize.cc b/spot/twaalgos/determinize.cc index ba4fb3ded..d2d35a824 100644 --- a/spot/twaalgos/determinize.cc +++ b/spot/twaalgos/determinize.cc @@ -472,15 +472,23 @@ namespace spot std::vector res; for (const auto& n: s.nodes_) { - int brace = n.second; - std::vector tmp; - while (brace >= 0) + // First, count the number of braces. + unsigned nbraces = 0; + for (int brace = n.second; brace >= 0; brace = s.braces_[brace]) + ++nbraces; + // Then list them in reverse order. Since we know the + // number of braces, we can allocate exactly what we need. + if (nbraces > 0) { - // FIXME: is there a smarter way? - tmp.insert(tmp.begin(), brace); - brace = s.braces_[brace]; + std::vector tmp(nbraces, 0); + for (int brace = n.second; brace >= 0; brace = s.braces_[brace]) + tmp[--nbraces] = brace; + res.emplace_back(n.first, std::move(tmp)); + } + else + { + res.emplace_back(n.first, std::vector{}); } - res.emplace_back(n.first, std::move(tmp)); } std::sort(res.begin(), res.end(), compare()); return res; From daf797b9d48936b6a6ebc119bad0210dcc5e232f Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Wed, 4 Jan 2023 15:11:21 +0100 Subject: [PATCH 216/337] fix merging of initial states in state-based automata Fixes #522 reported by Raven Beutner. * spot/parseaut/parseaut.yy: Make sure all edges leaving the initial state have the same color. * THANKS: Add Raven. * NEWS: Mention the bug. * tests/core/522.test: New file. * tests/Makefile.am: Add it. --- NEWS | 6 ++++++ THANKS | 1 + spot/parseaut/parseaut.yy | 33 ++++++++++++++++++++++++------ tests/Makefile.am | 3 ++- tests/core/522.test | 43 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 79 insertions(+), 7 deletions(-) create mode 100755 tests/core/522.test diff --git a/NEWS b/NEWS index 384ddc8bc..3a950996a 100644 --- a/NEWS +++ b/NEWS @@ -13,6 +13,12 @@ New in spot 2.11.3.dev (not yet released) - spot.acd() no longer depends on jQuery for interactivity. + Bug fixes: + + - When merging initial states from state-based automata with + multiple initial states (because Spot supports only one), the HOA + parser could break state-based acceptance. (Issue #522.) + New in spot 2.11.3 (2022-12-09) Bug fixes: diff --git a/THANKS b/THANKS index 356d187a1..93155f9d1 100644 --- a/THANKS +++ b/THANKS @@ -48,6 +48,7 @@ Nikos Gorogiannis Ondřej Lengál Paul Guénézan Pierre Ganty +Raven Beutner Reuben Rowe Roei Nahum Rüdiger Ehlers diff --git a/spot/parseaut/parseaut.yy b/spot/parseaut/parseaut.yy index 4d96b8c1c..7d5fac361 100644 --- a/spot/parseaut/parseaut.yy +++ b/spot/parseaut/parseaut.yy @@ -1,5 +1,5 @@ /* -*- coding: utf-8 -*- -** Copyright (C) 2014-2022 Laboratoire de Recherche et Développement +** Copyright (C) 2014-2023 Laboratoire de Recherche et Développement ** de l'Epita (LRDE). ** ** This file is part of Spot, a model checking library. @@ -2610,7 +2610,7 @@ static void fix_initial_state(result_& r) start.resize(std::distance(start.begin(), res)); assert(start.size() >= 1); - if (start.size() == 1) + if (start.size() == 1) { if (r.opts.want_kripke) r.h->ks->set_init_state(start.front().front()); @@ -2627,13 +2627,13 @@ static void fix_initial_state(result_& r) "a single initial state"); return; } + auto& aut = r.h->aut; // Fiddling with initial state may turn an incomplete automaton // into a complete one. - if (r.complete.is_false()) - r.complete = spot::trival::maybe(); + if (aut->prop_complete().is_false()) + aut->prop_complete(spot::trival::maybe()); // Multiple initial states. We might need to add a fake one, // unless one of the actual initial state has no incoming edge. - auto& aut = r.h->aut; std::vector has_incoming(aut->num_states(), 0); for (auto& t: aut->edges()) for (unsigned ud: aut->univ_dests(t)) @@ -2672,6 +2672,9 @@ static void fix_initial_state(result_& r) { unsigned p = pp.front(); if (p != init) + // FIXME: If p has no incoming we should be able to + // change the source of the edges of p instead of + // adding new edges. for (auto& t: aut->out(p)) aut->new_edge(init, t.dst, t.cond); } @@ -2694,6 +2697,24 @@ static void fix_initial_state(result_& r) } combiner.new_dests(init, comb_or); } + + // Merging two states may break state-based acceptance + // make sure all outgoing edges have the same color. + if (aut->prop_state_acc().is_true()) + { + bool first = true; + spot::acc_cond::mark_t prev; + for (auto& e: aut->out(init)) + if (first) + { + first = false; + prev = e.acc; + } + else if (e.acc != prev) + { + e.acc = prev; + } + } } } @@ -2871,8 +2892,8 @@ namespace spot r.aut_or_ks->set_named_prop("aliases", p); } fix_acceptance(r); + fix_properties(r); // before fix_initial_state fix_initial_state(r); - fix_properties(r); if (r.h->aut && !r.h->aut->is_existential()) r.h->aut->merge_univ_dests(); return r.h; diff --git a/tests/Makefile.am b/tests/Makefile.am index 0810df809..3bd43d5f4 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -1,5 +1,5 @@ ## -*- coding: utf-8 -*- -## Copyright (C) 2009-2022 Laboratoire de Recherche et Développement +## Copyright (C) 2009-2023 Laboratoire de Recherche et Développement ## de l'Epita (LRDE). ## Copyright (C) 2003-2006 Laboratoire d'Informatique de Paris 6 ## (LIP6), département Systèmes Répartis Coopératifs (SRC), Université @@ -223,6 +223,7 @@ TESTS_misc = \ TESTS_twa = \ core/385.test \ core/521.test \ + core/522.test \ core/acc.test \ core/acc2.test \ core/bdddict.test \ diff --git a/tests/core/522.test b/tests/core/522.test new file mode 100755 index 000000000..5fe6ba945 --- /dev/null +++ b/tests/core/522.test @@ -0,0 +1,43 @@ +#!/bin/sh +# -*- coding: utf-8 -*- +# Copyright (C) 2023 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 . + +. ./defs + +set -e + +# For issue #522. + +cat >552.hoa < out.hoa +grep 'States: 7' out.hoa From 396009c014beb3437508dd1ec1cc2a625c68081f Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Thu, 5 Jan 2023 15:09:26 +0100 Subject: [PATCH 217/337] parseaut: better merge of multiple initial states If an initial states without incoming transition has to be merged into another one, its outgoing edges can be reused by just changing their source. * spot/parseaut/parseaut.yy (fix_initial_state): Implement this here. * tests/core/522.test: Add more tests. * tests/core/readsave.test: Adjust one expected output. * doc/org/hoa.org: Mention the completeness change. * NEWS: Mention the new feature. --- NEWS | 5 +++++ doc/org/hoa.org | 5 +++-- spot/parseaut/parseaut.yy | 30 +++++++++++++++++++++++------ tests/core/522.test | 40 ++++++++++++++++++++++++++++++++++++--- tests/core/readsave.test | 7 +++---- 5 files changed, 72 insertions(+), 15 deletions(-) diff --git a/NEWS b/NEWS index 3a950996a..d6f6a702b 100644 --- a/NEWS +++ b/NEWS @@ -9,6 +9,11 @@ New in spot 2.11.3.dev (not yet released) - b:b[*i..j] = b[*max(i,1)..j] - b[*i..j]:b[*k..l] = b[*max(i,1)+max(k,1)-1, j+l-1] + - The HOA parser is a bit smarter when merging multiple initial + states into a single initial state (Spot's automaton class + supports only one): it now reuse the edges leaving initial states + without incoming transitions. + Python: - spot.acd() no longer depends on jQuery for interactivity. diff --git a/doc/org/hoa.org b/doc/org/hoa.org index 26969e4ed..6994abdc5 100644 --- a/doc/org/hoa.org +++ b/doc/org/hoa.org @@ -66,7 +66,7 @@ the HOA format, the output may not be exactly the same as the input. sets. This hard-coded limit can be augmented at configure time - using option `--enable-max-accsets=N`, but doing so will consume + using option =--enable-max-accsets=N=, but doing so will consume more memory and time. - Multiple (or missing) initial states are emulated. @@ -76,7 +76,8 @@ the HOA format, the output may not be exactly the same as the input. is transformed into an equivalent TωA by merging the initial states into a single one. The merged state can either be one of the original initial states (if one of those has no incoming edge) or a - new state introduced for that purpose. + new state introduced for that purpose. This "conversion" may change + the completeness property of the automaton. Similarly, when an automaton with no initial state is loaded (this includes the case where the automaton has no state), a disconnected diff --git a/spot/parseaut/parseaut.yy b/spot/parseaut/parseaut.yy index 7d5fac361..5b8792e96 100644 --- a/spot/parseaut/parseaut.yy +++ b/spot/parseaut/parseaut.yy @@ -2671,12 +2671,30 @@ static void fix_initial_state(result_& r) for (auto& pp: start) { unsigned p = pp.front(); - if (p != init) - // FIXME: If p has no incoming we should be able to - // change the source of the edges of p instead of - // adding new edges. - for (auto& t: aut->out(p)) - aut->new_edge(init, t.dst, t.cond); + if (p == init) + continue; + if (!has_incoming[p]) + { + // If p has no incoming edge, we can simply take + // out its outgoing edges and "re-source" them on init. + // This will avoid creating new edges. + for (auto& t: aut->out(p)) + t.src = init; + auto& gr = aut->get_graph(); + auto& ps = gr.state_storage(p); + auto& is = gr.state_storage(init); + gr.edge_storage(is.succ_tail).next_succ = ps.succ; + is.succ_tail = ps.succ_tail; + ps.succ = ps.succ_tail = 0; + // we just created a state without successors + aut->prop_complete(false); + } + else + { + // duplicate all edges + for (auto& t: aut->out(p)) + aut->new_edge(init, t.dst, t.cond); + } } } else diff --git a/tests/core/522.test b/tests/core/522.test index 5fe6ba945..3f1596514 100755 --- a/tests/core/522.test +++ b/tests/core/522.test @@ -38,6 +38,40 @@ State: 0 {0} [t] 2 [t] 3 State: 1 {0} [t] 4 [t] 5 State: 2 State: 5 {1} [t] 6 [t] 7 State: 6 [t] 6 [t] 7 State: 7 [t] 6 [t] 7 --END-- EOF -# This command, even without --remove-dead, used to break during print_hoa() -autfilt --remove-dead 552.hoa > out.hoa -grep 'States: 7' out.hoa +# This command used to break during print_hoa() +autfilt 552.hoa > out.hoa +test "8 1 16 0" = "`autfilt --stats='%[a]s %[u]s %[a]e %[u]e' out.hoa`" + +cat >552loop1.hoa < out.hoa +test "8 0 20 0" = "`autfilt --stats='%[a]s %[u]s %[a]e %[u]e' out.hoa`" + +cat >552loop2.hoa < out.hoa +test "9 0 24 0" = "`autfilt --stats='%[a]s %[u]s %[a]e %[u]e' out.hoa`" diff --git a/tests/core/readsave.test b/tests/core/readsave.test index 3780b4766..cf6f43b89 100755 --- a/tests/core/readsave.test +++ b/tests/core/readsave.test @@ -1,6 +1,6 @@ #!/bin/sh # -*- coding: utf-8 -*- -# Copyright (C) 2009, 2010, 2012, 2014-2022 Laboratoire de +# Copyright (C) 2009, 2010, 2012, 2014-2023 Laboratoire de # Recherche et Développement de l'Epita (LRDE). # Copyright (C) 2003, 2004 Laboratoire d'Informatique de Paris 6 (LIP6), # département Systèmes Répartis Coopératifs (SRC), Université Pierre @@ -346,9 +346,8 @@ digraph "" { 0 -> 0 [label="b", id="E1", tooltip="\\\\E\n#1"] 1 -> 1 [label="a", id="E2", tooltip="\\\\E\n#2"] 2 [label="s2"] - 2 -> 0 [label="b", id="E3", tooltip="\\\\E\n#3"] - 3 -> 1 [label="a", id="E4", tooltip="\\\\E\n#4"] - 3 -> 0 [label="b", id="E5", tooltip="\\\\E\n#5"] + 3 -> 1 [label="a", id="E3", tooltip="\\\\E\n#3"] + 3 -> 0 [label="b", id="E4", tooltip="\\\\E\n#4"] } EOF From 16ad7bdf77713e9a4b96bd2d9a37990b5612f26b Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Thu, 5 Jan 2023 17:47:46 +0100 Subject: [PATCH 218/337] * doc/org/spot.css: Do not define background twice. --- doc/org/spot.css | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/org/spot.css b/doc/org/spot.css index ca8b12395..569ca37a9 100644 --- a/doc/org/spot.css +++ b/doc/org/spot.css @@ -16,9 +16,9 @@ h1::before{content:"";position:absolute;z-index:-1;background-color:#ffe35e;left #table-of-contents #text-table-of-contents{text-align:left} #org-div-home-and-up{text-align:center;font-size:100%} .outline-2 h2{display:block;width:100%;position:relative} -.outline-2 h2::before{content:"";height:100%;width:calc(100% + 2em);position:absolute;z-index:-1;bottom:0em;left:-1em;background-color:#ffe35e;background:linear-gradient(45deg,#ffe35e 50%,transparent 75%);transform:skew(10deg);border-radius:5px;} +.outline-2 h2::before{content:"";height:100%;width:calc(100% + 2em);position:absolute;z-index:-1;bottom:0em;left:-1em;background:linear-gradient(45deg,#ffe35e 50%,transparent 75%);transform:skew(10deg);border-radius:5px;} .outline-3 h3{display:block;width:auto;position:relative} -.outline-3 h3::before{content:"";position:absolute;z-index:-1;width:calc(100% + 2em);height:100%;left:-1em;bottom:0em;;background-color:#ffe35e;background:linear-gradient(45deg,#ffe35e 25%,transparent 50%);transform:skew(10deg);border-radius:3px} +.outline-3 h3::before{content:"";position:absolute;z-index:-1;width:calc(100% + 2em);height:100%;left:-1em;bottom:0em;background:linear-gradient(45deg,#ffe35e 25%,transparent 50%);transform:skew(10deg);border-radius:3px} .outline-2 h2:hover::before,.outline-3 h3:hover::before{background-color:#ffe35e} pre{margin:1.2ex} pre.src{padding-top:8px;border-left-style:solid;border-color:#00adad;overflow:auto;margin-top:0;margin-bottom:0} From 2ba6fba29f14c591cc63ebb3b47d185704b3b647 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Thu, 5 Jan 2023 17:48:14 +0100 Subject: [PATCH 219/337] simplify several comparison operators * spot/twaalgos/dtbasat.cc, spot/twaalgos/dtwasat.cc, spot/twaalgos/simulation.cc: Simplify, as reported by sonarcloud. --- spot/twaalgos/dtbasat.cc | 8 ++------ spot/twaalgos/dtwasat.cc | 8 ++------ spot/twaalgos/simulation.cc | 10 ++-------- 3 files changed, 6 insertions(+), 20 deletions(-) diff --git a/spot/twaalgos/dtbasat.cc b/spot/twaalgos/dtbasat.cc index b2147ebb4..c4bf3d1bc 100644 --- a/spot/twaalgos/dtbasat.cc +++ b/spot/twaalgos/dtbasat.cc @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2013-2018, 2021, 2022 Laboratoire de Recherche et +// Copyright (C) 2013-2018, 2021-2023 Laboratoire de Recherche et // Développement de l'Epita. // // This file is part of Spot, a model checking library. @@ -77,11 +77,7 @@ namespace spot return true; if (this->src_ref > other.src_ref) return false; - if (this->dst_ref < other.dst_ref) - return true; - if (this->dst_ref > other.dst_ref) - return false; - return false; + return this->dst_ref < other.dst_ref; } }; diff --git a/spot/twaalgos/dtwasat.cc b/spot/twaalgos/dtwasat.cc index 25a299154..2ecf38fd1 100644 --- a/spot/twaalgos/dtwasat.cc +++ b/spot/twaalgos/dtwasat.cc @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2013-2022 Laboratoire de Recherche +// Copyright (C) 2013-2023 Laboratoire de Recherche // et Développement de l'Epita. // // This file is part of Spot, a model checking library. @@ -98,11 +98,7 @@ namespace spot return true; if (this->acc_ref > other.acc_ref) return false; - if (this->acc_cand < other.acc_cand) - return true; - if (this->acc_cand > other.acc_cand) - return false; - return false; + return this->acc_cand < other.acc_cand; } }; diff --git a/spot/twaalgos/simulation.cc b/spot/twaalgos/simulation.cc index ca8928888..ed53929b3 100644 --- a/spot/twaalgos/simulation.cc +++ b/spot/twaalgos/simulation.cc @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2012-2022 Laboratoire de Recherche et Développement +// Copyright (C) 2012-2023 Laboratoire de Recherche et Développement // de l'Epita (LRDE). // // This file is part of Spot, a model checking library. @@ -93,13 +93,7 @@ namespace spot return true; if (states > r.states) return false; - - if (edges < r.edges) - return true; - if (edges > r.edges) - return false; - - return false; + return edges < r.edges; } inline bool operator>(const automaton_size& r) From 716bb781eb57a4884eee0ea4d3a2637653a0063d Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Thu, 5 Jan 2023 17:49:00 +0100 Subject: [PATCH 220/337] * spot/twaalgos/game.cc: Fix incorrect std::forward. --- spot/twaalgos/game.cc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/spot/twaalgos/game.cc b/spot/twaalgos/game.cc index f5699bf49..df259b84a 100644 --- a/spot/twaalgos/game.cc +++ b/spot/twaalgos/game.cc @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2017-2018, 2020-2022 Laboratoire de Recherche et +// Copyright (C) 2017-2018, 2020-2023 Laboratoire de Recherche et // Développement de l'Epita (LRDE). // // This file is part of Spot, a model checking library. @@ -1034,7 +1034,7 @@ namespace spot ("set_state_players(): There must be as many owners as states"); arena->set_named_prop("state-player", - new region_t(std::forward(owners))); + new region_t(std::move(owners))); } void set_state_player(twa_graph_ptr arena, unsigned state, bool owner) @@ -1101,7 +1101,7 @@ namespace spot throw std::runtime_error("set_strategy(): strategies need to have " "the same size as the automaton."); arena->set_named_prop("strategy", - new strategy_t(std::forward(strat))); + new strategy_t(std::move(strat))); } void set_synthesis_outputs(const twa_graph_ptr& arena, const bdd& outs) @@ -1152,7 +1152,7 @@ namespace spot ("set_state_winners(): There must be as many winners as states"); arena->set_named_prop("state-winner", - new region_t(std::forward(winners))); + new region_t(std::move(winners))); } void set_state_winner(twa_graph_ptr arena, unsigned state, bool winner) From 05edab3f5ae20b0d2122d0e5a551ce62e22f0acc Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Thu, 5 Jan 2023 23:34:10 +0100 Subject: [PATCH 221/337] fix some code smells reported by sonarcloud * bench/dtgbasat/gen.py, bin/autcross.cc, bin/autfilt.cc, bin/common_aoutput.cc, bin/common_aoutput.hh: Various cleanups. --- bench/dtgbasat/gen.py | 6 ++-- bin/autcross.cc | 12 +++---- bin/autfilt.cc | 79 ++++++++++++++++++------------------------- bin/common_aoutput.cc | 12 +++---- bin/common_aoutput.hh | 8 ++--- 5 files changed, 51 insertions(+), 66 deletions(-) diff --git a/bench/dtgbasat/gen.py b/bench/dtgbasat/gen.py index e96bf2825..dabf77971 100755 --- a/bench/dtgbasat/gen.py +++ b/bench/dtgbasat/gen.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (C) 2016-2018 Laboratoire de Recherche et Développement de +# Copyright (C) 2016-2018, 2023 Laboratoire de Recherche et Développement de # l'Epita (LRDE). # # This file is part of Spot, a model checking library. @@ -55,12 +55,12 @@ class BenchConfig(object): if line[0] == '#' or line.isspace(): continue elif line[0:2] == "sh": - sh = re.search('sh (.+?)$', line).group(1) + sh = re.search('sh (.+)$', line).group(1) continue else: name = re.search('(.+?):', line).group(1) code = re.search(':(.+?)>', line).group(1) - xoptions = re.search('>(.+?)$', line).group(1) + xoptions = re.search('>(.+)$', line).group(1) b = Bench(name=name, code=code, xoptions=xoptions) self.l.append(b) self.sh.append(sh) diff --git a/bin/autcross.cc b/bin/autcross.cc index 24cd9bcd4..b3e504bb3 100644 --- a/bin/autcross.cc +++ b/bin/autcross.cc @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2017-2020, 2022 Laboratoire de Recherche et +// Copyright (C) 2017-2020, 2022-2023 Laboratoire de Recherche et // Développement de l'Epita (LRDE). // // This file is part of Spot, a model checking library. @@ -549,7 +549,7 @@ namespace { if (!quiet) std::cerr << "info: building " << autname(i, is_really_comp(i)) - << '*' << autname(j, true ^ is_really_comp(j)) + << '*' << autname(j, !is_really_comp(j)) << " requires more acceptance sets than supported\n"; return false; } @@ -557,14 +557,14 @@ namespace if (verbose) std::cerr << "info: check_empty " << autname(i, is_really_comp(i)) - << '*' << autname(j, true ^ is_really_comp(j)) << '\n'; + << '*' << autname(j, !is_really_comp(j)) << '\n'; auto w = aut_i->intersecting_word(aut_j); if (w) { std::ostream& err = global_error(); err << "error: " << autname(i, is_really_comp(i)) - << '*' << autname(j, true ^ is_really_comp(j)) + << '*' << autname(j, !is_really_comp(j)) << (" is nonempty; both automata accept the infinite word:\n" " "); example() << *w << '\n'; @@ -613,7 +613,7 @@ namespace return src.str(); }(); - input_statistics.push_back(in_statistics()); + input_statistics.emplace_back(in_statistics()); input_statistics[round_num].input_source = std::move(source); if (auto name = input->get_named_prop("automaton-name")) @@ -658,7 +658,7 @@ namespace problems += prob; } spot::cleanup_tmpfiles(); - output_statistics.push_back(std::move(stats)); + output_statistics.emplace_back(std::move(stats)); if (verbose) { diff --git a/bin/autfilt.cc b/bin/autfilt.cc index 7cff60e8b..b55d1bc9f 100644 --- a/bin/autfilt.cc +++ b/bin/autfilt.cc @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2013-2022 Laboratoire de Recherche et Développement +// Copyright (C) 2013-2023 Laboratoire de Recherche et Développement // de l'Epita (LRDE). // // This file is part of Spot, a model checking library. @@ -448,7 +448,7 @@ struct canon_aut std::vector edges; std::string acc; - canon_aut(const spot::const_twa_graph_ptr& aut) + explicit canon_aut(const spot::const_twa_graph_ptr& aut) : num_states(aut->num_states()) , edges(aut->edge_vector().begin() + 1, aut->edge_vector().end()) @@ -755,6 +755,22 @@ product_or(spot::twa_graph_ptr left, spot::twa_graph_ptr right) return spot::product_or(left, right); } +static spot::twa_graph_ptr +word_to_aut(const char* word, const char *argname) +{ + try + { + return spot::parse_word(word, opt->dict)->as_automaton(); + } + catch (const spot::parse_error& e) + { + error(2, 0, "failed to parse the argument of --%s:\n%s", + argname, e.what()); + } + SPOT_UNREACHABLE(); + return nullptr; +} + static int parse_opt(int key, char* arg, struct argp_state*) { @@ -776,17 +792,14 @@ parse_opt(int key, char* arg, struct argp_state*) opt_nth = parse_range(arg, 0, std::numeric_limits::max()); break; case 'u': - opt->uniq = std::unique_ptr(new std::set()); + opt->uniq = std::make_unique(); break; case 'v': opt_invert = true; break; case 'x': - { - const char* opt = extra_options.parse_options(arg); - if (opt) - error(2, 0, "failed to parse --options near '%s'", opt); - } + if (const char* opt = extra_options.parse_options(arg)) + error(2, 0, "failed to parse --options near '%s'", opt); break; case OPT_ALIASES: opt_aliases = XARGMATCH("--aliases", arg, aliases_args, aliases_types); @@ -802,16 +815,7 @@ parse_opt(int key, char* arg, struct argp_state*) opt_art_sccs_set = true; break; case OPT_ACCEPT_WORD: - try - { - opt->acc_words.push_back(spot::parse_word(arg, opt->dict) - ->as_automaton()); - } - catch (const spot::parse_error& e) - { - error(2, 0, "failed to parse the argument of --accept-word:\n%s", - e.what()); - } + opt->acc_words.emplace_back(word_to_aut(arg, "accept-word")); break; case OPT_ACCEPTANCE_IS: { @@ -964,16 +968,7 @@ parse_opt(int key, char* arg, struct argp_state*) "%d should be followed by a comma and WORD", res); arg = endptr + 1; } - try - { - opt->hl_words.emplace_back(spot::parse_word(arg, opt->dict) - ->as_automaton(), res); - } - catch (const spot::parse_error& e) - { - error(2, 0, "failed to parse the argument of --highlight-word:\n%s", - e.what()); - } + opt->hl_words.emplace_back(word_to_aut(arg, "highlight-word"), res); } break; case OPT_HIGHLIGHT_LANGUAGES: @@ -1157,16 +1152,7 @@ parse_opt(int key, char* arg, struct argp_state*) opt_art_sccs_set = true; break; case OPT_REJECT_WORD: - try - { - opt->rej_words.push_back(spot::parse_word(arg, opt->dict) - ->as_automaton()); - } - catch (const spot::parse_error& e) - { - error(2, 0, "failed to parse the argument of --reject-word:\n%s", - e.what()); - } + opt->rej_words.emplace_back(word_to_aut(arg, "reject-word")); break; case OPT_REM_AP: opt->rem_ap.add_ap(arg); @@ -1291,7 +1277,7 @@ namespace static bool match_acceptance(spot::twa_graph_ptr aut) { - auto& acc = aut->acc(); + const spot::acc_cond& acc = aut->acc(); switch (opt_acceptance_is) { case ACC_Any: @@ -1346,8 +1332,7 @@ namespace { bool max; bool odd; - bool is_p = acc.is_parity(max, odd, true); - if (!is_p) + if (!acc.is_parity(max, odd, true)) return false; switch (opt_acceptance_is) { @@ -1460,7 +1445,7 @@ namespace if (matched && opt_acceptance_is) matched = match_acceptance(aut); - if (matched && (opt_sccs_set | opt_art_sccs_set)) + if (matched && (opt_sccs_set || opt_art_sccs_set)) { spot::scc_info si(aut); unsigned n = si.scc_count(); @@ -1540,14 +1525,14 @@ namespace && spot::contains(aut, opt->equivalent_pos); if (matched && !opt->acc_words.empty()) - for (auto& word_aut: opt->acc_words) + for (const spot::twa_graph_ptr& word_aut: opt->acc_words) if (spot::product(aut, word_aut)->is_empty()) { matched = false; break; } if (matched && !opt->rej_words.empty()) - for (auto& word_aut: opt->rej_words) + for (const spot::twa_graph_ptr& word_aut: opt->rej_words) if (!spot::product(aut, word_aut)->is_empty()) { matched = false; @@ -1681,13 +1666,13 @@ namespace aut->accepting_run()->highlight(opt_highlight_accepting_run); if (!opt->hl_words.empty()) - for (auto& word_aut: opt->hl_words) + for (auto& [word_aut, color]: opt->hl_words) { if (aut->acc().uses_fin_acceptance()) error(2, 0, "--highlight-word does not yet work with Fin acceptance"); - if (auto run = spot::product(aut, word_aut.first)->accepting_run()) - run->project(aut)->highlight(word_aut.second); + if (auto run = spot::product(aut, word_aut)->accepting_run()) + run->project(aut)->highlight(color); } timer.stop(); diff --git a/bin/common_aoutput.cc b/bin/common_aoutput.cc index fcc79fc3c..60f83289e 100644 --- a/bin/common_aoutput.cc +++ b/bin/common_aoutput.cc @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2012-2022 Laboratoire de Recherche et Développement +// Copyright (C) 2012-2023 Laboratoire de Recherche et Développement // de l'Epita (LRDE). // // This file is part of Spot, a model checking library. @@ -453,7 +453,7 @@ hoa_stat_printer::print(const spot::const_parsed_aut_ptr& haut, const spot::const_twa_graph_ptr& aut, spot::formula f, const char* filename, int loc, - spot::process_timer& ptimer, + const spot::process_timer& ptimer, const char* csv_prefix, const char* csv_suffix) { timer_ = ptimer; @@ -633,10 +633,10 @@ automaton_printer::print(const spot::twa_graph_ptr& aut, outputnamer.print(haut, aut, f, filename, loc, ptimer, csv_prefix, csv_suffix); std::string fname = outputname.str(); - auto p = outputfiles.emplace(fname, nullptr); - if (p.second) - p.first->second.reset(new output_file(fname.c_str())); - out = &p.first->second->ostream(); + auto [it, b] = outputfiles.try_emplace(fname, nullptr); + if (b) + it->second.reset(new output_file(fname.c_str())); + out = &it->second->ostream(); } // Output it. diff --git a/bin/common_aoutput.hh b/bin/common_aoutput.hh index d33b687d2..f57beae84 100644 --- a/bin/common_aoutput.hh +++ b/bin/common_aoutput.hh @@ -1,6 +1,6 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2014-2018, 2020, 2022 Laboratoire de Recherche et -// Développement de l'Epita (LRDE). +// Copyright (C) 2014-2018, 2020, 2022, 2023 Laboratoire de Recherche +// et Développement de l'Epita (LRDE). // // This file is part of Spot, a model checking library. // @@ -155,7 +155,7 @@ public: print(const spot::const_parsed_aut_ptr& haut, const spot::const_twa_graph_ptr& aut, spot::formula f, - const char* filename, int loc, spot::process_timer& ptimer, + const char* filename, int loc, const spot::process_timer& ptimer, const char* csv_prefix, const char* csv_suffix); private: @@ -196,7 +196,7 @@ class automaton_printer std::map> outputfiles; public: - automaton_printer(stat_style input = no_input); + explicit automaton_printer(stat_style input = no_input); ~automaton_printer(); void From 96c3972c5c59d7ef7766919f92e872afcdf26772 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Thu, 5 Jan 2023 23:43:31 +0100 Subject: [PATCH 222/337] bin: detect overflows in conversion functions * bin/common_conv.cc (to_int, to_unsigned): Here. * bin/common_range.cc (parse_range): And there. * tests/core/ltlgrind.test, tests/core/genaut.test, tests/core/randaut.test: Add test cases. --- bin/common_conv.cc | 15 ++++++++++++--- bin/common_range.cc | 22 ++++++++++++++-------- tests/core/genaut.test | 9 ++++++--- tests/core/ltlgrind.test | 5 ++++- tests/core/randaut.test | 7 +++++-- 5 files changed, 41 insertions(+), 17 deletions(-) diff --git a/bin/common_conv.cc b/bin/common_conv.cc index e63969b16..02b1815fd 100644 --- a/bin/common_conv.cc +++ b/bin/common_conv.cc @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2015, 2018 Laboratoire de Recherche et Développement +// Copyright (C) 2015, 2018, 2023 Laboratoire de Recherche et Développement // de l'Epita (LRDE). // // This file is part of Spot, a model checking library. @@ -25,10 +25,14 @@ int to_int(const char* s, const char* where) { char* endptr; - int res = strtol(s, &endptr, 10); + long int lres = strtol(s, &endptr, 10); if (*endptr) error(2, 0, "failed to parse '%s' as an integer (in argument of %s).", s, where); + int res = lres; + if (res != lres) + error(2, 0, "value '%s' is too large for an int (in argument of %s).", + s, where); return res; } @@ -45,11 +49,16 @@ unsigned to_unsigned (const char *s, const char* where) { char* endptr; - unsigned res = strtoul(s, &endptr, 10); + unsigned long lres = strtoul(s, &endptr, 10); if (*endptr) error(2, 0, "failed to parse '%s' as an unsigned integer (in argument of %s).", s, where); + unsigned res = lres; + if (res != lres) + error(2, 0, + "value '%s' is too large for a unsigned int (in argument of %s).", + s, where); return res; } diff --git a/bin/common_range.cc b/bin/common_range.cc index 8909a26c0..9419cc389 100644 --- a/bin/common_range.cc +++ b/bin/common_range.cc @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2012, 2014, 2016 Laboratoire de Recherche et +// Copyright (C) 2012, 2014, 2016, 2023 Laboratoire de Recherche et // Développement de l'Epita (LRDE). // // This file is part of Spot, a model checking library. @@ -36,13 +36,16 @@ parse_range(const char* str, int missing_left, int missing_right) { range res; char* end; - res.min = strtol(str, &end, 10); + long lres = strtol(str, &end, 10); + res.min = lres; + if (res.min != lres) + error(2, 0, "start of range '%s' is too large for an int.", str); if (end == str) { // No leading number. It's OK as long as the string is not // empty. if (!*end) - error(1, 0, "invalid empty range"); + error(2, 0, "invalid empty range"); res.min = missing_left; } if (!*end) @@ -66,19 +69,22 @@ parse_range(const char* str, int missing_left, int missing_right) { // Parse the next integer. char* end2; - res.max = strtol(end, &end2, 10); + lres = strtol(end, &end2, 10); + res.max = lres; + if (res.max != lres) + error(2, 0, "end of range '%s' is too large for an int.", str); if (str == end2) - error(1, 0, "invalid range '%s' " + error(2, 0, "invalid range '%s' " "(should start with digits, dots, or colon)", str); if (end == end2) - error(1, 0, "invalid range '%s' (missing end?)", str); + error(2, 0, "invalid range '%s' (missing end?)", str); if (*end2) - error(1, 0, "invalid range '%s' (trailing garbage?)", str); + error(2, 0, "invalid range '%s' (trailing garbage?)", str); } } if (res.min < 0 || res.max < 0) - error(1, 0, "invalid range '%s': values must be positive", str); + error(2, 0, "invalid range '%s': values must be positive", str); return res; } diff --git a/tests/core/genaut.test b/tests/core/genaut.test index 5da9509ed..f364569e1 100644 --- a/tests/core/genaut.test +++ b/tests/core/genaut.test @@ -1,7 +1,7 @@ #!/bin/sh # -*- coding: utf-8 -*- -# Copyright (C) 2017, 2018, 2019, 2020 Laboratoire de Recherche et Développement -# de l'Epita (LRDE). +# Copyright (C) 2017-2020, 2023 Laboratoire de Recherche et +# Développement de l'Epita (LRDE). # # This file is part of Spot, a model checking library. # @@ -60,7 +60,10 @@ genaut --l-nba='1..3?' 2>err && exit 1 grep 'invalid range.*trailing garbage' err genaut --l-nba='1..' 2>err && exit 1 grep 'invalid range.*missing end' err - +genaut --l-nba='9999999999999999999999999..' 2>err && exit 1 +grep 'start.*too large' err +genaut --l-nba='1..9999999999999999999999999' 2>err && exit 1 +grep 'end.*too large' err # Tests for autfilt -N/--nth genaut --ks-nca=1..5 | autfilt -N 2..4 > range1.hoa diff --git a/tests/core/ltlgrind.test b/tests/core/ltlgrind.test index 292756bc6..09e75ee4e 100755 --- a/tests/core/ltlgrind.test +++ b/tests/core/ltlgrind.test @@ -1,6 +1,6 @@ #! /bin/sh # -*- coding: utf-8 -*- -# Copyright (C) 2014, 2015, 2019 Laboratoire de Recherche et Développement +# Copyright (C) 2014, 2015, 2019, 2023 Laboratoire de Recherche et Développement # de l'Epita (LRDE). # # This file is part of Spot, a model checking library. @@ -198,3 +198,6 @@ checkopt_noparse -F input/2 --format '%<,%f,%>,%F,%L' <err && exit 1 +grep 'too large' err diff --git a/tests/core/randaut.test b/tests/core/randaut.test index 7ff851646..50558e790 100755 --- a/tests/core/randaut.test +++ b/tests/core/randaut.test @@ -1,7 +1,7 @@ #!/bin/sh # -*- coding: utf-8 -*- -# Copyright (C) 2014-2018, 2020, 2022 Laboratoire de Recherche et -# Développement de l'Epita (LRDE). +# Copyright (C) 2014-2018, 2020, 2022, 2023 Laboratoire de Recherche +# et Développement de l'Epita (LRDE). # # This file is part of Spot, a model checking library. # @@ -29,6 +29,9 @@ grep "randaut: 3.1.*is not between 0 and 1 (in argument of -e" err randaut -n1a 3 2>err && exit 1 grep "randaut: failed to parse '1a' as an integer.* -n/--automata)" err +randaut -n99999999999999999999999999 3 2>err && exit 1 +grep "randaut:.*too large" err + randaut --spin -Q4 a b | ../ikwiad -H -XN - >out grep 'States: 4' out grep 'AP: 2' out From 09bbaa1e418f94d318d2593a984d3d5f3766442d Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Fri, 6 Jan 2023 11:55:34 +0100 Subject: [PATCH 223/337] more code smells * bin/common_file.cc, bin/common_file.hh, bin/common_finput.cc, bin/common_finput.hh, bin/common_output.cc, bin/common_setup.cc, bin/common_setup.hh, bin/common_trans.cc, bin/common_trans.hh, bin/dstar2tgba.cc, bin/genaut.cc, bin/genltl.cc, bin/ltl2tgba.cc, bin/ltl2tgta.cc, bin/ltlcross.cc, bin/ltldo.cc, bin/ltlfilt.cc, bin/ltlsynt.cc, bin/randltl.cc: Fix minor code issues reported by sonarcloud. --- bin/common_file.cc | 7 ++-- bin/common_file.hh | 13 +++---- bin/common_finput.cc | 10 ++---- bin/common_finput.hh | 16 +++++---- bin/common_output.cc | 20 +++++------ bin/common_setup.cc | 7 ++-- bin/common_setup.hh | 6 ++-- bin/common_trans.cc | 46 ++++++++++++------------ bin/common_trans.hh | 19 +++++----- bin/dstar2tgba.cc | 4 +-- bin/genaut.cc | 4 +-- bin/genltl.cc | 6 ++-- bin/ltl2tgba.cc | 8 ++--- bin/ltl2tgta.cc | 4 +-- bin/ltlcross.cc | 86 ++++++++++++++++---------------------------- bin/ltldo.cc | 14 +++----- bin/ltlfilt.cc | 20 +++++------ bin/ltlsynt.cc | 15 ++++---- bin/randltl.cc | 6 ++-- 19 files changed, 133 insertions(+), 178 deletions(-) diff --git a/bin/common_file.cc b/bin/common_file.cc index 005bb5479..4e56c6d54 100644 --- a/bin/common_file.cc +++ b/bin/common_file.cc @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2015, 2016, 2022 Laboratoire de Recherche et +// Copyright (C) 2015, 2016, 2022, 2023 Laboratoire de Recherche et // Développement de l'Epita (LRDE). // // This file is part of Spot, a model checking library. @@ -21,7 +21,6 @@ #include #include - output_file::output_file(const char* name, bool force_append) { std::ios_base::openmode mode = std::ios_base::trunc; @@ -39,10 +38,10 @@ output_file::output_file(const char* name, bool force_append) os_ = &std::cout; return; } - of_ = new std::ofstream(name, mode); + of_ = std::make_unique(name, mode); if (!*of_) error(2, errno, "cannot open '%s'", name); - os_ = of_; + os_ = of_.get(); } diff --git a/bin/common_file.hh b/bin/common_file.hh index b8f9842b8..b6aa0bec3 100644 --- a/bin/common_file.hh +++ b/bin/common_file.hh @@ -1,6 +1,6 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2015, 2016, 2022 Laboratoire de Recherche et Développement de -// l'Epita (LRDE). +// Copyright (C) 2015-2016, 2022-2023 Laboratoire de Recherche et +// Développement de l'Epita (LRDE). // // This file is part of Spot, a model checking library. // @@ -21,13 +21,13 @@ #include "common_sys.hh" #include +#include #include -#include class output_file { std::ostream* os_; - std::ofstream* of_ = nullptr; + std::unique_ptr of_; bool append_ = false; public: // Open a file for output. "-" is interpreted as stdout. @@ -37,11 +37,6 @@ public: void close(const std::string& name); - ~output_file() - { - delete of_; - } - bool append() const { return append_; diff --git a/bin/common_finput.cc b/bin/common_finput.cc index 80aca5df7..dbcdb3849 100644 --- a/bin/common_finput.cc +++ b/bin/common_finput.cc @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2012-2017, 2019, 2021, 2022 Laboratoire de Recherche +// Copyright (C) 2012-2017, 2019, 2021-2023 Laboratoire de Recherche // et Développement de l'Epita (LRDE). // // This file is part of Spot, a model checking library. @@ -96,12 +96,6 @@ parse_formula(const std::string& s) (s, spot::default_environment::instance(), false, lenient); } -job_processor::job_processor() - : abort_run(false), real_filename(nullptr), - col_to_read(0), prefix(nullptr), suffix(nullptr) -{ -} - job_processor::~job_processor() { if (real_filename) @@ -370,7 +364,7 @@ int job_processor::run() { int error = 0; - for (auto& j: jobs) + for (const auto& j: jobs) { switch (j.type) { diff --git a/bin/common_finput.hh b/bin/common_finput.hh index 2a5815fc3..9ecb5b025 100644 --- a/bin/common_finput.hh +++ b/bin/common_finput.hh @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2012-2017, 2022 Laboratoire de Recherche et +// Copyright (C) 2012-2017, 2022, 2023 Laboratoire de Recherche et // Développement de l'Epita (LRDE). // // This file is part of Spot, a model checking library. @@ -56,9 +56,11 @@ spot::parsed_formula parse_formula(const std::string& s); class job_processor { protected: - bool abort_run; // Set to true in process_formula() to abort run(). + bool abort_run = false; // Set to true in process_formula() to abort run(). public: - job_processor(); + job_processor() = default; + job_processor(const job_processor&) = delete; + job_processor& operator=(const job_processor&) = delete; virtual ~job_processor(); @@ -84,10 +86,10 @@ public: virtual int run(); - char* real_filename; - long int col_to_read; - char* prefix; - char* suffix; + char* real_filename = nullptr; + long int col_to_read = 0; + char* prefix = nullptr; + char* suffix = nullptr; }; // Report and error message or add a default job depending on whether diff --git a/bin/common_output.cc b/bin/common_output.cc index e9c61a513..93cb2dfaf 100644 --- a/bin/common_output.cc +++ b/bin/common_output.cc @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2012-2019 Laboratoire de Recherche et Développement +// Copyright (C) 2012-2019, 2023 Laboratoire de Recherche et Développement // de l'Epita (LRDE). // // This file is part of Spot, a model checking library. @@ -23,6 +23,7 @@ #include "common_setup.hh" #include #include +#include #include #include #include @@ -297,9 +298,9 @@ namespace }; } -static formula_printer* format = nullptr; +static std::unique_ptr format; static std::ostringstream outputname; -static formula_printer* outputnamer = nullptr; +static std::unique_ptr outputnamer; static std::map> outputfiles; int @@ -320,7 +321,7 @@ parse_opt_output(int key, char* arg, struct argp_state*) output_format = lbt_output; break; case 'o': - outputnamer = new formula_printer(outputname, arg); + outputnamer = std::make_unique(outputname, arg); break; case 'p': full_parenth = true; @@ -341,8 +342,7 @@ parse_opt_output(int key, char* arg, struct argp_state*) output_format = wring_output; break; case OPT_FORMAT: - delete format; - format = new formula_printer(std::cout, arg); + format = std::make_unique(std::cout, arg); break; default: return ARGP_ERR_UNKNOWN; @@ -417,10 +417,10 @@ output_formula_checked(spot::formula f, spot::process_timer* ptimer, formula_with_location fl = { f, filename, linenum, prefix, suffix }; outputnamer->print(fl, ptimer); std::string fname = outputname.str(); - auto p = outputfiles.emplace(fname, nullptr); - if (p.second) - p.first->second.reset(new output_file(fname.c_str())); - out = &p.first->second->ostream(); + auto [it, b] = outputfiles.try_emplace(fname, nullptr); + if (b) + it->second.reset(new output_file(fname.c_str())); + out = &it->second->ostream(); } output_formula(*out, f, ptimer, filename, linenum, prefix, suffix); *out << output_terminator; diff --git a/bin/common_setup.cc b/bin/common_setup.cc index 24cacae85..af033a47f 100644 --- a/bin/common_setup.cc +++ b/bin/common_setup.cc @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2012-2022 Laboratoire de Recherche et Développement +// Copyright (C) 2012-2023 Laboratoire de Recherche et Développement // de l'Epita (LRDE). // // This file is part of Spot, a model checking library. @@ -20,13 +20,14 @@ #include "common_setup.hh" #include "common_aoutput.hh" -#include "argp.h" -#include "closeout.h" +#include +#include #include #include #include #include #include +#include #include static void diff --git a/bin/common_setup.hh b/bin/common_setup.hh index e2fce84e0..94cd16f4f 100644 --- a/bin/common_setup.hh +++ b/bin/common_setup.hh @@ -1,6 +1,6 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2012, 2013, 2018, 2019 Laboratoire de Recherche et -// Développement de l'Epita (LRDE). +// Copyright (C) 2012-2013, 2018-2019, 2023 Laboratoire de Recherche +// et Développement de l'Epita (LRDE). // // This file is part of Spot, a model checking library. // @@ -34,5 +34,5 @@ int protected_main(char** progname, std::function mainfun); // Diagnose exceptions. [[noreturn]] void handle_any_exception(); -#define BEGIN_EXCEPTION_PROTECT try { (void)0; +#define BEGIN_EXCEPTION_PROTECT try { (void)0 #define END_EXCEPTION_PROTECT } catch (...) { handle_any_exception(); } diff --git a/bin/common_trans.cc b/bin/common_trans.cc index e34f3d77d..b93535173 100644 --- a/bin/common_trans.cc +++ b/bin/common_trans.cc @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2015-2022 Laboratoire de Recherche et Développement +// Copyright (C) 2015-2023 Laboratoire de Recherche et Développement // de l'Epita (LRDE). // // This file is part of Spot, a model checking library. @@ -53,7 +53,7 @@ struct shorthands_t }; #define SHORTHAND(PRE, POST) { PRE, std::regex("^" PRE), POST } -static shorthands_t shorthands_ltl[] = { +static const shorthands_t shorthands_ltl[] = { SHORTHAND("delag", " %f>%O"), SHORTHAND("lbt", " <%L>%O"), SHORTHAND("ltl2ba", " -f %s>%O"), @@ -73,7 +73,7 @@ static shorthands_t shorthands_ltl[] = { SHORTHAND("owl.* ltl-utilities\\b", " -f %f"), }; -static shorthands_t shorthands_autproc[] = { +static const shorthands_t shorthands_autproc[] = { SHORTHAND("autfilt", " %H>%O"), SHORTHAND("dra2dpa", " <%H>%O"), SHORTHAND("dstar2tgba", " %H>%O"), @@ -85,7 +85,7 @@ static shorthands_t shorthands_autproc[] = { " <%H>%O"), }; -static void show_shorthands(shorthands_t* begin, shorthands_t* end) +static void show_shorthands(const shorthands_t* begin, const shorthands_t* end) { std::cout << ("If a COMMANDFMT does not use any %-sequence, and starts with one of\n" @@ -100,7 +100,8 @@ static void show_shorthands(shorthands_t* begin, shorthands_t* end) } -tool_spec::tool_spec(const char* spec, shorthands_t* begin, shorthands_t* end, +tool_spec::tool_spec(const char* spec, + const shorthands_t* begin, const shorthands_t* end, bool is_ref) noexcept : spec(spec), cmd(spec), name(spec), reference(is_ref) { @@ -113,15 +114,15 @@ tool_spec::tool_spec(const char* spec, shorthands_t* begin, shorthands_t* end, { if (*pos == '{') ++count; - else if (*pos == '}') - if (!--count) - { - name = strndup(cmd + 1, pos - cmd - 1); - cmd = pos + 1; - while (*cmd == ' ' || *cmd == '\t') - ++cmd; - break; - } + else if (*pos == '}' && --count == 0) + { + name = strndup(cmd + 1, pos - cmd - 1); + cmd = pos + 1; + // skip leading whitespace + while (*cmd == ' ' || *cmd == '\t') + ++cmd; + break; + } } } // If there is no % in the string, look for a known @@ -147,11 +148,11 @@ tool_spec::tool_spec(const char* spec, shorthands_t* begin, shorthands_t* end, auto& p = *begin++; if (std::regex_search(basename, p.rprefix)) { - int m = strlen(p.suffix); - int q = strlen(cmd); + size_t m = strlen(p.suffix); + size_t q = strlen(cmd); char* tmp = static_cast(malloc(q + m + 1)); - strcpy(tmp, cmd); - strcpy(tmp + q, p.suffix); + memcpy(tmp, cmd, q); + memcpy(tmp + q, p.suffix, m + 1); cmd = tmp; allocated = true; break; @@ -490,9 +491,8 @@ read_stdout_of_command(char* const* args) if (close(cout_pipe[1]) < 0) error(2, errno, "closing write-side of pipe failed"); - std::string buffer(32, 0); std::string results; - int bytes_read; + ssize_t bytes_read; for (;;) { static char buffer[512]; @@ -612,7 +612,7 @@ get_arg(const char*& cmd) { const char* start = cmd; std::string arg; - while (int c = *cmd) + while (char c = *cmd) { switch (c) { @@ -642,14 +642,14 @@ get_arg(const char*& cmd) goto end_loop; case '\'': { - int d = 0; + char d = '\0'; while ((d = *++cmd)) { if (d == '\'') break; arg.push_back(d); } - if (d == 0) + if (d == '\0') return nullptr; } break; diff --git a/bin/common_trans.hh b/bin/common_trans.hh index 31c88c80c..0ebe59e8c 100644 --- a/bin/common_trans.hh +++ b/bin/common_trans.hh @@ -51,7 +51,8 @@ struct tool_spec // Whether the tool is a reference. bool reference; - tool_spec(const char* spec, shorthands_t* begin, shorthands_t* end, + tool_spec(const char* spec, + const shorthands_t* begin, const shorthands_t* end, bool is_ref) noexcept; tool_spec(const tool_spec& other) noexcept; tool_spec& operator=(const tool_spec& other); @@ -71,7 +72,7 @@ struct quoted_formula final: public spot::printable_value struct filed_formula final: public spot::printable { - filed_formula(const quoted_formula& ltl) : f_(ltl) + explicit filed_formula(const quoted_formula& ltl) : f_(ltl) { } @@ -89,9 +90,7 @@ struct filed_formula final: public spot::printable struct filed_automaton final: public spot::printable { - filed_automaton() - { - } + filed_automaton() = default; void print(std::ostream& os, const char* pos) const override; @@ -112,7 +111,7 @@ struct printable_result_filename final: unsigned translator_num; printable_result_filename(); - ~printable_result_filename(); + ~printable_result_filename() override; void reset(unsigned n); void cleanup(); @@ -126,7 +125,7 @@ protected: spot::bdd_dict_ptr dict; // Round-specific variables quoted_formula ltl_formula; - filed_formula filename_formula = ltl_formula; + filed_formula filename_formula{ltl_formula}; // Run-specific variables printable_result_filename output; public: @@ -151,9 +150,9 @@ protected: public: using spot::formater::has; - autproc_runner(// whether we accept the absence of output - // specifier - bool no_output_allowed = false); + explicit 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); }; diff --git a/bin/dstar2tgba.cc b/bin/dstar2tgba.cc index 5b60a0ecc..4b2ec9662 100644 --- a/bin/dstar2tgba.cc +++ b/bin/dstar2tgba.cc @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2013-2019, 2022 Laboratoire de Recherche et Développement +// Copyright (C) 2013-2019, 2022, 2023 Laboratoire de Recherche et Développement // de l'Epita (LRDE). // // This file is part of Spot, a model checking library. @@ -117,7 +117,7 @@ namespace spot::postprocessor& post; automaton_printer printer; - dstar_processor(spot::postprocessor& post) + explicit dstar_processor(spot::postprocessor& post) : hoa_processor(spot::make_bdd_dict()), post(post), printer(aut_input) { } diff --git a/bin/genaut.cc b/bin/genaut.cc index 26678c588..f8d6b93ff 100644 --- a/bin/genaut.cc +++ b/bin/genaut.cc @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2017-2019, 2022 Laboratoire de Recherche et +// Copyright (C) 2017-2019, 2022-2023 Laboratoire de Recherche et // Développement de l'Epita (LRDE). // // This file is part of Spot, a model checking library. @@ -128,7 +128,7 @@ output_pattern(gen::aut_pattern_id pattern, int n) static void run_jobs() { - for (auto& j: jobs) + for (const auto& j: jobs) { int inc = (j.range.max < j.range.min) ? -1 : 1; int n = j.range.min; diff --git a/bin/genltl.cc b/bin/genltl.cc index 96d8bd7d3..ef8049171 100644 --- a/bin/genltl.cc +++ b/bin/genltl.cc @@ -1,6 +1,6 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2012, 2013, 2015-2019, 2022 Laboratoire de Recherche et -// Développement de l'Epita (LRDE). +// Copyright (C) 2012, 2013, 2015-2019, 2022-2023 Laboratoire de +// Recherche et Développement de l'Epita (LRDE). // // This file is part of Spot, a model checking library. // @@ -317,7 +317,7 @@ output_pattern(gen::ltl_pattern_id pattern, int n, int n2) static void run_jobs() { - for (auto& j: jobs) + for (const auto& j: jobs) { int inc = (j.range.max < j.range.min) ? -1 : 1; int n = j.range.min; diff --git a/bin/ltl2tgba.cc b/bin/ltl2tgba.cc index d4fb2fc17..73a9a23c6 100644 --- a/bin/ltl2tgba.cc +++ b/bin/ltl2tgba.cc @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2012-2019, 2022 Laboratoire de Recherche et +// Copyright (C) 2012-2019, 2022-2023 Laboratoire de Recherche et // Développement de l'Epita (LRDE). // // This file is part of Spot, a model checking library. @@ -124,10 +124,10 @@ namespace { public: spot::translator& trans; - automaton_printer printer; + automaton_printer printer{ltl_input}; - trans_processor(spot::translator& trans) - : trans(trans), printer(ltl_input) + explicit trans_processor(spot::translator& trans) + : trans(trans) { } diff --git a/bin/ltl2tgta.cc b/bin/ltl2tgta.cc index ab925c7ac..60afcf9e8 100644 --- a/bin/ltl2tgta.cc +++ b/bin/ltl2tgta.cc @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2012-2020, 2022 Laboratoire de Recherche et +// Copyright (C) 2012-2020, 2022-2023 Laboratoire de Recherche et // Développement de l'Epita (LRDE). // // This file is part of Spot, a model checking library. @@ -168,7 +168,7 @@ namespace public: spot::translator& trans; - trans_processor(spot::translator& trans) + explicit trans_processor(spot::translator& trans) : trans(trans) { } diff --git a/bin/ltlcross.cc b/bin/ltlcross.cc index 0dfa09985..3219beb75 100644 --- a/bin/ltlcross.cc +++ b/bin/ltlcross.cc @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2012-2020, 2022 Laboratoire de Recherche et +// Copyright (C) 2012-2020, 2022, 2023 Laboratoire de Recherche et // Développement de l'Epita (LRDE). // // This file is part of Spot, a model checking library. @@ -264,55 +264,32 @@ end_error() struct statistics { - statistics() noexcept - : ok(false), - alternating(false), - status_str(nullptr), - status_code(0), - time(0), - states(0), - edges(0), - transitions(0), - acc(0), - scc(0), - nonacc_scc(0), - terminal_scc(0), - weak_scc(0), - strong_scc(0), - nondetstates(0), - nondeterministic(false), - terminal_aut(false), - weak_aut(false), - strong_aut(false) - { - } - // If OK is false, only the status_str, status_code, and time fields // should be valid. - bool ok; - bool alternating; - const char* status_str; - int status_code; - double time; - unsigned states; - unsigned edges; - unsigned long long transitions; - unsigned acc; - unsigned scc; - unsigned nonacc_scc; - unsigned terminal_scc; - unsigned weak_scc; - unsigned strong_scc; - unsigned nondetstates; - bool nondeterministic; - bool terminal_aut; - bool weak_aut; - bool strong_aut; + bool ok = false; + bool alternating = false; + const char* status_str = nullptr; + int status_code = 0; + double time = 0.0; + unsigned states = 0; + unsigned edges = 0; + unsigned long long transitions = 0; + unsigned acc = 0; + unsigned scc = 0; + unsigned nonacc_scc = 0; + unsigned terminal_scc = 0; + unsigned weak_scc = 0; + unsigned strong_scc = 0; + unsigned nondetstates = 0; + bool nondeterministic = false; + bool terminal_aut = false; + bool weak_aut = false; + bool strong_aut = false; std::vector product_states; std::vector product_transitions; std::vector product_scc; - bool ambiguous; - bool complete; + bool ambiguous = false; + bool complete = false; std::string hoa_str; static void @@ -581,7 +558,7 @@ namespace class xtranslator_runner final: public translator_runner { public: - xtranslator_runner(spot::bdd_dict_ptr dict) + explicit xtranslator_runner(spot::bdd_dict_ptr dict) : translator_runner(dict) { } @@ -1095,17 +1072,14 @@ namespace } // Make sure we do not translate the same formula twice. - if (!allow_dups) + if (!allow_dups && !unique_set.insert(f).second) { - if (!unique_set.insert(f).second) - { - if (!quiet) - std::cerr - << ("warning: This formula or its negation has already" - " been checked.\n Use --allow-dups if it " - "should not be ignored.\n\n"); - return 0; - } + if (!quiet) + std::cerr + << ("warning: This formula or its negation has already" + " been checked.\n Use --allow-dups if it " + "should not be ignored.\n\n"); + return 0; } int problems = 0; diff --git a/bin/ltldo.cc b/bin/ltldo.cc index ffbd4873e..6e7bf5ec7 100644 --- a/bin/ltldo.cc +++ b/bin/ltldo.cc @@ -1,6 +1,6 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2015-2020, 2022 Laboratoire de Recherche et Développement -// de l'Epita (LRDE). +// Copyright (C) 2015-2020, 2022-2023 Laboratoire de Recherche et +// Développement de l'Epita (LRDE). // // This file is part of Spot, a model checking library. // @@ -209,7 +209,7 @@ namespace class xtranslator_runner final: public translator_runner { public: - xtranslator_runner(spot::bdd_dict_ptr dict) + explicit xtranslator_runner(spot::bdd_dict_ptr dict) : translator_runner(dict, true) { } @@ -224,8 +224,6 @@ namespace format(command, tools[translator_num].cmd); std::string cmd = command.str(); - //std::cerr << "Running [" << l << translator_num << "]: " - // << cmd << std::endl; timer.start(); int es = exec_with_timeout(cmd.c_str()); timer.stop(); @@ -312,7 +310,7 @@ namespace spot::printable_value inputf; public: - processor(spot::postprocessor& post) + explicit processor(spot::postprocessor& post) : runner(dict), best_printer(best_stream, best_format), post(post) { printer.add_stat('T', &cmdname); @@ -323,9 +321,7 @@ namespace best_printer.declare('f', &inputf); } - ~processor() - { - } + ~processor() override = default; int process_string(const std::string& input, diff --git a/bin/ltlfilt.cc b/bin/ltlfilt.cc index c9064368d..81e895d42 100644 --- a/bin/ltlfilt.cc +++ b/bin/ltlfilt.cc @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2012-2022 Laboratoire de Recherche et Développement +// Copyright (C) 2012-2023 Laboratoire de Recherche et Développement // de l'Epita (LRDE). // // This file is part of Spot, a model checking library. @@ -586,7 +586,7 @@ namespace fset_t unique_set; spot::relabeling_map relmap; - ltl_processor(spot::tl_simplifier& simpl) + explicit ltl_processor(spot::tl_simplifier& simpl) : simpl(simpl) { } @@ -722,7 +722,7 @@ namespace matched &= !syntactic_si || f.is_syntactic_stutter_invariant(); if (matched && (ap_n.min > 0 || ap_n.max >= 0)) { - auto s = atomic_prop_collect(f); + spot::atomic_prop_set* s = atomic_prop_collect(f); int n = s->size(); delete s; matched &= (ap_n.min <= 0) || (n >= ap_n.min); @@ -761,7 +761,7 @@ namespace aut = ltl_to_tgba_fm(f, simpl.get_dict(), true); if (matched && !opt->acc_words.empty()) - for (auto& word_aut: opt->acc_words) + for (const spot::twa_graph_ptr& word_aut: opt->acc_words) if (spot::product(aut, word_aut)->is_empty()) { matched = false; @@ -769,7 +769,7 @@ namespace } if (matched && !opt->rej_words.empty()) - for (auto& word_aut: opt->rej_words) + for (const spot::twa_graph_ptr& word_aut: opt->rej_words) if (!spot::product(aut, word_aut)->is_empty()) { matched = false; @@ -843,12 +843,12 @@ namespace { // Sort the formulas alphabetically. std::map m; - for (auto& p: relmap) - m.emplace(str_psl(p.first), p.second); - for (auto& p: m) + for (const auto& [newformula, oldname]: relmap) + m.emplace(str_psl(newformula), oldname); + for (const auto& [newname, oldname]: m) stream_formula(opt->output_define->ostream() - << "#define " << p.first << " (", - p.second, filename, + << "#define " << newname << " (", + oldname, filename, std::to_string(linenum).c_str()) << ")\n"; } one_match = true; diff --git a/bin/ltlsynt.cc b/bin/ltlsynt.cc index aaea855a4..a2ec32cd1 100644 --- a/bin/ltlsynt.cc +++ b/bin/ltlsynt.cc @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2017-2022 Laboratoire de Recherche et Développement +// Copyright (C) 2017-2023 Laboratoire de Recherche et Développement // de l'Epita (LRDE). // // This file is part of Spot, a model checking library. @@ -152,7 +152,6 @@ static const struct argp_child children[] = { { &finput_argp_headless, 0, nullptr, 0 }, { &aoutput_argp, 0, nullptr, 0 }, - //{ &aoutput_o_format_argp, 0, nullptr, 0 }, { &misc_argp, 0, nullptr, 0 }, { nullptr, 0, nullptr, 0 } }; @@ -425,10 +424,6 @@ namespace auto sub_o = sub_outs_str.begin(); std::vector mealy_machines; - auto print_game = want_game ? - [](const spot::twa_graph_ptr& game)->void { dispatch_print_hoa(game); } - : [](const spot::twa_graph_ptr&)->void{}; - for (; sub_f != sub_form.end(); ++sub_f, ++sub_o) { spot::mealy_like m_like @@ -466,9 +461,11 @@ namespace assert((spptr->at(arena->get_init_state_number()) == false) && "Env needs first turn"); } - print_game(arena); if (want_game) - continue; + { + dispatch_print_hoa(arena); + continue; + } if (!spot::solve_game(arena, *gi)) { if (show_status) @@ -625,7 +622,7 @@ namespace } static void - split_aps(std::string arg, std::vector& where) + split_aps(const std::string& arg, std::vector& where) { std::istringstream aps(arg); std::string ap; diff --git a/bin/randltl.cc b/bin/randltl.cc index 986c437c1..749fcf373 100644 --- a/bin/randltl.cc +++ b/bin/randltl.cc @@ -1,6 +1,6 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2012-2016, 2018-2019, 2022 Laboratoire de Recherche -// et Développement de l'Epita (LRDE). +// Copyright (C) 2012-2016, 2018-2019, 2022, 2023 Laboratoire de +// Recherche et Développement de l'Epita (LRDE). // // This file is part of Spot, a model checking library. // @@ -65,7 +65,6 @@ enum { OPT_DUMP_PRIORITIES, OPT_DUPS, OPT_LTL_PRIORITIES, - OPT_PSL_PRIORITIES, OPT_SEED, OPT_SERE_PRIORITIES, OPT_TREE_SIZE, @@ -194,7 +193,6 @@ parse_opt(int key, char* arg, struct argp_state* as) case OPT_DUMP_PRIORITIES: opt_dump_priorities = true; break; - // case OPT_PSL_PRIORITIES: break; case OPT_SERE_PRIORITIES: opt_pS = arg; break; From a9c457f93fb38569849d8d1318c74dc16009cfcd Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Mon, 23 Jan 2023 11:59:49 +0100 Subject: [PATCH 224/337] dbranch: fix handling of states without successors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #524, reported by Rüdiger Ehlers. * spot/twaalgos/dbranch.cc: When merging an edge going to state without successors simply delete it. * bin/spot-x.cc: Typo in documentation. * tests/core/ltlcross.test: Add a test case. * NEWS: Mention the bug. --- NEWS | 4 ++++ bin/spot-x.cc | 4 ++-- spot/twaalgos/dbranch.cc | 39 +++++++++++++++++++++------------------ tests/core/ltlcross.test | 5 ++++- 4 files changed, 31 insertions(+), 21 deletions(-) diff --git a/NEWS b/NEWS index d6f6a702b..d25bf4ff4 100644 --- a/NEWS +++ b/NEWS @@ -24,6 +24,10 @@ New in spot 2.11.3.dev (not yet released) multiple initial states (because Spot supports only one), the HOA parser could break state-based acceptance. (Issue #522.) + - delay_branching_here(), a new optimization of Spot 2.11 had an + incorrectly handling of states without successors, causing some + segfaults. (Issue #524.) + New in spot 2.11.3 (2022-12-09) Bug fixes: diff --git a/bin/spot-x.cc b/bin/spot-x.cc index 1edb3f54e..964710dc1 100644 --- a/bin/spot-x.cc +++ b/bin/spot-x.cc @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2013-2022 Laboratoire de Recherche et Développement +// Copyright (C) 2013-2023 Laboratoire de Recherche et Développement // de l'Epita (LRDE). // // This file is part of Spot, a model checking library. @@ -53,7 +53,7 @@ implication-based simplifications are attempted. Defaults to 16.") }, { nullptr, 0, nullptr, 0, "Translation options:", 0 }, { DOC("ltl-split", "Set to 0 to disable the translation of automata \ as product or sum of subformulas.") }, - { DOC("branch-prop", "Set to 0 to disable branching-postponement \ + { DOC("branch-post", "Set to 0 to disable branching-postponement \ (done during translation, may create more states) and delayed-branching \ (almost similar, but done after translation to only remove states). \ Set to 1 to force branching-postponement, and to 2 \ diff --git a/spot/twaalgos/dbranch.cc b/spot/twaalgos/dbranch.cc index 465f8326e..19a0d9474 100644 --- a/spot/twaalgos/dbranch.cc +++ b/spot/twaalgos/dbranch.cc @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2022 Laboratoire de Recherche et Développement +// Copyright (C) 2022-2023 Laboratoire de Recherche et Développement // de l'Epita (LRDE). // // This file is part of Spot, a model checking library. @@ -118,27 +118,30 @@ namespace spot continue; } unsigned mergedst = it2->second; - // we have to merge canddst into mergedst. This is as - // simple as: + // we have to merge canddst into mergedst. + // This is as simple as: // 1) connecting their list of transitions - unsigned& mergedfirst = g.state_storage(mergedst).succ; - unsigned& mergedlast = g.state_storage(mergedst).succ_tail; - unsigned& candfirst = g.state_storage(canddst).succ; unsigned& candlast = g.state_storage(canddst).succ_tail; - if (mergedlast) - aut->edge_storage(mergedlast).next_succ = candfirst; - else // mergedst had now successor - mergedfirst = candfirst; - mergedlast = candlast; - // 2) updating the source of the merged transitions - for (unsigned e2 = candfirst; e2 != 0;) + if (candlast) { - auto& edge = aut->edge_storage(e2); - edge.src = mergedst; - e2 = edge.next_succ; + unsigned& mergedfirst = g.state_storage(mergedst).succ; + unsigned& mergedlast = g.state_storage(mergedst).succ_tail; + unsigned& candfirst = g.state_storage(canddst).succ; + if (mergedlast) + aut->edge_storage(mergedlast).next_succ = candfirst; + else // mergedst had no successor + mergedfirst = candfirst; + mergedlast = candlast; + // 2) updating the source of the merged transitions + for (unsigned e2 = candfirst; e2 != 0;) + { + auto& edge = aut->edge_storage(e2); + edge.src = mergedst; + e2 = edge.next_succ; + } + // 3) deleting the edge to canddst. + candfirst = candlast = 0; } - // 3) deleting the edge to canddst. - candfirst = candlast = 0; it.erase(); // 4) updating succ_cand succ_cand[mergedst] += succ_cand[canddst]; diff --git a/tests/core/ltlcross.test b/tests/core/ltlcross.test index 1a5806ba8..ebe20fb26 100755 --- a/tests/core/ltlcross.test +++ b/tests/core/ltlcross.test @@ -1,6 +1,6 @@ #!/bin/sh # -*- coding: utf-8 -*- -# Copyright (C) 2012-2014, 2016, 2019 Laboratoire de Recherche et +# Copyright (C) 2012-2014, 2016, 2019, 2023 Laboratoire de Recherche et # Développement de l'Epita (LRDE). # # This file is part of Spot, a model checking library. @@ -65,3 +65,6 @@ ltlcross -D \ # Spot 2.8. We use ltl2tgba twice so ltlcross build cross-products. ltlcross --verbose ltl2tgba ltl2tgba \ -f '(G(F((a1)&(X(X(b1))))))&(G(F((a2)&(X(X(b2))))))&(G(F((a3)&(X(X(b3))))))' + +# Issue #524. +ltlcross ltl2tgba -f '!(X(v3 | G!v5) | ((Xv5 & !(v5 & !X!v3)) U !v5))' From 3aba452b5b7fc8b8399c288257f5f96872409d61 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Mon, 23 Jan 2023 15:25:06 +0100 Subject: [PATCH 225/337] robin_hood: update to version version 3.11.5 * spot/priv/robin_hood.hh: Update. * spot/priv/Makefile.am: Patch ROBIN_HOOD_IS_TRIVIALLY_COPYABLE to work around an issue with clang on Arch linux. --- spot/priv/Makefile.am | 10 ++++++++-- spot/priv/robin_hood.hh | 43 +++++++++++++++++++++++++++-------------- 2 files changed, 37 insertions(+), 16 deletions(-) diff --git a/spot/priv/Makefile.am b/spot/priv/Makefile.am index b2c75ab7d..9a23caaa3 100644 --- a/spot/priv/Makefile.am +++ b/spot/priv/Makefile.am @@ -1,5 +1,5 @@ ## -*- coding: utf-8 -*- -## Copyright (C) 2013-2019, 2021-2022 Laboratoire de Recherche et +## Copyright (C) 2013-2019, 2021-2023 Laboratoire de Recherche et ## Développement de l'Epita (LRDE). ## ## This file is part of Spot, a model checking library. @@ -43,5 +43,11 @@ RH = $(GH)/robin-hood-hashing/master/src/include/robin_hood.h .PHONY: update update: wget $(RH) -O robin_hood.tmp || curl $(RH) -o robin_hood.tmp - sed 's/std::malloc/malloc/' robin_hood.tmp > $(srcdir)/robin_hood.hh +## Do not use std::malloc but malloc, because gnulib may replace it by +## rpl_malloc instead. Also disable to tests of __GNUC__ about +## ROBIN_HOOD_IS_TRIVIALLY_COPYABLE because (1) all versions of G++ we +## support have std::is_trivially_copyable, and (2) clang define +## __GNUC__ to some value that fail this test, and then warn that +## __has_trivial_copy is obsoleted. + sed 's/std::malloc/malloc/;/https:\/\/stackoverflow.com\/a\/31798726/{n;s/defined.*/false/}' robin_hood.tmp > $(srcdir)/robin_hood.hh rm -f robin_hood.tmp diff --git a/spot/priv/robin_hood.hh b/spot/priv/robin_hood.hh index 8c151d517..a4bc8beae 100644 --- a/spot/priv/robin_hood.hh +++ b/spot/priv/robin_hood.hh @@ -36,7 +36,7 @@ // see https://semver.org/ #define ROBIN_HOOD_VERSION_MAJOR 3 // for incompatible API changes #define ROBIN_HOOD_VERSION_MINOR 11 // for adding functionality in a backwards-compatible manner -#define ROBIN_HOOD_VERSION_PATCH 3 // for backwards-compatible bug fixes +#define ROBIN_HOOD_VERSION_PATCH 5 // for backwards-compatible bug fixes #include #include @@ -206,7 +206,7 @@ static Counts& counts() { // workaround missing "is_trivially_copyable" in g++ < 5.0 // See https://stackoverflow.com/a/31798726/48181 -#if defined(__GNUC__) && __GNUC__ < 5 +#if false # define ROBIN_HOOD_IS_TRIVIALLY_COPYABLE(...) __has_trivial_copy(__VA_ARGS__) #else # define ROBIN_HOOD_IS_TRIVIALLY_COPYABLE(...) std::is_trivially_copyable<__VA_ARGS__>::value @@ -1820,6 +1820,12 @@ public: InsertionState::key_found != idxAndState.second); } + template + iterator emplace_hint(const_iterator position, Args&&... args) { + (void)position; + return emplace(std::forward(args)...).first; + } + template std::pair try_emplace(const key_type& key, Args&&... args) { return try_emplace_impl(key, std::forward(args)...); @@ -1831,16 +1837,15 @@ public: } template - std::pair try_emplace(const_iterator hint, const key_type& key, - Args&&... args) { + iterator try_emplace(const_iterator hint, const key_type& key, Args&&... args) { (void)hint; - return try_emplace_impl(key, std::forward(args)...); + return try_emplace_impl(key, std::forward(args)...).first; } template - std::pair try_emplace(const_iterator hint, key_type&& key, Args&&... args) { + iterator try_emplace(const_iterator hint, key_type&& key, Args&&... args) { (void)hint; - return try_emplace_impl(std::move(key), std::forward(args)...); + return try_emplace_impl(std::move(key), std::forward(args)...).first; } template @@ -1854,16 +1859,15 @@ public: } template - std::pair insert_or_assign(const_iterator hint, const key_type& key, - Mapped&& obj) { + iterator insert_or_assign(const_iterator hint, const key_type& key, Mapped&& obj) { (void)hint; - return insertOrAssignImpl(key, std::forward(obj)); + return insertOrAssignImpl(key, std::forward(obj)).first; } template - std::pair insert_or_assign(const_iterator hint, key_type&& key, Mapped&& obj) { + iterator insert_or_assign(const_iterator hint, key_type&& key, Mapped&& obj) { (void)hint; - return insertOrAssignImpl(std::move(key), std::forward(obj)); + return insertOrAssignImpl(std::move(key), std::forward(obj)).first; } std::pair insert(const value_type& keyval) { @@ -1871,10 +1875,20 @@ public: return emplace(keyval); } + iterator insert(const_iterator hint, const value_type& keyval) { + (void)hint; + return emplace(keyval).first; + } + std::pair insert(value_type&& keyval) { return emplace(std::move(keyval)); } + iterator insert(const_iterator hint, value_type&& keyval) { + (void)hint; + return emplace(std::move(keyval)).first; + } + // Returns 1 if key is found, 0 otherwise. size_t count(const key_type& key) const { // NOLINT(modernize-use-nodiscard) ROBIN_HOOD_TRACE(this) @@ -2308,13 +2322,14 @@ private: auto const numElementsWithBuffer = calcNumElementsWithBuffer(max_elements); - // calloc also zeroes everything + // malloc & zero mInfo. Faster than calloc everything. auto const numBytesTotal = calcNumBytesTotal(numElementsWithBuffer); ROBIN_HOOD_LOG("std::calloc " << numBytesTotal << " = calcNumBytesTotal(" << numElementsWithBuffer << ")") mKeyVals = reinterpret_cast( - detail::assertNotNull(std::calloc(1, numBytesTotal))); + detail::assertNotNull(malloc(numBytesTotal))); mInfo = reinterpret_cast(mKeyVals + numElementsWithBuffer); + std::memset(mInfo, 0, numBytesTotal - numElementsWithBuffer * sizeof(Node)); // set sentinel mInfo[numElementsWithBuffer] = 1; From 60abfeb31fee32092205b5ae0b60c2877291d233 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Mon, 23 Jan 2023 16:07:49 +0100 Subject: [PATCH 226/337] bin: update copyright year and laboratory name * bin/common_setup.cc: Here. --- bin/common_setup.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/common_setup.cc b/bin/common_setup.cc index af033a47f..c59ec0695 100644 --- a/bin/common_setup.cc +++ b/bin/common_setup.cc @@ -36,7 +36,7 @@ display_version(FILE *stream, struct argp_state*) fputs(program_name, stream); fputs(" (" PACKAGE_NAME ") " PACKAGE_VERSION "\n\ \n\ -Copyright (C) 2022 Laboratoire de Recherche et Développement de l'Epita.\n\ +Copyright (C) 2023 Laboratoire de Recherche de l'Epita (LRE)\n\ License GPLv3+: \ GNU GPL version 3 or later .\n\ This is free software: you are free to change and redistribute it.\n\ From 5b0143eba61d000a683f825142d4b30c24e1717e Mon Sep 17 00:00:00 2001 From: Florian Renkin Date: Fri, 20 Jan 2023 15:57:46 +0100 Subject: [PATCH 227/337] ltlsynt: typo in doc * bin/ltlsynt.cc: here --- bin/ltlsynt.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bin/ltlsynt.cc b/bin/ltlsynt.cc index a2ec32cd1..35ac4194b 100644 --- a/bin/ltlsynt.cc +++ b/bin/ltlsynt.cc @@ -102,8 +102,8 @@ static const argp_option options[] = "whether to decompose the specification as multiple output-disjoint " "problems to solve independently (enabled by default)", 0 }, { "simplify", OPT_SIMPLIFY, "no|bisim|bwoa|sat|bisim-sat|bwoa-sat", 0, - "simplification to apply to the controler (no) nothing, " - "(bisim) bisimulation-based reduction, (bwoa) bissimulation-based " + "simplification to apply to the controller (no) nothing, " + "(bisim) bisimulation-based reduction, (bwoa) bisimulation-based " "reduction with output assignment, (sat) SAT-based minimization, " "(bisim-sat) SAT after bisim, (bwoa-sat) SAT after bwoa. Defaults " "to 'bwoa'.", 0 }, From e5150d03140de87ced36b3a2328b62f9d17477e0 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Tue, 24 Jan 2023 11:35:14 +0100 Subject: [PATCH 228/337] autfilt: allow --highlight-word to work on Fin acceptance Fixes #523. * bin/autfilt.cc: Remove the restriction. * tests/core/acc_word.test: Add test case. * NEWS: Mention the fix. --- NEWS | 4 ++++ bin/autfilt.cc | 9 ++------- tests/core/acc_word.test | 20 +++++++++++++------- 3 files changed, 19 insertions(+), 14 deletions(-) diff --git a/NEWS b/NEWS index d25bf4ff4..4d9d1def2 100644 --- a/NEWS +++ b/NEWS @@ -24,6 +24,10 @@ New in spot 2.11.3.dev (not yet released) multiple initial states (because Spot supports only one), the HOA parser could break state-based acceptance. (Issue #522.) + - autfilt --highlight-word refused to work on automata with Fin + acceptance for historical reason, but the cose is perfectly able + to handle this now. (Issue #523.) + - delay_branching_here(), a new optimization of Spot 2.11 had an incorrectly handling of states without successors, causing some segfaults. (Issue #524.) diff --git a/bin/autfilt.cc b/bin/autfilt.cc index b55d1bc9f..4487fad8b 100644 --- a/bin/autfilt.cc +++ b/bin/autfilt.cc @@ -1667,13 +1667,8 @@ namespace if (!opt->hl_words.empty()) for (auto& [word_aut, color]: opt->hl_words) - { - if (aut->acc().uses_fin_acceptance()) - error(2, 0, - "--highlight-word does not yet work with Fin acceptance"); - if (auto run = spot::product(aut, word_aut)->accepting_run()) - run->project(aut)->highlight(color); - } + if (auto run = spot::product(aut, word_aut)->accepting_run()) + run->project(aut)->highlight(color); timer.stop(); if (opt->uniq) diff --git a/tests/core/acc_word.test b/tests/core/acc_word.test index 53ce4b98e..5f3b6880b 100644 --- a/tests/core/acc_word.test +++ b/tests/core/acc_word.test @@ -1,7 +1,7 @@ #!/bin/sh # -*- coding: utf-8 -*- -# Copyright (C) 2016, 2017, 2018, 2019 Laboratoire de Recherche et Développement -# de l'Epita (LRDE). +# Copyright (C) 2016-2019, 2023 Laboratoire de Recherche +# et Développement de l'Epita (LRDE). # # This file is part of Spot, a model checking library. # @@ -91,6 +91,15 @@ State: 1 EOF diff expected out +ltl2tgba -G '(GF(a & X!a) -> GF(b & XXb)) & GFc' > aut.hoa +word='!a&!c;cycle{!a&b&!c;!a&c;!a&b&c}' +autfilt -H1.1 aut.hoa --highlight-word="$word" > out.hoa +grep spot.highlight.edges out.hoa >out.edges +cat >expected <stderr && exit 1 -test $? -eq 2 -grep 'highlight-word.*Fin' stderr - +# highlight-word used not to work with Fin acceptance, but it's ok now +ltl2tgba -G -D 'FGa' | autfilt --highlight-word='cycle{a}' ltlfilt -f 'GFa' --accept-word 'cycle{!a}' && exit 1 ltlfilt -f 'GF!a' --accept-word 'cycle{!a}' From 121d5e5524d9fb0fe7d3555172613c7f1750265f Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Tue, 24 Jan 2023 15:48:06 +0100 Subject: [PATCH 229/337] bin: fix number conversion routines on 32bit On 32bit archetectures, long int = int the current check for detecting values that overflow int will fail. Conversion routings should check errno. * bin/common_conv.cc, bin/common_range.cc: Here. --- bin/common_conv.cc | 12 ++++++++---- bin/common_range.cc | 6 ++++-- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/bin/common_conv.cc b/bin/common_conv.cc index 02b1815fd..b23a67c51 100644 --- a/bin/common_conv.cc +++ b/bin/common_conv.cc @@ -25,12 +25,13 @@ int to_int(const char* s, const char* where) { char* endptr; + errno = 0; long int lres = strtol(s, &endptr, 10); if (*endptr) error(2, 0, "failed to parse '%s' as an integer (in argument of %s).", s, where); int res = lres; - if (res != lres) + if (res != lres || errno == ERANGE) error(2, 0, "value '%s' is too large for an int (in argument of %s).", s, where); return res; @@ -49,13 +50,14 @@ unsigned to_unsigned (const char *s, const char* where) { char* endptr; + errno = 0; unsigned long lres = strtoul(s, &endptr, 10); if (*endptr) error(2, 0, "failed to parse '%s' as an unsigned integer (in argument of %s).", s, where); unsigned res = lres; - if (res != lres) + if (res != lres || errno == ERANGE) error(2, 0, "value '%s' is too large for a unsigned int (in argument of %s).", s, where); @@ -66,8 +68,9 @@ float to_float(const char* s, const char* where) { char* endptr; + errno = 0; float res = strtof(s, &endptr); - if (*endptr) + if (*endptr || errno == ERANGE) error(2, 0, "failed to parse '%s' as a float (in argument of %s)", s, where); return res; @@ -89,8 +92,9 @@ to_longs(const char* arg) while (*arg) { char* endptr; + errno = 0; long value = strtol(arg, &endptr, 10); - if (endptr == arg) + if (endptr == arg || errno) error(2, 0, "failed to parse '%s' as an integer.", arg); res.push_back(value); while (*endptr == ' ' || *endptr == ',') diff --git a/bin/common_range.cc b/bin/common_range.cc index 9419cc389..98e568b41 100644 --- a/bin/common_range.cc +++ b/bin/common_range.cc @@ -36,9 +36,10 @@ parse_range(const char* str, int missing_left, int missing_right) { range res; char* end; + errno = 0; long lres = strtol(str, &end, 10); res.min = lres; - if (res.min != lres) + if (res.min != lres || errno == ERANGE) error(2, 0, "start of range '%s' is too large for an int.", str); if (end == str) { @@ -69,9 +70,10 @@ parse_range(const char* str, int missing_left, int missing_right) { // Parse the next integer. char* end2; + errno = 0; lres = strtol(end, &end2, 10); res.max = lres; - if (res.max != lres) + if (res.max != lres || errno == ERANGE) error(2, 0, "end of range '%s' is too large for an int.", str); if (str == end2) error(2, 0, "invalid range '%s' " From d16183c05345fc33e5b196635666af09489a0bab Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Tue, 24 Jan 2023 15:54:39 +0100 Subject: [PATCH 230/337] * .gitlab-ci.yml: Use pipeline id to name volumes. --- .gitlab-ci.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index a2006ee7f..348bacba1 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -276,7 +276,7 @@ debpkg-stable: - stable script: - docker pull gitlab-registry.lre.epita.fr/spot/buildenv/debian:stable - - vol=spot-stable-$CI_COMMIT_SHA + - vol=spot-stable-$CI_COMMIT_SHA-$CI_PIPELINE_ID - docker volume create $vol - exitcode=0 - docker run -v $vol:/build/result --name helper-$vol gitlab-registry.lre.epita.fr/spot/buildenv/debian:stable ./build-spot.sh $CI_COMMIT_REF_NAME -j${NBPROC-1} || exitcode=$? @@ -304,7 +304,7 @@ debpkg-stable-i386: needs: ["debpkg-stable"] script: - docker pull gitlab-registry.lre.epita.fr/spot/buildenv/debian-i386:stable - - vol=spot-stable-$CI_COMMIT_SHA + - vol=spot-stable-$CI_COMMIT_SHA-$CI_PIPELINE_ID - docker volume create $vol - exitcode=0 - docker create -v $vol:/build/result --name helper-$vol gitlab-registry.lre.epita.fr/spot/buildenv/debian-i386:stable ./bin-spot.sh -j${NBPROC-1} || exitcode=$? @@ -331,7 +331,7 @@ debpkg-unstable: - next script: - docker pull gitlab-registry.lre.epita.fr/spot/buildenv/debian - - vol=spot-unstable-$CI_COMMIT_SHA + - vol=spot-unstable-$CI_COMMIT_SHA-$CI_PIPELINE_ID - docker volume create $vol - exitcode=0 - docker run -v $vol:/build/result --name helper-$vol gitlab-registry.lre.epita.fr/spot/buildenv/debian ./build-spot.sh $CI_COMMIT_REF_NAME -j${NBPROC-1} || exitcode=$? @@ -357,7 +357,7 @@ debpkg-unstable-i386: needs: ["debpkg-unstable"] script: - docker pull gitlab-registry.lre.epita.fr/spot/buildenv/debian-i386 - - vol=spot-unstable-$CI_COMMIT_SHA + - vol=spot-unstable-$CI_COMMIT_SHA-$CI_PIPELINE_ID - docker volume create $vol - exitcode=0 - docker create -v $vol:/build/result --name helper-$vol gitlab-registry.lre.epita.fr/spot/buildenv/debian-i386 ./bin-spot.sh -j${NBPROC-1} || exitcode=$? From bdaa31ef2158d914d5bd21f22c2f1ef9b8665382 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Mon, 30 Jan 2023 17:51:48 +0100 Subject: [PATCH 231/337] work around gcc-snapshot warnings about dangling references * spot/twaalgos/game.hh, spot/twaalgos/game.cc (get_state_players, get_strategy, get_state_winners): Take argument by reference, not copy. * spot/twaalgos/synthesis.cc, spot/twaalgos/mealy_machine.cc: Replace auto by actual type for readability. --- spot/twaalgos/game.cc | 17 ++++++++++++++--- spot/twaalgos/game.hh | 12 ++++++++---- spot/twaalgos/mealy_machine.cc | 8 ++++---- spot/twaalgos/synthesis.cc | 12 ++++++------ 4 files changed, 32 insertions(+), 17 deletions(-) diff --git a/spot/twaalgos/game.cc b/spot/twaalgos/game.cc index df259b84a..17f94a7e4 100644 --- a/spot/twaalgos/game.cc +++ b/spot/twaalgos/game.cc @@ -1056,7 +1056,18 @@ namespace spot (*owners)[state] = owner; } - const region_t& get_state_players(const_twa_graph_ptr arena) + const region_t& get_state_players(const const_twa_graph_ptr& arena) + { + region_t *owners = arena->get_named_prop + ("state-player"); + if (!owners) + throw std::runtime_error + ("get_state_players(): state-player property not defined, not a game?"); + + return *owners; + } + + const region_t& get_state_players(twa_graph_ptr& arena) { region_t *owners = arena->get_named_prop ("state-player"); @@ -1081,7 +1092,7 @@ namespace spot } - const strategy_t& get_strategy(const_twa_graph_ptr arena) + const strategy_t& get_strategy(const const_twa_graph_ptr& arena) { auto strat_ptr = arena->get_named_prop("strategy"); if (!strat_ptr) @@ -1174,7 +1185,7 @@ namespace spot (*winners)[state] = winner; } - const region_t& get_state_winners(const_twa_graph_ptr arena) + const region_t& get_state_winners(const const_twa_graph_ptr& arena) { region_t *winners = arena->get_named_prop("state-winner"); if (!winners) diff --git a/spot/twaalgos/game.hh b/spot/twaalgos/game.hh index df5d27439..dbaccce75 100644 --- a/spot/twaalgos/game.hh +++ b/spot/twaalgos/game.hh @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2017-2022 Laboratoire de Recherche et Développement +// Copyright (C) 2017-2023 Laboratoire de Recherche et Développement // de l'Epita (LRDE). // // This file is part of Spot, a model checking library. @@ -163,14 +163,18 @@ namespace spot /// \ingroup games /// \brief Get the owner of all states + ///@{ SPOT_API - const region_t& get_state_players(const_twa_graph_ptr arena); + const region_t& get_state_players(const const_twa_graph_ptr& arena); + SPOT_API + const region_t& get_state_players(twa_graph_ptr& arena); + ///@} /// \ingroup games /// \brief Get or set the strategy /// @{ SPOT_API - const strategy_t& get_strategy(const_twa_graph_ptr arena); + const strategy_t& get_strategy(const const_twa_graph_ptr& arena); SPOT_API void set_strategy(twa_graph_ptr arena, const strategy_t& strat); SPOT_API @@ -214,5 +218,5 @@ namespace spot /// \ingroup games /// \brief Get the winner of all states SPOT_API - const region_t& get_state_winners(const_twa_graph_ptr arena); + const region_t& get_state_winners(const const_twa_graph_ptr& arena); } diff --git a/spot/twaalgos/mealy_machine.cc b/spot/twaalgos/mealy_machine.cc index e2b1523de..25bab05a9 100644 --- a/spot/twaalgos/mealy_machine.cc +++ b/spot/twaalgos/mealy_machine.cc @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2021, 2022 Laboratoire de Recherche et Développement +// Copyright (C) 2021, 2022, 2023 Laboratoire de Recherche et Développement // de l'Epita (LRDE). // // This file is part of Spot, a model checking library. @@ -3995,7 +3995,7 @@ namespace // 0 -> "Env" next is input props // 1 -> "Player" next is output prop - const auto& spref = get_state_players(mmw); + const region_t& spref = get_state_players(mmw); assert((spref.size() == mmw->num_states()) && "Inconsistent state players"); @@ -4146,9 +4146,9 @@ namespace spot const unsigned initl = left->get_init_state_number(); const unsigned initr = right->get_init_state_number(); - auto& spr = get_state_players(right); + const region_t& spr = get_state_players(right); #ifndef NDEBUG - auto& spl = get_state_players(left); + const region_t& spl = get_state_players(left); // todo auto check_out = [](const const_twa_graph_ptr& aut, const auto& sp) diff --git a/spot/twaalgos/synthesis.cc b/spot/twaalgos/synthesis.cc index 4e38efd5b..aef11d27b 100644 --- a/spot/twaalgos/synthesis.cc +++ b/spot/twaalgos/synthesis.cc @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2020-2022 Laboratoire de Recherche et +// Copyright (C) 2020-2023 Laboratoire de Recherche et // Développement de l'Epita (LRDE). // // This file is part of Spot, a model checking library. @@ -138,12 +138,12 @@ namespace{ // Note, this only deals with deterministic strategies // Note, assumes that env starts playing twa_graph_ptr - apply_strategy(const twa_graph_ptr& arena, + apply_strategy(const const_twa_graph_ptr& arena, bool unsplit, bool keep_acc) { - const auto& win = get_state_winners(arena); - const auto& strat = get_strategy(arena); - const auto& sp = get_state_players(arena); + const region_t& win = get_state_winners(arena); + const strategy_t& strat = get_strategy(arena); + const region_t& sp = get_state_players(arena); auto outs = get_synthesis_outputs(arena); if (!win[arena->get_init_state_number()]) @@ -1955,7 +1955,7 @@ namespace spot throw std::runtime_error("arena is null."); auto& arena_r = *arena; - const auto& sp = get_state_players(arena); + const region_t& sp = get_state_players(arena); bdd all_ap = arena->ap_vars(); if (std::find_if(arena->ap().cbegin(), arena->ap().cend(), From cab3ea7faf394d2bfaa1dc619b9dc48f026e9b6d Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Fri, 9 Dec 2022 12:04:15 +0100 Subject: [PATCH 232/337] acd: rewrite Python wrapper without jQuery * python/spot/__init__.py (acd): Rewrite javascript so that it does not use jQuery, to make it easier to use in jupyterlab, or with nbconvert. * tests/python/zlktree.ipynb: Adjust. * NEWS: Mention this. --- NEWS | 4 +- python/spot/__init__.py | 58 ++-- tests/python/zlktree.ipynb | 683 ++++++++++++++++++++++++++++--------- 3 files changed, 563 insertions(+), 182 deletions(-) diff --git a/NEWS b/NEWS index b1f7c2d79..0ac838737 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,8 @@ New in spot 2.11.3.dev (not yet released) - Nothing yet. + Python: + + - spot.acd() no longer depends on jQuery for interactivity. New in spot 2.11.3 (2022-12-09) diff --git a/python/spot/__init__.py b/python/spot/__init__.py index edbf4a4e6..ef4cd772e 100644 --- a/python/spot/__init__.py +++ b/python/spot/__init__.py @@ -502,51 +502,57 @@ class acd: .acdacc polygon{fill:green;} ''' js = ''' -function acd{num}_clear(){{ - $("#acd{num} .node,#acdaut{num} .node,#acdaut{num} .edge") - .removeClass("acdhigh acdbold acdacc acdrej"); +function acdremclasses(sel, classes) {{ +document.querySelectorAll(sel).forEach(n=>{{n.classList.remove(...classes)}});}} +function acdaddclasses(sel, classes) {{ +document.querySelectorAll(sel).forEach(n=>{{n.classList.add(...classes)}});}} +function acdonclick(sel, fn) {{ + document.querySelectorAll(sel).forEach(n=> + {{n.addEventListener("click", fn)}}); +}} +function acd{num}_clear() {{ + acdremclasses("#acd{num} .node,#acdaut{num} .node,#acdaut{num} .edge", + ["acdhigh", "acdbold", "acdacc", "acdrej"]); }}; function acd{num}_state(state){{ - acd{num}_clear(); - $("#acd{num} .acdS" + state).addClass("acdhigh acdbold"); - $("#acdaut{num} #S" + state).addClass("acdbold"); + acd{num}_clear(); + acdaddclasses("#acd{num} .acdS" + state, ["acdhigh", "acdbold"]); + acdaddclasses("#acdaut{num} #S" + state, ["acdbold"]); }}; function acd{num}_edge(edge){{ - acd{num}_clear(); - var theedge = $('#acdaut{num} #E' + edge) - var classList = theedge.attr('class').split(/\s+/); - $.each(classList, function(index, item) {{ - if (item.startsWith('acdN')) {{ - $("#acd{num} #" + item.substring(3)).addClass("acdhigh acdbold"); - }} - }}); - theedge.addClass("acdbold"); + acd{num}_clear(); + var theedge = document.querySelector('#acdaut{num} #E' + edge); + theedge.classList.forEach(function(item, index) {{ + if (item.startsWith('acdN')) {{ + acdaddclasses("#acd{num} #" + item.substring(3), ["acdhigh", "acdbold"]); + }} + }}); + theedge.classList.add("acdbold"); }}; function acd{num}_node(node, acc){{ acd{num}_clear(); - $("#acdaut{num} .acdN" + node).addClass(acc - ? "acdacc acdbold" - : "acdrej acdbold"); - $("#acd{num} #N" + node).addClass("acdbold acdhigh"); + acdaddclasses("#acdaut{num} .acdN" + node, + [acc ? "acdacc" : "acdrej", "acdbold"]); + acdaddclasses("#acd{num} #N" + node, ["acdbold", "acdhigh"]); }};'''.format(num=num) me = 0 for n in range(self.node_count()): for e in self.edges_of_node(n): me = max(e, me) - js += '$("#acdaut{num} #E{e}").addClass("acdN{n}");'\ + js += 'acdaddclasses("#acdaut{num} #E{e}", ["acdN{n}"]);\n'\ .format(num=num, e=e, n=n) for e in range(1, me + 1): - js += '$("#acdaut{num} #E{e}")'\ - '.click(function(){{acd{num}_edge({e});}});'\ + js += 'acdonclick("#acdaut{num} #E{e}",'\ + 'function(){{acd{num}_edge({e});}});\n'\ .format(num=num, e=e) for s in range(self.get_aut().num_states()): - js += '$("#acdaut{num} #S{s}")'\ - '.click(function(){{acd{num}_state({s});}});'\ + js += 'acdonclick("#acdaut{num} #S{s}",'\ + 'function(){{acd{num}_state({s});}});\n'\ .format(num=num, s=s) for n in range(self.node_count()): v = int(self.node_acceptance(n)) - js += '$("#acd{num} #N{n}")'\ - '.click(function(){{acd{num}_node({n}, {v});}});'\ + js += 'acdonclick("#acd{num} #N{n}",'\ + 'function(){{acd{num}_node({n}, {v});}});\n'\ .format(num=num, n=n, v=v) html = '
{}
{}
'\ .format(style, diff --git a/tests/python/zlktree.ipynb b/tests/python/zlktree.ipynb index ae44ad37d..c9eb3503d 100644 --- a/tests/python/zlktree.ipynb +++ b/tests/python/zlktree.ipynb @@ -216,7 +216,7 @@ "\n" ], "text/plain": [ - " >" + " >" ] }, "execution_count": 2, @@ -640,7 +640,7 @@ "\n" ], "text/plain": [ - " *' at 0x7f14701b7510> >" + " *' at 0x7f82c009d7a0> >" ] }, "execution_count": 10, @@ -1063,7 +1063,7 @@ "\n" ], "text/plain": [ - " *' at 0x7f1470220960> >" + " *' at 0x7f82c009c630> >" ] }, "execution_count": 11, @@ -1256,7 +1256,7 @@ "\n" ], "text/plain": [ - " *' at 0x7f14701b75d0> >" + " *' at 0x7f82c009c6c0> >" ] }, "execution_count": 13, @@ -1701,7 +1701,7 @@ "\n" ], "text/plain": [ - " *' at 0x7f1470142240> >" + " *' at 0x7f82c009c480> >" ] }, "execution_count": 14, @@ -2096,7 +2096,7 @@ "\n" ], "text/plain": [ - " >" + " >" ] }, "metadata": {}, @@ -2427,7 +2427,7 @@ "\n" ], "text/plain": [ - " >" + " >" ] }, "metadata": {}, @@ -2513,7 +2513,7 @@ "\n" ], "text/plain": [ - " >" + " >" ] }, "metadata": {}, @@ -2624,7 +2624,7 @@ "\n" ], "text/plain": [ - " >" + " >" ] }, "metadata": {}, @@ -2662,7 +2662,7 @@ "\n" ], "text/plain": [ - " >" + " >" ] }, "metadata": {}, @@ -2700,7 +2700,7 @@ "\n" ], "text/plain": [ - " >" + " >" ] }, "metadata": {}, @@ -2928,7 +2928,7 @@ "\n" ], "text/plain": [ - " >" + " >" ] }, "execution_count": 18, @@ -4064,36 +4064,159 @@ "\n", "\n", "" + " acdaddclasses(\"#acdaut0 .acdN\" + node,\n", + " [acc ? \"acdacc\" : \"acdrej\", \"acdbold\"]);\n", + " acdaddclasses(\"#acd0 #N\" + node, [\"acdbold\", \"acdhigh\"]);\n", + "};acdaddclasses(\"#acdaut0 #E9\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut0 #E10\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut0 #E11\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut0 #E12\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut0 #E13\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut0 #E14\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut0 #E15\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut0 #E16\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut0 #E21\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut0 #E22\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut0 #E23\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut0 #E24\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut0 #E25\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut0 #E26\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut0 #E27\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut0 #E28\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut0 #E33\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut0 #E34\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut0 #E35\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut0 #E36\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut0 #E31\", [\"acdN1\"]);\n", + "acdaddclasses(\"#acdaut0 #E32\", [\"acdN1\"]);\n", + "acdaddclasses(\"#acdaut0 #E39\", [\"acdN1\"]);\n", + "acdaddclasses(\"#acdaut0 #E40\", [\"acdN1\"]);\n", + "acdaddclasses(\"#acdaut0 #E5\", [\"acdN2\"]);\n", + "acdaddclasses(\"#acdaut0 #E7\", [\"acdN2\"]);\n", + "acdaddclasses(\"#acdaut0 #E17\", [\"acdN2\"]);\n", + "acdaddclasses(\"#acdaut0 #E1\", [\"acdN3\"]);\n", + "acdaddclasses(\"#acdaut0 #E10\", [\"acdN4\"]);\n", + "acdaddclasses(\"#acdaut0 #E12\", [\"acdN4\"]);\n", + "acdaddclasses(\"#acdaut0 #E13\", [\"acdN4\"]);\n", + "acdaddclasses(\"#acdaut0 #E15\", [\"acdN4\"]);\n", + "acdaddclasses(\"#acdaut0 #E21\", [\"acdN4\"]);\n", + "acdaddclasses(\"#acdaut0 #E22\", [\"acdN4\"]);\n", + "acdaddclasses(\"#acdaut0 #E23\", [\"acdN5\"]);\n", + "acdaddclasses(\"#acdaut0 #E24\", [\"acdN5\"]);\n", + "acdaddclasses(\"#acdaut0 #E34\", [\"acdN5\"]);\n", + "acdaddclasses(\"#acdaut0 #E36\", [\"acdN5\"]);\n", + "acdaddclasses(\"#acdaut0 #E14\", [\"acdN6\"]);\n", + "acdaddclasses(\"#acdaut0 #E15\", [\"acdN6\"]);\n", + "acdaddclasses(\"#acdaut0 #E22\", [\"acdN6\"]);\n", + "acdaddclasses(\"#acdaut0 #E23\", [\"acdN6\"]);\n", + "acdaddclasses(\"#acdaut0 #E14\", [\"acdN7\"]);\n", + "acdaddclasses(\"#acdaut0 #E16\", [\"acdN7\"]);\n", + "acdaddclasses(\"#acdaut0 #E26\", [\"acdN7\"]);\n", + "acdaddclasses(\"#acdaut0 #E9\", [\"acdN8\"]);\n", + "acdaddclasses(\"#acdaut0 #E40\", [\"acdN9\"]);\n", + "acdaddclasses(\"#acdaut0 #E5\", [\"acdN10\"]);\n", + "acdaddclasses(\"#acdaut0 #E23\", [\"acdN11\"]);\n", + "acdaddclasses(\"#acdaut0 #E14\", [\"acdN12\"]);\n", + "acdaddclasses(\"#acdaut0 #E23\", [\"acdN13\"]);\n", + "acdaddclasses(\"#acdaut0 #E14\", [\"acdN14\"]);\n", + "acdonclick(\"#acdaut0 #E1\",function(){acd0_edge(1);});\n", + "acdonclick(\"#acdaut0 #E2\",function(){acd0_edge(2);});\n", + "acdonclick(\"#acdaut0 #E3\",function(){acd0_edge(3);});\n", + "acdonclick(\"#acdaut0 #E4\",function(){acd0_edge(4);});\n", + "acdonclick(\"#acdaut0 #E5\",function(){acd0_edge(5);});\n", + "acdonclick(\"#acdaut0 #E6\",function(){acd0_edge(6);});\n", + "acdonclick(\"#acdaut0 #E7\",function(){acd0_edge(7);});\n", + "acdonclick(\"#acdaut0 #E8\",function(){acd0_edge(8);});\n", + "acdonclick(\"#acdaut0 #E9\",function(){acd0_edge(9);});\n", + "acdonclick(\"#acdaut0 #E10\",function(){acd0_edge(10);});\n", + "acdonclick(\"#acdaut0 #E11\",function(){acd0_edge(11);});\n", + "acdonclick(\"#acdaut0 #E12\",function(){acd0_edge(12);});\n", + "acdonclick(\"#acdaut0 #E13\",function(){acd0_edge(13);});\n", + "acdonclick(\"#acdaut0 #E14\",function(){acd0_edge(14);});\n", + "acdonclick(\"#acdaut0 #E15\",function(){acd0_edge(15);});\n", + "acdonclick(\"#acdaut0 #E16\",function(){acd0_edge(16);});\n", + "acdonclick(\"#acdaut0 #E17\",function(){acd0_edge(17);});\n", + "acdonclick(\"#acdaut0 #E18\",function(){acd0_edge(18);});\n", + "acdonclick(\"#acdaut0 #E19\",function(){acd0_edge(19);});\n", + "acdonclick(\"#acdaut0 #E20\",function(){acd0_edge(20);});\n", + "acdonclick(\"#acdaut0 #E21\",function(){acd0_edge(21);});\n", + "acdonclick(\"#acdaut0 #E22\",function(){acd0_edge(22);});\n", + "acdonclick(\"#acdaut0 #E23\",function(){acd0_edge(23);});\n", + "acdonclick(\"#acdaut0 #E24\",function(){acd0_edge(24);});\n", + "acdonclick(\"#acdaut0 #E25\",function(){acd0_edge(25);});\n", + "acdonclick(\"#acdaut0 #E26\",function(){acd0_edge(26);});\n", + "acdonclick(\"#acdaut0 #E27\",function(){acd0_edge(27);});\n", + "acdonclick(\"#acdaut0 #E28\",function(){acd0_edge(28);});\n", + "acdonclick(\"#acdaut0 #E29\",function(){acd0_edge(29);});\n", + "acdonclick(\"#acdaut0 #E30\",function(){acd0_edge(30);});\n", + "acdonclick(\"#acdaut0 #E31\",function(){acd0_edge(31);});\n", + "acdonclick(\"#acdaut0 #E32\",function(){acd0_edge(32);});\n", + "acdonclick(\"#acdaut0 #E33\",function(){acd0_edge(33);});\n", + "acdonclick(\"#acdaut0 #E34\",function(){acd0_edge(34);});\n", + "acdonclick(\"#acdaut0 #E35\",function(){acd0_edge(35);});\n", + "acdonclick(\"#acdaut0 #E36\",function(){acd0_edge(36);});\n", + "acdonclick(\"#acdaut0 #E37\",function(){acd0_edge(37);});\n", + "acdonclick(\"#acdaut0 #E38\",function(){acd0_edge(38);});\n", + "acdonclick(\"#acdaut0 #E39\",function(){acd0_edge(39);});\n", + "acdonclick(\"#acdaut0 #E40\",function(){acd0_edge(40);});\n", + "acdonclick(\"#acdaut0 #S0\",function(){acd0_state(0);});\n", + "acdonclick(\"#acdaut0 #S1\",function(){acd0_state(1);});\n", + "acdonclick(\"#acdaut0 #S2\",function(){acd0_state(2);});\n", + "acdonclick(\"#acdaut0 #S3\",function(){acd0_state(3);});\n", + "acdonclick(\"#acdaut0 #S4\",function(){acd0_state(4);});\n", + "acdonclick(\"#acdaut0 #S5\",function(){acd0_state(5);});\n", + "acdonclick(\"#acdaut0 #S6\",function(){acd0_state(6);});\n", + "acdonclick(\"#acdaut0 #S7\",function(){acd0_state(7);});\n", + "acdonclick(\"#acdaut0 #S8\",function(){acd0_state(8);});\n", + "acdonclick(\"#acdaut0 #S9\",function(){acd0_state(9);});\n", + "acdonclick(\"#acd0 #N0\",function(){acd0_node(0, 0);});\n", + "acdonclick(\"#acd0 #N1\",function(){acd0_node(1, 1);});\n", + "acdonclick(\"#acd0 #N2\",function(){acd0_node(2, 1);});\n", + "acdonclick(\"#acd0 #N3\",function(){acd0_node(3, 1);});\n", + "acdonclick(\"#acd0 #N4\",function(){acd0_node(4, 1);});\n", + "acdonclick(\"#acd0 #N5\",function(){acd0_node(5, 1);});\n", + "acdonclick(\"#acd0 #N6\",function(){acd0_node(6, 1);});\n", + "acdonclick(\"#acd0 #N7\",function(){acd0_node(7, 1);});\n", + "acdonclick(\"#acd0 #N8\",function(){acd0_node(8, 1);});\n", + "acdonclick(\"#acd0 #N9\",function(){acd0_node(9, 0);});\n", + "acdonclick(\"#acd0 #N10\",function(){acd0_node(10, 0);});\n", + "acdonclick(\"#acd0 #N11\",function(){acd0_node(11, 0);});\n", + "acdonclick(\"#acd0 #N12\",function(){acd0_node(12, 0);});\n", + "acdonclick(\"#acd0 #N13\",function(){acd0_node(13, 0);});\n", + "acdonclick(\"#acd0 #N14\",function(){acd0_node(14, 0);});\n", + "" ], "text/plain": [ - " >" + " >" ] }, "execution_count": 20, @@ -4968,7 +5091,7 @@ "\n" ], "text/plain": [ - " *' at 0x7f14701670f0> >" + " *' at 0x7f82c00bc870> >" ] }, "execution_count": 29, @@ -5607,7 +5730,7 @@ "\n" ], "text/plain": [ - " *' at 0x7f1470167210> >" + " *' at 0x7f82c00bc060> >" ] }, "execution_count": 31, @@ -5807,7 +5930,9 @@ "cell_type": "code", "execution_count": 40, "id": "813d15ed", - "metadata": {}, + "metadata": { + "scrolled": false + }, "outputs": [ { "data": { @@ -6875,36 +7000,159 @@ "\n", "\n", "" + " acdaddclasses(\"#acdaut1 .acdN\" + node,\n", + " [acc ? \"acdacc\" : \"acdrej\", \"acdbold\"]);\n", + " acdaddclasses(\"#acd1 #N\" + node, [\"acdbold\", \"acdhigh\"]);\n", + "};acdaddclasses(\"#acdaut1 #E9\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut1 #E10\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut1 #E11\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut1 #E12\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut1 #E13\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut1 #E14\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut1 #E15\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut1 #E16\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut1 #E21\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut1 #E22\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut1 #E23\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut1 #E24\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut1 #E25\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut1 #E26\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut1 #E27\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut1 #E28\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut1 #E33\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut1 #E34\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut1 #E35\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut1 #E36\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut1 #E31\", [\"acdN1\"]);\n", + "acdaddclasses(\"#acdaut1 #E32\", [\"acdN1\"]);\n", + "acdaddclasses(\"#acdaut1 #E39\", [\"acdN1\"]);\n", + "acdaddclasses(\"#acdaut1 #E40\", [\"acdN1\"]);\n", + "acdaddclasses(\"#acdaut1 #E5\", [\"acdN2\"]);\n", + "acdaddclasses(\"#acdaut1 #E7\", [\"acdN2\"]);\n", + "acdaddclasses(\"#acdaut1 #E17\", [\"acdN2\"]);\n", + "acdaddclasses(\"#acdaut1 #E1\", [\"acdN3\"]);\n", + "acdaddclasses(\"#acdaut1 #E10\", [\"acdN4\"]);\n", + "acdaddclasses(\"#acdaut1 #E12\", [\"acdN4\"]);\n", + "acdaddclasses(\"#acdaut1 #E13\", [\"acdN4\"]);\n", + "acdaddclasses(\"#acdaut1 #E15\", [\"acdN4\"]);\n", + "acdaddclasses(\"#acdaut1 #E21\", [\"acdN4\"]);\n", + "acdaddclasses(\"#acdaut1 #E22\", [\"acdN4\"]);\n", + "acdaddclasses(\"#acdaut1 #E14\", [\"acdN5\"]);\n", + "acdaddclasses(\"#acdaut1 #E15\", [\"acdN5\"]);\n", + "acdaddclasses(\"#acdaut1 #E22\", [\"acdN5\"]);\n", + "acdaddclasses(\"#acdaut1 #E23\", [\"acdN5\"]);\n", + "acdaddclasses(\"#acdaut1 #E23\", [\"acdN6\"]);\n", + "acdaddclasses(\"#acdaut1 #E24\", [\"acdN6\"]);\n", + "acdaddclasses(\"#acdaut1 #E34\", [\"acdN6\"]);\n", + "acdaddclasses(\"#acdaut1 #E36\", [\"acdN6\"]);\n", + "acdaddclasses(\"#acdaut1 #E14\", [\"acdN7\"]);\n", + "acdaddclasses(\"#acdaut1 #E16\", [\"acdN7\"]);\n", + "acdaddclasses(\"#acdaut1 #E26\", [\"acdN7\"]);\n", + "acdaddclasses(\"#acdaut1 #E9\", [\"acdN8\"]);\n", + "acdaddclasses(\"#acdaut1 #E40\", [\"acdN9\"]);\n", + "acdaddclasses(\"#acdaut1 #E5\", [\"acdN10\"]);\n", + "acdaddclasses(\"#acdaut1 #E14\", [\"acdN11\"]);\n", + "acdaddclasses(\"#acdaut1 #E23\", [\"acdN12\"]);\n", + "acdaddclasses(\"#acdaut1 #E23\", [\"acdN13\"]);\n", + "acdaddclasses(\"#acdaut1 #E14\", [\"acdN14\"]);\n", + "acdonclick(\"#acdaut1 #E1\",function(){acd1_edge(1);});\n", + "acdonclick(\"#acdaut1 #E2\",function(){acd1_edge(2);});\n", + "acdonclick(\"#acdaut1 #E3\",function(){acd1_edge(3);});\n", + "acdonclick(\"#acdaut1 #E4\",function(){acd1_edge(4);});\n", + "acdonclick(\"#acdaut1 #E5\",function(){acd1_edge(5);});\n", + "acdonclick(\"#acdaut1 #E6\",function(){acd1_edge(6);});\n", + "acdonclick(\"#acdaut1 #E7\",function(){acd1_edge(7);});\n", + "acdonclick(\"#acdaut1 #E8\",function(){acd1_edge(8);});\n", + "acdonclick(\"#acdaut1 #E9\",function(){acd1_edge(9);});\n", + "acdonclick(\"#acdaut1 #E10\",function(){acd1_edge(10);});\n", + "acdonclick(\"#acdaut1 #E11\",function(){acd1_edge(11);});\n", + "acdonclick(\"#acdaut1 #E12\",function(){acd1_edge(12);});\n", + "acdonclick(\"#acdaut1 #E13\",function(){acd1_edge(13);});\n", + "acdonclick(\"#acdaut1 #E14\",function(){acd1_edge(14);});\n", + "acdonclick(\"#acdaut1 #E15\",function(){acd1_edge(15);});\n", + "acdonclick(\"#acdaut1 #E16\",function(){acd1_edge(16);});\n", + "acdonclick(\"#acdaut1 #E17\",function(){acd1_edge(17);});\n", + "acdonclick(\"#acdaut1 #E18\",function(){acd1_edge(18);});\n", + "acdonclick(\"#acdaut1 #E19\",function(){acd1_edge(19);});\n", + "acdonclick(\"#acdaut1 #E20\",function(){acd1_edge(20);});\n", + "acdonclick(\"#acdaut1 #E21\",function(){acd1_edge(21);});\n", + "acdonclick(\"#acdaut1 #E22\",function(){acd1_edge(22);});\n", + "acdonclick(\"#acdaut1 #E23\",function(){acd1_edge(23);});\n", + "acdonclick(\"#acdaut1 #E24\",function(){acd1_edge(24);});\n", + "acdonclick(\"#acdaut1 #E25\",function(){acd1_edge(25);});\n", + "acdonclick(\"#acdaut1 #E26\",function(){acd1_edge(26);});\n", + "acdonclick(\"#acdaut1 #E27\",function(){acd1_edge(27);});\n", + "acdonclick(\"#acdaut1 #E28\",function(){acd1_edge(28);});\n", + "acdonclick(\"#acdaut1 #E29\",function(){acd1_edge(29);});\n", + "acdonclick(\"#acdaut1 #E30\",function(){acd1_edge(30);});\n", + "acdonclick(\"#acdaut1 #E31\",function(){acd1_edge(31);});\n", + "acdonclick(\"#acdaut1 #E32\",function(){acd1_edge(32);});\n", + "acdonclick(\"#acdaut1 #E33\",function(){acd1_edge(33);});\n", + "acdonclick(\"#acdaut1 #E34\",function(){acd1_edge(34);});\n", + "acdonclick(\"#acdaut1 #E35\",function(){acd1_edge(35);});\n", + "acdonclick(\"#acdaut1 #E36\",function(){acd1_edge(36);});\n", + "acdonclick(\"#acdaut1 #E37\",function(){acd1_edge(37);});\n", + "acdonclick(\"#acdaut1 #E38\",function(){acd1_edge(38);});\n", + "acdonclick(\"#acdaut1 #E39\",function(){acd1_edge(39);});\n", + "acdonclick(\"#acdaut1 #E40\",function(){acd1_edge(40);});\n", + "acdonclick(\"#acdaut1 #S0\",function(){acd1_state(0);});\n", + "acdonclick(\"#acdaut1 #S1\",function(){acd1_state(1);});\n", + "acdonclick(\"#acdaut1 #S2\",function(){acd1_state(2);});\n", + "acdonclick(\"#acdaut1 #S3\",function(){acd1_state(3);});\n", + "acdonclick(\"#acdaut1 #S4\",function(){acd1_state(4);});\n", + "acdonclick(\"#acdaut1 #S5\",function(){acd1_state(5);});\n", + "acdonclick(\"#acdaut1 #S6\",function(){acd1_state(6);});\n", + "acdonclick(\"#acdaut1 #S7\",function(){acd1_state(7);});\n", + "acdonclick(\"#acdaut1 #S8\",function(){acd1_state(8);});\n", + "acdonclick(\"#acdaut1 #S9\",function(){acd1_state(9);});\n", + "acdonclick(\"#acd1 #N0\",function(){acd1_node(0, 0);});\n", + "acdonclick(\"#acd1 #N1\",function(){acd1_node(1, 1);});\n", + "acdonclick(\"#acd1 #N2\",function(){acd1_node(2, 1);});\n", + "acdonclick(\"#acd1 #N3\",function(){acd1_node(3, 1);});\n", + "acdonclick(\"#acd1 #N4\",function(){acd1_node(4, 1);});\n", + "acdonclick(\"#acd1 #N5\",function(){acd1_node(5, 1);});\n", + "acdonclick(\"#acd1 #N6\",function(){acd1_node(6, 1);});\n", + "acdonclick(\"#acd1 #N7\",function(){acd1_node(7, 1);});\n", + "acdonclick(\"#acd1 #N8\",function(){acd1_node(8, 1);});\n", + "acdonclick(\"#acd1 #N9\",function(){acd1_node(9, 0);});\n", + "acdonclick(\"#acd1 #N10\",function(){acd1_node(10, 0);});\n", + "acdonclick(\"#acd1 #N11\",function(){acd1_node(11, 0);});\n", + "acdonclick(\"#acd1 #N12\",function(){acd1_node(12, 0);});\n", + "acdonclick(\"#acd1 #N13\",function(){acd1_node(13, 0);});\n", + "acdonclick(\"#acd1 #N14\",function(){acd1_node(14, 0);});\n", + "" ], "text/plain": [ - " >" + " >" ] }, "execution_count": 40, @@ -7817,7 +8065,7 @@ "\n" ], "text/plain": [ - " *' at 0x7f14700fe1e0> >" + " *' at 0x7f82c00be460> >" ] }, "execution_count": 45, @@ -8114,36 +8362,69 @@ "\n", "\n", "" + " acdaddclasses(\"#acdaut2 .acdN\" + node,\n", + " [acc ? \"acdacc\" : \"acdrej\", \"acdbold\"]);\n", + " acdaddclasses(\"#acd2 #N\" + node, [\"acdbold\", \"acdhigh\"]);\n", + "};acdaddclasses(\"#acdaut2 #E1\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut2 #E2\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut2 #E3\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut2 #E4\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut2 #E5\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut2 #E6\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut2 #E2\", [\"acdN1\"]);\n", + "acdaddclasses(\"#acdaut2 #E3\", [\"acdN1\"]);\n", + "acdaddclasses(\"#acdaut2 #E4\", [\"acdN1\"]);\n", + "acdaddclasses(\"#acdaut2 #E5\", [\"acdN1\"]);\n", + "acdaddclasses(\"#acdaut2 #E6\", [\"acdN1\"]);\n", + "acdaddclasses(\"#acdaut2 #E1\", [\"acdN2\"]);\n", + "acdaddclasses(\"#acdaut2 #E2\", [\"acdN2\"]);\n", + "acdaddclasses(\"#acdaut2 #E4\", [\"acdN2\"]);\n", + "acdaddclasses(\"#acdaut2 #E6\", [\"acdN2\"]);\n", + "acdonclick(\"#acdaut2 #E1\",function(){acd2_edge(1);});\n", + "acdonclick(\"#acdaut2 #E2\",function(){acd2_edge(2);});\n", + "acdonclick(\"#acdaut2 #E3\",function(){acd2_edge(3);});\n", + "acdonclick(\"#acdaut2 #E4\",function(){acd2_edge(4);});\n", + "acdonclick(\"#acdaut2 #E5\",function(){acd2_edge(5);});\n", + "acdonclick(\"#acdaut2 #E6\",function(){acd2_edge(6);});\n", + "acdonclick(\"#acdaut2 #S0\",function(){acd2_state(0);});\n", + "acdonclick(\"#acdaut2 #S1\",function(){acd2_state(1);});\n", + "acdonclick(\"#acdaut2 #S2\",function(){acd2_state(2);});\n", + "acdonclick(\"#acd2 #N0\",function(){acd2_node(0, 1);});\n", + "acdonclick(\"#acd2 #N1\",function(){acd2_node(1, 0);});\n", + "acdonclick(\"#acd2 #N2\",function(){acd2_node(2, 0);});\n", + "" ], "text/plain": [ - " >" + " >" ] }, "execution_count": 47, @@ -8353,7 +8634,7 @@ "\n" ], "text/plain": [ - " *' at 0x7f14700feb40> >" + " *' at 0x7f82c00bdd40> >" ] }, "execution_count": 48, @@ -8628,36 +8909,69 @@ "\n", "\n", "" + " acdaddclasses(\"#acdaut3 .acdN\" + node,\n", + " [acc ? \"acdacc\" : \"acdrej\", \"acdbold\"]);\n", + " acdaddclasses(\"#acd3 #N\" + node, [\"acdbold\", \"acdhigh\"]);\n", + "};acdaddclasses(\"#acdaut3 #E1\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut3 #E2\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut3 #E3\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut3 #E4\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut3 #E5\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut3 #E6\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut3 #E1\", [\"acdN1\"]);\n", + "acdaddclasses(\"#acdaut3 #E2\", [\"acdN1\"]);\n", + "acdaddclasses(\"#acdaut3 #E4\", [\"acdN1\"]);\n", + "acdaddclasses(\"#acdaut3 #E6\", [\"acdN1\"]);\n", + "acdaddclasses(\"#acdaut3 #E2\", [\"acdN2\"]);\n", + "acdaddclasses(\"#acdaut3 #E3\", [\"acdN2\"]);\n", + "acdaddclasses(\"#acdaut3 #E4\", [\"acdN2\"]);\n", + "acdaddclasses(\"#acdaut3 #E5\", [\"acdN2\"]);\n", + "acdaddclasses(\"#acdaut3 #E6\", [\"acdN2\"]);\n", + "acdonclick(\"#acdaut3 #E1\",function(){acd3_edge(1);});\n", + "acdonclick(\"#acdaut3 #E2\",function(){acd3_edge(2);});\n", + "acdonclick(\"#acdaut3 #E3\",function(){acd3_edge(3);});\n", + "acdonclick(\"#acdaut3 #E4\",function(){acd3_edge(4);});\n", + "acdonclick(\"#acdaut3 #E5\",function(){acd3_edge(5);});\n", + "acdonclick(\"#acdaut3 #E6\",function(){acd3_edge(6);});\n", + "acdonclick(\"#acdaut3 #S0\",function(){acd3_state(0);});\n", + "acdonclick(\"#acdaut3 #S1\",function(){acd3_state(1);});\n", + "acdonclick(\"#acdaut3 #S2\",function(){acd3_state(2);});\n", + "acdonclick(\"#acd3 #N0\",function(){acd3_node(0, 1);});\n", + "acdonclick(\"#acd3 #N1\",function(){acd3_node(1, 0);});\n", + "acdonclick(\"#acd3 #N2\",function(){acd3_node(2, 0);});\n", + "" ], "text/plain": [ - " >" + " >" ] }, "execution_count": 49, @@ -8841,7 +9155,7 @@ "\n" ], "text/plain": [ - " *' at 0x7f14700fea80> >" + " *' at 0x7f82c00bf300> >" ] }, "execution_count": 50, @@ -8993,7 +9307,7 @@ "\n" ], "text/plain": [ - " *' at 0x7f1470107240> >" + " *' at 0x7f82c00be5b0> >" ] }, "execution_count": 51, @@ -9105,7 +9419,7 @@ "\n" ], "text/plain": [ - " >" + " >" ] }, "execution_count": 52, @@ -9271,7 +9585,7 @@ "\n" ], "text/plain": [ - " *' at 0x7f1470107030> >" + " *' at 0x7f82c00bf5d0> >" ] }, "execution_count": 53, @@ -9535,36 +9849,63 @@ "\n", "\n", "" + " acdaddclasses(\"#acdaut4 .acdN\" + node,\n", + " [acc ? \"acdacc\" : \"acdrej\", \"acdbold\"]);\n", + " acdaddclasses(\"#acd4 #N\" + node, [\"acdbold\", \"acdhigh\"]);\n", + "};acdaddclasses(\"#acdaut4 #E1\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut4 #E2\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut4 #E3\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut4 #E4\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut4 #E5\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut4 #E6\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut4 #E7\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut4 #E8\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut4 #E6\", [\"acdN1\"]);\n", + "acdonclick(\"#acdaut4 #E1\",function(){acd4_edge(1);});\n", + "acdonclick(\"#acdaut4 #E2\",function(){acd4_edge(2);});\n", + "acdonclick(\"#acdaut4 #E3\",function(){acd4_edge(3);});\n", + "acdonclick(\"#acdaut4 #E4\",function(){acd4_edge(4);});\n", + "acdonclick(\"#acdaut4 #E5\",function(){acd4_edge(5);});\n", + "acdonclick(\"#acdaut4 #E6\",function(){acd4_edge(6);});\n", + "acdonclick(\"#acdaut4 #E7\",function(){acd4_edge(7);});\n", + "acdonclick(\"#acdaut4 #E8\",function(){acd4_edge(8);});\n", + "acdonclick(\"#acdaut4 #S0\",function(){acd4_state(0);});\n", + "acdonclick(\"#acdaut4 #S1\",function(){acd4_state(1);});\n", + "acdonclick(\"#acd4 #N0\",function(){acd4_node(0, 1);});\n", + "acdonclick(\"#acd4 #N1\",function(){acd4_node(1, 0);});\n", + "" ], "text/plain": [ - " >" + " >" ] }, "execution_count": 55, @@ -9708,7 +10049,7 @@ "\n" ], "text/plain": [ - " *' at 0x7f1470107b70> >" + " *' at 0x7f82c00f4240> >" ] }, "execution_count": 57, @@ -9855,7 +10196,7 @@ "\n" ], "text/plain": [ - " *' at 0x7f147010d240> >" + " *' at 0x7f82c00f4090> >" ] }, "execution_count": 58, @@ -10165,36 +10506,68 @@ "\n", "\n", "" + " acdaddclasses(\"#acdaut5 .acdN\" + node,\n", + " [acc ? \"acdacc\" : \"acdrej\", \"acdbold\"]);\n", + " acdaddclasses(\"#acd5 #N\" + node, [\"acdbold\", \"acdhigh\"]);\n", + "};acdaddclasses(\"#acdaut5 #E1\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut5 #E2\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut5 #E3\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut5 #E4\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut5 #E5\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut5 #E6\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut5 #E7\", [\"acdN0\"]);\n", + "acdaddclasses(\"#acdaut5 #E1\", [\"acdN1\"]);\n", + "acdaddclasses(\"#acdaut5 #E3\", [\"acdN1\"]);\n", + "acdaddclasses(\"#acdaut5 #E4\", [\"acdN1\"]);\n", + "acdaddclasses(\"#acdaut5 #E5\", [\"acdN1\"]);\n", + "acdaddclasses(\"#acdaut5 #E7\", [\"acdN2\"]);\n", + "acdonclick(\"#acdaut5 #E1\",function(){acd5_edge(1);});\n", + "acdonclick(\"#acdaut5 #E2\",function(){acd5_edge(2);});\n", + "acdonclick(\"#acdaut5 #E3\",function(){acd5_edge(3);});\n", + "acdonclick(\"#acdaut5 #E4\",function(){acd5_edge(4);});\n", + "acdonclick(\"#acdaut5 #E5\",function(){acd5_edge(5);});\n", + "acdonclick(\"#acdaut5 #E6\",function(){acd5_edge(6);});\n", + "acdonclick(\"#acdaut5 #E7\",function(){acd5_edge(7);});\n", + "acdonclick(\"#acdaut5 #S0\",function(){acd5_state(0);});\n", + "acdonclick(\"#acdaut5 #S1\",function(){acd5_state(1);});\n", + "acdonclick(\"#acdaut5 #S2\",function(){acd5_state(2);});\n", + "acdonclick(\"#acdaut5 #S3\",function(){acd5_state(3);});\n", + "acdonclick(\"#acd5 #N0\",function(){acd5_node(0, 1);});\n", + "acdonclick(\"#acd5 #N1\",function(){acd5_node(1, 0);});\n", + "acdonclick(\"#acd5 #N2\",function(){acd5_node(2, 0);});\n", + "" ], "text/plain": [ - " >" + " >" ] }, "execution_count": 60, @@ -10322,7 +10695,7 @@ "\n" ], "text/plain": [ - " *' at 0x7f147010d5a0> >" + " *' at 0x7f82c00f50b0> >" ] }, "execution_count": 61, @@ -10452,7 +10825,7 @@ "\n" ], "text/plain": [ - " *' at 0x7f147010d6f0> >" + " *' at 0x7f82c00f52c0> >" ] }, "execution_count": 62, @@ -10732,7 +11105,7 @@ "\n" ], "text/plain": [ - " *' at 0x7f1470116270> >" + " *' at 0x7f82c00f4960> >" ] }, "execution_count": 63, @@ -10826,7 +11199,7 @@ "\n" ], "text/plain": [ - " *' at 0x7f1470116630> >" + " *' at 0x7f82c00f5a10> >" ] }, "execution_count": 64, @@ -10937,7 +11310,7 @@ "\n" ], "text/plain": [ - " *' at 0x7f1470116450> >" + " *' at 0x7f82c00f5ce0> >" ] }, "execution_count": 66, @@ -10995,7 +11368,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.2" + "version": "3.10.7" } }, "nbformat": 4, From 104e98aca61d4c526eaade2c6a4a703666a11528 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Wed, 4 Jan 2023 15:11:21 +0100 Subject: [PATCH 233/337] fix merging of initial states in state-based automata Fixes #522 reported by Raven Beutner. * spot/parseaut/parseaut.yy: Make sure all edges leaving the initial state have the same color. * THANKS: Add Raven. * NEWS: Mention the bug. * tests/core/522.test: New file. * tests/Makefile.am: Add it. --- NEWS | 6 ++++++ THANKS | 1 + spot/parseaut/parseaut.yy | 33 ++++++++++++++++++++++++------ tests/Makefile.am | 3 ++- tests/core/522.test | 43 +++++++++++++++++++++++++++++++++++++++ 5 files changed, 79 insertions(+), 7 deletions(-) create mode 100755 tests/core/522.test diff --git a/NEWS b/NEWS index 0ac838737..9775339fe 100644 --- a/NEWS +++ b/NEWS @@ -4,6 +4,12 @@ New in spot 2.11.3.dev (not yet released) - spot.acd() no longer depends on jQuery for interactivity. + Bug fixes: + + - When merging initial states from state-based automata with + multiple initial states (because Spot supports only one), the HOA + parser could break state-based acceptance. (Issue #522.) + New in spot 2.11.3 (2022-12-09) Bug fixes: diff --git a/THANKS b/THANKS index 356d187a1..93155f9d1 100644 --- a/THANKS +++ b/THANKS @@ -48,6 +48,7 @@ Nikos Gorogiannis Ondřej Lengál Paul Guénézan Pierre Ganty +Raven Beutner Reuben Rowe Roei Nahum Rüdiger Ehlers diff --git a/spot/parseaut/parseaut.yy b/spot/parseaut/parseaut.yy index 4d96b8c1c..7d5fac361 100644 --- a/spot/parseaut/parseaut.yy +++ b/spot/parseaut/parseaut.yy @@ -1,5 +1,5 @@ /* -*- coding: utf-8 -*- -** Copyright (C) 2014-2022 Laboratoire de Recherche et Développement +** Copyright (C) 2014-2023 Laboratoire de Recherche et Développement ** de l'Epita (LRDE). ** ** This file is part of Spot, a model checking library. @@ -2610,7 +2610,7 @@ static void fix_initial_state(result_& r) start.resize(std::distance(start.begin(), res)); assert(start.size() >= 1); - if (start.size() == 1) + if (start.size() == 1) { if (r.opts.want_kripke) r.h->ks->set_init_state(start.front().front()); @@ -2627,13 +2627,13 @@ static void fix_initial_state(result_& r) "a single initial state"); return; } + auto& aut = r.h->aut; // Fiddling with initial state may turn an incomplete automaton // into a complete one. - if (r.complete.is_false()) - r.complete = spot::trival::maybe(); + if (aut->prop_complete().is_false()) + aut->prop_complete(spot::trival::maybe()); // Multiple initial states. We might need to add a fake one, // unless one of the actual initial state has no incoming edge. - auto& aut = r.h->aut; std::vector has_incoming(aut->num_states(), 0); for (auto& t: aut->edges()) for (unsigned ud: aut->univ_dests(t)) @@ -2672,6 +2672,9 @@ static void fix_initial_state(result_& r) { unsigned p = pp.front(); if (p != init) + // FIXME: If p has no incoming we should be able to + // change the source of the edges of p instead of + // adding new edges. for (auto& t: aut->out(p)) aut->new_edge(init, t.dst, t.cond); } @@ -2694,6 +2697,24 @@ static void fix_initial_state(result_& r) } combiner.new_dests(init, comb_or); } + + // Merging two states may break state-based acceptance + // make sure all outgoing edges have the same color. + if (aut->prop_state_acc().is_true()) + { + bool first = true; + spot::acc_cond::mark_t prev; + for (auto& e: aut->out(init)) + if (first) + { + first = false; + prev = e.acc; + } + else if (e.acc != prev) + { + e.acc = prev; + } + } } } @@ -2871,8 +2892,8 @@ namespace spot r.aut_or_ks->set_named_prop("aliases", p); } fix_acceptance(r); + fix_properties(r); // before fix_initial_state fix_initial_state(r); - fix_properties(r); if (r.h->aut && !r.h->aut->is_existential()) r.h->aut->merge_univ_dests(); return r.h; diff --git a/tests/Makefile.am b/tests/Makefile.am index 4c2fe830c..8a180ddda 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -1,5 +1,5 @@ ## -*- coding: utf-8 -*- -## Copyright (C) 2009-2022 Laboratoire de Recherche et Développement +## Copyright (C) 2009-2023 Laboratoire de Recherche et Développement ## de l'Epita (LRDE). ## Copyright (C) 2003-2006 Laboratoire d'Informatique de Paris 6 ## (LIP6), département Systèmes Répartis Coopératifs (SRC), Université @@ -223,6 +223,7 @@ TESTS_misc = \ TESTS_twa = \ core/385.test \ core/521.test \ + core/522.test \ core/acc.test \ core/acc2.test \ core/bdddict.test \ diff --git a/tests/core/522.test b/tests/core/522.test new file mode 100755 index 000000000..5fe6ba945 --- /dev/null +++ b/tests/core/522.test @@ -0,0 +1,43 @@ +#!/bin/sh +# -*- coding: utf-8 -*- +# Copyright (C) 2023 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 . + +. ./defs + +set -e + +# For issue #522. + +cat >552.hoa < out.hoa +grep 'States: 7' out.hoa From 403e55d555df75e5b910669aa3301d98a04f04c5 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Thu, 5 Jan 2023 17:47:46 +0100 Subject: [PATCH 234/337] * doc/org/spot.css: Do not define background twice. --- doc/org/spot.css | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/org/spot.css b/doc/org/spot.css index ca8b12395..569ca37a9 100644 --- a/doc/org/spot.css +++ b/doc/org/spot.css @@ -16,9 +16,9 @@ h1::before{content:"";position:absolute;z-index:-1;background-color:#ffe35e;left #table-of-contents #text-table-of-contents{text-align:left} #org-div-home-and-up{text-align:center;font-size:100%} .outline-2 h2{display:block;width:100%;position:relative} -.outline-2 h2::before{content:"";height:100%;width:calc(100% + 2em);position:absolute;z-index:-1;bottom:0em;left:-1em;background-color:#ffe35e;background:linear-gradient(45deg,#ffe35e 50%,transparent 75%);transform:skew(10deg);border-radius:5px;} +.outline-2 h2::before{content:"";height:100%;width:calc(100% + 2em);position:absolute;z-index:-1;bottom:0em;left:-1em;background:linear-gradient(45deg,#ffe35e 50%,transparent 75%);transform:skew(10deg);border-radius:5px;} .outline-3 h3{display:block;width:auto;position:relative} -.outline-3 h3::before{content:"";position:absolute;z-index:-1;width:calc(100% + 2em);height:100%;left:-1em;bottom:0em;;background-color:#ffe35e;background:linear-gradient(45deg,#ffe35e 25%,transparent 50%);transform:skew(10deg);border-radius:3px} +.outline-3 h3::before{content:"";position:absolute;z-index:-1;width:calc(100% + 2em);height:100%;left:-1em;bottom:0em;background:linear-gradient(45deg,#ffe35e 25%,transparent 50%);transform:skew(10deg);border-radius:3px} .outline-2 h2:hover::before,.outline-3 h3:hover::before{background-color:#ffe35e} pre{margin:1.2ex} pre.src{padding-top:8px;border-left-style:solid;border-color:#00adad;overflow:auto;margin-top:0;margin-bottom:0} From 344d82f2b49837e7e98bcfb0fb10a566e5c97560 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Thu, 5 Jan 2023 17:48:14 +0100 Subject: [PATCH 235/337] simplify several comparison operators * spot/twaalgos/dtbasat.cc, spot/twaalgos/dtwasat.cc, spot/twaalgos/simulation.cc: Simplify, as reported by sonarcloud. --- spot/twaalgos/dtbasat.cc | 8 ++------ spot/twaalgos/dtwasat.cc | 8 ++------ spot/twaalgos/simulation.cc | 10 ++-------- 3 files changed, 6 insertions(+), 20 deletions(-) diff --git a/spot/twaalgos/dtbasat.cc b/spot/twaalgos/dtbasat.cc index b2147ebb4..c4bf3d1bc 100644 --- a/spot/twaalgos/dtbasat.cc +++ b/spot/twaalgos/dtbasat.cc @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2013-2018, 2021, 2022 Laboratoire de Recherche et +// Copyright (C) 2013-2018, 2021-2023 Laboratoire de Recherche et // Développement de l'Epita. // // This file is part of Spot, a model checking library. @@ -77,11 +77,7 @@ namespace spot return true; if (this->src_ref > other.src_ref) return false; - if (this->dst_ref < other.dst_ref) - return true; - if (this->dst_ref > other.dst_ref) - return false; - return false; + return this->dst_ref < other.dst_ref; } }; diff --git a/spot/twaalgos/dtwasat.cc b/spot/twaalgos/dtwasat.cc index 25a299154..2ecf38fd1 100644 --- a/spot/twaalgos/dtwasat.cc +++ b/spot/twaalgos/dtwasat.cc @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2013-2022 Laboratoire de Recherche +// Copyright (C) 2013-2023 Laboratoire de Recherche // et Développement de l'Epita. // // This file is part of Spot, a model checking library. @@ -98,11 +98,7 @@ namespace spot return true; if (this->acc_ref > other.acc_ref) return false; - if (this->acc_cand < other.acc_cand) - return true; - if (this->acc_cand > other.acc_cand) - return false; - return false; + return this->acc_cand < other.acc_cand; } }; diff --git a/spot/twaalgos/simulation.cc b/spot/twaalgos/simulation.cc index ca8928888..ed53929b3 100644 --- a/spot/twaalgos/simulation.cc +++ b/spot/twaalgos/simulation.cc @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2012-2022 Laboratoire de Recherche et Développement +// Copyright (C) 2012-2023 Laboratoire de Recherche et Développement // de l'Epita (LRDE). // // This file is part of Spot, a model checking library. @@ -93,13 +93,7 @@ namespace spot return true; if (states > r.states) return false; - - if (edges < r.edges) - return true; - if (edges > r.edges) - return false; - - return false; + return edges < r.edges; } inline bool operator>(const automaton_size& r) From 36e79ecca6314a220982131567e6302ac1ea4035 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Thu, 5 Jan 2023 17:49:00 +0100 Subject: [PATCH 236/337] * spot/twaalgos/game.cc: Fix incorrect std::forward. --- spot/twaalgos/game.cc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/spot/twaalgos/game.cc b/spot/twaalgos/game.cc index f5699bf49..df259b84a 100644 --- a/spot/twaalgos/game.cc +++ b/spot/twaalgos/game.cc @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2017-2018, 2020-2022 Laboratoire de Recherche et +// Copyright (C) 2017-2018, 2020-2023 Laboratoire de Recherche et // Développement de l'Epita (LRDE). // // This file is part of Spot, a model checking library. @@ -1034,7 +1034,7 @@ namespace spot ("set_state_players(): There must be as many owners as states"); arena->set_named_prop("state-player", - new region_t(std::forward(owners))); + new region_t(std::move(owners))); } void set_state_player(twa_graph_ptr arena, unsigned state, bool owner) @@ -1101,7 +1101,7 @@ namespace spot throw std::runtime_error("set_strategy(): strategies need to have " "the same size as the automaton."); arena->set_named_prop("strategy", - new strategy_t(std::forward(strat))); + new strategy_t(std::move(strat))); } void set_synthesis_outputs(const twa_graph_ptr& arena, const bdd& outs) @@ -1152,7 +1152,7 @@ namespace spot ("set_state_winners(): There must be as many winners as states"); arena->set_named_prop("state-winner", - new region_t(std::forward(winners))); + new region_t(std::move(winners))); } void set_state_winner(twa_graph_ptr arena, unsigned state, bool winner) From 4a78d1bff4fb113bdbd09b7989c527cfbd6339b3 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Thu, 5 Jan 2023 23:34:10 +0100 Subject: [PATCH 237/337] fix some code smells reported by sonarcloud * bench/dtgbasat/gen.py, bin/autcross.cc, bin/autfilt.cc, bin/common_aoutput.cc, bin/common_aoutput.hh: Various cleanups. --- bench/dtgbasat/gen.py | 6 ++-- bin/autcross.cc | 12 +++---- bin/autfilt.cc | 79 ++++++++++++++++++------------------------- bin/common_aoutput.cc | 12 +++---- bin/common_aoutput.hh | 8 ++--- 5 files changed, 51 insertions(+), 66 deletions(-) diff --git a/bench/dtgbasat/gen.py b/bench/dtgbasat/gen.py index e96bf2825..dabf77971 100755 --- a/bench/dtgbasat/gen.py +++ b/bench/dtgbasat/gen.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -# Copyright (C) 2016-2018 Laboratoire de Recherche et Développement de +# Copyright (C) 2016-2018, 2023 Laboratoire de Recherche et Développement de # l'Epita (LRDE). # # This file is part of Spot, a model checking library. @@ -55,12 +55,12 @@ class BenchConfig(object): if line[0] == '#' or line.isspace(): continue elif line[0:2] == "sh": - sh = re.search('sh (.+?)$', line).group(1) + sh = re.search('sh (.+)$', line).group(1) continue else: name = re.search('(.+?):', line).group(1) code = re.search(':(.+?)>', line).group(1) - xoptions = re.search('>(.+?)$', line).group(1) + xoptions = re.search('>(.+)$', line).group(1) b = Bench(name=name, code=code, xoptions=xoptions) self.l.append(b) self.sh.append(sh) diff --git a/bin/autcross.cc b/bin/autcross.cc index 24cd9bcd4..b3e504bb3 100644 --- a/bin/autcross.cc +++ b/bin/autcross.cc @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2017-2020, 2022 Laboratoire de Recherche et +// Copyright (C) 2017-2020, 2022-2023 Laboratoire de Recherche et // Développement de l'Epita (LRDE). // // This file is part of Spot, a model checking library. @@ -549,7 +549,7 @@ namespace { if (!quiet) std::cerr << "info: building " << autname(i, is_really_comp(i)) - << '*' << autname(j, true ^ is_really_comp(j)) + << '*' << autname(j, !is_really_comp(j)) << " requires more acceptance sets than supported\n"; return false; } @@ -557,14 +557,14 @@ namespace if (verbose) std::cerr << "info: check_empty " << autname(i, is_really_comp(i)) - << '*' << autname(j, true ^ is_really_comp(j)) << '\n'; + << '*' << autname(j, !is_really_comp(j)) << '\n'; auto w = aut_i->intersecting_word(aut_j); if (w) { std::ostream& err = global_error(); err << "error: " << autname(i, is_really_comp(i)) - << '*' << autname(j, true ^ is_really_comp(j)) + << '*' << autname(j, !is_really_comp(j)) << (" is nonempty; both automata accept the infinite word:\n" " "); example() << *w << '\n'; @@ -613,7 +613,7 @@ namespace return src.str(); }(); - input_statistics.push_back(in_statistics()); + input_statistics.emplace_back(in_statistics()); input_statistics[round_num].input_source = std::move(source); if (auto name = input->get_named_prop("automaton-name")) @@ -658,7 +658,7 @@ namespace problems += prob; } spot::cleanup_tmpfiles(); - output_statistics.push_back(std::move(stats)); + output_statistics.emplace_back(std::move(stats)); if (verbose) { diff --git a/bin/autfilt.cc b/bin/autfilt.cc index 7cff60e8b..b55d1bc9f 100644 --- a/bin/autfilt.cc +++ b/bin/autfilt.cc @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2013-2022 Laboratoire de Recherche et Développement +// Copyright (C) 2013-2023 Laboratoire de Recherche et Développement // de l'Epita (LRDE). // // This file is part of Spot, a model checking library. @@ -448,7 +448,7 @@ struct canon_aut std::vector edges; std::string acc; - canon_aut(const spot::const_twa_graph_ptr& aut) + explicit canon_aut(const spot::const_twa_graph_ptr& aut) : num_states(aut->num_states()) , edges(aut->edge_vector().begin() + 1, aut->edge_vector().end()) @@ -755,6 +755,22 @@ product_or(spot::twa_graph_ptr left, spot::twa_graph_ptr right) return spot::product_or(left, right); } +static spot::twa_graph_ptr +word_to_aut(const char* word, const char *argname) +{ + try + { + return spot::parse_word(word, opt->dict)->as_automaton(); + } + catch (const spot::parse_error& e) + { + error(2, 0, "failed to parse the argument of --%s:\n%s", + argname, e.what()); + } + SPOT_UNREACHABLE(); + return nullptr; +} + static int parse_opt(int key, char* arg, struct argp_state*) { @@ -776,17 +792,14 @@ parse_opt(int key, char* arg, struct argp_state*) opt_nth = parse_range(arg, 0, std::numeric_limits::max()); break; case 'u': - opt->uniq = std::unique_ptr(new std::set()); + opt->uniq = std::make_unique(); break; case 'v': opt_invert = true; break; case 'x': - { - const char* opt = extra_options.parse_options(arg); - if (opt) - error(2, 0, "failed to parse --options near '%s'", opt); - } + if (const char* opt = extra_options.parse_options(arg)) + error(2, 0, "failed to parse --options near '%s'", opt); break; case OPT_ALIASES: opt_aliases = XARGMATCH("--aliases", arg, aliases_args, aliases_types); @@ -802,16 +815,7 @@ parse_opt(int key, char* arg, struct argp_state*) opt_art_sccs_set = true; break; case OPT_ACCEPT_WORD: - try - { - opt->acc_words.push_back(spot::parse_word(arg, opt->dict) - ->as_automaton()); - } - catch (const spot::parse_error& e) - { - error(2, 0, "failed to parse the argument of --accept-word:\n%s", - e.what()); - } + opt->acc_words.emplace_back(word_to_aut(arg, "accept-word")); break; case OPT_ACCEPTANCE_IS: { @@ -964,16 +968,7 @@ parse_opt(int key, char* arg, struct argp_state*) "%d should be followed by a comma and WORD", res); arg = endptr + 1; } - try - { - opt->hl_words.emplace_back(spot::parse_word(arg, opt->dict) - ->as_automaton(), res); - } - catch (const spot::parse_error& e) - { - error(2, 0, "failed to parse the argument of --highlight-word:\n%s", - e.what()); - } + opt->hl_words.emplace_back(word_to_aut(arg, "highlight-word"), res); } break; case OPT_HIGHLIGHT_LANGUAGES: @@ -1157,16 +1152,7 @@ parse_opt(int key, char* arg, struct argp_state*) opt_art_sccs_set = true; break; case OPT_REJECT_WORD: - try - { - opt->rej_words.push_back(spot::parse_word(arg, opt->dict) - ->as_automaton()); - } - catch (const spot::parse_error& e) - { - error(2, 0, "failed to parse the argument of --reject-word:\n%s", - e.what()); - } + opt->rej_words.emplace_back(word_to_aut(arg, "reject-word")); break; case OPT_REM_AP: opt->rem_ap.add_ap(arg); @@ -1291,7 +1277,7 @@ namespace static bool match_acceptance(spot::twa_graph_ptr aut) { - auto& acc = aut->acc(); + const spot::acc_cond& acc = aut->acc(); switch (opt_acceptance_is) { case ACC_Any: @@ -1346,8 +1332,7 @@ namespace { bool max; bool odd; - bool is_p = acc.is_parity(max, odd, true); - if (!is_p) + if (!acc.is_parity(max, odd, true)) return false; switch (opt_acceptance_is) { @@ -1460,7 +1445,7 @@ namespace if (matched && opt_acceptance_is) matched = match_acceptance(aut); - if (matched && (opt_sccs_set | opt_art_sccs_set)) + if (matched && (opt_sccs_set || opt_art_sccs_set)) { spot::scc_info si(aut); unsigned n = si.scc_count(); @@ -1540,14 +1525,14 @@ namespace && spot::contains(aut, opt->equivalent_pos); if (matched && !opt->acc_words.empty()) - for (auto& word_aut: opt->acc_words) + for (const spot::twa_graph_ptr& word_aut: opt->acc_words) if (spot::product(aut, word_aut)->is_empty()) { matched = false; break; } if (matched && !opt->rej_words.empty()) - for (auto& word_aut: opt->rej_words) + for (const spot::twa_graph_ptr& word_aut: opt->rej_words) if (!spot::product(aut, word_aut)->is_empty()) { matched = false; @@ -1681,13 +1666,13 @@ namespace aut->accepting_run()->highlight(opt_highlight_accepting_run); if (!opt->hl_words.empty()) - for (auto& word_aut: opt->hl_words) + for (auto& [word_aut, color]: opt->hl_words) { if (aut->acc().uses_fin_acceptance()) error(2, 0, "--highlight-word does not yet work with Fin acceptance"); - if (auto run = spot::product(aut, word_aut.first)->accepting_run()) - run->project(aut)->highlight(word_aut.second); + if (auto run = spot::product(aut, word_aut)->accepting_run()) + run->project(aut)->highlight(color); } timer.stop(); diff --git a/bin/common_aoutput.cc b/bin/common_aoutput.cc index fcc79fc3c..60f83289e 100644 --- a/bin/common_aoutput.cc +++ b/bin/common_aoutput.cc @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2012-2022 Laboratoire de Recherche et Développement +// Copyright (C) 2012-2023 Laboratoire de Recherche et Développement // de l'Epita (LRDE). // // This file is part of Spot, a model checking library. @@ -453,7 +453,7 @@ hoa_stat_printer::print(const spot::const_parsed_aut_ptr& haut, const spot::const_twa_graph_ptr& aut, spot::formula f, const char* filename, int loc, - spot::process_timer& ptimer, + const spot::process_timer& ptimer, const char* csv_prefix, const char* csv_suffix) { timer_ = ptimer; @@ -633,10 +633,10 @@ automaton_printer::print(const spot::twa_graph_ptr& aut, outputnamer.print(haut, aut, f, filename, loc, ptimer, csv_prefix, csv_suffix); std::string fname = outputname.str(); - auto p = outputfiles.emplace(fname, nullptr); - if (p.second) - p.first->second.reset(new output_file(fname.c_str())); - out = &p.first->second->ostream(); + auto [it, b] = outputfiles.try_emplace(fname, nullptr); + if (b) + it->second.reset(new output_file(fname.c_str())); + out = &it->second->ostream(); } // Output it. diff --git a/bin/common_aoutput.hh b/bin/common_aoutput.hh index d33b687d2..f57beae84 100644 --- a/bin/common_aoutput.hh +++ b/bin/common_aoutput.hh @@ -1,6 +1,6 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2014-2018, 2020, 2022 Laboratoire de Recherche et -// Développement de l'Epita (LRDE). +// Copyright (C) 2014-2018, 2020, 2022, 2023 Laboratoire de Recherche +// et Développement de l'Epita (LRDE). // // This file is part of Spot, a model checking library. // @@ -155,7 +155,7 @@ public: print(const spot::const_parsed_aut_ptr& haut, const spot::const_twa_graph_ptr& aut, spot::formula f, - const char* filename, int loc, spot::process_timer& ptimer, + const char* filename, int loc, const spot::process_timer& ptimer, const char* csv_prefix, const char* csv_suffix); private: @@ -196,7 +196,7 @@ class automaton_printer std::map> outputfiles; public: - automaton_printer(stat_style input = no_input); + explicit automaton_printer(stat_style input = no_input); ~automaton_printer(); void From 7b0507a950a2734cdf2a7031d24286cd2ce88cb3 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Thu, 5 Jan 2023 23:43:31 +0100 Subject: [PATCH 238/337] bin: detect overflows in conversion functions * bin/common_conv.cc (to_int, to_unsigned): Here. * bin/common_range.cc (parse_range): And there. * tests/core/ltlgrind.test, tests/core/genaut.test, tests/core/randaut.test: Add test cases. --- bin/common_conv.cc | 15 ++++++++++++--- bin/common_range.cc | 22 ++++++++++++++-------- tests/core/genaut.test | 9 ++++++--- tests/core/ltlgrind.test | 5 ++++- tests/core/randaut.test | 7 +++++-- 5 files changed, 41 insertions(+), 17 deletions(-) diff --git a/bin/common_conv.cc b/bin/common_conv.cc index e63969b16..02b1815fd 100644 --- a/bin/common_conv.cc +++ b/bin/common_conv.cc @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2015, 2018 Laboratoire de Recherche et Développement +// Copyright (C) 2015, 2018, 2023 Laboratoire de Recherche et Développement // de l'Epita (LRDE). // // This file is part of Spot, a model checking library. @@ -25,10 +25,14 @@ int to_int(const char* s, const char* where) { char* endptr; - int res = strtol(s, &endptr, 10); + long int lres = strtol(s, &endptr, 10); if (*endptr) error(2, 0, "failed to parse '%s' as an integer (in argument of %s).", s, where); + int res = lres; + if (res != lres) + error(2, 0, "value '%s' is too large for an int (in argument of %s).", + s, where); return res; } @@ -45,11 +49,16 @@ unsigned to_unsigned (const char *s, const char* where) { char* endptr; - unsigned res = strtoul(s, &endptr, 10); + unsigned long lres = strtoul(s, &endptr, 10); if (*endptr) error(2, 0, "failed to parse '%s' as an unsigned integer (in argument of %s).", s, where); + unsigned res = lres; + if (res != lres) + error(2, 0, + "value '%s' is too large for a unsigned int (in argument of %s).", + s, where); return res; } diff --git a/bin/common_range.cc b/bin/common_range.cc index 8909a26c0..9419cc389 100644 --- a/bin/common_range.cc +++ b/bin/common_range.cc @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2012, 2014, 2016 Laboratoire de Recherche et +// Copyright (C) 2012, 2014, 2016, 2023 Laboratoire de Recherche et // Développement de l'Epita (LRDE). // // This file is part of Spot, a model checking library. @@ -36,13 +36,16 @@ parse_range(const char* str, int missing_left, int missing_right) { range res; char* end; - res.min = strtol(str, &end, 10); + long lres = strtol(str, &end, 10); + res.min = lres; + if (res.min != lres) + error(2, 0, "start of range '%s' is too large for an int.", str); if (end == str) { // No leading number. It's OK as long as the string is not // empty. if (!*end) - error(1, 0, "invalid empty range"); + error(2, 0, "invalid empty range"); res.min = missing_left; } if (!*end) @@ -66,19 +69,22 @@ parse_range(const char* str, int missing_left, int missing_right) { // Parse the next integer. char* end2; - res.max = strtol(end, &end2, 10); + lres = strtol(end, &end2, 10); + res.max = lres; + if (res.max != lres) + error(2, 0, "end of range '%s' is too large for an int.", str); if (str == end2) - error(1, 0, "invalid range '%s' " + error(2, 0, "invalid range '%s' " "(should start with digits, dots, or colon)", str); if (end == end2) - error(1, 0, "invalid range '%s' (missing end?)", str); + error(2, 0, "invalid range '%s' (missing end?)", str); if (*end2) - error(1, 0, "invalid range '%s' (trailing garbage?)", str); + error(2, 0, "invalid range '%s' (trailing garbage?)", str); } } if (res.min < 0 || res.max < 0) - error(1, 0, "invalid range '%s': values must be positive", str); + error(2, 0, "invalid range '%s': values must be positive", str); return res; } diff --git a/tests/core/genaut.test b/tests/core/genaut.test index 5da9509ed..f364569e1 100644 --- a/tests/core/genaut.test +++ b/tests/core/genaut.test @@ -1,7 +1,7 @@ #!/bin/sh # -*- coding: utf-8 -*- -# Copyright (C) 2017, 2018, 2019, 2020 Laboratoire de Recherche et Développement -# de l'Epita (LRDE). +# Copyright (C) 2017-2020, 2023 Laboratoire de Recherche et +# Développement de l'Epita (LRDE). # # This file is part of Spot, a model checking library. # @@ -60,7 +60,10 @@ genaut --l-nba='1..3?' 2>err && exit 1 grep 'invalid range.*trailing garbage' err genaut --l-nba='1..' 2>err && exit 1 grep 'invalid range.*missing end' err - +genaut --l-nba='9999999999999999999999999..' 2>err && exit 1 +grep 'start.*too large' err +genaut --l-nba='1..9999999999999999999999999' 2>err && exit 1 +grep 'end.*too large' err # Tests for autfilt -N/--nth genaut --ks-nca=1..5 | autfilt -N 2..4 > range1.hoa diff --git a/tests/core/ltlgrind.test b/tests/core/ltlgrind.test index 292756bc6..09e75ee4e 100755 --- a/tests/core/ltlgrind.test +++ b/tests/core/ltlgrind.test @@ -1,6 +1,6 @@ #! /bin/sh # -*- coding: utf-8 -*- -# Copyright (C) 2014, 2015, 2019 Laboratoire de Recherche et Développement +# Copyright (C) 2014, 2015, 2019, 2023 Laboratoire de Recherche et Développement # de l'Epita (LRDE). # # This file is part of Spot, a model checking library. @@ -198,3 +198,6 @@ checkopt_noparse -F input/2 --format '%<,%f,%>,%F,%L' <err && exit 1 +grep 'too large' err diff --git a/tests/core/randaut.test b/tests/core/randaut.test index 7ff851646..50558e790 100755 --- a/tests/core/randaut.test +++ b/tests/core/randaut.test @@ -1,7 +1,7 @@ #!/bin/sh # -*- coding: utf-8 -*- -# Copyright (C) 2014-2018, 2020, 2022 Laboratoire de Recherche et -# Développement de l'Epita (LRDE). +# Copyright (C) 2014-2018, 2020, 2022, 2023 Laboratoire de Recherche +# et Développement de l'Epita (LRDE). # # This file is part of Spot, a model checking library. # @@ -29,6 +29,9 @@ grep "randaut: 3.1.*is not between 0 and 1 (in argument of -e" err randaut -n1a 3 2>err && exit 1 grep "randaut: failed to parse '1a' as an integer.* -n/--automata)" err +randaut -n99999999999999999999999999 3 2>err && exit 1 +grep "randaut:.*too large" err + randaut --spin -Q4 a b | ../ikwiad -H -XN - >out grep 'States: 4' out grep 'AP: 2' out From 39212bbcd27daa650f1851f57d0722424fb97ff0 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Fri, 6 Jan 2023 11:55:34 +0100 Subject: [PATCH 239/337] more code smells * bin/common_file.cc, bin/common_file.hh, bin/common_finput.cc, bin/common_finput.hh, bin/common_output.cc, bin/common_setup.cc, bin/common_setup.hh, bin/common_trans.cc, bin/common_trans.hh, bin/dstar2tgba.cc, bin/genaut.cc, bin/genltl.cc, bin/ltl2tgba.cc, bin/ltl2tgta.cc, bin/ltlcross.cc, bin/ltldo.cc, bin/ltlfilt.cc, bin/ltlsynt.cc, bin/randltl.cc: Fix minor code issues reported by sonarcloud. --- bin/common_file.cc | 7 ++-- bin/common_file.hh | 13 +++---- bin/common_finput.cc | 10 ++---- bin/common_finput.hh | 16 +++++---- bin/common_output.cc | 20 +++++------ bin/common_setup.cc | 7 ++-- bin/common_setup.hh | 6 ++-- bin/common_trans.cc | 46 ++++++++++++------------ bin/common_trans.hh | 19 +++++----- bin/dstar2tgba.cc | 4 +-- bin/genaut.cc | 4 +-- bin/genltl.cc | 6 ++-- bin/ltl2tgba.cc | 8 ++--- bin/ltl2tgta.cc | 4 +-- bin/ltlcross.cc | 86 ++++++++++++++++---------------------------- bin/ltldo.cc | 14 +++----- bin/ltlfilt.cc | 20 +++++------ bin/ltlsynt.cc | 15 ++++---- bin/randltl.cc | 6 ++-- 19 files changed, 133 insertions(+), 178 deletions(-) diff --git a/bin/common_file.cc b/bin/common_file.cc index 005bb5479..4e56c6d54 100644 --- a/bin/common_file.cc +++ b/bin/common_file.cc @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2015, 2016, 2022 Laboratoire de Recherche et +// Copyright (C) 2015, 2016, 2022, 2023 Laboratoire de Recherche et // Développement de l'Epita (LRDE). // // This file is part of Spot, a model checking library. @@ -21,7 +21,6 @@ #include #include - output_file::output_file(const char* name, bool force_append) { std::ios_base::openmode mode = std::ios_base::trunc; @@ -39,10 +38,10 @@ output_file::output_file(const char* name, bool force_append) os_ = &std::cout; return; } - of_ = new std::ofstream(name, mode); + of_ = std::make_unique(name, mode); if (!*of_) error(2, errno, "cannot open '%s'", name); - os_ = of_; + os_ = of_.get(); } diff --git a/bin/common_file.hh b/bin/common_file.hh index b8f9842b8..b6aa0bec3 100644 --- a/bin/common_file.hh +++ b/bin/common_file.hh @@ -1,6 +1,6 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2015, 2016, 2022 Laboratoire de Recherche et Développement de -// l'Epita (LRDE). +// Copyright (C) 2015-2016, 2022-2023 Laboratoire de Recherche et +// Développement de l'Epita (LRDE). // // This file is part of Spot, a model checking library. // @@ -21,13 +21,13 @@ #include "common_sys.hh" #include +#include #include -#include class output_file { std::ostream* os_; - std::ofstream* of_ = nullptr; + std::unique_ptr of_; bool append_ = false; public: // Open a file for output. "-" is interpreted as stdout. @@ -37,11 +37,6 @@ public: void close(const std::string& name); - ~output_file() - { - delete of_; - } - bool append() const { return append_; diff --git a/bin/common_finput.cc b/bin/common_finput.cc index 80aca5df7..dbcdb3849 100644 --- a/bin/common_finput.cc +++ b/bin/common_finput.cc @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2012-2017, 2019, 2021, 2022 Laboratoire de Recherche +// Copyright (C) 2012-2017, 2019, 2021-2023 Laboratoire de Recherche // et Développement de l'Epita (LRDE). // // This file is part of Spot, a model checking library. @@ -96,12 +96,6 @@ parse_formula(const std::string& s) (s, spot::default_environment::instance(), false, lenient); } -job_processor::job_processor() - : abort_run(false), real_filename(nullptr), - col_to_read(0), prefix(nullptr), suffix(nullptr) -{ -} - job_processor::~job_processor() { if (real_filename) @@ -370,7 +364,7 @@ int job_processor::run() { int error = 0; - for (auto& j: jobs) + for (const auto& j: jobs) { switch (j.type) { diff --git a/bin/common_finput.hh b/bin/common_finput.hh index 2a5815fc3..9ecb5b025 100644 --- a/bin/common_finput.hh +++ b/bin/common_finput.hh @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2012-2017, 2022 Laboratoire de Recherche et +// Copyright (C) 2012-2017, 2022, 2023 Laboratoire de Recherche et // Développement de l'Epita (LRDE). // // This file is part of Spot, a model checking library. @@ -56,9 +56,11 @@ spot::parsed_formula parse_formula(const std::string& s); class job_processor { protected: - bool abort_run; // Set to true in process_formula() to abort run(). + bool abort_run = false; // Set to true in process_formula() to abort run(). public: - job_processor(); + job_processor() = default; + job_processor(const job_processor&) = delete; + job_processor& operator=(const job_processor&) = delete; virtual ~job_processor(); @@ -84,10 +86,10 @@ public: virtual int run(); - char* real_filename; - long int col_to_read; - char* prefix; - char* suffix; + char* real_filename = nullptr; + long int col_to_read = 0; + char* prefix = nullptr; + char* suffix = nullptr; }; // Report and error message or add a default job depending on whether diff --git a/bin/common_output.cc b/bin/common_output.cc index e9c61a513..93cb2dfaf 100644 --- a/bin/common_output.cc +++ b/bin/common_output.cc @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2012-2019 Laboratoire de Recherche et Développement +// Copyright (C) 2012-2019, 2023 Laboratoire de Recherche et Développement // de l'Epita (LRDE). // // This file is part of Spot, a model checking library. @@ -23,6 +23,7 @@ #include "common_setup.hh" #include #include +#include #include #include #include @@ -297,9 +298,9 @@ namespace }; } -static formula_printer* format = nullptr; +static std::unique_ptr format; static std::ostringstream outputname; -static formula_printer* outputnamer = nullptr; +static std::unique_ptr outputnamer; static std::map> outputfiles; int @@ -320,7 +321,7 @@ parse_opt_output(int key, char* arg, struct argp_state*) output_format = lbt_output; break; case 'o': - outputnamer = new formula_printer(outputname, arg); + outputnamer = std::make_unique(outputname, arg); break; case 'p': full_parenth = true; @@ -341,8 +342,7 @@ parse_opt_output(int key, char* arg, struct argp_state*) output_format = wring_output; break; case OPT_FORMAT: - delete format; - format = new formula_printer(std::cout, arg); + format = std::make_unique(std::cout, arg); break; default: return ARGP_ERR_UNKNOWN; @@ -417,10 +417,10 @@ output_formula_checked(spot::formula f, spot::process_timer* ptimer, formula_with_location fl = { f, filename, linenum, prefix, suffix }; outputnamer->print(fl, ptimer); std::string fname = outputname.str(); - auto p = outputfiles.emplace(fname, nullptr); - if (p.second) - p.first->second.reset(new output_file(fname.c_str())); - out = &p.first->second->ostream(); + auto [it, b] = outputfiles.try_emplace(fname, nullptr); + if (b) + it->second.reset(new output_file(fname.c_str())); + out = &it->second->ostream(); } output_formula(*out, f, ptimer, filename, linenum, prefix, suffix); *out << output_terminator; diff --git a/bin/common_setup.cc b/bin/common_setup.cc index 24cacae85..af033a47f 100644 --- a/bin/common_setup.cc +++ b/bin/common_setup.cc @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2012-2022 Laboratoire de Recherche et Développement +// Copyright (C) 2012-2023 Laboratoire de Recherche et Développement // de l'Epita (LRDE). // // This file is part of Spot, a model checking library. @@ -20,13 +20,14 @@ #include "common_setup.hh" #include "common_aoutput.hh" -#include "argp.h" -#include "closeout.h" +#include +#include #include #include #include #include #include +#include #include static void diff --git a/bin/common_setup.hh b/bin/common_setup.hh index e2fce84e0..94cd16f4f 100644 --- a/bin/common_setup.hh +++ b/bin/common_setup.hh @@ -1,6 +1,6 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2012, 2013, 2018, 2019 Laboratoire de Recherche et -// Développement de l'Epita (LRDE). +// Copyright (C) 2012-2013, 2018-2019, 2023 Laboratoire de Recherche +// et Développement de l'Epita (LRDE). // // This file is part of Spot, a model checking library. // @@ -34,5 +34,5 @@ int protected_main(char** progname, std::function mainfun); // Diagnose exceptions. [[noreturn]] void handle_any_exception(); -#define BEGIN_EXCEPTION_PROTECT try { (void)0; +#define BEGIN_EXCEPTION_PROTECT try { (void)0 #define END_EXCEPTION_PROTECT } catch (...) { handle_any_exception(); } diff --git a/bin/common_trans.cc b/bin/common_trans.cc index e34f3d77d..b93535173 100644 --- a/bin/common_trans.cc +++ b/bin/common_trans.cc @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2015-2022 Laboratoire de Recherche et Développement +// Copyright (C) 2015-2023 Laboratoire de Recherche et Développement // de l'Epita (LRDE). // // This file is part of Spot, a model checking library. @@ -53,7 +53,7 @@ struct shorthands_t }; #define SHORTHAND(PRE, POST) { PRE, std::regex("^" PRE), POST } -static shorthands_t shorthands_ltl[] = { +static const shorthands_t shorthands_ltl[] = { SHORTHAND("delag", " %f>%O"), SHORTHAND("lbt", " <%L>%O"), SHORTHAND("ltl2ba", " -f %s>%O"), @@ -73,7 +73,7 @@ static shorthands_t shorthands_ltl[] = { SHORTHAND("owl.* ltl-utilities\\b", " -f %f"), }; -static shorthands_t shorthands_autproc[] = { +static const shorthands_t shorthands_autproc[] = { SHORTHAND("autfilt", " %H>%O"), SHORTHAND("dra2dpa", " <%H>%O"), SHORTHAND("dstar2tgba", " %H>%O"), @@ -85,7 +85,7 @@ static shorthands_t shorthands_autproc[] = { " <%H>%O"), }; -static void show_shorthands(shorthands_t* begin, shorthands_t* end) +static void show_shorthands(const shorthands_t* begin, const shorthands_t* end) { std::cout << ("If a COMMANDFMT does not use any %-sequence, and starts with one of\n" @@ -100,7 +100,8 @@ static void show_shorthands(shorthands_t* begin, shorthands_t* end) } -tool_spec::tool_spec(const char* spec, shorthands_t* begin, shorthands_t* end, +tool_spec::tool_spec(const char* spec, + const shorthands_t* begin, const shorthands_t* end, bool is_ref) noexcept : spec(spec), cmd(spec), name(spec), reference(is_ref) { @@ -113,15 +114,15 @@ tool_spec::tool_spec(const char* spec, shorthands_t* begin, shorthands_t* end, { if (*pos == '{') ++count; - else if (*pos == '}') - if (!--count) - { - name = strndup(cmd + 1, pos - cmd - 1); - cmd = pos + 1; - while (*cmd == ' ' || *cmd == '\t') - ++cmd; - break; - } + else if (*pos == '}' && --count == 0) + { + name = strndup(cmd + 1, pos - cmd - 1); + cmd = pos + 1; + // skip leading whitespace + while (*cmd == ' ' || *cmd == '\t') + ++cmd; + break; + } } } // If there is no % in the string, look for a known @@ -147,11 +148,11 @@ tool_spec::tool_spec(const char* spec, shorthands_t* begin, shorthands_t* end, auto& p = *begin++; if (std::regex_search(basename, p.rprefix)) { - int m = strlen(p.suffix); - int q = strlen(cmd); + size_t m = strlen(p.suffix); + size_t q = strlen(cmd); char* tmp = static_cast(malloc(q + m + 1)); - strcpy(tmp, cmd); - strcpy(tmp + q, p.suffix); + memcpy(tmp, cmd, q); + memcpy(tmp + q, p.suffix, m + 1); cmd = tmp; allocated = true; break; @@ -490,9 +491,8 @@ read_stdout_of_command(char* const* args) if (close(cout_pipe[1]) < 0) error(2, errno, "closing write-side of pipe failed"); - std::string buffer(32, 0); std::string results; - int bytes_read; + ssize_t bytes_read; for (;;) { static char buffer[512]; @@ -612,7 +612,7 @@ get_arg(const char*& cmd) { const char* start = cmd; std::string arg; - while (int c = *cmd) + while (char c = *cmd) { switch (c) { @@ -642,14 +642,14 @@ get_arg(const char*& cmd) goto end_loop; case '\'': { - int d = 0; + char d = '\0'; while ((d = *++cmd)) { if (d == '\'') break; arg.push_back(d); } - if (d == 0) + if (d == '\0') return nullptr; } break; diff --git a/bin/common_trans.hh b/bin/common_trans.hh index 31c88c80c..0ebe59e8c 100644 --- a/bin/common_trans.hh +++ b/bin/common_trans.hh @@ -51,7 +51,8 @@ struct tool_spec // Whether the tool is a reference. bool reference; - tool_spec(const char* spec, shorthands_t* begin, shorthands_t* end, + tool_spec(const char* spec, + const shorthands_t* begin, const shorthands_t* end, bool is_ref) noexcept; tool_spec(const tool_spec& other) noexcept; tool_spec& operator=(const tool_spec& other); @@ -71,7 +72,7 @@ struct quoted_formula final: public spot::printable_value struct filed_formula final: public spot::printable { - filed_formula(const quoted_formula& ltl) : f_(ltl) + explicit filed_formula(const quoted_formula& ltl) : f_(ltl) { } @@ -89,9 +90,7 @@ struct filed_formula final: public spot::printable struct filed_automaton final: public spot::printable { - filed_automaton() - { - } + filed_automaton() = default; void print(std::ostream& os, const char* pos) const override; @@ -112,7 +111,7 @@ struct printable_result_filename final: unsigned translator_num; printable_result_filename(); - ~printable_result_filename(); + ~printable_result_filename() override; void reset(unsigned n); void cleanup(); @@ -126,7 +125,7 @@ protected: spot::bdd_dict_ptr dict; // Round-specific variables quoted_formula ltl_formula; - filed_formula filename_formula = ltl_formula; + filed_formula filename_formula{ltl_formula}; // Run-specific variables printable_result_filename output; public: @@ -151,9 +150,9 @@ protected: public: using spot::formater::has; - autproc_runner(// whether we accept the absence of output - // specifier - bool no_output_allowed = false); + explicit 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); }; diff --git a/bin/dstar2tgba.cc b/bin/dstar2tgba.cc index 5b60a0ecc..4b2ec9662 100644 --- a/bin/dstar2tgba.cc +++ b/bin/dstar2tgba.cc @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2013-2019, 2022 Laboratoire de Recherche et Développement +// Copyright (C) 2013-2019, 2022, 2023 Laboratoire de Recherche et Développement // de l'Epita (LRDE). // // This file is part of Spot, a model checking library. @@ -117,7 +117,7 @@ namespace spot::postprocessor& post; automaton_printer printer; - dstar_processor(spot::postprocessor& post) + explicit dstar_processor(spot::postprocessor& post) : hoa_processor(spot::make_bdd_dict()), post(post), printer(aut_input) { } diff --git a/bin/genaut.cc b/bin/genaut.cc index 26678c588..f8d6b93ff 100644 --- a/bin/genaut.cc +++ b/bin/genaut.cc @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2017-2019, 2022 Laboratoire de Recherche et +// Copyright (C) 2017-2019, 2022-2023 Laboratoire de Recherche et // Développement de l'Epita (LRDE). // // This file is part of Spot, a model checking library. @@ -128,7 +128,7 @@ output_pattern(gen::aut_pattern_id pattern, int n) static void run_jobs() { - for (auto& j: jobs) + for (const auto& j: jobs) { int inc = (j.range.max < j.range.min) ? -1 : 1; int n = j.range.min; diff --git a/bin/genltl.cc b/bin/genltl.cc index 96d8bd7d3..ef8049171 100644 --- a/bin/genltl.cc +++ b/bin/genltl.cc @@ -1,6 +1,6 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2012, 2013, 2015-2019, 2022 Laboratoire de Recherche et -// Développement de l'Epita (LRDE). +// Copyright (C) 2012, 2013, 2015-2019, 2022-2023 Laboratoire de +// Recherche et Développement de l'Epita (LRDE). // // This file is part of Spot, a model checking library. // @@ -317,7 +317,7 @@ output_pattern(gen::ltl_pattern_id pattern, int n, int n2) static void run_jobs() { - for (auto& j: jobs) + for (const auto& j: jobs) { int inc = (j.range.max < j.range.min) ? -1 : 1; int n = j.range.min; diff --git a/bin/ltl2tgba.cc b/bin/ltl2tgba.cc index d4fb2fc17..73a9a23c6 100644 --- a/bin/ltl2tgba.cc +++ b/bin/ltl2tgba.cc @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2012-2019, 2022 Laboratoire de Recherche et +// Copyright (C) 2012-2019, 2022-2023 Laboratoire de Recherche et // Développement de l'Epita (LRDE). // // This file is part of Spot, a model checking library. @@ -124,10 +124,10 @@ namespace { public: spot::translator& trans; - automaton_printer printer; + automaton_printer printer{ltl_input}; - trans_processor(spot::translator& trans) - : trans(trans), printer(ltl_input) + explicit trans_processor(spot::translator& trans) + : trans(trans) { } diff --git a/bin/ltl2tgta.cc b/bin/ltl2tgta.cc index ab925c7ac..60afcf9e8 100644 --- a/bin/ltl2tgta.cc +++ b/bin/ltl2tgta.cc @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2012-2020, 2022 Laboratoire de Recherche et +// Copyright (C) 2012-2020, 2022-2023 Laboratoire de Recherche et // Développement de l'Epita (LRDE). // // This file is part of Spot, a model checking library. @@ -168,7 +168,7 @@ namespace public: spot::translator& trans; - trans_processor(spot::translator& trans) + explicit trans_processor(spot::translator& trans) : trans(trans) { } diff --git a/bin/ltlcross.cc b/bin/ltlcross.cc index 0dfa09985..3219beb75 100644 --- a/bin/ltlcross.cc +++ b/bin/ltlcross.cc @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2012-2020, 2022 Laboratoire de Recherche et +// Copyright (C) 2012-2020, 2022, 2023 Laboratoire de Recherche et // Développement de l'Epita (LRDE). // // This file is part of Spot, a model checking library. @@ -264,55 +264,32 @@ end_error() struct statistics { - statistics() noexcept - : ok(false), - alternating(false), - status_str(nullptr), - status_code(0), - time(0), - states(0), - edges(0), - transitions(0), - acc(0), - scc(0), - nonacc_scc(0), - terminal_scc(0), - weak_scc(0), - strong_scc(0), - nondetstates(0), - nondeterministic(false), - terminal_aut(false), - weak_aut(false), - strong_aut(false) - { - } - // If OK is false, only the status_str, status_code, and time fields // should be valid. - bool ok; - bool alternating; - const char* status_str; - int status_code; - double time; - unsigned states; - unsigned edges; - unsigned long long transitions; - unsigned acc; - unsigned scc; - unsigned nonacc_scc; - unsigned terminal_scc; - unsigned weak_scc; - unsigned strong_scc; - unsigned nondetstates; - bool nondeterministic; - bool terminal_aut; - bool weak_aut; - bool strong_aut; + bool ok = false; + bool alternating = false; + const char* status_str = nullptr; + int status_code = 0; + double time = 0.0; + unsigned states = 0; + unsigned edges = 0; + unsigned long long transitions = 0; + unsigned acc = 0; + unsigned scc = 0; + unsigned nonacc_scc = 0; + unsigned terminal_scc = 0; + unsigned weak_scc = 0; + unsigned strong_scc = 0; + unsigned nondetstates = 0; + bool nondeterministic = false; + bool terminal_aut = false; + bool weak_aut = false; + bool strong_aut = false; std::vector product_states; std::vector product_transitions; std::vector product_scc; - bool ambiguous; - bool complete; + bool ambiguous = false; + bool complete = false; std::string hoa_str; static void @@ -581,7 +558,7 @@ namespace class xtranslator_runner final: public translator_runner { public: - xtranslator_runner(spot::bdd_dict_ptr dict) + explicit xtranslator_runner(spot::bdd_dict_ptr dict) : translator_runner(dict) { } @@ -1095,17 +1072,14 @@ namespace } // Make sure we do not translate the same formula twice. - if (!allow_dups) + if (!allow_dups && !unique_set.insert(f).second) { - if (!unique_set.insert(f).second) - { - if (!quiet) - std::cerr - << ("warning: This formula or its negation has already" - " been checked.\n Use --allow-dups if it " - "should not be ignored.\n\n"); - return 0; - } + if (!quiet) + std::cerr + << ("warning: This formula or its negation has already" + " been checked.\n Use --allow-dups if it " + "should not be ignored.\n\n"); + return 0; } int problems = 0; diff --git a/bin/ltldo.cc b/bin/ltldo.cc index ffbd4873e..6e7bf5ec7 100644 --- a/bin/ltldo.cc +++ b/bin/ltldo.cc @@ -1,6 +1,6 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2015-2020, 2022 Laboratoire de Recherche et Développement -// de l'Epita (LRDE). +// Copyright (C) 2015-2020, 2022-2023 Laboratoire de Recherche et +// Développement de l'Epita (LRDE). // // This file is part of Spot, a model checking library. // @@ -209,7 +209,7 @@ namespace class xtranslator_runner final: public translator_runner { public: - xtranslator_runner(spot::bdd_dict_ptr dict) + explicit xtranslator_runner(spot::bdd_dict_ptr dict) : translator_runner(dict, true) { } @@ -224,8 +224,6 @@ namespace format(command, tools[translator_num].cmd); std::string cmd = command.str(); - //std::cerr << "Running [" << l << translator_num << "]: " - // << cmd << std::endl; timer.start(); int es = exec_with_timeout(cmd.c_str()); timer.stop(); @@ -312,7 +310,7 @@ namespace spot::printable_value inputf; public: - processor(spot::postprocessor& post) + explicit processor(spot::postprocessor& post) : runner(dict), best_printer(best_stream, best_format), post(post) { printer.add_stat('T', &cmdname); @@ -323,9 +321,7 @@ namespace best_printer.declare('f', &inputf); } - ~processor() - { - } + ~processor() override = default; int process_string(const std::string& input, diff --git a/bin/ltlfilt.cc b/bin/ltlfilt.cc index c9064368d..81e895d42 100644 --- a/bin/ltlfilt.cc +++ b/bin/ltlfilt.cc @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2012-2022 Laboratoire de Recherche et Développement +// Copyright (C) 2012-2023 Laboratoire de Recherche et Développement // de l'Epita (LRDE). // // This file is part of Spot, a model checking library. @@ -586,7 +586,7 @@ namespace fset_t unique_set; spot::relabeling_map relmap; - ltl_processor(spot::tl_simplifier& simpl) + explicit ltl_processor(spot::tl_simplifier& simpl) : simpl(simpl) { } @@ -722,7 +722,7 @@ namespace matched &= !syntactic_si || f.is_syntactic_stutter_invariant(); if (matched && (ap_n.min > 0 || ap_n.max >= 0)) { - auto s = atomic_prop_collect(f); + spot::atomic_prop_set* s = atomic_prop_collect(f); int n = s->size(); delete s; matched &= (ap_n.min <= 0) || (n >= ap_n.min); @@ -761,7 +761,7 @@ namespace aut = ltl_to_tgba_fm(f, simpl.get_dict(), true); if (matched && !opt->acc_words.empty()) - for (auto& word_aut: opt->acc_words) + for (const spot::twa_graph_ptr& word_aut: opt->acc_words) if (spot::product(aut, word_aut)->is_empty()) { matched = false; @@ -769,7 +769,7 @@ namespace } if (matched && !opt->rej_words.empty()) - for (auto& word_aut: opt->rej_words) + for (const spot::twa_graph_ptr& word_aut: opt->rej_words) if (!spot::product(aut, word_aut)->is_empty()) { matched = false; @@ -843,12 +843,12 @@ namespace { // Sort the formulas alphabetically. std::map m; - for (auto& p: relmap) - m.emplace(str_psl(p.first), p.second); - for (auto& p: m) + for (const auto& [newformula, oldname]: relmap) + m.emplace(str_psl(newformula), oldname); + for (const auto& [newname, oldname]: m) stream_formula(opt->output_define->ostream() - << "#define " << p.first << " (", - p.second, filename, + << "#define " << newname << " (", + oldname, filename, std::to_string(linenum).c_str()) << ")\n"; } one_match = true; diff --git a/bin/ltlsynt.cc b/bin/ltlsynt.cc index aaea855a4..a2ec32cd1 100644 --- a/bin/ltlsynt.cc +++ b/bin/ltlsynt.cc @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2017-2022 Laboratoire de Recherche et Développement +// Copyright (C) 2017-2023 Laboratoire de Recherche et Développement // de l'Epita (LRDE). // // This file is part of Spot, a model checking library. @@ -152,7 +152,6 @@ static const struct argp_child children[] = { { &finput_argp_headless, 0, nullptr, 0 }, { &aoutput_argp, 0, nullptr, 0 }, - //{ &aoutput_o_format_argp, 0, nullptr, 0 }, { &misc_argp, 0, nullptr, 0 }, { nullptr, 0, nullptr, 0 } }; @@ -425,10 +424,6 @@ namespace auto sub_o = sub_outs_str.begin(); std::vector mealy_machines; - auto print_game = want_game ? - [](const spot::twa_graph_ptr& game)->void { dispatch_print_hoa(game); } - : [](const spot::twa_graph_ptr&)->void{}; - for (; sub_f != sub_form.end(); ++sub_f, ++sub_o) { spot::mealy_like m_like @@ -466,9 +461,11 @@ namespace assert((spptr->at(arena->get_init_state_number()) == false) && "Env needs first turn"); } - print_game(arena); if (want_game) - continue; + { + dispatch_print_hoa(arena); + continue; + } if (!spot::solve_game(arena, *gi)) { if (show_status) @@ -625,7 +622,7 @@ namespace } static void - split_aps(std::string arg, std::vector& where) + split_aps(const std::string& arg, std::vector& where) { std::istringstream aps(arg); std::string ap; diff --git a/bin/randltl.cc b/bin/randltl.cc index 986c437c1..749fcf373 100644 --- a/bin/randltl.cc +++ b/bin/randltl.cc @@ -1,6 +1,6 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2012-2016, 2018-2019, 2022 Laboratoire de Recherche -// et Développement de l'Epita (LRDE). +// Copyright (C) 2012-2016, 2018-2019, 2022, 2023 Laboratoire de +// Recherche et Développement de l'Epita (LRDE). // // This file is part of Spot, a model checking library. // @@ -65,7 +65,6 @@ enum { OPT_DUMP_PRIORITIES, OPT_DUPS, OPT_LTL_PRIORITIES, - OPT_PSL_PRIORITIES, OPT_SEED, OPT_SERE_PRIORITIES, OPT_TREE_SIZE, @@ -194,7 +193,6 @@ parse_opt(int key, char* arg, struct argp_state* as) case OPT_DUMP_PRIORITIES: opt_dump_priorities = true; break; - // case OPT_PSL_PRIORITIES: break; case OPT_SERE_PRIORITIES: opt_pS = arg; break; From 7e1d68479762a9ce5d66ec226d42ab23d0b38cbd Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Mon, 23 Jan 2023 11:59:49 +0100 Subject: [PATCH 240/337] dbranch: fix handling of states without successors MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Fixes #524, reported by Rüdiger Ehlers. * spot/twaalgos/dbranch.cc: When merging an edge going to state without successors simply delete it. * bin/spot-x.cc: Typo in documentation. * tests/core/ltlcross.test: Add a test case. * NEWS: Mention the bug. --- NEWS | 4 ++++ bin/spot-x.cc | 4 ++-- spot/twaalgos/dbranch.cc | 39 +++++++++++++++++++++------------------ tests/core/ltlcross.test | 5 ++++- 4 files changed, 31 insertions(+), 21 deletions(-) diff --git a/NEWS b/NEWS index 9775339fe..e6d484f12 100644 --- a/NEWS +++ b/NEWS @@ -10,6 +10,10 @@ New in spot 2.11.3.dev (not yet released) multiple initial states (because Spot supports only one), the HOA parser could break state-based acceptance. (Issue #522.) + - delay_branching_here(), a new optimization of Spot 2.11 had an + incorrect handling of states without successors, causing some + segfaults. (Issue #524.) + New in spot 2.11.3 (2022-12-09) Bug fixes: diff --git a/bin/spot-x.cc b/bin/spot-x.cc index 1edb3f54e..964710dc1 100644 --- a/bin/spot-x.cc +++ b/bin/spot-x.cc @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2013-2022 Laboratoire de Recherche et Développement +// Copyright (C) 2013-2023 Laboratoire de Recherche et Développement // de l'Epita (LRDE). // // This file is part of Spot, a model checking library. @@ -53,7 +53,7 @@ implication-based simplifications are attempted. Defaults to 16.") }, { nullptr, 0, nullptr, 0, "Translation options:", 0 }, { DOC("ltl-split", "Set to 0 to disable the translation of automata \ as product or sum of subformulas.") }, - { DOC("branch-prop", "Set to 0 to disable branching-postponement \ + { DOC("branch-post", "Set to 0 to disable branching-postponement \ (done during translation, may create more states) and delayed-branching \ (almost similar, but done after translation to only remove states). \ Set to 1 to force branching-postponement, and to 2 \ diff --git a/spot/twaalgos/dbranch.cc b/spot/twaalgos/dbranch.cc index 465f8326e..19a0d9474 100644 --- a/spot/twaalgos/dbranch.cc +++ b/spot/twaalgos/dbranch.cc @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2022 Laboratoire de Recherche et Développement +// Copyright (C) 2022-2023 Laboratoire de Recherche et Développement // de l'Epita (LRDE). // // This file is part of Spot, a model checking library. @@ -118,27 +118,30 @@ namespace spot continue; } unsigned mergedst = it2->second; - // we have to merge canddst into mergedst. This is as - // simple as: + // we have to merge canddst into mergedst. + // This is as simple as: // 1) connecting their list of transitions - unsigned& mergedfirst = g.state_storage(mergedst).succ; - unsigned& mergedlast = g.state_storage(mergedst).succ_tail; - unsigned& candfirst = g.state_storage(canddst).succ; unsigned& candlast = g.state_storage(canddst).succ_tail; - if (mergedlast) - aut->edge_storage(mergedlast).next_succ = candfirst; - else // mergedst had now successor - mergedfirst = candfirst; - mergedlast = candlast; - // 2) updating the source of the merged transitions - for (unsigned e2 = candfirst; e2 != 0;) + if (candlast) { - auto& edge = aut->edge_storage(e2); - edge.src = mergedst; - e2 = edge.next_succ; + unsigned& mergedfirst = g.state_storage(mergedst).succ; + unsigned& mergedlast = g.state_storage(mergedst).succ_tail; + unsigned& candfirst = g.state_storage(canddst).succ; + if (mergedlast) + aut->edge_storage(mergedlast).next_succ = candfirst; + else // mergedst had no successor + mergedfirst = candfirst; + mergedlast = candlast; + // 2) updating the source of the merged transitions + for (unsigned e2 = candfirst; e2 != 0;) + { + auto& edge = aut->edge_storage(e2); + edge.src = mergedst; + e2 = edge.next_succ; + } + // 3) deleting the edge to canddst. + candfirst = candlast = 0; } - // 3) deleting the edge to canddst. - candfirst = candlast = 0; it.erase(); // 4) updating succ_cand succ_cand[mergedst] += succ_cand[canddst]; diff --git a/tests/core/ltlcross.test b/tests/core/ltlcross.test index 1a5806ba8..ebe20fb26 100755 --- a/tests/core/ltlcross.test +++ b/tests/core/ltlcross.test @@ -1,6 +1,6 @@ #!/bin/sh # -*- coding: utf-8 -*- -# Copyright (C) 2012-2014, 2016, 2019 Laboratoire de Recherche et +# Copyright (C) 2012-2014, 2016, 2019, 2023 Laboratoire de Recherche et # Développement de l'Epita (LRDE). # # This file is part of Spot, a model checking library. @@ -65,3 +65,6 @@ ltlcross -D \ # Spot 2.8. We use ltl2tgba twice so ltlcross build cross-products. ltlcross --verbose ltl2tgba ltl2tgba \ -f '(G(F((a1)&(X(X(b1))))))&(G(F((a2)&(X(X(b2))))))&(G(F((a3)&(X(X(b3))))))' + +# Issue #524. +ltlcross ltl2tgba -f '!(X(v3 | G!v5) | ((Xv5 & !(v5 & !X!v3)) U !v5))' From eae91e97cd1b450ac75f121ac314c61691af3ff0 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Mon, 23 Jan 2023 15:25:06 +0100 Subject: [PATCH 241/337] robin_hood: update to version version 3.11.5 * spot/priv/robin_hood.hh: Update. * spot/priv/Makefile.am: Patch ROBIN_HOOD_IS_TRIVIALLY_COPYABLE to work around an issue with clang on Arch linux. --- spot/priv/Makefile.am | 10 ++++++++-- spot/priv/robin_hood.hh | 43 +++++++++++++++++++++++++++-------------- 2 files changed, 37 insertions(+), 16 deletions(-) diff --git a/spot/priv/Makefile.am b/spot/priv/Makefile.am index d4e9cc77c..7ec7e6148 100644 --- a/spot/priv/Makefile.am +++ b/spot/priv/Makefile.am @@ -1,5 +1,5 @@ ## -*- coding: utf-8 -*- -## Copyright (C) 2013-2019, 2021 Laboratoire de Recherche et +## Copyright (C) 2013-2019, 2021-2023 Laboratoire de Recherche et ## Développement de l'Epita (LRDE). ## ## This file is part of Spot, a model checking library. @@ -42,5 +42,11 @@ RH = $(GH)/robin-hood-hashing/master/src/include/robin_hood.h .PHONY: update update: wget $(RH) -O robin_hood.tmp || curl $(RH) -o robin_hood.tmp - sed 's/std::malloc/malloc/' robin_hood.tmp > $(srcdir)/robin_hood.hh +## Do not use std::malloc but malloc, because gnulib may replace it by +## rpl_malloc instead. Also disable to tests of __GNUC__ about +## ROBIN_HOOD_IS_TRIVIALLY_COPYABLE because (1) all versions of G++ we +## support have std::is_trivially_copyable, and (2) clang define +## __GNUC__ to some value that fail this test, and then warn that +## __has_trivial_copy is obsoleted. + sed 's/std::malloc/malloc/;/https:\/\/stackoverflow.com\/a\/31798726/{n;s/defined.*/false/}' robin_hood.tmp > $(srcdir)/robin_hood.hh rm -f robin_hood.tmp diff --git a/spot/priv/robin_hood.hh b/spot/priv/robin_hood.hh index 8c151d517..a4bc8beae 100644 --- a/spot/priv/robin_hood.hh +++ b/spot/priv/robin_hood.hh @@ -36,7 +36,7 @@ // see https://semver.org/ #define ROBIN_HOOD_VERSION_MAJOR 3 // for incompatible API changes #define ROBIN_HOOD_VERSION_MINOR 11 // for adding functionality in a backwards-compatible manner -#define ROBIN_HOOD_VERSION_PATCH 3 // for backwards-compatible bug fixes +#define ROBIN_HOOD_VERSION_PATCH 5 // for backwards-compatible bug fixes #include #include @@ -206,7 +206,7 @@ static Counts& counts() { // workaround missing "is_trivially_copyable" in g++ < 5.0 // See https://stackoverflow.com/a/31798726/48181 -#if defined(__GNUC__) && __GNUC__ < 5 +#if false # define ROBIN_HOOD_IS_TRIVIALLY_COPYABLE(...) __has_trivial_copy(__VA_ARGS__) #else # define ROBIN_HOOD_IS_TRIVIALLY_COPYABLE(...) std::is_trivially_copyable<__VA_ARGS__>::value @@ -1820,6 +1820,12 @@ public: InsertionState::key_found != idxAndState.second); } + template + iterator emplace_hint(const_iterator position, Args&&... args) { + (void)position; + return emplace(std::forward(args)...).first; + } + template std::pair try_emplace(const key_type& key, Args&&... args) { return try_emplace_impl(key, std::forward(args)...); @@ -1831,16 +1837,15 @@ public: } template - std::pair try_emplace(const_iterator hint, const key_type& key, - Args&&... args) { + iterator try_emplace(const_iterator hint, const key_type& key, Args&&... args) { (void)hint; - return try_emplace_impl(key, std::forward(args)...); + return try_emplace_impl(key, std::forward(args)...).first; } template - std::pair try_emplace(const_iterator hint, key_type&& key, Args&&... args) { + iterator try_emplace(const_iterator hint, key_type&& key, Args&&... args) { (void)hint; - return try_emplace_impl(std::move(key), std::forward(args)...); + return try_emplace_impl(std::move(key), std::forward(args)...).first; } template @@ -1854,16 +1859,15 @@ public: } template - std::pair insert_or_assign(const_iterator hint, const key_type& key, - Mapped&& obj) { + iterator insert_or_assign(const_iterator hint, const key_type& key, Mapped&& obj) { (void)hint; - return insertOrAssignImpl(key, std::forward(obj)); + return insertOrAssignImpl(key, std::forward(obj)).first; } template - std::pair insert_or_assign(const_iterator hint, key_type&& key, Mapped&& obj) { + iterator insert_or_assign(const_iterator hint, key_type&& key, Mapped&& obj) { (void)hint; - return insertOrAssignImpl(std::move(key), std::forward(obj)); + return insertOrAssignImpl(std::move(key), std::forward(obj)).first; } std::pair insert(const value_type& keyval) { @@ -1871,10 +1875,20 @@ public: return emplace(keyval); } + iterator insert(const_iterator hint, const value_type& keyval) { + (void)hint; + return emplace(keyval).first; + } + std::pair insert(value_type&& keyval) { return emplace(std::move(keyval)); } + iterator insert(const_iterator hint, value_type&& keyval) { + (void)hint; + return emplace(std::move(keyval)).first; + } + // Returns 1 if key is found, 0 otherwise. size_t count(const key_type& key) const { // NOLINT(modernize-use-nodiscard) ROBIN_HOOD_TRACE(this) @@ -2308,13 +2322,14 @@ private: auto const numElementsWithBuffer = calcNumElementsWithBuffer(max_elements); - // calloc also zeroes everything + // malloc & zero mInfo. Faster than calloc everything. auto const numBytesTotal = calcNumBytesTotal(numElementsWithBuffer); ROBIN_HOOD_LOG("std::calloc " << numBytesTotal << " = calcNumBytesTotal(" << numElementsWithBuffer << ")") mKeyVals = reinterpret_cast( - detail::assertNotNull(std::calloc(1, numBytesTotal))); + detail::assertNotNull(malloc(numBytesTotal))); mInfo = reinterpret_cast(mKeyVals + numElementsWithBuffer); + std::memset(mInfo, 0, numBytesTotal - numElementsWithBuffer * sizeof(Node)); // set sentinel mInfo[numElementsWithBuffer] = 1; From 9ca2927291f6338f69373683d45db686a0b5907f Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Mon, 23 Jan 2023 16:07:49 +0100 Subject: [PATCH 242/337] bin: update copyright year and laboratory name * bin/common_setup.cc: Here. --- bin/common_setup.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bin/common_setup.cc b/bin/common_setup.cc index af033a47f..c59ec0695 100644 --- a/bin/common_setup.cc +++ b/bin/common_setup.cc @@ -36,7 +36,7 @@ display_version(FILE *stream, struct argp_state*) fputs(program_name, stream); fputs(" (" PACKAGE_NAME ") " PACKAGE_VERSION "\n\ \n\ -Copyright (C) 2022 Laboratoire de Recherche et Développement de l'Epita.\n\ +Copyright (C) 2023 Laboratoire de Recherche de l'Epita (LRE)\n\ License GPLv3+: \ GNU GPL version 3 or later .\n\ This is free software: you are free to change and redistribute it.\n\ From 315872a54b037a6a1206bfeb119618f8bf64b5ae Mon Sep 17 00:00:00 2001 From: Florian Renkin Date: Fri, 20 Jan 2023 15:57:46 +0100 Subject: [PATCH 243/337] ltlsynt: typo in doc * bin/ltlsynt.cc: here --- bin/ltlsynt.cc | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/bin/ltlsynt.cc b/bin/ltlsynt.cc index a2ec32cd1..35ac4194b 100644 --- a/bin/ltlsynt.cc +++ b/bin/ltlsynt.cc @@ -102,8 +102,8 @@ static const argp_option options[] = "whether to decompose the specification as multiple output-disjoint " "problems to solve independently (enabled by default)", 0 }, { "simplify", OPT_SIMPLIFY, "no|bisim|bwoa|sat|bisim-sat|bwoa-sat", 0, - "simplification to apply to the controler (no) nothing, " - "(bisim) bisimulation-based reduction, (bwoa) bissimulation-based " + "simplification to apply to the controller (no) nothing, " + "(bisim) bisimulation-based reduction, (bwoa) bisimulation-based " "reduction with output assignment, (sat) SAT-based minimization, " "(bisim-sat) SAT after bisim, (bwoa-sat) SAT after bwoa. Defaults " "to 'bwoa'.", 0 }, From a1c02856acfd5421f16c8fa22c182f3766b75e76 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Tue, 24 Jan 2023 11:35:14 +0100 Subject: [PATCH 244/337] autfilt: allow --highlight-word to work on Fin acceptance Fixes #523. * bin/autfilt.cc: Remove the restriction. * tests/core/acc_word.test: Add test case. * NEWS: Mention the fix. --- NEWS | 4 ++++ bin/autfilt.cc | 9 ++------- tests/core/acc_word.test | 20 +++++++++++++------- 3 files changed, 19 insertions(+), 14 deletions(-) diff --git a/NEWS b/NEWS index e6d484f12..10ecce97c 100644 --- a/NEWS +++ b/NEWS @@ -10,6 +10,10 @@ New in spot 2.11.3.dev (not yet released) multiple initial states (because Spot supports only one), the HOA parser could break state-based acceptance. (Issue #522.) + - autfilt --highlight-word refused to work on automata with Fin + acceptance for historical reasons, however the code has been + perfectly able to handle this for a while. (Issue #523.) + - delay_branching_here(), a new optimization of Spot 2.11 had an incorrect handling of states without successors, causing some segfaults. (Issue #524.) diff --git a/bin/autfilt.cc b/bin/autfilt.cc index b55d1bc9f..4487fad8b 100644 --- a/bin/autfilt.cc +++ b/bin/autfilt.cc @@ -1667,13 +1667,8 @@ namespace if (!opt->hl_words.empty()) for (auto& [word_aut, color]: opt->hl_words) - { - if (aut->acc().uses_fin_acceptance()) - error(2, 0, - "--highlight-word does not yet work with Fin acceptance"); - if (auto run = spot::product(aut, word_aut)->accepting_run()) - run->project(aut)->highlight(color); - } + if (auto run = spot::product(aut, word_aut)->accepting_run()) + run->project(aut)->highlight(color); timer.stop(); if (opt->uniq) diff --git a/tests/core/acc_word.test b/tests/core/acc_word.test index 53ce4b98e..5f3b6880b 100644 --- a/tests/core/acc_word.test +++ b/tests/core/acc_word.test @@ -1,7 +1,7 @@ #!/bin/sh # -*- coding: utf-8 -*- -# Copyright (C) 2016, 2017, 2018, 2019 Laboratoire de Recherche et Développement -# de l'Epita (LRDE). +# Copyright (C) 2016-2019, 2023 Laboratoire de Recherche +# et Développement de l'Epita (LRDE). # # This file is part of Spot, a model checking library. # @@ -91,6 +91,15 @@ State: 1 EOF diff expected out +ltl2tgba -G '(GF(a & X!a) -> GF(b & XXb)) & GFc' > aut.hoa +word='!a&!c;cycle{!a&b&!c;!a&c;!a&b&c}' +autfilt -H1.1 aut.hoa --highlight-word="$word" > out.hoa +grep spot.highlight.edges out.hoa >out.edges +cat >expected <stderr && exit 1 -test $? -eq 2 -grep 'highlight-word.*Fin' stderr - +# highlight-word used not to work with Fin acceptance, but it's ok now +ltl2tgba -G -D 'FGa' | autfilt --highlight-word='cycle{a}' ltlfilt -f 'GFa' --accept-word 'cycle{!a}' && exit 1 ltlfilt -f 'GF!a' --accept-word 'cycle{!a}' From 126d9bc103b238a910b26cc54523883b42cba607 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Tue, 24 Jan 2023 15:48:06 +0100 Subject: [PATCH 245/337] bin: fix number conversion routines on 32bit On 32bit archetectures, long int = int the current check for detecting values that overflow int will fail. Conversion routings should check errno. * bin/common_conv.cc, bin/common_range.cc: Here. --- bin/common_conv.cc | 12 ++++++++---- bin/common_range.cc | 6 ++++-- 2 files changed, 12 insertions(+), 6 deletions(-) diff --git a/bin/common_conv.cc b/bin/common_conv.cc index 02b1815fd..b23a67c51 100644 --- a/bin/common_conv.cc +++ b/bin/common_conv.cc @@ -25,12 +25,13 @@ int to_int(const char* s, const char* where) { char* endptr; + errno = 0; long int lres = strtol(s, &endptr, 10); if (*endptr) error(2, 0, "failed to parse '%s' as an integer (in argument of %s).", s, where); int res = lres; - if (res != lres) + if (res != lres || errno == ERANGE) error(2, 0, "value '%s' is too large for an int (in argument of %s).", s, where); return res; @@ -49,13 +50,14 @@ unsigned to_unsigned (const char *s, const char* where) { char* endptr; + errno = 0; unsigned long lres = strtoul(s, &endptr, 10); if (*endptr) error(2, 0, "failed to parse '%s' as an unsigned integer (in argument of %s).", s, where); unsigned res = lres; - if (res != lres) + if (res != lres || errno == ERANGE) error(2, 0, "value '%s' is too large for a unsigned int (in argument of %s).", s, where); @@ -66,8 +68,9 @@ float to_float(const char* s, const char* where) { char* endptr; + errno = 0; float res = strtof(s, &endptr); - if (*endptr) + if (*endptr || errno == ERANGE) error(2, 0, "failed to parse '%s' as a float (in argument of %s)", s, where); return res; @@ -89,8 +92,9 @@ to_longs(const char* arg) while (*arg) { char* endptr; + errno = 0; long value = strtol(arg, &endptr, 10); - if (endptr == arg) + if (endptr == arg || errno) error(2, 0, "failed to parse '%s' as an integer.", arg); res.push_back(value); while (*endptr == ' ' || *endptr == ',') diff --git a/bin/common_range.cc b/bin/common_range.cc index 9419cc389..98e568b41 100644 --- a/bin/common_range.cc +++ b/bin/common_range.cc @@ -36,9 +36,10 @@ parse_range(const char* str, int missing_left, int missing_right) { range res; char* end; + errno = 0; long lres = strtol(str, &end, 10); res.min = lres; - if (res.min != lres) + if (res.min != lres || errno == ERANGE) error(2, 0, "start of range '%s' is too large for an int.", str); if (end == str) { @@ -69,9 +70,10 @@ parse_range(const char* str, int missing_left, int missing_right) { // Parse the next integer. char* end2; + errno = 0; lres = strtol(end, &end2, 10); res.max = lres; - if (res.max != lres) + if (res.max != lres || errno == ERANGE) error(2, 0, "end of range '%s' is too large for an int.", str); if (str == end2) error(2, 0, "invalid range '%s' " From 26660728674567f0a0ff97e1eb5a34aa38939955 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Tue, 24 Jan 2023 15:54:39 +0100 Subject: [PATCH 246/337] * .gitlab-ci.yml: Use pipeline id to name volumes. --- .gitlab-ci.yml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index a2006ee7f..348bacba1 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -276,7 +276,7 @@ debpkg-stable: - stable script: - docker pull gitlab-registry.lre.epita.fr/spot/buildenv/debian:stable - - vol=spot-stable-$CI_COMMIT_SHA + - vol=spot-stable-$CI_COMMIT_SHA-$CI_PIPELINE_ID - docker volume create $vol - exitcode=0 - docker run -v $vol:/build/result --name helper-$vol gitlab-registry.lre.epita.fr/spot/buildenv/debian:stable ./build-spot.sh $CI_COMMIT_REF_NAME -j${NBPROC-1} || exitcode=$? @@ -304,7 +304,7 @@ debpkg-stable-i386: needs: ["debpkg-stable"] script: - docker pull gitlab-registry.lre.epita.fr/spot/buildenv/debian-i386:stable - - vol=spot-stable-$CI_COMMIT_SHA + - vol=spot-stable-$CI_COMMIT_SHA-$CI_PIPELINE_ID - docker volume create $vol - exitcode=0 - docker create -v $vol:/build/result --name helper-$vol gitlab-registry.lre.epita.fr/spot/buildenv/debian-i386:stable ./bin-spot.sh -j${NBPROC-1} || exitcode=$? @@ -331,7 +331,7 @@ debpkg-unstable: - next script: - docker pull gitlab-registry.lre.epita.fr/spot/buildenv/debian - - vol=spot-unstable-$CI_COMMIT_SHA + - vol=spot-unstable-$CI_COMMIT_SHA-$CI_PIPELINE_ID - docker volume create $vol - exitcode=0 - docker run -v $vol:/build/result --name helper-$vol gitlab-registry.lre.epita.fr/spot/buildenv/debian ./build-spot.sh $CI_COMMIT_REF_NAME -j${NBPROC-1} || exitcode=$? @@ -357,7 +357,7 @@ debpkg-unstable-i386: needs: ["debpkg-unstable"] script: - docker pull gitlab-registry.lre.epita.fr/spot/buildenv/debian-i386 - - vol=spot-unstable-$CI_COMMIT_SHA + - vol=spot-unstable-$CI_COMMIT_SHA-$CI_PIPELINE_ID - docker volume create $vol - exitcode=0 - docker create -v $vol:/build/result --name helper-$vol gitlab-registry.lre.epita.fr/spot/buildenv/debian-i386 ./bin-spot.sh -j${NBPROC-1} || exitcode=$? From 5969aa4925e52fbfd07e0edc6933fed3faf7ae20 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Mon, 30 Jan 2023 17:51:48 +0100 Subject: [PATCH 247/337] work around gcc-snapshot warnings about dangling references * spot/twaalgos/game.hh, spot/twaalgos/game.cc (get_state_players, get_strategy, get_state_winners): Take argument by reference, not copy. * spot/twaalgos/synthesis.cc, spot/twaalgos/mealy_machine.cc: Replace auto by actual type for readability. --- spot/twaalgos/game.cc | 17 ++++++++++++++--- spot/twaalgos/game.hh | 12 ++++++++---- spot/twaalgos/mealy_machine.cc | 8 ++++---- spot/twaalgos/synthesis.cc | 10 +++++----- 4 files changed, 31 insertions(+), 16 deletions(-) diff --git a/spot/twaalgos/game.cc b/spot/twaalgos/game.cc index df259b84a..17f94a7e4 100644 --- a/spot/twaalgos/game.cc +++ b/spot/twaalgos/game.cc @@ -1056,7 +1056,18 @@ namespace spot (*owners)[state] = owner; } - const region_t& get_state_players(const_twa_graph_ptr arena) + const region_t& get_state_players(const const_twa_graph_ptr& arena) + { + region_t *owners = arena->get_named_prop + ("state-player"); + if (!owners) + throw std::runtime_error + ("get_state_players(): state-player property not defined, not a game?"); + + return *owners; + } + + const region_t& get_state_players(twa_graph_ptr& arena) { region_t *owners = arena->get_named_prop ("state-player"); @@ -1081,7 +1092,7 @@ namespace spot } - const strategy_t& get_strategy(const_twa_graph_ptr arena) + const strategy_t& get_strategy(const const_twa_graph_ptr& arena) { auto strat_ptr = arena->get_named_prop("strategy"); if (!strat_ptr) @@ -1174,7 +1185,7 @@ namespace spot (*winners)[state] = winner; } - const region_t& get_state_winners(const_twa_graph_ptr arena) + const region_t& get_state_winners(const const_twa_graph_ptr& arena) { region_t *winners = arena->get_named_prop("state-winner"); if (!winners) diff --git a/spot/twaalgos/game.hh b/spot/twaalgos/game.hh index df5d27439..dbaccce75 100644 --- a/spot/twaalgos/game.hh +++ b/spot/twaalgos/game.hh @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2017-2022 Laboratoire de Recherche et Développement +// Copyright (C) 2017-2023 Laboratoire de Recherche et Développement // de l'Epita (LRDE). // // This file is part of Spot, a model checking library. @@ -163,14 +163,18 @@ namespace spot /// \ingroup games /// \brief Get the owner of all states + ///@{ SPOT_API - const region_t& get_state_players(const_twa_graph_ptr arena); + const region_t& get_state_players(const const_twa_graph_ptr& arena); + SPOT_API + const region_t& get_state_players(twa_graph_ptr& arena); + ///@} /// \ingroup games /// \brief Get or set the strategy /// @{ SPOT_API - const strategy_t& get_strategy(const_twa_graph_ptr arena); + const strategy_t& get_strategy(const const_twa_graph_ptr& arena); SPOT_API void set_strategy(twa_graph_ptr arena, const strategy_t& strat); SPOT_API @@ -214,5 +218,5 @@ namespace spot /// \ingroup games /// \brief Get the winner of all states SPOT_API - const region_t& get_state_winners(const_twa_graph_ptr arena); + const region_t& get_state_winners(const const_twa_graph_ptr& arena); } diff --git a/spot/twaalgos/mealy_machine.cc b/spot/twaalgos/mealy_machine.cc index 1126ad8e0..386e44126 100644 --- a/spot/twaalgos/mealy_machine.cc +++ b/spot/twaalgos/mealy_machine.cc @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2021, 2022 Laboratoire de Recherche et Développement +// Copyright (C) 2021, 2022, 2023 Laboratoire de Recherche et Développement // de l'Epita (LRDE). // // This file is part of Spot, a model checking library. @@ -3849,7 +3849,7 @@ namespace spot // 0 -> "Env" next is input props // 1 -> "Player" next is output prop - const auto& spref = get_state_players(mmw); + const region_t& spref = get_state_players(mmw); assert((spref.size() == mmw->num_states()) && "Inconsistent state players"); @@ -3989,9 +3989,9 @@ namespace spot const unsigned initl = left->get_init_state_number(); const unsigned initr = right->get_init_state_number(); - auto& spr = get_state_players(right); + const region_t& spr = get_state_players(right); #ifndef NDEBUG - auto& spl = get_state_players(left); + const region_t& spl = get_state_players(left); // todo auto check_out = [](const const_twa_graph_ptr& aut, const auto& sp) diff --git a/spot/twaalgos/synthesis.cc b/spot/twaalgos/synthesis.cc index 88e22ff04..494cc0f1f 100644 --- a/spot/twaalgos/synthesis.cc +++ b/spot/twaalgos/synthesis.cc @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2020-2022 Laboratoire de Recherche et +// Copyright (C) 2020-2023 Laboratoire de Recherche et // Développement de l'Epita (LRDE). // // This file is part of Spot, a model checking library. @@ -137,12 +137,12 @@ namespace{ // Note, this only deals with deterministic strategies // Note, assumes that env starts playing twa_graph_ptr - apply_strategy(const twa_graph_ptr& arena, + apply_strategy(const const_twa_graph_ptr& arena, bool unsplit, bool keep_acc) { - const auto& win = get_state_winners(arena); - const auto& strat = get_strategy(arena); - const auto& sp = get_state_players(arena); + const region_t& win = get_state_winners(arena); + const strategy_t& strat = get_strategy(arena); + const region_t& sp = get_state_players(arena); auto outs = get_synthesis_outputs(arena); if (!win[arena->get_init_state_number()]) From 43b4d80da14dccbd2740a1595f418ad6a3f84ae8 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Fri, 3 Feb 2023 09:35:46 +0100 Subject: [PATCH 248/337] dbranch: fix handling of state-based acceptance Fixes issue #525. * spot/twaalgos/dbranch.hh, NEWS: Document. * spot/twaalgos/dbranch.cc: Detect cases where the acceptance should be changed from state-based to transition-based. * tests/python/dbranch.py: Add a test case. --- NEWS | 4 ++++ spot/twaalgos/dbranch.cc | 19 +++++++++++++++++-- spot/twaalgos/dbranch.hh | 15 ++++++++++----- tests/python/dbranch.py | 29 ++++++++++++++++++++++++++++- 4 files changed, 59 insertions(+), 8 deletions(-) diff --git a/NEWS b/NEWS index 4d9d1def2..e8774df45 100644 --- a/NEWS +++ b/NEWS @@ -32,6 +32,10 @@ New in spot 2.11.3.dev (not yet released) incorrectly handling of states without successors, causing some segfaults. (Issue #524.) + - Running delay_branching_here() on state-based automata (this was not + done in Spot so far) may require the output to use transition-based + acceptance. (Issue #525.) + New in spot 2.11.3 (2022-12-09) Bug fixes: diff --git a/spot/twaalgos/dbranch.cc b/spot/twaalgos/dbranch.cc index 19a0d9474..7cf1b262e 100644 --- a/spot/twaalgos/dbranch.cc +++ b/spot/twaalgos/dbranch.cc @@ -66,6 +66,10 @@ namespace spot hashmap_t first_dest[1 + is_game]; auto& g = aut->get_graph(); + // Merging outgoing transitions may cause the automaton to need + // transition-based acceptance. + bool need_trans = !aut->prop_state_acc().is_true(); + // setup a DFS std::vector seen(ns); std::stack todo; @@ -128,9 +132,18 @@ namespace spot unsigned& mergedlast = g.state_storage(mergedst).succ_tail; unsigned& candfirst = g.state_storage(canddst).succ; if (mergedlast) - aut->edge_storage(mergedlast).next_succ = candfirst; + { + aut->edge_storage(mergedlast).next_succ = candfirst; + // Do we need to require transition-based acceptance? + if (!need_trans) + need_trans = + (aut->edge_storage(candfirst).acc + != aut->edge_storage(mergedfirst).acc); + } else // mergedst had no successor - mergedfirst = candfirst; + { + mergedfirst = candfirst; + } mergedlast = candlast; // 2) updating the source of the merged transitions for (unsigned e2 = candfirst; e2 != 0;) @@ -149,6 +162,8 @@ namespace spot changed = true; } } + if (need_trans) + aut->prop_state_acc(false); return changed; } } diff --git a/spot/twaalgos/dbranch.hh b/spot/twaalgos/dbranch.hh index 9cd0efa5e..022c1a75b 100644 --- a/spot/twaalgos/dbranch.hh +++ b/spot/twaalgos/dbranch.hh @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2022 Laboratoire de Recherche et Développement +// Copyright (C) 2022, 2023 Laboratoire de Recherche et Développement // de l'Epita (LRDE). // // This file is part of Spot, a model checking library. @@ -26,10 +26,15 @@ namespace spot /// \ingroup twa_algorithms /// \brief Merge states to delay /// - /// If a state (x) has two outgoing transitions (x,l,m,y) and - /// (x,l,m,z) going to states (x) and (y) that have no other - /// incoming edges, then (y) and (z) can be merged (keeping the - /// union of their outgoing destinations). + /// In an automaton with transition-based acceptance, if a state (x) + /// has two outgoing transitions (x,l,m,y) and (x,l,m,z) going to + /// states (x) and (y) that have no other incoming edges, then (y) + /// and (z) can be merged (keeping the union of their outgoing + /// destinations). + /// + /// If the input automaton uses state-based acceptance, running this + /// function might make the acceptance transition-based, but only if + /// two states with different acceptance are merged at some point. /// /// \return true iff the automaton was modified. SPOT_API bool delay_branching_here(const twa_graph_ptr& aut); diff --git a/tests/python/dbranch.py b/tests/python/dbranch.py index ecf17d7d0..268c4a3c6 100644 --- a/tests/python/dbranch.py +++ b/tests/python/dbranch.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Copyright (C) 2022 Laboratoire de Recherche et +# Copyright (C) 2022, 2023 Laboratoire de Recherche et # Développement de l'Epita (LRDE). # # This file is part of Spot, a model checking library. @@ -145,3 +145,30 @@ State: 5 State: 6 [t] 6 --END--""") + +# Running delay_branching_here on state-based acceptance may require +# the output to use transition-based acceptance. (Issue #525.) +a = spot.automaton(""" +HOA: v1 States: 4 Start: 0 AP: 2 "a" "b" Acceptance: 1 Inf(0) --BODY-- +State: 0 [0] 1 [0] 2 State: 1 [1] 3 State: 2 {0} [!1] 3 State: 3 [t] 0 +--END--""") +copy = spot.make_twa_graph(a, spot.twa_prop_set.all()) +if spot.delay_branching_here(a): + a.purge_unreachable_states() +tc.assertTrue(spot.are_equivalent(a, copy)) +tc.assertEqual(a.to_str(), """HOA: v1 +States: 3 +Start: 0 +AP: 2 "b" "a" +acc-name: Buchi +Acceptance: 1 Inf(0) +properties: trans-labels explicit-labels trans-acc deterministic +--BODY-- +State: 0 +[1] 1 +State: 1 +[0] 2 +[!0] 2 {0} +State: 2 +[t] 0 +--END--""") From 058975c167bfe9af29475e593b133e4f249f4760 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Fri, 3 Feb 2023 09:35:46 +0100 Subject: [PATCH 249/337] dbranch: fix handling of state-based acceptance Fixes issue #525. * spot/twaalgos/dbranch.hh, NEWS: Document. * spot/twaalgos/dbranch.cc: Detect cases where the acceptance should be changed from state-based to transition-based. * tests/python/dbranch.py: Add a test case. --- NEWS | 4 ++++ spot/twaalgos/dbranch.cc | 19 +++++++++++++++++-- spot/twaalgos/dbranch.hh | 15 ++++++++++----- tests/python/dbranch.py | 29 ++++++++++++++++++++++++++++- 4 files changed, 59 insertions(+), 8 deletions(-) diff --git a/NEWS b/NEWS index 10ecce97c..c734fb995 100644 --- a/NEWS +++ b/NEWS @@ -18,6 +18,10 @@ New in spot 2.11.3.dev (not yet released) incorrect handling of states without successors, causing some segfaults. (Issue #524.) + - Running delay_branching_here() on state-based automata (this was not + done in Spot so far) may require the output to use transition-based + acceptance. (Issue #525.) + New in spot 2.11.3 (2022-12-09) Bug fixes: diff --git a/spot/twaalgos/dbranch.cc b/spot/twaalgos/dbranch.cc index 19a0d9474..7cf1b262e 100644 --- a/spot/twaalgos/dbranch.cc +++ b/spot/twaalgos/dbranch.cc @@ -66,6 +66,10 @@ namespace spot hashmap_t first_dest[1 + is_game]; auto& g = aut->get_graph(); + // Merging outgoing transitions may cause the automaton to need + // transition-based acceptance. + bool need_trans = !aut->prop_state_acc().is_true(); + // setup a DFS std::vector seen(ns); std::stack todo; @@ -128,9 +132,18 @@ namespace spot unsigned& mergedlast = g.state_storage(mergedst).succ_tail; unsigned& candfirst = g.state_storage(canddst).succ; if (mergedlast) - aut->edge_storage(mergedlast).next_succ = candfirst; + { + aut->edge_storage(mergedlast).next_succ = candfirst; + // Do we need to require transition-based acceptance? + if (!need_trans) + need_trans = + (aut->edge_storage(candfirst).acc + != aut->edge_storage(mergedfirst).acc); + } else // mergedst had no successor - mergedfirst = candfirst; + { + mergedfirst = candfirst; + } mergedlast = candlast; // 2) updating the source of the merged transitions for (unsigned e2 = candfirst; e2 != 0;) @@ -149,6 +162,8 @@ namespace spot changed = true; } } + if (need_trans) + aut->prop_state_acc(false); return changed; } } diff --git a/spot/twaalgos/dbranch.hh b/spot/twaalgos/dbranch.hh index 9cd0efa5e..022c1a75b 100644 --- a/spot/twaalgos/dbranch.hh +++ b/spot/twaalgos/dbranch.hh @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2022 Laboratoire de Recherche et Développement +// Copyright (C) 2022, 2023 Laboratoire de Recherche et Développement // de l'Epita (LRDE). // // This file is part of Spot, a model checking library. @@ -26,10 +26,15 @@ namespace spot /// \ingroup twa_algorithms /// \brief Merge states to delay /// - /// If a state (x) has two outgoing transitions (x,l,m,y) and - /// (x,l,m,z) going to states (x) and (y) that have no other - /// incoming edges, then (y) and (z) can be merged (keeping the - /// union of their outgoing destinations). + /// In an automaton with transition-based acceptance, if a state (x) + /// has two outgoing transitions (x,l,m,y) and (x,l,m,z) going to + /// states (x) and (y) that have no other incoming edges, then (y) + /// and (z) can be merged (keeping the union of their outgoing + /// destinations). + /// + /// If the input automaton uses state-based acceptance, running this + /// function might make the acceptance transition-based, but only if + /// two states with different acceptance are merged at some point. /// /// \return true iff the automaton was modified. SPOT_API bool delay_branching_here(const twa_graph_ptr& aut); diff --git a/tests/python/dbranch.py b/tests/python/dbranch.py index ecf17d7d0..268c4a3c6 100644 --- a/tests/python/dbranch.py +++ b/tests/python/dbranch.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Copyright (C) 2022 Laboratoire de Recherche et +# Copyright (C) 2022, 2023 Laboratoire de Recherche et # Développement de l'Epita (LRDE). # # This file is part of Spot, a model checking library. @@ -145,3 +145,30 @@ State: 5 State: 6 [t] 6 --END--""") + +# Running delay_branching_here on state-based acceptance may require +# the output to use transition-based acceptance. (Issue #525.) +a = spot.automaton(""" +HOA: v1 States: 4 Start: 0 AP: 2 "a" "b" Acceptance: 1 Inf(0) --BODY-- +State: 0 [0] 1 [0] 2 State: 1 [1] 3 State: 2 {0} [!1] 3 State: 3 [t] 0 +--END--""") +copy = spot.make_twa_graph(a, spot.twa_prop_set.all()) +if spot.delay_branching_here(a): + a.purge_unreachable_states() +tc.assertTrue(spot.are_equivalent(a, copy)) +tc.assertEqual(a.to_str(), """HOA: v1 +States: 3 +Start: 0 +AP: 2 "b" "a" +acc-name: Buchi +Acceptance: 1 Inf(0) +properties: trans-labels explicit-labels trans-acc deterministic +--BODY-- +State: 0 +[1] 1 +State: 1 +[0] 2 +[!0] 2 {0} +State: 2 +[t] 0 +--END--""") From a117fe1a22d1735995a9f032c6372611b96e5abc Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Tue, 7 Feb 2023 14:40:20 +0100 Subject: [PATCH 250/337] to_finit: fix issue #526 * spot/twaalgos/remprop.cc: Use bdd_restrict instead of bdd_exists. * tests/core/ltlf.test: Add test case. * NEWS: Mention the bug. --- NEWS | 4 ++++ spot/twaalgos/remprop.cc | 6 ++--- tests/core/ltlf.test | 51 +++++++++++++++++++++++++++++++++++++++- 3 files changed, 57 insertions(+), 4 deletions(-) diff --git a/NEWS b/NEWS index e8774df45..2ffc85d38 100644 --- a/NEWS +++ b/NEWS @@ -36,6 +36,10 @@ New in spot 2.11.3.dev (not yet released) done in Spot so far) may require the output to use transition-based acceptance. (Issue #525.) + - to_finite(), introduce in 2.11, had a bug that could break the + completeness of automata and trigger an exception from the HOA + printer. (Issue #526.) + New in spot 2.11.3 (2022-12-09) Bug fixes: diff --git a/spot/twaalgos/remprop.cc b/spot/twaalgos/remprop.cc index 942a1b4b5..8d4be8fbc 100644 --- a/spot/twaalgos/remprop.cc +++ b/spot/twaalgos/remprop.cc @@ -1,6 +1,6 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2015-2019, 2022 Laboratoire de Recherche et Développement -// de l'Epita (LRDE). +// Copyright (C) 2015-2019, 2022, 2023 Laboratoire de Recherche et +// Développement de l'Epita (LRDE). // // This file is part of Spot, a model checking library. // @@ -205,7 +205,7 @@ namespace spot } else { - e.cond = bdd_exist(e.cond, rem); + e.cond = bdd_restrict(e.cond, rem); } } diff --git a/tests/core/ltlf.test b/tests/core/ltlf.test index 11f2132ac..74a2da79e 100755 --- a/tests/core/ltlf.test +++ b/tests/core/ltlf.test @@ -1,6 +1,6 @@ #! /bin/sh # -*- coding: utf-8 -*- -# Copyright (C) 2022 Laboratoire de Recherche et Développement de +# Copyright (C) 2022, 2023 Laboratoire de Recherche et Développement de # l'Epita (LRDE). # # This file is part of Spot, a model checking library. @@ -173,3 +173,52 @@ grep -v '\[f\]' out4 > out3 cmp out3 out4 && exit 1 # make sure we did remove something autfilt out3 > out4 diff out4 expected3 + +# Issue #526 +ltlfilt -f '(i->XXo)|G(i<->Xo2)' --from-ltlf | ltl2tgba -D |\ + autfilt -C --to-finite > out +cat >exp < Date: Tue, 7 Feb 2023 14:40:20 +0100 Subject: [PATCH 251/337] to_finit: fix issue #526 * spot/twaalgos/remprop.cc: Use bdd_restrict instead of bdd_exists. * tests/core/ltlf.test: Add test case. * NEWS: Mention the bug. --- NEWS | 4 ++++ spot/twaalgos/remprop.cc | 6 ++--- tests/core/ltlf.test | 51 +++++++++++++++++++++++++++++++++++++++- 3 files changed, 57 insertions(+), 4 deletions(-) diff --git a/NEWS b/NEWS index c734fb995..0886eb41d 100644 --- a/NEWS +++ b/NEWS @@ -22,6 +22,10 @@ New in spot 2.11.3.dev (not yet released) done in Spot so far) may require the output to use transition-based acceptance. (Issue #525.) + - to_finite(), introduce in 2.11, had a bug that could break the + completeness of automata and trigger an exception from the HOA + printer. (Issue #526.) + New in spot 2.11.3 (2022-12-09) Bug fixes: diff --git a/spot/twaalgos/remprop.cc b/spot/twaalgos/remprop.cc index 942a1b4b5..8d4be8fbc 100644 --- a/spot/twaalgos/remprop.cc +++ b/spot/twaalgos/remprop.cc @@ -1,6 +1,6 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2015-2019, 2022 Laboratoire de Recherche et Développement -// de l'Epita (LRDE). +// Copyright (C) 2015-2019, 2022, 2023 Laboratoire de Recherche et +// Développement de l'Epita (LRDE). // // This file is part of Spot, a model checking library. // @@ -205,7 +205,7 @@ namespace spot } else { - e.cond = bdd_exist(e.cond, rem); + e.cond = bdd_restrict(e.cond, rem); } } diff --git a/tests/core/ltlf.test b/tests/core/ltlf.test index 11f2132ac..74a2da79e 100755 --- a/tests/core/ltlf.test +++ b/tests/core/ltlf.test @@ -1,6 +1,6 @@ #! /bin/sh # -*- coding: utf-8 -*- -# Copyright (C) 2022 Laboratoire de Recherche et Développement de +# Copyright (C) 2022, 2023 Laboratoire de Recherche et Développement de # l'Epita (LRDE). # # This file is part of Spot, a model checking library. @@ -173,3 +173,52 @@ grep -v '\[f\]' out4 > out3 cmp out3 out4 && exit 1 # make sure we did remove something autfilt out3 > out4 diff out4 expected3 + +# Issue #526 +ltlfilt -f '(i->XXo)|G(i<->Xo2)' --from-ltlf | ltl2tgba -D |\ + autfilt -C --to-finite > out +cat >exp < Date: Fri, 10 Feb 2023 08:49:26 +0100 Subject: [PATCH 252/337] Release spot 2.11.4 * NEWS, configure.ac, doc/org/setup.org: Update version. --- NEWS | 2 +- configure.ac | 4 ++-- doc/org/setup.org | 8 ++++---- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/NEWS b/NEWS index 0886eb41d..7a8ab4e46 100644 --- a/NEWS +++ b/NEWS @@ -1,4 +1,4 @@ -New in spot 2.11.3.dev (not yet released) +New in spot 2.11.4 (2023-02-10) Python: diff --git a/configure.ac b/configure.ac index 68fe4cab7..4643c0b66 100644 --- a/configure.ac +++ b/configure.ac @@ -1,5 +1,5 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2008-2022, Laboratoire de Recherche et Développement +# Copyright (C) 2008-2023, Laboratoire de Recherche et Développement # de l'Epita (LRDE). # Copyright (C) 2003-2007 Laboratoire d'Informatique de Paris 6 # (LIP6), département Systèmes Répartis Coopératifs (SRC), Université @@ -21,7 +21,7 @@ # along with this program. If not, see . AC_PREREQ([2.69]) -AC_INIT([spot], [2.11.3.dev], [spot@lrde.epita.fr]) +AC_INIT([spot], [2.11.4], [spot@lrde.epita.fr]) AC_CONFIG_AUX_DIR([tools]) AC_CONFIG_MACRO_DIR([m4]) AM_INIT_AUTOMAKE([1.11 gnu tar-ustar color-tests parallel-tests]) diff --git a/doc/org/setup.org b/doc/org/setup.org index 78091ea45..7b6a4fa70 100644 --- a/doc/org/setup.org +++ b/doc/org/setup.org @@ -1,11 +1,11 @@ #+OPTIONS: H:2 num:nil toc:t html-postamble:nil ^:nil #+EMAIL: spot@lrde.epita.fr #+HTML_LINK_HOME: index.html -#+MACRO: SPOTVERSION 2.11.3 -#+MACRO: LASTRELEASE 2.11.3 -#+MACRO: LASTTARBALL [[http://www.lrde.epita.fr/dload/spot/spot-2.11.3.tar.gz][=spot-2.11.3.tar.gz=]] +#+MACRO: SPOTVERSION 2.11.4 +#+MACRO: LASTRELEASE 2.11.4 +#+MACRO: LASTTARBALL [[http://www.lrde.epita.fr/dload/spot/spot-2.11.3.tar.gz][=spot-2.11.4.tar.gz=]] #+MACRO: LASTNEWS [[https://gitlab.lre.epita.fr/spot/spot/blob/spot-2-11-3/NEWS][summary of the changes]] -#+MACRO: LASTDATE 2022-12-09 +#+MACRO: LASTDATE 2023-02-10 #+ATTR_HTML: :id spotlogo [[file:spot2.svg]] From e44cb5152aeebada10578f7bbd2788b701edf0da Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Fri, 10 Feb 2023 08:51:29 +0100 Subject: [PATCH 253/337] Bump version to 2.11.4.dev * NEWS, configure.ac: Here. --- NEWS | 4 ++++ configure.ac | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index 7a8ab4e46..7f3be814d 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,7 @@ +New in spot 2.11.4.dev (not yet released) + + Nothing yet. + New in spot 2.11.4 (2023-02-10) Python: diff --git a/configure.ac b/configure.ac index 4643c0b66..e47e2eb29 100644 --- a/configure.ac +++ b/configure.ac @@ -21,7 +21,7 @@ # along with this program. If not, see . AC_PREREQ([2.69]) -AC_INIT([spot], [2.11.4], [spot@lrde.epita.fr]) +AC_INIT([spot], [2.11.4.dev], [spot@lrde.epita.fr]) AC_CONFIG_AUX_DIR([tools]) AC_CONFIG_MACRO_DIR([m4]) AM_INIT_AUTOMAKE([1.11 gnu tar-ustar color-tests parallel-tests]) From 4bd023e51510d9b64cb516c9b7120e0b8f741b7e Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Thu, 16 Feb 2023 17:46:51 +0100 Subject: [PATCH 254/337] org: do not require org-install org-install has been obsolete for a long time, and has been removed from Org 9.6. * doc/org/init.el.in: Remove org-install. --- doc/org/init.el.in | 1 - 1 file changed, 1 deletion(-) diff --git a/doc/org/init.el.in b/doc/org/init.el.in index 9f589bb35..5e51c7250 100644 --- a/doc/org/init.el.in +++ b/doc/org/init.el.in @@ -51,7 +51,6 @@ (package-install ess))))) (require 'ox-publish) -(require 'org-install) (require 'hoa-mode) ; See https://github.com/emacs-ess/ESS/issues/1052 From 8a5b86521cd80c3542ea8d787ed2f9089dec028c Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Thu, 16 Feb 2023 17:48:49 +0100 Subject: [PATCH 255/337] * NEWS: Remove duplicate entries. --- NEWS | 22 ---------------------- 1 file changed, 22 deletions(-) diff --git a/NEWS b/NEWS index c3d646fe1..3cfa6edc2 100644 --- a/NEWS +++ b/NEWS @@ -18,28 +18,6 @@ New in spot 2.11.4.dev (not yet released) - spot.acd() no longer depends on jQuery for interactivity. - Bug fixes: - - - When merging initial states from state-based automata with - multiple initial states (because Spot supports only one), the HOA - parser could break state-based acceptance. (Issue #522.) - - - autfilt --highlight-word refused to work on automata with Fin - acceptance for historical reason, but the cose is perfectly able - to handle this now. (Issue #523.) - - - delay_branching_here(), a new optimization of Spot 2.11 had an - incorrectly handling of states without successors, causing some - segfaults. (Issue #524.) - - - Running delay_branching_here() on state-based automata (this was not - done in Spot so far) may require the output to use transition-based - acceptance. (Issue #525.) - - - to_finite(), introduce in 2.11, had a bug that could break the - completeness of automata and trigger an exception from the HOA - printer. (Issue #526.) - New in spot 2.11.4 (2023-02-10) Python: From 66839b1a2920ef6f219e248beb5660ca24ef9491 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Thu, 23 Feb 2023 11:53:07 +0100 Subject: [PATCH 256/337] bdd_to_formula: add CNF variant * spot/twa/formula2bdd.hh, spot/twa/formula2bdd.cc (bdd_to_cnf_formula): New function. * python/spot/__init__.py: Add a default dictionary for convenience. * tests/python/bdditer.py: Add test cases. * NEWS: Mention it. --- NEWS | 3 ++ python/spot/__init__.py | 8 ++++-- spot/twa/formula2bdd.cc | 32 +++++++++++++++++---- spot/twa/formula2bdd.hh | 23 +++++++++++----- tests/python/bdditer.py | 61 +++++++++++++++++++++++++++++++++++++++-- 5 files changed, 111 insertions(+), 16 deletions(-) diff --git a/NEWS b/NEWS index 3cfa6edc2..86fd461e1 100644 --- a/NEWS +++ b/NEWS @@ -14,6 +14,9 @@ New in spot 2.11.4.dev (not yet released) supports only one): it now reuse the edges leaving initial states without incoming transitions. + - spot::bdd_to_cnf_formula() is a new variant of spot::bdd_to_formula() + that converts a BDD into a CNF instead of a DNF. + Python: - spot.acd() no longer depends on jQuery for interactivity. diff --git a/python/spot/__init__.py b/python/spot/__init__.py index ef4cd772e..02bdcb1f6 100644 --- a/python/spot/__init__.py +++ b/python/spot/__init__.py @@ -1,6 +1,6 @@ # -*- coding: utf-8 -*- -# Copyright (C) 2014-2022 Laboratoire de -# Recherche et Développement de l'Epita (LRDE). +# Copyright (C) 2014-2023 Laboratoire de Recherche et Développement de +# l'Epita (LRDE). # # This file is part of Spot, a model checking library. # @@ -1347,6 +1347,10 @@ def bdd_to_formula(b, dic=_bdd_dict): from spot.impl import bdd_to_formula as bf return bf(b, dic) +def bdd_to_cnf_formula(b, dic=_bdd_dict): + from spot.impl import bdd_to_cnf_formula as bf + return bf(b, dic) + def language_containment_checker(dic=_bdd_dict): from spot.impl import language_containment_checker as c diff --git a/spot/twa/formula2bdd.cc b/spot/twa/formula2bdd.cc index 7596c0759..15434395f 100644 --- a/spot/twa/formula2bdd.cc +++ b/spot/twa/formula2bdd.cc @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2009-2019 Laboratoire de Recherche et Développement +// Copyright (C) 2009-2019, 2023 Laboratoire de Recherche et Développement // de l'Epita (LRDE). // Copyright (C) 2003, 2004 Laboratoire d'Informatique de Paris // 6 (LIP6), département Systèmes Répartis Coopératifs (SRC), @@ -30,11 +30,14 @@ namespace spot namespace { // Convert a BDD which is known to be a conjonction into a formula. + // If dual is true, dualize the result, i.e., negate literals, and + // exchange ∧ and ∨. + template static formula conj_to_formula(bdd b, const bdd_dict_ptr d) { if (b == bddfalse) - return formula::ff(); + return dual ? formula::tt() : formula::ff(); std::vector v; while (b != bddtrue) { @@ -49,11 +52,14 @@ namespace spot bdd high = bdd_high(b); if (high == bddfalse) { - res = formula::Not(res); + if (!dual) + res = formula::Not(res); b = bdd_low(b); } else { + if (dual) + res = formula::Not(res); // If bdd_low is not false, then b was not a conjunction. assert(bdd_low(b) == bddfalse); b = high; @@ -61,7 +67,7 @@ namespace spot assert(b != bddfalse); v.emplace_back(res); } - return formula::And(v); + return dual ? formula::Or(v) : formula::And(v); } } // anonymous @@ -143,7 +149,23 @@ namespace spot minato_isop isop(f); bdd cube; while ((cube = isop.next()) != bddfalse) - v.emplace_back(conj_to_formula(cube, d)); + v.emplace_back(conj_to_formula(cube, d)); return formula::Or(std::move(v)); } + + formula + bdd_to_cnf_formula(bdd f, const bdd_dict_ptr d) + { + if (f == bddtrue) + return formula::tt(); + + std::vector v; + + minato_isop isop(!f); + bdd cube; + while ((cube = isop.next()) != bddfalse) + v.emplace_back(conj_to_formula(cube, d)); + return formula::And(std::move(v)); + } + } diff --git a/spot/twa/formula2bdd.hh b/spot/twa/formula2bdd.hh index 4d5c81a60..a84d27996 100644 --- a/spot/twa/formula2bdd.hh +++ b/spot/twa/formula2bdd.hh @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2012, 2013, 2014, 2015 Laboratoire de Recherche et +// Copyright (C) 2012-2015, 2023 Laboratoire de Recherche et // Développement de l'Epita (LRDE). // Copyright (C) 2003 Laboratoire d'Informatique de Paris 6 (LIP6), // département Systèmes Répartis Coopératifs (SRC), Université Pierre @@ -52,12 +52,21 @@ namespace spot /// \brief Convert a BDD into a formula. /// - /// Format the BDD as an irredundant sum of product (see the - /// minato_isop class for details) and map the BDD variables back - /// into their atomic propositions. This works only for Boolean - /// formulas, and all the BDD variables used in \a f should have - /// been registered in \a d. Although the result has type - /// formula, it obviously does not use any temporal operator. + /// Format the BDD as a Boolean spot::formula object. This works only + /// for Boolean formulas, and all the BDD variables used in \a f + /// should have been registered in \a d. Although the result has + /// type formula, it obviously does not use any temporal operator. + /// + /// The bdd_to_formula() version produces an irredundant sum of + /// product (see the minato_isop class for details) and map the BDD + /// variables back into their atomic propositions. + /// + /// The bdd_to_cnf_formula() version produces an irredundant product of + /// sum, using the dual construction. + /// @{ SPOT_API formula bdd_to_formula(bdd f, const bdd_dict_ptr d); + SPOT_API + formula bdd_to_cnf_formula(bdd f, const bdd_dict_ptr d); + /// @} } diff --git a/tests/python/bdditer.py b/tests/python/bdditer.py index 95cc441b3..4a2afeea1 100644 --- a/tests/python/bdditer.py +++ b/tests/python/bdditer.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Copyright (C) 2017, 2018, 2021, 2022 Laboratoire de Recherche et +# Copyright (C) 2017, 2018, 2021, 2022, 2023 Laboratoire de Recherche et # Développement de l'Epita (LRDE). # # This file is part of Spot, a model checking library. @@ -27,6 +27,19 @@ import sys from unittest import TestCase tc = TestCase() +# CPython use reference counting, so that automata are destructed +# when we expect them to be. However other implementations like +# PyPy may call destructors latter, causing different output. +from platform import python_implementation +if python_implementation() == 'CPython': + def gcollect(): + pass +else: + import gc + def gcollect(): + gc.collect() + + run = spot.translate('a & !b').accepting_run() b = run.prefix[0].label c = buddy.bdd_satone(b) @@ -43,12 +56,15 @@ while c != buddy.bddtrue: c = h tc.assertEqual(res, [0, -1]) +del res res2 = [] for i in run.aut.ap(): res2.append((str(i), run.aut.register_ap(i))) tc.assertEqual(str(res2), "[('a', 0), ('b', 1)]") - +del res2 +del c +gcollect() f = spot.bdd_to_formula(b) tc.assertTrue(f._is(spot.op_And)) @@ -56,9 +72,50 @@ tc.assertTrue(f[0]._is(spot.op_ap)) tc.assertTrue(f[1]._is(spot.op_Not)) tc.assertTrue(f[1][0]._is(spot.op_ap)) tc.assertEqual(str(f), 'a & !b') +del f +gcollect() try: f = spot.bdd_to_formula(b, spot.make_bdd_dict()) sys.exit(2) except RuntimeError as e: tc.assertIn("not in the dictionary", str(e)) + +f = spot.bdd_to_cnf_formula(b) +tc.assertEqual(str(f), 'a & !b') + +del run +del f + +gcollect() + +f = spot.bdd_to_cnf_formula(buddy.bddtrue) +tc.assertEqual(str(f), '1') +del f +gcollect() + +f = spot.bdd_to_cnf_formula(buddy.bddfalse) +tc.assertEqual(str(f), '0') +del f +gcollect() + +aut = spot.translate('(a & b) <-> c') +# With pypy, running GC here will destroy the translator object used +# by translate(). That object has temporary automata that reference +# the BDDs variables and those affect the order in which the +# bdd_to_formula() result is object is presented. The different order +# is not wrong, but it makes it diffuclt to write tests. +gcollect() + +for e in aut.out(aut.get_init_state_number()): + b = e.cond + break + +f1 = spot.bdd_to_formula(b) +tc.assertEqual(str(f1), '(!a & !c) | (a & b & c) | (!b & !c)') +f2 = spot.bdd_to_cnf_formula(b) +tc.assertEqual(str(f2), '(a | !c) & (!a | !b | c) & (b | !c)') + +b1 = spot.formula_to_bdd(f1, spot._bdd_dict, aut) +b2 = spot.formula_to_bdd(f2, spot._bdd_dict, aut) +tc.assertEqual(b1, b2) From f117159ec416d93a63efcc4e64edd151d44bba8b Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Thu, 23 Feb 2023 12:02:06 +0100 Subject: [PATCH 257/337] * doc/org/tut03.org: Typos. --- doc/org/tut03.org | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/org/tut03.org b/doc/org/tut03.org index b48366a82..c70a3dab3 100644 --- a/doc/org/tut03.org +++ b/doc/org/tut03.org @@ -81,7 +81,7 @@ simplifications called /trivial identities/. For instance =formula::F(formula::X(formula::tt()))= will return the same formula as =formula::tt()=. These simplifications are those that involve the true and false constants, impotence (=F(F(e))=F(e)=), involutions -(=Not(Not(e)=e=), associativity +(=Not(Not(e))=e=), associativity (=And({And({e1,e2},e3})=And({e1,e2,e3})=). See [[https://spot.lrde.epita.fr/tl.pdf][tl.pdf]] for a list of these /trivial identities/. @@ -113,7 +113,7 @@ detail of the top-level operator in the formula. std::cout << f << '\n'; - // kindstar() prints the name of the operator + // kindstr() prints the name of the operator // size() return the number of operands of the operators std::cout << f.kindstr() << ", " << f.size() << " children\n"; // operator[] accesses each operand @@ -157,7 +157,7 @@ The Python equivalent is similar: print(f) - # kindstar() prints the name of the operator + # kindstr() prints the name of the operator # size() return the number of operands of the operators print("{}, {} children".format(f.kindstr(), f.size())) # [] accesses each operand From 146942953aecba4deaabd6091960b162a96459a9 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Fri, 3 Mar 2023 00:14:18 +0100 Subject: [PATCH 258/337] org: fix rendering of R examples for recent ESS/Org * doc/org/.dir-locals.el.in, doc/org/init.el.in: Newer ESS version need to be taught to use default-directory instead of the project directory. * doc/org/ltlcross.org: Use "result file" to render the output. --- doc/org/.dir-locals.el.in | 4 +++- doc/org/init.el.in | 4 +++- doc/org/ltlcross.org | 4 ++-- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/doc/org/.dir-locals.el.in b/doc/org/.dir-locals.el.in index cba9892fb..80c0a1385 100644 --- a/doc/org/.dir-locals.el.in +++ b/doc/org/.dir-locals.el.in @@ -27,6 +27,9 @@ (setenv "SPOT_DOTEXTRA" "node[fontsize=12] fontsize=12 stylesheet=\"spot.css\" edge[arrowhead=vee, arrowsize=.7, fontsize=12]") (setq org-babel-temporary-directory "@abs_top_builddir@/doc/org/tmp") (make-directory org-babel-temporary-directory t) + ; has to be set globally, not buffer-local + (setq ess-ask-for-ess-directory nil) + (setq ess-startup-directory 'default-directory) (org-babel-do-load-languages 'org-babel-load-languages `((,(if (version< org-version "8.3") 'sh 'shell) . t) (python . t) @@ -39,7 +42,6 @@ (org-babel-python-command . "@PYTHON@") (org-babel-C++-compiler . "./g++wrap") (shell-file-name . "@SHELL@") - (ess-ask-for-ess-directory . nil) (org-export-html-postamble . nil) (org-html-table-header-tags "
" . "
") diff --git a/doc/org/init.el.in b/doc/org/init.el.in index 5e51c7250..4258a95f7 100644 --- a/doc/org/init.el.in +++ b/doc/org/init.el.in @@ -88,7 +88,9 @@ (setq org-babel-C++-compiler "./g++wrap") (setq shell-file-name "@SHELL@") (setq ess-ask-for-ess-directory nil) - +; setting ess-startup-directory to 'default-directory is enough with +; newer ESS version (after Fev 2022) but does not work with older ones. +(setq ess-startup-directory "@abs_top_builddir@/doc/org") (setq org-babel-default-header-args:plantuml '((:results . "file") (:exports . "results") diff --git a/doc/org/ltlcross.org b/doc/org/ltlcross.org index 0fdebae1f..36cce5cbb 100644 --- a/doc/org/ltlcross.org +++ b/doc/org/ltlcross.org @@ -924,7 +924,7 @@ compare the number of states produced by the two configurations of =ltl2tgba= for each formula, we just need to plot column =dt2$state.small= against =dt2$state.deter=. -#+BEGIN_SRC R :results output graphics :width 5 :height 5 :file ltlcross-r.svg +#+BEGIN_SRC R :results output graphics file :width 5 :height 5 :file ltlcross-r.svg library(ggplot2) ggplot(dt2, aes(x=states.small, y=states.deter)) + geom_abline(colour='white') + geom_point() @@ -937,7 +937,7 @@ ggplot(dt2, aes(x=states.small, y=states.deter)) + We should probably print the formulas for the cases where the two sizes differ. -#+BEGIN_SRC R :results output graphics :width 5 :height 5 :file ltlcross-r2.svg +#+BEGIN_SRC R :results output graphics file :width 5 :height 5 :file ltlcross-r2.svg ggplot(dt2, aes(x=states.small, y=states.deter)) + geom_abline(colour='white') + geom_point() + geom_text(data=subset(dt2, states.small != states.deter), From e7e23d5ffcafe3ab362faa58b8864b283c5c3681 Mon Sep 17 00:00:00 2001 From: Philipp Schlehuber Date: Thu, 9 Mar 2023 22:41:44 +0100 Subject: [PATCH 259/337] Adding option to solve parity games globally Parity games have been solved semi-locally so far. We deduced a strategy for the reachable part of the arena This lead to some inconsistencies when not all state were rachable. Now you can chose to solve parity games truely globally. * spot/twaalgos/game.cc, spot/twaalgos/game.hh: Here * tests/python/games.ipynb: Test --- spot/twaalgos/game.cc | 164 ++++-- spot/twaalgos/game.hh | 8 +- tests/python/games.ipynb | 1146 +++++++++++++++++++++++++++++++++++++- 3 files changed, 1258 insertions(+), 60 deletions(-) diff --git a/spot/twaalgos/game.cc b/spot/twaalgos/game.cc index 17f94a7e4..faf29b2ba 100644 --- a/spot/twaalgos/game.cc +++ b/spot/twaalgos/game.cc @@ -149,73 +149,133 @@ namespace spot { public: - bool solve(const twa_graph_ptr& arena) + bool solve(const twa_graph_ptr& arena, bool solve_globally) { // todo check if reordering states according to scc is worth it set_up(arena); // Start recursive zielonka in a bottom-up fashion on each scc subgame_info_t subgame_info; - for (c_scc_idx_ = 0; c_scc_idx_ < info_->scc_count(); ++c_scc_idx_) + while (true) { - // Testing - // Make sure that every state that has a winner also - // belongs to a subgame - assert([&]() - { - for (unsigned i = 0; i < arena_->num_states(); ++i) - if (w_.has_winner_[i] - && (subgame_[i] == unseen_mark)) - return false; - return true; - }()); - // Useless SCCs are winning for player 0. - if (!info_->is_useful_scc(c_scc_idx_)) + // If we solve globally, + auto maybe_useful = [&](unsigned scc_idx){ + if (info_->is_useful_scc(scc_idx)) + return true; + if (!solve_globally) + return false; + // Check if we have an out-edge to a winning state + // in another scc + return std::any_of( + info_->states_of(scc_idx).begin(), + info_->states_of(scc_idx).end(), + [&](unsigned s){ + return std::any_of( + arena->out(s).begin(), + arena->out(s).end(), + [&](const auto& e){ + assert ((subgame_[e.dst] == unseen_mark) + || (info_->scc_of(e.dst) != scc_idx)); + return (info_->scc_of(e.dst) != scc_idx) + && w_.winner(e.dst); + }); + }); + }; + + for (c_scc_idx_ = 0; c_scc_idx_ < info_->scc_count(); ++c_scc_idx_) { - // This scc also gets its own subgame - ++rd_; - for (unsigned v: c_states()) - { - subgame_[v] = rd_; - w_.set(v, false); - // The strategy for player 0 is to take the first - // available edge. - if ((*owner_ptr_)[v] == false) - for (const auto &e : arena_->out(v)) + // Testing + // Make sure that every state that has a winner also + // belongs to a subgame + assert([&]() { - s_[v] = arena_->edge_number(e); - break; - } - } - continue; - } - // Convert transitions leaving edges to self-loops - // and check if trivially solvable - subgame_info = fix_scc(); - // If empty, the scc was trivially solved - if (!subgame_info.is_empty) - { - // Check for special cases - if (subgame_info.is_one_parity) - one_par_subgame_solver(subgame_info, max_abs_par_); - else + for (unsigned i = 0; i < arena_->num_states(); ++i) + if (w_.has_winner_[i] + && (subgame_[i] == unseen_mark)) + return false; + return true; + }()); + // Useless SCCs are winning for player 0. + if (!maybe_useful(c_scc_idx_)) { - // "Regular" solver - max_abs_par_ = *subgame_info.all_parities.begin(); - w_stack_.emplace_back(0, 0, - min_par_graph_, max_abs_par_); - zielonka(); + // This scc also gets its own subgame + ++rd_; + for (unsigned v: c_states()) + { + subgame_[v] = rd_; + w_.set(v, false); + // The strategy for player 0 is to take the first + // available edge. + if ((*owner_ptr_)[v] == false) + for (const auto &e : arena_->out(v)) + { + s_[v] = arena_->edge_number(e); + break; + } + } + continue; + } + // Convert transitions leaving edges to self-loops + // and check if trivially solvable + subgame_info = fix_scc(); + // If empty, the scc was trivially solved + if (!subgame_info.is_empty) + { + // Check for special cases + if (subgame_info.is_one_parity) + one_par_subgame_solver(subgame_info, max_abs_par_); + else + { + // "Regular" solver + max_abs_par_ = *subgame_info.all_parities.begin(); + w_stack_.emplace_back(0, 0, + min_par_graph_, max_abs_par_); + zielonka(); + } } } + if (!solve_globally) + break; + + // Update the scc_info and continue + unsigned new_init + = std::distance(subgame_.begin(), + std::find(subgame_.begin(), subgame_.end(), + unseen_mark)); + if (new_init == arena->num_states()) + break; // All states have been solved + // Compute new sccs + scc_info::edge_filter ef + = [](const twa_graph::edge_storage_t&, + unsigned dst, void* subgame){ + const auto& sg = *static_cast*>(subgame); + return sg[dst] == unseen_mark ? + scc_info::edge_filter_choice::keep : + scc_info::edge_filter_choice::ignore; + }; + info_ = std::make_unique(arena, new_init, ef, &subgame_); } - // Every state needs a winner - assert(std::all_of(w_.has_winner_.cbegin(), w_.has_winner_.cend(), - [](bool b) - { return b; })); + // Every state needs a winner (solve_globally) + // Or only those reachable + assert((solve_globally + && std::all_of(w_.has_winner_.cbegin(), w_.has_winner_.cend(), + [](bool b) { return b; })) + || (!solve_globally + && [&](){ + for (unsigned s = 0; s < arena->num_states(); ++s) + { + if ((info_->scc_of(s) != -1u) + && !w_.has_winner_.at(s)) + return false; + } + return true; + }())); // Only the states owned by the winner need a strategy assert([&]() { for (unsigned v = 0; v < arena_->num_states(); ++v) { + if (!solve_globally && (info_->scc_of(v) == -1u)) + continue; if (((*owner_ptr_)[v] == w_.winner(v)) && ((s_[v] <= 0) || (s_[v] > arena_->num_edges()))) return false; @@ -817,10 +877,10 @@ namespace spot } // anonymous - bool solve_parity_game(const twa_graph_ptr& arena) + bool solve_parity_game(const twa_graph_ptr& arena, bool solve_globally) { parity_game pg; - return pg.solve(arena); + return pg.solve(arena, solve_globally); } bool solve_game(const twa_graph_ptr& arena) diff --git a/spot/twaalgos/game.hh b/spot/twaalgos/game.hh index dbaccce75..d4937e46c 100644 --- a/spot/twaalgos/game.hh +++ b/spot/twaalgos/game.hh @@ -70,13 +70,19 @@ namespace spot /// This computes the winning strategy and winning region using /// Zielonka's recursive algorithm. \cite zielonka.98.tcs /// + /// By default only a 'local' strategy is computed: + /// Only the part of the arena reachable from the init state is considered. + /// If you want to compute a strategy for ALL states, set + /// \a solve_globally to true + /// /// Also includes some inspiration from Oink. /// \cite vandijk.18.tacas /// /// Returns the player winning in the initial state, and sets /// the state-winner and strategy named properties. SPOT_API - bool solve_parity_game(const twa_graph_ptr& arena); + bool solve_parity_game(const twa_graph_ptr& arena, + bool solve_globally = false); /// \ingroup games /// \brief Solve a safety game. diff --git a/tests/python/games.ipynb b/tests/python/games.ipynb index a6168b07e..9ec8bb76e 100644 --- a/tests/python/games.ipynb +++ b/tests/python/games.ipynb @@ -897,7 +897,7 @@ "\n" ], "text/plain": [ - " *' at 0x7feee9b0ebb0> >" + " *' at 0x7fcbe436f840> >" ] }, "execution_count": 8, @@ -1224,7 +1224,7 @@ "\n" ], "text/plain": [ - " *' at 0x7fef001c87b0> >" + " *' at 0x7fcbe436e9a0> >" ] }, "execution_count": 11, @@ -1663,11 +1663,1143 @@ ] }, { - "cell_type": "code", - "execution_count": null, + "cell_type": "markdown", "metadata": {}, - "outputs": [], - "source": [] + "source": [ + "# Global vs local solver\n", + "\n", + "The parity game solver now supports \"local\" and global solutions.\n", + "\n", + "- \"Local\" solutions are the ones computed so far. A strategy is only computed for the part of the automaton that is rachable from the initial state\n", + "- Global solutions can now be obtained by setting the argument \"solve_globally\" to true. In this case a strategy will be computed even for states not reachable in the original automaton.\n" + ] + }, + { + "cell_type": "code", + "execution_count": 14, + "metadata": {}, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "Inf(\n", + "\n", + ")\n", + "[Büchi]\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "1\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "0->1\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "2\n", + "\n", + "2\n", + "\n", + "\n", + "\n", + "0->2\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "3\n", + "\n", + "3\n", + "\n", + "\n", + "\n", + "1->3\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "2->3\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "4\n", + "\n", + "4\n", + "\n", + "\n", + "\n", + "3->4\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "4->0\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "\n", + "5\n", + "\n", + "5\n", + "\n", + "\n", + "\n", + "5->0\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "6\n", + "\n", + "6\n", + "\n", + "\n", + "\n", + "5->6\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "6->5\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "7\n", + "\n", + "7\n", + "\n", + "\n", + "\n", + "8\n", + "\n", + "8\n", + "\n", + "\n", + "\n", + "7->8\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "9\n", + "\n", + "9\n", + "\n", + "\n", + "\n", + "7->9\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "10\n", + "\n", + "10\n", + "\n", + "\n", + "\n", + "8->10\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "9->10\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "11\n", + "\n", + "11\n", + "\n", + "\n", + "\n", + "10->11\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "11->7\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "\n", + "12\n", + "\n", + "12\n", + "\n", + "\n", + "\n", + "12->7\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "13\n", + "\n", + "13\n", + "\n", + "\n", + "\n", + "12->13\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "13->12\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "14\n", + "\n", + "14\n", + "\n", + "\n", + "\n", + "15\n", + "\n", + "15\n", + "\n", + "\n", + "\n", + "14->15\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "16\n", + "\n", + "16\n", + "\n", + "\n", + "\n", + "14->16\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "17\n", + "\n", + "17\n", + "\n", + "\n", + "\n", + "15->17\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "16->17\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "18\n", + "\n", + "18\n", + "\n", + "\n", + "\n", + "17->18\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "18->14\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "19\n", + "\n", + "19\n", + "\n", + "\n", + "\n", + "19->14\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "20\n", + "\n", + "20\n", + "\n", + "\n", + "\n", + "19->20\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "20->19\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "\n" + ], + "text/plain": [ + " *' at 0x7fcbe4382370> >" + ] + }, + "execution_count": 14, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "arena = spot.make_twa_graph()\n", + "\n", + "arena.new_states(3*7)\n", + "arena.set_buchi()\n", + "\n", + "edges = [(0,1), (0,2), (1,3), (2,3), (3,4), (4,0), (5,0), (5,6), (6,5)]\n", + "\n", + "for src, dst in edges:\n", + " arena.new_edge(src, dst, bddtrue, [0] if src == 4 else [])\n", + " arena.new_edge(src + 7, dst + 7, bddtrue, [0] if src == 4 else [])\n", + " arena.new_edge(src + 14, dst + 14, bddtrue, [0] if src == 6 else [])\n", + "\n", + "arena.set_state_players(3*[False, True, True, False, True, True, False])\n", + "arena" + ] + }, + { + "cell_type": "code", + "execution_count": 15, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "(0, 7, 10, 0, 16, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0)\n" + ] + }, + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "Inf(\n", + "\n", + ")\n", + "[Büchi]\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "1\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "0->1\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "2\n", + "\n", + "2\n", + "\n", + "\n", + "\n", + "0->2\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "3\n", + "\n", + "3\n", + "\n", + "\n", + "\n", + "1->3\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "2->3\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "4\n", + "\n", + "4\n", + "\n", + "\n", + "\n", + "3->4\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "4->0\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "\n", + "5\n", + "\n", + "5\n", + "\n", + "\n", + "\n", + "5->0\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "6\n", + "\n", + "6\n", + "\n", + "\n", + "\n", + "5->6\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "6->5\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "7\n", + "\n", + "7\n", + "\n", + "\n", + "\n", + "8\n", + "\n", + "8\n", + "\n", + "\n", + "\n", + "7->8\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "9\n", + "\n", + "9\n", + "\n", + "\n", + "\n", + "7->9\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "10\n", + "\n", + "10\n", + "\n", + "\n", + "\n", + "8->10\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "9->10\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "11\n", + "\n", + "11\n", + "\n", + "\n", + "\n", + "10->11\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "11->7\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "\n", + "12\n", + "\n", + "12\n", + "\n", + "\n", + "\n", + "12->7\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "13\n", + "\n", + "13\n", + "\n", + "\n", + "\n", + "12->13\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "13->12\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "14\n", + "\n", + "14\n", + "\n", + "\n", + "\n", + "15\n", + "\n", + "15\n", + "\n", + "\n", + "\n", + "14->15\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "16\n", + "\n", + "16\n", + "\n", + "\n", + "\n", + "14->16\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "17\n", + "\n", + "17\n", + "\n", + "\n", + "\n", + "15->17\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "16->17\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "18\n", + "\n", + "18\n", + "\n", + "\n", + "\n", + "17->18\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "18->14\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "19\n", + "\n", + "19\n", + "\n", + "\n", + "\n", + "19->14\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "20\n", + "\n", + "20\n", + "\n", + "\n", + "\n", + "19->20\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "20->19\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "\n" + ], + "text/plain": [ + " *' at 0x7fcbe4382370> >" + ] + }, + "execution_count": 15, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# 1) Solving the game locally\n", + "# Unreachable parts are ignored, all of them are \"won\" by the env,\n", + "# the associated strategy is the 0 edges indicating no strategy\n", + "spot.solve_parity_game(arena)\n", + "spot.highlight_strategy(arena)\n", + "print(arena.get_strategy())\n", + "arena" + ] + }, + { + "cell_type": "code", + "execution_count": 16, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "(0, 7, 10, 0, 16, 19, 0, 0, 8, 11, 0, 17, 20, 0, 3, 0, 0, 15, 0, 24, 0)\n" + ] + }, + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "Inf(\n", + "\n", + ")\n", + "[Büchi]\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0\n", + "\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "1\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "0->1\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "2\n", + "\n", + "2\n", + "\n", + "\n", + "\n", + "0->2\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "3\n", + "\n", + "3\n", + "\n", + "\n", + "\n", + "1->3\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "2->3\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "4\n", + "\n", + "4\n", + "\n", + "\n", + "\n", + "3->4\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "4->0\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "\n", + "5\n", + "\n", + "5\n", + "\n", + "\n", + "\n", + "5->0\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "6\n", + "\n", + "6\n", + "\n", + "\n", + "\n", + "5->6\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "6->5\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "7\n", + "\n", + "7\n", + "\n", + "\n", + "\n", + "8\n", + "\n", + "8\n", + "\n", + "\n", + "\n", + "7->8\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "9\n", + "\n", + "9\n", + "\n", + "\n", + "\n", + "7->9\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "10\n", + "\n", + "10\n", + "\n", + "\n", + "\n", + "8->10\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "9->10\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "11\n", + "\n", + "11\n", + "\n", + "\n", + "\n", + "10->11\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "11->7\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "\n", + "12\n", + "\n", + "12\n", + "\n", + "\n", + "\n", + "12->7\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "13\n", + "\n", + "13\n", + "\n", + "\n", + "\n", + "12->13\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "13->12\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "14\n", + "\n", + "14\n", + "\n", + "\n", + "\n", + "15\n", + "\n", + "15\n", + "\n", + "\n", + "\n", + "14->15\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "16\n", + "\n", + "16\n", + "\n", + "\n", + "\n", + "14->16\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "17\n", + "\n", + "17\n", + "\n", + "\n", + "\n", + "15->17\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "16->17\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "18\n", + "\n", + "18\n", + "\n", + "\n", + "\n", + "17->18\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "18->14\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "19\n", + "\n", + "19\n", + "\n", + "\n", + "\n", + "19->14\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "20\n", + "\n", + "20\n", + "\n", + "\n", + "\n", + "19->20\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "20->19\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "\n" + ], + "text/plain": [ + " *' at 0x7fcbe4382370> >" + ] + }, + "execution_count": 16, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "# 1) Solving the game globally\n", + "# The whole automaton is considered in this case\n", + "spot.solve_parity_game(arena, True)\n", + "spot.highlight_strategy(arena)\n", + "print(arena.get_strategy())\n", + "arena" + ] } ], "metadata": { @@ -1686,7 +2818,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.10.5" + "version": "3.10.7" } }, "nbformat": 4, From 7a91cf78ec4eea3dda0857dfd0de7f677c06599a Mon Sep 17 00:00:00 2001 From: Philipp Schlehuber Date: Wed, 22 Mar 2023 11:00:48 +0100 Subject: [PATCH 260/337] Ignore ltargz.m4 * .gitignore: Ignore it * m4/ltargz.m4: Remove it --- .gitignore | 1 + m4/ltargz.m4 | 74 ---------------------------------------------------- 2 files changed, 1 insertion(+), 74 deletions(-) delete mode 100644 m4/ltargz.m4 diff --git a/.gitignore b/.gitignore index 7392a79db..155a9b5e7 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ configure config.log config.status aclocal.m4 +ltargz.m4 autom4te.cache libtool auto diff --git a/m4/ltargz.m4 b/m4/ltargz.m4 deleted file mode 100644 index 0908d90b9..000000000 --- a/m4/ltargz.m4 +++ /dev/null @@ -1,74 +0,0 @@ -# Portability macros for glibc argz. -*- Autoconf -*- -# -# Copyright (C) 2004-2007, 2011-2015 Free Software Foundation, Inc. -# Written by Gary V. Vaughan -# -# This file is free software; the Free Software Foundation gives -# unlimited permission to copy and/or distribute it, with or without -# modifications, as long as this notice is preserved. - -# serial 1 ltargz.m4 - -AC_DEFUN([LT_FUNC_ARGZ], [ -AC_CHECK_HEADERS([argz.h], [], [], [AC_INCLUDES_DEFAULT]) - -AC_CHECK_TYPES([error_t], - [], - [AC_DEFINE([error_t], [int], - [Define to a type to use for 'error_t' if it is not otherwise available.]) - AC_DEFINE([__error_t_defined], [1], [Define so that glibc/gnulib argp.h - does not typedef error_t.])], - [#if defined(HAVE_ARGZ_H) -# include -#endif]) - -LT_ARGZ_H= -AC_CHECK_FUNCS([argz_add argz_append argz_count argz_create_sep argz_insert \ - argz_next argz_stringify], [], [LT_ARGZ_H=lt__argz.h; AC_LIBOBJ([lt__argz])]) - -dnl if have system argz functions, allow forced use of -dnl libltdl-supplied implementation (and default to do so -dnl on "known bad" systems). Could use a runtime check, but -dnl (a) detecting malloc issues is notoriously unreliable -dnl (b) only known system that declares argz functions, -dnl provides them, yet they are broken, is cygwin -dnl releases prior to 16-Mar-2007 (1.5.24 and earlier) -dnl So, it's more straightforward simply to special case -dnl this for known bad systems. -AS_IF([test -z "$LT_ARGZ_H"], - [AC_CACHE_CHECK( - [if argz actually works], - [lt_cv_sys_argz_works], - [[case $host_os in #( - *cygwin*) - lt_cv_sys_argz_works=no - if test no != "$cross_compiling"; then - lt_cv_sys_argz_works="guessing no" - else - lt_sed_extract_leading_digits='s/^\([0-9\.]*\).*/\1/' - save_IFS=$IFS - IFS=-. - set x `uname -r | sed -e "$lt_sed_extract_leading_digits"` - IFS=$save_IFS - lt_os_major=${2-0} - lt_os_minor=${3-0} - lt_os_micro=${4-0} - if test 1 -lt "$lt_os_major" \ - || { test 1 -eq "$lt_os_major" \ - && { test 5 -lt "$lt_os_minor" \ - || { test 5 -eq "$lt_os_minor" \ - && test 24 -lt "$lt_os_micro"; }; }; }; then - lt_cv_sys_argz_works=yes - fi - fi - ;; #( - *) lt_cv_sys_argz_works=yes ;; - esac]]) - AS_IF([test yes = "$lt_cv_sys_argz_works"], - [AC_DEFINE([HAVE_WORKING_ARGZ], 1, - [This value is set to 1 to indicate that the system argz facility works])], - [LT_ARGZ_H=lt__argz.h - AC_LIBOBJ([lt__argz])])]) - -AC_SUBST([LT_ARGZ_H]) -]) From 7a97a6080ce0f92f9892cec8c2e72fceda40dbf6 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Fri, 24 Mar 2023 13:52:37 +0100 Subject: [PATCH 261/337] * doc/tl/tl.tex: Typo in firstmatch semantics. --- doc/tl/tl.tex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/tl/tl.tex b/doc/tl/tl.tex index 288a5da0c..e7c283bc6 100644 --- a/doc/tl/tl.tex +++ b/doc/tl/tl.tex @@ -701,7 +701,7 @@ $a$ is an atomic proposition. \VDash f\FSTAR{\mvar{i-1}..}))\\ \end{cases}\\ \sigma\VDash \FIRSTMATCH\code(f\code) & \iff - (\sigma\VDash f)\land (\forall k<|\sigma|,\,\sigma^{0..k}\nVDash f) + (\sigma\VDash f)\land (\forall k<|\sigma|,\,\sigma^{0..k-1}\nVDash f) \end{align*}} Notes: From 039cd756d5b18fbdb6f510395222cd648f3f2bc8 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Wed, 29 Mar 2023 16:20:51 +0200 Subject: [PATCH 262/337] fix spurious test-case failure when Python is not installed Fixes #530. * tests/core/ltlsynt2.test: Skip when PYTHON is empty. * NEWS: Mention the fix. --- NEWS | 5 +++-- tests/core/ltlsynt2.test | 4 +++- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/NEWS b/NEWS index 86fd461e1..e3de94172 100644 --- a/NEWS +++ b/NEWS @@ -17,9 +17,10 @@ New in spot 2.11.4.dev (not yet released) - spot::bdd_to_cnf_formula() is a new variant of spot::bdd_to_formula() that converts a BDD into a CNF instead of a DNF. - Python: + Bug fixes: - - spot.acd() no longer depends on jQuery for interactivity. + - Fix spurious failure of ltlsynt2.test when Python is not installed + (issue #530). New in spot 2.11.4 (2023-02-10) diff --git a/tests/core/ltlsynt2.test b/tests/core/ltlsynt2.test index dbb754d92..546cb0d27 100755 --- a/tests/core/ltlsynt2.test +++ b/tests/core/ltlsynt2.test @@ -1,6 +1,6 @@ #! /bin/sh # -*- coding: utf-8 -*- -# Copyright (C) 2022 Laboratoire de Recherche et Développement de +# Copyright (C) 2022, 2023 Laboratoire de Recherche et Développement de # l'Epita (LRDE). # # This file is part of Spot, a model checking library. @@ -36,6 +36,8 @@ ltlsynt --ins=i1,i2 -F formulas.ltl -f 'o1 & F(i1 <-> o2)' -q --csv=out.csv &&\ exit 2 test $? -eq 1 || exit 2 +test -z "$PYTHON" && exit 77 + cat >test.py < Date: Wed, 29 Mar 2023 17:01:13 +0200 Subject: [PATCH 263/337] correctly fails if emacs needed and missing Fixes #528. * configure.ac: Define EMACS using tools/missing. * NEWS: Mention the bug. --- NEWS | 3 +++ configure.ac | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index e3de94172..37a8ebf88 100644 --- a/NEWS +++ b/NEWS @@ -22,6 +22,9 @@ New in spot 2.11.4.dev (not yet released) - Fix spurious failure of ltlsynt2.test when Python is not installed (issue #530). + - Building from the git repository would fail to report a missing + emacs (issue #528). + New in spot 2.11.4 (2023-02-10) Python: diff --git a/configure.ac b/configure.ac index e47e2eb29..772b4c24a 100644 --- a/configure.ac +++ b/configure.ac @@ -217,7 +217,7 @@ AC_CHECK_PROG([LTL3BA], [ltl3ba], [ltl3ba]) AC_CHECK_PROG([PERL], [perl], [perl]) AC_CHECK_PROG([SPIN], [spin], [spin]) AC_CHECK_PROG([LBTT], [lbtt], [lbtt]) -AC_CHECK_PROG([EMACS], [emacs], [emacs]) +AM_MISSING_PROG([EMACS], [emacs]) AC_CHECK_PROGS([IPYTHON], [ipython3 ipython], [ipython]) AC_CHECK_PROGS([JUPYTER], [jupyter], [jupyter]) AC_CHECK_PROG([LBTT_TRANSLATE], [lbtt-translate], [lbtt-translate]) From d152b3a316171e02c256d258b4eb1c73b63db9ec Mon Sep 17 00:00:00 2001 From: Philipp Schlehuber-Caissier Date: Thu, 30 Mar 2023 14:32:26 +0200 Subject: [PATCH 264/337] Fix parity solver if edgevector is not contiguous Validity of strategies was tested relying on num_edges() which might be smaller than the edge_number * spot/twaalgos/game.cc: Fix here * tests/python/game.py: Test here --- spot/twaalgos/game.cc | 6 +- tests/python/game.py | 139 +++++++++++++++++++++++++++++++++++++++++- 2 files changed, 143 insertions(+), 2 deletions(-) diff --git a/spot/twaalgos/game.cc b/spot/twaalgos/game.cc index faf29b2ba..add0926fe 100644 --- a/spot/twaalgos/game.cc +++ b/spot/twaalgos/game.cc @@ -272,12 +272,16 @@ namespace spot // Only the states owned by the winner need a strategy assert([&]() { + std::unordered_set valid_strat; + for (const auto& e : arena_->edges()) + valid_strat.insert(arena_->edge_number(e)); + for (unsigned v = 0; v < arena_->num_states(); ++v) { if (!solve_globally && (info_->scc_of(v) == -1u)) continue; if (((*owner_ptr_)[v] == w_.winner(v)) - && ((s_[v] <= 0) || (s_[v] > arena_->num_edges()))) + && (valid_strat.count(s_.at(v)) == 0)) return false; } return true; diff --git a/tests/python/game.py b/tests/python/game.py index a7080b696..857390335 100644 --- a/tests/python/game.py +++ b/tests/python/game.py @@ -384,4 +384,141 @@ spot.solve_game(aut) S1 = list(spot.get_strategy(aut)) spot.solve_game(aut) S2 = list(spot.get_strategy(aut)) -tc.assertEqual(S1, S2) \ No newline at end of file +tc.assertEqual(S1, S2) + + +# Finite games +alive = "__alive__" +def finite_existential(auts): + # 1 Accepting state -> selfloop + # 2 Prune + acc_state = set() + sp = list(spot.get_state_players(auts)) + for e in auts.edges(): + if e.acc: + acc_state.add(e.src) + for s in acc_state: + e_kill = auts.out_iteraser(s) + while (e_kill): + e_kill.erase() + for s in acc_state: + sprime = auts.new_state() + sp.append(not sp[s]) + auts.new_edge(s, sprime, buddy.bddtrue, [0]) + auts.new_edge(sprime, s, buddy.bddtrue, [0]) + spot.set_state_players(auts, sp) + auts.purge_dead_states() + spot.alternate_players(auts, False, False) + return auts + +def is_input_complete(auts): + sp = spot.get_state_players(auts) + for s in range(auts.num_states()): + if sp[s]: + continue # Player + cumul = buddy.bddfalse + for e in auts.out(s): + cumul |= e.cond + if cumul != buddy.bddtrue: + return False + + return True + +def synt_from_ltlf(f:str, outs): + ff = spot.from_ltlf(f, alive) + aut = ff.translate("buchi", "sbacc") + outbdd = buddy.bddtrue + for out in outs: + outbdd &= buddy.bdd_ithvar(aut.register_ap(out)) + alive_bdd = buddy.bdd_ithvar(aut.register_ap(alive)) + auts = spot.split_2step(aut, outbdd & alive_bdd, False) + auts = spot.to_finite(auts, alive) + spot.alternate_players(auts, False, False) + spot.set_synthesis_outputs(auts, outbdd) + if not is_input_complete(auts): + print("Not synthesizable") + return None + auts = finite_existential(auts) + + return auts + +def synt_ltlf(f:str, outs, res:str = "aut"): + auts = synt_from_ltlf(f, outs) + + succ = spot.solve_parity_game(auts) + if not succ: + if res == "aut": + return False, auts + else: + return False, None + + mealy_cc = spot.solved_game_to_split_mealy(auts) + + if res == "aut": + return True, mealy_cc + elif res == "aig": + return True, spot.mealy_machine_to_aig(mealy_cc, "isop") + else: + raise RuntimeError("Unknown option") + + +sink_player = None + +def negate_ltlf(f:str, outs, opt = "buchi"): + + global sink_player + sink_player = None + + aut = synt_from_ltlf(f, outs) + # Implies input completeness + # We need output completeness + acc = [] + + sp = list(spot.get_state_players(aut)) + + def get_sink(): + global sink_player + if sink_player is None: + sink_player = aut.new_states(2) + aut.new_edge(sink_player, sink_player + 1, buddy.bddtrue, acc) + aut.new_edge(sink_player + 1, sink_player, buddy.bddtrue, acc) + sp.append(False) + sp.append(True) + spot.set_state_players(aut, sp) + return sink_player + + for s in range(aut.num_states()): + if not sp[s]: + continue + rem = buddy.bddtrue + for e in aut.out(s): + rem -= e.cond + if rem != buddy.bddfalse: + aut.new_edge(s, get_sink(), rem) + + # Better to invert colors or condition? + if opt == "buchi": + for e in aut.edges(): + if e.acc: + e.acc = spot.mark_t() + else: + e.acc = spot.mark_t([0]) + elif opt == "cobuchi": + aut.set_co_buchi() + else: + raise RuntimeError("Unknown opt") + return aut + +# Game where the edge_vector is larger +# than the number of transitions +f1 = "((((G (F (idle))) && (G (((idle) && (X ((! (grant_0)) \ + && (! (grant_1))))) -> (X (idle))))) && (G ((X (! (grant_0))) \ + || (X (((! (request_0)) && (! (idle))) U ((! (request_0)) \ + && (idle))))))) -> (((G (((((X (((! (grant_0)) && (true)) \ + || ((true) && (! (grant_1))))) && ((X (grant_0)) -> (request_0))) \ + && ((X (grant_1)) -> (request_1))) && ((request_0) -> (grant_1))) \ + && ((! (idle)) -> (X ((! (grant_0)) && (! (grant_1))))))) \ + && (! (F (G ((request_0) && (X (! (grant_0)))))))) \ + && (! (F (G ((request_1) && (X (! (grant_1)))))))))" +outs = ["grant_0", "grant1"] +tc.assertEqual(synt_ltlf(f1, outs)[0], False) \ No newline at end of file From ae10361bddb9a3a858272a272be7ccd34800c677 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Tue, 18 Apr 2023 14:48:10 +0200 Subject: [PATCH 265/337] twa_run: let as_twa work on the result of intersecting_run Reported by Philipp Schlehuber-Caissier. * spot/twaalgos/emptiness.cc (as_twa): Simplify considerably. Don't try to replay the run, and don't merge identical states. * spot/twaalgos/word.hh, spot/twaalgos/emptiness.hh: Improve documentation. * tests/python/intrun.py: Add a test case. * NEWS: Mention the bug. --- NEWS | 5 +++ spot/twaalgos/emptiness.cc | 75 +++++++++++--------------------------- spot/twaalgos/emptiness.hh | 6 +-- spot/twaalgos/word.hh | 7 +++- tests/python/intrun.py | 40 +++++++++++++++++++- 5 files changed, 73 insertions(+), 60 deletions(-) diff --git a/NEWS b/NEWS index 37a8ebf88..7fbb4f66a 100644 --- a/NEWS +++ b/NEWS @@ -25,6 +25,11 @@ New in spot 2.11.4.dev (not yet released) - Building from the git repository would fail to report a missing emacs (issue #528). + - Fix exception raised by aut1.intersecting_run(aut2).as_twa() + because the run did not match transitions present in aut1 + verbatim. We also changed the behavior of as_twa() to not merge + identical states. + New in spot 2.11.4 (2023-02-10) Python: diff --git a/spot/twaalgos/emptiness.cc b/spot/twaalgos/emptiness.cc index fd3319141..ef8890f95 100644 --- a/spot/twaalgos/emptiness.cc +++ b/spot/twaalgos/emptiness.cc @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2009, 2011-2019, 2021 Laboratoire de Recherche et +// Copyright (C) 2009, 2011-2019, 2021, 2023 Laboratoire de Recherche et // Développement de l'Epita (LRDE). // Copyright (C) 2004, 2005 Laboratoire d'Informatique de Paris 6 (LIP6), // département Systèmes Répartis Coopératifs (SRC), Université Pierre @@ -570,7 +570,7 @@ namespace spot if (debug) os << "ERROR: First state of run (in " << in << "): " << aut->format_state(i->s) - << "\ndoes not match initial state of automata: " + << "\ndoes not match initial state of automaton: " << aut->format_state(s) << '\n'; s->destroy(); return false; @@ -802,38 +802,38 @@ namespace spot res->set_named_prop("state-names", names); } - const state* s = aut->get_init_state(); unsigned src; unsigned dst; const twa_run::steps* l; - acc_cond::mark_t seen_acc = {}; - - state_map seen; + unsigned cycle_entry = 0; if (prefix.empty()) - l = &cycle; + l = &cycle; else - l = &prefix; + l = &prefix; twa_run::steps::const_iterator i = l->begin(); - assert(s->compare(i->s) == 0); +#if NDEBUG + const state* init = aut->get_init_state(); + assert(init->compare(i->s) == 0); + init->destroy(); +#endif + src = res->new_state(); - seen.emplace(i->s, src); if (names) - names->push_back(aut->format_state(s)); + names->push_back(aut->format_state(i->s)); for (; i != l->end();) { - // expected outgoing transition bdd label = i->label; acc_cond::mark_t acc = i->acc; - // compute the next expected state const state* next; ++i; if (i != l->end()) { + dst = res->new_state(); next = i->s; } else @@ -842,57 +842,24 @@ namespace spot { l = &cycle; i = l->begin(); + cycle_entry = dst = res->new_state(); + } + else + { + dst = cycle_entry; } next = l->begin()->s; } - // browse the actual outgoing transitions and - // look for next; - const state* the_next = nullptr; - for (auto j: aut->succ(s)) + if (names && i != l->end()) { - if (j->cond() != label - || j->acc() != acc) - continue; - - const state* s2 = j->dst(); - if (s2->compare(next) == 0) - { - the_next = s2; - break; - } - s2->destroy(); + assert(dst == names->size()); + names->push_back(aut->format_state(next)); } - s->destroy(); - if (!the_next) - throw std::runtime_error("twa_run::as_twa() unable to replay run"); - s = the_next; - - - auto p = seen.emplace(next, 0); - if (p.second) - { - unsigned ns = res->new_state(); - p.first->second = ns; - if (names) - { - assert(ns == names->size()); - names->push_back(aut->format_state(next)); - } - } - dst = p.first->second; - res->new_edge(src, dst, label, acc); src = dst; - - // Sum acceptance conditions. - if (l == &cycle && i != l->begin()) - seen_acc |= acc; } - s->destroy(); - - assert(aut->acc().accepting(seen_acc)); return res; } diff --git a/spot/twaalgos/emptiness.hh b/spot/twaalgos/emptiness.hh index 47896a1d7..66bf8ca56 100644 --- a/spot/twaalgos/emptiness.hh +++ b/spot/twaalgos/emptiness.hh @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2011, 2013-2018, 2020-2021 Laboratoire de +// Copyright (C) 2011, 2013-2018, 2020-2021, 2023 Laboratoire de // Recherche et Developpement de l'Epita (LRDE). // Copyright (C) 2004, 2005 Laboratoire d'Informatique de Paris 6 (LIP6), // département Systèmes Répartis Coopératifs (SRC), Université Pierre @@ -451,9 +451,9 @@ namespace spot /// Note that this works only if the automaton is a twa_graph_ptr. void highlight(unsigned color); - /// \brief Return a twa_graph_ptr corresponding to \a run + /// \brief Convert the run into a lasso-shaped automaton /// - /// Identical states are merged. + /// This preserves the original acceptance condition. /// /// If \a preserve_names is set, the created states are named /// using the format_state() result from the original state. diff --git a/spot/twaalgos/word.hh b/spot/twaalgos/word.hh index f6f70fc14..979a4070b 100644 --- a/spot/twaalgos/word.hh +++ b/spot/twaalgos/word.hh @@ -1,6 +1,6 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2013, 2014, 2015, 2016, 2018, 2019 Laboratoire de Recherche et -// Développement de l'Epita (LRDE). +// Copyright (C) 2013-2016, 2018-2019, 2023 Laboratoire de Recherche +// et Développement de l'Epita (LRDE). // // This file is part of Spot, a model checking library. // @@ -80,6 +80,9 @@ namespace spot /// \brief Convert the twa_word as an automaton. /// + /// Convert the twa_word into a lasso-shapred automaton + /// with "true" acceptance condition. + /// /// This is useful to evaluate a word on an automaton. twa_graph_ptr as_automaton() const; diff --git a/tests/python/intrun.py b/tests/python/intrun.py index e3b708a95..02a7aedd6 100644 --- a/tests/python/intrun.py +++ b/tests/python/intrun.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Copyright (C) 2020, 2022 Laboratoire de Recherche et Développement +# Copyright (C) 2020, 2022, 2023 Laboratoire de Recherche et Développement # de l'Epita (LRDE). # # This file is part of Spot, a model checking library. @@ -38,3 +38,41 @@ r = b.intersecting_run(spot.complement(a)); c = spot.twa_word(r).as_automaton() tc.assertTrue(c.intersects(b)) tc.assertFalse(c.intersects(a)) + +# The next test came from Philipp Schlehuber-Caissier: running +# as_twa() on a run built from a A.intersecting_run(B) failed to build +# the automaton because it tried to rebuild the run on A and did not +# find transitions matching exactly. Additionally the idea of merging +# states in as_twa() seems to be a way to create some disasters, so we +# removed that too. +a = spot.translate("a"); +b = spot.translate("{a;1;a}"); +r = a.intersecting_run(b) +tc.assertEqual(str(r), """Prefix: + 1 + | a + 0 + | 1 {0} + 0 + | a {0} +Cycle: + 0 + | 1 {0} +""") +tc.assertEqual(r.as_twa().to_str(), """HOA: v1 +States: 4 +Start: 0 +AP: 1 "a" +acc-name: Buchi +Acceptance: 1 Inf(0) +properties: trans-labels explicit-labels state-acc deterministic +--BODY-- +State: 0 +[0] 1 +State: 1 {0} +[t] 2 +State: 2 {0} +[0] 3 +State: 3 {0} +[t] 3 +--END--""") From 0e54a853104f1124fd7dd60546cbbc14e9985d41 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Tue, 18 Apr 2023 15:04:58 +0200 Subject: [PATCH 266/337] powerset: fix segfault when the initial state is a sink Reported by Raven Beutner. * spot/twaalgos/minimize.cc: Improve comment. * spot/twaalgos/powerset.cc: Fix handling of an initial state that is also a sink. * tests/core/wdba2.test: Add test case. * NEWS: Mention the bug. --- NEWS | 3 +++ spot/twaalgos/minimize.cc | 6 +++--- spot/twaalgos/powerset.cc | 26 ++++++++++++++------------ tests/core/wdba2.test | 37 +++++++++++++++++++++++++++++++++++-- 4 files changed, 55 insertions(+), 17 deletions(-) diff --git a/NEWS b/NEWS index 7fbb4f66a..32ef6b9ed 100644 --- a/NEWS +++ b/NEWS @@ -30,6 +30,9 @@ New in spot 2.11.4.dev (not yet released) verbatim. We also changed the behavior of as_twa() to not merge identical states. + - Fix segfaults occuring in determinization of 1-state terminal + automata. + New in spot 2.11.4 (2023-02-10) Python: diff --git a/spot/twaalgos/minimize.cc b/spot/twaalgos/minimize.cc index 4fd6847b3..1ac961d46 100644 --- a/spot/twaalgos/minimize.cc +++ b/spot/twaalgos/minimize.cc @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2010-2020 Laboratoire de Recherche et Développement +// Copyright (C) 2010-2020, 2023 Laboratoire de Recherche et Développement // de l'Epita (LRDE). // // This file is part of Spot, a model checking library. @@ -394,8 +394,8 @@ namespace spot else { // Find any accepting sink state, to speed up the - // determinization by merging all states containing a sink - // state. + // determinization by merging all macro-states containing a + // sink state. std::vector acc_sinks; unsigned ns = a->num_states(); if (!a->prop_terminal().is_true()) diff --git a/spot/twaalgos/powerset.cc b/spot/twaalgos/powerset.cc index c5fc07f94..326de7c76 100644 --- a/spot/twaalgos/powerset.cc +++ b/spot/twaalgos/powerset.cc @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2009-2011, 2013-2019, 2021 Laboratoire de Recherche et +// Copyright (C) 2009-2011, 2013-2019, 2021, 2023 Laboratoire de Recherche et // Développement de l'Epita (LRDE). // Copyright (C) 2004 Laboratoire d'Informatique de Paris 6 (LIP6), // département Systèmes Répartis Coopératifs (SRC), Université Pierre @@ -217,17 +217,19 @@ namespace spot pm.map_.emplace_back(std::move(ps)); } - { - unsigned init_num = aut->get_init_state_number(); - auto bvi = make_bitvect(ns); - bvi->set(init_num); - power_state ps{init_num}; - unsigned num = res->new_state(); - res->set_init_state(num); - seen[bvi] = num; - assert(pm.map_.size() == num); - pm.map_.emplace_back(std::move(ps)); - toclean.emplace_back(bvi); + // Add the initial state unless it's a sink. + if (unsigned init_num = aut->get_init_state_number(); + !acc_sinks || !acc_sinks->get(init_num)) + { + auto bvi = make_bitvect(ns); + bvi->set(init_num); + power_state ps{init_num}; + unsigned num = res->new_state(); + res->set_init_state(num); + seen[bvi] = num; + assert(pm.map_.size() == num); + pm.map_.emplace_back(std::move(ps)); + toclean.emplace_back(bvi); } // outgoing map diff --git a/tests/core/wdba2.test b/tests/core/wdba2.test index ca49bad94..3850a447a 100755 --- a/tests/core/wdba2.test +++ b/tests/core/wdba2.test @@ -1,7 +1,7 @@ #!/bin/sh # -*- coding: utf-8 -*- -# Copyright (C) 2012, 2014, 2015, 2018, 2019 Laboratoire de Recherche et -# Développement de l'Epita (LRDE). +# Copyright (C) 2012, 2014-2015, 2018-2019, 2023 Laboratoire de +# Recherche et Développement de l'Epita (LRDE). # # This file is part of Spot, a model checking library. # @@ -82,3 +82,36 @@ EOF autfilt --small --high -C -Hi input > output diff output expected + +# This test comes from a report from Raven Beutner and used to cause a +# segfault. +cat >input <output +cat >expected < Date: Tue, 18 Apr 2023 17:35:05 +0200 Subject: [PATCH 267/337] org: replace version references with org-babel blocks This way we have fewer lines to edit multiple when making releases. * doc/org/index.org, doc/org/init.el.in, doc/org/install.org, doc/org/setup.org, doc/org/tools.org: Use org-babel instead of macros for release version and links. --- doc/org/index.org | 2 +- doc/org/init.el.in | 2 +- doc/org/install.org | 4 ++-- doc/org/setup.org | 20 ++++++++++++++++---- doc/org/tools.org | 6 +++--- 5 files changed, 23 insertions(+), 11 deletions(-) diff --git a/doc/org/index.org b/doc/org/index.org index 9af23dba4..08fa16a3d 100644 --- a/doc/org/index.org +++ b/doc/org/index.org @@ -37,7 +37,7 @@ checking. It has the following notable features: * Latest version -The latest version is *{{{LASTRELEASE}}}* and was released on +The latest version is *call_SPOT_VERSION()* and was released on *{{{LASTDATE}}}*. Please see the [[file:install.org][download and installation instructions]]. * Documentation diff --git a/doc/org/init.el.in b/doc/org/init.el.in index 4258a95f7..c46363096 100644 --- a/doc/org/init.el.in +++ b/doc/org/init.el.in @@ -160,7 +160,7 @@ up.html points to index.html, then the result is: (setq body res) (not cmp))) (concat "#+TITLE: " title - "\n#+SETUPFILE: setup.org\n#+HTML_LINK_UP: index.html\n\n" + "\n#+INCLUDE: setup.org\n#+HTML_LINK_UP: index.html\n\n" body))) (setq org-publish-project-alist diff --git a/doc/org/install.org b/doc/org/install.org index dc492af57..b65c02074 100644 --- a/doc/org/install.org +++ b/doc/org/install.org @@ -9,9 +9,9 @@ :CUSTOM_ID: tar :END: -The latest release of Spot is version {{{LASTRELEASE}}}: +The latest release of Spot is version call_SPOT_VERSION() and was released on {{{LASTDATE}}}: -- {{{LASTTARBALL}}} (see also the {{{LASTNEWS}}}) +- call_TARBALL_LINK() (see also the call_NEWS_LINK()) Past releases can be found [[https://www.lrde.epita.fr/dload/spot/][in the same directory]]. If you are interested in /future/ releases, you can always peek at the [[https://gitlab.lre.epita.fr/spot/spot/-/jobs/artifacts/next/browse?job=make-dist][last diff --git a/doc/org/setup.org b/doc/org/setup.org index 7b6a4fa70..974272774 100644 --- a/doc/org/setup.org +++ b/doc/org/setup.org @@ -1,11 +1,23 @@ #+OPTIONS: H:2 num:nil toc:t html-postamble:nil ^:nil #+EMAIL: spot@lrde.epita.fr #+HTML_LINK_HOME: index.html -#+MACRO: SPOTVERSION 2.11.4 -#+MACRO: LASTRELEASE 2.11.4 -#+MACRO: LASTTARBALL [[http://www.lrde.epita.fr/dload/spot/spot-2.11.3.tar.gz][=spot-2.11.4.tar.gz=]] -#+MACRO: LASTNEWS [[https://gitlab.lre.epita.fr/spot/spot/blob/spot-2-11-3/NEWS][summary of the changes]] #+MACRO: LASTDATE 2023-02-10 +#+NAME: SPOT_VERSION +#+BEGIN_SRC python :exports none :results value :wrap org +return "2.11.4" +#+END_SRC + +#+NAME: TARBALL_LINK +#+BEGIN_SRC python :exports none :var version=SPOT_VERSION :results output :wrap org + print(f"[[http://www.lrde.epita.fr/dload/spot/spot-{version}.tar.gz][=spot-{version}.tar.gz=]]") +#+END_SRC + +#+NAME: NEWS_LINK +#+BEGIN_SRC python :exports none :var version=SPOT_VERSION :results output :wrap org + version = version.replace('.', '-') + print(f"[[https://gitlab.lre.epita.fr/spot/spot/blob/spot-{version}/NEWS][summary of the changes]]") +#+END_SRC + #+ATTR_HTML: :id spotlogo [[file:spot2.svg]] diff --git a/doc/org/tools.org b/doc/org/tools.org index 5227f1b4e..46ca38ccd 100644 --- a/doc/org/tools.org +++ b/doc/org/tools.org @@ -1,12 +1,12 @@ # -*- coding: utf-8 -*- -#+TITLE: Command-line tools installed by Spot {{{SPOTVERSION}}} -#+DESCRIPTION: List of all the command-line tools installed by Spot {{{SPOTVERSION}}} #+INCLUDE: setup.org +#+TITLE: Command-line tools installed by Spot +#+DESCRIPTION: List of all the command-line tools installed by Spot #+HTML_LINK_UP: index.html #+PROPERTY: header-args:sh :results verbatim :exports both This document introduces command-line tools that are installed with -the Spot library. We give some examples to highlight possible +Spot call_SPOT_VERSION(). We give some examples to highlight possible use-cases but shall not attempt to cover all features exhaustively (please check the man pages for further inspiration). From d3013b072d7c5bac44c9d33e5d43fa43d1a89212 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Thu, 16 Feb 2023 17:46:51 +0100 Subject: [PATCH 268/337] org: do not require org-install org-install has been obsolete for a long time, and has been removed from Org 9.6. * doc/org/init.el.in: Remove org-install. --- doc/org/init.el.in | 1 - 1 file changed, 1 deletion(-) diff --git a/doc/org/init.el.in b/doc/org/init.el.in index 9f589bb35..5e51c7250 100644 --- a/doc/org/init.el.in +++ b/doc/org/init.el.in @@ -51,7 +51,6 @@ (package-install ess))))) (require 'ox-publish) -(require 'org-install) (require 'hoa-mode) ; See https://github.com/emacs-ess/ESS/issues/1052 From a146457ea16e99dc47527f3fb05cd08b5d7448a6 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Thu, 23 Feb 2023 12:02:06 +0100 Subject: [PATCH 269/337] * doc/org/tut03.org: Typos. --- doc/org/tut03.org | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/doc/org/tut03.org b/doc/org/tut03.org index b48366a82..c70a3dab3 100644 --- a/doc/org/tut03.org +++ b/doc/org/tut03.org @@ -81,7 +81,7 @@ simplifications called /trivial identities/. For instance =formula::F(formula::X(formula::tt()))= will return the same formula as =formula::tt()=. These simplifications are those that involve the true and false constants, impotence (=F(F(e))=F(e)=), involutions -(=Not(Not(e)=e=), associativity +(=Not(Not(e))=e=), associativity (=And({And({e1,e2},e3})=And({e1,e2,e3})=). See [[https://spot.lrde.epita.fr/tl.pdf][tl.pdf]] for a list of these /trivial identities/. @@ -113,7 +113,7 @@ detail of the top-level operator in the formula. std::cout << f << '\n'; - // kindstar() prints the name of the operator + // kindstr() prints the name of the operator // size() return the number of operands of the operators std::cout << f.kindstr() << ", " << f.size() << " children\n"; // operator[] accesses each operand @@ -157,7 +157,7 @@ The Python equivalent is similar: print(f) - # kindstar() prints the name of the operator + # kindstr() prints the name of the operator # size() return the number of operands of the operators print("{}, {} children".format(f.kindstr(), f.size())) # [] accesses each operand From dcd4759896cb2940ca6c0f6af9c4f50a38e94eca Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Fri, 3 Mar 2023 00:14:18 +0100 Subject: [PATCH 270/337] org: fix rendering of R examples for recent ESS/Org * doc/org/.dir-locals.el.in, doc/org/init.el.in: Newer ESS version need to be taught to use default-directory instead of the project directory. * doc/org/ltlcross.org: Use "result file" to render the output. --- doc/org/.dir-locals.el.in | 4 +++- doc/org/init.el.in | 4 +++- doc/org/ltlcross.org | 4 ++-- 3 files changed, 8 insertions(+), 4 deletions(-) diff --git a/doc/org/.dir-locals.el.in b/doc/org/.dir-locals.el.in index cba9892fb..80c0a1385 100644 --- a/doc/org/.dir-locals.el.in +++ b/doc/org/.dir-locals.el.in @@ -27,6 +27,9 @@ (setenv "SPOT_DOTEXTRA" "node[fontsize=12] fontsize=12 stylesheet=\"spot.css\" edge[arrowhead=vee, arrowsize=.7, fontsize=12]") (setq org-babel-temporary-directory "@abs_top_builddir@/doc/org/tmp") (make-directory org-babel-temporary-directory t) + ; has to be set globally, not buffer-local + (setq ess-ask-for-ess-directory nil) + (setq ess-startup-directory 'default-directory) (org-babel-do-load-languages 'org-babel-load-languages `((,(if (version< org-version "8.3") 'sh 'shell) . t) (python . t) @@ -39,7 +42,6 @@ (org-babel-python-command . "@PYTHON@") (org-babel-C++-compiler . "./g++wrap") (shell-file-name . "@SHELL@") - (ess-ask-for-ess-directory . nil) (org-export-html-postamble . nil) (org-html-table-header-tags "
" . "
") diff --git a/doc/org/init.el.in b/doc/org/init.el.in index 5e51c7250..4258a95f7 100644 --- a/doc/org/init.el.in +++ b/doc/org/init.el.in @@ -88,7 +88,9 @@ (setq org-babel-C++-compiler "./g++wrap") (setq shell-file-name "@SHELL@") (setq ess-ask-for-ess-directory nil) - +; setting ess-startup-directory to 'default-directory is enough with +; newer ESS version (after Fev 2022) but does not work with older ones. +(setq ess-startup-directory "@abs_top_builddir@/doc/org") (setq org-babel-default-header-args:plantuml '((:results . "file") (:exports . "results") diff --git a/doc/org/ltlcross.org b/doc/org/ltlcross.org index 0fdebae1f..36cce5cbb 100644 --- a/doc/org/ltlcross.org +++ b/doc/org/ltlcross.org @@ -924,7 +924,7 @@ compare the number of states produced by the two configurations of =ltl2tgba= for each formula, we just need to plot column =dt2$state.small= against =dt2$state.deter=. -#+BEGIN_SRC R :results output graphics :width 5 :height 5 :file ltlcross-r.svg +#+BEGIN_SRC R :results output graphics file :width 5 :height 5 :file ltlcross-r.svg library(ggplot2) ggplot(dt2, aes(x=states.small, y=states.deter)) + geom_abline(colour='white') + geom_point() @@ -937,7 +937,7 @@ ggplot(dt2, aes(x=states.small, y=states.deter)) + We should probably print the formulas for the cases where the two sizes differ. -#+BEGIN_SRC R :results output graphics :width 5 :height 5 :file ltlcross-r2.svg +#+BEGIN_SRC R :results output graphics file :width 5 :height 5 :file ltlcross-r2.svg ggplot(dt2, aes(x=states.small, y=states.deter)) + geom_abline(colour='white') + geom_point() + geom_text(data=subset(dt2, states.small != states.deter), From 5714ecce3291ed94937dfd14f85c411430d53ab9 Mon Sep 17 00:00:00 2001 From: Philipp Schlehuber Date: Wed, 22 Mar 2023 11:00:48 +0100 Subject: [PATCH 271/337] Ignore ltargz.m4 * .gitignore: Ignore it * m4/ltargz.m4: Remove it --- .gitignore | 1 + m4/ltargz.m4 | 74 ---------------------------------------------------- 2 files changed, 1 insertion(+), 74 deletions(-) delete mode 100644 m4/ltargz.m4 diff --git a/.gitignore b/.gitignore index 7392a79db..155a9b5e7 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ configure config.log config.status aclocal.m4 +ltargz.m4 autom4te.cache libtool auto diff --git a/m4/ltargz.m4 b/m4/ltargz.m4 deleted file mode 100644 index 0908d90b9..000000000 --- a/m4/ltargz.m4 +++ /dev/null @@ -1,74 +0,0 @@ -# Portability macros for glibc argz. -*- Autoconf -*- -# -# Copyright (C) 2004-2007, 2011-2015 Free Software Foundation, Inc. -# Written by Gary V. Vaughan -# -# This file is free software; the Free Software Foundation gives -# unlimited permission to copy and/or distribute it, with or without -# modifications, as long as this notice is preserved. - -# serial 1 ltargz.m4 - -AC_DEFUN([LT_FUNC_ARGZ], [ -AC_CHECK_HEADERS([argz.h], [], [], [AC_INCLUDES_DEFAULT]) - -AC_CHECK_TYPES([error_t], - [], - [AC_DEFINE([error_t], [int], - [Define to a type to use for 'error_t' if it is not otherwise available.]) - AC_DEFINE([__error_t_defined], [1], [Define so that glibc/gnulib argp.h - does not typedef error_t.])], - [#if defined(HAVE_ARGZ_H) -# include -#endif]) - -LT_ARGZ_H= -AC_CHECK_FUNCS([argz_add argz_append argz_count argz_create_sep argz_insert \ - argz_next argz_stringify], [], [LT_ARGZ_H=lt__argz.h; AC_LIBOBJ([lt__argz])]) - -dnl if have system argz functions, allow forced use of -dnl libltdl-supplied implementation (and default to do so -dnl on "known bad" systems). Could use a runtime check, but -dnl (a) detecting malloc issues is notoriously unreliable -dnl (b) only known system that declares argz functions, -dnl provides them, yet they are broken, is cygwin -dnl releases prior to 16-Mar-2007 (1.5.24 and earlier) -dnl So, it's more straightforward simply to special case -dnl this for known bad systems. -AS_IF([test -z "$LT_ARGZ_H"], - [AC_CACHE_CHECK( - [if argz actually works], - [lt_cv_sys_argz_works], - [[case $host_os in #( - *cygwin*) - lt_cv_sys_argz_works=no - if test no != "$cross_compiling"; then - lt_cv_sys_argz_works="guessing no" - else - lt_sed_extract_leading_digits='s/^\([0-9\.]*\).*/\1/' - save_IFS=$IFS - IFS=-. - set x `uname -r | sed -e "$lt_sed_extract_leading_digits"` - IFS=$save_IFS - lt_os_major=${2-0} - lt_os_minor=${3-0} - lt_os_micro=${4-0} - if test 1 -lt "$lt_os_major" \ - || { test 1 -eq "$lt_os_major" \ - && { test 5 -lt "$lt_os_minor" \ - || { test 5 -eq "$lt_os_minor" \ - && test 24 -lt "$lt_os_micro"; }; }; }; then - lt_cv_sys_argz_works=yes - fi - fi - ;; #( - *) lt_cv_sys_argz_works=yes ;; - esac]]) - AS_IF([test yes = "$lt_cv_sys_argz_works"], - [AC_DEFINE([HAVE_WORKING_ARGZ], 1, - [This value is set to 1 to indicate that the system argz facility works])], - [LT_ARGZ_H=lt__argz.h - AC_LIBOBJ([lt__argz])])]) - -AC_SUBST([LT_ARGZ_H]) -]) From 1a0b1f235d16439cac4f20a13ab247f0fad9dc18 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Fri, 24 Mar 2023 13:52:37 +0100 Subject: [PATCH 272/337] * doc/tl/tl.tex: Typo in firstmatch semantics. --- doc/tl/tl.tex | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/tl/tl.tex b/doc/tl/tl.tex index f9205cced..371886711 100644 --- a/doc/tl/tl.tex +++ b/doc/tl/tl.tex @@ -701,7 +701,7 @@ $a$ is an atomic proposition. \VDash f\FSTAR{\mvar{i-1}..}))\\ \end{cases}\\ \sigma\VDash \FIRSTMATCH\code(f\code) & \iff - (\sigma\VDash f)\land (\forall k<|\sigma|,\,\sigma^{0..k}\nVDash f) + (\sigma\VDash f)\land (\forall k<|\sigma|,\,\sigma^{0..k-1}\nVDash f) \end{align*}} Notes: From 646b6e546f0549eb7e30100815d8a0a1e1505382 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Wed, 29 Mar 2023 16:20:51 +0200 Subject: [PATCH 273/337] fix spurious test-case failure when Python is not installed Fixes #530. * tests/core/ltlsynt2.test: Skip when PYTHON is empty. * NEWS: Mention the fix. --- NEWS | 5 ++++- tests/core/ltlsynt2.test | 4 +++- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/NEWS b/NEWS index 7f3be814d..7db7e111c 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,9 @@ New in spot 2.11.4.dev (not yet released) - Nothing yet. + Bug fixes: + + - Fix spurious failure of ltlsynt2.test when Python is not installed + (issue #530). New in spot 2.11.4 (2023-02-10) diff --git a/tests/core/ltlsynt2.test b/tests/core/ltlsynt2.test index dbb754d92..546cb0d27 100755 --- a/tests/core/ltlsynt2.test +++ b/tests/core/ltlsynt2.test @@ -1,6 +1,6 @@ #! /bin/sh # -*- coding: utf-8 -*- -# Copyright (C) 2022 Laboratoire de Recherche et Développement de +# Copyright (C) 2022, 2023 Laboratoire de Recherche et Développement de # l'Epita (LRDE). # # This file is part of Spot, a model checking library. @@ -36,6 +36,8 @@ ltlsynt --ins=i1,i2 -F formulas.ltl -f 'o1 & F(i1 <-> o2)' -q --csv=out.csv &&\ exit 2 test $? -eq 1 || exit 2 +test -z "$PYTHON" && exit 77 + cat >test.py < Date: Wed, 29 Mar 2023 17:01:13 +0200 Subject: [PATCH 274/337] correctly fails if emacs needed and missing Fixes #528. * configure.ac: Define EMACS using tools/missing. * NEWS: Mention the bug. --- NEWS | 3 +++ configure.ac | 2 +- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index 7db7e111c..2c8bb7d7d 100644 --- a/NEWS +++ b/NEWS @@ -5,6 +5,9 @@ New in spot 2.11.4.dev (not yet released) - Fix spurious failure of ltlsynt2.test when Python is not installed (issue #530). + - Building from the git repository would fail to report a missing + emacs (issue #528). + New in spot 2.11.4 (2023-02-10) Python: diff --git a/configure.ac b/configure.ac index e47e2eb29..772b4c24a 100644 --- a/configure.ac +++ b/configure.ac @@ -217,7 +217,7 @@ AC_CHECK_PROG([LTL3BA], [ltl3ba], [ltl3ba]) AC_CHECK_PROG([PERL], [perl], [perl]) AC_CHECK_PROG([SPIN], [spin], [spin]) AC_CHECK_PROG([LBTT], [lbtt], [lbtt]) -AC_CHECK_PROG([EMACS], [emacs], [emacs]) +AM_MISSING_PROG([EMACS], [emacs]) AC_CHECK_PROGS([IPYTHON], [ipython3 ipython], [ipython]) AC_CHECK_PROGS([JUPYTER], [jupyter], [jupyter]) AC_CHECK_PROG([LBTT_TRANSLATE], [lbtt-translate], [lbtt-translate]) From 993695a2c4c7f86948259ae49b58067bc95049a7 Mon Sep 17 00:00:00 2001 From: Philipp Schlehuber-Caissier Date: Thu, 30 Mar 2023 14:32:26 +0200 Subject: [PATCH 275/337] Fix parity solver if edgevector is not contiguous Validity of strategies was tested relying on num_edges() which might be smaller than the edge_number * spot/twaalgos/game.cc: Fix here * tests/python/game.py: Test here --- spot/twaalgos/game.cc | 6 +- tests/python/game.py | 139 +++++++++++++++++++++++++++++++++++++++++- 2 files changed, 143 insertions(+), 2 deletions(-) diff --git a/spot/twaalgos/game.cc b/spot/twaalgos/game.cc index 17f94a7e4..ac0e46520 100644 --- a/spot/twaalgos/game.cc +++ b/spot/twaalgos/game.cc @@ -214,10 +214,14 @@ namespace spot // Only the states owned by the winner need a strategy assert([&]() { + std::unordered_set valid_strat; + for (const auto& e : arena_->edges()) + valid_strat.insert(arena_->edge_number(e)); + for (unsigned v = 0; v < arena_->num_states(); ++v) { if (((*owner_ptr_)[v] == w_.winner(v)) - && ((s_[v] <= 0) || (s_[v] > arena_->num_edges()))) + && (valid_strat.count(s_.at(v)) == 0)) return false; } return true; diff --git a/tests/python/game.py b/tests/python/game.py index a7080b696..857390335 100644 --- a/tests/python/game.py +++ b/tests/python/game.py @@ -384,4 +384,141 @@ spot.solve_game(aut) S1 = list(spot.get_strategy(aut)) spot.solve_game(aut) S2 = list(spot.get_strategy(aut)) -tc.assertEqual(S1, S2) \ No newline at end of file +tc.assertEqual(S1, S2) + + +# Finite games +alive = "__alive__" +def finite_existential(auts): + # 1 Accepting state -> selfloop + # 2 Prune + acc_state = set() + sp = list(spot.get_state_players(auts)) + for e in auts.edges(): + if e.acc: + acc_state.add(e.src) + for s in acc_state: + e_kill = auts.out_iteraser(s) + while (e_kill): + e_kill.erase() + for s in acc_state: + sprime = auts.new_state() + sp.append(not sp[s]) + auts.new_edge(s, sprime, buddy.bddtrue, [0]) + auts.new_edge(sprime, s, buddy.bddtrue, [0]) + spot.set_state_players(auts, sp) + auts.purge_dead_states() + spot.alternate_players(auts, False, False) + return auts + +def is_input_complete(auts): + sp = spot.get_state_players(auts) + for s in range(auts.num_states()): + if sp[s]: + continue # Player + cumul = buddy.bddfalse + for e in auts.out(s): + cumul |= e.cond + if cumul != buddy.bddtrue: + return False + + return True + +def synt_from_ltlf(f:str, outs): + ff = spot.from_ltlf(f, alive) + aut = ff.translate("buchi", "sbacc") + outbdd = buddy.bddtrue + for out in outs: + outbdd &= buddy.bdd_ithvar(aut.register_ap(out)) + alive_bdd = buddy.bdd_ithvar(aut.register_ap(alive)) + auts = spot.split_2step(aut, outbdd & alive_bdd, False) + auts = spot.to_finite(auts, alive) + spot.alternate_players(auts, False, False) + spot.set_synthesis_outputs(auts, outbdd) + if not is_input_complete(auts): + print("Not synthesizable") + return None + auts = finite_existential(auts) + + return auts + +def synt_ltlf(f:str, outs, res:str = "aut"): + auts = synt_from_ltlf(f, outs) + + succ = spot.solve_parity_game(auts) + if not succ: + if res == "aut": + return False, auts + else: + return False, None + + mealy_cc = spot.solved_game_to_split_mealy(auts) + + if res == "aut": + return True, mealy_cc + elif res == "aig": + return True, spot.mealy_machine_to_aig(mealy_cc, "isop") + else: + raise RuntimeError("Unknown option") + + +sink_player = None + +def negate_ltlf(f:str, outs, opt = "buchi"): + + global sink_player + sink_player = None + + aut = synt_from_ltlf(f, outs) + # Implies input completeness + # We need output completeness + acc = [] + + sp = list(spot.get_state_players(aut)) + + def get_sink(): + global sink_player + if sink_player is None: + sink_player = aut.new_states(2) + aut.new_edge(sink_player, sink_player + 1, buddy.bddtrue, acc) + aut.new_edge(sink_player + 1, sink_player, buddy.bddtrue, acc) + sp.append(False) + sp.append(True) + spot.set_state_players(aut, sp) + return sink_player + + for s in range(aut.num_states()): + if not sp[s]: + continue + rem = buddy.bddtrue + for e in aut.out(s): + rem -= e.cond + if rem != buddy.bddfalse: + aut.new_edge(s, get_sink(), rem) + + # Better to invert colors or condition? + if opt == "buchi": + for e in aut.edges(): + if e.acc: + e.acc = spot.mark_t() + else: + e.acc = spot.mark_t([0]) + elif opt == "cobuchi": + aut.set_co_buchi() + else: + raise RuntimeError("Unknown opt") + return aut + +# Game where the edge_vector is larger +# than the number of transitions +f1 = "((((G (F (idle))) && (G (((idle) && (X ((! (grant_0)) \ + && (! (grant_1))))) -> (X (idle))))) && (G ((X (! (grant_0))) \ + || (X (((! (request_0)) && (! (idle))) U ((! (request_0)) \ + && (idle))))))) -> (((G (((((X (((! (grant_0)) && (true)) \ + || ((true) && (! (grant_1))))) && ((X (grant_0)) -> (request_0))) \ + && ((X (grant_1)) -> (request_1))) && ((request_0) -> (grant_1))) \ + && ((! (idle)) -> (X ((! (grant_0)) && (! (grant_1))))))) \ + && (! (F (G ((request_0) && (X (! (grant_0)))))))) \ + && (! (F (G ((request_1) && (X (! (grant_1)))))))))" +outs = ["grant_0", "grant1"] +tc.assertEqual(synt_ltlf(f1, outs)[0], False) \ No newline at end of file From eb0f40b9d650a264b0c2d3f21f7c8910e70c59fa Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Tue, 18 Apr 2023 14:48:10 +0200 Subject: [PATCH 276/337] twa_run: let as_twa work on the result of intersecting_run Reported by Philipp Schlehuber-Caissier. * spot/twaalgos/emptiness.cc (as_twa): Simplify considerably. Don't try to replay the run, and don't merge identical states. * spot/twaalgos/word.hh, spot/twaalgos/emptiness.hh: Improve documentation. * tests/python/intrun.py: Add a test case. * NEWS: Mention the bug. --- NEWS | 5 +++ spot/twaalgos/emptiness.cc | 75 +++++++++++--------------------------- spot/twaalgos/emptiness.hh | 6 +-- spot/twaalgos/word.hh | 7 +++- tests/python/intrun.py | 40 +++++++++++++++++++- 5 files changed, 73 insertions(+), 60 deletions(-) diff --git a/NEWS b/NEWS index 2c8bb7d7d..d670ba105 100644 --- a/NEWS +++ b/NEWS @@ -8,6 +8,11 @@ New in spot 2.11.4.dev (not yet released) - Building from the git repository would fail to report a missing emacs (issue #528). + - Fix exception raised by aut1.intersecting_run(aut2).as_twa() + because the run did not match transitions present in aut1 + verbatim. We also changed the behavior of as_twa() to not merge + identical states. + New in spot 2.11.4 (2023-02-10) Python: diff --git a/spot/twaalgos/emptiness.cc b/spot/twaalgos/emptiness.cc index fd3319141..ef8890f95 100644 --- a/spot/twaalgos/emptiness.cc +++ b/spot/twaalgos/emptiness.cc @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2009, 2011-2019, 2021 Laboratoire de Recherche et +// Copyright (C) 2009, 2011-2019, 2021, 2023 Laboratoire de Recherche et // Développement de l'Epita (LRDE). // Copyright (C) 2004, 2005 Laboratoire d'Informatique de Paris 6 (LIP6), // département Systèmes Répartis Coopératifs (SRC), Université Pierre @@ -570,7 +570,7 @@ namespace spot if (debug) os << "ERROR: First state of run (in " << in << "): " << aut->format_state(i->s) - << "\ndoes not match initial state of automata: " + << "\ndoes not match initial state of automaton: " << aut->format_state(s) << '\n'; s->destroy(); return false; @@ -802,38 +802,38 @@ namespace spot res->set_named_prop("state-names", names); } - const state* s = aut->get_init_state(); unsigned src; unsigned dst; const twa_run::steps* l; - acc_cond::mark_t seen_acc = {}; - - state_map seen; + unsigned cycle_entry = 0; if (prefix.empty()) - l = &cycle; + l = &cycle; else - l = &prefix; + l = &prefix; twa_run::steps::const_iterator i = l->begin(); - assert(s->compare(i->s) == 0); +#if NDEBUG + const state* init = aut->get_init_state(); + assert(init->compare(i->s) == 0); + init->destroy(); +#endif + src = res->new_state(); - seen.emplace(i->s, src); if (names) - names->push_back(aut->format_state(s)); + names->push_back(aut->format_state(i->s)); for (; i != l->end();) { - // expected outgoing transition bdd label = i->label; acc_cond::mark_t acc = i->acc; - // compute the next expected state const state* next; ++i; if (i != l->end()) { + dst = res->new_state(); next = i->s; } else @@ -842,57 +842,24 @@ namespace spot { l = &cycle; i = l->begin(); + cycle_entry = dst = res->new_state(); + } + else + { + dst = cycle_entry; } next = l->begin()->s; } - // browse the actual outgoing transitions and - // look for next; - const state* the_next = nullptr; - for (auto j: aut->succ(s)) + if (names && i != l->end()) { - if (j->cond() != label - || j->acc() != acc) - continue; - - const state* s2 = j->dst(); - if (s2->compare(next) == 0) - { - the_next = s2; - break; - } - s2->destroy(); + assert(dst == names->size()); + names->push_back(aut->format_state(next)); } - s->destroy(); - if (!the_next) - throw std::runtime_error("twa_run::as_twa() unable to replay run"); - s = the_next; - - - auto p = seen.emplace(next, 0); - if (p.second) - { - unsigned ns = res->new_state(); - p.first->second = ns; - if (names) - { - assert(ns == names->size()); - names->push_back(aut->format_state(next)); - } - } - dst = p.first->second; - res->new_edge(src, dst, label, acc); src = dst; - - // Sum acceptance conditions. - if (l == &cycle && i != l->begin()) - seen_acc |= acc; } - s->destroy(); - - assert(aut->acc().accepting(seen_acc)); return res; } diff --git a/spot/twaalgos/emptiness.hh b/spot/twaalgos/emptiness.hh index 47896a1d7..66bf8ca56 100644 --- a/spot/twaalgos/emptiness.hh +++ b/spot/twaalgos/emptiness.hh @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2011, 2013-2018, 2020-2021 Laboratoire de +// Copyright (C) 2011, 2013-2018, 2020-2021, 2023 Laboratoire de // Recherche et Developpement de l'Epita (LRDE). // Copyright (C) 2004, 2005 Laboratoire d'Informatique de Paris 6 (LIP6), // département Systèmes Répartis Coopératifs (SRC), Université Pierre @@ -451,9 +451,9 @@ namespace spot /// Note that this works only if the automaton is a twa_graph_ptr. void highlight(unsigned color); - /// \brief Return a twa_graph_ptr corresponding to \a run + /// \brief Convert the run into a lasso-shaped automaton /// - /// Identical states are merged. + /// This preserves the original acceptance condition. /// /// If \a preserve_names is set, the created states are named /// using the format_state() result from the original state. diff --git a/spot/twaalgos/word.hh b/spot/twaalgos/word.hh index f6f70fc14..979a4070b 100644 --- a/spot/twaalgos/word.hh +++ b/spot/twaalgos/word.hh @@ -1,6 +1,6 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2013, 2014, 2015, 2016, 2018, 2019 Laboratoire de Recherche et -// Développement de l'Epita (LRDE). +// Copyright (C) 2013-2016, 2018-2019, 2023 Laboratoire de Recherche +// et Développement de l'Epita (LRDE). // // This file is part of Spot, a model checking library. // @@ -80,6 +80,9 @@ namespace spot /// \brief Convert the twa_word as an automaton. /// + /// Convert the twa_word into a lasso-shapred automaton + /// with "true" acceptance condition. + /// /// This is useful to evaluate a word on an automaton. twa_graph_ptr as_automaton() const; diff --git a/tests/python/intrun.py b/tests/python/intrun.py index e3b708a95..02a7aedd6 100644 --- a/tests/python/intrun.py +++ b/tests/python/intrun.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Copyright (C) 2020, 2022 Laboratoire de Recherche et Développement +# Copyright (C) 2020, 2022, 2023 Laboratoire de Recherche et Développement # de l'Epita (LRDE). # # This file is part of Spot, a model checking library. @@ -38,3 +38,41 @@ r = b.intersecting_run(spot.complement(a)); c = spot.twa_word(r).as_automaton() tc.assertTrue(c.intersects(b)) tc.assertFalse(c.intersects(a)) + +# The next test came from Philipp Schlehuber-Caissier: running +# as_twa() on a run built from a A.intersecting_run(B) failed to build +# the automaton because it tried to rebuild the run on A and did not +# find transitions matching exactly. Additionally the idea of merging +# states in as_twa() seems to be a way to create some disasters, so we +# removed that too. +a = spot.translate("a"); +b = spot.translate("{a;1;a}"); +r = a.intersecting_run(b) +tc.assertEqual(str(r), """Prefix: + 1 + | a + 0 + | 1 {0} + 0 + | a {0} +Cycle: + 0 + | 1 {0} +""") +tc.assertEqual(r.as_twa().to_str(), """HOA: v1 +States: 4 +Start: 0 +AP: 1 "a" +acc-name: Buchi +Acceptance: 1 Inf(0) +properties: trans-labels explicit-labels state-acc deterministic +--BODY-- +State: 0 +[0] 1 +State: 1 {0} +[t] 2 +State: 2 {0} +[0] 3 +State: 3 {0} +[t] 3 +--END--""") From eb80f5d5af51a7ce1d94bf5c712880b4e44ee09f Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Tue, 18 Apr 2023 15:04:58 +0200 Subject: [PATCH 277/337] powerset: fix segfault when the initial state is a sink Reported by Raven Beutner. * spot/twaalgos/minimize.cc: Improve comment. * spot/twaalgos/powerset.cc: Fix handling of an initial state that is also a sink. * tests/core/wdba2.test: Add test case. * NEWS: Mention the bug. --- NEWS | 3 +++ spot/twaalgos/minimize.cc | 6 +++--- spot/twaalgos/powerset.cc | 26 ++++++++++++++------------ tests/core/wdba2.test | 37 +++++++++++++++++++++++++++++++++++-- 4 files changed, 55 insertions(+), 17 deletions(-) diff --git a/NEWS b/NEWS index d670ba105..9d14735e7 100644 --- a/NEWS +++ b/NEWS @@ -13,6 +13,9 @@ New in spot 2.11.4.dev (not yet released) verbatim. We also changed the behavior of as_twa() to not merge identical states. + - Fix segfaults occuring in determinization of 1-state terminal + automata. + New in spot 2.11.4 (2023-02-10) Python: diff --git a/spot/twaalgos/minimize.cc b/spot/twaalgos/minimize.cc index 4fd6847b3..1ac961d46 100644 --- a/spot/twaalgos/minimize.cc +++ b/spot/twaalgos/minimize.cc @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2010-2020 Laboratoire de Recherche et Développement +// Copyright (C) 2010-2020, 2023 Laboratoire de Recherche et Développement // de l'Epita (LRDE). // // This file is part of Spot, a model checking library. @@ -394,8 +394,8 @@ namespace spot else { // Find any accepting sink state, to speed up the - // determinization by merging all states containing a sink - // state. + // determinization by merging all macro-states containing a + // sink state. std::vector acc_sinks; unsigned ns = a->num_states(); if (!a->prop_terminal().is_true()) diff --git a/spot/twaalgos/powerset.cc b/spot/twaalgos/powerset.cc index c5fc07f94..326de7c76 100644 --- a/spot/twaalgos/powerset.cc +++ b/spot/twaalgos/powerset.cc @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2009-2011, 2013-2019, 2021 Laboratoire de Recherche et +// Copyright (C) 2009-2011, 2013-2019, 2021, 2023 Laboratoire de Recherche et // Développement de l'Epita (LRDE). // Copyright (C) 2004 Laboratoire d'Informatique de Paris 6 (LIP6), // département Systèmes Répartis Coopératifs (SRC), Université Pierre @@ -217,17 +217,19 @@ namespace spot pm.map_.emplace_back(std::move(ps)); } - { - unsigned init_num = aut->get_init_state_number(); - auto bvi = make_bitvect(ns); - bvi->set(init_num); - power_state ps{init_num}; - unsigned num = res->new_state(); - res->set_init_state(num); - seen[bvi] = num; - assert(pm.map_.size() == num); - pm.map_.emplace_back(std::move(ps)); - toclean.emplace_back(bvi); + // Add the initial state unless it's a sink. + if (unsigned init_num = aut->get_init_state_number(); + !acc_sinks || !acc_sinks->get(init_num)) + { + auto bvi = make_bitvect(ns); + bvi->set(init_num); + power_state ps{init_num}; + unsigned num = res->new_state(); + res->set_init_state(num); + seen[bvi] = num; + assert(pm.map_.size() == num); + pm.map_.emplace_back(std::move(ps)); + toclean.emplace_back(bvi); } // outgoing map diff --git a/tests/core/wdba2.test b/tests/core/wdba2.test index ca49bad94..3850a447a 100755 --- a/tests/core/wdba2.test +++ b/tests/core/wdba2.test @@ -1,7 +1,7 @@ #!/bin/sh # -*- coding: utf-8 -*- -# Copyright (C) 2012, 2014, 2015, 2018, 2019 Laboratoire de Recherche et -# Développement de l'Epita (LRDE). +# Copyright (C) 2012, 2014-2015, 2018-2019, 2023 Laboratoire de +# Recherche et Développement de l'Epita (LRDE). # # This file is part of Spot, a model checking library. # @@ -82,3 +82,36 @@ EOF autfilt --small --high -C -Hi input > output diff output expected + +# This test comes from a report from Raven Beutner and used to cause a +# segfault. +cat >input <output +cat >expected < Date: Tue, 18 Apr 2023 17:35:05 +0200 Subject: [PATCH 278/337] org: replace version references with org-babel blocks This way we have fewer lines to edit multiple when making releases. * doc/org/index.org, doc/org/init.el.in, doc/org/install.org, doc/org/setup.org, doc/org/tools.org: Use org-babel instead of macros for release version and links. --- doc/org/index.org | 2 +- doc/org/init.el.in | 2 +- doc/org/install.org | 4 ++-- doc/org/setup.org | 20 ++++++++++++++++---- doc/org/tools.org | 6 +++--- 5 files changed, 23 insertions(+), 11 deletions(-) diff --git a/doc/org/index.org b/doc/org/index.org index 9af23dba4..08fa16a3d 100644 --- a/doc/org/index.org +++ b/doc/org/index.org @@ -37,7 +37,7 @@ checking. It has the following notable features: * Latest version -The latest version is *{{{LASTRELEASE}}}* and was released on +The latest version is *call_SPOT_VERSION()* and was released on *{{{LASTDATE}}}*. Please see the [[file:install.org][download and installation instructions]]. * Documentation diff --git a/doc/org/init.el.in b/doc/org/init.el.in index 4258a95f7..c46363096 100644 --- a/doc/org/init.el.in +++ b/doc/org/init.el.in @@ -160,7 +160,7 @@ up.html points to index.html, then the result is: (setq body res) (not cmp))) (concat "#+TITLE: " title - "\n#+SETUPFILE: setup.org\n#+HTML_LINK_UP: index.html\n\n" + "\n#+INCLUDE: setup.org\n#+HTML_LINK_UP: index.html\n\n" body))) (setq org-publish-project-alist diff --git a/doc/org/install.org b/doc/org/install.org index dc492af57..b65c02074 100644 --- a/doc/org/install.org +++ b/doc/org/install.org @@ -9,9 +9,9 @@ :CUSTOM_ID: tar :END: -The latest release of Spot is version {{{LASTRELEASE}}}: +The latest release of Spot is version call_SPOT_VERSION() and was released on {{{LASTDATE}}}: -- {{{LASTTARBALL}}} (see also the {{{LASTNEWS}}}) +- call_TARBALL_LINK() (see also the call_NEWS_LINK()) Past releases can be found [[https://www.lrde.epita.fr/dload/spot/][in the same directory]]. If you are interested in /future/ releases, you can always peek at the [[https://gitlab.lre.epita.fr/spot/spot/-/jobs/artifacts/next/browse?job=make-dist][last diff --git a/doc/org/setup.org b/doc/org/setup.org index 7b6a4fa70..974272774 100644 --- a/doc/org/setup.org +++ b/doc/org/setup.org @@ -1,11 +1,23 @@ #+OPTIONS: H:2 num:nil toc:t html-postamble:nil ^:nil #+EMAIL: spot@lrde.epita.fr #+HTML_LINK_HOME: index.html -#+MACRO: SPOTVERSION 2.11.4 -#+MACRO: LASTRELEASE 2.11.4 -#+MACRO: LASTTARBALL [[http://www.lrde.epita.fr/dload/spot/spot-2.11.3.tar.gz][=spot-2.11.4.tar.gz=]] -#+MACRO: LASTNEWS [[https://gitlab.lre.epita.fr/spot/spot/blob/spot-2-11-3/NEWS][summary of the changes]] #+MACRO: LASTDATE 2023-02-10 +#+NAME: SPOT_VERSION +#+BEGIN_SRC python :exports none :results value :wrap org +return "2.11.4" +#+END_SRC + +#+NAME: TARBALL_LINK +#+BEGIN_SRC python :exports none :var version=SPOT_VERSION :results output :wrap org + print(f"[[http://www.lrde.epita.fr/dload/spot/spot-{version}.tar.gz][=spot-{version}.tar.gz=]]") +#+END_SRC + +#+NAME: NEWS_LINK +#+BEGIN_SRC python :exports none :var version=SPOT_VERSION :results output :wrap org + version = version.replace('.', '-') + print(f"[[https://gitlab.lre.epita.fr/spot/spot/blob/spot-{version}/NEWS][summary of the changes]]") +#+END_SRC + #+ATTR_HTML: :id spotlogo [[file:spot2.svg]] diff --git a/doc/org/tools.org b/doc/org/tools.org index 5227f1b4e..46ca38ccd 100644 --- a/doc/org/tools.org +++ b/doc/org/tools.org @@ -1,12 +1,12 @@ # -*- coding: utf-8 -*- -#+TITLE: Command-line tools installed by Spot {{{SPOTVERSION}}} -#+DESCRIPTION: List of all the command-line tools installed by Spot {{{SPOTVERSION}}} #+INCLUDE: setup.org +#+TITLE: Command-line tools installed by Spot +#+DESCRIPTION: List of all the command-line tools installed by Spot #+HTML_LINK_UP: index.html #+PROPERTY: header-args:sh :results verbatim :exports both This document introduces command-line tools that are installed with -the Spot library. We give some examples to highlight possible +Spot call_SPOT_VERSION(). We give some examples to highlight possible use-cases but shall not attempt to cover all features exhaustively (please check the man pages for further inspiration). From b6c076ce1946a083ab2cd14146160edbce8ce72c Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Thu, 20 Apr 2023 09:43:33 +0200 Subject: [PATCH 279/337] release Spot 2.11.5 * NEWS, configure.ac, doc/org/setup.org: Update version. --- NEWS | 5 ++++- configure.ac | 2 +- doc/org/setup.org | 4 ++-- 3 files changed, 7 insertions(+), 4 deletions(-) diff --git a/NEWS b/NEWS index 9d14735e7..bc43ce155 100644 --- a/NEWS +++ b/NEWS @@ -1,4 +1,4 @@ -New in spot 2.11.4.dev (not yet released) +New in spot 2.11.5 (2023-04-20) Bug fixes: @@ -16,6 +16,9 @@ New in spot 2.11.4.dev (not yet released) - Fix segfaults occuring in determinization of 1-state terminal automata. + - Fix incorrect assertion in game solver when the edge vector + contains deleted transitions. + New in spot 2.11.4 (2023-02-10) Python: diff --git a/configure.ac b/configure.ac index 772b4c24a..ef5ce7391 100644 --- a/configure.ac +++ b/configure.ac @@ -21,7 +21,7 @@ # along with this program. If not, see . AC_PREREQ([2.69]) -AC_INIT([spot], [2.11.4.dev], [spot@lrde.epita.fr]) +AC_INIT([spot], [2.11.5], [spot@lrde.epita.fr]) AC_CONFIG_AUX_DIR([tools]) AC_CONFIG_MACRO_DIR([m4]) AM_INIT_AUTOMAKE([1.11 gnu tar-ustar color-tests parallel-tests]) diff --git a/doc/org/setup.org b/doc/org/setup.org index 974272774..255a01c3d 100644 --- a/doc/org/setup.org +++ b/doc/org/setup.org @@ -1,11 +1,11 @@ #+OPTIONS: H:2 num:nil toc:t html-postamble:nil ^:nil #+EMAIL: spot@lrde.epita.fr #+HTML_LINK_HOME: index.html -#+MACRO: LASTDATE 2023-02-10 +#+MACRO: LASTDATE 2023-04-20 #+NAME: SPOT_VERSION #+BEGIN_SRC python :exports none :results value :wrap org -return "2.11.4" +return "2.11.5" #+END_SRC #+NAME: TARBALL_LINK From d0ae0dfc388df2c744c25af722de19e863a38232 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Thu, 20 Apr 2023 09:48:22 +0200 Subject: [PATCH 280/337] * NEWS, configure.ac: Bump version to 2.11.5.dev. --- NEWS | 4 ++++ configure.ac | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/NEWS b/NEWS index bc43ce155..0b425272f 100644 --- a/NEWS +++ b/NEWS @@ -1,3 +1,7 @@ +New in spot 2.11.5.dev (not yet released) + + Nothing yet. + New in spot 2.11.5 (2023-04-20) Bug fixes: diff --git a/configure.ac b/configure.ac index ef5ce7391..09fe45364 100644 --- a/configure.ac +++ b/configure.ac @@ -21,7 +21,7 @@ # along with this program. If not, see . AC_PREREQ([2.69]) -AC_INIT([spot], [2.11.5], [spot@lrde.epita.fr]) +AC_INIT([spot], [2.11.5.dev], [spot@lrde.epita.fr]) AC_CONFIG_AUX_DIR([tools]) AC_CONFIG_MACRO_DIR([m4]) AM_INIT_AUTOMAKE([1.11 gnu tar-ustar color-tests parallel-tests]) From 747ec8b1c56e0b286086762b73e34fafaf2f1c18 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Thu, 11 May 2023 21:25:59 +0200 Subject: [PATCH 281/337] debian: add missing build dependencies * debian/control: Add Build-Depends on graphviz, jupyter-nbconvert, doxygen. --- debian/control | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/debian/control b/debian/control index d1f9c652c..e29454c54 100644 --- a/debian/control +++ b/debian/control @@ -2,7 +2,7 @@ Source: spot Section: science Priority: optional Maintainer: Alexandre Duret-Lutz -Build-Depends: debhelper (>= 12), python3-all-dev, ipython3-notebook | python3-ipykernel, ipython3-notebook | python3-nbconvert, libltdl-dev, dh-python +Build-Depends: debhelper (>= 12), python3-all-dev, ipython3-notebook | python3-ipykernel, ipython3-notebook | python3-nbconvert, libltdl-dev, dh-python, graphviz, jupyter-nbconvert, doxygen Standards-Version: 4.5.1 Homepage: http://spot.lrde.epita.fr/ From 134da9209c50be09fbe0fc3511898787acdb6fcd Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Thu, 11 May 2023 21:40:14 +0200 Subject: [PATCH 282/337] genem: Add yet another version of the algorithm * spot/twa/acc.hh, spot/twa/acc.cc (fin_unit_one_split_improved): New function. * python/spot/impl.i: Add bindings for fin_unit_one_split_improved. * spot/twaalgos/genem.cc: Add the spot212 version. * tests/python/genem.py: Test it. --- python/spot/impl.i | 12 +++++++++- spot/twa/acc.cc | 51 ++++++++++++++++++++++++++++++++++++++---- spot/twa/acc.hh | 36 ++++++++++++++++++++--------- spot/twaalgos/genem.cc | 26 +++++++++++++++++---- tests/python/genem.py | 11 +++++---- 5 files changed, 112 insertions(+), 24 deletions(-) diff --git a/python/spot/impl.i b/python/spot/impl.i index 502770fcb..f95270e21 100644 --- a/python/spot/impl.i +++ b/python/spot/impl.i @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2009-2022 Laboratoire de Recherche et Développement +// Copyright (C) 2009-2023 Laboratoire de Recherche et Développement // de l'Epita (LRDE). // Copyright (C) 2003-2006 Laboratoire d'Informatique de Paris 6 // (LIP6), département Systèmes Répartis Coopératifs (SRC), Université @@ -564,6 +564,16 @@ namespace std { swig::from(std::get<2>(v))); } %} +// Must occur before the twa declaration +%typemap(out) SWIGTYPE spot::acc_cond::fin_unit_one_split_improved %{ + { + auto& v = static_cast>($1); + $result = PyTuple_Pack(3, + swig::from(std::get<0>(v)), + swig::from(std::get<1>(v)), + swig::from(std::get<2>(v))); + } +%} %include %template(pair_bool_mark) std::pair; diff --git a/spot/twa/acc.cc b/spot/twa/acc.cc index 07aac36f9..d73af33b0 100644 --- a/spot/twa/acc.cc +++ b/spot/twa/acc.cc @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2015-2022 Laboratoire de Recherche et Développement +// Copyright (C) 2015-2023 Laboratoire de Recherche et Développement // de l'Epita. // // This file is part of Spot, a model checking library. @@ -2707,8 +2707,9 @@ namespace spot return false; } - // Check wheter pos looks like Fin(f) or Fin(f)&rest - bool is_conj_fin(const acc_cond::acc_word* pos, acc_cond::mark_t f) + // Check if pos contains Fin(f) in a substree + template + bool has_fin(const acc_cond::acc_word* pos, acc_cond::mark_t f) { auto sub = pos - pos->sub.size; do @@ -2719,7 +2720,10 @@ namespace spot --pos; break; case acc_cond::acc_op::Or: - pos -= pos->sub.size + 1; + if constexpr (top_conjunct_only) + pos -= pos->sub.size + 1; + else + --pos; break; case acc_cond::acc_op::Fin: if (pos[-1].mark & f) @@ -2736,6 +2740,12 @@ namespace spot return false; } + // Check whether pos looks like Fin(f) or Fin(f)&rest + bool is_conj_fin(const acc_cond::acc_word* pos, acc_cond::mark_t f) + { + return has_fin(pos, f); + } + acc_cond::acc_code extract_fin(const acc_cond::acc_word* pos, acc_cond::mark_t f) { @@ -2772,6 +2782,7 @@ namespace spot return {}; } + template std::pair split_top_fin(const acc_cond::acc_word* pos, acc_cond::mark_t f) { @@ -2798,6 +2809,17 @@ namespace spot tmp |= std::move(left); std::swap(tmp, left); } + else if (deeper_check + && has_top_fin(pos) == -1 + && has_fin(pos, f)) + { + auto tmp = strip_rec(pos, f, true, false); + tmp |= std::move(left); + std::swap(tmp, left); + tmp = force_inf_rec(pos, f); + tmp |= std::move(right); + std::swap(tmp, right); + } else { acc_cond::acc_code tmp(pos); @@ -2851,6 +2873,27 @@ namespace spot return {selected_fin, extract_fin(pos, fo_m), force_inf(fo_m)}; } + std::tuple + acc_cond::acc_code::fin_unit_one_split_improved() const + { + if (SPOT_UNLIKELY(is_t() || is_f())) + err: + throw std::runtime_error("fin_unit_one_split_improved(): no Fin"); + const acc_cond::acc_word* pos = &back(); + int selected_fin = has_top_fin(pos); + if (selected_fin >= 0) + { + auto [left, right] = + split_top_fin(pos, {(unsigned) selected_fin}); + return {selected_fin, std::move(left), std::move(right)}; + } + selected_fin = fin_one(); + if (selected_fin < 0) + goto err; + acc_cond::mark_t fo_m = {(unsigned) selected_fin}; + return {selected_fin, extract_fin(pos, fo_m), force_inf(fo_m)}; + } + namespace { bool diff --git a/spot/twa/acc.hh b/spot/twa/acc.hh index 1c460cfc4..1b46e4024 100644 --- a/spot/twa/acc.hh +++ b/spot/twa/acc.hh @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2014-2022 Laboratoire de Recherche et Développement +// Copyright (C) 2014-2023 Laboratoire de Recherche et Développement // de l'Epita. // // This file is part of Spot, a model checking library. @@ -1300,21 +1300,26 @@ namespace spot /// \brief Split an acceptance condition, trying to select one /// unit-Fin. /// - /// If the condition is a disjunction and one of the disjunct as - /// has the shape `...&Fin(i)&...`, then this will return - /// (i, left, right), where left is all disjunct of this form, and - /// right are all the others. + /// If the condition is a disjunction and one of the disjunct has + /// the shape `...&Fin(i)&...`, then this will return (i, left, + /// right), where left is all disjunct of this form (with Fin(i) + /// replaced by true), and right are all the others. /// /// If the input formula has the shape `...&Fin(i)&...` then left - /// is set to the entire formula, and right is empty. + /// is set to the entire formula (with Fin(i) replaced by true), + /// and right is empty. /// /// If no disjunct has the right shape, then a random Fin(i) is /// searched in the formula, and the output (i, left, right). /// is such that left contains all disjuncts containing Fin(i) /// (at any depth), and right contains the original formlula /// where Fin(i) has been replaced by false. + /// @{ std::tuple fin_unit_one_split() const; + std::tuple + fin_unit_one_split_improved() const; + /// @} /// \brief Help closing accepting or rejecting cycle. /// @@ -2258,25 +2263,34 @@ namespace spot /// \brief Split an acceptance condition, trying to select one /// unit-Fin. /// - /// If the condition is a disjunction and one of the disjunct as - /// has the shape `...&Fin(i)&...`, then this will return - /// (i, left, right), where left is all disjunct of this form, and - /// right are all the others. + /// If the condition is a disjunction and one of the disjunct has + /// the shape `...&Fin(i)&...`, then this will return (i, left, + /// right), where left is all disjunct of this form (with Fin(i) + /// replaced by true), and right are all the others. /// /// If the input formula has the shape `...&Fin(i)&...` then left - /// is set to the entire formula, and right is empty. + /// is set to the entire formula (with Fin(i) replaced by true), + /// and right is empty. /// /// If no disjunct has the right shape, then a random Fin(i) is /// searched in the formula, and the output (i, left, right). /// is such that left contains all disjuncts containing Fin(i) /// (at any depth), and right contains the original formlula /// where Fin(i) has been replaced by false. + /// @{ std::tuple fin_unit_one_split() const { auto [f, l, r] = code_.fin_unit_one_split(); return {f, {num_sets(), std::move(l)}, {num_sets(), std::move(r)}}; } + std::tuple + fin_unit_one_split_improved() const + { + auto [f, l, r] = code_.fin_unit_one_split_improved(); + return {f, {num_sets(), std::move(l)}, {num_sets(), std::move(r)}}; + } + /// @} /// \brief Return the top-level disjuncts. /// diff --git a/spot/twaalgos/genem.cc b/spot/twaalgos/genem.cc index 237b10118..0b0d1fd5f 100644 --- a/spot/twaalgos/genem.cc +++ b/spot/twaalgos/genem.cc @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2017-2022 Laboratoire de Recherche et Developpement +// Copyright (C) 2017-2023 Laboratoire de Recherche et Developpement // de l'Epita (LRDE). // // This file is part of Spot, a model checking library. @@ -25,7 +25,7 @@ namespace spot { namespace { - enum genem_version_t { spot28, atva19, spot29, spot210, spot211 }; + enum genem_version_t { spot28, atva19, spot29, spot210, spot211, spot212 }; static genem_version_t genem_version = spot29; } @@ -33,6 +33,8 @@ namespace spot { if (emversion == nullptr || !strcasecmp(emversion, "spot29")) genem_version = spot29; + else if (!strcasecmp(emversion, "spot212")) + genem_version = spot212; else if (!strcasecmp(emversion, "spot211")) genem_version = spot211; else if (!strcasecmp(emversion, "spot210")) @@ -44,7 +46,7 @@ namespace spot else throw std::invalid_argument("generic_emptiness_check version should be " "one of {spot28, atva19, spot29, spot210, " - "spot211}"); + "spot211, spot212}"); } namespace @@ -87,7 +89,9 @@ namespace spot scc_split_check(const scc_info& si, unsigned scc, const acc_cond& acc, Extra extra, acc_cond::mark_t tocut) { - if (genem_version == spot211 || genem_version == spot210) + if (genem_version == spot211 + || genem_version == spot212 + || genem_version == spot210) tocut |= acc.fin_unit(); scc_and_mark_filter filt(si, scc, tocut); filt.override_acceptance(acc); @@ -144,6 +148,20 @@ namespace spot } while (!acc.is_f()); } + else if (genem_version == spot212) + { + do + { + auto [fo, fpart, rest] = acc.fin_unit_one_split_improved(); + acc_cond::mark_t fo_m = {(unsigned) fo}; + if (!scc_split_check + (si, scc, fpart, extra, fo_m)) + if constexpr (EarlyStop) + return false; + acc = rest; + } + while (!acc.is_f()); + } else if (genem_version == spot29) do { diff --git a/tests/python/genem.py b/tests/python/genem.py index 962112ac0..970fe705b 100644 --- a/tests/python/genem.py +++ b/tests/python/genem.py @@ -1,5 +1,5 @@ # -*- mode: python; coding: utf-8 -*- -# Copyright (C) 2018-2022 Laboratoire de Recherche et Développement de +# Copyright (C) 2018-2023 Laboratoire de Recherche et Développement de # l'Epita (LRDE). # # This file is part of Spot, a model checking library. @@ -307,15 +307,18 @@ def run_bench(automata): res3d = spot.generic_emptiness_check(aut) spot.generic_emptiness_check_select_version("spot211") res3e = spot.generic_emptiness_check(aut) + spot.generic_emptiness_check_select_version("spot212") + res3f = spot.generic_emptiness_check(aut) spot.generic_emptiness_check_select_version("spot29") res2 = spot.remove_fin(aut).is_empty() res1 = generic_emptiness2(aut) res = (str(res1)[0] + str(res2)[0] + str(res3a)[0] + str(res3b)[0] + str(res3c)[0] + str(res3d)[0] - + str(res3e)[0] + str(res4)[0] + str(res5)[0]) + + str(res3e)[0] + str(res3f)[0] + str(res4)[0] + + str(res5)[0]) print(res) - tc.assertIn(res, ('TTTTTTTTT', 'FFFFFFFFF')) - if res == 'FFFFFFFFF': + tc.assertIn(res, ('TTTTTTTTTT', 'FFFFFFFFFF')) + if res == 'FFFFFFFFFF': run3 = spot.generic_accepting_run(aut) tc.assertTrue(run3.replay(spot.get_cout())) From abe722297306c75ee1b2521e5dbc7a32a83d5df0 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Fri, 12 May 2023 11:32:46 +0200 Subject: [PATCH 283/337] bitvect: work around incorrect warning from gcc * spot/misc/bitvect.hh: Don't free the old ptr if realloc() returns NULL, as this confuse GCC who warns that we are freeing something that has already been freed. Instead, let the ~bitvect() destructor handle this. --- spot/misc/bitvect.hh | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/spot/misc/bitvect.hh b/spot/misc/bitvect.hh index 3588b406e..74ab2bf3f 100644 --- a/spot/misc/bitvect.hh +++ b/spot/misc/bitvect.hh @@ -1,5 +1,5 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2013-2021 Laboratoire de Recherche et Développement +// Copyright (C) 2013-2021, 2023 Laboratoire de Recherche et Développement // de l'Epita (LRDE). // // This file is part of Spot, a model checking library. @@ -111,22 +111,22 @@ namespace spot return; if (storage_ == &local_storage_) { - block_t* new_storage_ = static_cast + block_t* new_storage = static_cast (malloc(new_block_count * sizeof(block_t))); + if (SPOT_UNLIKELY(!new_storage)) + throw std::bad_alloc(); for (size_t i = 0; i < block_count_; ++i) - new_storage_[i] = storage_[i]; - storage_ = new_storage_; + new_storage[i] = storage_[i]; + storage_ = new_storage; } else { - auto old = storage_; - storage_ = static_cast - (realloc(old, new_block_count * sizeof(block_t))); - if (!storage_) - { - free(old); - throw std::bad_alloc(); - } + block_t* new_storage = static_cast + (realloc(storage_, new_block_count * sizeof(block_t))); + if (SPOT_UNLIKELY(!new_storage)) + // storage_, untouched, will be freed by the destructor. + throw std::bad_alloc(); + storage_ = new_storage; } block_count_ = new_block_count; } @@ -134,8 +134,8 @@ namespace spot private: void grow() { - size_t new_block_count_ = (block_count_ + 1) * 7 / 5; - reserve_blocks(new_block_count_); + size_t new_block_count = (block_count_ + 1) * 7 / 5; + reserve_blocks(new_block_count); } public: From 7868115a8bcf98e6b89b9de1d2c06953c2ee21f2 Mon Sep 17 00:00:00 2001 From: Florian Renkin Date: Thu, 4 May 2023 15:28:48 +0200 Subject: [PATCH 284/337] parity_type_to_parity: Add missing cases * spot/twaalgos/toparity.cc: Correct some cases where the solution was not detected. * tests/python/toparity.py: Update tests. --- spot/twaalgos/toparity.cc | 53 ++++++++++++++++++++++++--------------- tests/python/toparity.py | 24 +++++++++++++++++- 2 files changed, 56 insertions(+), 21 deletions(-) diff --git a/spot/twaalgos/toparity.cc b/spot/twaalgos/toparity.cc index c936ef57b..a82c7d57a 100644 --- a/spot/twaalgos/toparity.cc +++ b/spot/twaalgos/toparity.cc @@ -95,7 +95,8 @@ namespace spot const bool need_equivalent, std::vector &status, std::vector &res_colors, - acc_cond &new_cond, bool &was_able_to_color) + acc_cond &new_cond, bool &was_able_to_color, + unsigned max_col) { auto& ev = aut->edge_vector(); const auto ev_size = ev.size(); @@ -134,7 +135,7 @@ namespace spot kind == cond_kind::INF_PARITY; unsigned max_iter = want_parity ? -1U : 1; - unsigned color = want_parity ? SPOT_MAX_ACCSETS - 1 : 0; + unsigned color = max_col; // Do we want always accepting transitions? // Don't consider CO_BUCHI as it is done by Büchi bool search_inf = kind != cond_kind::FIN_PARITY; @@ -167,14 +168,15 @@ namespace spot auto filter_data = filter_data_t{aut, status}; scc_info si(aut, aut_init, filter, &filter_data, scc_info_options::TRACK_STATES); + if (search_inf) + si.determine_unknown_acceptance(); bool worked = false; unsigned ssc_size = si.scc_count(); for (unsigned scc = 0; scc < ssc_size; ++scc) { // scc_info can detect that we will not be able to find an - // accepting/rejecting cycle. - if (!((search_inf && !si.is_accepting_scc(scc)) || - (!search_inf && !si.is_rejecting_scc(scc)))) + // accepting cycle. + if ((search_inf && si.is_accepting_scc(scc)) || !search_inf) { accepting_transitions_scc(si, scc, cond, {}, not_decidable_transitions, *keep); @@ -224,6 +226,8 @@ namespace spot break; } + new_cond = acc_cond(new_code); + // We check parity if (need_equivalent) { @@ -269,19 +273,19 @@ namespace spot aut->set_acceptance(acc_cond(aut_acc_comp)); } } - new_cond = acc_cond(new_code); + return true; } static twa_graph_ptr cond_type_main(const twa_graph_ptr &aut, const cond_kind kind, - bool &was_able_to_color) + bool &was_able_to_color, unsigned max_color) { std::vector res_colors; std::vector status; acc_cond new_cond; if (cond_type_main_aux(aut, kind, true, status, res_colors, new_cond, - was_able_to_color)) + was_able_to_color, max_color)) { auto res = make_twa_graph(aut, twa::prop_set::all()); auto &res_vector = res->edge_vector(); @@ -311,14 +315,19 @@ namespace spot bool was_able_to_color; // If the automaton is parity-type with a condition that has Inf as // outermost term - auto res = cond_type_main(aut, cond_kind::INF_PARITY, was_able_to_color); + auto res = cond_type_main(aut, cond_kind::INF_PARITY, + was_able_to_color, SPOT_MAX_ACCSETS - 1); // If it was impossible to find an accepting edge, it is perhaps possible // to find a rejecting transition if (res == nullptr && !was_able_to_color) - res = cond_type_main(aut, cond_kind::FIN_PARITY, was_able_to_color); + res = cond_type_main(aut, cond_kind::FIN_PARITY, + was_able_to_color, SPOT_MAX_ACCSETS - 1); if (res) + { + res->prop_state_acc(false); reduce_parity_here(res); + } return res; } @@ -326,14 +335,14 @@ namespace spot buchi_type_to_buchi(const twa_graph_ptr &aut) { bool useless; - return cond_type_main(aut, cond_kind::BUCHI, useless); + return cond_type_main(aut, cond_kind::BUCHI, useless, 0); } twa_graph_ptr co_buchi_type_to_co_buchi(const twa_graph_ptr &aut) { bool useless; - return cond_type_main(aut, cond_kind::CO_BUCHI, useless); + return cond_type_main(aut, cond_kind::CO_BUCHI, useless, 0); } // New version for paritizing @@ -1943,12 +1952,14 @@ namespace spot // Is the maximal color accepting? bool start_inf = true; cond_type_main_aux(sub_aut, cond_kind::INF_PARITY, false, status, - res_colors, new_cond, was_able_to_color); + res_colors, new_cond, was_able_to_color, + SPOT_MAX_ACCSETS - 1); // Otherwise we can try to find a rejecting transition as first step if (!was_able_to_color) { cond_type_main_aux(sub_aut, cond_kind::FIN_PARITY, false, status, - res_colors, new_cond, was_able_to_color); + res_colors, new_cond, was_able_to_color, + SPOT_MAX_ACCSETS - 1); if (!was_able_to_color) return false; start_inf = false; @@ -2127,11 +2138,11 @@ namespace spot bool is_co_bu = false; bool was_able_to_color; if (!cond_type_main_aux(sub_aut, cond_kind::BUCHI, true, status, - res_colors, new_cond, was_able_to_color)) + res_colors, new_cond, was_able_to_color, 0)) { is_co_bu = true; if (!cond_type_main_aux(sub_aut, cond_kind::CO_BUCHI, true, status, - res_colors, new_cond, was_able_to_color)) + res_colors, new_cond, was_able_to_color, 0)) return false; change_to_odd(); } @@ -2172,16 +2183,18 @@ namespace spot acc_cond new_cond; bool was_able_to_color; if (!cond_type_main_aux(sub_aut, cond_kind::INF_PARITY, true, status, - res_colors, new_cond, was_able_to_color)) + res_colors, new_cond, was_able_to_color, + SPOT_MAX_ACCSETS - 3)) { if (!cond_type_main_aux(sub_aut, cond_kind::FIN_PARITY, true, status, - res_colors, new_cond, was_able_to_color)) + res_colors, new_cond, was_able_to_color, + SPOT_MAX_ACCSETS - 3)) return false; } bool is_max, is_odd; new_cond.is_parity(is_max, is_odd); - auto [min, max] = - std::minmax_element(res_colors.begin() + 1, res_colors.end()); + auto min = + std::min_element(res_colors.begin() + 1, res_colors.end()); // cond_type_main_aux returns a parity max condition assert(is_max); auto col_fun = diff --git a/tests/python/toparity.py b/tests/python/toparity.py index ab5fbf314..80c2c19ef 100644 --- a/tests/python/toparity.py +++ b/tests/python/toparity.py @@ -547,4 +547,26 @@ State: 9 3 {4} 2 3 {4} 6 --END-- b = spot.iar_maybe(a) tc.assertEqual(b.num_states(), 87) tc.assertTrue(a.equivalent_to(b)) -test(a, [87, 91, 91, 87, 87, 87, 51, 51, 21]) +test(a, [87, 91, 91, 87, 87, 87, 51, 35, 21]) + +a = spot.automaton("""HOA: v1 +States: 4 +Start: 0 +AP: 2 "p0" "p1" +Acceptance: 2 Fin(1) & Fin(0) +properties: trans-labels explicit-labels state-acc +--BODY-- +State: 0 +[!0&!1] 2 +[!0&!1] 1 +State: 1 +[!0&1] 0 +[0&1] 3 +State: 2 +[0&!1] 1 +State: 3 {0} +[!0&1] 3 +[!0&!1] 1 +--END--""") +b = spot.parity_type_to_parity(a) +tc.assertTrue(spot.are_equivalent(a, b)) From 4535b4a91542b2fd269608db89ae9b3ab711a091 Mon Sep 17 00:00:00 2001 From: Antoine Martin Date: Wed, 20 Oct 2021 11:54:16 +0200 Subject: [PATCH 285/337] nix: setup Nix Flake file * flake.nix, flake.lock: here --- flake.lock | 43 +++++++++++ flake.nix | 211 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 254 insertions(+) create mode 100644 flake.lock create mode 100644 flake.nix diff --git a/flake.lock b/flake.lock new file mode 100644 index 000000000..95016b487 --- /dev/null +++ b/flake.lock @@ -0,0 +1,43 @@ +{ + "nodes": { + "flake-utils": { + "locked": { + "lastModified": 1642700792, + "narHash": "sha256-XqHrk7hFb+zBvRg6Ghl+AZDq03ov6OshJLiSWOoX5es=", + "owner": "numtide", + "repo": "flake-utils", + "rev": "846b2ae0fc4cc943637d3d1def4454213e203cba", + "type": "github" + }, + "original": { + "owner": "numtide", + "repo": "flake-utils", + "type": "github" + } + }, + "nixpkgs": { + "locked": { + "lastModified": 1673800717, + "narHash": "sha256-SFHraUqLSu5cC6IxTprex/nTsI81ZQAtDvlBvGDWfnA=", + "owner": "NixOS", + "repo": "nixpkgs", + "rev": "2f9fd351ec37f5d479556cd48be4ca340da59b8f", + "type": "github" + }, + "original": { + "owner": "NixOS", + "ref": "nixos-22.11", + "repo": "nixpkgs", + "type": "github" + } + }, + "root": { + "inputs": { + "flake-utils": "flake-utils", + "nixpkgs": "nixpkgs" + } + } + }, + "root": "root", + "version": 7 +} diff --git a/flake.nix b/flake.nix new file mode 100644 index 000000000..cc29db3fe --- /dev/null +++ b/flake.nix @@ -0,0 +1,211 @@ +{ + inputs = { + nixpkgs.url = "github:NixOS/nixpkgs/nixos-22.11"; + flake-utils.url = "github:numtide/flake-utils"; + }; + outputs = { self, nixpkgs, flake-utils, ... }: + flake-utils.lib.eachSystem + [ + "x86_64-linux" + ] + + (system: + let + pkgs = import nixpkgs { inherit system; }; + lib = pkgs.lib; + + mkSpotApps = appNames: + pkgs.lib.genAttrs appNames + (name: flake-utils.lib.mkApp { + drv = self.packages.${system}.spot; + name = name; + }); + + spotPackage = + let + inherit (builtins) + filter + head + isString + match + readFile + split + ; + + # NOTE: Maintaining the version separately would be a pain, and we + # can't have a flake.nix.in with a @VERSION@ because it would make + # the flake unusable without running autoconf first, defeating some + # of its purpose. + # + # So let's get it the hard way instead :) + extractVersionRegex = ''^AC_INIT\(\[spot], \[([^]]+)], \[spot@lrde\.epita\.fr]\)$''; + getLines = (fileContent: + filter isString (split "\n" fileContent) + ); + findVersionLine = (lines: + lib.lists.findFirst + (l: lib.strings.hasPrefix "AC_INIT(" l) + null + lines + ); + getVersion = (file: + let + lines = getLines (readFile file); + versionLine = findVersionLine lines; + version = head (match extractVersionRegex versionLine); + in + version + ); + in + { + lib, + pkgs, + stdenv, + # FIXME: do we want this flag? + buildOrgDoc ? false, + # Whether to enable Spot's Python 3 bindings + enablePython ? false + }: + stdenv.mkDerivation { + pname = "spot"; + version = getVersion ./configure.ac; + + src = self; + + enableParallelBuilding = true; + + # NOTE: Nix enables a lot of hardening flags by default, some of + # these probably harm performance so I've disabled everything + # (haven't benchmarked with vs without these, though). + hardeningDisable = [ "all" ]; + + # NOTE: mktexpk fails without a HOME set + preBuild = '' + export HOME=$TMPDIR + patchShebangs tools + '' + (if buildOrgDoc then '' + ln -s ${pkgs.plantuml}/lib/plantuml.jar doc/org/plantuml.jar + '' else '' + touch doc/org-stamp + ''); + + configureFlags = [ + "--disable-devel" + "--enable-optimizations" + ] ++ lib.optional (!enablePython) [ + "--disable-python" + ]; + + nativeBuildInputs = with pkgs; [ + autoreconfHook + + autoconf + automake + bison + flex + libtool + perl + ] ++ lib.optional buildOrgDoc [ + graphviz + groff + plantuml + pdf2svg + R + ] ++ lib.optional enablePython [ + python3 + swig4 + ]; + + buildInputs = with pkgs; [ + # should provide the minimum amount of packages necessary for + # building tl.pdf + (texlive.combine { + inherit (texlive) + scheme-basic + latexmk + + booktabs + cm-super + doi + doublestroke + etoolbox + koma-script + mathabx-type1 + mathpazo + metafont + microtype + nag + pgf + standalone + stmaryrd + tabulary + todonotes + wasy-type1 + wasysym + ; + }) + ]; + }; + in + { + defaultPackage = self.packages.${system}.spot; + + packages = { + # binaries + library only + spot = pkgs.callPackage spotPackage {}; + + # NOTE: clang build is broken on Nix when linking to stdlib++, using + # libcxx instead. See: + # https://github.com/NixOS/nixpkgs/issues/91285 + spotClang = pkgs.callPackage spotPackage { + stdenv = pkgs.llvmPackages.libcxxStdenv; + }; + + spotWithOrgDoc = pkgs.callPackage spotPackage { + buildOrgDoc = true; + }; + + spotWithPython = pkgs.python3Packages.toPythonModule ( + pkgs.callPackage spotPackage { + enablePython = true; + } + ); + + spotFull = pkgs.python3Packages.toPythonModule ( + pkgs.callPackage spotPackage { + buildOrgDoc = true; enablePython = true; + } + ); + }; + + apps = mkSpotApps [ + "autcross" + "autfilt" + "dstar2tgba" + "genaut" + "genltl" + "ltl2tgba" + "ltl2tgta" + "ltlcross" + "ltldo" + "ltlfilt" + "ltlgrind" + "ltlsynt" + "randaut" + "randltl" + ]; + + devShell = pkgs.mkShell { + name = "spot-dev"; + inputsFrom = [ self.packages.${system}.spotFull ]; + buildInputs = [ + pkgs.gdb + + (pkgs.python3.withPackages (p: [ + p.jupyter + p.ipython # otherwise ipython module isn't found when running ipynb tests + ])) + ]; + }; + }); +} From d2bc10065678bb4f4718ede113459a705b916ef3 Mon Sep 17 00:00:00 2001 From: Antoine Martin Date: Thu, 3 Mar 2022 11:31:03 +0100 Subject: [PATCH 286/337] nix: provide package in release tarballs --- .gitignore | 1 + Makefile.am | 6 +++++- default.nix.in | 35 +++++++++++++++++++++++++++++++++++ 3 files changed, 41 insertions(+), 1 deletion(-) create mode 100644 default.nix.in diff --git a/.gitignore b/.gitignore index 155a9b5e7..73745a48f 100644 --- a/.gitignore +++ b/.gitignore @@ -82,3 +82,4 @@ GTAGS *.dsc *.gcov spot.spec +default.nix diff --git a/Makefile.am b/Makefile.am index db7a60d9b..f1751c2a2 100644 --- a/Makefile.am +++ b/Makefile.am @@ -69,7 +69,8 @@ EXTRA_DIST = HACKING ChangeLog.1 tools/gitlog-to-changelog \ tools/help2man tools/man2html.pl \ tools/test-driver-teamcity $(UTF8) $(DEBIAN) \ m4/gnulib-cache.m4 .dir-locals.el \ - spot.spec spot.spec.in + spot.spec spot.spec.in \ + default.nix default.nix.in dist-hook: gen-ChangeLog @@ -115,3 +116,6 @@ deb: dist spot.spec: configure.ac spot.spec.in sed 's/[@]VERSION[@]/$(VERSION)/;s/[@]GITPATCH[@]/@@@$(GITPATCH)/;s/@@@\.//' spot.spec.in > $@.tmp && mv $@.tmp $@ + +default.nix: configure.ac default.nix.in + sed 's/[@]VERSION[@]/$(VERSION)/' default.nix.in > $@.tmp && mv $@.tmp $@ diff --git a/default.nix.in b/default.nix.in new file mode 100644 index 000000000..8101e4f74 --- /dev/null +++ b/default.nix.in @@ -0,0 +1,35 @@ +# -*- mode: nix; coding: utf-8 -*- +# Copyright (C) 2022 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 . + +{ pkgs ? import {} }: +let + version = "@VERSION@"; +in +pkgs.stdenv.mkDerivation { + inherit version; + pname = "spot"; + + buildInputs = [ + pkgs.python3 + ]; + + src = ./.; + + enableParallelBuilding = true; +} From f0e4efa238f57d72bf90952bbafe3a10edb5c6c7 Mon Sep 17 00:00:00 2001 From: Antoine Martin Date: Thu, 10 Mar 2022 12:16:18 +0100 Subject: [PATCH 287/337] twagraph: merge_edges supports finite automata * spot/twa/twagraph.cc: don't remove false-labeled edges if the automaton uses state-based acceptance and the edge is a self loop --- spot/twa/twagraph.cc | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/spot/twa/twagraph.cc b/spot/twa/twagraph.cc index 2a72702f3..197ea190c 100644 --- a/spot/twa/twagraph.cc +++ b/spot/twa/twagraph.cc @@ -241,11 +241,15 @@ namespace spot // them. }); + bool is_state_acc = this->prop_state_acc().is_true(); + unsigned out = 0; unsigned in = 1; // Skip any leading false edge. - while (in < tend && trans[in].cond == bddfalse) + while (in < tend + && trans[in].cond == bddfalse + && (!is_state_acc || trans[in].src != trans[in].dst)) ++in; if (in < tend) { @@ -254,7 +258,9 @@ namespace spot trans[out] = trans[in]; for (++in; in < tend; ++in) { - if (trans[in].cond == bddfalse) // Unusable edge + if (trans[in].cond == bddfalse + && (!is_state_acc + || trans[in].src != trans[in].dst)) // Unusable edge continue; // Merge edges with the same source, destination, and // colors. (We test the source last, because this is the From 1092e6c0c26abcad803e05dcb54709c72d3fa5b6 Mon Sep 17 00:00:00 2001 From: Antoine Martin Date: Fri, 14 Jan 2022 08:56:28 +0100 Subject: [PATCH 288/337] tl: implement SERE derivation --- python/spot/impl.i | 2 + spot/tl/Makefile.am | 2 + spot/tl/derive.cc | 383 ++++++++++++++++++++++++++++++++++++++++++++ spot/tl/derive.hh | 43 +++++ 4 files changed, 430 insertions(+) create mode 100644 spot/tl/derive.cc create mode 100644 spot/tl/derive.hh diff --git a/python/spot/impl.i b/python/spot/impl.i index f95270e21..529d946bc 100644 --- a/python/spot/impl.i +++ b/python/spot/impl.i @@ -90,6 +90,7 @@ #include #include +#include #include #include #include @@ -603,6 +604,7 @@ namespace std { %include %include +%include %include %include %include diff --git a/spot/tl/Makefile.am b/spot/tl/Makefile.am index cdedddffd..7440cbae3 100644 --- a/spot/tl/Makefile.am +++ b/spot/tl/Makefile.am @@ -28,6 +28,7 @@ tl_HEADERS = \ contain.hh \ declenv.hh \ defaultenv.hh \ + derive.hh \ dot.hh \ environment.hh \ exclusive.hh \ @@ -53,6 +54,7 @@ libtl_la_SOURCES = \ contain.cc \ declenv.cc \ defaultenv.cc \ + derive.cc \ dot.cc \ exclusive.cc \ formula.cc \ diff --git a/spot/tl/derive.cc b/spot/tl/derive.cc new file mode 100644 index 000000000..cec4f3bcd --- /dev/null +++ b/spot/tl/derive.cc @@ -0,0 +1,383 @@ +// -*- coding: utf-8 -*- +// Copyright (C) 2021 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 . + +#include "config.h" +#include +#include +#include +#include + +namespace spot +{ + namespace + { + static std::vector + formula_aps(formula f) + { + auto res = std::unordered_set(); + + f.traverse([&res](formula f) + { + if (f.is(op::ap)) + { + res.insert(f.ap_name()); + return true; + } + + return false; + }); + + return std::vector(res.begin(), res.end()); + } + } + + twa_graph_ptr + derive_finite_automaton(formula f, bool deterministic) + { + auto bdd_dict = make_bdd_dict(); + auto aut = make_twa_graph(bdd_dict); + + aut->prop_state_acc(true); + const auto acc_mark = aut->set_buchi(); + + auto formula2state = robin_hood::unordered_map(); + + unsigned init_state = aut->new_state(); + aut->set_init_state(init_state); + + formula2state.insert({ f, init_state }); + + auto f_aps = formula_aps(f); + for (auto& ap : f_aps) + aut->register_ap(ap); + bdd all_aps = aut->ap_vars(); + + auto todo = std::vector>(); + todo.push_back({f, init_state}); + + auto state_names = new std::vector(); + std::ostringstream ss; + ss << f; + state_names->push_back(ss.str()); + + auto find_dst = [&](formula derivative) -> unsigned + { + unsigned dst; + auto it = formula2state.find(derivative); + if (it != formula2state.end()) + { + dst = it->second; + } + else + { + dst = aut->new_state(); + todo.push_back({derivative, dst}); + formula2state.insert({derivative, dst}); + std::ostringstream ss; + ss << derivative; + state_names->push_back(ss.str()); + } + + return dst; + }; + + while (!todo.empty()) + { + auto [curr_f, curr_state] = todo[todo.size() - 1]; + todo.pop_back(); + + auto curr_acc_mark = curr_f.accepts_eword() + ? acc_mark + : acc_cond::mark_t(); + + for (const bdd one : minterms_of(bddtrue, all_aps)) + { + formula derivative = + partial_derivation(curr_f, one, bdd_dict, aut.get()); + + // no transition possible from this letter + if (derivative.is(op::ff)) + continue; + + // either the formula isn't an OrRat, or if it is we consider it as + // as a whole to get a deterministic automaton + if (deterministic || !derivative.is(op::OrRat)) + { + auto dst = find_dst(derivative); + aut->new_edge(curr_state, dst, one, curr_acc_mark); + continue; + } + + // formula is an OrRat and we want a non deterministic automaton, + // so consider each child as a destination + for (const auto& subformula : derivative) + { + auto dst = find_dst(subformula); + aut->new_edge(curr_state, dst, one, curr_acc_mark); + } + } + + // if state has no transitions and should be accepting, create + // artificial transition + if (aut->get_graph().state_storage(curr_state).succ == 0 + && curr_f.accepts_eword()) + aut->new_edge(curr_state, curr_state, bddfalse, acc_mark); + } + + aut->set_named_prop("state-names", state_names); + + aut->merge_edges(); + + return aut; + } + + twa_graph_ptr + derive_automaton(formula f, bool deterministic) + { + auto bdd_dict = make_bdd_dict(); + auto aut = make_twa_graph(bdd_dict); + + aut->prop_state_acc(true); + const auto acc_mark = aut->set_buchi(); + + auto formula2state = robin_hood::unordered_map(); + + unsigned init_state = aut->new_state(); + aut->set_init_state(init_state); + + formula2state.insert({ f, init_state }); + + auto f_aps = formula_aps(f); + for (auto& ap : f_aps) + aut->register_ap(ap); + bdd all_aps = aut->ap_vars(); + bdd alive = bdd_ithvar(aut->register_ap("alive")); + + auto todo = std::vector>(); + todo.push_back({f, init_state}); + + auto state_names = new std::vector(); + std::ostringstream ss; + ss << f; + state_names->push_back(ss.str()); + + auto find_dst = [&](formula derivative) -> unsigned + { + unsigned dst; + auto it = formula2state.find(derivative); + if (it != formula2state.end()) + { + dst = it->second; + } + else + { + dst = aut->new_state(); + todo.push_back({derivative, dst}); + formula2state.insert({derivative, dst}); + std::ostringstream ss; + ss << derivative; + state_names->push_back(ss.str()); + } + + return dst; + }; + + while (!todo.empty()) + { + auto [curr_f, curr_state] = todo[todo.size() - 1]; + todo.pop_back(); + + for (const bdd one : minterms_of(bddtrue, all_aps)) + { + formula derivative = + partial_derivation(curr_f, one, bdd_dict, aut.get()); + + // no transition possible from this letter + if (derivative.is(op::ff)) + continue; + + // either the formula isn't an OrRat, or if it is we consider it as + // a whole to get a deterministic automaton + if (deterministic || !derivative.is(op::OrRat)) + { + auto dst = find_dst(derivative); + aut->new_edge(curr_state, dst, one & alive); + continue; + } + + // formula is an OrRat and we want a non deterministic automaton, + // so consider each child as a destination + for (const auto& subformula : derivative) + { + auto dst = find_dst(subformula); + aut->new_edge(curr_state, dst, one & alive); + } + } + } + + unsigned end_state = aut->new_state(); + state_names->push_back("end"); + + for (const auto& [state_formula, state] : formula2state) + { + if (!state_formula.accepts_eword()) + continue; + + aut->new_edge(state, end_state, !alive); + } + + aut->new_edge(end_state, end_state, !alive, acc_mark); + + aut->set_named_prop("state-names", state_names); + + return aut; + } + + formula + partial_derivation(formula f, const bdd var, const bdd_dict_ptr& d, + void* owner) + { + if (f.is_boolean()) + { + auto f_bdd = formula_to_bdd(f, d, owner); + + if (bdd_implies(var, f_bdd)) + return formula::eword(); + + return formula::ff(); + } + + switch (f.kind()) + { + // handled by is_boolean above + case op::ff: + case op::tt: + case op::ap: + SPOT_UNREACHABLE(); + + case op::eword: + return formula::ff(); + + // d(E.F) = { d(E).F } U { c(E).d(F) } + case op::Concat: + { + formula E = f[0]; + formula F = f.all_but(0); + + auto res = + formula::Concat({ partial_derivation(E, var, d, owner), F }); + + if (E.accepts_eword()) + res = formula::OrRat( + { res, partial_derivation(F, var, d, owner) }); + + return res; + } + + // d(E*) = d(E).E* + // d(E[*i..j]) = d(E).E[*(i-1)..(j-1)] + case op::Star: + { + auto min = f.min() == 0 ? 0 : (f.min() - 1); + auto max = f.max() == formula::unbounded() + ? formula::unbounded() + : (f.max() - 1); + + formula d_E = partial_derivation(f[0], var, d, owner); + + return formula::Concat({ d_E, formula::Star(f[0], min, max) }); + } + + // d(E[:*i..j]) = E:E[:*(i-1)..(j-1)] + (eword if i == 0 or c(d(E))) + case op::FStar: + { + formula E = f[0]; + + if (f.min() == 0 && f.max() == 0) + return formula::tt(); + + auto d_E = partial_derivation(E, var, d, owner); + + auto min = f.min() == 0 ? 0 : (f.min() - 1); + auto max = f.max() == formula::unbounded() + ? formula::unbounded() + : (f.max() - 1); + + auto results = std::vector(); + + auto E_i_j_minus = formula::FStar(E, min, max); + results.push_back(formula::Fusion({ d_E, E_i_j_minus })); + + if (d_E.accepts_eword()) + results.push_back(d_E); + + if (f.min() == 0) + results.push_back(formula::eword()); + + return formula::OrRat(std::move(results)); + } + + // d(E && F) = d(E) && d(F) + // d(E + F) = {d(E)} U {d(F)} + case op::AndRat: + case op::OrRat: + { + std::vector subderivations; + for (auto subformula : f) + { + auto subderivation = + partial_derivation(subformula, var, d, owner); + subderivations.push_back(subderivation); + } + return formula::multop(f.kind(), std::move(subderivations)); + } + + // d(E:F) = {d(E):F} U {c(d(E)).d(F)} + case op::Fusion: + { + formula E = f[0]; + formula F = f.all_but(0); + + auto d_E = partial_derivation(E, var, d, owner); + auto res = formula::Fusion({ d_E, F }); + + if (d_E.accepts_eword()) + res = + formula::OrRat({ res, partial_derivation(F, var, d, owner) }); + + return res; + } + + case op::first_match: + { + formula E = f[0]; + auto d_E = partial_derivation(E, var, d, owner); + // if d_E.accepts_eword(), first_match(d_E) will return eword + return formula::first_match(d_E); + } + + default: + std::cerr << "unimplemented kind " + << static_cast(f.kind()) + << std::endl; + SPOT_UNIMPLEMENTED(); + } + return formula::ff(); + } +} diff --git a/spot/tl/derive.hh b/spot/tl/derive.hh new file mode 100644 index 000000000..410a24e37 --- /dev/null +++ b/spot/tl/derive.hh @@ -0,0 +1,43 @@ +// -*- coding: utf-8 -*- +// Copyright (C) 2021 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 . + +#pragma once + +#include + +#include + +#include +#include +#include + +namespace spot +{ + /// \ingroup tl_misc + /// \brief Produce a SERE formula's partial derivative + SPOT_API formula + partial_derivation(formula f, const bdd var, const bdd_dict_ptr& d, + void* owner); + + SPOT_API twa_graph_ptr + derive_automaton(formula f, bool deterministic = true); + + SPOT_API twa_graph_ptr + derive_finite_automaton(formula f, bool deterministic = true); +} From 04112b26cce3dda68835a7d57aed5ec991042936 Mon Sep 17 00:00:00 2001 From: Antoine Martin Date: Thu, 10 Mar 2022 15:45:50 +0100 Subject: [PATCH 289/337] twaalgos: extract internal sere2dfa --- spot/twaalgos/ltl2tgba_fm.cc | 55 ++++++++++++++++++++++++++++++++++++ spot/twaalgos/ltl2tgba_fm.hh | 3 ++ 2 files changed, 58 insertions(+) diff --git a/spot/twaalgos/ltl2tgba_fm.cc b/spot/twaalgos/ltl2tgba_fm.cc index 9768dfbfd..5e6866c94 100644 --- a/spot/twaalgos/ltl2tgba_fm.cc +++ b/spot/twaalgos/ltl2tgba_fm.cc @@ -2268,4 +2268,59 @@ namespace spot return a; } + twa_graph_ptr + sere_to_tgba(formula f, const bdd_dict_ptr& dict) + { + f = negative_normal_form(f); + + tl_simplifier* s = new tl_simplifier(dict); + twa_graph_ptr a = make_twa_graph(dict); + + translate_dict d(a, s, false, false, false); + ratexp_to_dfa sere2dfa(d); + + auto [dfa, namer, state] = sere2dfa.succ(f); + + // language was empty, build an automaton with one non accepting state + if (dfa == nullptr) + { + auto res = make_twa_graph(dict); + res->set_init_state(res->new_state()); + res->prop_universal(true); + res->prop_complete(false); + res->prop_stutter_invariant(true); + res->prop_terminal(true); + res->prop_state_acc(true); + return res; + } + + auto res = make_twa_graph(dfa, {false, false, true, false, false, false}); + + // HACK: translate_dict registers the atomic propositions in the "final" + // automaton that would be produced by a full translation, not in the + // intermediate automaton we're interested in. We can copy them from the + // resulting automaton. + res->copy_ap_of(a); + + res->prop_state_acc(true); + const auto acc_mark = res->set_buchi(); + + size_t sn = namer->state_to_name.size(); + for (size_t i = 0; i < sn; ++i) + { + formula g = namer->state_to_name[i]; + if (g.accepts_eword()) + { + if (res->get_graph().state_storage(i).succ == 0) + res->new_edge(i, i, bddfalse, acc_mark); + else + { + for (auto& e : res->out(i)) + e.acc = acc_mark; + } + } + } + + return res; + } } diff --git a/spot/twaalgos/ltl2tgba_fm.hh b/spot/twaalgos/ltl2tgba_fm.hh index 8c1827490..aa9bc1d1d 100644 --- a/spot/twaalgos/ltl2tgba_fm.hh +++ b/spot/twaalgos/ltl2tgba_fm.hh @@ -88,4 +88,7 @@ namespace spot tl_simplifier* simplifier = nullptr, bool unambiguous = false, const output_aborter* aborter = nullptr); + + SPOT_API twa_graph_ptr + sere_to_tgba(formula f, const bdd_dict_ptr& dict); } From d2667d48f6bc3f97561bccf5b1bef422b5ec818f Mon Sep 17 00:00:00 2001 From: Antoine Martin Date: Mon, 14 Mar 2022 15:17:51 +0100 Subject: [PATCH 290/337] twaalgos: add from_finite * spot/twaalgos/remprop.cc, spot/twaalgos/remprop.hh: add a from_finite function to perform the opposite operation to to_finite --- spot/twaalgos/remprop.cc | 47 ++++++++++++++++++++++++++++++++++++++++ spot/twaalgos/remprop.hh | 3 +++ 2 files changed, 50 insertions(+) diff --git a/spot/twaalgos/remprop.cc b/spot/twaalgos/remprop.cc index 8d4be8fbc..c84ee9188 100644 --- a/spot/twaalgos/remprop.cc +++ b/spot/twaalgos/remprop.cc @@ -244,4 +244,51 @@ namespace spot } + twa_graph_ptr from_finite(const_twa_graph_ptr aut, const char* alive) + { + twa_graph_ptr res = + make_twa_graph(aut, + { true, false, true, false, false, false }); + + if (aut->get_named_prop>("state-names")) + res->copy_state_names_from(aut); + auto* names = res->get_named_prop>("state-names"); + + unsigned alive_sink = res->new_state(); + if (names != nullptr) + names->push_back("sink"); + auto acc = res->acc().all_sets(); + auto alive_bdd = bdd_ithvar(res->register_ap(alive)); + res->new_edge(alive_sink, alive_sink, !alive_bdd, acc); + + unsigned ns = res->num_states(); + for (unsigned s = 0; s < ns; ++s) + { + if (s == alive_sink) + continue; + + bool was_acc = res->state_is_accepting(s); + + // erase accepting marks, require alive on non-accepting transition, + // and remove self-loop edges used to mark acceptance + auto i = res->out_iteraser(s); + while (i) + { + if (i->src == i->dst && i->cond == bddfalse) + { + i.erase(); + continue; + } + + i->cond &= alive_bdd; + i->acc = {}; + ++i; + } + + if (was_acc) + res->new_edge(s, alive_sink, !alive_bdd); + } + + return res; + } } diff --git a/spot/twaalgos/remprop.hh b/spot/twaalgos/remprop.hh index 09d75ffac..4b496e65a 100644 --- a/spot/twaalgos/remprop.hh +++ b/spot/twaalgos/remprop.hh @@ -54,5 +54,8 @@ namespace spot SPOT_API twa_graph_ptr to_finite(const_twa_graph_ptr aut, const char* alive = "alive"); + /// \brief The opposite of the to_finite operation + SPOT_API twa_graph_ptr + from_finite(const_twa_graph_ptr aut, const char* alive = "alive"); } From 90be62be3d0ef8c26e47ff73e39be870f62a830d Mon Sep 17 00:00:00 2001 From: Antoine Martin Date: Tue, 15 Mar 2022 17:06:05 +0100 Subject: [PATCH 291/337] derive: use from_finite --- spot/tl/derive.cc | 106 +++++----------------------------------------- 1 file changed, 11 insertions(+), 95 deletions(-) diff --git a/spot/tl/derive.cc b/spot/tl/derive.cc index cec4f3bcd..9def9e2eb 100644 --- a/spot/tl/derive.cc +++ b/spot/tl/derive.cc @@ -22,6 +22,7 @@ #include #include #include +#include namespace spot { @@ -140,6 +141,12 @@ namespace spot aut->new_edge(curr_state, curr_state, bddfalse, acc_mark); } + // if we only have an initial state with no transitions, then our language + // is empty + if (aut->num_states() == 1 + && aut->get_graph().state_storage(aut->get_init_state_number()).succ == 0) + return nullptr; + aut->set_named_prop("state-names", state_names); aut->merge_edges(); @@ -150,103 +157,12 @@ namespace spot twa_graph_ptr derive_automaton(formula f, bool deterministic) { - auto bdd_dict = make_bdd_dict(); - auto aut = make_twa_graph(bdd_dict); + auto finite = derive_finite_automaton(f, deterministic); - aut->prop_state_acc(true); - const auto acc_mark = aut->set_buchi(); + if (finite == nullptr) + return nullptr; - auto formula2state = robin_hood::unordered_map(); - - unsigned init_state = aut->new_state(); - aut->set_init_state(init_state); - - formula2state.insert({ f, init_state }); - - auto f_aps = formula_aps(f); - for (auto& ap : f_aps) - aut->register_ap(ap); - bdd all_aps = aut->ap_vars(); - bdd alive = bdd_ithvar(aut->register_ap("alive")); - - auto todo = std::vector>(); - todo.push_back({f, init_state}); - - auto state_names = new std::vector(); - std::ostringstream ss; - ss << f; - state_names->push_back(ss.str()); - - auto find_dst = [&](formula derivative) -> unsigned - { - unsigned dst; - auto it = formula2state.find(derivative); - if (it != formula2state.end()) - { - dst = it->second; - } - else - { - dst = aut->new_state(); - todo.push_back({derivative, dst}); - formula2state.insert({derivative, dst}); - std::ostringstream ss; - ss << derivative; - state_names->push_back(ss.str()); - } - - return dst; - }; - - while (!todo.empty()) - { - auto [curr_f, curr_state] = todo[todo.size() - 1]; - todo.pop_back(); - - for (const bdd one : minterms_of(bddtrue, all_aps)) - { - formula derivative = - partial_derivation(curr_f, one, bdd_dict, aut.get()); - - // no transition possible from this letter - if (derivative.is(op::ff)) - continue; - - // either the formula isn't an OrRat, or if it is we consider it as - // a whole to get a deterministic automaton - if (deterministic || !derivative.is(op::OrRat)) - { - auto dst = find_dst(derivative); - aut->new_edge(curr_state, dst, one & alive); - continue; - } - - // formula is an OrRat and we want a non deterministic automaton, - // so consider each child as a destination - for (const auto& subformula : derivative) - { - auto dst = find_dst(subformula); - aut->new_edge(curr_state, dst, one & alive); - } - } - } - - unsigned end_state = aut->new_state(); - state_names->push_back("end"); - - for (const auto& [state_formula, state] : formula2state) - { - if (!state_formula.accepts_eword()) - continue; - - aut->new_edge(state, end_state, !alive); - } - - aut->new_edge(end_state, end_state, !alive, acc_mark); - - aut->set_named_prop("state-names", state_names); - - return aut; + return from_finite(finite); } formula From 2c89e09a473f2e4f3e08644e0f61c4e06e8cd541 Mon Sep 17 00:00:00 2001 From: Antoine Martin Date: Fri, 18 Mar 2022 18:05:53 +0100 Subject: [PATCH 292/337] derive: no nullptr handling --- spot/tl/derive.cc | 9 --------- 1 file changed, 9 deletions(-) diff --git a/spot/tl/derive.cc b/spot/tl/derive.cc index 9def9e2eb..699c8634f 100644 --- a/spot/tl/derive.cc +++ b/spot/tl/derive.cc @@ -141,12 +141,6 @@ namespace spot aut->new_edge(curr_state, curr_state, bddfalse, acc_mark); } - // if we only have an initial state with no transitions, then our language - // is empty - if (aut->num_states() == 1 - && aut->get_graph().state_storage(aut->get_init_state_number()).succ == 0) - return nullptr; - aut->set_named_prop("state-names", state_names); aut->merge_edges(); @@ -159,9 +153,6 @@ namespace spot { auto finite = derive_finite_automaton(f, deterministic); - if (finite == nullptr) - return nullptr; - return from_finite(finite); } From 6882611d2500ab9b89d69ac55dbf96a4faf63f3d Mon Sep 17 00:00:00 2001 From: Antoine Martin Date: Fri, 18 Mar 2022 19:27:19 +0100 Subject: [PATCH 293/337] derive: extract AndNLM rewriting --- spot/tl/derive.cc | 55 ++++++++++++++++++++++++++++++++++++ spot/tl/derive.hh | 3 ++ spot/twaalgos/ltl2tgba_fm.cc | 51 ++------------------------------- 3 files changed, 60 insertions(+), 49 deletions(-) diff --git a/spot/tl/derive.cc b/spot/tl/derive.cc index 699c8634f..a24fbac53 100644 --- a/spot/tl/derive.cc +++ b/spot/tl/derive.cc @@ -48,6 +48,61 @@ namespace spot } } + formula + rewrite_and_nlm(formula f) + { + unsigned s = f.size(); + std::vector final; + std::vector non_final; + + for (auto g: f) + if (g.accepts_eword()) + final.emplace_back(g); + else + non_final.emplace_back(g); + + if (non_final.empty()) + // (a* & b*);c = (a*|b*);c + return formula::OrRat(std::move(final)); + if (!final.empty()) + { + // let F_i be final formulae + // N_i be non final formula + // (F_1 & ... & F_n & N_1 & ... & N_m) + // = (F_1 | ... | F_n);[*] && (N_1 & ... & N_m) + // | (F_1 | ... | F_n) && (N_1 & ... & N_m);[*] + formula f = formula::OrRat(std::move(final)); + formula n = formula::AndNLM(std::move(non_final)); + formula t = formula::one_star(); + formula ft = formula::Concat({f, t}); + formula nt = formula::Concat({n, t}); + formula ftn = formula::AndRat({ft, n}); + formula fnt = formula::AndRat({f, nt}); + return formula::OrRat({ftn, fnt}); + } + // No final formula. + // Translate N_1 & N_2 & ... & N_n into + // N_1 && (N_2;[*]) && ... && (N_n;[*]) + // | (N_1;[*]) && N_2 && ... && (N_n;[*]) + // | (N_1;[*]) && (N_2;[*]) && ... && N_n + formula star = formula::one_star(); + std::vector disj; + for (unsigned n = 0; n < s; ++n) + { + std::vector conj; + for (unsigned m = 0; m < s; ++m) + { + formula g = f[m]; + if (n != m) + g = formula::Concat({g, star}); + conj.emplace_back(g); + } + disj.emplace_back(formula::AndRat(std::move(conj))); + } + return formula::OrRat(std::move(disj)); + } + + twa_graph_ptr derive_finite_automaton(formula f, bool deterministic) { diff --git a/spot/tl/derive.hh b/spot/tl/derive.hh index 410a24e37..247f85b59 100644 --- a/spot/tl/derive.hh +++ b/spot/tl/derive.hh @@ -40,4 +40,7 @@ namespace spot SPOT_API twa_graph_ptr derive_finite_automaton(formula f, bool deterministic = true); + + SPOT_API formula + rewrite_and_nlm(formula f); } diff --git a/spot/twaalgos/ltl2tgba_fm.cc b/spot/twaalgos/ltl2tgba_fm.cc index 5e6866c94..97008b0df 100644 --- a/spot/twaalgos/ltl2tgba_fm.cc +++ b/spot/twaalgos/ltl2tgba_fm.cc @@ -24,6 +24,7 @@ #include #include #include +#include #include #include #include @@ -753,55 +754,7 @@ namespace spot SPOT_UNREACHABLE(); case op::AndNLM: { - unsigned s = f.size(); - vec final; - vec non_final; - - for (auto g: f) - if (g.accepts_eword()) - final.emplace_back(g); - else - non_final.emplace_back(g); - - if (non_final.empty()) - // (a* & b*);c = (a*|b*);c - return recurse_and_concat(formula::OrRat(std::move(final))); - if (!final.empty()) - { - // let F_i be final formulae - // N_i be non final formula - // (F_1 & ... & F_n & N_1 & ... & N_m) - // = (F_1 | ... | F_n);[*] && (N_1 & ... & N_m) - // | (F_1 | ... | F_n) && (N_1 & ... & N_m);[*] - formula f = formula::OrRat(std::move(final)); - formula n = formula::AndNLM(std::move(non_final)); - formula t = formula::one_star(); - formula ft = formula::Concat({f, t}); - formula nt = formula::Concat({n, t}); - formula ftn = formula::AndRat({ft, n}); - formula fnt = formula::AndRat({f, nt}); - return recurse_and_concat(formula::OrRat({ftn, fnt})); - } - // No final formula. - // Translate N_1 & N_2 & ... & N_n into - // N_1 && (N_2;[*]) && ... && (N_n;[*]) - // | (N_1;[*]) && N_2 && ... && (N_n;[*]) - // | (N_1;[*]) && (N_2;[*]) && ... && N_n - formula star = formula::one_star(); - vec disj; - for (unsigned n = 0; n < s; ++n) - { - vec conj; - for (unsigned m = 0; m < s; ++m) - { - formula g = f[m]; - if (n != m) - g = formula::Concat({g, star}); - conj.emplace_back(g); - } - disj.emplace_back(formula::AndRat(std::move(conj))); - } - return recurse_and_concat(formula::OrRat(std::move(disj))); + return recurse_and_concat(rewrite_and_nlm(f)); } case op::AndRat: { From 0d6c3cd6e9e60cd1fe0a7fb04e5cfa24132a0dda Mon Sep 17 00:00:00 2001 From: Antoine Martin Date: Fri, 18 Mar 2022 19:27:37 +0100 Subject: [PATCH 294/337] derive: handle AndNLM --- spot/tl/derive.cc | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/spot/tl/derive.cc b/spot/tl/derive.cc index a24fbac53..3180d4815 100644 --- a/spot/tl/derive.cc +++ b/spot/tl/derive.cc @@ -310,6 +310,12 @@ namespace spot return formula::multop(f.kind(), std::move(subderivations)); } + case op::AndNLM: + { + formula rewrite = rewrite_and_nlm(f); + return partial_derivation(rewrite, var, d, owner); + } + // d(E:F) = {d(E):F} U {c(d(E)).d(F)} case op::Fusion: { From f2063b7fc32be7916cb7af173e3a55dba792d6df Mon Sep 17 00:00:00 2001 From: Antoine Martin Date: Wed, 30 Mar 2022 21:52:34 +0200 Subject: [PATCH 295/337] derive: use first --- spot/tl/derive.cc | 207 ++++++++++++++++++++++++++++++++++++++++++++++ spot/tl/derive.hh | 6 ++ 2 files changed, 213 insertions(+) diff --git a/spot/tl/derive.cc b/spot/tl/derive.cc index 3180d4815..2b1873ed2 100644 --- a/spot/tl/derive.cc +++ b/spot/tl/derive.cc @@ -28,6 +28,106 @@ namespace spot { namespace { + static std::pair + first(formula f, const bdd_dict_ptr& d, void* owner) + { + if (f.is_boolean()) + { + bdd res = formula_to_bdd(f, d, owner); + return { res, bdd_support(res) }; + } + + switch (f.kind()) + { + // handled by is_boolean above + case op::ff: + case op::tt: + case op::ap: + case op::And: + case op::Or: + SPOT_UNREACHABLE(); + + case op::eword: + return { bddfalse, bddtrue }; + + case op::OrRat: + { + bdd res = bddfalse; + bdd support = bddtrue; + for (auto subformula : f) + { + auto [r, sup] = first(subformula, d, owner); + res |= r; + support &= sup; + } + return { res, support }; + } + + case op::AndRat: + { + bdd res = bddtrue; + bdd support = bddtrue; + for (auto subformula : f) + { + auto [r, sup] = first(subformula, d, owner); + res &= r; + support &= sup; + } + return { res, support }; + } + + case op::AndNLM: + return first(rewrite_and_nlm(f), d, owner); + + case op::Concat: + { + auto [res, support] = first(f[0], d, owner); + + if (f[0].accepts_eword()) + { + auto [r, sup] = first(f.all_but(0), d, owner); + res |= r; + support &= sup; + } + + return { res, support }; + } + + case op::Fusion: + { + auto [res, support] = first(f[0], d, owner); + + // this should be computed only if f[0] recognizes words of size 1 + // or accepts eword ? + auto p = first(f.all_but(0), d, owner); + + return { res, support & p.second }; + } + + case op::Star: + case op::first_match: + return first(f[0], d, owner); + + case op::FStar: + { + auto [res, support] = first(f[0], d, owner); + + if (f.min() == 0) + res = bddtrue; + + return { res, support }; + } + + default: + std::cerr << "unimplemented kind " + << static_cast(f.kind()) + << std::endl; + SPOT_UNIMPLEMENTED(); + } + + return { bddfalse, bddtrue }; + } + static std::vector formula_aps(formula f) { @@ -102,6 +202,105 @@ namespace spot return formula::OrRat(std::move(disj)); } + twa_graph_ptr + derive_finite_automaton_with_first(formula f, bool deterministic) + { + auto bdd_dict = make_bdd_dict(); + auto aut = make_twa_graph(bdd_dict); + + aut->prop_state_acc(true); + const auto acc_mark = aut->set_buchi(); + + auto formula2state = robin_hood::unordered_map(); + + unsigned init_state = aut->new_state(); + aut->set_init_state(init_state); + + formula2state.insert({ f, init_state }); + + auto f_aps = formula_aps(f); + for (auto& ap : f_aps) + aut->register_ap(ap); + + auto todo = std::vector>(); + todo.push_back({f, init_state}); + + auto state_names = new std::vector(); + std::ostringstream ss; + ss << f; + state_names->push_back(ss.str()); + + auto find_dst = [&](formula derivative) -> unsigned + { + unsigned dst; + auto it = formula2state.find(derivative); + if (it != formula2state.end()) + { + dst = it->second; + } + else + { + dst = aut->new_state(); + todo.push_back({derivative, dst}); + formula2state.insert({derivative, dst}); + std::ostringstream ss; + ss << derivative; + state_names->push_back(ss.str()); + } + + return dst; + }; + + while (!todo.empty()) + { + auto [curr_f, curr_state] = todo[todo.size() - 1]; + todo.pop_back(); + + auto curr_acc_mark = curr_f.accepts_eword() + ? acc_mark + : acc_cond::mark_t(); + + auto [firsts, firsts_support] = first(curr_f, bdd_dict, aut.get()); + for (const bdd one : minterms_of(firsts, firsts_support)) + { + formula derivative = + partial_derivation(curr_f, one, bdd_dict, aut.get()); + + // no transition possible from this letter + if (derivative.is(op::ff)) + continue; + + // either the formula isn't an OrRat, or if it is we consider it as + // as a whole to get a deterministic automaton + if (deterministic || !derivative.is(op::OrRat)) + { + auto dst = find_dst(derivative); + aut->new_edge(curr_state, dst, one, curr_acc_mark); + continue; + } + + // formula is an OrRat and we want a non deterministic automaton, + // so consider each child as a destination + for (const auto& subformula : derivative) + { + auto dst = find_dst(subformula); + aut->new_edge(curr_state, dst, one, curr_acc_mark); + } + } + + // if state has no transitions and should be accepting, create + // artificial transition + if (aut->get_graph().state_storage(curr_state).succ == 0 + && curr_f.accepts_eword()) + aut->new_edge(curr_state, curr_state, bddfalse, acc_mark); + } + + aut->set_named_prop("state-names", state_names); + + aut->merge_edges(); + + return aut; + } twa_graph_ptr derive_finite_automaton(formula f, bool deterministic) @@ -203,6 +402,14 @@ namespace spot return aut; } + twa_graph_ptr + derive_automaton_with_first(formula f, bool deterministic) + { + auto finite = derive_finite_automaton_with_first(f, deterministic); + + return from_finite(finite); + } + twa_graph_ptr derive_automaton(formula f, bool deterministic) { diff --git a/spot/tl/derive.hh b/spot/tl/derive.hh index 247f85b59..1947951ed 100644 --- a/spot/tl/derive.hh +++ b/spot/tl/derive.hh @@ -38,9 +38,15 @@ namespace spot SPOT_API twa_graph_ptr derive_automaton(formula f, bool deterministic = true); + SPOT_API twa_graph_ptr + derive_automaton_with_first(formula f, bool deterministic = true); + SPOT_API twa_graph_ptr derive_finite_automaton(formula f, bool deterministic = true); + SPOT_API twa_graph_ptr + derive_finite_automaton_with_first(formula f, bool deterministic = true); + SPOT_API formula rewrite_and_nlm(formula f); } From abe3da54fb798d27f2a50ce0cad6529c1983b72d Mon Sep 17 00:00:00 2001 From: Antoine Martin Date: Thu, 7 Jul 2022 16:38:33 +0200 Subject: [PATCH 296/337] graph: filter accepting sinks in univ_dest_mapper --- spot/graph/graph.hh | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/spot/graph/graph.hh b/spot/graph/graph.hh index 04c21fec9..e6afda738 100644 --- a/spot/graph/graph.hh +++ b/spot/graph/graph.hh @@ -557,10 +557,11 @@ namespace spot { std::map, unsigned> uniq_; G& g_; + unsigned acc_sink_; public: - univ_dest_mapper(G& graph) - : g_(graph) + univ_dest_mapper(G& graph, unsigned sink = -1u) + : g_(graph), acc_sink_(sink) { } @@ -570,6 +571,9 @@ namespace spot std::vector tmp(begin, end); std::sort(tmp.begin(), tmp.end()); tmp.erase(std::unique(tmp.begin(), tmp.end()), tmp.end()); + if (acc_sink_ != -1u && tmp.size() > 1) + tmp.erase(std::remove(tmp.begin(), tmp.end(), acc_sink_), + tmp.end()); auto p = uniq_.emplace(tmp, 0); if (p.second) p.first->second = g_.new_univ_dests(tmp.begin(), tmp.end()); From 382acca320cca1d9965c16a7ce9e5c137b4773cb Mon Sep 17 00:00:00 2001 From: Antoine Martin Date: Thu, 7 Jul 2022 16:40:41 +0200 Subject: [PATCH 297/337] twaalgos: filter accepting sinks in oe combiner --- spot/twaalgos/alternation.cc | 21 ++++++++++++++++++--- spot/twaalgos/alternation.hh | 3 ++- 2 files changed, 20 insertions(+), 4 deletions(-) diff --git a/spot/twaalgos/alternation.cc b/spot/twaalgos/alternation.cc index bdbe07982..3e94dfe3b 100644 --- a/spot/twaalgos/alternation.cc +++ b/spot/twaalgos/alternation.cc @@ -26,8 +26,8 @@ namespace spot { - outedge_combiner::outedge_combiner(const twa_graph_ptr& aut) - : aut_(aut), vars_(bddtrue) + outedge_combiner::outedge_combiner(const twa_graph_ptr& aut, unsigned sink) + : aut_(aut), vars_(bddtrue), acc_sink_(sink) { } @@ -48,6 +48,9 @@ namespace spot bdd out = bddtrue; for (unsigned d: aut_->univ_dests(e.dst)) { + if (d == acc_sink_) + continue; + auto p = state_to_var.emplace(d, 0); if (p.second) { @@ -76,7 +79,17 @@ namespace spot { bdd cond = bdd_exist(cube, vars_); bdd dest = bdd_existcomp(cube, vars_); - while (dest != bddtrue) + + if (dest == bddtrue) + { + // if dest is bddtrue then the accepting sink is the only + // destination for this edge, in that case don't filter it out + assert(acc_sink_ != -1u); + aut_->new_edge(st, acc_sink_, cond); + continue; + } + + do { assert(bdd_low(dest) == bddfalse); auto it = var_to_state.find(bdd_var(dest)); @@ -84,6 +97,8 @@ namespace spot univ_dest.push_back(it->second); dest = bdd_high(dest); } + while (dest != bddtrue); + std::sort(univ_dest.begin(), univ_dest.end()); aut_->new_univ_edge(st, univ_dest.begin(), univ_dest.end(), cond); univ_dest.clear(); diff --git a/spot/twaalgos/alternation.hh b/spot/twaalgos/alternation.hh index a4665aacf..f3092eb10 100644 --- a/spot/twaalgos/alternation.hh +++ b/spot/twaalgos/alternation.hh @@ -50,8 +50,9 @@ namespace spot std::map state_to_var; std::map var_to_state; bdd vars_; + unsigned acc_sink_; public: - outedge_combiner(const twa_graph_ptr& aut); + outedge_combiner(const twa_graph_ptr& aut, unsigned sink = -1u); ~outedge_combiner(); bdd operator()(unsigned st); void new_dests(unsigned st, bdd out) const; From 06f21899b136317b3d7ac4ce2257d5c1bd948b8a Mon Sep 17 00:00:00 2001 From: Antoine Martin Date: Tue, 21 Jun 2022 13:54:32 +0200 Subject: [PATCH 298/337] twaalgos: add LTL to AA translation --- python/spot/impl.i | 2 + spot/twaalgos/Makefile.am | 2 + spot/twaalgos/translate_aa.cc | 320 ++++++++++++++++++++++++++++++++++ spot/twaalgos/translate_aa.hh | 32 ++++ 4 files changed, 356 insertions(+) create mode 100644 spot/twaalgos/translate_aa.cc create mode 100644 spot/twaalgos/translate_aa.hh diff --git a/python/spot/impl.i b/python/spot/impl.i index 529d946bc..5f3ac1a67 100644 --- a/python/spot/impl.i +++ b/python/spot/impl.i @@ -162,6 +162,7 @@ #include #include #include +#include #include #include #include @@ -757,6 +758,7 @@ def state_is_accepting(self, src) -> "bool": %include %include %include +%include %include %include %include diff --git a/spot/twaalgos/Makefile.am b/spot/twaalgos/Makefile.am index 57ae8ce9f..6a8fbe6b7 100644 --- a/spot/twaalgos/Makefile.am +++ b/spot/twaalgos/Makefile.am @@ -99,6 +99,7 @@ twaalgos_HEADERS = \ totgba.hh \ toweak.hh \ translate.hh \ + translate_aa.hh \ word.hh \ zlktree.hh @@ -173,6 +174,7 @@ libtwaalgos_la_SOURCES = \ totgba.cc \ toweak.cc \ translate.cc \ + translate_aa.cc \ word.cc \ zlktree.cc diff --git a/spot/twaalgos/translate_aa.cc b/spot/twaalgos/translate_aa.cc new file mode 100644 index 000000000..0663651de --- /dev/null +++ b/spot/twaalgos/translate_aa.cc @@ -0,0 +1,320 @@ +// -*- coding: utf-8 -*- +// Copyright (C) 2013-2018, 2020-2021 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 . + +#include "config.h" +#include +#include +#include + +#include + +namespace spot +{ + namespace + { + struct ltl_to_aa_builder + { + ltl_to_aa_builder(twa_graph_ptr aut, unsigned accepting_sink) + : aut_(aut) + , accepting_sink_(accepting_sink) + , uniq_(aut_->get_graph(), accepting_sink) + , oe_(aut_, accepting_sink) + { + } + + twa_graph_ptr aut_; + unsigned accepting_sink_; + internal::univ_dest_mapper uniq_; + outedge_combiner oe_; + + unsigned recurse(formula f) + { + switch (f.kind()) + { + case op::ff: + return aut_->new_state(); + + case op::tt: + { + unsigned init_state = aut_->new_state(); + aut_->new_edge(init_state, accepting_sink_, bddtrue, {}); + return init_state; + } + + case op::ap: + case op::Not: + { + unsigned init_state = aut_->new_state(); + + bdd ap; + if (f.kind() == op::ap) + ap = bdd_ithvar(aut_->register_ap(f.ap_name())); + else + ap = bdd_nithvar(aut_->register_ap(f[0].ap_name())); + + aut_->new_edge(init_state, accepting_sink_, ap, {}); + return init_state; + } + + // FIXME: is this right for LTLf? + case op::strong_X: + case op::X: + { + unsigned sub_init_state = recurse(f[0]); + unsigned new_init_state = aut_->new_state(); + aut_->new_edge(new_init_state, sub_init_state, bddtrue, {}); + return new_init_state; + } + + case op::Or: + { + unsigned init_state = aut_->new_state(); + + for (const auto& sub_formula : f) + { + unsigned sub_init = recurse(sub_formula); + for (auto& e : aut_->out(sub_init)) + { + unsigned dst = e.dst; + if (aut_->is_univ_dest(e.dst)) + { + auto dests = aut_->univ_dests(e); + dst = uniq_.new_univ_dests(dests.begin(), dests.end()); + } + aut_->new_edge(init_state, dst, e.cond, {}); + } + } + + return init_state; + } + + case op::And: + { + unsigned init_state = aut_->new_state(); + + outedge_combiner oe(aut_, accepting_sink_); + bdd comb = bddtrue; + for (const auto& sub_formula : f) + { + unsigned sub_init = recurse(sub_formula); + comb &= oe_(sub_init); + } + oe_.new_dests(init_state, comb); + + return init_state; + } + + case op::U: + case op::W: + { + auto acc = f.kind() == op::U + ? acc_cond::mark_t{0} + : acc_cond::mark_t{}; + + unsigned init_state = aut_->new_state(); + + unsigned lhs_init = recurse(f[0]); + unsigned rhs_init = recurse(f[1]); + + std::vector new_dests; + for (auto& e : aut_->out(lhs_init)) + { + auto dests = aut_->univ_dests(e); + std::copy(dests.begin(), dests.end(), + std::back_inserter(new_dests)); + new_dests.push_back(init_state); + + unsigned dest = uniq_.new_univ_dests(new_dests.begin(), + new_dests.end()); + aut_->new_edge(init_state, dest, e.cond, acc); + + new_dests.clear(); + } + + for (auto& e : aut_->out(rhs_init)) + { + unsigned dst = e.dst; + if (aut_->is_univ_dest(e.dst)) + { + auto dests = aut_->univ_dests(e); + dst = uniq_.new_univ_dests(dests.begin(), dests.end()); + } + aut_->new_edge(init_state, dst, e.cond, {}); + } + + return init_state; + } + + case op::R: + case op::M: + { + auto acc = f.kind() == op::M + ? acc_cond::mark_t{0} + : acc_cond::mark_t{}; + + unsigned init_state = aut_->new_state(); + + unsigned lhs_init = recurse(f[0]); + unsigned rhs_init = recurse(f[1]); + + std::vector new_dests; + for (auto& e : aut_->out(rhs_init)) + { + auto dests = aut_->univ_dests(e); + std::copy(dests.begin(), dests.end(), + std::back_inserter(new_dests)); + new_dests.push_back(init_state); + + unsigned dst = uniq_.new_univ_dests(new_dests.begin(), + new_dests.end()); + aut_->new_edge(init_state, dst, e.cond, acc); + + new_dests.clear(); + } + + std::vector dsts; + for (const auto& lhs_e : aut_->out(lhs_init)) + { + const auto& lhs_dsts = aut_->univ_dests(lhs_e); + std::copy(lhs_dsts.begin(), lhs_dsts.end(), + std::back_inserter(dsts)); + size_t lhs_dest_num = dsts.size(); + + for (const auto& rhs_e : aut_->out(rhs_init)) + { + const auto& rhs_dsts = aut_->univ_dests(rhs_e); + std::copy(rhs_dsts.begin(), rhs_dsts.end(), + std::back_inserter(dsts)); + + bdd cond = lhs_e.cond & rhs_e.cond; + + unsigned dst = uniq_.new_univ_dests(dsts.begin(), + dsts.end()); + aut_->new_edge(init_state, dst, cond, {}); + + // reset to only lhs' current edge destinations + dsts.resize(lhs_dest_num); + } + dsts.clear(); + } + + return init_state; + } + + // F(phi) = tt U phi + case op::F: + { + auto acc = acc_cond::mark_t{0}; + + // if phi is boolean then we can reuse its initial state (otherwise + // we can't because of potential self loops) + if (f[0].is_boolean()) + { + unsigned init_state = recurse(f[0]); + aut_->new_edge(init_state, init_state, bddtrue, acc); + return init_state; + } + + unsigned init_state = aut_->new_state(); + unsigned sub_init = recurse(f[0]); + + aut_->new_edge(init_state, init_state, bddtrue, acc); + + for (auto& e : aut_->out(sub_init)) + aut_->new_edge(init_state, e.dst, e.cond, {}); + + return init_state; + } + + // G phi = ff R phi + case op::G: + { + unsigned init_state = aut_->new_state(); + + unsigned sub_init = recurse(f[0]); + + // translate like R, but only the self loop part; `ff` cancels out + // the product of edges + std::vector new_dests; + for (auto& e : aut_->out(sub_init)) + { + auto dests = aut_->univ_dests(e); + std::copy(dests.begin(), dests.end(), + std::back_inserter(new_dests)); + new_dests.push_back(init_state); + + unsigned dst = uniq_.new_univ_dests(new_dests.begin(), + new_dests.end()); + aut_->new_edge(init_state, dst, e.cond, {}); + + new_dests.clear(); + } + + return init_state; + } + + case op::eword: + case op::Xor: + case op::Implies: + case op::Equiv: + case op::Closure: + case op::NegClosure: + case op::NegClosureMarked: + case op::EConcat: + case op::EConcatMarked: + case op::UConcat: + case op::OrRat: + case op::AndRat: + case op::AndNLM: + case op::Concat: + case op::Fusion: + case op::Star: + case op::FStar: + case op::first_match: + SPOT_UNREACHABLE(); + return -1; + } + + SPOT_UNREACHABLE(); + } + }; + } + + twa_graph_ptr + ltl_to_aa(formula f, bdd_dict_ptr& dict, bool purge_dead_states) + { + SPOT_ASSERT(f.is_ltl_formula()); + f = negative_normal_form(f); + + auto aut = make_twa_graph(dict); + aut->set_co_buchi(); + + unsigned accepting_sink = aut->new_state(); + aut->new_edge(accepting_sink, accepting_sink, bddtrue, {}); + auto builder = ltl_to_aa_builder(aut, accepting_sink); + + unsigned init_state = builder.recurse(f); + aut->set_init_state(init_state); + + if (purge_dead_states) + aut->purge_dead_states(); + + return aut; + } +} diff --git a/spot/twaalgos/translate_aa.hh b/spot/twaalgos/translate_aa.hh new file mode 100644 index 000000000..9a8760072 --- /dev/null +++ b/spot/twaalgos/translate_aa.hh @@ -0,0 +1,32 @@ +// -*- coding: utf-8 -*- +// Copyright (C) 2010-2015, 2017, 2019-2020 Laboratoire de +// Recherche et Développement de l'Epita (LRDE). +// Copyright (C) 2003, 2004, 2005, 2006 Laboratoire d'Informatique de +// Paris 6 (LIP6), département Systèmes Répartis Coopératifs (SRC), +// Université Pierre et Marie Curie. +// +// 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 . + +#pragma once + +#include +#include + +namespace spot +{ + SPOT_API twa_graph_ptr + ltl_to_aa(formula f, bdd_dict_ptr& dict, bool purge_dead_states = false); +} From be45ccd46dd57670ef059608a9bbeba9708d40cd Mon Sep 17 00:00:00 2001 From: Antoine Martin Date: Thu, 7 Jul 2022 17:57:05 +0200 Subject: [PATCH 299/337] ltl2aa: factorize self-loop creation --- spot/twaalgos/translate_aa.cc | 63 +++++++++++++++-------------------- 1 file changed, 26 insertions(+), 37 deletions(-) diff --git a/spot/twaalgos/translate_aa.cc b/spot/twaalgos/translate_aa.cc index 0663651de..531196442 100644 --- a/spot/twaalgos/translate_aa.cc +++ b/spot/twaalgos/translate_aa.cc @@ -43,6 +43,29 @@ namespace spot internal::univ_dest_mapper uniq_; outedge_combiner oe_; + void add_self_loop(twa_graph::edge_storage_t const& e, + unsigned init_state, + acc_cond::mark_t acc) + { + if (e.dst == accepting_sink_) + { + // avoid creating a univ_dests vector if the only dest is an + // accepting sink, which can be simplified, only keeping the self + // loop + aut_->new_edge(init_state, init_state, e.cond, acc); + return; + } + + auto dests = aut_->univ_dests(e); + std::vector new_dests(dests.begin(), dests.end()); + new_dests.push_back(init_state); + + unsigned dst = uniq_.new_univ_dests(new_dests.begin(), + new_dests.end()); + aut_->new_edge(init_state, dst, e.cond, acc); + } + + unsigned recurse(formula f) { switch (f.kind()) @@ -134,18 +157,7 @@ namespace spot std::vector new_dests; for (auto& e : aut_->out(lhs_init)) - { - auto dests = aut_->univ_dests(e); - std::copy(dests.begin(), dests.end(), - std::back_inserter(new_dests)); - new_dests.push_back(init_state); - - unsigned dest = uniq_.new_univ_dests(new_dests.begin(), - new_dests.end()); - aut_->new_edge(init_state, dest, e.cond, acc); - - new_dests.clear(); - } + add_self_loop(e, init_state, acc); for (auto& e : aut_->out(rhs_init)) { @@ -173,20 +185,8 @@ namespace spot unsigned lhs_init = recurse(f[0]); unsigned rhs_init = recurse(f[1]); - std::vector new_dests; for (auto& e : aut_->out(rhs_init)) - { - auto dests = aut_->univ_dests(e); - std::copy(dests.begin(), dests.end(), - std::back_inserter(new_dests)); - new_dests.push_back(init_state); - - unsigned dst = uniq_.new_univ_dests(new_dests.begin(), - new_dests.end()); - aut_->new_edge(init_state, dst, e.cond, acc); - - new_dests.clear(); - } + add_self_loop(e, init_state, acc); std::vector dsts; for (const auto& lhs_e : aut_->out(lhs_init)) @@ -253,18 +253,7 @@ namespace spot // the product of edges std::vector new_dests; for (auto& e : aut_->out(sub_init)) - { - auto dests = aut_->univ_dests(e); - std::copy(dests.begin(), dests.end(), - std::back_inserter(new_dests)); - new_dests.push_back(init_state); - - unsigned dst = uniq_.new_univ_dests(new_dests.begin(), - new_dests.end()); - aut_->new_edge(init_state, dst, e.cond, {}); - - new_dests.clear(); - } + add_self_loop(e, init_state, {}); return init_state; } From 8f4ba3ec1a3d3a6cc2655ba19cf48da56d87e657 Mon Sep 17 00:00:00 2001 From: Antoine Martin Date: Wed, 13 Jul 2022 16:11:54 +0200 Subject: [PATCH 300/337] psl not working --- spot/twaalgos/translate_aa.cc | 78 ++++++++++++++++++++++++++++++++++- 1 file changed, 76 insertions(+), 2 deletions(-) diff --git a/spot/twaalgos/translate_aa.cc b/spot/twaalgos/translate_aa.cc index 531196442..c68b30268 100644 --- a/spot/twaalgos/translate_aa.cc +++ b/spot/twaalgos/translate_aa.cc @@ -20,6 +20,7 @@ #include "config.h" #include #include +#include #include #include @@ -258,6 +259,81 @@ namespace spot return init_state; } + case op::UConcat: + { + // FIXME: combine out edges with rhs ! + //unsigned rhs_init = recurse(f[1]); + twa_graph_ptr sere_aut = derive_finite_automaton_with_first(f[0]); + + const auto& dict = sere_aut->get_dict(); + + std::map old_to_new; + std::map state_to_var; + std::map var_to_state; + bdd vars = bddtrue; + bdd aps = sere_aut->ap_vars(); + std::vector univ_dest; + + // registers a state in various maps and returns the index of the + // anonymous bdd var representing that state + auto register_state = [&](unsigned st) -> int { + auto p = state_to_var.emplace(st, 0); + if (p.second) + { + int v = dict->register_anonymous_variables(1, this); + p.first->second = v; + var_to_state.emplace(v, st); + + unsigned new_st = aut_->new_state(); + old_to_new.emplace(st, new_st); + + vars &= bdd_ithvar(v); + } + + return p.first->second; + }; + + unsigned ns = sere_aut->num_states(); + for (unsigned st = 0; st < ns; ++st) + { + register_state(st); + + bdd sig = bddfalse; + for (const auto& e : sere_aut->out(st)) + { + int st_bddi = register_state(e.dst); + sig |= e.cond & bdd_ithvar(st_bddi); + } + + for (bdd cond : minterms_of(bddtrue, aps)) + { + bdd dest = bdd_appex(sig, cond, bddop_and, aps); + while (dest != bddtrue) + { + assert(bdd_low(dest) == bddfalse); + auto it = var_to_state.find(bdd_var(dest)); + assert(it != var_to_state.end()); + auto it2 = old_to_new.find(it->second); + assert(it2 != old_to_new.end()); + univ_dest.push_back(it2->second); + dest = bdd_high(dest); + } + + auto it = old_to_new.find(st); + assert(it != old_to_new.end()); + unsigned src = it->second; + unsigned dst = uniq_.new_univ_dests(univ_dest.begin(), + univ_dest.end()); + aut_->new_edge(src, dst, cond, {}); + } + } + + auto it = old_to_new.find(sere_aut->get_init_state_number()); + assert(it != old_to_new.end()); + + return it->second; + } + case op::eword: case op::Xor: case op::Implies: @@ -267,7 +343,6 @@ namespace spot case op::NegClosureMarked: case op::EConcat: case op::EConcatMarked: - case op::UConcat: case op::OrRat: case op::AndRat: case op::AndNLM: @@ -288,7 +363,6 @@ namespace spot twa_graph_ptr ltl_to_aa(formula f, bdd_dict_ptr& dict, bool purge_dead_states) { - SPOT_ASSERT(f.is_ltl_formula()); f = negative_normal_form(f); auto aut = make_twa_graph(dict); From 11c469648fb952b9bd8ffccc4058c9c457c55cae Mon Sep 17 00:00:00 2001 From: Antoine Martin Date: Wed, 31 Aug 2022 10:59:39 +0200 Subject: [PATCH 301/337] Add ltl2aa binary to tests/core --- tests/Makefile.am | 2 ++ tests/core/.gitignore | 1 + tests/core/ltl2aa.cc | 22 ++++++++++++++++++++++ 3 files changed, 25 insertions(+) create mode 100644 tests/core/ltl2aa.cc diff --git a/tests/Makefile.am b/tests/Makefile.am index 3bd43d5f4..93cc4f5bf 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -80,6 +80,7 @@ check_PROGRAMS = \ core/intvcomp \ core/intvcmp2 \ core/kripkecat \ + core/ltl2aa \ core/ltl2dot \ core/ltl2text \ core/ltlrel \ @@ -129,6 +130,7 @@ core_cube_SOURCES = core/cube.cc core_equals_SOURCES = core/equalsf.cc core_kind_SOURCES = core/kind.cc core_length_SOURCES = core/length.cc +core_ltl2aa_SOURCES = core/ltl2aa.cc core_ltl2dot_SOURCES = core/readltl.cc core_ltl2dot_CPPFLAGS = $(AM_CPPFLAGS) -DDOTTY core_ltl2text_SOURCES = core/readltl.cc diff --git a/tests/core/.gitignore b/tests/core/.gitignore index d4ebfae45..fdee02715 100644 --- a/tests/core/.gitignore +++ b/tests/core/.gitignore @@ -33,6 +33,7 @@ kripkecat length .libs ikwiad +ltl2aa ltl2dot ltl2text ltlmagic diff --git a/tests/core/ltl2aa.cc b/tests/core/ltl2aa.cc new file mode 100644 index 000000000..82b4b9c7e --- /dev/null +++ b/tests/core/ltl2aa.cc @@ -0,0 +1,22 @@ +#include "config.h" + +#include + +#include +#include +#include +#include + +int main(int argc, char * argv[]) +{ + if (argc < 3) + return 1; + + spot::formula f = spot::parse_formula(argv[1]); + spot::bdd_dict_ptr d = spot::make_bdd_dict(); + auto aut = ltl_to_aa(f, d, true); + + std::ofstream out(argv[2]); + spot::print_hoa(out, aut); + return 0; +} From 7eacf99f7632dbdf11b3834456fe0029211b3a8b Mon Sep 17 00:00:00 2001 From: Antoine Martin Date: Tue, 9 Aug 2022 12:24:37 +0200 Subject: [PATCH 302/337] ltl2aa: fix R & M operators handling --- spot/twaalgos/translate_aa.cc | 28 +++------------------------- 1 file changed, 3 insertions(+), 25 deletions(-) diff --git a/spot/twaalgos/translate_aa.cc b/spot/twaalgos/translate_aa.cc index c68b30268..490ffd7a7 100644 --- a/spot/twaalgos/translate_aa.cc +++ b/spot/twaalgos/translate_aa.cc @@ -189,31 +189,9 @@ namespace spot for (auto& e : aut_->out(rhs_init)) add_self_loop(e, init_state, acc); - std::vector dsts; - for (const auto& lhs_e : aut_->out(lhs_init)) - { - const auto& lhs_dsts = aut_->univ_dests(lhs_e); - std::copy(lhs_dsts.begin(), lhs_dsts.end(), - std::back_inserter(dsts)); - size_t lhs_dest_num = dsts.size(); - - for (const auto& rhs_e : aut_->out(rhs_init)) - { - const auto& rhs_dsts = aut_->univ_dests(rhs_e); - std::copy(rhs_dsts.begin(), rhs_dsts.end(), - std::back_inserter(dsts)); - - bdd cond = lhs_e.cond & rhs_e.cond; - - unsigned dst = uniq_.new_univ_dests(dsts.begin(), - dsts.end()); - aut_->new_edge(init_state, dst, cond, {}); - - // reset to only lhs' current edge destinations - dsts.resize(lhs_dest_num); - } - dsts.clear(); - } + bdd comb = oe_(lhs_init); + comb &= oe_(rhs_init); + oe_.new_dests(init_state, comb); return init_state; } From c1a0b5aa4665003c562d6f927ddea2be68e40df0 Mon Sep 17 00:00:00 2001 From: Antoine Martin Date: Wed, 31 Aug 2022 13:59:05 +0200 Subject: [PATCH 303/337] ltl2aa: fix bdd manipulation in UConcat --- spot/twaalgos/translate_aa.cc | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/spot/twaalgos/translate_aa.cc b/spot/twaalgos/translate_aa.cc index 490ffd7a7..b15dfc279 100644 --- a/spot/twaalgos/translate_aa.cc +++ b/spot/twaalgos/translate_aa.cc @@ -286,22 +286,26 @@ namespace spot for (bdd cond : minterms_of(bddtrue, aps)) { bdd dest = bdd_appex(sig, cond, bddop_and, aps); - while (dest != bddtrue) + while (dest != bddfalse) { - assert(bdd_low(dest) == bddfalse); + assert(bdd_high(dest) == bddtrue); auto it = var_to_state.find(bdd_var(dest)); assert(it != var_to_state.end()); auto it2 = old_to_new.find(it->second); assert(it2 != old_to_new.end()); univ_dest.push_back(it2->second); - dest = bdd_high(dest); + dest = bdd_low(dest); } auto it = old_to_new.find(st); assert(it != old_to_new.end()); unsigned src = it->second; - unsigned dst = uniq_.new_univ_dests(univ_dest.begin(), - univ_dest.end()); + + unsigned dst = univ_dest.empty() + ? accepting_sink_ + : (uniq_.new_univ_dests(univ_dest.begin(), + univ_dest.end())); + aut_->new_edge(src, dst, cond, {}); } } From 85b8717c051b206a2f2a18591b5c713fe279912c Mon Sep 17 00:00:00 2001 From: Antoine Martin Date: Tue, 6 Sep 2022 16:07:28 +0200 Subject: [PATCH 304/337] ltl2aa: share dict between sere and final aut --- spot/tl/derive.cc | 10 ++++++---- spot/tl/derive.hh | 6 ++++-- spot/twaalgos/translate_aa.cc | 10 ++++++++-- 3 files changed, 18 insertions(+), 8 deletions(-) diff --git a/spot/tl/derive.cc b/spot/tl/derive.cc index 2b1873ed2..c6c328786 100644 --- a/spot/tl/derive.cc +++ b/spot/tl/derive.cc @@ -203,9 +203,9 @@ namespace spot } twa_graph_ptr - derive_finite_automaton_with_first(formula f, bool deterministic) + derive_finite_automaton_with_first(formula f, bdd_dict_ptr bdd_dict, + bool deterministic) { - auto bdd_dict = make_bdd_dict(); auto aut = make_twa_graph(bdd_dict); aut->prop_state_acc(true); @@ -403,9 +403,11 @@ namespace spot } twa_graph_ptr - derive_automaton_with_first(formula f, bool deterministic) + derive_automaton_with_first(formula f, bdd_dict_ptr bdd_dict, + bool deterministic) { - auto finite = derive_finite_automaton_with_first(f, deterministic); + auto finite = derive_finite_automaton_with_first(f, bdd_dict, + deterministic); return from_finite(finite); } diff --git a/spot/tl/derive.hh b/spot/tl/derive.hh index 1947951ed..9e094c7b6 100644 --- a/spot/tl/derive.hh +++ b/spot/tl/derive.hh @@ -39,13 +39,15 @@ namespace spot derive_automaton(formula f, bool deterministic = true); SPOT_API twa_graph_ptr - derive_automaton_with_first(formula f, bool deterministic = true); + derive_automaton_with_first(formula f, bdd_dict_ptr bdd_dict, + bool deterministic = true); SPOT_API twa_graph_ptr derive_finite_automaton(formula f, bool deterministic = true); SPOT_API twa_graph_ptr - derive_finite_automaton_with_first(formula f, bool deterministic = true); + derive_finite_automaton_with_first(formula f, bdd_dict_ptr bdd_dict, + bool deterministic = true); SPOT_API formula rewrite_and_nlm(formula f); diff --git a/spot/twaalgos/translate_aa.cc b/spot/twaalgos/translate_aa.cc index b15dfc279..0a29a0671 100644 --- a/spot/twaalgos/translate_aa.cc +++ b/spot/twaalgos/translate_aa.cc @@ -39,6 +39,11 @@ namespace spot { } + ~ltl_to_aa_builder() + { + aut_->get_dict()->unregister_all_my_variables(this); + } + twa_graph_ptr aut_; unsigned accepting_sink_; internal::univ_dest_mapper uniq_; @@ -241,9 +246,9 @@ namespace spot { // FIXME: combine out edges with rhs ! //unsigned rhs_init = recurse(f[1]); - twa_graph_ptr sere_aut = derive_finite_automaton_with_first(f[0]); + const auto& dict = aut_->get_dict(); + twa_graph_ptr sere_aut = derive_finite_automaton_with_first(f[0], dict); - const auto& dict = sere_aut->get_dict(); std::map old_to_new; std::map state_to_var; @@ -271,6 +276,7 @@ namespace spot return p.first->second; }; + aut_->copy_ap_of(sere_aut); unsigned ns = sere_aut->num_states(); for (unsigned st = 0; st < ns; ++st) { From 44568b562221dfbbfb1dc787f811bd2dee059370 Mon Sep 17 00:00:00 2001 From: Antoine Martin Date: Tue, 6 Sep 2022 16:31:17 +0200 Subject: [PATCH 305/337] ltl2aa: implem closure --- spot/twaalgos/translate_aa.cc | 41 ++++++++++++++++++++++++++++++++--- 1 file changed, 38 insertions(+), 3 deletions(-) diff --git a/spot/twaalgos/translate_aa.cc b/spot/twaalgos/translate_aa.cc index 0a29a0671..5330c787d 100644 --- a/spot/twaalgos/translate_aa.cc +++ b/spot/twaalgos/translate_aa.cc @@ -71,6 +71,35 @@ namespace spot aut_->new_edge(init_state, dst, e.cond, acc); } + unsigned copy_sere_aut_to_res(twa_graph_ptr sere_aut) + { + std::map old_to_new; + auto register_state = [&](unsigned st) -> unsigned { + auto p = old_to_new.emplace(st, 0); + if (p.second) + { + unsigned new_st = aut_->new_state(); + p.first->second = new_st; + } + return p.first->second; + }; + + unsigned ns = sere_aut->num_states(); + for (unsigned st = 0; st < ns; ++st) + { + unsigned new_st = register_state(st); + for (const auto& e : sere_aut->out(st)) + { + if (sere_aut->state_is_accepting(e.dst)) + aut_->new_edge(new_st, accepting_sink_, e.cond); + else + aut_->new_edge(new_st, register_state(e.dst), e.cond); + } + } + + return register_state(sere_aut->get_init_state_number()); + } + unsigned recurse(formula f) { @@ -322,13 +351,19 @@ namespace spot return it->second; } + case op::Closure: + { + twa_graph_ptr sere_aut = + derive_finite_automaton_with_first(f[0], aut_->get_dict()); + return copy_sere_aut_to_res(sere_aut); + } + + case op::NegClosure: + case op::NegClosureMarked: case op::eword: case op::Xor: case op::Implies: case op::Equiv: - case op::Closure: - case op::NegClosure: - case op::NegClosureMarked: case op::EConcat: case op::EConcatMarked: case op::OrRat: From 2af19a485b48ba55b2c0d6de39f4107fd6645bcf Mon Sep 17 00:00:00 2001 From: Antoine Martin Date: Tue, 6 Sep 2022 16:31:50 +0200 Subject: [PATCH 306/337] ltl2aa: place new state in var_to_state map --- spot/twaalgos/translate_aa.cc | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/spot/twaalgos/translate_aa.cc b/spot/twaalgos/translate_aa.cc index 5330c787d..c82b81564 100644 --- a/spot/twaalgos/translate_aa.cc +++ b/spot/twaalgos/translate_aa.cc @@ -294,10 +294,10 @@ namespace spot { int v = dict->register_anonymous_variables(1, this); p.first->second = v; - var_to_state.emplace(v, st); unsigned new_st = aut_->new_state(); old_to_new.emplace(st, new_st); + var_to_state.emplace(v, new_st); vars &= bdd_ithvar(v); } @@ -326,9 +326,7 @@ namespace spot assert(bdd_high(dest) == bddtrue); auto it = var_to_state.find(bdd_var(dest)); assert(it != var_to_state.end()); - auto it2 = old_to_new.find(it->second); - assert(it2 != old_to_new.end()); - univ_dest.push_back(it2->second); + univ_dest.push_back(it->second); dest = bdd_low(dest); } From 87c99cb38fa7f63cbf8f472b9b56ea577022f2f5 Mon Sep 17 00:00:00 2001 From: Antoine Martin Date: Fri, 16 Sep 2022 03:40:22 +0200 Subject: [PATCH 307/337] ltl2aa: fix two bugs in SERE aut merge --- spot/twaalgos/translate_aa.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/spot/twaalgos/translate_aa.cc b/spot/twaalgos/translate_aa.cc index c82b81564..daf9126cb 100644 --- a/spot/twaalgos/translate_aa.cc +++ b/spot/twaalgos/translate_aa.cc @@ -73,6 +73,7 @@ namespace spot unsigned copy_sere_aut_to_res(twa_graph_ptr sere_aut) { + aut_->copy_ap_of(sere_aut); std::map old_to_new; auto register_state = [&](unsigned st) -> unsigned { auto p = old_to_new.emplace(st, 0); @@ -340,6 +341,7 @@ namespace spot univ_dest.end())); aut_->new_edge(src, dst, cond, {}); + univ_dest.clear(); } } From 88914c58c700edc6f0f8a072342f31f1fddefac4 Mon Sep 17 00:00:00 2001 From: Antoine Martin Date: Fri, 16 Sep 2022 03:41:30 +0200 Subject: [PATCH 308/337] ltl2aa: finish SERE aut merging with rhs outedges --- spot/twaalgos/alternation.cc | 21 +++++++++++++++++++-- spot/twaalgos/alternation.hh | 2 +- spot/twaalgos/translate_aa.cc | 20 ++++++++++++++++++-- 3 files changed, 38 insertions(+), 5 deletions(-) diff --git a/spot/twaalgos/alternation.cc b/spot/twaalgos/alternation.cc index 3e94dfe3b..eef15b2c4 100644 --- a/spot/twaalgos/alternation.cc +++ b/spot/twaalgos/alternation.cc @@ -36,7 +36,7 @@ namespace spot aut_->get_dict()->unregister_all_my_variables(this); } - bdd outedge_combiner::operator()(unsigned st) + bdd outedge_combiner::operator()(unsigned st, const std::vector& dst_filter) { const auto& dict = aut_->get_dict(); bdd res = bddtrue; @@ -45,6 +45,21 @@ namespace spot bdd res2 = bddfalse; for (auto& e: aut_->out(d1)) { + // handle edge filtering + if (!dst_filter.empty()) + { + // if any edge destination is an accepting state in the SERE + // automaton, handle the edge, otherwise skip it + auto univ_dests = aut_->univ_dests(e.dst); + if (std::all_of(univ_dests.begin(), univ_dests.end(), + [&](unsigned dst) + { + return std::find(dst_filter.begin(), dst_filter.end(), dst) + == dst_filter.end(); + })) + continue; + } + bdd out = bddtrue; for (unsigned d: aut_->univ_dests(e.dst)) { @@ -63,7 +78,9 @@ namespace spot } res2 |= e.cond & out; } - res &= res2; + + if (res2 != bddfalse) + res &= res2; } return res; } diff --git a/spot/twaalgos/alternation.hh b/spot/twaalgos/alternation.hh index f3092eb10..7f0f22bff 100644 --- a/spot/twaalgos/alternation.hh +++ b/spot/twaalgos/alternation.hh @@ -54,7 +54,7 @@ namespace spot public: outedge_combiner(const twa_graph_ptr& aut, unsigned sink = -1u); ~outedge_combiner(); - bdd operator()(unsigned st); + bdd operator()(unsigned st, const std::vector& dst_filter = std::vector()); void new_dests(unsigned st, bdd out) const; }; diff --git a/spot/twaalgos/translate_aa.cc b/spot/twaalgos/translate_aa.cc index daf9126cb..11b783691 100644 --- a/spot/twaalgos/translate_aa.cc +++ b/spot/twaalgos/translate_aa.cc @@ -274,8 +274,7 @@ namespace spot case op::UConcat: { - // FIXME: combine out edges with rhs ! - //unsigned rhs_init = recurse(f[1]); + unsigned rhs_init = recurse(f[1]); const auto& dict = aut_->get_dict(); twa_graph_ptr sere_aut = derive_finite_automaton_with_first(f[0], dict); @@ -286,6 +285,7 @@ namespace spot bdd vars = bddtrue; bdd aps = sere_aut->ap_vars(); std::vector univ_dest; + std::vector acc_states; // registers a state in various maps and returns the index of the // anonymous bdd var representing that state @@ -300,6 +300,9 @@ namespace spot old_to_new.emplace(st, new_st); var_to_state.emplace(v, new_st); + if (sere_aut->state_is_accepting(st)) + acc_states.push_back(new_st); + vars &= bdd_ithvar(v); } @@ -345,9 +348,22 @@ namespace spot } } + for (unsigned st = 0; st < ns; ++st) + { + auto it = old_to_new.find(st); + assert(it != old_to_new.end()); + unsigned new_st = it->second; + + bdd comb = bddtrue; + comb &= oe_(new_st, acc_states); + comb &= oe_(rhs_init); + oe_.new_dests(new_st, comb); + } + auto it = old_to_new.find(sere_aut->get_init_state_number()); assert(it != old_to_new.end()); + //aut_->merge_edges(); return it->second; } From 36b09fa1f666213694accfe09a50780e960c21bd Mon Sep 17 00:00:00 2001 From: Antoine Martin Date: Fri, 16 Sep 2022 15:48:07 +0200 Subject: [PATCH 309/337] ltl2aa: finalize UConcat --- spot/twaalgos/alternation.cc | 6 +++++- spot/twaalgos/alternation.hh | 3 ++- spot/twaalgos/translate_aa.cc | 11 +++++++---- 3 files changed, 14 insertions(+), 6 deletions(-) diff --git a/spot/twaalgos/alternation.cc b/spot/twaalgos/alternation.cc index eef15b2c4..c9bd4ddb4 100644 --- a/spot/twaalgos/alternation.cc +++ b/spot/twaalgos/alternation.cc @@ -36,7 +36,8 @@ namespace spot aut_->get_dict()->unregister_all_my_variables(this); } - bdd outedge_combiner::operator()(unsigned st, const std::vector& dst_filter) + bdd outedge_combiner::operator()(unsigned st, const std::vector& dst_filter, + bool remove_original_edges) { const auto& dict = aut_->get_dict(); bdd res = bddtrue; @@ -77,6 +78,9 @@ namespace spot out &= bdd_ithvar(p.first->second); } res2 |= e.cond & out; + + if (remove_original_edges) + e.cond = bddfalse; } if (res2 != bddfalse) diff --git a/spot/twaalgos/alternation.hh b/spot/twaalgos/alternation.hh index 7f0f22bff..0017f27bb 100644 --- a/spot/twaalgos/alternation.hh +++ b/spot/twaalgos/alternation.hh @@ -54,7 +54,8 @@ namespace spot public: outedge_combiner(const twa_graph_ptr& aut, unsigned sink = -1u); ~outedge_combiner(); - bdd operator()(unsigned st, const std::vector& dst_filter = std::vector()); + bdd operator()(unsigned st, const std::vector& dst_filter = std::vector(), + bool remove_original_edges = false); void new_dests(unsigned st, bdd out) const; }; diff --git a/spot/twaalgos/translate_aa.cc b/spot/twaalgos/translate_aa.cc index 11b783691..bd1a1d3de 100644 --- a/spot/twaalgos/translate_aa.cc +++ b/spot/twaalgos/translate_aa.cc @@ -355,15 +355,18 @@ namespace spot unsigned new_st = it->second; bdd comb = bddtrue; - comb &= oe_(new_st, acc_states); - comb &= oe_(rhs_init); - oe_.new_dests(new_st, comb); + comb &= oe_(new_st, acc_states, true); + if (comb != bddtrue) + { + comb &= oe_(rhs_init); + oe_.new_dests(new_st, comb); + } } auto it = old_to_new.find(sere_aut->get_init_state_number()); assert(it != old_to_new.end()); - //aut_->merge_edges(); + aut_->merge_edges(); return it->second; } From 3744d0cbed8c05cfb8129766d4c88f5f3fc34689 Mon Sep 17 00:00:00 2001 From: Antoine Martin Date: Fri, 16 Sep 2022 15:49:56 +0200 Subject: [PATCH 310/337] ltl2aa: comment --- spot/twaalgos/translate_aa.cc | 1 + 1 file changed, 1 insertion(+) diff --git a/spot/twaalgos/translate_aa.cc b/spot/twaalgos/translate_aa.cc index bd1a1d3de..1fd6e03df 100644 --- a/spot/twaalgos/translate_aa.cc +++ b/spot/twaalgos/translate_aa.cc @@ -285,6 +285,7 @@ namespace spot bdd vars = bddtrue; bdd aps = sere_aut->ap_vars(); std::vector univ_dest; + // TODO: this should be a std::vector ! std::vector acc_states; // registers a state in various maps and returns the index of the From 66f0ab85d051f9350b3767130af1900188244024 Mon Sep 17 00:00:00 2001 From: Antoine Martin Date: Tue, 20 Sep 2022 22:42:40 +0200 Subject: [PATCH 311/337] ltl2aa: implement EConcat --- spot/twaalgos/translate_aa.cc | 69 +++++++++++++++++++++++++++++++---- 1 file changed, 61 insertions(+), 8 deletions(-) diff --git a/spot/twaalgos/translate_aa.cc b/spot/twaalgos/translate_aa.cc index 1fd6e03df..d4128ed4f 100644 --- a/spot/twaalgos/translate_aa.cc +++ b/spot/twaalgos/translate_aa.cc @@ -71,34 +71,43 @@ namespace spot aut_->new_edge(init_state, dst, e.cond, acc); } - unsigned copy_sere_aut_to_res(twa_graph_ptr sere_aut) + unsigned copy_sere_aut_to_res(twa_graph_ptr sere_aut, + std::map& old_to_new, + std::vector* acc_states = nullptr, + bool use_accepting_sink = true) { + unsigned ns = sere_aut->num_states(); + + // TODO: create all new states at once, keeping an initial offset (the + // number of states already present in aut_) aut_->copy_ap_of(sere_aut); - std::map old_to_new; auto register_state = [&](unsigned st) -> unsigned { auto p = old_to_new.emplace(st, 0); if (p.second) { unsigned new_st = aut_->new_state(); p.first->second = new_st; + if (acc_states != nullptr && sere_aut->state_is_accepting(st)) + acc_states->push_back(new_st); } return p.first->second; }; - unsigned ns = sere_aut->num_states(); for (unsigned st = 0; st < ns; ++st) { unsigned new_st = register_state(st); for (const auto& e : sere_aut->out(st)) { - if (sere_aut->state_is_accepting(e.dst)) + if (use_accepting_sink && sere_aut->state_is_accepting(e.dst)) aut_->new_edge(new_st, accepting_sink_, e.cond); else aut_->new_edge(new_st, register_state(e.dst), e.cond); } } - return register_state(sere_aut->get_init_state_number()); + auto it = old_to_new.find(sere_aut->get_init_state_number()); + assert(it != old_to_new.end()); + return it->second; } @@ -272,6 +281,47 @@ namespace spot return init_state; } + case op::EConcat: + { + unsigned rhs_init = recurse(f[1]); + const auto& dict = aut_->get_dict(); + twa_graph_ptr sere_aut = derive_finite_automaton_with_first(f[0], dict); + + // TODO: this should be a std::vector ! + std::vector acc_states; + std::map old_to_new; + copy_sere_aut_to_res(sere_aut, old_to_new, &acc_states, false); + + std::vector acc_edges; + unsigned ns = sere_aut->num_states(); + for (unsigned st = 0; st < ns; ++st) + { + auto it = old_to_new.find(st); + assert(it != old_to_new.end()); + unsigned new_st = it->second; + + for (auto& e : aut_->out(new_st)) + { + e.acc = acc_cond::mark_t{0}; + if (std::find(acc_states.begin(), acc_states.end(), e.dst) + != acc_states.end()) + acc_edges.push_back(aut_->edge_number(e)); + } + } + + for (unsigned i : acc_edges) + { + auto& e1 = aut_->edge_storage(i); + for (const auto& e2 : aut_->out(rhs_init)) + aut_->new_edge(e1.src, e2.dst, e1.cond & e2.cond); + } + + auto it = old_to_new.find(sere_aut->get_init_state_number()); + assert(it != old_to_new.end()); + + return it->second; + } + case op::UConcat: { unsigned rhs_init = recurse(f[1]); @@ -375,7 +425,8 @@ namespace spot { twa_graph_ptr sere_aut = derive_finite_automaton_with_first(f[0], aut_->get_dict()); - return copy_sere_aut_to_res(sere_aut); + std::map old_to_new; + return copy_sere_aut_to_res(sere_aut, old_to_new); } case op::NegClosure: @@ -384,7 +435,6 @@ namespace spot case op::Xor: case op::Implies: case op::Equiv: - case op::EConcat: case op::EConcatMarked: case op::OrRat: case op::AndRat: @@ -419,7 +469,10 @@ namespace spot aut->set_init_state(init_state); if (purge_dead_states) - aut->purge_dead_states(); + { + aut->purge_dead_states(); + aut->merge_edges(); + } return aut; } From 2d11d907ef4af4bd400d49a49a4bb6b3ebf8e434 Mon Sep 17 00:00:00 2001 From: Antoine Martin Date: Fri, 30 Sep 2022 01:25:45 +0200 Subject: [PATCH 312/337] alternation: fix bug introduced in oe_combiner turns out sometimes we want to account for bddfalse --- spot/twaalgos/alternation.cc | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/spot/twaalgos/alternation.cc b/spot/twaalgos/alternation.cc index c9bd4ddb4..694796c4b 100644 --- a/spot/twaalgos/alternation.cc +++ b/spot/twaalgos/alternation.cc @@ -83,8 +83,7 @@ namespace spot e.cond = bddfalse; } - if (res2 != bddfalse) - res &= res2; + res &= res2; } return res; } From 8abad2b4f75be700fb18514bf6e034b343529d9f Mon Sep 17 00:00:00 2001 From: Antoine Martin Date: Fri, 30 Sep 2022 01:32:01 +0200 Subject: [PATCH 313/337] ltl2aa: handle edge case in UConcat If SERE recognizes false, then combined with UConcat the property is always true. --- spot/twaalgos/translate_aa.cc | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/spot/twaalgos/translate_aa.cc b/spot/twaalgos/translate_aa.cc index d4128ed4f..c18570d41 100644 --- a/spot/twaalgos/translate_aa.cc +++ b/spot/twaalgos/translate_aa.cc @@ -328,6 +328,13 @@ namespace spot const auto& dict = aut_->get_dict(); twa_graph_ptr sere_aut = derive_finite_automaton_with_first(f[0], dict); + // DFA recognizes the empty language, so {0} []-> rhs is always true + unsigned ns = sere_aut->num_states(); + bool has_accepting_state = false; + for (unsigned st = 0; st < ns && !has_accepting_state; ++st) + has_accepting_state = sere_aut->state_is_accepting(st); + if (!has_accepting_state) + return accepting_sink_; std::map old_to_new; std::map state_to_var; @@ -361,7 +368,6 @@ namespace spot }; aut_->copy_ap_of(sere_aut); - unsigned ns = sere_aut->num_states(); for (unsigned st = 0; st < ns; ++st) { register_state(st); From 2ef0ea00f44cfb3901d43591dcb49a9cb59532dd Mon Sep 17 00:00:00 2001 From: Antoine Martin Date: Thu, 3 Nov 2022 06:58:21 +0100 Subject: [PATCH 314/337] sere_to_tgba: produce state-names --- spot/twaalgos/ltl2tgba_fm.cc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/spot/twaalgos/ltl2tgba_fm.cc b/spot/twaalgos/ltl2tgba_fm.cc index 97008b0df..97a2eceb3 100644 --- a/spot/twaalgos/ltl2tgba_fm.cc +++ b/spot/twaalgos/ltl2tgba_fm.cc @@ -2259,9 +2259,11 @@ namespace spot const auto acc_mark = res->set_buchi(); size_t sn = namer->state_to_name.size(); + auto names = new std::vector(sn); for (size_t i = 0; i < sn; ++i) { formula g = namer->state_to_name[i]; + (*names)[i] = str_psl(g); if (g.accepts_eword()) { if (res->get_graph().state_storage(i).succ == 0) @@ -2274,6 +2276,8 @@ namespace spot } } + res->set_named_prop("state-names", names); + return res; } } From b2b80831ca337cea94beeae70fa5ee0b13a5379f Mon Sep 17 00:00:00 2001 From: Antoine Martin Date: Thu, 10 Nov 2022 07:18:29 +0100 Subject: [PATCH 315/337] derive: option for some optimisations --- spot/tl/derive.cc | 17 ++++++++++++++--- spot/tl/derive.hh | 10 ++++++++-- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/spot/tl/derive.cc b/spot/tl/derive.cc index c6c328786..f55998660 100644 --- a/spot/tl/derive.cc +++ b/spot/tl/derive.cc @@ -204,7 +204,7 @@ namespace spot twa_graph_ptr derive_finite_automaton_with_first(formula f, bdd_dict_ptr bdd_dict, - bool deterministic) + bool deterministic, derive_opts options) { auto aut = make_twa_graph(bdd_dict); @@ -264,7 +264,7 @@ namespace spot for (const bdd one : minterms_of(firsts, firsts_support)) { formula derivative = - partial_derivation(curr_f, one, bdd_dict, aut.get()); + partial_derivation(curr_f, one, bdd_dict, aut.get(), options); // no transition possible from this letter if (derivative.is(op::ff)) @@ -422,7 +422,7 @@ namespace spot formula partial_derivation(formula f, const bdd var, const bdd_dict_ptr& d, - void* owner) + void* owner, derive_opts options) { if (f.is_boolean()) { @@ -472,6 +472,17 @@ namespace spot formula d_E = partial_derivation(f[0], var, d, owner); + if (options.concat_star_distribute && !f[0].is_finite() && d_E.is(op::OrRat)) + { + std::vector distributed; + for (auto g : d_E) + { + distributed.push_back(formula::Concat({g, formula::Star(f[0], min, max)})); + } + + return formula::OrRat(distributed); + } + return formula::Concat({ d_E, formula::Star(f[0], min, max) }); } diff --git a/spot/tl/derive.hh b/spot/tl/derive.hh index 9e094c7b6..993db2ed2 100644 --- a/spot/tl/derive.hh +++ b/spot/tl/derive.hh @@ -29,11 +29,17 @@ namespace spot { + + struct derive_opts + { + bool concat_star_distribute = true; + }; + /// \ingroup tl_misc /// \brief Produce a SERE formula's partial derivative SPOT_API formula partial_derivation(formula f, const bdd var, const bdd_dict_ptr& d, - void* owner); + void* owner, derive_opts options = {}); SPOT_API twa_graph_ptr derive_automaton(formula f, bool deterministic = true); @@ -47,7 +53,7 @@ namespace spot SPOT_API twa_graph_ptr derive_finite_automaton_with_first(formula f, bdd_dict_ptr bdd_dict, - bool deterministic = true); + bool deterministic = true, derive_opts options = {}); SPOT_API formula rewrite_and_nlm(formula f); From 4ce9c483c152bd0b2fc17b07f10d8a1743723103 Mon Sep 17 00:00:00 2001 From: Antoine Martin Date: Mon, 21 Nov 2022 10:37:14 +0100 Subject: [PATCH 316/337] derive: add options to control distribution --- spot/tl/derive.cc | 38 ++++++++++++++++++++++++++++---------- 1 file changed, 28 insertions(+), 10 deletions(-) diff --git a/spot/tl/derive.cc b/spot/tl/derive.cc index f55998660..5e8526eec 100644 --- a/spot/tl/derive.cc +++ b/spot/tl/derive.cc @@ -451,12 +451,30 @@ namespace spot formula E = f[0]; formula F = f.all_but(0); - auto res = - formula::Concat({ partial_derivation(E, var, d, owner), F }); + formula d_E = partial_derivation(E, var, d, owner, options); + + formula res; + + if (options.concat_star_distribute && d_E.is(op::OrRat)) + { + std::vector distributed; + for (auto g : d_E) + { + distributed.push_back(formula::Concat({g, F})); + } + + res = formula::OrRat(distributed); + } + else + { + res = + formula::Concat({ partial_derivation(E, var, d, owner, options), F }); + } + if (E.accepts_eword()) res = formula::OrRat( - { res, partial_derivation(F, var, d, owner) }); + { res, partial_derivation(F, var, d, owner, options) }); return res; } @@ -470,7 +488,7 @@ namespace spot ? formula::unbounded() : (f.max() - 1); - formula d_E = partial_derivation(f[0], var, d, owner); + formula d_E = partial_derivation(f[0], var, d, owner, options); if (options.concat_star_distribute && !f[0].is_finite() && d_E.is(op::OrRat)) { @@ -494,7 +512,7 @@ namespace spot if (f.min() == 0 && f.max() == 0) return formula::tt(); - auto d_E = partial_derivation(E, var, d, owner); + auto d_E = partial_derivation(E, var, d, owner, options); auto min = f.min() == 0 ? 0 : (f.min() - 1); auto max = f.max() == formula::unbounded() @@ -524,7 +542,7 @@ namespace spot for (auto subformula : f) { auto subderivation = - partial_derivation(subformula, var, d, owner); + partial_derivation(subformula, var, d, owner, options); subderivations.push_back(subderivation); } return formula::multop(f.kind(), std::move(subderivations)); @@ -533,7 +551,7 @@ namespace spot case op::AndNLM: { formula rewrite = rewrite_and_nlm(f); - return partial_derivation(rewrite, var, d, owner); + return partial_derivation(rewrite, var, d, owner, options); } // d(E:F) = {d(E):F} U {c(d(E)).d(F)} @@ -542,12 +560,12 @@ namespace spot formula E = f[0]; formula F = f.all_but(0); - auto d_E = partial_derivation(E, var, d, owner); + auto d_E = partial_derivation(E, var, d, owner, options); auto res = formula::Fusion({ d_E, F }); if (d_E.accepts_eword()) res = - formula::OrRat({ res, partial_derivation(F, var, d, owner) }); + formula::OrRat({ res, partial_derivation(F, var, d, owner, options) }); return res; } @@ -555,7 +573,7 @@ namespace spot case op::first_match: { formula E = f[0]; - auto d_E = partial_derivation(E, var, d, owner); + auto d_E = partial_derivation(E, var, d, owner, options); // if d_E.accepts_eword(), first_match(d_E) will return eword return formula::first_match(d_E); } From 16fd28d29b570c2f2867b4329bda554a5eb64477 Mon Sep 17 00:00:00 2001 From: Antoine Martin Date: Wed, 30 Nov 2022 15:28:49 +0100 Subject: [PATCH 317/337] expansions: draft --- python/spot/impl.i | 2 + spot/tl/Makefile.am | 2 + spot/tl/expansions.cc | 418 ++++++++++++++++++++++++++++++++++++++++++ spot/tl/expansions.hh | 46 +++++ tests/Makefile.am | 2 + tests/core/expand.cc | 25 +++ 6 files changed, 495 insertions(+) create mode 100644 spot/tl/expansions.cc create mode 100644 spot/tl/expansions.hh create mode 100644 tests/core/expand.cc diff --git a/python/spot/impl.i b/python/spot/impl.i index 5f3ac1a67..c186e8c07 100644 --- a/python/spot/impl.i +++ b/python/spot/impl.i @@ -92,6 +92,7 @@ #include #include #include +#include #include #include #include @@ -606,6 +607,7 @@ namespace std { %include %include %include +%include %include %include %include diff --git a/spot/tl/Makefile.am b/spot/tl/Makefile.am index 7440cbae3..bd7516c30 100644 --- a/spot/tl/Makefile.am +++ b/spot/tl/Makefile.am @@ -32,6 +32,7 @@ tl_HEADERS = \ dot.hh \ environment.hh \ exclusive.hh \ + expansions.hh \ formula.hh \ hierarchy.hh \ length.hh \ @@ -57,6 +58,7 @@ libtl_la_SOURCES = \ derive.cc \ dot.cc \ exclusive.cc \ + expansions.cc \ formula.cc \ hierarchy.cc \ length.cc \ diff --git a/spot/tl/expansions.cc b/spot/tl/expansions.cc new file mode 100644 index 000000000..4fb2c91c3 --- /dev/null +++ b/spot/tl/expansions.cc @@ -0,0 +1,418 @@ +// -*- coding: utf-8 -*- +// Copyright (C) 2021 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 . + +#include "config.h" +#include +#include +#include +#include +#include + +namespace spot +{ + namespace + { + static void + insert_or_merge(expansion_t& exp, bdd letter, formula suffix) + { + auto res = exp.insert({letter, suffix}); + if (!res.second) + { + auto it = res.first; + it->second = formula::OrRat({it->second, suffix}); + } + } + + // FIXME: could probably just return a map directly + static std::vector + formula_aps(formula f) + { + auto res = std::unordered_set(); + + f.traverse([&res](formula f) + { + if (f.is(op::ap)) + { + res.insert(f.ap_name()); + return true; + } + + return false; + }); + + return std::vector(res.begin(), res.end()); + } + formula + rewrite_and_nlm(formula f) + { + unsigned s = f.size(); + std::vector final; + std::vector non_final; + + for (auto g: f) + if (g.accepts_eword()) + final.emplace_back(g); + else + non_final.emplace_back(g); + + if (non_final.empty()) + // (a* & b*);c = (a*|b*);c + return formula::OrRat(std::move(final)); + if (!final.empty()) + { + // let F_i be final formulae + // N_i be non final formula + // (F_1 & ... & F_n & N_1 & ... & N_m) + // = (F_1 | ... | F_n);[*] && (N_1 & ... & N_m) + // | (F_1 | ... | F_n) && (N_1 & ... & N_m);[*] + formula f = formula::OrRat(std::move(final)); + formula n = formula::AndNLM(std::move(non_final)); + formula t = formula::one_star(); + formula ft = formula::Concat({f, t}); + formula nt = formula::Concat({n, t}); + formula ftn = formula::AndRat({ft, n}); + formula fnt = formula::AndRat({f, nt}); + return formula::OrRat({ftn, fnt}); + } + // No final formula. + // Translate N_1 & N_2 & ... & N_n into + // N_1 && (N_2;[*]) && ... && (N_n;[*]) + // | (N_1;[*]) && N_2 && ... && (N_n;[*]) + // | (N_1;[*]) && (N_2;[*]) && ... && N_n + formula star = formula::one_star(); + std::vector disj; + for (unsigned n = 0; n < s; ++n) + { + std::vector conj; + for (unsigned m = 0; m < s; ++m) + { + formula g = f[m]; + if (n != m) + g = formula::Concat({g, star}); + conj.emplace_back(g); + } + disj.emplace_back(formula::AndRat(std::move(conj))); + } + return formula::OrRat(std::move(disj)); + } + } + + formula + expansion_to_formula(expansion_t e, bdd_dict_ptr& d) + { + std::vector res; + + for (const auto& [key, val] : e) + { + formula prefix = bdd_to_formula(key, d); + res.push_back(formula::Concat({prefix, val})); + } + + return formula::OrRat(res); + } + + expansion_t + expansion(formula f, const bdd_dict_ptr& d, void *owner) + { + if (f.is_boolean()) + { + auto f_bdd = formula_to_bdd(f, d, owner); + + if (f_bdd == bddfalse) + return {}; + + return {{f_bdd, formula::eword()}}; + } + + + switch (f.kind()) + { + case op::ff: + case op::tt: + case op::ap: + SPOT_UNREACHABLE(); + + case op::eword: + return {{bddfalse, formula::ff()}}; + + case op::Concat: + { + auto exps = expansion(f[0], d, owner); + + expansion_t res; + for (const auto& [bdd_l, form] : exps) + { + res.insert({bdd_l, formula::Concat({form, f.all_but(0)})}); + } + + if (f[0].accepts_eword()) + { + auto exps_rest = expansion(f.all_but(0), d, owner); + for (const auto& [bdd_l, form] : exps_rest) + { + insert_or_merge(res, bdd_l, form); + } + } + return res; + } + + case op::FStar: + { + formula E = f[0]; + + if (f.min() == 0 && f.max() == 0) + return {{bddtrue, formula::eword()}}; + + auto min = f.min() == 0 ? 0 : (f.min() - 1); + auto max = f.max() == formula::unbounded() + ? formula::unbounded() + : (f.max() - 1); + + auto E_i_j_minus = formula::FStar(E, min, max); + + auto exp = expansion(E, d, owner); + expansion_t res; + for (const auto& [li, ei] : exp) + { + insert_or_merge(res, li, formula::Fusion({ei, E_i_j_minus})); + + if (ei.accepts_eword() && f.min() != 0) + { + for (const auto& [ki, fi] : expansion(E_i_j_minus, d, owner)) + { + // FIXME: build bdd once + if ((li & ki) != bddfalse) + insert_or_merge(res, li & ki, fi); + } + } + } + if (f.min() == 0) + insert_or_merge(res, bddtrue, formula::eword()); + + return res; + } + + case op::Star: + { + auto min = f.min() == 0 ? 0 : (f.min() - 1); + auto max = f.max() == formula::unbounded() + ? formula::unbounded() + : (f.max() - 1); + + auto exps = expansion(f[0], d, owner); + + expansion_t res; + for (const auto& [bdd_l, form] : exps) + { + res.insert({bdd_l, formula::Concat({form, formula::Star(f[0], min, max)})}); + } + + return res; + } + + case op::AndNLM: + { + formula rewrite = rewrite_and_nlm(f); + return expansion(rewrite, d, owner); + } + + case op::first_match: + { + auto exps = expansion(f[0], d, owner); + + expansion_t res; + for (const auto& [bdd_l, form] : exps) + { + res.insert({bdd_l, formula::first_match(form)}); + } + + return res; + } + + case op::Fusion: + { + expansion_t res; + formula E = f[0]; + formula F = f.all_but(0); + + expansion_t Ei = expansion(E, d, owner); + // TODO: std::option + expansion_t Fj = expansion(F, d, owner); + + for (const auto& [li, ei] : Ei) + { + if (ei.accepts_eword()) + { + for (const auto& [kj, fj] : Fj) + if ((li & kj) != bddfalse) + insert_or_merge(res, li & kj, fj); + } + insert_or_merge(res, li, formula::Fusion({ei, F})); + } + + return res; + } + + case op::AndRat: + case op::OrRat: + { + expansion_t res; + for (const auto& sub_f : f) + { + auto exps = expansion(sub_f, d, owner); + + if (exps.empty()) + { + if (f.kind() == op::OrRat) + continue; + + // op::AndRat: one of the expansions was empty (the only + // edge was `false`), so the AndRat is empty as + // well + res.clear(); + break; + } + + if (res.empty()) + { + res = std::move(exps); + continue; + } + + expansion_t new_res; + for (const auto& [l_key, l_val] : exps) + { + for (const auto& [r_key, r_val] : res) + { + if ((l_key & r_key) != bddfalse) + insert_or_merge(new_res, l_key & r_key, formula::multop(f.kind(), {l_val, r_val})); + + if (f.is(op::OrRat)) + { + if ((l_key & !r_key) != bddfalse) + insert_or_merge(new_res, l_key & !r_key, l_val); + + if ((!l_key & r_key) != bddfalse) + insert_or_merge(new_res, !l_key & r_key, r_val); + } + } + } + + res = std::move(new_res); + } + + return res; + } + + default: + std::cerr << "unimplemented kind " + << static_cast(f.kind()) + << std::endl; + SPOT_UNIMPLEMENTED(); + } + + return {}; + } + + twa_graph_ptr + expand_automaton(formula f, bdd_dict_ptr d) + { + auto finite = expand_finite_automaton(f, d); + return from_finite(finite); + } + + twa_graph_ptr + expand_finite_automaton(formula f, bdd_dict_ptr d) + { + auto aut = make_twa_graph(d); + + aut->prop_state_acc(true); + const auto acc_mark = aut->set_buchi(); + + auto formula2state = robin_hood::unordered_map(); + + unsigned init_state = aut->new_state(); + aut->set_init_state(init_state); + formula2state.insert({ f, init_state }); + + auto f_aps = formula_aps(f); + for (auto& ap : f_aps) + aut->register_ap(ap); + + auto todo = std::vector>(); + todo.push_back({f, init_state}); + + auto state_names = new std::vector(); + std::ostringstream ss; + ss << f; + state_names->push_back(ss.str()); + + auto find_dst = [&](formula suffix) -> unsigned + { + unsigned dst; + auto it = formula2state.find(suffix); + if (it != formula2state.end()) + { + dst = it->second; + } + else + { + dst = aut->new_state(); + todo.push_back({suffix, dst}); + formula2state.insert({suffix, dst}); + std::ostringstream ss; + ss << suffix; + state_names->push_back(ss.str()); + } + + return dst; + }; + + while (!todo.empty()) + { + auto [curr_f, curr_state] = todo[todo.size() - 1]; + todo.pop_back(); + + auto curr_acc_mark= curr_f.accepts_eword() + ? acc_mark + : acc_cond::mark_t(); + + auto exp = expansion(curr_f, d, aut.get()); + + for (const auto& [letter, suffix] : exp) + { + if (suffix.is(op::ff)) + continue; + + auto dst = find_dst(suffix); + aut->new_edge(curr_state, dst, letter, curr_acc_mark); + } + + // if state has no transitions and should be accepting, create + // artificial transition + if (aut->get_graph().state_storage(curr_state).succ == 0 + && curr_f.accepts_eword()) + aut->new_edge(curr_state, curr_state, bddfalse, acc_mark); + } + + aut->set_named_prop("state-names", state_names); + aut->merge_edges(); + return aut; + } +} diff --git a/spot/tl/expansions.hh b/spot/tl/expansions.hh new file mode 100644 index 000000000..af80d7e8b --- /dev/null +++ b/spot/tl/expansions.hh @@ -0,0 +1,46 @@ +// -*- coding: utf-8 -*- +// Copyright (C) 2021 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 . + +#pragma once + +#include + +#include + +#include +#include +#include +#include + +namespace spot +{ + using expansion_t = std::map; + + SPOT_API expansion_t + expansion(formula f, const bdd_dict_ptr& d, void *owner); + + SPOT_API formula + expansion_to_formula(expansion_t e, bdd_dict_ptr& d); + + SPOT_API twa_graph_ptr + expand_automaton(formula f, bdd_dict_ptr d); + + SPOT_API twa_graph_ptr + expand_finite_automaton(formula f, bdd_dict_ptr d); +} diff --git a/tests/Makefile.am b/tests/Makefile.am index 93cc4f5bf..6728ea0ba 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -73,6 +73,7 @@ check_PROGRAMS = \ core/cube \ core/emptchk \ core/equals \ + core/expand \ core/graph \ core/kind \ core/length \ @@ -115,6 +116,7 @@ core_bricks_SOURCES = core/bricks.cc core_checkpsl_SOURCES = core/checkpsl.cc core_checkta_SOURCES = core/checkta.cc core_emptchk_SOURCES = core/emptchk.cc +core_expand_SOURCES = core/expand.cc core_graph_SOURCES = core/graph.cc core_ikwiad_SOURCES = core/ikwiad.cc core_intvcomp_SOURCES = core/intvcomp.cc diff --git a/tests/core/expand.cc b/tests/core/expand.cc new file mode 100644 index 000000000..a589d6370 --- /dev/null +++ b/tests/core/expand.cc @@ -0,0 +1,25 @@ +#include "config.h" + +#include +#include +#include +#include + +int main(int argc, char** argv) +{ + if (argc != 2) + return 1; + + spot::formula f = spot::parse_infix_sere(argv[1]).f; + auto d = spot::make_bdd_dict(); + + auto m = spot::expansion(f, d, nullptr); + + for (const auto& [bdd_l, form] : m) + std::cout << '[' << bdd_to_formula(bdd_l, d) << ']' << ": " << form << std::endl; + std::cout << "formula: " << expansion_to_formula(m, d) << std::endl; + + d->unregister_all_my_variables(nullptr); + + return 0; +} From bd8b5b4b5115fb290cf8564307e19bf4987e80cf Mon Sep 17 00:00:00 2001 From: Antoine Martin Date: Thu, 15 Dec 2022 08:39:13 +0100 Subject: [PATCH 318/337] expansions: first_match deterministic --- spot/tl/expansions.cc | 39 +++++++++++++++++++++++++++++++++++++-- 1 file changed, 37 insertions(+), 2 deletions(-) diff --git a/spot/tl/expansions.cc b/spot/tl/expansions.cc index 4fb2c91c3..689e90a82 100644 --- a/spot/tl/expansions.cc +++ b/spot/tl/expansions.cc @@ -236,10 +236,45 @@ namespace spot { auto exps = expansion(f[0], d, owner); - expansion_t res; + expansion_t ndet_res; for (const auto& [bdd_l, form] : exps) { - res.insert({bdd_l, formula::first_match(form)}); + ndet_res.insert({bdd_l, form}); + } + + bdd or_labels = bddfalse; + bdd support = bddtrue; + bool is_det = true; + for (const auto& [l, _] : ndet_res) + { + support &= bdd_support(l); + if (is_det) + is_det = !bdd_have_common_assignment(l, or_labels); + or_labels |= l; + } + + if (is_det) + { + // we don't need to determinize the expansion, it's already + // deterministic + for (auto& [_, dest] : ndet_res) + dest = formula::first_match(dest); + return ndet_res; + } + + expansion_t res; + // TODO: extraire en fonction indépendante + lambda choix wrapper + std::vector dests; + for (bdd l: minterms_of(or_labels, support)) + { + for (const auto& [ndet_label, ndet_dest] : ndet_res) + { + if (bdd_implies(l, ndet_label)) + dests.push_back(ndet_dest); + } + formula or_dests = formula::OrRat(dests); + res.insert({l, formula::first_match(or_dests)}); + dests.clear(); } return res; From 2e40892fd68fa80e5e5e26bb8e550ec24f1bcf05 Mon Sep 17 00:00:00 2001 From: Antoine Martin Date: Thu, 15 Dec 2022 10:44:37 +0100 Subject: [PATCH 319/337] expansions: split-off OrRat case --- spot/tl/expansions.cc | 35 ++++++++++++++++++++++------------- 1 file changed, 22 insertions(+), 13 deletions(-) diff --git a/spot/tl/expansions.cc b/spot/tl/expansions.cc index 689e90a82..b3f6eed9b 100644 --- a/spot/tl/expansions.cc +++ b/spot/tl/expansions.cc @@ -305,7 +305,6 @@ namespace spot } case op::AndRat: - case op::OrRat: { expansion_t res; for (const auto& sub_f : f) @@ -314,9 +313,6 @@ namespace spot if (exps.empty()) { - if (f.kind() == op::OrRat) - continue; - // op::AndRat: one of the expansions was empty (the only // edge was `false`), so the AndRat is empty as // well @@ -337,15 +333,6 @@ namespace spot { if ((l_key & r_key) != bddfalse) insert_or_merge(new_res, l_key & r_key, formula::multop(f.kind(), {l_val, r_val})); - - if (f.is(op::OrRat)) - { - if ((l_key & !r_key) != bddfalse) - insert_or_merge(new_res, l_key & !r_key, l_val); - - if ((!l_key & r_key) != bddfalse) - insert_or_merge(new_res, !l_key & r_key, r_val); - } } } @@ -355,6 +342,28 @@ namespace spot return res; } + case op::OrRat: + { + expansion_t res; + for (const auto& sub_f : f) + { + auto exps = expansion(sub_f, d, owner); + if (exps.empty()) + continue; + + if (res.empty()) + { + res = std::move(exps); + continue; + } + + for (const auto& [label, dest] : exps) + insert_or_merge(res, label, dest); + } + + return res; + } + default: std::cerr << "unimplemented kind " << static_cast(f.kind()) From 806b7319b9dd32b89fd5bb279c85492c26b259c1 Mon Sep 17 00:00:00 2001 From: Antoine Martin Date: Wed, 21 Dec 2022 11:05:16 +0100 Subject: [PATCH 320/337] expansions: multiple implementations --- spot/tl/expansions.cc | 214 ++++++++++++++++++++++++++++++++++-------- spot/tl/expansions.hh | 12 +++ 2 files changed, 188 insertions(+), 38 deletions(-) diff --git a/spot/tl/expansions.cc b/spot/tl/expansions.cc index b3f6eed9b..8a7e3d8ad 100644 --- a/spot/tl/expansions.cc +++ b/spot/tl/expansions.cc @@ -28,10 +28,47 @@ namespace spot { namespace { - static void - insert_or_merge(expansion_t& exp, bdd letter, formula suffix) + class expansion_basic final : expansion_builder { - auto res = exp.insert({letter, suffix}); + public: + using exp_map = expansion_builder::exp_map; + + expansion_basic() + {} + + expansion_basic(exp_map&& m) + : bdd2formula_(m) + , formula2bdd_() + {} + + void insert(bdd letter, formula suffix) final; + + void finalize() final + {} + + exp_map& result() final + { + return bdd2formula_; + } + + bool empty() final + { + return bdd2formula_.empty(); + } + + void clear() final + { + bdd2formula_.clear(); + } + + private: + exp_map bdd2formula_; + std::map formula2bdd_; + }; + + void expansion_basic::insert(bdd letter, formula suffix) + { + auto res = bdd2formula_.insert({letter, suffix}); if (!res.second) { auto it = res.first; @@ -39,6 +76,93 @@ namespace spot } } + class expansion_merge_formulas final : expansion_builder + { + public: + using exp_map = expansion_builder::exp_map; + + expansion_merge_formulas() + {} + + expansion_merge_formulas(exp_map&& m) + : res_() + , terms_(m.begin(), m.end()) + {} + + void insert(bdd letter, formula suffix) final; + + void finalize() final; + + exp_map& result() final + { + return res_; + } + + bool empty() final + { + return terms_.empty(); + } + + void clear() final + { + terms_.clear(); + res_.clear(); + } + + private: + std::vector> terms_; + exp_map res_; + }; + + void expansion_merge_formulas::insert(bdd letter, formula suffix) + { + terms_.push_back({letter, suffix}); + } + + void expansion_merge_formulas::finalize() + { + res_.clear(); + + // Given such terms: + // + // - a . ϕ1 + // - a . ϕ2 + // - b . ϕ1 + // + // Merge them by suffix: + // + // - (a ∨ b) . ϕ1 + // - a . ϕ2 + std::map suffix2letter; + for (const auto& [letter, suffix]: terms_) + { + auto res = suffix2letter.insert({suffix, letter}); + if (!res.second) + { + auto it = res.first; + it->second |= letter; + } + } + + // Given such terms: + // + // - a . ϕ1 + // - a . ϕ2 + // + // Merge them by letter: + // + // - a . (ϕ1 ∨ ϕ2) + for (const auto& [suffix, letter]: suffix2letter) + { + auto res = res_.insert({letter, suffix}); + if (!res.second) + { + auto it = res.first; + it->second = formula::OrRat({it->second, suffix}); + } + } + } + // FIXME: could probably just return a map directly static std::vector formula_aps(formula f) @@ -58,6 +182,7 @@ namespace spot return std::vector(res.begin(), res.end()); } + formula rewrite_and_nlm(formula f) { @@ -130,6 +255,8 @@ namespace spot expansion_t expansion(formula f, const bdd_dict_ptr& d, void *owner) { + using expansion_type = expansion_merge_formulas; + if (f.is_boolean()) { auto f_bdd = formula_to_bdd(f, d, owner); @@ -155,10 +282,10 @@ namespace spot { auto exps = expansion(f[0], d, owner); - expansion_t res; + expansion_type res; for (const auto& [bdd_l, form] : exps) { - res.insert({bdd_l, formula::Concat({form, f.all_but(0)})}); + res.insert(bdd_l, formula::Concat({form, f.all_but(0)})); } if (f[0].accepts_eword()) @@ -166,10 +293,12 @@ namespace spot auto exps_rest = expansion(f.all_but(0), d, owner); for (const auto& [bdd_l, form] : exps_rest) { - insert_or_merge(res, bdd_l, form); + res.insert(bdd_l, form); } } - return res; + + res.finalize(); + return res.result(); } case op::FStar: @@ -187,10 +316,10 @@ namespace spot auto E_i_j_minus = formula::FStar(E, min, max); auto exp = expansion(E, d, owner); - expansion_t res; + expansion_type res; for (const auto& [li, ei] : exp) { - insert_or_merge(res, li, formula::Fusion({ei, E_i_j_minus})); + res.insert(li, formula::Fusion({ei, E_i_j_minus})); if (ei.accepts_eword() && f.min() != 0) { @@ -198,14 +327,15 @@ namespace spot { // FIXME: build bdd once if ((li & ki) != bddfalse) - insert_or_merge(res, li & ki, fi); + res.insert(li & ki, fi); } } } if (f.min() == 0) - insert_or_merge(res, bddtrue, formula::eword()); + res.insert(bddtrue, formula::eword()); - return res; + res.finalize(); + return res.result(); } case op::Star: @@ -217,13 +347,14 @@ namespace spot auto exps = expansion(f[0], d, owner); - expansion_t res; + expansion_type res; for (const auto& [bdd_l, form] : exps) { - res.insert({bdd_l, formula::Concat({form, formula::Star(f[0], min, max)})}); + res.insert(bdd_l, formula::Concat({form, formula::Star(f[0], min, max)})); } - return res; + res.finalize(); + return res.result(); } case op::AndNLM: @@ -236,16 +367,17 @@ namespace spot { auto exps = expansion(f[0], d, owner); - expansion_t ndet_res; + expansion_type ndet_res; for (const auto& [bdd_l, form] : exps) { - ndet_res.insert({bdd_l, form}); + ndet_res.insert(bdd_l, form); } bdd or_labels = bddfalse; bdd support = bddtrue; bool is_det = true; - for (const auto& [l, _] : ndet_res) + ndet_res.finalize(); + for (const auto& [l, _] : ndet_res.result()) { support &= bdd_support(l); if (is_det) @@ -257,32 +389,33 @@ namespace spot { // we don't need to determinize the expansion, it's already // deterministic - for (auto& [_, dest] : ndet_res) + for (auto& [_, dest] : ndet_res.result()) dest = formula::first_match(dest); - return ndet_res; + return ndet_res.result(); } - expansion_t res; + expansion_type res; // TODO: extraire en fonction indépendante + lambda choix wrapper std::vector dests; for (bdd l: minterms_of(or_labels, support)) { - for (const auto& [ndet_label, ndet_dest] : ndet_res) + for (const auto& [ndet_label, ndet_dest] : ndet_res.result()) { if (bdd_implies(l, ndet_label)) dests.push_back(ndet_dest); } formula or_dests = formula::OrRat(dests); - res.insert({l, formula::first_match(or_dests)}); + res.insert(l, formula::first_match(or_dests)); dests.clear(); } - return res; + res.finalize(); + return res.result(); } case op::Fusion: { - expansion_t res; + expansion_type res; formula E = f[0]; formula F = f.all_but(0); @@ -296,17 +429,18 @@ namespace spot { for (const auto& [kj, fj] : Fj) if ((li & kj) != bddfalse) - insert_or_merge(res, li & kj, fj); + res.insert(li & kj, fj); } - insert_or_merge(res, li, formula::Fusion({ei, F})); + res.insert(li, formula::Fusion({ei, F})); } - return res; + res.finalize(); + return res.result(); } case op::AndRat: { - expansion_t res; + expansion_type res; for (const auto& sub_f : f) { auto exps = expansion(sub_f, d, owner); @@ -322,29 +456,32 @@ namespace spot if (res.empty()) { - res = std::move(exps); + res = expansion_type(std::move(exps)); + res.finalize(); continue; } - expansion_t new_res; + expansion_type new_res; for (const auto& [l_key, l_val] : exps) { - for (const auto& [r_key, r_val] : res) + for (const auto& [r_key, r_val] : res.result()) { if ((l_key & r_key) != bddfalse) - insert_or_merge(new_res, l_key & r_key, formula::multop(f.kind(), {l_val, r_val})); + new_res.insert(l_key & r_key, formula::multop(f.kind(), {l_val, r_val})); } } res = std::move(new_res); + res.finalize(); } - return res; + res.finalize(); + return res.result(); } case op::OrRat: { - expansion_t res; + expansion_type res; for (const auto& sub_f : f) { auto exps = expansion(sub_f, d, owner); @@ -353,15 +490,16 @@ namespace spot if (res.empty()) { - res = std::move(exps); + res = expansion_type(std::move(exps)); continue; } for (const auto& [label, dest] : exps) - insert_or_merge(res, label, dest); + res.insert(label, dest); } - return res; + res.finalize(); + return res.result(); } default: diff --git a/spot/tl/expansions.hh b/spot/tl/expansions.hh index af80d7e8b..8a5e3cb07 100644 --- a/spot/tl/expansions.hh +++ b/spot/tl/expansions.hh @@ -32,6 +32,18 @@ namespace spot { using expansion_t = std::map; + class expansion_builder + { + public: + using exp_map = std::map; + + virtual void insert(bdd letter, formula suffix) = 0; + virtual void finalize() = 0; + virtual exp_map& result() = 0; + virtual bool empty() = 0; + virtual void clear() = 0; + }; + SPOT_API expansion_t expansion(formula f, const bdd_dict_ptr& d, void *owner); From f4b2637c045f722a3d018760663bfe27c9d908b5 Mon Sep 17 00:00:00 2001 From: Antoine Martin Date: Fri, 20 Jan 2023 14:28:35 +0100 Subject: [PATCH 321/337] expansions: add BDD method --- spot/tl/expansions.cc | 260 ++++++++++++++++++++++++++++++++++++------ spot/tl/expansions.hh | 12 +- 2 files changed, 233 insertions(+), 39 deletions(-) diff --git a/spot/tl/expansions.cc b/spot/tl/expansions.cc index 8a7e3d8ad..593bd0c4d 100644 --- a/spot/tl/expansions.cc +++ b/spot/tl/expansions.cc @@ -18,6 +18,7 @@ // along with this program. If not, see . #include "config.h" +#include #include #include #include @@ -33,10 +34,10 @@ namespace spot public: using exp_map = expansion_builder::exp_map; - expansion_basic() + expansion_basic(bdd_dict_ptr d) {} - expansion_basic(exp_map&& m) + expansion_basic(exp_map&& m, bdd_dict_ptr d) : bdd2formula_(m) , formula2bdd_() {} @@ -81,10 +82,10 @@ namespace spot public: using exp_map = expansion_builder::exp_map; - expansion_merge_formulas() + expansion_merge_formulas(bdd_dict_ptr d) {} - expansion_merge_formulas(exp_map&& m) + expansion_merge_formulas(exp_map&& m, bdd_dict_ptr d) : res_() , terms_(m.begin(), m.end()) {} @@ -163,6 +164,182 @@ namespace spot } } + class expansion_bdd final : expansion_builder + { + public: + using exp_map = expansion_builder::exp_map; + + expansion_bdd(bdd_dict_ptr d) + : anon_set_(bddtrue) + , d_(d) + {} + + expansion_bdd(exp_map&& m, bdd_dict_ptr d) + : anon_set_(bddtrue) + , d_(d) + { + for (const auto& [letter, suffix] : m) + { + insert(letter, suffix); + } + } + + expansion_bdd(const expansion_bdd&) = delete; + + expansion_bdd& + operator=(const expansion_bdd& other) = delete; + + expansion_bdd& + operator=(const expansion_bdd&& other) + { + d_->unregister_all_my_variables(this); + + anon_set_ = std::move(other.anon_set_); + exp_ = std::move(other.exp_); + res_ = std::move(other.res_); + formula2bdd_ = std::move(other.formula2bdd_); + bdd2formula_ = std::move(other.bdd2formula_); + + d_ = other.d_; + d_->register_all_variables_of(&other, this); + + return *this; + } + + ~expansion_bdd() + { + d_->unregister_all_my_variables(this); + } + + void insert(bdd letter, formula suffix) final; + + void finalize() final; + + exp_map& result() final + { + return res_; + } + + bool empty() final + { + return formula2bdd_.empty(); + } + + void clear() final + { + formula2bdd_.clear(); + bdd2formula_.clear(); + exp_ = bddfalse; + anon_set_ = bddtrue; + res_.clear(); + } + + private: + bdd exp_; + bdd anon_set_; + std::map formula2bdd_; + std::map bdd2formula_; + exp_map res_; + bdd_dict_ptr d_; + + formula var_to_formula(int var); + formula conj_bdd_to_sere(bdd b); + }; + + formula + expansion_bdd::var_to_formula(int var) + { + formula f = bdd2formula_[var]; + assert(f); + return f; + } + + formula + expansion_bdd::conj_bdd_to_sere(bdd b) + { + if (b == bddtrue) + return formula::tt(); + if (b == bddfalse) + return formula::ff(); + + // Unroll the first loop of the next do/while loop so that we + // do not have to create v when b is not a conjunction. + formula res = var_to_formula(bdd_var(b)); + bdd high = bdd_high(b); + if (high == bddfalse) + { + res = formula::Not(res); + b = bdd_low(b); + } + else + { + assert(bdd_low(b) == bddfalse); + b = high; + } + if (b == bddtrue) + return res; + std::vector v{std::move(res)}; + do + { + res = var_to_formula(bdd_var(b)); + high = bdd_high(b); + if (high == bddfalse) + { + res = formula::Not(res); + b = bdd_low(b); + } + else + { + assert(bdd_low(b) == bddfalse); + b = high; + } + assert(b != bddfalse); + v.emplace_back(std::move(res)); + } + while (b != bddtrue); + return formula::multop(op::AndRat, std::move(v)); + } + + void expansion_bdd::insert(bdd letter, formula suffix) + { + + int anon_var_num; + auto it = formula2bdd_.find(suffix); + if (it != formula2bdd_.end()) + { + anon_var_num = it->second; + } + else + { + anon_var_num = d_->register_anonymous_variables(1, this); + formula2bdd_.insert({suffix, anon_var_num}); + bdd2formula_.insert({anon_var_num, suffix}); + } + + bdd var = bdd_ithvar(anon_var_num); + anon_set_ &= var; + exp_ |= letter & var; + } + + void expansion_bdd::finalize() + { + minato_isop isop(exp_); + bdd cube; + while ((cube = isop.next()) != bddfalse) + { + bdd letter = bdd_exist(cube, anon_set_); + bdd suffix = bdd_existcomp(cube, anon_set_); + formula dest = conj_bdd_to_sere(suffix); + + auto it = res_.insert({letter, dest}); + if (!it.second) + { + auto it2 = it.first; + it2->second = formula::OrRat({it2->second, dest}); + } + } + } + // FIXME: could probably just return a map directly static std::vector formula_aps(formula f) @@ -252,11 +429,11 @@ namespace spot return formula::OrRat(res); } - expansion_t - expansion(formula f, const bdd_dict_ptr& d, void *owner) - { - using expansion_type = expansion_merge_formulas; + template + expansion_t + expansion_impl(formula f, const bdd_dict_ptr& d, void *owner, expansion_builder::expand_opt opts) + { if (f.is_boolean()) { auto f_bdd = formula_to_bdd(f, d, owner); @@ -280,9 +457,9 @@ namespace spot case op::Concat: { - auto exps = expansion(f[0], d, owner); + auto exps = expansion(f[0], d, owner, opts); - expansion_type res; + ExpansionBuilder res(d); for (const auto& [bdd_l, form] : exps) { res.insert(bdd_l, formula::Concat({form, f.all_but(0)})); @@ -290,7 +467,7 @@ namespace spot if (f[0].accepts_eword()) { - auto exps_rest = expansion(f.all_but(0), d, owner); + auto exps_rest = expansion(f.all_but(0), d, owner, opts); for (const auto& [bdd_l, form] : exps_rest) { res.insert(bdd_l, form); @@ -315,15 +492,15 @@ namespace spot auto E_i_j_minus = formula::FStar(E, min, max); - auto exp = expansion(E, d, owner); - expansion_type res; + auto exp = expansion(E, d, owner, opts); + ExpansionBuilder res(d); for (const auto& [li, ei] : exp) { res.insert(li, formula::Fusion({ei, E_i_j_minus})); if (ei.accepts_eword() && f.min() != 0) { - for (const auto& [ki, fi] : expansion(E_i_j_minus, d, owner)) + for (const auto& [ki, fi] : expansion(E_i_j_minus, d, owner, opts)) { // FIXME: build bdd once if ((li & ki) != bddfalse) @@ -345,9 +522,9 @@ namespace spot ? formula::unbounded() : (f.max() - 1); - auto exps = expansion(f[0], d, owner); + auto exps = expansion(f[0], d, owner, opts); - expansion_type res; + ExpansionBuilder res(d); for (const auto& [bdd_l, form] : exps) { res.insert(bdd_l, formula::Concat({form, formula::Star(f[0], min, max)})); @@ -360,14 +537,14 @@ namespace spot case op::AndNLM: { formula rewrite = rewrite_and_nlm(f); - return expansion(rewrite, d, owner); + return expansion(rewrite, d, owner, opts); } case op::first_match: { - auto exps = expansion(f[0], d, owner); + auto exps = expansion(f[0], d, owner, opts); - expansion_type ndet_res; + ExpansionBuilder ndet_res(d); for (const auto& [bdd_l, form] : exps) { ndet_res.insert(bdd_l, form); @@ -394,7 +571,7 @@ namespace spot return ndet_res.result(); } - expansion_type res; + ExpansionBuilder res(d); // TODO: extraire en fonction indépendante + lambda choix wrapper std::vector dests; for (bdd l: minterms_of(or_labels, support)) @@ -415,13 +592,13 @@ namespace spot case op::Fusion: { - expansion_type res; + ExpansionBuilder res(d); formula E = f[0]; formula F = f.all_but(0); - expansion_t Ei = expansion(E, d, owner); + expansion_t Ei = expansion(E, d, owner, opts); // TODO: std::option - expansion_t Fj = expansion(F, d, owner); + expansion_t Fj = expansion(F, d, owner, opts); for (const auto& [li, ei] : Ei) { @@ -440,10 +617,10 @@ namespace spot case op::AndRat: { - expansion_type res; + ExpansionBuilder res(d); for (const auto& sub_f : f) { - auto exps = expansion(sub_f, d, owner); + auto exps = expansion(sub_f, d, owner, opts); if (exps.empty()) { @@ -456,12 +633,12 @@ namespace spot if (res.empty()) { - res = expansion_type(std::move(exps)); + res = ExpansionBuilder(std::move(exps), d); res.finalize(); continue; } - expansion_type new_res; + ExpansionBuilder new_res(d); for (const auto& [l_key, l_val] : exps) { for (const auto& [r_key, r_val] : res.result()) @@ -481,16 +658,16 @@ namespace spot case op::OrRat: { - expansion_type res; + ExpansionBuilder res(d); for (const auto& sub_f : f) { - auto exps = expansion(sub_f, d, owner); + auto exps = expansion(sub_f, d, owner, opts); if (exps.empty()) continue; if (res.empty()) { - res = expansion_type(std::move(exps)); + res = ExpansionBuilder(std::move(exps), d); continue; } @@ -509,18 +686,29 @@ namespace spot SPOT_UNIMPLEMENTED(); } - return {}; - } + return {}; + } + + expansion_t + expansion(formula f, const bdd_dict_ptr& d, void *owner, expansion_builder::expand_opt opts) + { + if (opts & expansion_builder::Basic) + return expansion_impl(f, d, owner, opts); + else if (opts & expansion_builder::MergeSuffix) + return expansion_impl(f, d, owner, opts); + else // expansion_builder::Bdd + return expansion_impl(f, d, owner, opts); + } twa_graph_ptr - expand_automaton(formula f, bdd_dict_ptr d) + expand_automaton(formula f, bdd_dict_ptr d, expansion_builder::expand_opt opts) { - auto finite = expand_finite_automaton(f, d); + auto finite = expand_finite_automaton(f, d, opts); return from_finite(finite); } twa_graph_ptr - expand_finite_automaton(formula f, bdd_dict_ptr d) + expand_finite_automaton(formula f, bdd_dict_ptr d, expansion_builder::expand_opt opts) { auto aut = make_twa_graph(d); @@ -575,7 +763,7 @@ namespace spot ? acc_mark : acc_cond::mark_t(); - auto exp = expansion(curr_f, d, aut.get()); + auto exp = expansion(curr_f, d, aut.get(), opts); for (const auto& [letter, suffix] : exp) { diff --git a/spot/tl/expansions.hh b/spot/tl/expansions.hh index 8a5e3cb07..eb6d6e60f 100644 --- a/spot/tl/expansions.hh +++ b/spot/tl/expansions.hh @@ -42,17 +42,23 @@ namespace spot virtual exp_map& result() = 0; virtual bool empty() = 0; virtual void clear() = 0; + enum expand_opt { + Deterministic = 1, + Basic = 2, + MergeSuffix = 4, + Bdd = 8, + }; }; SPOT_API expansion_t - expansion(formula f, const bdd_dict_ptr& d, void *owner); + expansion(formula f, const bdd_dict_ptr& d, void *owner, expansion_builder::expand_opt opts); SPOT_API formula expansion_to_formula(expansion_t e, bdd_dict_ptr& d); SPOT_API twa_graph_ptr - expand_automaton(formula f, bdd_dict_ptr d); + expand_automaton(formula f, bdd_dict_ptr d, expansion_builder::expand_opt opts); SPOT_API twa_graph_ptr - expand_finite_automaton(formula f, bdd_dict_ptr d); + expand_finite_automaton(formula f, bdd_dict_ptr d, expansion_builder::expand_opt opts); } From b62945b5de4856b2f1da16b6dd8797221febaa09 Mon Sep 17 00:00:00 2001 From: Antoine Martin Date: Wed, 1 Feb 2023 17:31:22 +0100 Subject: [PATCH 322/337] expansions: fix bdd method --- spot/tl/expansions.cc | 231 +++++++++++++++++++++++++++++------------- spot/tl/expansions.hh | 19 ++-- 2 files changed, 168 insertions(+), 82 deletions(-) diff --git a/spot/tl/expansions.cc b/spot/tl/expansions.cc index 593bd0c4d..1a966936a 100644 --- a/spot/tl/expansions.cc +++ b/spot/tl/expansions.cc @@ -29,6 +29,18 @@ namespace spot { namespace { + class expansion_builder + { + public: + using exp_map = std::map; + + virtual void insert(bdd letter, formula suffix) = 0; + virtual void finalize(bool deterministic, std::function wrap = formula_identity) = 0; + virtual exp_map& result() = 0; + virtual bool empty() = 0; + virtual void clear() = 0; + }; + class expansion_basic final : expansion_builder { public: @@ -44,8 +56,7 @@ namespace spot void insert(bdd letter, formula suffix) final; - void finalize() final - {} + void finalize(bool deterministic, std::function wrap = formula_identity) final; exp_map& result() final { @@ -77,6 +88,48 @@ namespace spot } } + void expansion_basic::finalize(bool deterministic, std::function wrap) + { + if (!deterministic) + return; + + bdd or_labels = bddfalse; + bdd support = bddtrue; + bool is_det = true; + for (const auto& [l, _] : bdd2formula_) + { + support &= bdd_support(l); + if (is_det) + is_det = !bdd_have_common_assignment(l, or_labels); + or_labels |= l; + } + + if (is_det) + { + // we don't need to determinize the expansion, it's already + // deterministic + for (auto& [_, dest] : bdd2formula_) + dest = wrap(dest); + return; + } + + exp_map res; + std::vector dests; + for (bdd l: minterms_of(or_labels, support)) + { + for (const auto& [ndet_label, ndet_dest] : bdd2formula_) + { + if (bdd_implies(l, ndet_label)) + dests.push_back(ndet_dest); + } + formula or_dests = formula::OrRat(dests); + res.insert({l, wrap(or_dests)}); + dests.clear(); + } + + bdd2formula_ = std::move(res); + } + class expansion_merge_formulas final : expansion_builder { public: @@ -92,7 +145,7 @@ namespace spot void insert(bdd letter, formula suffix) final; - void finalize() final; + void finalize(bool deterministic, std::function wrap = formula_identity) final; exp_map& result() final { @@ -120,7 +173,7 @@ namespace spot terms_.push_back({letter, suffix}); } - void expansion_merge_formulas::finalize() + void expansion_merge_formulas::finalize(bool deterministic, std::function wrap) { res_.clear(); @@ -162,6 +215,45 @@ namespace spot it->second = formula::OrRat({it->second, suffix}); } } + + if (!deterministic) + return; + + bdd or_labels = bddfalse; + bdd support = bddtrue; + bool is_det = true; + for (const auto& [l, _] : res_) + { + support &= bdd_support(l); + if (is_det) + is_det = !bdd_have_common_assignment(l, or_labels); + or_labels |= l; + } + + if (is_det) + { + // we don't need to determinize the expansion, it's already + // deterministic + for (auto& [_, dest] : res_) + dest = wrap(dest); + return; + } + + exp_map res; + std::vector dests; + for (bdd l: minterms_of(or_labels, support)) + { + for (const auto& [ndet_label, ndet_dest] : res_) + { + if (bdd_implies(l, ndet_label)) + dests.push_back(ndet_dest); + } + formula or_dests = formula::OrRat(dests); + res.insert({l, wrap(or_dests)}); + dests.clear(); + } + + res_ = std::move(res); } class expansion_bdd final : expansion_builder @@ -213,7 +305,7 @@ namespace spot void insert(bdd letter, formula suffix) final; - void finalize() final; + void finalize(bool deterministic, std::function wrap = formula_identity) final; exp_map& result() final { @@ -244,6 +336,7 @@ namespace spot formula var_to_formula(int var); formula conj_bdd_to_sere(bdd b); + formula bdd_to_sere(bdd b); }; formula @@ -320,22 +413,52 @@ namespace spot anon_set_ &= var; exp_ |= letter & var; } + formula + expansion_bdd::bdd_to_sere(bdd f) + { + if (f == bddfalse) + return formula::ff(); - void expansion_bdd::finalize() + std::vector v; + minato_isop isop(f); + bdd cube; + while ((cube = isop.next()) != bddfalse) + v.emplace_back(conj_bdd_to_sere(cube)); + return formula::OrRat(std::move(v)); + } + + void expansion_bdd::finalize(bool deterministic, std::function wrap) { - minato_isop isop(exp_); - bdd cube; - while ((cube = isop.next()) != bddfalse) + if (deterministic) { - bdd letter = bdd_exist(cube, anon_set_); - bdd suffix = bdd_existcomp(cube, anon_set_); - formula dest = conj_bdd_to_sere(suffix); - - auto it = res_.insert({letter, dest}); - if (!it.second) + bdd prop_set = bdd_exist(bdd_support(exp_), anon_set_); + bdd or_labels = bdd_exist(exp_, anon_set_); + for (bdd letter: minterms_of(exp_, prop_set)) { - auto it2 = it.first; - it2->second = formula::OrRat({it2->second, dest}); + bdd dest_bdd = bdd_restrict(exp_, letter); + formula dest = wrap(bdd_to_sere(dest_bdd)); + + auto it = res_.insert({letter, dest}); + assert(it.second); + (void) it; + } + } + else + { + minato_isop isop(exp_); + bdd cube; + while ((cube = isop.next()) != bddfalse) + { + bdd letter = bdd_exist(cube, anon_set_); + bdd suffix = bdd_existcomp(cube, anon_set_); + formula dest = conj_bdd_to_sere(suffix); + + auto it = res_.insert({letter, dest}); + if (!it.second) + { + auto it2 = it.first; + it2->second = formula::OrRat({it2->second, dest}); + } } } } @@ -432,7 +555,7 @@ namespace spot template expansion_t - expansion_impl(formula f, const bdd_dict_ptr& d, void *owner, expansion_builder::expand_opt opts) + expansion_impl(formula f, const bdd_dict_ptr& d, void *owner, exp_opts::expand_opt opts) { if (f.is_boolean()) { @@ -474,7 +597,7 @@ namespace spot } } - res.finalize(); + res.finalize(opts & exp_opts::Deterministic); return res.result(); } @@ -511,7 +634,7 @@ namespace spot if (f.min() == 0) res.insert(bddtrue, formula::eword()); - res.finalize(); + res.finalize(opts & exp_opts::Deterministic); return res.result(); } @@ -530,7 +653,7 @@ namespace spot res.insert(bdd_l, formula::Concat({form, formula::Star(f[0], min, max)})); } - res.finalize(); + res.finalize(opts & exp_opts::Deterministic); return res.result(); } @@ -544,49 +667,15 @@ namespace spot { auto exps = expansion(f[0], d, owner, opts); - ExpansionBuilder ndet_res(d); + ExpansionBuilder res(d); for (const auto& [bdd_l, form] : exps) { - ndet_res.insert(bdd_l, form); + res.insert(bdd_l, form); } - bdd or_labels = bddfalse; - bdd support = bddtrue; - bool is_det = true; - ndet_res.finalize(); - for (const auto& [l, _] : ndet_res.result()) - { - support &= bdd_support(l); - if (is_det) - is_det = !bdd_have_common_assignment(l, or_labels); - or_labels |= l; - } - - if (is_det) - { - // we don't need to determinize the expansion, it's already - // deterministic - for (auto& [_, dest] : ndet_res.result()) - dest = formula::first_match(dest); - return ndet_res.result(); - } - - ExpansionBuilder res(d); - // TODO: extraire en fonction indépendante + lambda choix wrapper - std::vector dests; - for (bdd l: minterms_of(or_labels, support)) - { - for (const auto& [ndet_label, ndet_dest] : ndet_res.result()) - { - if (bdd_implies(l, ndet_label)) - dests.push_back(ndet_dest); - } - formula or_dests = formula::OrRat(dests); - res.insert(l, formula::first_match(or_dests)); - dests.clear(); - } - - res.finalize(); + res.finalize(true, [](formula f){ + return formula::first_match(f); + }); return res.result(); } @@ -611,7 +700,7 @@ namespace spot res.insert(li, formula::Fusion({ei, F})); } - res.finalize(); + res.finalize(opts & exp_opts::Deterministic); return res.result(); } @@ -634,7 +723,7 @@ namespace spot if (res.empty()) { res = ExpansionBuilder(std::move(exps), d); - res.finalize(); + res.finalize(false); continue; } @@ -649,10 +738,10 @@ namespace spot } res = std::move(new_res); - res.finalize(); + res.finalize(false); } - res.finalize(); + res.finalize(opts & exp_opts::Deterministic); return res.result(); } @@ -675,7 +764,7 @@ namespace spot res.insert(label, dest); } - res.finalize(); + res.finalize(opts & exp_opts::Deterministic); return res.result(); } @@ -690,25 +779,25 @@ namespace spot } expansion_t - expansion(formula f, const bdd_dict_ptr& d, void *owner, expansion_builder::expand_opt opts) + expansion(formula f, const bdd_dict_ptr& d, void *owner, exp_opts::expand_opt opts) { - if (opts & expansion_builder::Basic) + if (opts & exp_opts::Basic) return expansion_impl(f, d, owner, opts); - else if (opts & expansion_builder::MergeSuffix) + else if (opts & exp_opts::MergeSuffix) return expansion_impl(f, d, owner, opts); - else // expansion_builder::Bdd + else // exp_opts::Bdd return expansion_impl(f, d, owner, opts); } twa_graph_ptr - expand_automaton(formula f, bdd_dict_ptr d, expansion_builder::expand_opt opts) + expand_automaton(formula f, bdd_dict_ptr d, exp_opts::expand_opt opts) { auto finite = expand_finite_automaton(f, d, opts); return from_finite(finite); } twa_graph_ptr - expand_finite_automaton(formula f, bdd_dict_ptr d, expansion_builder::expand_opt opts) + expand_finite_automaton(formula f, bdd_dict_ptr d, exp_opts::expand_opt opts) { auto aut = make_twa_graph(d); diff --git a/spot/tl/expansions.hh b/spot/tl/expansions.hh index eb6d6e60f..4ca15b174 100644 --- a/spot/tl/expansions.hh +++ b/spot/tl/expansions.hh @@ -32,16 +32,13 @@ namespace spot { using expansion_t = std::map; - class expansion_builder + formula formula_identity(formula f) { - public: - using exp_map = std::map; + return f; + } - virtual void insert(bdd letter, formula suffix) = 0; - virtual void finalize() = 0; - virtual exp_map& result() = 0; - virtual bool empty() = 0; - virtual void clear() = 0; + struct exp_opts + { enum expand_opt { Deterministic = 1, Basic = 2, @@ -51,14 +48,14 @@ namespace spot }; SPOT_API expansion_t - expansion(formula f, const bdd_dict_ptr& d, void *owner, expansion_builder::expand_opt opts); + expansion(formula f, const bdd_dict_ptr& d, void *owner, exp_opts::expand_opt opts); SPOT_API formula expansion_to_formula(expansion_t e, bdd_dict_ptr& d); SPOT_API twa_graph_ptr - expand_automaton(formula f, bdd_dict_ptr d, expansion_builder::expand_opt opts); + expand_automaton(formula f, bdd_dict_ptr d, exp_opts::expand_opt opts); SPOT_API twa_graph_ptr - expand_finite_automaton(formula f, bdd_dict_ptr d, expansion_builder::expand_opt opts); + expand_finite_automaton(formula f, bdd_dict_ptr d, exp_opts::expand_opt opts); } From 66761b398088d19c4b43df7277b313971d6b7e08 Mon Sep 17 00:00:00 2001 From: Antoine Martin Date: Mon, 6 Feb 2023 11:04:47 +0100 Subject: [PATCH 323/337] expansions: determinize only once per state --- spot/tl/expansions.cc | 59 ++++++++++++++++++++++--------------------- spot/tl/expansions.hh | 6 +---- 2 files changed, 31 insertions(+), 34 deletions(-) diff --git a/spot/tl/expansions.cc b/spot/tl/expansions.cc index 1a966936a..40fe68415 100644 --- a/spot/tl/expansions.cc +++ b/spot/tl/expansions.cc @@ -35,7 +35,7 @@ namespace spot using exp_map = std::map; virtual void insert(bdd letter, formula suffix) = 0; - virtual void finalize(bool deterministic, std::function wrap = formula_identity) = 0; + virtual void finalize(bool deterministic) = 0; virtual exp_map& result() = 0; virtual bool empty() = 0; virtual void clear() = 0; @@ -56,7 +56,7 @@ namespace spot void insert(bdd letter, formula suffix) final; - void finalize(bool deterministic, std::function wrap = formula_identity) final; + void finalize(bool deterministic) final; exp_map& result() final { @@ -88,7 +88,7 @@ namespace spot } } - void expansion_basic::finalize(bool deterministic, std::function wrap) + void expansion_basic::finalize(bool deterministic) { if (!deterministic) return; @@ -108,8 +108,6 @@ namespace spot { // we don't need to determinize the expansion, it's already // deterministic - for (auto& [_, dest] : bdd2formula_) - dest = wrap(dest); return; } @@ -123,7 +121,7 @@ namespace spot dests.push_back(ndet_dest); } formula or_dests = formula::OrRat(dests); - res.insert({l, wrap(or_dests)}); + res.insert({l, or_dests}); dests.clear(); } @@ -145,7 +143,7 @@ namespace spot void insert(bdd letter, formula suffix) final; - void finalize(bool deterministic, std::function wrap = formula_identity) final; + void finalize(bool deterministic) final; exp_map& result() final { @@ -173,7 +171,7 @@ namespace spot terms_.push_back({letter, suffix}); } - void expansion_merge_formulas::finalize(bool deterministic, std::function wrap) + void expansion_merge_formulas::finalize(bool deterministic) { res_.clear(); @@ -234,8 +232,6 @@ namespace spot { // we don't need to determinize the expansion, it's already // deterministic - for (auto& [_, dest] : res_) - dest = wrap(dest); return; } @@ -249,7 +245,7 @@ namespace spot dests.push_back(ndet_dest); } formula or_dests = formula::OrRat(dests); - res.insert({l, wrap(or_dests)}); + res.insert({l, or_dests}); dests.clear(); } @@ -305,7 +301,7 @@ namespace spot void insert(bdd letter, formula suffix) final; - void finalize(bool deterministic, std::function wrap = formula_identity) final; + void finalize(bool deterministic) final; exp_map& result() final { @@ -427,7 +423,7 @@ namespace spot return formula::OrRat(std::move(v)); } - void expansion_bdd::finalize(bool deterministic, std::function wrap) + void expansion_bdd::finalize(bool deterministic) { if (deterministic) { @@ -436,7 +432,7 @@ namespace spot for (bdd letter: minterms_of(exp_, prop_set)) { bdd dest_bdd = bdd_restrict(exp_, letter); - formula dest = wrap(bdd_to_sere(dest_bdd)); + formula dest = bdd_to_sere(dest_bdd); auto it = res_.insert({letter, dest}); assert(it.second); @@ -567,6 +563,10 @@ namespace spot return {{f_bdd, formula::eword()}}; } + auto rec = [&d, owner](formula f){ + return expansion_impl(f, d, owner, exp_opts::None); + }; + switch (f.kind()) { @@ -580,7 +580,7 @@ namespace spot case op::Concat: { - auto exps = expansion(f[0], d, owner, opts); + auto exps = rec(f[0]); ExpansionBuilder res(d); for (const auto& [bdd_l, form] : exps) @@ -590,7 +590,7 @@ namespace spot if (f[0].accepts_eword()) { - auto exps_rest = expansion(f.all_but(0), d, owner, opts); + auto exps_rest = rec(f.all_but(0)); for (const auto& [bdd_l, form] : exps_rest) { res.insert(bdd_l, form); @@ -615,7 +615,7 @@ namespace spot auto E_i_j_minus = formula::FStar(E, min, max); - auto exp = expansion(E, d, owner, opts); + auto exp = rec(E); ExpansionBuilder res(d); for (const auto& [li, ei] : exp) { @@ -623,7 +623,7 @@ namespace spot if (ei.accepts_eword() && f.min() != 0) { - for (const auto& [ki, fi] : expansion(E_i_j_minus, d, owner, opts)) + for (const auto& [ki, fi] : rec(E_i_j_minus)) { // FIXME: build bdd once if ((li & ki) != bddfalse) @@ -645,7 +645,7 @@ namespace spot ? formula::unbounded() : (f.max() - 1); - auto exps = expansion(f[0], d, owner, opts); + auto exps = rec(f[0]); ExpansionBuilder res(d); for (const auto& [bdd_l, form] : exps) @@ -660,12 +660,12 @@ namespace spot case op::AndNLM: { formula rewrite = rewrite_and_nlm(f); - return expansion(rewrite, d, owner, opts); + return rec(rewrite); } case op::first_match: { - auto exps = expansion(f[0], d, owner, opts); + auto exps = rec(f[0]); ExpansionBuilder res(d); for (const auto& [bdd_l, form] : exps) @@ -673,10 +673,11 @@ namespace spot res.insert(bdd_l, form); } - res.finalize(true, [](formula f){ - return formula::first_match(f); - }); - return res.result(); + res.finalize(true); + auto res2 = res.result(); + for (auto& [_, dest] : res2) + dest = formula::first_match(dest); + return res2; } case op::Fusion: @@ -685,9 +686,9 @@ namespace spot formula E = f[0]; formula F = f.all_but(0); - expansion_t Ei = expansion(E, d, owner, opts); + expansion_t Ei = rec(E); // TODO: std::option - expansion_t Fj = expansion(F, d, owner, opts); + expansion_t Fj = rec(F); for (const auto& [li, ei] : Ei) { @@ -709,7 +710,7 @@ namespace spot ExpansionBuilder res(d); for (const auto& sub_f : f) { - auto exps = expansion(sub_f, d, owner, opts); + auto exps = rec(sub_f); if (exps.empty()) { @@ -750,7 +751,7 @@ namespace spot ExpansionBuilder res(d); for (const auto& sub_f : f) { - auto exps = expansion(sub_f, d, owner, opts); + auto exps = rec(sub_f); if (exps.empty()) continue; diff --git a/spot/tl/expansions.hh b/spot/tl/expansions.hh index 4ca15b174..c8046a7a4 100644 --- a/spot/tl/expansions.hh +++ b/spot/tl/expansions.hh @@ -32,14 +32,10 @@ namespace spot { using expansion_t = std::map; - formula formula_identity(formula f) - { - return f; - } - struct exp_opts { enum expand_opt { + None = 0, Deterministic = 1, Basic = 2, MergeSuffix = 4, From 22f76b7e1c437c9de1b9dd1db8ff23f5ff92ca38 Mon Sep 17 00:00:00 2001 From: Antoine Martin Date: Mon, 13 Feb 2023 14:35:47 +0100 Subject: [PATCH 324/337] expansions: multimap version --- spot/tl/expansions.cc | 335 ++++++++++++++++++++++++++++++++++++++++++ spot/tl/expansions.hh | 9 ++ 2 files changed, 344 insertions(+) diff --git a/spot/tl/expansions.cc b/spot/tl/expansions.cc index 40fe68415..20efe4eb2 100644 --- a/spot/tl/expansions.cc +++ b/spot/tl/expansions.cc @@ -548,6 +548,255 @@ namespace spot return formula::OrRat(res); } + std::multimap + expansion_simple(formula f, const bdd_dict_ptr& d, void *owner) + { + using exp_t = std::multimap; + + if (f.is_boolean()) + { + auto f_bdd = formula_to_bdd(f, d, owner); + + if (f_bdd == bddfalse) + return {}; + + return {{f_bdd, formula::eword()}}; + } + + auto rec = [&d, owner](formula f){ + return expansion_simple(f, d, owner); + }; + + + switch (f.kind()) + { + case op::ff: + case op::tt: + case op::ap: + SPOT_UNREACHABLE(); + + case op::eword: + return {{bddfalse, formula::ff()}}; + + case op::Concat: + { + auto exps = rec(f[0]); + + exp_t res; + for (const auto& [bdd_l, form] : exps) + { + res.insert({bdd_l, formula::Concat({form, f.all_but(0)})}); + } + + if (f[0].accepts_eword()) + { + auto exps_rest = rec(f.all_but(0)); + for (const auto& [bdd_l, form] : exps_rest) + { + res.insert({bdd_l, form}); + } + } + + return res; + } + + case op::FStar: + { + formula E = f[0]; + + if (f.min() == 0 && f.max() == 0) + return {{bddtrue, formula::eword()}}; + + auto min = f.min() == 0 ? 0 : (f.min() - 1); + auto max = f.max() == formula::unbounded() + ? formula::unbounded() + : (f.max() - 1); + + auto E_i_j_minus = formula::FStar(E, min, max); + + auto exp = rec(E); + exp_t res; + for (const auto& [li, ei] : exp) + { + res.insert({li, formula::Fusion({ei, E_i_j_minus})}); + + if (ei.accepts_eword() && f.min() != 0) + { + for (const auto& [ki, fi] : rec(E_i_j_minus)) + { + // FIXME: build bdd once + if ((li & ki) != bddfalse) + res.insert({li & ki, fi}); + } + } + } + if (f.min() == 0) + res.insert({bddtrue, formula::eword()}); + + return res; + } + + case op::Star: + { + auto min = f.min() == 0 ? 0 : (f.min() - 1); + auto max = f.max() == formula::unbounded() + ? formula::unbounded() + : (f.max() - 1); + + auto exps = rec(f[0]); + + exp_t res; + for (const auto& [bdd_l, form] : exps) + { + res.insert({bdd_l, formula::Concat({form, formula::Star(f[0], min, max)})}); + } + + return res; + } + + case op::AndNLM: + { + formula rewrite = rewrite_and_nlm(f); + return rec(rewrite); + } + + case op::first_match: + { + auto exps = rec(f[0]); + + exp_t res; + for (const auto& [bdd_l, form] : exps) + { + res.insert({bdd_l, form}); + } + + // determinize + bdd or_labels = bddfalse; + bdd support = bddtrue; + bool is_det = true; + for (const auto& [l, _] : res) + { + support &= bdd_support(l); + if (is_det) + is_det = !bdd_have_common_assignment(l, or_labels); + or_labels |= l; + } + + if (is_det) + return res; + + exp_t res_det; + std::vector dests; + for (bdd l: minterms_of(or_labels, support)) + { + for (const auto& [ndet_label, ndet_dest] : res) + { + if (bdd_implies(l, ndet_label)) + dests.push_back(ndet_dest); + } + formula or_dests = formula::OrRat(dests); + res_det.insert({l, or_dests}); + dests.clear(); + } + + for (auto& [_, dest] : res_det) + dest = formula::first_match(dest); + return res_det; + } + + case op::Fusion: + { + exp_t res; + formula E = f[0]; + formula F = f.all_but(0); + + exp_t Ei = rec(E); + // TODO: std::option + exp_t Fj = rec(F); + + for (const auto& [li, ei] : Ei) + { + if (ei.accepts_eword()) + { + for (const auto& [kj, fj] : Fj) + if ((li & kj) != bddfalse) + res.insert({li & kj, fj}); + } + res.insert({li, formula::Fusion({ei, F})}); + } + + return res; + } + + case op::AndRat: + { + exp_t res; + for (const auto& sub_f : f) + { + auto exps = rec(sub_f); + + if (exps.empty()) + { + // op::AndRat: one of the expansions was empty (the only + // edge was `false`), so the AndRat is empty as + // well + res.clear(); + break; + } + + if (res.empty()) + { + res = std::move(exps); + continue; + } + + exp_t new_res; + for (const auto& [l_key, l_val] : exps) + { + for (const auto& [r_key, r_val] : res) + { + if ((l_key & r_key) != bddfalse) + new_res.insert({l_key & r_key, formula::multop(f.kind(), {l_val, r_val})}); + } + } + + res = std::move(new_res); + } + + return res; + } + + case op::OrRat: + { + exp_t res; + for (const auto& sub_f : f) + { + auto exps = rec(sub_f); + if (exps.empty()) + continue; + + if (res.empty()) + { + res = std::move(exps); + continue; + } + + for (const auto& [label, dest] : exps) + res.insert({label, dest}); + } + + return res; + } + + default: + std::cerr << "unimplemented kind " + << static_cast(f.kind()) + << std::endl; + SPOT_UNIMPLEMENTED(); + } + + return {}; + } template expansion_t @@ -875,4 +1124,90 @@ namespace spot aut->merge_edges(); return aut; } + + twa_graph_ptr + expand_simple_automaton(formula f, bdd_dict_ptr d) + { + auto finite = expand_simple_finite_automaton(f, d); + return from_finite(finite); + } + + twa_graph_ptr + expand_simple_finite_automaton(formula f, bdd_dict_ptr d) + { + auto aut = make_twa_graph(d); + + aut->prop_state_acc(true); + const auto acc_mark = aut->set_buchi(); + + auto formula2state = robin_hood::unordered_map(); + + unsigned init_state = aut->new_state(); + aut->set_init_state(init_state); + formula2state.insert({ f, init_state }); + + auto f_aps = formula_aps(f); + for (auto& ap : f_aps) + aut->register_ap(ap); + + auto todo = std::vector>(); + todo.push_back({f, init_state}); + + auto state_names = new std::vector(); + std::ostringstream ss; + ss << f; + state_names->push_back(ss.str()); + + auto find_dst = [&](formula suffix) -> unsigned + { + unsigned dst; + auto it = formula2state.find(suffix); + if (it != formula2state.end()) + { + dst = it->second; + } + else + { + dst = aut->new_state(); + todo.push_back({suffix, dst}); + formula2state.insert({suffix, dst}); + std::ostringstream ss; + ss << suffix; + state_names->push_back(ss.str()); + } + + return dst; + }; + + while (!todo.empty()) + { + auto [curr_f, curr_state] = todo[todo.size() - 1]; + todo.pop_back(); + + auto curr_acc_mark= curr_f.accepts_eword() + ? acc_mark + : acc_cond::mark_t(); + + auto exp = expansion_simple(curr_f, d, aut.get()); + + for (const auto& [letter, suffix] : exp) + { + if (suffix.is(op::ff)) + continue; + + auto dst = find_dst(suffix); + aut->new_edge(curr_state, dst, letter, curr_acc_mark); + } + + // if state has no transitions and should be accepting, create + // artificial transition + if (aut->get_graph().state_storage(curr_state).succ == 0 + && curr_f.accepts_eword()) + aut->new_edge(curr_state, curr_state, bddfalse, acc_mark); + } + + aut->set_named_prop("state-names", state_names); + aut->merge_edges(); + return aut; + } } diff --git a/spot/tl/expansions.hh b/spot/tl/expansions.hh index c8046a7a4..ff6977f9b 100644 --- a/spot/tl/expansions.hh +++ b/spot/tl/expansions.hh @@ -43,6 +43,15 @@ namespace spot }; }; + SPOT_API twa_graph_ptr + expand_simple_automaton(formula f, bdd_dict_ptr d); + + SPOT_API twa_graph_ptr + expand_simple_finite_automaton(formula f, bdd_dict_ptr d); + + SPOT_API std::multimap + expansion_simple(formula f, const bdd_dict_ptr& d, void *owner); + SPOT_API expansion_t expansion(formula f, const bdd_dict_ptr& d, void *owner, exp_opts::expand_opt opts); From 0168efea60334b5f3336d1f1fe3e7490e4429e15 Mon Sep 17 00:00:00 2001 From: Antoine Martin Date: Mon, 6 Mar 2023 18:37:12 +0100 Subject: [PATCH 325/337] expansions: latest implementation --- spot/tl/expansions.cc | 560 +++++++++++++++++++++++++++++++++++++++++- spot/tl/expansions.hh | 20 ++ 2 files changed, 576 insertions(+), 4 deletions(-) diff --git a/spot/tl/expansions.cc b/spot/tl/expansions.cc index 20efe4eb2..1d225b603 100644 --- a/spot/tl/expansions.cc +++ b/spot/tl/expansions.cc @@ -548,6 +548,471 @@ namespace spot return formula::OrRat(res); } + class bdd_finalizer + { + public: + bdd_finalizer(std::multimap& exp, bdd_dict_ptr d) + : anon_set_(bddtrue) + , d_(d) + { + for (const auto& [prefix, suffix] : exp) + { + int anon_var_num; + auto it = formula2bdd_.find(suffix); + if (it != formula2bdd_.end()) + { + anon_var_num = it->second; + } + else + { + anon_var_num = d_->register_anonymous_variables(1, this); + formula2bdd_.insert({suffix, anon_var_num}); + bdd2formula_.insert({anon_var_num, suffix}); + } + + bdd var = bdd_ithvar(anon_var_num); + anon_set_ &= var; + exp_ |= prefix & var; + } + } + + ~bdd_finalizer() + { + d_->unregister_all_my_variables(this); + } + + std::multimap + simplify(exp_opts_new::expand_opt_new opts); + + private: + bdd exp_; + bdd anon_set_; + std::map formula2bdd_; + std::map bdd2formula_; + bdd_dict_ptr d_; + + formula var_to_formula(int var); + formula conj_bdd_to_sere(bdd b); + formula bdd_to_sere(bdd b); + }; + + formula + bdd_finalizer::var_to_formula(int var) + { + formula f = bdd2formula_[var]; + assert(f); + return f; + } + + formula + bdd_finalizer::bdd_to_sere(bdd f) + { + if (f == bddfalse) + return formula::ff(); + + std::vector v; + minato_isop isop(f); + bdd cube; + while ((cube = isop.next()) != bddfalse) + v.emplace_back(conj_bdd_to_sere(cube)); + return formula::OrRat(std::move(v)); + } + + formula + bdd_finalizer::conj_bdd_to_sere(bdd b) + { + if (b == bddtrue) + return formula::tt(); + if (b == bddfalse) + return formula::ff(); + + // Unroll the first loop of the next do/while loop so that we + // do not have to create v when b is not a conjunction. + formula res = var_to_formula(bdd_var(b)); + bdd high = bdd_high(b); + if (high == bddfalse) + { + res = formula::Not(res); + b = bdd_low(b); + } + else + { + assert(bdd_low(b) == bddfalse); + b = high; + } + if (b == bddtrue) + return res; + std::vector v{std::move(res)}; + do + { + res = var_to_formula(bdd_var(b)); + high = bdd_high(b); + if (high == bddfalse) + { + res = formula::Not(res); + b = bdd_low(b); + } + else + { + assert(bdd_low(b) == bddfalse); + b = high; + } + assert(b != bddfalse); + v.emplace_back(std::move(res)); + } + while (b != bddtrue); + return formula::multop(op::AndRat, std::move(v)); + } + + std::multimap + bdd_finalizer::simplify(exp_opts_new::expand_opt_new opts) + { + std::multimap res; + + if (opts & exp_opts_new::expand_opt_new::BddMinterm) + { + bdd prop_set = bdd_exist(bdd_support(exp_), anon_set_); + bdd or_labels = bdd_exist(exp_, anon_set_); + for (bdd letter: minterms_of(exp_, prop_set)) + { + bdd dest_bdd = bdd_restrict(exp_, letter); + formula dest = bdd_to_sere(dest_bdd); + + auto it = res.insert({letter, dest}); + assert(it.second); + (void) it; + } + } + else // BddIsop + { + minato_isop isop(exp_); + bdd cube; + while ((cube = isop.next()) != bddfalse) + { + bdd letter = bdd_exist(cube, anon_set_); + bdd suffix = bdd_existcomp(cube, anon_set_); + formula dest = conj_bdd_to_sere(suffix); + + res.insert({letter, dest}); + } + } + + return res; + } + + void + finalize_new(std::multimap& exp, exp_opts_new::expand_opt_new opts, bdd_dict_ptr d) + { + if (opts & (exp_opts_new::expand_opt_new::BddIsop + | exp_opts_new::expand_opt_new::BddMinterm)) + { + bdd_finalizer bddf(exp, d); + exp = bddf.simplify(opts); + } + + if (opts & exp_opts_new::expand_opt_new::UniqueSuffix) + { + std::map unique_map; + for (const auto& [prefix, suffix] : exp) + { + auto res = unique_map.insert({suffix, prefix}); + if (!res.second) + { + auto it = res.first; + it->second |= prefix; + } + } + + exp.clear(); + for (const auto [suffix, prefix] : unique_map) + { + exp.insert({prefix, suffix}); + } + } + + if (opts & exp_opts_new::expand_opt_new::UniquePrefix) + { + std::map unique_map; + for (const auto& [prefix, suffix] : exp) + { + auto res = unique_map.insert({prefix, suffix}); + if (!res.second) + { + auto it = res.first; + it->second = formula::OrRat({it->second, suffix}); + } + } + + exp.clear(); + for (const auto [prefix, suffix] : unique_map) + { + exp.insert({prefix, suffix}); + } + } + } + + std::multimap + expansion_new(formula f, const bdd_dict_ptr& d, void *owner, exp_opts_new::expand_opt_new opts) + { + using exp_t = std::multimap; + + if (f.is_boolean()) + { + auto f_bdd = formula_to_bdd(f, d, owner); + + if (f_bdd == bddfalse) + return {}; + + return {{f_bdd, formula::eword()}}; + } + + auto rec = [&d, owner, opts](formula f){ + return expansion_new(f, d, owner, exp_opts_new::None); + }; + + + switch (f.kind()) + { + case op::ff: + case op::tt: + case op::ap: + SPOT_UNREACHABLE(); + + case op::eword: + return {{bddfalse, formula::ff()}}; + + case op::Concat: + { + auto exps = rec(f[0]); + + exp_t res; + for (const auto& [bdd_l, form] : exps) + { + res.insert({bdd_l, formula::Concat({form, f.all_but(0)})}); + } + + if (f[0].accepts_eword()) + { + auto exps_rest = rec(f.all_but(0)); + for (const auto& [bdd_l, form] : exps_rest) + { + res.insert({bdd_l, form}); + } + } + + finalize_new(res, opts, d); + return res; + } + + case op::FStar: + { + formula E = f[0]; + + if (f.min() == 0 && f.max() == 0) + return {{bddtrue, formula::eword()}}; + + auto min = f.min() == 0 ? 0 : (f.min() - 1); + auto max = f.max() == formula::unbounded() + ? formula::unbounded() + : (f.max() - 1); + + auto E_i_j_minus = formula::FStar(E, min, max); + + auto exp = rec(E); + exp_t res; + for (const auto& [li, ei] : exp) + { + res.insert({li, formula::Fusion({ei, E_i_j_minus})}); + + if (ei.accepts_eword() && f.min() != 0) + { + for (const auto& [ki, fi] : rec(E_i_j_minus)) + { + // FIXME: build bdd once + if ((li & ki) != bddfalse) + res.insert({li & ki, fi}); + } + } + } + if (f.min() == 0) + res.insert({bddtrue, formula::eword()}); + + finalize_new(res, opts, d); + return res; + } + + case op::Star: + { + auto min = f.min() == 0 ? 0 : (f.min() - 1); + auto max = f.max() == formula::unbounded() + ? formula::unbounded() + : (f.max() - 1); + + auto exps = rec(f[0]); + + exp_t res; + for (const auto& [bdd_l, form] : exps) + { + res.insert({bdd_l, formula::Concat({form, formula::Star(f[0], min, max)})}); + } + + finalize_new(res, opts, d); + return res; + } + + case op::AndNLM: + { + formula rewrite = rewrite_and_nlm(f); + auto res = rec(rewrite); + finalize_new(res, opts, d); + return res; + } + + case op::first_match: + { + auto exps = rec(f[0]); + + exp_t res; + for (const auto& [bdd_l, form] : exps) + { + res.insert({bdd_l, form}); + } + + // determinize + bdd or_labels = bddfalse; + bdd support = bddtrue; + bool is_det = true; + for (const auto& [l, _] : res) + { + support &= bdd_support(l); + if (is_det) + is_det = !bdd_have_common_assignment(l, or_labels); + or_labels |= l; + } + + if (is_det) + { + finalize_new(res, opts, d); + return res; + } + + exp_t res_det; + std::vector dests; + for (bdd l: minterms_of(or_labels, support)) + { + for (const auto& [ndet_label, ndet_dest] : res) + { + if (bdd_implies(l, ndet_label)) + dests.push_back(ndet_dest); + } + formula or_dests = formula::OrRat(dests); + res_det.insert({l, or_dests}); + dests.clear(); + } + + for (auto& [_, dest] : res_det) + dest = formula::first_match(dest); + finalize_new(res_det, opts, d); + return res_det; + } + + case op::Fusion: + { + exp_t res; + formula E = f[0]; + formula F = f.all_but(0); + + exp_t Ei = rec(E); + // TODO: std::option + exp_t Fj = rec(F); + + for (const auto& [li, ei] : Ei) + { + if (ei.accepts_eword()) + { + for (const auto& [kj, fj] : Fj) + if ((li & kj) != bddfalse) + res.insert({li & kj, fj}); + } + res.insert({li, formula::Fusion({ei, F})}); + } + + finalize_new(res, opts, d); + return res; + } + + case op::AndRat: + { + exp_t res; + for (const auto& sub_f : f) + { + auto exps = rec(sub_f); + + if (exps.empty()) + { + // op::AndRat: one of the expansions was empty (the only + // edge was `false`), so the AndRat is empty as + // well + res.clear(); + break; + } + + if (res.empty()) + { + res = std::move(exps); + continue; + } + + exp_t new_res; + for (const auto& [l_key, l_val] : exps) + { + for (const auto& [r_key, r_val] : res) + { + if ((l_key & r_key) != bddfalse) + new_res.insert({l_key & r_key, formula::multop(f.kind(), {l_val, r_val})}); + } + } + + res = std::move(new_res); + } + + finalize_new(res, opts, d); + return res; + } + + case op::OrRat: + { + exp_t res; + for (const auto& sub_f : f) + { + auto exps = rec(sub_f); + if (exps.empty()) + continue; + + if (res.empty()) + { + res = std::move(exps); + continue; + } + + for (const auto& [label, dest] : exps) + res.insert({label, dest}); + } + + finalize_new(res, opts, d); + return res; + } + + default: + std::cerr << "unimplemented kind " + << static_cast(f.kind()) + << std::endl; + SPOT_UNIMPLEMENTED(); + } + + return {}; + } + std::multimap expansion_simple(formula f, const bdd_dict_ptr& d, void *owner) { @@ -1031,12 +1496,13 @@ namespace spot expansion_t expansion(formula f, const bdd_dict_ptr& d, void *owner, exp_opts::expand_opt opts) { - if (opts & exp_opts::Basic) - return expansion_impl(f, d, owner, opts); + + if (opts & exp_opts::Bdd) + return expansion_impl(f, d, owner, opts); else if (opts & exp_opts::MergeSuffix) return expansion_impl(f, d, owner, opts); - else // exp_opts::Bdd - return expansion_impl(f, d, owner, opts); + else // exp_opts::Basic + return expansion_impl(f, d, owner, opts); } twa_graph_ptr @@ -1210,4 +1676,90 @@ namespace spot aut->merge_edges(); return aut; } + + twa_graph_ptr + expand_new_automaton(formula f, bdd_dict_ptr d, exp_opts_new::expand_opt_new opts) + { + auto finite = expand_new_finite_automaton(f, d, opts); + return from_finite(finite); + } + + twa_graph_ptr + expand_new_finite_automaton(formula f, bdd_dict_ptr d, exp_opts_new::expand_opt_new opts) + { + auto aut = make_twa_graph(d); + + aut->prop_state_acc(true); + const auto acc_mark = aut->set_buchi(); + + auto formula2state = robin_hood::unordered_map(); + + unsigned init_state = aut->new_state(); + aut->set_init_state(init_state); + formula2state.insert({ f, init_state }); + + auto f_aps = formula_aps(f); + for (auto& ap : f_aps) + aut->register_ap(ap); + + auto todo = std::vector>(); + todo.push_back({f, init_state}); + + auto state_names = new std::vector(); + std::ostringstream ss; + ss << f; + state_names->push_back(ss.str()); + + auto find_dst = [&](formula suffix) -> unsigned + { + unsigned dst; + auto it = formula2state.find(suffix); + if (it != formula2state.end()) + { + dst = it->second; + } + else + { + dst = aut->new_state(); + todo.push_back({suffix, dst}); + formula2state.insert({suffix, dst}); + std::ostringstream ss; + ss << suffix; + state_names->push_back(ss.str()); + } + + return dst; + }; + + while (!todo.empty()) + { + auto [curr_f, curr_state] = todo[todo.size() - 1]; + todo.pop_back(); + + auto curr_acc_mark= curr_f.accepts_eword() + ? acc_mark + : acc_cond::mark_t(); + + auto exp = expansion_new(curr_f, d, aut.get(), opts); + + for (const auto& [letter, suffix] : exp) + { + if (suffix.is(op::ff)) + continue; + + auto dst = find_dst(suffix); + aut->new_edge(curr_state, dst, letter, curr_acc_mark); + } + + // if state has no transitions and should be accepting, create + // artificial transition + if (aut->get_graph().state_storage(curr_state).succ == 0 + && curr_f.accepts_eword()) + aut->new_edge(curr_state, curr_state, bddfalse, acc_mark); + } + + aut->set_named_prop("state-names", state_names); + aut->merge_edges(); + return aut; + } } diff --git a/spot/tl/expansions.hh b/spot/tl/expansions.hh index ff6977f9b..4286e8fd6 100644 --- a/spot/tl/expansions.hh +++ b/spot/tl/expansions.hh @@ -43,6 +43,26 @@ namespace spot }; }; + struct exp_opts_new + { + enum expand_opt_new { + None = 0, + UniqueSuffix = 1, + UniquePrefix = 2, + BddIsop = 4, + BddMinterm = 8, + }; + }; + + SPOT_API std::multimap + expansion_new(formula f, const bdd_dict_ptr& d, void *owner, exp_opts_new::expand_opt_new opts); + + SPOT_API twa_graph_ptr + expand_new_automaton(formula f, bdd_dict_ptr d, exp_opts_new::expand_opt_new opts); + + SPOT_API twa_graph_ptr + expand_new_finite_automaton(formula f, bdd_dict_ptr d, exp_opts_new::expand_opt_new opts); + SPOT_API twa_graph_ptr expand_simple_automaton(formula f, bdd_dict_ptr d); From d410435adc345df3b08490d8d6d0e82a1445ee97 Mon Sep 17 00:00:00 2001 From: Antoine Martin Date: Mon, 6 Mar 2023 18:37:28 +0100 Subject: [PATCH 326/337] expansions: allow toggling merge_edges off --- spot/tl/expansions.cc | 6 +++++- spot/tl/expansions.hh | 1 + 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/spot/tl/expansions.cc b/spot/tl/expansions.cc index 1d225b603..f68fb2d9d 100644 --- a/spot/tl/expansions.cc +++ b/spot/tl/expansions.cc @@ -1759,7 +1759,11 @@ namespace spot } aut->set_named_prop("state-names", state_names); - aut->merge_edges(); + + if ((opts & exp_opts_new::MergeEdges) + && !(opts & exp_opts_new::UniqueSuffix)) + aut->merge_edges(); + return aut; } } diff --git a/spot/tl/expansions.hh b/spot/tl/expansions.hh index 4286e8fd6..52e83917f 100644 --- a/spot/tl/expansions.hh +++ b/spot/tl/expansions.hh @@ -51,6 +51,7 @@ namespace spot UniquePrefix = 2, BddIsop = 4, BddMinterm = 8, + MergeEdges = 16, }; }; From 2c4f85f687a4db7d75ea940dead2617d7d21334a Mon Sep 17 00:00:00 2001 From: Antoine Martin Date: Mon, 6 Mar 2023 18:37:44 +0100 Subject: [PATCH 327/337] twaalgos: ltl2tgba_fm: allow disabling SCC trim --- spot/twaalgos/ltl2tgba_fm.cc | 16 ++++++++++++---- spot/twaalgos/ltl2tgba_fm.hh | 2 +- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/spot/twaalgos/ltl2tgba_fm.cc b/spot/twaalgos/ltl2tgba_fm.cc index 97a2eceb3..ed9ad7d06 100644 --- a/spot/twaalgos/ltl2tgba_fm.cc +++ b/spot/twaalgos/ltl2tgba_fm.cc @@ -107,7 +107,7 @@ namespace spot { typedef twa_graph::namer namer; public: - ratexp_to_dfa(translate_dict& dict); + ratexp_to_dfa(translate_dict& dict, bool disable_scc_trimming = false); std::tuple succ(formula f); ~ratexp_to_dfa(); @@ -122,6 +122,7 @@ namespace spot typedef robin_hood::unordered_node_map f2a_t; std::vector automata_; f2a_t f2a_; + bool disable_scc_trimming_; }; // Helper dictionary. We represent formulae using BDDs to @@ -907,8 +908,9 @@ namespace spot } - ratexp_to_dfa::ratexp_to_dfa(translate_dict& dict) + ratexp_to_dfa::ratexp_to_dfa(translate_dict& dict, bool disable_scc_trimming) : dict_(dict) + , disable_scc_trimming_(disable_scc_trimming) { } @@ -961,6 +963,12 @@ namespace spot } } + if (disable_scc_trimming_) + { + automata_.emplace_back(a, namer); + return labelled_aut(a, namer); + } + // The following code trims the automaton in a crude way by // eliminating SCCs that are not coaccessible. It does not // actually remove the states, it simply marks the corresponding @@ -2222,7 +2230,7 @@ namespace spot } twa_graph_ptr - sere_to_tgba(formula f, const bdd_dict_ptr& dict) + sere_to_tgba(formula f, const bdd_dict_ptr& dict, bool disable_scc_trimming) { f = negative_normal_form(f); @@ -2230,7 +2238,7 @@ namespace spot twa_graph_ptr a = make_twa_graph(dict); translate_dict d(a, s, false, false, false); - ratexp_to_dfa sere2dfa(d); + ratexp_to_dfa sere2dfa(d, disable_scc_trimming); auto [dfa, namer, state] = sere2dfa.succ(f); diff --git a/spot/twaalgos/ltl2tgba_fm.hh b/spot/twaalgos/ltl2tgba_fm.hh index aa9bc1d1d..18341fb37 100644 --- a/spot/twaalgos/ltl2tgba_fm.hh +++ b/spot/twaalgos/ltl2tgba_fm.hh @@ -90,5 +90,5 @@ namespace spot const output_aborter* aborter = nullptr); SPOT_API twa_graph_ptr - sere_to_tgba(formula f, const bdd_dict_ptr& dict); + sere_to_tgba(formula f, const bdd_dict_ptr& dict, bool disable_scc_trimming = false); } From 564e3af5ddef4d8c3ea9855691c6b49ff241dae6 Mon Sep 17 00:00:00 2001 From: Antoine Martin Date: Thu, 9 Mar 2023 10:26:50 +0100 Subject: [PATCH 328/337] expansions: fix first_match case --- spot/tl/expansions.cc | 2 ++ 1 file changed, 2 insertions(+) diff --git a/spot/tl/expansions.cc b/spot/tl/expansions.cc index f68fb2d9d..e92a74621 100644 --- a/spot/tl/expansions.cc +++ b/spot/tl/expansions.cc @@ -892,6 +892,8 @@ namespace spot if (is_det) { + for (auto& [_, dest] : res) + dest = formula::first_match(dest); finalize_new(res, opts, d); return res; } From aeba9ff674627f3e57a3e5116ea34e01514b36f3 Mon Sep 17 00:00:00 2001 From: Antoine Martin Date: Thu, 16 Mar 2023 07:39:34 +0100 Subject: [PATCH 329/337] expansions: remove multiple old implementations --- spot/tl/expansions.cc | 1419 +++++------------------------------------ spot/tl/expansions.hh | 37 +- 2 files changed, 167 insertions(+), 1289 deletions(-) diff --git a/spot/tl/expansions.cc b/spot/tl/expansions.cc index e92a74621..4a6e6ca0c 100644 --- a/spot/tl/expansions.cc +++ b/spot/tl/expansions.cc @@ -29,436 +29,6 @@ namespace spot { namespace { - class expansion_builder - { - public: - using exp_map = std::map; - - virtual void insert(bdd letter, formula suffix) = 0; - virtual void finalize(bool deterministic) = 0; - virtual exp_map& result() = 0; - virtual bool empty() = 0; - virtual void clear() = 0; - }; - - class expansion_basic final : expansion_builder - { - public: - using exp_map = expansion_builder::exp_map; - - expansion_basic(bdd_dict_ptr d) - {} - - expansion_basic(exp_map&& m, bdd_dict_ptr d) - : bdd2formula_(m) - , formula2bdd_() - {} - - void insert(bdd letter, formula suffix) final; - - void finalize(bool deterministic) final; - - exp_map& result() final - { - return bdd2formula_; - } - - bool empty() final - { - return bdd2formula_.empty(); - } - - void clear() final - { - bdd2formula_.clear(); - } - - private: - exp_map bdd2formula_; - std::map formula2bdd_; - }; - - void expansion_basic::insert(bdd letter, formula suffix) - { - auto res = bdd2formula_.insert({letter, suffix}); - if (!res.second) - { - auto it = res.first; - it->second = formula::OrRat({it->second, suffix}); - } - } - - void expansion_basic::finalize(bool deterministic) - { - if (!deterministic) - return; - - bdd or_labels = bddfalse; - bdd support = bddtrue; - bool is_det = true; - for (const auto& [l, _] : bdd2formula_) - { - support &= bdd_support(l); - if (is_det) - is_det = !bdd_have_common_assignment(l, or_labels); - or_labels |= l; - } - - if (is_det) - { - // we don't need to determinize the expansion, it's already - // deterministic - return; - } - - exp_map res; - std::vector dests; - for (bdd l: minterms_of(or_labels, support)) - { - for (const auto& [ndet_label, ndet_dest] : bdd2formula_) - { - if (bdd_implies(l, ndet_label)) - dests.push_back(ndet_dest); - } - formula or_dests = formula::OrRat(dests); - res.insert({l, or_dests}); - dests.clear(); - } - - bdd2formula_ = std::move(res); - } - - class expansion_merge_formulas final : expansion_builder - { - public: - using exp_map = expansion_builder::exp_map; - - expansion_merge_formulas(bdd_dict_ptr d) - {} - - expansion_merge_formulas(exp_map&& m, bdd_dict_ptr d) - : res_() - , terms_(m.begin(), m.end()) - {} - - void insert(bdd letter, formula suffix) final; - - void finalize(bool deterministic) final; - - exp_map& result() final - { - return res_; - } - - bool empty() final - { - return terms_.empty(); - } - - void clear() final - { - terms_.clear(); - res_.clear(); - } - - private: - std::vector> terms_; - exp_map res_; - }; - - void expansion_merge_formulas::insert(bdd letter, formula suffix) - { - terms_.push_back({letter, suffix}); - } - - void expansion_merge_formulas::finalize(bool deterministic) - { - res_.clear(); - - // Given such terms: - // - // - a . ϕ1 - // - a . ϕ2 - // - b . ϕ1 - // - // Merge them by suffix: - // - // - (a ∨ b) . ϕ1 - // - a . ϕ2 - std::map suffix2letter; - for (const auto& [letter, suffix]: terms_) - { - auto res = suffix2letter.insert({suffix, letter}); - if (!res.second) - { - auto it = res.first; - it->second |= letter; - } - } - - // Given such terms: - // - // - a . ϕ1 - // - a . ϕ2 - // - // Merge them by letter: - // - // - a . (ϕ1 ∨ ϕ2) - for (const auto& [suffix, letter]: suffix2letter) - { - auto res = res_.insert({letter, suffix}); - if (!res.second) - { - auto it = res.first; - it->second = formula::OrRat({it->second, suffix}); - } - } - - if (!deterministic) - return; - - bdd or_labels = bddfalse; - bdd support = bddtrue; - bool is_det = true; - for (const auto& [l, _] : res_) - { - support &= bdd_support(l); - if (is_det) - is_det = !bdd_have_common_assignment(l, or_labels); - or_labels |= l; - } - - if (is_det) - { - // we don't need to determinize the expansion, it's already - // deterministic - return; - } - - exp_map res; - std::vector dests; - for (bdd l: minterms_of(or_labels, support)) - { - for (const auto& [ndet_label, ndet_dest] : res_) - { - if (bdd_implies(l, ndet_label)) - dests.push_back(ndet_dest); - } - formula or_dests = formula::OrRat(dests); - res.insert({l, or_dests}); - dests.clear(); - } - - res_ = std::move(res); - } - - class expansion_bdd final : expansion_builder - { - public: - using exp_map = expansion_builder::exp_map; - - expansion_bdd(bdd_dict_ptr d) - : anon_set_(bddtrue) - , d_(d) - {} - - expansion_bdd(exp_map&& m, bdd_dict_ptr d) - : anon_set_(bddtrue) - , d_(d) - { - for (const auto& [letter, suffix] : m) - { - insert(letter, suffix); - } - } - - expansion_bdd(const expansion_bdd&) = delete; - - expansion_bdd& - operator=(const expansion_bdd& other) = delete; - - expansion_bdd& - operator=(const expansion_bdd&& other) - { - d_->unregister_all_my_variables(this); - - anon_set_ = std::move(other.anon_set_); - exp_ = std::move(other.exp_); - res_ = std::move(other.res_); - formula2bdd_ = std::move(other.formula2bdd_); - bdd2formula_ = std::move(other.bdd2formula_); - - d_ = other.d_; - d_->register_all_variables_of(&other, this); - - return *this; - } - - ~expansion_bdd() - { - d_->unregister_all_my_variables(this); - } - - void insert(bdd letter, formula suffix) final; - - void finalize(bool deterministic) final; - - exp_map& result() final - { - return res_; - } - - bool empty() final - { - return formula2bdd_.empty(); - } - - void clear() final - { - formula2bdd_.clear(); - bdd2formula_.clear(); - exp_ = bddfalse; - anon_set_ = bddtrue; - res_.clear(); - } - - private: - bdd exp_; - bdd anon_set_; - std::map formula2bdd_; - std::map bdd2formula_; - exp_map res_; - bdd_dict_ptr d_; - - formula var_to_formula(int var); - formula conj_bdd_to_sere(bdd b); - formula bdd_to_sere(bdd b); - }; - - formula - expansion_bdd::var_to_formula(int var) - { - formula f = bdd2formula_[var]; - assert(f); - return f; - } - - formula - expansion_bdd::conj_bdd_to_sere(bdd b) - { - if (b == bddtrue) - return formula::tt(); - if (b == bddfalse) - return formula::ff(); - - // Unroll the first loop of the next do/while loop so that we - // do not have to create v when b is not a conjunction. - formula res = var_to_formula(bdd_var(b)); - bdd high = bdd_high(b); - if (high == bddfalse) - { - res = formula::Not(res); - b = bdd_low(b); - } - else - { - assert(bdd_low(b) == bddfalse); - b = high; - } - if (b == bddtrue) - return res; - std::vector v{std::move(res)}; - do - { - res = var_to_formula(bdd_var(b)); - high = bdd_high(b); - if (high == bddfalse) - { - res = formula::Not(res); - b = bdd_low(b); - } - else - { - assert(bdd_low(b) == bddfalse); - b = high; - } - assert(b != bddfalse); - v.emplace_back(std::move(res)); - } - while (b != bddtrue); - return formula::multop(op::AndRat, std::move(v)); - } - - void expansion_bdd::insert(bdd letter, formula suffix) - { - - int anon_var_num; - auto it = formula2bdd_.find(suffix); - if (it != formula2bdd_.end()) - { - anon_var_num = it->second; - } - else - { - anon_var_num = d_->register_anonymous_variables(1, this); - formula2bdd_.insert({suffix, anon_var_num}); - bdd2formula_.insert({anon_var_num, suffix}); - } - - bdd var = bdd_ithvar(anon_var_num); - anon_set_ &= var; - exp_ |= letter & var; - } - formula - expansion_bdd::bdd_to_sere(bdd f) - { - if (f == bddfalse) - return formula::ff(); - - std::vector v; - minato_isop isop(f); - bdd cube; - while ((cube = isop.next()) != bddfalse) - v.emplace_back(conj_bdd_to_sere(cube)); - return formula::OrRat(std::move(v)); - } - - void expansion_bdd::finalize(bool deterministic) - { - if (deterministic) - { - bdd prop_set = bdd_exist(bdd_support(exp_), anon_set_); - bdd or_labels = bdd_exist(exp_, anon_set_); - for (bdd letter: minterms_of(exp_, prop_set)) - { - bdd dest_bdd = bdd_restrict(exp_, letter); - formula dest = bdd_to_sere(dest_bdd); - - auto it = res_.insert({letter, dest}); - assert(it.second); - (void) it; - } - } - else - { - minato_isop isop(exp_); - bdd cube; - while ((cube = isop.next()) != bddfalse) - { - bdd letter = bdd_exist(cube, anon_set_); - bdd suffix = bdd_existcomp(cube, anon_set_); - formula dest = conj_bdd_to_sere(suffix); - - auto it = res_.insert({letter, dest}); - if (!it.second) - { - auto it2 = it.first; - it2->second = formula::OrRat({it2->second, dest}); - } - } - } - } - // FIXME: could probably just return a map directly static std::vector formula_aps(formula f) @@ -479,8 +49,8 @@ namespace spot return std::vector(res.begin(), res.end()); } - formula - rewrite_and_nlm(formula f) + formula + rewrite_and_nlm(formula f) { unsigned s = f.size(); std::vector final; @@ -532,30 +102,15 @@ namespace spot } return formula::OrRat(std::move(disj)); } - } - formula - expansion_to_formula(expansion_t e, bdd_dict_ptr& d) - { - std::vector res; - - for (const auto& [key, val] : e) - { - formula prefix = bdd_to_formula(key, d); - res.push_back(formula::Concat({prefix, val})); - } - - return formula::OrRat(res); - } - - class bdd_finalizer - { - public: - bdd_finalizer(std::multimap& exp, bdd_dict_ptr d) - : anon_set_(bddtrue) - , d_(d) - { - for (const auto& [prefix, suffix] : exp) + class bdd_finalizer + { + public: + bdd_finalizer(std::multimap& exp, bdd_dict_ptr d) + : anon_set_(bddtrue) + , d_(d) + { + for (const auto& [prefix, suffix] : exp) { int anon_var_num; auto it = formula2bdd_.find(suffix); @@ -574,185 +129,201 @@ namespace spot anon_set_ &= var; exp_ |= prefix & var; } - } + } - ~bdd_finalizer() - { - d_->unregister_all_my_variables(this); - } + ~bdd_finalizer() + { + d_->unregister_all_my_variables(this); + } - std::multimap - simplify(exp_opts_new::expand_opt_new opts); + expansion_t + simplify(exp_opts::expand_opt opts); - private: - bdd exp_; - bdd anon_set_; - std::map formula2bdd_; - std::map bdd2formula_; - bdd_dict_ptr d_; + private: + bdd exp_; + bdd anon_set_; + std::map formula2bdd_; + std::map bdd2formula_; + bdd_dict_ptr d_; - formula var_to_formula(int var); - formula conj_bdd_to_sere(bdd b); - formula bdd_to_sere(bdd b); - }; + formula var_to_formula(int var); + formula conj_bdd_to_sere(bdd b); + formula bdd_to_sere(bdd b); + }; - formula - bdd_finalizer::var_to_formula(int var) - { - formula f = bdd2formula_[var]; - assert(f); - return f; - } + formula + bdd_finalizer::var_to_formula(int var) + { + formula f = bdd2formula_[var]; + assert(f); + return f; + } - formula - bdd_finalizer::bdd_to_sere(bdd f) - { - if (f == bddfalse) - return formula::ff(); + formula + bdd_finalizer::bdd_to_sere(bdd f) + { + if (f == bddfalse) + return formula::ff(); - std::vector v; - minato_isop isop(f); - bdd cube; - while ((cube = isop.next()) != bddfalse) - v.emplace_back(conj_bdd_to_sere(cube)); - return formula::OrRat(std::move(v)); - } + std::vector v; + minato_isop isop(f); + bdd cube; + while ((cube = isop.next()) != bddfalse) + v.emplace_back(conj_bdd_to_sere(cube)); + return formula::OrRat(std::move(v)); + } - formula - bdd_finalizer::conj_bdd_to_sere(bdd b) - { - if (b == bddtrue) - return formula::tt(); - if (b == bddfalse) - return formula::ff(); + formula + bdd_finalizer::conj_bdd_to_sere(bdd b) + { + if (b == bddtrue) + return formula::tt(); + if (b == bddfalse) + return formula::ff(); - // Unroll the first loop of the next do/while loop so that we - // do not have to create v when b is not a conjunction. - formula res = var_to_formula(bdd_var(b)); - bdd high = bdd_high(b); - if (high == bddfalse) + // Unroll the first loop of the next do/while loop so that we + // do not have to create v when b is not a conjunction. + formula res = var_to_formula(bdd_var(b)); + bdd high = bdd_high(b); + if (high == bddfalse) { res = formula::Not(res); b = bdd_low(b); } - else + else { assert(bdd_low(b) == bddfalse); b = high; } - if (b == bddtrue) - return res; - std::vector v{std::move(res)}; - do + if (b == bddtrue) + return res; + std::vector v{std::move(res)}; + do { res = var_to_formula(bdd_var(b)); high = bdd_high(b); if (high == bddfalse) - { - res = formula::Not(res); - b = bdd_low(b); - } + { + res = formula::Not(res); + b = bdd_low(b); + } else - { - assert(bdd_low(b) == bddfalse); - b = high; - } + { + assert(bdd_low(b) == bddfalse); + b = high; + } assert(b != bddfalse); v.emplace_back(std::move(res)); } - while (b != bddtrue); - return formula::multop(op::AndRat, std::move(v)); - } - - std::multimap - bdd_finalizer::simplify(exp_opts_new::expand_opt_new opts) - { - std::multimap res; - - if (opts & exp_opts_new::expand_opt_new::BddMinterm) - { - bdd prop_set = bdd_exist(bdd_support(exp_), anon_set_); - bdd or_labels = bdd_exist(exp_, anon_set_); - for (bdd letter: minterms_of(exp_, prop_set)) - { - bdd dest_bdd = bdd_restrict(exp_, letter); - formula dest = bdd_to_sere(dest_bdd); - - auto it = res.insert({letter, dest}); - assert(it.second); - (void) it; - } - } - else // BddIsop - { - minato_isop isop(exp_); - bdd cube; - while ((cube = isop.next()) != bddfalse) - { - bdd letter = bdd_exist(cube, anon_set_); - bdd suffix = bdd_existcomp(cube, anon_set_); - formula dest = conj_bdd_to_sere(suffix); - - res.insert({letter, dest}); - } + while (b != bddtrue); + return formula::multop(op::AndRat, std::move(v)); } - return res; - } + expansion_t + bdd_finalizer::simplify(exp_opts::expand_opt opts) + { + expansion_t res; - void - finalize_new(std::multimap& exp, exp_opts_new::expand_opt_new opts, bdd_dict_ptr d) - { - if (opts & (exp_opts_new::expand_opt_new::BddIsop - | exp_opts_new::expand_opt_new::BddMinterm)) + if (opts & exp_opts::expand_opt::BddMinterm) + { + bdd prop_set = bdd_exist(bdd_support(exp_), anon_set_); + bdd or_labels = bdd_exist(exp_, anon_set_); + for (bdd letter: minterms_of(exp_, prop_set)) + { + bdd dest_bdd = bdd_restrict(exp_, letter); + formula dest = bdd_to_sere(dest_bdd); + + auto it = res.insert({letter, dest}); + assert(it.second); + (void) it; + } + } + else // BddIsop + { + minato_isop isop(exp_); + bdd cube; + while ((cube = isop.next()) != bddfalse) + { + bdd letter = bdd_exist(cube, anon_set_); + bdd suffix = bdd_existcomp(cube, anon_set_); + formula dest = conj_bdd_to_sere(suffix); + + res.insert({letter, dest}); + } + } + + return res; + } + + void + finalize(expansion_t& exp, exp_opts::expand_opt opts, bdd_dict_ptr d) + { + if (opts & (exp_opts::expand_opt::BddIsop + | exp_opts::expand_opt::BddMinterm)) { bdd_finalizer bddf(exp, d); exp = bddf.simplify(opts); } - if (opts & exp_opts_new::expand_opt_new::UniqueSuffix) + if (opts & exp_opts::expand_opt::UniqueSuffix) { std::map unique_map; for (const auto& [prefix, suffix] : exp) + { + auto res = unique_map.insert({suffix, prefix}); + if (!res.second) { - auto res = unique_map.insert({suffix, prefix}); - if (!res.second) - { - auto it = res.first; - it->second |= prefix; - } + auto it = res.first; + it->second |= prefix; } + } exp.clear(); for (const auto [suffix, prefix] : unique_map) - { - exp.insert({prefix, suffix}); - } + { + exp.insert({prefix, suffix}); + } } - if (opts & exp_opts_new::expand_opt_new::UniquePrefix) + if (opts & exp_opts::expand_opt::UniquePrefix) { std::map unique_map; for (const auto& [prefix, suffix] : exp) + { + auto res = unique_map.insert({prefix, suffix}); + if (!res.second) { - auto res = unique_map.insert({prefix, suffix}); - if (!res.second) - { - auto it = res.first; - it->second = formula::OrRat({it->second, suffix}); - } + auto it = res.first; + it->second = formula::OrRat({it->second, suffix}); } + } exp.clear(); for (const auto [prefix, suffix] : unique_map) - { - exp.insert({prefix, suffix}); - } + { + exp.insert({prefix, suffix}); + } } + } } - std::multimap - expansion_new(formula f, const bdd_dict_ptr& d, void *owner, exp_opts_new::expand_opt_new opts) + formula + expansion_to_formula(expansion_t e, bdd_dict_ptr& d) + { + std::vector res; + + for (const auto& [key, val] : e) + { + formula prefix = bdd_to_formula(key, d); + res.push_back(formula::Concat({prefix, val})); + } + + return formula::OrRat(res); + } + + + expansion_t + expansion(formula f, const bdd_dict_ptr& d, void *owner, exp_opts::expand_opt opts) { using exp_t = std::multimap; @@ -767,7 +338,7 @@ namespace spot } auto rec = [&d, owner, opts](formula f){ - return expansion_new(f, d, owner, exp_opts_new::None); + return expansion(f, d, owner, exp_opts::None); }; @@ -800,7 +371,7 @@ namespace spot } } - finalize_new(res, opts, d); + finalize(res, opts, d); return res; } @@ -837,7 +408,7 @@ namespace spot if (f.min() == 0) res.insert({bddtrue, formula::eword()}); - finalize_new(res, opts, d); + finalize(res, opts, d); return res; } @@ -856,7 +427,7 @@ namespace spot res.insert({bdd_l, formula::Concat({form, formula::Star(f[0], min, max)})}); } - finalize_new(res, opts, d); + finalize(res, opts, d); return res; } @@ -864,7 +435,7 @@ namespace spot { formula rewrite = rewrite_and_nlm(f); auto res = rec(rewrite); - finalize_new(res, opts, d); + finalize(res, opts, d); return res; } @@ -894,7 +465,7 @@ namespace spot { for (auto& [_, dest] : res) dest = formula::first_match(dest); - finalize_new(res, opts, d); + finalize(res, opts, d); return res; } @@ -914,7 +485,7 @@ namespace spot for (auto& [_, dest] : res_det) dest = formula::first_match(dest); - finalize_new(res_det, opts, d); + finalize(res_det, opts, d); return res_det; } @@ -939,7 +510,7 @@ namespace spot res.insert({li, formula::Fusion({ei, F})}); } - finalize_new(res, opts, d); + finalize(res, opts, d); return res; } @@ -978,7 +549,7 @@ namespace spot res = std::move(new_res); } - finalize_new(res, opts, d); + finalize(res, opts, d); return res; } @@ -1001,7 +572,7 @@ namespace spot res.insert({label, dest}); } - finalize_new(res, opts, d); + finalize(res, opts, d); return res; } @@ -1015,498 +586,6 @@ namespace spot return {}; } - std::multimap - expansion_simple(formula f, const bdd_dict_ptr& d, void *owner) - { - using exp_t = std::multimap; - - if (f.is_boolean()) - { - auto f_bdd = formula_to_bdd(f, d, owner); - - if (f_bdd == bddfalse) - return {}; - - return {{f_bdd, formula::eword()}}; - } - - auto rec = [&d, owner](formula f){ - return expansion_simple(f, d, owner); - }; - - - switch (f.kind()) - { - case op::ff: - case op::tt: - case op::ap: - SPOT_UNREACHABLE(); - - case op::eword: - return {{bddfalse, formula::ff()}}; - - case op::Concat: - { - auto exps = rec(f[0]); - - exp_t res; - for (const auto& [bdd_l, form] : exps) - { - res.insert({bdd_l, formula::Concat({form, f.all_but(0)})}); - } - - if (f[0].accepts_eword()) - { - auto exps_rest = rec(f.all_but(0)); - for (const auto& [bdd_l, form] : exps_rest) - { - res.insert({bdd_l, form}); - } - } - - return res; - } - - case op::FStar: - { - formula E = f[0]; - - if (f.min() == 0 && f.max() == 0) - return {{bddtrue, formula::eword()}}; - - auto min = f.min() == 0 ? 0 : (f.min() - 1); - auto max = f.max() == formula::unbounded() - ? formula::unbounded() - : (f.max() - 1); - - auto E_i_j_minus = formula::FStar(E, min, max); - - auto exp = rec(E); - exp_t res; - for (const auto& [li, ei] : exp) - { - res.insert({li, formula::Fusion({ei, E_i_j_minus})}); - - if (ei.accepts_eword() && f.min() != 0) - { - for (const auto& [ki, fi] : rec(E_i_j_minus)) - { - // FIXME: build bdd once - if ((li & ki) != bddfalse) - res.insert({li & ki, fi}); - } - } - } - if (f.min() == 0) - res.insert({bddtrue, formula::eword()}); - - return res; - } - - case op::Star: - { - auto min = f.min() == 0 ? 0 : (f.min() - 1); - auto max = f.max() == formula::unbounded() - ? formula::unbounded() - : (f.max() - 1); - - auto exps = rec(f[0]); - - exp_t res; - for (const auto& [bdd_l, form] : exps) - { - res.insert({bdd_l, formula::Concat({form, formula::Star(f[0], min, max)})}); - } - - return res; - } - - case op::AndNLM: - { - formula rewrite = rewrite_and_nlm(f); - return rec(rewrite); - } - - case op::first_match: - { - auto exps = rec(f[0]); - - exp_t res; - for (const auto& [bdd_l, form] : exps) - { - res.insert({bdd_l, form}); - } - - // determinize - bdd or_labels = bddfalse; - bdd support = bddtrue; - bool is_det = true; - for (const auto& [l, _] : res) - { - support &= bdd_support(l); - if (is_det) - is_det = !bdd_have_common_assignment(l, or_labels); - or_labels |= l; - } - - if (is_det) - return res; - - exp_t res_det; - std::vector dests; - for (bdd l: minterms_of(or_labels, support)) - { - for (const auto& [ndet_label, ndet_dest] : res) - { - if (bdd_implies(l, ndet_label)) - dests.push_back(ndet_dest); - } - formula or_dests = formula::OrRat(dests); - res_det.insert({l, or_dests}); - dests.clear(); - } - - for (auto& [_, dest] : res_det) - dest = formula::first_match(dest); - return res_det; - } - - case op::Fusion: - { - exp_t res; - formula E = f[0]; - formula F = f.all_but(0); - - exp_t Ei = rec(E); - // TODO: std::option - exp_t Fj = rec(F); - - for (const auto& [li, ei] : Ei) - { - if (ei.accepts_eword()) - { - for (const auto& [kj, fj] : Fj) - if ((li & kj) != bddfalse) - res.insert({li & kj, fj}); - } - res.insert({li, formula::Fusion({ei, F})}); - } - - return res; - } - - case op::AndRat: - { - exp_t res; - for (const auto& sub_f : f) - { - auto exps = rec(sub_f); - - if (exps.empty()) - { - // op::AndRat: one of the expansions was empty (the only - // edge was `false`), so the AndRat is empty as - // well - res.clear(); - break; - } - - if (res.empty()) - { - res = std::move(exps); - continue; - } - - exp_t new_res; - for (const auto& [l_key, l_val] : exps) - { - for (const auto& [r_key, r_val] : res) - { - if ((l_key & r_key) != bddfalse) - new_res.insert({l_key & r_key, formula::multop(f.kind(), {l_val, r_val})}); - } - } - - res = std::move(new_res); - } - - return res; - } - - case op::OrRat: - { - exp_t res; - for (const auto& sub_f : f) - { - auto exps = rec(sub_f); - if (exps.empty()) - continue; - - if (res.empty()) - { - res = std::move(exps); - continue; - } - - for (const auto& [label, dest] : exps) - res.insert({label, dest}); - } - - return res; - } - - default: - std::cerr << "unimplemented kind " - << static_cast(f.kind()) - << std::endl; - SPOT_UNIMPLEMENTED(); - } - - return {}; - } - - template - expansion_t - expansion_impl(formula f, const bdd_dict_ptr& d, void *owner, exp_opts::expand_opt opts) - { - if (f.is_boolean()) - { - auto f_bdd = formula_to_bdd(f, d, owner); - - if (f_bdd == bddfalse) - return {}; - - return {{f_bdd, formula::eword()}}; - } - - auto rec = [&d, owner](formula f){ - return expansion_impl(f, d, owner, exp_opts::None); - }; - - - switch (f.kind()) - { - case op::ff: - case op::tt: - case op::ap: - SPOT_UNREACHABLE(); - - case op::eword: - return {{bddfalse, formula::ff()}}; - - case op::Concat: - { - auto exps = rec(f[0]); - - ExpansionBuilder res(d); - for (const auto& [bdd_l, form] : exps) - { - res.insert(bdd_l, formula::Concat({form, f.all_but(0)})); - } - - if (f[0].accepts_eword()) - { - auto exps_rest = rec(f.all_but(0)); - for (const auto& [bdd_l, form] : exps_rest) - { - res.insert(bdd_l, form); - } - } - - res.finalize(opts & exp_opts::Deterministic); - return res.result(); - } - - case op::FStar: - { - formula E = f[0]; - - if (f.min() == 0 && f.max() == 0) - return {{bddtrue, formula::eword()}}; - - auto min = f.min() == 0 ? 0 : (f.min() - 1); - auto max = f.max() == formula::unbounded() - ? formula::unbounded() - : (f.max() - 1); - - auto E_i_j_minus = formula::FStar(E, min, max); - - auto exp = rec(E); - ExpansionBuilder res(d); - for (const auto& [li, ei] : exp) - { - res.insert(li, formula::Fusion({ei, E_i_j_minus})); - - if (ei.accepts_eword() && f.min() != 0) - { - for (const auto& [ki, fi] : rec(E_i_j_minus)) - { - // FIXME: build bdd once - if ((li & ki) != bddfalse) - res.insert(li & ki, fi); - } - } - } - if (f.min() == 0) - res.insert(bddtrue, formula::eword()); - - res.finalize(opts & exp_opts::Deterministic); - return res.result(); - } - - case op::Star: - { - auto min = f.min() == 0 ? 0 : (f.min() - 1); - auto max = f.max() == formula::unbounded() - ? formula::unbounded() - : (f.max() - 1); - - auto exps = rec(f[0]); - - ExpansionBuilder res(d); - for (const auto& [bdd_l, form] : exps) - { - res.insert(bdd_l, formula::Concat({form, formula::Star(f[0], min, max)})); - } - - res.finalize(opts & exp_opts::Deterministic); - return res.result(); - } - - case op::AndNLM: - { - formula rewrite = rewrite_and_nlm(f); - return rec(rewrite); - } - - case op::first_match: - { - auto exps = rec(f[0]); - - ExpansionBuilder res(d); - for (const auto& [bdd_l, form] : exps) - { - res.insert(bdd_l, form); - } - - res.finalize(true); - auto res2 = res.result(); - for (auto& [_, dest] : res2) - dest = formula::first_match(dest); - return res2; - } - - case op::Fusion: - { - ExpansionBuilder res(d); - formula E = f[0]; - formula F = f.all_but(0); - - expansion_t Ei = rec(E); - // TODO: std::option - expansion_t Fj = rec(F); - - for (const auto& [li, ei] : Ei) - { - if (ei.accepts_eword()) - { - for (const auto& [kj, fj] : Fj) - if ((li & kj) != bddfalse) - res.insert(li & kj, fj); - } - res.insert(li, formula::Fusion({ei, F})); - } - - res.finalize(opts & exp_opts::Deterministic); - return res.result(); - } - - case op::AndRat: - { - ExpansionBuilder res(d); - for (const auto& sub_f : f) - { - auto exps = rec(sub_f); - - if (exps.empty()) - { - // op::AndRat: one of the expansions was empty (the only - // edge was `false`), so the AndRat is empty as - // well - res.clear(); - break; - } - - if (res.empty()) - { - res = ExpansionBuilder(std::move(exps), d); - res.finalize(false); - continue; - } - - ExpansionBuilder new_res(d); - for (const auto& [l_key, l_val] : exps) - { - for (const auto& [r_key, r_val] : res.result()) - { - if ((l_key & r_key) != bddfalse) - new_res.insert(l_key & r_key, formula::multop(f.kind(), {l_val, r_val})); - } - } - - res = std::move(new_res); - res.finalize(false); - } - - res.finalize(opts & exp_opts::Deterministic); - return res.result(); - } - - case op::OrRat: - { - ExpansionBuilder res(d); - for (const auto& sub_f : f) - { - auto exps = rec(sub_f); - if (exps.empty()) - continue; - - if (res.empty()) - { - res = ExpansionBuilder(std::move(exps), d); - continue; - } - - for (const auto& [label, dest] : exps) - res.insert(label, dest); - } - - res.finalize(opts & exp_opts::Deterministic); - return res.result(); - } - - default: - std::cerr << "unimplemented kind " - << static_cast(f.kind()) - << std::endl; - SPOT_UNIMPLEMENTED(); - } - - return {}; - } - - expansion_t - expansion(formula f, const bdd_dict_ptr& d, void *owner, exp_opts::expand_opt opts) - { - - if (opts & exp_opts::Bdd) - return expansion_impl(f, d, owner, opts); - else if (opts & exp_opts::MergeSuffix) - return expansion_impl(f, d, owner, opts); - else // exp_opts::Basic - return expansion_impl(f, d, owner, opts); - } - twa_graph_ptr expand_automaton(formula f, bdd_dict_ptr d, exp_opts::expand_opt opts) { @@ -1589,181 +668,9 @@ namespace spot } aut->set_named_prop("state-names", state_names); - aut->merge_edges(); - return aut; - } - twa_graph_ptr - expand_simple_automaton(formula f, bdd_dict_ptr d) - { - auto finite = expand_simple_finite_automaton(f, d); - return from_finite(finite); - } - - twa_graph_ptr - expand_simple_finite_automaton(formula f, bdd_dict_ptr d) - { - auto aut = make_twa_graph(d); - - aut->prop_state_acc(true); - const auto acc_mark = aut->set_buchi(); - - auto formula2state = robin_hood::unordered_map(); - - unsigned init_state = aut->new_state(); - aut->set_init_state(init_state); - formula2state.insert({ f, init_state }); - - auto f_aps = formula_aps(f); - for (auto& ap : f_aps) - aut->register_ap(ap); - - auto todo = std::vector>(); - todo.push_back({f, init_state}); - - auto state_names = new std::vector(); - std::ostringstream ss; - ss << f; - state_names->push_back(ss.str()); - - auto find_dst = [&](formula suffix) -> unsigned - { - unsigned dst; - auto it = formula2state.find(suffix); - if (it != formula2state.end()) - { - dst = it->second; - } - else - { - dst = aut->new_state(); - todo.push_back({suffix, dst}); - formula2state.insert({suffix, dst}); - std::ostringstream ss; - ss << suffix; - state_names->push_back(ss.str()); - } - - return dst; - }; - - while (!todo.empty()) - { - auto [curr_f, curr_state] = todo[todo.size() - 1]; - todo.pop_back(); - - auto curr_acc_mark= curr_f.accepts_eword() - ? acc_mark - : acc_cond::mark_t(); - - auto exp = expansion_simple(curr_f, d, aut.get()); - - for (const auto& [letter, suffix] : exp) - { - if (suffix.is(op::ff)) - continue; - - auto dst = find_dst(suffix); - aut->new_edge(curr_state, dst, letter, curr_acc_mark); - } - - // if state has no transitions and should be accepting, create - // artificial transition - if (aut->get_graph().state_storage(curr_state).succ == 0 - && curr_f.accepts_eword()) - aut->new_edge(curr_state, curr_state, bddfalse, acc_mark); - } - - aut->set_named_prop("state-names", state_names); - aut->merge_edges(); - return aut; - } - - twa_graph_ptr - expand_new_automaton(formula f, bdd_dict_ptr d, exp_opts_new::expand_opt_new opts) - { - auto finite = expand_new_finite_automaton(f, d, opts); - return from_finite(finite); - } - - twa_graph_ptr - expand_new_finite_automaton(formula f, bdd_dict_ptr d, exp_opts_new::expand_opt_new opts) - { - auto aut = make_twa_graph(d); - - aut->prop_state_acc(true); - const auto acc_mark = aut->set_buchi(); - - auto formula2state = robin_hood::unordered_map(); - - unsigned init_state = aut->new_state(); - aut->set_init_state(init_state); - formula2state.insert({ f, init_state }); - - auto f_aps = formula_aps(f); - for (auto& ap : f_aps) - aut->register_ap(ap); - - auto todo = std::vector>(); - todo.push_back({f, init_state}); - - auto state_names = new std::vector(); - std::ostringstream ss; - ss << f; - state_names->push_back(ss.str()); - - auto find_dst = [&](formula suffix) -> unsigned - { - unsigned dst; - auto it = formula2state.find(suffix); - if (it != formula2state.end()) - { - dst = it->second; - } - else - { - dst = aut->new_state(); - todo.push_back({suffix, dst}); - formula2state.insert({suffix, dst}); - std::ostringstream ss; - ss << suffix; - state_names->push_back(ss.str()); - } - - return dst; - }; - - while (!todo.empty()) - { - auto [curr_f, curr_state] = todo[todo.size() - 1]; - todo.pop_back(); - - auto curr_acc_mark= curr_f.accepts_eword() - ? acc_mark - : acc_cond::mark_t(); - - auto exp = expansion_new(curr_f, d, aut.get(), opts); - - for (const auto& [letter, suffix] : exp) - { - if (suffix.is(op::ff)) - continue; - - auto dst = find_dst(suffix); - aut->new_edge(curr_state, dst, letter, curr_acc_mark); - } - - // if state has no transitions and should be accepting, create - // artificial transition - if (aut->get_graph().state_storage(curr_state).succ == 0 - && curr_f.accepts_eword()) - aut->new_edge(curr_state, curr_state, bddfalse, acc_mark); - } - - aut->set_named_prop("state-names", state_names); - - if ((opts & exp_opts_new::MergeEdges) - && !(opts & exp_opts_new::UniqueSuffix)) + if ((opts & exp_opts::MergeEdges) + && !(opts & exp_opts::UniqueSuffix)) aut->merge_edges(); return aut; diff --git a/spot/tl/expansions.hh b/spot/tl/expansions.hh index 52e83917f..43a51e721 100644 --- a/spot/tl/expansions.hh +++ b/spot/tl/expansions.hh @@ -30,22 +30,11 @@ namespace spot { - using expansion_t = std::map; + using expansion_t = std::multimap; struct exp_opts { enum expand_opt { - None = 0, - Deterministic = 1, - Basic = 2, - MergeSuffix = 4, - Bdd = 8, - }; - }; - - struct exp_opts_new - { - enum expand_opt_new { None = 0, UniqueSuffix = 1, UniquePrefix = 2, @@ -55,33 +44,15 @@ namespace spot }; }; - SPOT_API std::multimap - expansion_new(formula f, const bdd_dict_ptr& d, void *owner, exp_opts_new::expand_opt_new opts); - - SPOT_API twa_graph_ptr - expand_new_automaton(formula f, bdd_dict_ptr d, exp_opts_new::expand_opt_new opts); - - SPOT_API twa_graph_ptr - expand_new_finite_automaton(formula f, bdd_dict_ptr d, exp_opts_new::expand_opt_new opts); - - SPOT_API twa_graph_ptr - expand_simple_automaton(formula f, bdd_dict_ptr d); - - SPOT_API twa_graph_ptr - expand_simple_finite_automaton(formula f, bdd_dict_ptr d); - - SPOT_API std::multimap - expansion_simple(formula f, const bdd_dict_ptr& d, void *owner); - SPOT_API expansion_t expansion(formula f, const bdd_dict_ptr& d, void *owner, exp_opts::expand_opt opts); - SPOT_API formula - expansion_to_formula(expansion_t e, bdd_dict_ptr& d); - SPOT_API twa_graph_ptr expand_automaton(formula f, bdd_dict_ptr d, exp_opts::expand_opt opts); SPOT_API twa_graph_ptr expand_finite_automaton(formula f, bdd_dict_ptr d, exp_opts::expand_opt opts); + + SPOT_API formula + expansion_to_formula(expansion_t e, bdd_dict_ptr& d); } From 81a635c831edf351194249305445c0f61b5c34b0 Mon Sep 17 00:00:00 2001 From: Antoine Martin Date: Tue, 4 Apr 2023 14:52:14 +0200 Subject: [PATCH 330/337] expansions: optimize sigma star encoding --- spot/tl/expansions.cc | 31 ++++++++++++++++++++++++++----- spot/tl/expansions.hh | 3 ++- 2 files changed, 28 insertions(+), 6 deletions(-) diff --git a/spot/tl/expansions.cc b/spot/tl/expansions.cc index 4a6e6ca0c..39e7b0303 100644 --- a/spot/tl/expansions.cc +++ b/spot/tl/expansions.cc @@ -106,9 +106,10 @@ namespace spot class bdd_finalizer { public: - bdd_finalizer(std::multimap& exp, bdd_dict_ptr d) + bdd_finalizer(std::multimap& exp, bdd_dict_ptr d, bool opt_sigma_star) : anon_set_(bddtrue) , d_(d) + , opt_sigma_star_(opt_sigma_star) { for (const auto& [prefix, suffix] : exp) { @@ -120,12 +121,25 @@ namespace spot } else { - anon_var_num = d_->register_anonymous_variables(1, this); + if (opt_sigma_star_ && (suffix.is(op::Star) + && suffix[0].is(op::tt) + && suffix.min() == 0 + && suffix.max() == formula::unbounded())) + { + anon_var_num = -1; + } + else + { + anon_var_num = d_->register_anonymous_variables(1, this); + } + formula2bdd_.insert({suffix, anon_var_num}); bdd2formula_.insert({anon_var_num, suffix}); } - bdd var = bdd_ithvar(anon_var_num); + bdd var = bddtrue; + if (anon_var_num != -1) + var = bdd_ithvar(anon_var_num); anon_set_ &= var; exp_ |= prefix & var; } @@ -145,6 +159,7 @@ namespace spot std::map formula2bdd_; std::map bdd2formula_; bdd_dict_ptr d_; + bool opt_sigma_star_; formula var_to_formula(int var); formula conj_bdd_to_sere(bdd b); @@ -177,7 +192,13 @@ namespace spot bdd_finalizer::conj_bdd_to_sere(bdd b) { if (b == bddtrue) - return formula::tt(); + { + if (opt_sigma_star_){ + return formula::Star(formula::tt(), 0, formula::unbounded()); + } else { + return formula::tt(); + } + } if (b == bddfalse) return formula::ff(); @@ -261,7 +282,7 @@ namespace spot if (opts & (exp_opts::expand_opt::BddIsop | exp_opts::expand_opt::BddMinterm)) { - bdd_finalizer bddf(exp, d); + bdd_finalizer bddf(exp, d, opts & exp_opts::expand_opt::BddSigmaStar); exp = bddf.simplify(opts); } diff --git a/spot/tl/expansions.hh b/spot/tl/expansions.hh index 43a51e721..1d2fbedba 100644 --- a/spot/tl/expansions.hh +++ b/spot/tl/expansions.hh @@ -40,7 +40,8 @@ namespace spot UniquePrefix = 2, BddIsop = 4, BddMinterm = 8, - MergeEdges = 16, + BddSigmaStar = 16, + MergeEdges = 32, }; }; From 189dde38d311108b9f0807ba23877501c898419c Mon Sep 17 00:00:00 2001 From: Antoine Martin Date: Wed, 12 Apr 2023 15:15:36 +0200 Subject: [PATCH 331/337] expansions: signature merge impl --- spot/tl/expansions.cc | 40 ++++++++++++++++++++++++++++++++++++++++ spot/tl/expansions.hh | 1 + 2 files changed, 41 insertions(+) diff --git a/spot/tl/expansions.cc b/spot/tl/expansions.cc index 39e7b0303..dc0e09182 100644 --- a/spot/tl/expansions.cc +++ b/spot/tl/expansions.cc @@ -614,20 +614,44 @@ namespace spot return from_finite(finite); } + struct signature_hash + { + std::size_t + operator() (const std::pair>& sig) const + { + size_t hash = std::hash()(sig.first); + + for (const auto& keyvalue : sig.second) + { + hash ^= (bdd_hash()(keyvalue.first) ^ std::hash()(keyvalue.second)) + + 0x9e3779b9 + (hash << 6) + (hash >> 2); + } + + return hash; + } + }; + twa_graph_ptr expand_finite_automaton(formula f, bdd_dict_ptr d, exp_opts::expand_opt opts) { + bool signature_merge = opts & exp_opts::expand_opt::SignatureMerge; + auto aut = make_twa_graph(d); aut->prop_state_acc(true); const auto acc_mark = aut->set_buchi(); auto formula2state = robin_hood::unordered_map(); + auto signature2state = std::unordered_map, unsigned, signature_hash>(); unsigned init_state = aut->new_state(); aut->set_init_state(init_state); formula2state.insert({ f, init_state }); + if (signature_merge) + signature2state.insert({ {f.accepts_eword(), expansion(f, d, aut.get(), opts)}, init_state}); + + auto f_aps = formula_aps(f); for (auto& ap : f_aps) aut->register_ap(ap); @@ -650,9 +674,25 @@ namespace spot } else { + if (signature_merge) + { + auto exp = expansion(suffix, d, aut.get(), opts); + bool accepting = suffix.accepts_eword(); + auto it2 = signature2state.find({accepting, exp}); + if (it2 != signature2state.end()) + { + formula2state.insert({suffix, it2->second}); + return it2->second; + } + } + dst = aut->new_state(); todo.push_back({suffix, dst}); + formula2state.insert({suffix, dst}); + if (signature_merge) + signature2state.insert({{suffix.accepts_eword(), expansion(suffix, d, aut.get(), opts)}, dst}); + std::ostringstream ss; ss << suffix; state_names->push_back(ss.str()); diff --git a/spot/tl/expansions.hh b/spot/tl/expansions.hh index 1d2fbedba..0aec0a106 100644 --- a/spot/tl/expansions.hh +++ b/spot/tl/expansions.hh @@ -42,6 +42,7 @@ namespace spot BddMinterm = 8, BddSigmaStar = 16, MergeEdges = 32, + SignatureMerge = 64, }; }; From 094fa85b024729dbf0abac7cd5166fbea32de9d9 Mon Sep 17 00:00:00 2001 From: Antoine Martin Date: Fri, 12 May 2023 08:45:59 +0200 Subject: [PATCH 332/337] expansions: simple determinization --- spot/tl/expansions.cc | 23 +++++++++++++++++++++++ spot/tl/expansions.hh | 6 ++++-- 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/spot/tl/expansions.cc b/spot/tl/expansions.cc index dc0e09182..5fb13c0de 100644 --- a/spot/tl/expansions.cc +++ b/spot/tl/expansions.cc @@ -325,6 +325,29 @@ namespace spot exp.insert({prefix, suffix}); } } + + if (opts & exp_opts::expand_opt::Determinize) + { + std::multimap exp_new; + + bdd props = bddtrue; + for (const auto& [prefix, _] : exp) + props &= bdd_support(prefix); + + std::vector dests; + for (bdd letter : minterms_of(bddtrue, props)) + { + for (const auto& [prefix, suffix] : exp) + { + if (bdd_implies(letter, prefix)) + dests.push_back(suffix); + } + formula or_dests = formula::OrRat(dests); + exp_new.insert({letter, or_dests}); + dests.clear(); + } + exp = exp_new; + } } } diff --git a/spot/tl/expansions.hh b/spot/tl/expansions.hh index 0aec0a106..949b25e29 100644 --- a/spot/tl/expansions.hh +++ b/spot/tl/expansions.hh @@ -41,8 +41,10 @@ namespace spot BddIsop = 4, BddMinterm = 8, BddSigmaStar = 16, - MergeEdges = 32, - SignatureMerge = 64, + BddEncode = 32, + MergeEdges = 64, + SignatureMerge = 128, + Determinize = 256, }; }; From 46aee256eb79846017e76d0e1beff65f0b4ff9d4 Mon Sep 17 00:00:00 2001 From: Antoine Martin Date: Tue, 4 Jul 2023 07:21:20 +0200 Subject: [PATCH 333/337] expansions: fixes + BDD encode changes + printer --- spot/tl/expansions.cc | 98 +++++++++++++++++++++++++++++++------------ spot/tl/expansions.hh | 3 ++ 2 files changed, 74 insertions(+), 27 deletions(-) diff --git a/spot/tl/expansions.cc b/spot/tl/expansions.cc index 5fb13c0de..1086b0f67 100644 --- a/spot/tl/expansions.cc +++ b/spot/tl/expansions.cc @@ -106,41 +106,63 @@ namespace spot class bdd_finalizer { public: - bdd_finalizer(std::multimap& exp, bdd_dict_ptr d, bool opt_sigma_star) - : anon_set_(bddtrue) - , d_(d) - , opt_sigma_star_(opt_sigma_star) - { - for (const auto& [prefix, suffix] : exp) + int encode(formula f) { - int anon_var_num; - auto it = formula2bdd_.find(suffix); + bool is_anon = false; + int var_num; + auto it = formula2bdd_.find(f); if (it != formula2bdd_.end()) { - anon_var_num = it->second; + var_num = it->second; } else { - if (opt_sigma_star_ && (suffix.is(op::Star) - && suffix[0].is(op::tt) - && suffix.min() == 0 - && suffix.max() == formula::unbounded())) + if (opt_sigma_star_ && (f.is(op::Star) + && f[0].is(op::tt) + && f.min() == 0 + && f.max() == formula::unbounded())) { - anon_var_num = -1; + var_num = bddtrue.id(); + } + else if (opt_bdd_encode_ && (f.is(op::AndRat) || f.is(op::OrRat))) + { + bdd var = f.is(op::AndRat) ? bdd(bddtrue) : bdd(bddfalse); + for (const auto& sub_f : f) + { + int bddid = encode(sub_f); + bdd subvar = bdd_ithvar(bddid); + var = f.is(op::AndRat) ? var & subvar : var | subvar; + } + var_num = var.id(); } else { - anon_var_num = d_->register_anonymous_variables(1, this); + var_num = d_->register_anonymous_variables(1, this); + is_anon = true; } - formula2bdd_.insert({suffix, anon_var_num}); - bdd2formula_.insert({anon_var_num, suffix}); + formula2bdd_.insert({f, var_num}); + bdd2formula_.insert({var_num, f}); } - bdd var = bddtrue; - if (anon_var_num != -1) - var = bdd_ithvar(anon_var_num); - anon_set_ &= var; + bdd var = bdd_ithvar(var_num); + + if (is_anon) + anon_set_ &= var; + + return var_num; + } + + bdd_finalizer(std::multimap& exp, bdd_dict_ptr d, bool opt_sigma_star, bool opt_bdd_encode) + : anon_set_(bddtrue) + , d_(d) + , opt_sigma_star_(opt_sigma_star) + , opt_bdd_encode_(opt_bdd_encode) + { + for (const auto& [prefix, suffix] : exp) + { + int var_num = encode(suffix); + bdd var = bdd_ithvar(var_num); exp_ |= prefix & var; } } @@ -160,6 +182,7 @@ namespace spot std::map bdd2formula_; bdd_dict_ptr d_; bool opt_sigma_star_; + bool opt_bdd_encode_; formula var_to_formula(int var); formula conj_bdd_to_sere(bdd b); @@ -249,7 +272,8 @@ namespace spot { bdd prop_set = bdd_exist(bdd_support(exp_), anon_set_); bdd or_labels = bdd_exist(exp_, anon_set_); - for (bdd letter: minterms_of(exp_, prop_set)) + // TODO: check are_equivalent avec or_labels/exp_ en premier argument + for (bdd letter: minterms_of(or_labels, prop_set)) { bdd dest_bdd = bdd_restrict(exp_, letter); formula dest = bdd_to_sere(dest_bdd); @@ -282,7 +306,7 @@ namespace spot if (opts & (exp_opts::expand_opt::BddIsop | exp_opts::expand_opt::BddMinterm)) { - bdd_finalizer bddf(exp, d, opts & exp_opts::expand_opt::BddSigmaStar); + bdd_finalizer bddf(exp, d, opts & exp_opts::expand_opt::BddSigmaStar, opts & exp_opts::expand_opt::BddEncode); exp = bddf.simplify(opts); } @@ -365,6 +389,14 @@ namespace spot return formula::OrRat(res); } + void print_expansion(const expansion_t& exp, const bdd_dict_ptr& dict) + { + for (const auto& [prefix, suffix] : exp) + { + std::cout << bdd_to_formula(prefix, dict) << ": " << suffix << std::endl; + } + } + expansion_t expansion(formula f, const bdd_dict_ptr& d, void *owner, exp_opts::expand_opt opts) @@ -581,15 +613,26 @@ namespace spot } exp_t new_res; + bool inserted = false; for (const auto& [l_key, l_val] : exps) { for (const auto& [r_key, r_val] : res) { if ((l_key & r_key) != bddfalse) - new_res.insert({l_key & r_key, formula::multop(f.kind(), {l_val, r_val})}); + { + new_res.insert({l_key & r_key, formula::multop(f.kind(), {l_val, r_val})}); + inserted = true; + } } } + if (!inserted) + { + // all prefix conjuctions led to bddfalse, And is empty + res.clear(); + break; + } + res = std::move(new_res); } @@ -671,14 +714,15 @@ namespace spot aut->set_init_state(init_state); formula2state.insert({ f, init_state }); - if (signature_merge) - signature2state.insert({ {f.accepts_eword(), expansion(f, d, aut.get(), opts)}, init_state}); - auto f_aps = formula_aps(f); for (auto& ap : f_aps) aut->register_ap(ap); + if (signature_merge) + signature2state.insert({ {f.accepts_eword(), expansion(f, d, aut.get(), opts)}, init_state}); + + auto todo = std::vector>(); todo.push_back({f, init_state}); diff --git a/spot/tl/expansions.hh b/spot/tl/expansions.hh index 949b25e29..eba71db9e 100644 --- a/spot/tl/expansions.hh +++ b/spot/tl/expansions.hh @@ -59,4 +59,7 @@ namespace spot SPOT_API formula expansion_to_formula(expansion_t e, bdd_dict_ptr& d); + + SPOT_API void + print_expansion(const expansion_t& exp, const bdd_dict_ptr& dict); } From de2025298e7c36494303a83cbc10fec835a8f958 Mon Sep 17 00:00:00 2001 From: Antoine Martin Date: Wed, 20 Sep 2023 17:52:18 +0200 Subject: [PATCH 334/337] expansions: UniquePrefixSeenOpt --- spot/tl/expansions.cc | 71 +++++++++++++++++++++++++++++++------------ spot/tl/expansions.hh | 3 +- 2 files changed, 54 insertions(+), 20 deletions(-) diff --git a/spot/tl/expansions.cc b/spot/tl/expansions.cc index 1086b0f67..e2e89291f 100644 --- a/spot/tl/expansions.cc +++ b/spot/tl/expansions.cc @@ -301,7 +301,7 @@ namespace spot } void - finalize(expansion_t& exp, exp_opts::expand_opt opts, bdd_dict_ptr d) + finalize(expansion_t& exp, exp_opts::expand_opt opts, bdd_dict_ptr d, std::unordered_set* seen) { if (opts & (exp_opts::expand_opt::BddIsop | exp_opts::expand_opt::BddMinterm)) @@ -344,9 +344,37 @@ namespace spot } exp.clear(); + for (const auto [prefix, suffix] : unique_map) { - exp.insert({prefix, suffix}); + if ((opts & exp_opts::expand_opt::UniquePrefixSeenOpt) + && suffix.is(op::OrRat)) + { + std::vector merge; + std::vector single; + + for (const auto& sub_f : suffix) + { + if (seen->find(sub_f) != seen->end()) + { + single.push_back(sub_f); + } + else + { + merge.push_back(sub_f); + } + } + + for (const auto& sub_f : single) + exp.insert({prefix, sub_f}); + + if (!merge.empty()) + exp.insert({prefix, formula::OrRat(merge)}); + } + else + { + exp.insert({prefix, suffix}); + } } } @@ -399,7 +427,7 @@ namespace spot expansion_t - expansion(formula f, const bdd_dict_ptr& d, void *owner, exp_opts::expand_opt opts) + expansion(formula f, const bdd_dict_ptr& d, void *owner, exp_opts::expand_opt opts, std::unordered_set* seen) { using exp_t = std::multimap; @@ -413,8 +441,8 @@ namespace spot return {{f_bdd, formula::eword()}}; } - auto rec = [&d, owner, opts](formula f){ - return expansion(f, d, owner, exp_opts::None); + auto rec = [&d, owner, opts, seen](formula f){ + return expansion(f, d, owner, exp_opts::None, seen); }; @@ -426,7 +454,8 @@ namespace spot SPOT_UNREACHABLE(); case op::eword: - return {{bddfalse, formula::ff()}}; + // return {{bddfalse, formula::ff()}}; + return {}; case op::Concat: { @@ -447,7 +476,7 @@ namespace spot } } - finalize(res, opts, d); + finalize(res, opts, d, seen); return res; } @@ -484,7 +513,7 @@ namespace spot if (f.min() == 0) res.insert({bddtrue, formula::eword()}); - finalize(res, opts, d); + finalize(res, opts, d, seen); return res; } @@ -503,7 +532,7 @@ namespace spot res.insert({bdd_l, formula::Concat({form, formula::Star(f[0], min, max)})}); } - finalize(res, opts, d); + finalize(res, opts, d, seen); return res; } @@ -511,7 +540,7 @@ namespace spot { formula rewrite = rewrite_and_nlm(f); auto res = rec(rewrite); - finalize(res, opts, d); + finalize(res, opts, d, seen); return res; } @@ -541,7 +570,7 @@ namespace spot { for (auto& [_, dest] : res) dest = formula::first_match(dest); - finalize(res, opts, d); + finalize(res, opts, d, seen); return res; } @@ -561,7 +590,7 @@ namespace spot for (auto& [_, dest] : res_det) dest = formula::first_match(dest); - finalize(res_det, opts, d); + finalize(res_det, opts, d, seen); return res_det; } @@ -586,7 +615,7 @@ namespace spot res.insert({li, formula::Fusion({ei, F})}); } - finalize(res, opts, d); + finalize(res, opts, d, seen); return res; } @@ -636,7 +665,7 @@ namespace spot res = std::move(new_res); } - finalize(res, opts, d); + finalize(res, opts, d, seen); return res; } @@ -659,7 +688,7 @@ namespace spot res.insert({label, dest}); } - finalize(res, opts, d); + finalize(res, opts, d, seen); return res; } @@ -709,6 +738,8 @@ namespace spot auto formula2state = robin_hood::unordered_map(); auto signature2state = std::unordered_map, unsigned, signature_hash>(); + auto seen = std::unordered_set(); + seen.insert(f); unsigned init_state = aut->new_state(); aut->set_init_state(init_state); @@ -720,7 +751,7 @@ namespace spot aut->register_ap(ap); if (signature_merge) - signature2state.insert({ {f.accepts_eword(), expansion(f, d, aut.get(), opts)}, init_state}); + signature2state.insert({ {f.accepts_eword(), expansion(f, d, aut.get(), opts, &seen)}, init_state}); auto todo = std::vector>(); @@ -743,7 +774,7 @@ namespace spot { if (signature_merge) { - auto exp = expansion(suffix, d, aut.get(), opts); + auto exp = expansion(suffix, d, aut.get(), opts, &seen); bool accepting = suffix.accepts_eword(); auto it2 = signature2state.find({accepting, exp}); if (it2 != signature2state.end()) @@ -755,10 +786,11 @@ namespace spot dst = aut->new_state(); todo.push_back({suffix, dst}); + seen.insert(suffix); formula2state.insert({suffix, dst}); if (signature_merge) - signature2state.insert({{suffix.accepts_eword(), expansion(suffix, d, aut.get(), opts)}, dst}); + signature2state.insert({{suffix.accepts_eword(), expansion(suffix, d, aut.get(), opts, &seen)}, dst}); std::ostringstream ss; ss << suffix; @@ -777,11 +809,12 @@ namespace spot ? acc_mark : acc_cond::mark_t(); - auto exp = expansion(curr_f, d, aut.get(), opts); + auto exp = expansion(curr_f, d, aut.get(), opts, &seen); for (const auto& [letter, suffix] : exp) { if (suffix.is(op::ff)) + // TODO ASSERT NOT continue; auto dst = find_dst(suffix); diff --git a/spot/tl/expansions.hh b/spot/tl/expansions.hh index eba71db9e..36476bd31 100644 --- a/spot/tl/expansions.hh +++ b/spot/tl/expansions.hh @@ -45,11 +45,12 @@ namespace spot MergeEdges = 64, SignatureMerge = 128, Determinize = 256, + UniquePrefixSeenOpt = 512, }; }; SPOT_API expansion_t - expansion(formula f, const bdd_dict_ptr& d, void *owner, exp_opts::expand_opt opts); + expansion(formula f, const bdd_dict_ptr& d, void *owner, exp_opts::expand_opt opts, std::unordered_set* seen = nullptr); SPOT_API twa_graph_ptr expand_automaton(formula f, bdd_dict_ptr d, exp_opts::expand_opt opts); From 0199ebd5927d911ac95fb0f8f0f8fd28350b778c Mon Sep 17 00:00:00 2001 From: Antoine Martin Date: Wed, 27 Sep 2023 11:36:17 +0200 Subject: [PATCH 335/337] expansions: US order in pipeline configurable --- spot/tl/expansions.cc | 24 ++++++++++++++++++++++-- spot/tl/expansions.hh | 3 ++- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/spot/tl/expansions.cc b/spot/tl/expansions.cc index e2e89291f..c3d687a42 100644 --- a/spot/tl/expansions.cc +++ b/spot/tl/expansions.cc @@ -310,7 +310,7 @@ namespace spot exp = bddf.simplify(opts); } - if (opts & exp_opts::expand_opt::UniqueSuffix) + if (opts & exp_opts::expand_opt::UniqueSuffixPre) { std::map unique_map; for (const auto& [prefix, suffix] : exp) @@ -378,6 +378,26 @@ namespace spot } } + if (opts & exp_opts::expand_opt::UniqueSuffixPost) + { + std::map unique_map; + for (const auto& [prefix, suffix] : exp) + { + auto res = unique_map.insert({suffix, prefix}); + if (!res.second) + { + auto it = res.first; + it->second |= prefix; + } + } + + exp.clear(); + for (const auto [suffix, prefix] : unique_map) + { + exp.insert({prefix, suffix}); + } + } + if (opts & exp_opts::expand_opt::Determinize) { std::multimap exp_new; @@ -831,7 +851,7 @@ namespace spot aut->set_named_prop("state-names", state_names); if ((opts & exp_opts::MergeEdges) - && !(opts & exp_opts::UniqueSuffix)) + && !(opts & exp_opts::UniqueSuffixPre || opts & exp_opts::UniqueSuffixPost)) aut->merge_edges(); return aut; diff --git a/spot/tl/expansions.hh b/spot/tl/expansions.hh index 36476bd31..2418b1103 100644 --- a/spot/tl/expansions.hh +++ b/spot/tl/expansions.hh @@ -36,7 +36,7 @@ namespace spot { enum expand_opt { None = 0, - UniqueSuffix = 1, + UniqueSuffixPre = 1, UniquePrefix = 2, BddIsop = 4, BddMinterm = 8, @@ -46,6 +46,7 @@ namespace spot SignatureMerge = 128, Determinize = 256, UniquePrefixSeenOpt = 512, + UniqueSuffixPost = 1024, }; }; From 49f3c18ae3f139391b824fbe8e28c115c2e151ea Mon Sep 17 00:00:00 2001 From: Antoine Martin Date: Thu, 12 Oct 2023 15:04:06 +0200 Subject: [PATCH 336/337] expansions: store as vector of pairs --- spot/tl/expansions.cc | 56 +++++++++++++++++++++++-------------------- spot/tl/expansions.hh | 2 +- 2 files changed, 31 insertions(+), 27 deletions(-) diff --git a/spot/tl/expansions.cc b/spot/tl/expansions.cc index c3d687a42..f4dff44eb 100644 --- a/spot/tl/expansions.cc +++ b/spot/tl/expansions.cc @@ -153,7 +153,7 @@ namespace spot return var_num; } - bdd_finalizer(std::multimap& exp, bdd_dict_ptr d, bool opt_sigma_star, bool opt_bdd_encode) + bdd_finalizer(expansion_t& exp, bdd_dict_ptr d, bool opt_sigma_star, bool opt_bdd_encode) : anon_set_(bddtrue) , d_(d) , opt_sigma_star_(opt_sigma_star) @@ -278,9 +278,13 @@ namespace spot bdd dest_bdd = bdd_restrict(exp_, letter); formula dest = bdd_to_sere(dest_bdd); - auto it = res.insert({letter, dest}); - assert(it.second); - (void) it; + #ifndef NDEBUG + // make sure it didn't exist before + auto it = std::find(res.begin(), res.end(), {letter, dest}); + SPOT_ASSERT(it == res.end()); + #endif + + res.push_back({letter, dest}); } } else // BddIsop @@ -293,7 +297,7 @@ namespace spot bdd suffix = bdd_existcomp(cube, anon_set_); formula dest = conj_bdd_to_sere(suffix); - res.insert({letter, dest}); + res.push_back({letter, dest}); } } @@ -326,7 +330,7 @@ namespace spot exp.clear(); for (const auto [suffix, prefix] : unique_map) { - exp.insert({prefix, suffix}); + exp.push_back({prefix, suffix}); } } @@ -366,14 +370,14 @@ namespace spot } for (const auto& sub_f : single) - exp.insert({prefix, sub_f}); + exp.push_back({prefix, sub_f}); if (!merge.empty()) - exp.insert({prefix, formula::OrRat(merge)}); + exp.push_back({prefix, formula::OrRat(merge)}); } else { - exp.insert({prefix, suffix}); + exp.push_back({prefix, suffix}); } } } @@ -394,13 +398,13 @@ namespace spot exp.clear(); for (const auto [suffix, prefix] : unique_map) { - exp.insert({prefix, suffix}); + exp.push_back({prefix, suffix}); } } if (opts & exp_opts::expand_opt::Determinize) { - std::multimap exp_new; + expansion_t exp_new; bdd props = bddtrue; for (const auto& [prefix, _] : exp) @@ -415,7 +419,7 @@ namespace spot dests.push_back(suffix); } formula or_dests = formula::OrRat(dests); - exp_new.insert({letter, or_dests}); + exp_new.push_back({letter, or_dests}); dests.clear(); } exp = exp_new; @@ -449,7 +453,7 @@ namespace spot expansion_t expansion(formula f, const bdd_dict_ptr& d, void *owner, exp_opts::expand_opt opts, std::unordered_set* seen) { - using exp_t = std::multimap; + using exp_t = expansion_t; if (f.is_boolean()) { @@ -484,7 +488,7 @@ namespace spot exp_t res; for (const auto& [bdd_l, form] : exps) { - res.insert({bdd_l, formula::Concat({form, f.all_but(0)})}); + res.push_back({bdd_l, formula::Concat({form, f.all_but(0)})}); } if (f[0].accepts_eword()) @@ -492,7 +496,7 @@ namespace spot auto exps_rest = rec(f.all_but(0)); for (const auto& [bdd_l, form] : exps_rest) { - res.insert({bdd_l, form}); + res.push_back({bdd_l, form}); } } @@ -518,7 +522,7 @@ namespace spot exp_t res; for (const auto& [li, ei] : exp) { - res.insert({li, formula::Fusion({ei, E_i_j_minus})}); + res.push_back({li, formula::Fusion({ei, E_i_j_minus})}); if (ei.accepts_eword() && f.min() != 0) { @@ -526,12 +530,12 @@ namespace spot { // FIXME: build bdd once if ((li & ki) != bddfalse) - res.insert({li & ki, fi}); + res.push_back({li & ki, fi}); } } } if (f.min() == 0) - res.insert({bddtrue, formula::eword()}); + res.push_back({bddtrue, formula::eword()}); finalize(res, opts, d, seen); return res; @@ -549,7 +553,7 @@ namespace spot exp_t res; for (const auto& [bdd_l, form] : exps) { - res.insert({bdd_l, formula::Concat({form, formula::Star(f[0], min, max)})}); + res.push_back({bdd_l, formula::Concat({form, formula::Star(f[0], min, max)})}); } finalize(res, opts, d, seen); @@ -571,7 +575,7 @@ namespace spot exp_t res; for (const auto& [bdd_l, form] : exps) { - res.insert({bdd_l, form}); + res.push_back({bdd_l, form}); } // determinize @@ -604,7 +608,7 @@ namespace spot dests.push_back(ndet_dest); } formula or_dests = formula::OrRat(dests); - res_det.insert({l, or_dests}); + res_det.push_back({l, or_dests}); dests.clear(); } @@ -630,9 +634,9 @@ namespace spot { for (const auto& [kj, fj] : Fj) if ((li & kj) != bddfalse) - res.insert({li & kj, fj}); + res.push_back({li & kj, fj}); } - res.insert({li, formula::Fusion({ei, F})}); + res.push_back({li, formula::Fusion({ei, F})}); } finalize(res, opts, d, seen); @@ -669,7 +673,7 @@ namespace spot { if ((l_key & r_key) != bddfalse) { - new_res.insert({l_key & r_key, formula::multop(f.kind(), {l_val, r_val})}); + new_res.push_back({l_key & r_key, formula::multop(f.kind(), {l_val, r_val})}); inserted = true; } } @@ -705,7 +709,7 @@ namespace spot } for (const auto& [label, dest] : exps) - res.insert({label, dest}); + res.push_back({label, dest}); } finalize(res, opts, d, seen); @@ -732,7 +736,7 @@ namespace spot struct signature_hash { std::size_t - operator() (const std::pair>& sig) const + operator() (const std::pair& sig) const { size_t hash = std::hash()(sig.first); diff --git a/spot/tl/expansions.hh b/spot/tl/expansions.hh index 2418b1103..9a350dbb8 100644 --- a/spot/tl/expansions.hh +++ b/spot/tl/expansions.hh @@ -30,7 +30,7 @@ namespace spot { - using expansion_t = std::multimap; + using expansion_t = std::vector>; struct exp_opts { From b04c4d7fd9212f491ab19d67221a9d218dfec188 Mon Sep 17 00:00:00 2001 From: Antoine Martin Date: Thu, 12 Oct 2023 15:04:40 +0200 Subject: [PATCH 337/337] expansions: expose easy expansion in python --- python/spot/impl.i | 2 ++ spot/tl/expansions.cc | 15 +++++++++++++++ spot/tl/expansions.hh | 3 +++ 3 files changed, 20 insertions(+) diff --git a/python/spot/impl.i b/python/spot/impl.i index c186e8c07..a5a5bf5df 100644 --- a/python/spot/impl.i +++ b/python/spot/impl.i @@ -527,6 +527,8 @@ namespace std { %template(pair_formula_vectorstring) pair>; %template(atomic_prop_set) set; %template(relabeling_map) map; + %template(pair_formula) pair; + %template(vector_pair_formula) vector>; } %include diff --git a/spot/tl/expansions.cc b/spot/tl/expansions.cc index f4dff44eb..3a0a7800e 100644 --- a/spot/tl/expansions.cc +++ b/spot/tl/expansions.cc @@ -449,6 +449,21 @@ namespace spot } } + std::vector> + expansion_simple(formula f) + { + int owner = 42; + auto d = make_bdd_dict(); + + auto exp = expansion(f, d, &owner, exp_opts::None); + + std::vector> res; + for (const auto& [bdd, f] : exp) + res.push_back({bdd_to_formula(bdd, d), f}); + + d->unregister_all_my_variables(&owner); + return res; + } expansion_t expansion(formula f, const bdd_dict_ptr& d, void *owner, exp_opts::expand_opt opts, std::unordered_set* seen) diff --git a/spot/tl/expansions.hh b/spot/tl/expansions.hh index 9a350dbb8..036ac945a 100644 --- a/spot/tl/expansions.hh +++ b/spot/tl/expansions.hh @@ -50,6 +50,9 @@ namespace spot }; }; + SPOT_API std::vector> + expansion_simple(formula f); + SPOT_API expansion_t expansion(formula f, const bdd_dict_ptr& d, void *owner, exp_opts::expand_opt opts, std::unordered_set* seen = nullptr);