// -*- coding: utf-8 -*- // Copyright (C) 2017-2018 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 namespace spot { namespace gen { namespace { static twa_graph_ptr ks_nca(unsigned n, bdd_dict_ptr dict) { if (n == 0) throw std::runtime_error("ks_nca expects a positive argument"); // the alphabet has four letters: // i, s (for sigma), p (for pi), h (for hash) // we encode this four letters alphabet thanks to two AP a and b // the exact encoding is not important // each letter is a permutation of the set {1..2n} // s = (1 2 .. 2n) the rotation // p = (1 2) the swap of the first two elements // i is the identity // d is the identity on {2..2n} but is undefined on 1 // the automaton has 2n+1 states, numbered from 0 to 2n // 0 is the initial state and the only non-deterministic state auto aut = make_twa_graph(dict); // register aps bdd a = bdd_ithvar(aut->register_ap("a")); bdd b = bdd_ithvar(aut->register_ap("b")); // name the four letters bdd i = a & b; bdd s = a & (!b); bdd p = (!a) & b; bdd h = (!a) & (!b); // actually build the automaton aut->new_states(2*n+1); aut->set_init_state(0); aut->set_acceptance(1, acc_cond::acc_code::cobuchi()); // from 0, we can non-deterministically jump to any state // (except 0) with any letter. for (unsigned q = 1; q <= 2*n; ++q) aut->new_edge(0, q, bddtrue, {0}); // i is the identity for (unsigned q = 1; q <= 2*n; ++q) aut->new_edge(q, q, i); // p swaps 1 and 2, and leaves all other states invariant aut->new_edge(1, 2, p); aut->new_edge(2, 1, p); for (unsigned q = 3; q <= 2*n; ++q) aut->new_edge(q, q, p); // s goes to next state (mod 2*n, 0 excluded) aut->new_edge(2*n, 1, s); for (unsigned q = 1; q < 2*n; ++q) aut->new_edge(q, q+1, s); // h is the same as i, except on 1 where it goes back to the // initial state aut->new_edge(1, 0, h); for (unsigned q = 2; q <= 2*n; ++q) aut->new_edge(q, q, h); aut->merge_edges(); aut->prop_state_acc(true); aut->prop_universal(false); aut->prop_complete(true); aut->prop_inherently_weak(false); aut->prop_stutter_invariant(false); aut->prop_semi_deterministic(false); return aut; } static twa_graph_ptr l_nba(unsigned n, bdd_dict_ptr dict) { if (n == 0) throw std::runtime_error("l_nba expects a positive argument"); auto aut = make_twa_graph(dict); bdd p1 = bdd_ithvar(aut->register_ap("a")); bdd p2 = bdd_ithvar(aut->register_ap("b")); bdd a = p1 - p2; bdd b = p2 - p1; bdd s = p1 & p2; bdd all = p1 | p2; aut->new_states(3 * n + 1); aut->set_init_state(1); aut->set_buchi(); aut->new_acc_edge(0, n + 1, a); aut->new_edge(2 * n + 1, 0, b); for (unsigned s = 1; s <= n; ++s) { aut->new_edge(s + n, std::min(s + n + 1, 2 * n), a); aut->new_edge(s + n, s, b); aut->new_edge(s, s, all); aut->new_edge(s, s + 2 * n, a); aut->new_edge(std::min(s + 2 * n + 1, 3 * n), s + 2 * n, a); } aut->prop_state_acc(true); aut->prop_universal(false); aut->prop_complete(false); aut->prop_inherently_weak(false); aut->prop_stutter_invariant(false); aut->prop_semi_deterministic(false); return aut; } static twa_graph_ptr l_dsa(unsigned n, bdd_dict_ptr dict) { if (n < 1 || n > 16) throw std::runtime_error("l_dsa expects 1 <= n <= 16"); auto aut = make_twa_graph(dict); bdd a = bdd_ithvar(aut->register_ap("a")); bdd b = !a; aut->set_acceptance(2 * n, acc_cond::acc_code::streett(n)); aut->new_states(4 * n); aut->set_init_state(0); for (unsigned s = 0; s < n; ++s) { unsigned col1 = 4 * s; unsigned col2 = 4 * s + 1; unsigned col3 = 4 * s + 2; unsigned col4 = 4 * s + 3; aut->new_edge(col1, 2, a, {2 * s}); aut->new_edge(col2, col1, b); aut->new_edge(col2, std::min(col2 + 4, 4 * n - 3), a); aut->new_edge(col3, col4, b); aut->new_edge(col3, std::min(col3 + 4, 4 * n - 2), a); aut->new_edge(col4, 1, a, {2 * s + 1}); } aut->prop_state_acc(true); aut->prop_universal(true); aut->prop_complete(false); aut->prop_inherently_weak(false); aut->prop_stutter_invariant(false); aut->prop_semi_deterministic(true); 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"; throw std::runtime_error(err.str()); } switch (pattern) { // Keep this alphabetically-ordered! case AUT_KS_NCA: return ks_nca(n, dict); case AUT_L_NBA: return l_nba(n, dict); case AUT_L_DSA: return l_dsa(n, dict); case AUT_END: break; } throw std::runtime_error("unsupported pattern"); } const char* aut_pattern_name(aut_pattern_id pattern) { static const char* const class_name[] = { "ks-nca", "l-nba", "l-dsa", }; // Make sure we do not forget to update the above table every // time a new pattern is added. static_assert(sizeof(class_name)/sizeof(*class_name) == AUT_END - AUT_BEGIN, "size mismatch"); if (pattern < AUT_BEGIN || pattern >= AUT_END) throw std::runtime_error("unsupported pattern"); return class_name[pattern - AUT_BEGIN]; } } }