From 7cefe30d9774dec4703129727dc99abd733aa9b7 Mon Sep 17 00:00:00 2001 From: philipp Date: Fri, 7 Jan 2022 09:28:11 +0100 Subject: [PATCH] Fixes #495 Monitors can now be split AND completed at the same time. Split can be called on games without providing "synthesis-outputs" - relying on named prop. * spot/twaalgos/synthesis.cc, spot/twaalgos/synthesis.hh: Here * tests/python/_synthesis.ipynb: Testing --- spot/twaalgos/synthesis.cc | 70 +++- spot/twaalgos/synthesis.hh | 12 +- tests/python/_synthesis.ipynb | 764 +++++++++++++++++++++++++++++++++- 3 files changed, 824 insertions(+), 22 deletions(-) diff --git a/spot/twaalgos/synthesis.cc b/spot/twaalgos/synthesis.cc index 27e53b4d8..9025bb303 100644 --- a/spot/twaalgos/synthesis.cc +++ b/spot/twaalgos/synthesis.cc @@ -447,16 +447,28 @@ namespace spot const bdd& output_bdd, bool complete_env) { auto split = make_twa_graph(aut->get_dict()); + + auto [has_unsat, unsat_mark] = aut->acc().unsat_mark(); + split->copy_ap_of(aut); - split->copy_acceptance_of(aut); split->new_states(aut->num_states()); split->set_init_state(aut->get_init_state_number()); - split->set_named_prop("synthesis-outputs", new bdd(output_bdd)); + set_synthesis_outputs(split, output_bdd); - auto [is_unsat, unsat_mark] = aut->acc().unsat_mark(); - if (!is_unsat && complete_env) - throw std::runtime_error("split_2step(): Cannot complete arena for " - "env as there is no unsat mark."); + const auto use_color = has_unsat; + if (has_unsat) + split->copy_acceptance_of(aut); + else + { + if (complete_env) + { + split->set_co_buchi(); // Fin(0) + unsat_mark = acc_cond::mark_t({0}); + has_unsat = true; + } + else + split->acc().set_acceptance(acc_cond::acc_code::t()); + } bdd input_bdd = bddtrue; { @@ -504,9 +516,10 @@ namespace spot unsigned sink_con = -1u; unsigned sink_env = -1u; auto get_sink_con_state = [&split, &sink_con, &sink_env, - um = unsat_mark] + um = unsat_mark, hu = has_unsat] (bool create = true) { + assert(hu); if (SPOT_UNLIKELY((sink_con == -1u) && create)) { sink_con = split->new_state(); @@ -518,7 +531,8 @@ namespace spot }; // Loop over all states - for (unsigned src = 0; src < aut->num_states(); ++src) + const auto n_states = aut->num_states(); + for (unsigned src = 0; src < n_states; ++src) { env_edge_hash.clear(); e_cache.clear(); @@ -579,11 +593,16 @@ namespace spot auto out = split->out(i); if (std::equal(out.begin(), out.end(), dests.begin(), dests.end(), - [](const twa_graph::edge_storage_t& x, + [uc = use_color] + (const twa_graph::edge_storage_t& x, const e_info_t* y) { + // If there is no unsat -> we do not care + // about color + // todo further optim? return x.dst == y->dst - && x.acc == y->acc + && (!uc + || x.acc == y->acc) && x.cond.id() == y->econdout.id(); })) { @@ -592,6 +611,7 @@ 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)); break; @@ -605,7 +625,8 @@ namespace spot env_hash.emplace(h, d); env_edge_hash.emplace(d, eeh_t(n_e, one_letter)); for (const auto &t: dests) - split->new_edge(d, t->dst, t->econdout, t->acc); + split->new_edge(d, t->dst, t->econdout, + use_color ? t->acc : acc_cond::mark_t({})); } } // letters // save locally stored condition @@ -619,21 +640,34 @@ namespace spot // The named property // compute the owners // env is equal to false - std::vector* owner = - new std::vector(split->num_states(), false); + auto owner = std::vector(split->num_states(), false); // All "new" states belong to the player - std::fill(owner->begin()+aut->num_states(), owner->end(), true); + std::fill(owner.begin()+aut->num_states(), owner.end(), true); // Check if sinks have been created if (sink_env != -1u) - owner->at(sink_env) = false; - split->set_named_prop("state-player", owner); - split->set_named_prop("synthesis-outputs", - new bdd(output_bdd)); + owner.at(sink_env) = false; + + // !use_color -> all words accepted + // complete_env && sink_env == -1u + // complet. for env demanded but already + // satisfied -> split is also all true + if (complete_env && sink_env == -1u && !use_color) + split->acc() = acc_cond::acc_code::t(); + + set_state_players(split, std::move(owner)); // Done return split; } + twa_graph_ptr + split_2step(const const_twa_graph_ptr& aut, bool complete_env) + { + return split_2step(aut, + get_synthesis_outputs(aut), + complete_env); + } + twa_graph_ptr unsplit_2step(const const_twa_graph_ptr& aut) { diff --git a/spot/twaalgos/synthesis.hh b/spot/twaalgos/synthesis.hh index 44e6ca7dc..95590504c 100644 --- a/spot/twaalgos/synthesis.hh +++ b/spot/twaalgos/synthesis.hh @@ -52,11 +52,17 @@ namespace spot /// \param complete_env Whether the automaton should be complete for the /// environment, i.e. the player of inputs /// \note This function also computes the state players - /// \note If the automaton is to be completed for both env and player - /// then egdes between the sinks will be added + /// \note If the automaton is to be completed, sink states will + /// be added for both env and player if necessary SPOT_API twa_graph_ptr split_2step(const const_twa_graph_ptr& aut, - const bdd& output_bdd, bool complete_env); + const bdd& output_bdd, bool complete_env = true); + + /// \ingroup synthesis + /// \brief Like split_2step but relying on the named property + /// 'synthesis-outputs' + SPOT_API twa_graph_ptr + split_2step(const const_twa_graph_ptr& aut, bool complete_env = true); /// \ingroup synthesis /// \brief the inverse of split_2step diff --git a/tests/python/_synthesis.ipynb b/tests/python/_synthesis.ipynb index 1ed59760d..c99235f71 100644 --- a/tests/python/_synthesis.ipynb +++ b/tests/python/_synthesis.ipynb @@ -6,7 +6,7 @@ "metadata": {}, "outputs": [], "source": [ - "import spot\n", + "import spot, buddy\n", "spot.setup()" ] }, @@ -389,6 +389,768 @@ "assert(mmus3.equivalent_to(msep3))\n", "assert(mmus3.equivalent_to(mus3))" ] + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Testing related to #495" + ] + }, + { + "cell_type": "code", + "execution_count": 6, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "(0, t)\n" + ] + }, + { + "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", + "0\n", + "\n", + "0\n", + "\n", + "\n", + "\n", + "1->0\n", + "\n", + "\n", + "(!i & !o) | (i & o)\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n" + ], + "text/plain": [ + " *' at 0x7fee3716f7b0> >" + ] + }, + "execution_count": 6, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "a = spot.translate(\"i<->o\", \"parity\")\n", + "print(a.acc())\n", + "a" + ] + }, + { + "cell_type": "code", + "execution_count": 7, + "metadata": {}, + "outputs": [ + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "\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", + "\n", + "!i\n", + "/\n", + "\n", + "!o\n", + "\n", + "i\n", + "/\n", + "\n", + "o\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "\n", + "1\n", + "/\n", + "\n", + "1\n", + "\n", + "\n", + "\n" + ], + "text/plain": [ + " *' at 0x7fee3716f7b0> >" + ] + }, + "execution_count": 7, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "spot.set_synthesis_outputs(a, buddy.bdd_ithvar(a.register_ap(\"o\")))\n", + "a" + ] + }, + { + "cell_type": "code", + "execution_count": 8, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "(0, t)\n" + ] + }, + { + "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", + "3\n", + "\n", + "3\n", + "\n", + "\n", + "\n", + "1->3\n", + "\n", + "\n", + "!i\n", + "\n", + "\n", + "\n", + "4\n", + "\n", + "4\n", + "\n", + "\n", + "\n", + "1->4\n", + "\n", + "\n", + "i\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0\n", + "\n", + "\n", + "\n", + "2\n", + "\n", + "2\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", + "!o\n", + "\n", + "\n", + "\n", + "4->0\n", + "\n", + "\n", + "o\n", + "\n", + "\n", + "\n" + ], + "text/plain": [ + " *' at 0x7fee3716f630> >" + ] + }, + "execution_count": 8, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "a_s = spot.split_2step(a, True)\n", + "print(a.acc())\n", + "a_s" + ] + }, + { + "cell_type": "code", + "execution_count": 9, + "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", + "i0\n", + "/\n", + "\n", + "o\n", + "\n", + "\n", + "\n", + "\n", + "1->0\n", + "\n", + "\n", + "\n", + "i1\n", + "/\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "\n", + "2\n", + "\n", + "2\n", + "\n", + "\n", + "\n", + "1->2\n", + "\n", + "\n", + "\n", + "i0\n", + "/\n", + "\n", + "!o\n", + "\n", + "\n", + "\n", + "\n" + ], + "text/plain": [ + " *' at 0x7fee36703f60> >" + ] + }, + "execution_count": 9, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "a = spot.make_twa_graph()\n", + "a.acc().set_acceptance(spot.acc_code.t())\n", + "i0 = buddy.bdd_ithvar(a.register_ap('i0'))\n", + "i1 = buddy.bdd_ithvar(a.register_ap('i1'))\n", + "o = buddy.bdd_ithvar(a.register_ap('o'))\n", + "tt = buddy.bddtrue\n", + "a0 = spot.mark_t([0])\n", + "a.new_states(3)\n", + "a.new_edge(0,1,i0&o,a0)\n", + "a.new_edge(1,0,i1,a0)\n", + "a.new_edge(1,2,i0&(buddy.bdd_not(o)),a0)\n", + "spot.set_synthesis_outputs(a, o)\n", + "a" + ] + }, + { + "cell_type": "code", + "execution_count": 10, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "(0, t)\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", + "i0\n", + "\n", + "\n", + "\n", + "1\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "3->1\n", + "\n", + "\n", + "o\n", + "\n", + "\n", + "\n", + "4\n", + "\n", + "4\n", + "\n", + "\n", + "\n", + "1->4\n", + "\n", + "\n", + "!i0 & i1\n", + "\n", + "\n", + "\n", + "5\n", + "\n", + "5\n", + "\n", + "\n", + "\n", + "1->5\n", + "\n", + "\n", + "i0 & !i1\n", + "\n", + "\n", + "\n", + "6\n", + "\n", + "6\n", + "\n", + "\n", + "\n", + "1->6\n", + "\n", + "\n", + "i0 & i1\n", + "\n", + "\n", + "\n", + "4->0\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "2\n", + "\n", + "2\n", + "\n", + "\n", + "\n", + "5->2\n", + "\n", + "\n", + "!o\n", + "\n", + "\n", + "\n", + "6->0\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "6->2\n", + "\n", + "\n", + "!o\n", + "\n", + "\n", + "\n" + ], + "text/plain": [ + " *' at 0x7fee3716f930> >" + ] + }, + "execution_count": 10, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "a_snc = spot.split_2step(a, False)\n", + "print(a_snc.acc())\n", + "a_snc" + ] + }, + { + "cell_type": "code", + "execution_count": 11, + "metadata": {}, + "outputs": [ + { + "name": "stdout", + "output_type": "stream", + "text": [ + "(1, Fin(0))\n" + ] + }, + { + "data": { + "image/svg+xml": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\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", + "3\n", + "\n", + "3\n", + "\n", + "\n", + "\n", + "0->3\n", + "\n", + "\n", + "!i0\n", + "\n", + "\n", + "\n", + "5\n", + "\n", + "5\n", + "\n", + "\n", + "\n", + "0->5\n", + "\n", + "\n", + "i0\n", + "\n", + "\n", + "\n", + "4\n", + "\n", + "4\n", + "\n", + "\n", + "\n", + "3->4\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "\n", + "1\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "5->1\n", + "\n", + "\n", + "o\n", + "\n", + "\n", + "\n", + "1->3\n", + "\n", + "\n", + "!i0 & !i1\n", + "\n", + "\n", + "\n", + "6\n", + "\n", + "6\n", + "\n", + "\n", + "\n", + "1->6\n", + "\n", + "\n", + "!i0 & i1\n", + "\n", + "\n", + "\n", + "7\n", + "\n", + "7\n", + "\n", + "\n", + "\n", + "1->7\n", + "\n", + "\n", + "i0 & !i1\n", + "\n", + "\n", + "\n", + "8\n", + "\n", + "8\n", + "\n", + "\n", + "\n", + "1->8\n", + "\n", + "\n", + "i0 & i1\n", + "\n", + "\n", + "\n", + "6->0\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "2\n", + "\n", + "2\n", + "\n", + "\n", + "\n", + "7->2\n", + "\n", + "\n", + "!o\n", + "\n", + "\n", + "\n", + "8->0\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "8->2\n", + "\n", + "\n", + "!o\n", + "\n", + "\n", + "\n", + "2->3\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "4->3\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "\n", + "\n" + ], + "text/plain": [ + " *' at 0x7fee3716fa20> >" + ] + }, + "execution_count": 11, + "metadata": {}, + "output_type": "execute_result" + } + ], + "source": [ + "a_s = spot.split_2step(a, True)\n", + "print(a_s.acc())\n", + "a_s" + ] } ], "metadata": {