From 488efee7b3c1391116eb1effa61c8d5ee1b10de5 Mon Sep 17 00:00:00 2001 From: Philipp Schlehuber Date: Mon, 22 Nov 2021 14:14:28 +0100 Subject: [PATCH] aiger accepts splitted mealy machines * spot/twaalgos/aiger.cc, spot/twaalgos/aiger.hh: Here * tests/python/synthesis.ipynb: Tests --- spot/twaalgos/aiger.cc | 106 +++++++--- spot/twaalgos/aiger.hh | 4 +- tests/python/synthesis.ipynb | 384 +++++++++++++++++++++++++++++++++-- 3 files changed, 456 insertions(+), 38 deletions(-) diff --git a/spot/twaalgos/aiger.cc b/spot/twaalgos/aiger.cc index ae7b665e9..d846e678c 100644 --- a/spot/twaalgos/aiger.cc +++ b/spot/twaalgos/aiger.cc @@ -1379,7 +1379,8 @@ namespace using namespace spot; // We have to decide which actual output to use: - // Heuristic : Use the assignment having the least highs + // Heuristic : Use the assignment having the smallest cost + // according to satoneshortest static std::vector> choose_outc(const std::vector>& strat_vec, @@ -1387,18 +1388,44 @@ namespace { std::vector> used_outc; - for (auto&& astrat : strat_vec) + for (const auto& astrat : strat_vec) { used_outc.emplace_back(astrat.first->num_edges()+1); auto& this_outc = used_outc.back(); - for (auto&& e: astrat.first->edges()) + // Check if split or separated + if (auto sp_ptr = + astrat.first->get_named_prop("state-player")) { - assert(e.cond != bddfalse); - bdd bout = bdd_exist(e.cond, all_inputs); - // low is good, dc is ok, high is bad - this_outc[astrat.first->edge_number(e)] = - bdd_satoneshortest(bout, 0, 1, 5); + // Split -> We only need outs for env edges + auto sp = *sp_ptr; + const unsigned N = astrat.first->num_states(); + for (unsigned s = 0; s < N; ++s) + { + if (sp[s]) + continue; //Player state -> nothing to do + for (const auto& e : astrat.first->out(s)) + { + assert(e.cond != bddfalse); + bdd bout = astrat.first->out(e.dst).begin()->cond; + assert(bout != bddfalse); + // low is good, dc is ok, high is bad + this_outc[astrat.first->edge_number(e)] = + bdd_satoneshortest(bout, 0, 1, 5); + } + } + } + else + { + // separated + for (const auto& e: astrat.first->edges()) + { + assert(e.cond != bddfalse); + bdd bout = bdd_exist(e.cond, all_inputs); + // low is good, dc is ok, high is bad + this_outc[astrat.first->edge_number(e)] = + bdd_satoneshortest(bout, 0, 1, 5); + } } } //Done @@ -1409,6 +1436,7 @@ namespace state_to_vec(std::vector& v, unsigned s, unsigned offset) { + assert(s != -1u && "-1u is not a valid sstate"); v.clear(); unsigned i = offset; while (s > 0) @@ -1466,8 +1494,10 @@ namespace } // This checks the acc again, but does more and is to // costly for non-debug mode + // We can also handle split machines assert(std::all_of(strat_vec.begin(), strat_vec.end(), - [](const auto& p){ return is_separated_mealy(p.first); })); + [](const auto& p){ return is_separated_mealy(p.first) + || is_split_mealy(p.first); })); // get the propositions std::vector input_names; @@ -1558,30 +1588,48 @@ namespace unsigned n_latches = 0; for (const auto& astrat : strat_vec) { - state_numbers.emplace_back(); - state_numbers.back().reserve(astrat.first->num_states()); + const unsigned N = astrat.first->num_states(); + state_numbers.emplace_back(N, -1u); + auto& sn = state_numbers.back(); unsigned max_index = 0; // Check if named + auto sp_ptr = astrat.first->get_named_prop("state-player"); if (const auto* s_names = astrat.first-> get_named_prop>("state-names")) { - std::transform(s_names->cbegin(), s_names->cend(), - std::back_inserter(state_numbers.back()), - [&max_index](const auto& sn) - { - unsigned su = std::stoul(sn); - max_index = std::max(max_index, su); - return su; - }); + for (unsigned n = 0; n < N; ++n) + { + if (sp_ptr && (*sp_ptr)[n]) + continue; + // Remains -1u + unsigned su = std::stoul((*s_names)[n]); + max_index = std::max(max_index, su); + sn[n] = su; + } ++max_index; } else { - max_index = astrat.first->num_states(); - state_numbers.back().resize(astrat.first->num_states()); - std::iota(state_numbers.back().begin(), - state_numbers.back().end(), 0); + if (sp_ptr) + { + auto sp = *sp_ptr; + // Split + unsigned n_next = 0; + // Player -1u + // Env: Succesively numbered according to appearance + for (unsigned n = 0; n < N; ++n) + if (!sp[n]) + sn[n] = n_next++; + max_index = n_next; + } + else + { + std::iota(state_numbers.back().begin(), + state_numbers.back().end(), 0); + max_index = N; + } + // Ensure 0 <-> init state std::swap(state_numbers.back()[0], state_numbers.back()[astrat.first-> @@ -1621,6 +1669,8 @@ namespace auto&& [astrat, aouts] = strat_vec[i]; const auto& sn = state_numbers.at(i); + auto sp_ptr = astrat->get_named_prop("state-player"); + auto latchoff = latch_offset[i]; #ifndef NDEBUG auto latchoff_next = latch_offset.at(i+1); @@ -1628,6 +1678,8 @@ namespace auto alog2n = log2n[i]; auto state2bdd = [&](auto s) { + assert((!sp_ptr || !(*sp_ptr)[s]) + && "Only env states need to be encoded."); auto s2 = sn[s]; bdd b = bddtrue; for (unsigned j = 0; j < alog2n; ++j) @@ -1654,13 +1706,19 @@ namespace for (unsigned s = 0; s < astrat->num_states(); ++s) { + // Player state -> continue + if (sp_ptr && (*sp_ptr)[s]) + continue; // Convert src state to bdd bdd src_bdd = state2bdd(s); for (const auto& e : astrat->out(s)) { // High latches of dst - state_to_vec(next_state_vec, sn[e.dst], latchoff); + state_to_vec(next_state_vec, + sp_ptr ? sn[astrat->out(e.dst).begin()->dst] + : sn[e.dst], + latchoff); // Get high outs depending on cond output_to_vec(out_vec, out_dc_vec, diff --git a/spot/twaalgos/aiger.hh b/spot/twaalgos/aiger.hh index 707f1bc3c..c89ae1ecb 100644 --- a/spot/twaalgos/aiger.hh +++ b/spot/twaalgos/aiger.hh @@ -452,9 +452,9 @@ namespace spot ///@} /// \ingroup synthesis - /// \brief Convert multiple strategies into an aig relying on + /// \brief Convert multiple mealy machines into an aig relying on /// the transformation described by mode. - /// \note The states of each strategy are represented by a block of latches + /// \note The states of each machine are represented by a block of latches /// not affected by the others. For this to work in a general setting, /// the outputs must be disjoint. /// diff --git a/tests/python/synthesis.ipynb b/tests/python/synthesis.ipynb index 91f526508..be001a9b3 100644 --- a/tests/python/synthesis.ipynb +++ b/tests/python/synthesis.ipynb @@ -698,7 +698,7 @@ "\n" ], "text/plain": [ - " *' at 0x7fe9bc79b6f0> >" + " *' at 0x7ff27d57dc90> >" ] }, "metadata": {}, @@ -2718,7 +2718,7 @@ "\n" ], "text/plain": [ - " >" + " >" ] }, "metadata": {}, @@ -3039,7 +3039,7 @@ "\n" ], "text/plain": [ - " *' at 0x7fe9bc6a8360> >" + " *' at 0x7ff27ca90390> >" ] }, "metadata": {}, @@ -3114,7 +3114,7 @@ "\n" ], "text/plain": [ - " *' at 0x7fe9bd884390> >" + " *' at 0x7ff27d57dd20> >" ] }, "metadata": {}, @@ -3191,7 +3191,7 @@ "\n" ], "text/plain": [ - " >" + " >" ] }, "metadata": {}, @@ -3318,7 +3318,7 @@ "\n" ], "text/plain": [ - " >" + " >" ] }, "metadata": {}, @@ -3345,7 +3345,7 @@ }, { "cell_type": "code", - "execution_count": 17, + "execution_count": 11, "id": "4f9be142", "metadata": {}, "outputs": [ @@ -3753,7 +3753,7 @@ "\n" ], "text/plain": [ - " >" + " >" ] }, "metadata": {}, @@ -3926,7 +3926,7 @@ "\n" ], "text/plain": [ - " >" + " >" ] }, "metadata": {}, @@ -4050,7 +4050,7 @@ "\n" ], "text/plain": [ - " *' at 0x7fe9bc6a8330> >" + " *' at 0x7ff27ca90cf0> >" ] }, "execution_count": 16, @@ -4061,11 +4061,371 @@ "source": [ "this_aig.as_automaton()" ] + }, + { + "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." + ] + }, + { + "cell_type": "code", + "execution_count": 17, + "id": "fcf3b73e", + "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", + "(!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", + "
\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", + "(!i0 & !i1) | (i0 & i1)\n", + "\n", + "\n", + "\n", + "2\n", + "\n", + "2\n", + "\n", + "\n", + "\n", + "0->2\n", + "\n", + "\n", + "(!i0 & i1) | (i0 & !i1)\n", + "\n", + "\n", + "\n", + "1->0\n", + "\n", + "\n", + "!o0\n", + "\n", + "\n", + "\n", + "2->0\n", + "\n", + "\n", + "o0\n", + "\n", + "\n", + "\n", + "
" + ], + "text/plain": [ + "" + ] + }, + "metadata": {}, + "output_type": "display_data" + }, + { + "name": "stdout", + "output_type": "stream", + "text": [ + "('o0',)\n", + "('o0',)\n" + ] + } + ], + "source": [ + "strat1_s = spot.split_separated_mealy(strat1)\n", + "display_inline(strat1, strat1_s)\n", + "print(spot.get_synthesis_output_aps(strat1))\n", + "print(spot.get_synthesis_output_aps(strat1_s))" + ] + }, + { + "cell_type": "code", + "execution_count": 18, + "id": "cd06f9ab", + "metadata": {}, + "outputs": [ + { + "data": { + "text/html": [ + "
\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", + "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", + "
\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", + "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": {}, + "output_type": "display_data" + } + ], + "source": [ + "display_inline(spot.mealy_machine_to_aig(strat1, \"isop\"), spot.mealy_machine_to_aig(strat1_s, \"isop\"))" + ] } ], "metadata": { "kernelspec": { - "display_name": "Python 3", + "display_name": "Python 3 (ipykernel)", "language": "python", "name": "python3" }, @@ -4079,7 +4439,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.9.2" + "version": "3.8.10" } }, "nbformat": 4,