diff --git a/doc/org/autfilt.org b/doc/org/autfilt.org index 90ddb9817..d8556b013 100644 --- a/doc/org/autfilt.org +++ b/doc/org/autfilt.org @@ -260,8 +260,11 @@ autfilt --help | sed -n '/Transformations:/,/^$/p' | sed '1d;$d' --product=FILENAME build the product with the automaton in FILENAME --randomize[=s|t] randomize states and transitions (specify 's' or 't' to randomize only states or transitions) - --remove-fin rewrite the automaton without using Fin acceptance - + --remove-ap=AP[=0|=1][,AP...] + remove atomic propositions either by existential + quantification, or by assigning them 0 or 1 + --remove-fin rewrite the automaton without using Fin + acceptance --state-based-acceptance, --sbacc define the acceptance using states --strip-acceptance remove the acceptance condition and all acceptance @@ -531,3 +534,117 @@ $txt #+RESULTS: [[file:autfilt-ex5.png]] + + +Atomic propositions can be removed from an automaton in three ways: +- use ~--remove-ap=a~ to remove =a= by existential quantification, i.e., both =a= and its negation will be replaced by true. + This does not remove any transition. +- use ~--remove-ap=a=0~ to keep only transitions compatible with =!a= (i.e, transitions requiring =a= will be removed). +- use ~--remove-ap=a=1~ to keep only transitions compatible with =a= (i.e, transitions requiring =!a= will be removed). + +Here are the results of these three options on our example: + +#+NAME: autfilt-ex6a +#+BEGIN_SRC sh :results verbatim :export code +autfilt --remove-ap=a aut-ex1.hoa --dot=.a +#+END_SRC + +#+RESULTS: autfilt-ex6a +#+begin_example +digraph G { + rankdir=LR + label=<(Fin() & Fin() & Inf()) | (Inf()&Inf()) | Inf()> + labelloc="t" + fontname="Lato" + node [fontname="Lato"] + edge [fontname="Lato"] + node[style=filled, fillcolor="#ffffa0"] + I [label="", style=invis, width=0] + I -> 0 + 0 [label="0"] + 0 -> 0 [label=<1
>] + 0 -> 1 [label=<1
>] + 0 -> 2 [label=<1
>] + 1 [label="1"] + 1 -> 0 [label=>] + 1 -> 1 [label=>] + 1 -> 2 [label=>] + 2 [label="2"] + 2 -> 0 [label=] + 2 -> 1 [label=>] + 2 -> 2 [label=>] +} +#+end_example + +#+BEGIN_SRC dot :file autfilt-ex6a.png :cmdline -Tpng :var txt=autfilt-ex6a :exports results +$txt +#+END_SRC + +#+RESULTS: +[[file:autfilt-ex6a.png]] + +#+NAME: autfilt-ex6b +#+BEGIN_SRC sh :results verbatim :export code +autfilt --remove-ap=a=0 aut-ex1.hoa --dot=.a +#+END_SRC + +#+RESULTS: autfilt-ex6b +#+begin_example +digraph G { + rankdir=LR + label=<(Fin() & Fin() & Inf()) | (Inf()&Inf()) | Inf()> + labelloc="t" + fontname="Lato" + node [fontname="Lato"] + edge [fontname="Lato"] + node[style=filled, fillcolor="#ffffa0"] + I [label="", style=invis, width=0] + I -> 0 + 0 [label="0"] + 0 -> 0 [label=<1
>] + 0 -> 1 [label=<1
>] + 1 [label="1"] + 1 -> 0 [label=] + 1 -> 1 [label=>] +} +#+end_example + +#+BEGIN_SRC dot :file autfilt-ex6b.png :cmdline -Tpng :var txt=autfilt-ex6b :exports results +$txt +#+END_SRC + +#+RESULTS: +[[file:autfilt-ex6b.png]] + +#+NAME: autfilt-ex6c +#+BEGIN_SRC sh :results verbatim :export code +autfilt --remove-ap=a=1 aut-ex1.hoa --dot=.a +#+END_SRC + +#+RESULTS: autfilt-ex6c +#+begin_example +digraph G { + rankdir=LR + label=<(Fin() & Fin() & Inf()) | (Inf()&Inf()) | Inf()> + labelloc="t" + fontname="Lato" + node [fontname="Lato"] + edge [fontname="Lato"] + node[style=filled, fillcolor="#ffffa0"] + I [label="", style=invis, width=0] + I -> 0 + 0 [label="0"] + 0 -> 0 [label=<1
>] + 0 -> 1 [label=<1
>] + 1 [label="1"] + 1 -> 0 [label=>] + 1 -> 1 [label=>] +} +#+end_example + +#+BEGIN_SRC dot :file autfilt-ex6c.png :cmdline -Tpng :var txt=autfilt-ex6c :exports results +$txt +#+END_SRC + +#+RESULTS: +[[file:autfilt-ex6c.png]] diff --git a/src/bin/autfilt.cc b/src/bin/autfilt.cc index 3807244c4..d17fa3aa8 100644 --- a/src/bin/autfilt.cc +++ b/src/bin/autfilt.cc @@ -44,6 +44,7 @@ #include "misc/random.hh" #include "hoaparse/public.hh" #include "ltlvisit/exclusive.hh" +#include "tgbaalgos/remprop.hh" #include "tgbaalgos/randomize.hh" #include "tgbaalgos/are_isomorphic.hh" #include "tgbaalgos/canonicalize.hh" @@ -83,6 +84,7 @@ enum { OPT_MERGE, OPT_PRODUCT, OPT_RANDOMIZE, + OPT_REM_AP, OPT_REM_FIN, OPT_SBACC, OPT_SEED, @@ -145,6 +147,9 @@ static const argp_option options[] = "ensure two of them may not be true at the same time. Use this option " "multiple times to declare independent groups of exclusive " "propositions.", 0 }, + { "remove-ap", OPT_REM_AP, "AP[=0|=1][,AP...]", 0, + "remove atomic propositions either by existential quantification, or " + "by assigning them 0 or 1", 0 }, /**************************************************/ { 0, 0, 0, 0, "Filtering options:", 6 }, { "are-isomorphic", OPT_ARE_ISOMORPHIC, "FILENAME", 0, @@ -233,6 +238,7 @@ static spot::acc_cond::mark_t opt_mask_acc = 0U; static std::vector opt_keep_states = {}; static unsigned int opt_keep_states_initial = 0; static spot::exclusive_ap excl_ap; +static spot::remove_ap rem_ap; static int parse_opt(int key, char* arg, struct argp_state*) @@ -385,6 +391,9 @@ parse_opt(int key, char* arg, struct argp_state*) randomize_st = true; } break; + case OPT_REM_AP: + rem_ap.add_ap(arg); + break; case OPT_REM_FIN: opt_rem_fin = true; break; @@ -504,6 +513,9 @@ namespace if (!excl_ap.empty()) aut = excl_ap.constrain(aut); + if (!rem_ap.empty()) + aut = rem_ap.strip(aut); + if (opt_destut) aut = spot::closure(std::move(aut)); if (opt_instut == 1) @@ -586,38 +598,38 @@ main(int argc, char** argv) const argp ap = { options, parse_opt, "[FILENAMES...]", argp_program_doc, children, 0, 0 }; - // This will ensure that all objects stored in this struct are - // destroyed before global variables. - opt_t o; - opt = &o; - - // Disable post-processing as much as possible by default. - level = spot::postprocessor::Low; - pref = spot::postprocessor::Any; - if (int err = argp_parse(&ap, argc, argv, ARGP_NO_HELP, 0, 0)) - exit(err); - - if (jobs.empty()) - jobs.emplace_back("-", true); - - if (opt->are_isomorphic) - { - if (opt_merge) - opt->are_isomorphic->merge_transitions(); - opt->isomorphism_checker = std::unique_ptr - (new spot::isomorphism_checker(opt->are_isomorphic)); - } - - - spot::srand(opt_seed); - - spot::postprocessor post(&extra_options); - post.set_pref(pref | comp); - post.set_type(type); - post.set_level(level); - try { + // This will ensure that all objects stored in this struct are + // destroyed before global variables. + opt_t o; + opt = &o; + + // Disable post-processing as much as possible by default. + level = spot::postprocessor::Low; + pref = spot::postprocessor::Any; + if (int err = argp_parse(&ap, argc, argv, ARGP_NO_HELP, 0, 0)) + exit(err); + + if (jobs.empty()) + jobs.emplace_back("-", true); + + if (opt->are_isomorphic) + { + if (opt_merge) + opt->are_isomorphic->merge_transitions(); + opt->isomorphism_checker = std::unique_ptr + (new spot::isomorphism_checker(opt->are_isomorphic)); + } + + + spot::srand(opt_seed); + + spot::postprocessor post(&extra_options); + post.set_pref(pref | comp); + post.set_type(type); + post.set_level(level); + hoa_processor processor(post); if (processor.run()) return 2; @@ -626,6 +638,10 @@ main(int argc, char** argv) { error(2, 0, "%s", e.what()); } + catch (const std::invalid_argument& e) + { + error(2, 0, "%s", e.what()); + } if (automaton_format == Count) std::cout << match_count << std::endl; diff --git a/src/tgbaalgos/Makefile.am b/src/tgbaalgos/Makefile.am index 43234d711..56ca96595 100644 --- a/src/tgbaalgos/Makefile.am +++ b/src/tgbaalgos/Makefile.am @@ -64,6 +64,7 @@ tgbaalgos_HEADERS = \ reducerun.hh \ relabel.hh \ remfin.hh \ + remprop.hh \ replayrun.hh \ safety.hh \ sbacc.hh \ @@ -117,6 +118,7 @@ libtgbaalgos_la_SOURCES = \ reachiter.cc \ reducerun.cc \ remfin.cc \ + remprop.cc \ replayrun.cc \ relabel.cc \ safety.cc \ diff --git a/src/tgbaalgos/remprop.cc b/src/tgbaalgos/remprop.cc new file mode 100644 index 000000000..9be2b3c95 --- /dev/null +++ b/src/tgbaalgos/remprop.cc @@ -0,0 +1,188 @@ +// -*- coding: utf-8 -*- +// Copyright (C) 2015 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 "remprop.hh" +#include "ltlenv/defaultenv.hh" +#include "tgbaalgos/mask.hh" +#include "misc/casts.hh" +#include "ltlvisit/tostring.hh" +#include +#include + +namespace spot +{ + remove_ap::~remove_ap() + { + for (auto& ap: props_exist) + ap->destroy(); + for (auto& ap: props_pos) + ap->destroy(); + for (auto& ap: props_neg) + ap->destroy(); + } + + namespace + { + static + void unexpected_char(const char* arg, const char* pos) + { + std::ostringstream out; + out << "unexpected "; + if (isprint(*pos)) + out << '\'' << *pos << '\''; + else + out << "character"; + out << " at position " << pos - arg << " in '"; + out << arg << '\''; + throw std::invalid_argument(out.str()); + } + } + + + void remove_ap::add_ap(const char* arg) + { + auto& env = spot::ltl::default_environment::instance(); + std::vector group; + auto start = arg; + while (*start) + { + while (*start == ' ' || *start == '\t') + ++start; + if (!*start) + break; + if (*start == ',' || *start == '=') + unexpected_char(arg, start); + const spot::ltl::atomic_prop* the_ap = nullptr; + + if (*start == '"') + { + auto end = ++start; + while (*end && *end != '"') + { + if (*end == '\\') + ++end; + ++end; + } + if (!*end) + { + std::string s = "missing closing '\"' in "; + s += arg; + throw std::invalid_argument(s); + } + std::string ap(start, end - start); + auto* t = env.require(ap); + the_ap = down_cast(t); + do + ++end; + while (*end == ' ' || *end == '\t'); + start = end; + } + else + { + auto end = start; + while (*end && *end != ',' && *end != '=') + ++end; + auto rend = end; + while (rend > start && (rend[-1] == ' ' || rend[-1] == '\t')) + --rend; + std::string ap(start, rend - start); + auto* t = env.require(ap); + the_ap = down_cast(t); + start = end; + } + if (*start) + { + if (!(*start == ',' || *start == '=')) + unexpected_char(arg, start); + if (*start == '=') + { + do + ++start; + while (*start == ' ' || *start == '\t'); + if (*start == '0') + props_neg.insert(the_ap); + else if (*start == '1') + props_pos.insert(the_ap); + else + unexpected_char(arg, start); + the_ap = nullptr; + do + ++start; + while (*start == ' ' || *start == '\t'); + } + if (*start) + { + if (*start != ',') + unexpected_char(arg, start); + ++start; + } + } + if (the_ap) + props_exist.insert(the_ap); + } + } + + tgba_digraph_ptr remove_ap::strip(const_tgba_digraph_ptr aut) const + { + bdd restrict = bddtrue; + bdd exist = bddtrue; + auto d = aut->get_dict(); + + tgba_digraph_ptr res = make_tgba_digraph(d); + res->copy_ap_of(aut); + res->prop_copy(aut, { true, true, false, false }); + res->copy_acceptance_of(aut); + + for (auto ap: props_exist) + { + int v = d->has_registered_proposition(ap, aut); + if (v >= 0) + { + exist &= bdd_ithvar(v); + d->unregister_variable(v, res); + } + } + for (auto ap: props_pos) + { + int v = d->has_registered_proposition(ap, aut); + if (v >= 0) + { + restrict &= bdd_ithvar(v); + d->unregister_variable(v, res); + } + } + for (auto ap: props_neg) + { + int v = d->has_registered_proposition(ap, aut); + if (v >= 0) + { + restrict &= bdd_nithvar(v); + d->unregister_variable(v, res); + } + } + + transform_accessible(aut, res, [&](unsigned, bdd& cond, + acc_cond::mark_t&, unsigned) + { + cond = bdd_restrict(bdd_exist(cond, exist), + restrict); + }); + return res; + } +} diff --git a/src/tgbaalgos/remprop.hh b/src/tgbaalgos/remprop.hh new file mode 100644 index 000000000..dbd72bed8 --- /dev/null +++ b/src/tgbaalgos/remprop.hh @@ -0,0 +1,44 @@ +// -*- coding: utf-8 -*- +// Copyright (C) 2015 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 "ltlast/atomic_prop.hh" +#include "tgba/tgbagraph.hh" + +namespace spot +{ + class SPOT_API remove_ap + { + std::set props_exist; + std::set props_pos; + std::set props_neg; + public: + ~remove_ap(); + void add_ap(const char* ap_csv); + + bool empty() const + { + return props_exist.empty() && props_pos.empty() && props_neg.empty(); + } + + tgba_digraph_ptr strip(const_tgba_digraph_ptr aut) const; + }; +} diff --git a/src/tgbatest/Makefile.am b/src/tgbatest/Makefile.am index 41b734ad6..07968d46c 100644 --- a/src/tgbatest/Makefile.am +++ b/src/tgbatest/Makefile.am @@ -92,6 +92,7 @@ TESTS = \ tripprod.test \ dupexp.test \ exclusive.test \ + remprop.test \ degendet.test \ degenid.test \ degenlskip.test \ diff --git a/src/tgbatest/remprop.test b/src/tgbatest/remprop.test new file mode 100755 index 000000000..bf07fe053 --- /dev/null +++ b/src/tgbatest/remprop.test @@ -0,0 +1,97 @@ +#! /bin/sh +# -*- coding: utf-8 -*- +# Copyright (C) 2015 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 + +cat >automaton <expected <out +cat out +diff out expected + +cat >expected <out +cat out +diff out expected + +../../bin/autfilt -H --remove-ap=a==1 automaton 2>stderr && exit 1 +grep "autfilt: unexpected '=' at position 2 in 'a==1'" stderr