diff --git a/python/spot/impl.i b/python/spot/impl.i index 63d6b25e2..afbda909e 100644 --- a/python/spot/impl.i +++ b/python/spot/impl.i @@ -408,6 +408,37 @@ namespace swig $result = SWIG_FromCharPtr($1->c_str()); } +// For some reason, Swig can convert [aut1,aut2,...] into +// std::vector, but not into +// std::vector. Let's fix that by using +// the Swig machinery that successfully converts to +// std::vector, and then converting this into +// std::vector manually. + +%typemap(in) std::vector& { + void *tmp; + int res = SWIG_ConvertPtr($input, &tmp, $descriptor(std::vector*), + SWIG_POINTER_IMPLICIT_CONV); + if (!SWIG_IsOK(res) || !tmp) + %argument_fail(res, "std::vector", $symname, $argnum); + + std::vector* temp; + temp = reinterpret_cast*>(tmp); + $1 = new std::vector(temp->begin(), + temp->end()); + if (SWIG_IsNewObj(res)) delete temp; +} +%typemap(freearg) std::vector& { + delete $1; +} + +%typemap(typecheck, precedence=2000) std::vector& { + $1 = SWIG_CheckState(SWIG_ConvertPtr($input, nullptr, + $descriptor(std::vector*), + SWIG_POINTER_IMPLICIT_CONV)); +} + + %{ // This function is called whenever an exception has been caught. // Doing all the conversion in a separate function (rather than @@ -462,7 +493,6 @@ static void handle_any_exception() %implicitconv std::vector; %implicitconv std::vector; -%implicitconv std::vector; %implicitconv spot::formula; %implicitconv std::vector; @@ -672,7 +702,6 @@ def state_is_accepting(self, src) -> "bool": %template(scc_info_scc_edges) spot::internal::scc_edges const, spot::internal::keep_all>; %template(scc_info_inner_scc_edges) spot::internal::scc_edges const, spot::internal::keep_inner_scc>; %template(vector_twa_graph) std::vector; -%template(vector_const_twa_graph) std::vector; %include %include %include diff --git a/spot/twaalgos/aiger.cc b/spot/twaalgos/aiger.cc index 5b04d1f11..ae7b665e9 100644 --- a/spot/twaalgos/aiger.cc +++ b/spot/twaalgos/aiger.cc @@ -640,7 +640,7 @@ namespace spot unsigned aig::cube2var_(const bdd& b, const int use_split_off) { - assert(bdd_is_cube(b) && "bdd is not a cube!"); + assert(bdd_is_cube(b) && "bdd is not a cube"); static std::vector parts_; static std::vector prod_vars_; static std::vector prod_parts_; @@ -729,7 +729,7 @@ namespace spot if (method == 1 || method == 2) used_m.push_back(1); assert(used_m.size() - && "Can not convert the given method. " + && "Cannot convert the given method. " "Only 0,1 and 2 are currently supported"); const auto negate = use_dual ? std::vector{false} @@ -766,7 +766,7 @@ namespace spot { cond_vars.push_back(enc_1(cpart, m)); if (num_gates() >= ngates_min) - break; // Can not be optimal + break; // Cannot be optimal } // Compute the and if there is still hope unsigned this_res = -1u; @@ -967,7 +967,7 @@ namespace spot it2.first.first.id(), it2.first.second.id()); }); if (itm == occur_map.cend()) - throw std::runtime_error("Empty occurence map!"); + throw std::runtime_error("Empty occurence map"); return *itm; }; @@ -1134,7 +1134,7 @@ namespace spot if (it != var2bdd.end()) { assert(bdd2var.at(var2bdd.at(v).id()) == v - && "Inconsistent bdd!\n"); + && "Inconsistent bdd."); return it->second; } //get the vars of the input to the gates @@ -1170,7 +1170,7 @@ namespace spot { if (not (var2bdd.count(v))) throw std::runtime_error("variable " + std::to_string(v) - + " has no bdd associated!\n"); + + " has no bdd associated."); }; std::for_each(circ.next_latches_.begin(), circ.next_latches_.end(), check); @@ -1342,7 +1342,7 @@ namespace spot assert(inputs.size() == num_inputs() && "Input length does not match"); assert(state_.size() == max_var_ + 2 - && "State vector does not have the correct size.\n" + && "State vector does not have the correct size. " "Forgot to initialize?"); // Set the inputs for (unsigned i = 0; i < num_inputs(); ++i) @@ -1855,7 +1855,7 @@ namespace spot { if (!m) throw std::runtime_error("mealy_machine_to_aig(): " - "m cannot be null"); + "m cannot be null."); return auts_to_aiger({{m, get_synthesis_outputs(m)}}, mode); } @@ -1865,7 +1865,7 @@ namespace spot { if (m.success != mealy_like::realizability_code::REALIZABLE_REGULAR) throw std::runtime_error("mealy_machine_to_aig(): " - "Can only handle regular mealy machine, TBD"); + "Can only handle regular mealy machine, yet."); return mealy_machine_to_aig(m.mealy_like, mode); } @@ -1916,7 +1916,7 @@ namespace spot { if (m.success != mealy_like::realizability_code::REALIZABLE_REGULAR) throw std::runtime_error("mealy_machine_to_aig(): " - "Can only handle regular mealy machine, TBD"); + "Can only handle regular mealy machine, yet."); return mealy_machine_to_aig(m.mealy_like, mode, ins, outs); } @@ -1931,7 +1931,7 @@ namespace spot if (usedbdd != s->get_dict()) throw std::runtime_error("mealy_machines_to_aig(): " "All machines have to " - "share a bdd_dict!\n"); + "share a bdd_dict."); }); std::vector> new_vec; @@ -1945,7 +1945,7 @@ namespace spot if (bdd_and(bdd_not(this_outputs), all_outputs) == bddfalse) throw std::runtime_error("mealy_machines_to_aig(): " "\"outs\" of the machines are not " - "distinct!.\n"); + "distinct."); all_outputs &= this_outputs; new_vec.emplace_back(am, this_outputs); } @@ -1962,7 +1962,7 @@ namespace spot mealy_like::realizability_code::REALIZABLE_REGULAR; })) throw std::runtime_error("mealy_machines_to_aig(): " "Can only handle regular mealy machine for " - "the moment, TBD"); + "the moment."); auto new_vec = std::vector(); new_vec.reserve(m_vec.size()); std::transform(m_vec.cbegin(), m_vec.cend(), @@ -1974,14 +1974,17 @@ namespace spot // Note: This ignores the named property aig_ptr - mealy_machines_to_aig(const std::vector& m_vec, + mealy_machines_to_aig(const std::vector& m_vec, const char *mode, const std::vector& ins, const std::vector>& outs) { + if (m_vec.empty()) + throw std::runtime_error("mealy_machines_to_aig(): No strategy given."); + if (m_vec.size() != outs.size()) throw std::runtime_error("mealy_machines_to_aig(): " - "Expected as many outs as strategies!\n"); + "Expecting as many outs as strategies."); std::for_each(m_vec.begin()+1, m_vec.end(), [usedbdd = m_vec.at(0)->get_dict()](auto&& it) @@ -1989,7 +1992,7 @@ namespace spot if (usedbdd != it->get_dict()) throw std::runtime_error("mealy_machines_to_aig(): " "All strategies have to " - "share a bdd_dict!\n"); + "share a bdd_dict."); }); { @@ -2042,7 +2045,7 @@ namespace spot { // todo extend to TGBA and possibly others const unsigned ns = strat_vec.size(); - std::vector m_machines; + std::vector m_machines; m_machines.reserve(ns); std::vector> outs_used; outs_used.reserve(ns); diff --git a/spot/twaalgos/aiger.hh b/spot/twaalgos/aiger.hh index b58a687da..707f1bc3c 100644 --- a/spot/twaalgos/aiger.hh +++ b/spot/twaalgos/aiger.hh @@ -472,7 +472,7 @@ namespace spot mealy_machines_to_aig(const std::vector& m_vec, const char* mode); SPOT_API aig_ptr - mealy_machines_to_aig(const std::vector& m_vec, + mealy_machines_to_aig(const std::vector& m_vec, const char* mode, const std::vector& ins, const std::vector>& outs); @@ -483,7 +483,6 @@ namespace spot const std::vector>& outs); /// @} - /// \ingroup twa_io /// \brief Print the aig to stream in AIGER format SPOT_API std::ostream& diff --git a/tests/python/synthesis.ipynb b/tests/python/synthesis.ipynb index 5e175ee71..91f526508 100644 --- a/tests/python/synthesis.ipynb +++ b/tests/python/synthesis.ipynb @@ -3,6 +3,7 @@ { "cell_type": "code", "execution_count": 1, + "id": "452c00ae", "metadata": {}, "outputs": [], "source": [ @@ -13,6 +14,7 @@ }, { "cell_type": "markdown", + "id": "7545ab74", "metadata": {}, "source": [ "This notebook presents functions that can be used to solve the Reactive Synthesis problem using games.\n", @@ -20,13 +22,14 @@ "\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", "\n", - "# Reactive synthesis in three steps\n", + "# Reactive synthesis in four steps\n", "\n", "A strategy/control circuit can be derived more conveniently from an LTL/PSL specification.\n", - "The process is decomposed in to three steps:\n", + "The process is decomposed in three steps:\n", "- Creating the game\n", "- Solving the game\n", - "- Obtaining the strategy\n", + "- Simplifying the winnning strategy\n", + "- Building the circuit from the strategy\n", "\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", @@ -36,6 +39,7 @@ { "cell_type": "code", "execution_count": 2, + "id": "fb49e681", "metadata": {}, "outputs": [ { @@ -694,7 +698,7 @@ "\n" ], "text/plain": [ - " *' at 0x7f494811f630> >" + " *' at 0x7fe9bc79b6f0> >" ] }, "metadata": {}, @@ -713,6 +717,7 @@ }, { "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." @@ -721,6 +726,7 @@ { "cell_type": "code", "execution_count": 3, + "id": "62fb169f", "metadata": {}, "outputs": [ { @@ -1339,14 +1345,16 @@ }, { "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 are labeled by combination of input assignments and associated output assignments." + "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." ] }, { "cell_type": "code", "execution_count": 4, + "id": "cdf8f5f1", "metadata": {}, "outputs": [ { @@ -2344,22 +2352,22 @@ }, { "cell_type": "markdown", + "id": "511093c3", "metadata": {}, "source": [ - "The machines returned by 'solved_game_to_separated_mealy' are (obviously) separated.\n", - "If we need a split mealy machine, we can call the dedicated function, which is more efficient\n", - "than the general 'split_2step'." + "If needed, a separated Mealy machine can be turned into game shape using `split_sepearated_mealy()`, which is more efficient than `split_2step()`." ] }, { "cell_type": "code", "execution_count": 5, + "id": "cc977286", "metadata": {}, "outputs": [ { "data": { "text/html": [ - "
\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", - "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", - "\n", - "2\n", - "\n", - "2\n", - "\n", - "\n", - "\n", - "0->2\n", - "\n", - "\n", - "(!i0 & i1) | (i0 & !i1)\n", - "\n", - "\n", - "\n", - "\n", - "1->0\n", - "\n", - "\n", - "!o0\n", - "\n", - "\n", - "\n", - "\n", - "2->0\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", - "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", - "\n", - "2\n", - "\n", - "2\n", - "\n", - "\n", - "\n", - "0->2\n", - "\n", - "\n", - "(!i0 & i1) | (i0 & !i1)\n", - "\n", - "\n", - "\n", - "\n", - "1->0\n", - "\n", - "\n", - "o1\n", - "\n", - "\n", - "\n", - "\n", - "2->0\n", - "\n", - "\n", - "!o1\n", - "\n", - "\n", - "\n", - "\n", - "
" - ], - "text/plain": [ - "" - ] - }, - "metadata": {}, - "output_type": "display_data" - }, - { - "name": "stdout", - "output_type": "stream", - "text": [ - "solved games\n" + "Solved games:\n" ] }, { @@ -3686,7 +3530,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "reduced strategies\n" + "Reduced strategies:\n" ] }, { @@ -3799,7 +3643,7 @@ "name": "stdout", "output_type": "stream", "text": [ - "Circuit implementing both machines\n" + "Circuit implementing both machines:\n" ] }, { @@ -3909,7 +3753,7 @@ "\n" ], "text/plain": [ - " >" + " >" ] }, "metadata": {}, @@ -3918,36 +3762,32 @@ ], "source": [ "g1 = spot.ltl_to_game(\"G((i0 xor i1) <-> o0)\", [\"o0\"], si)\n", - "g2 = spot.ltl_to_game(\"G((i0 xor i1) <-> (~o1))\", [\"o1\"], si)\n", - "print(\"initial games\")\n", - "display_inline(g1, g2)\n", + "g2 = spot.ltl_to_game(\"G((i0 xor i1) <-> (!o1))\", [\"o1\"], si)\n", "spot.solve_game(g1)\n", "spot.highlight_strategy(g1)\n", "spot.solve_game(g2)\n", "spot.highlight_strategy(g2)\n", - "print(\"solved games\")\n", + "print(\"Solved games:\")\n", "display_inline(g1, g2)\n", "strat1 = spot.solved_game_to_separated_mealy(g1)\n", "strat2 = spot.solved_game_to_separated_mealy(g2)\n", - "print(\"reduced strategies\")\n", + "print(\"Reduced strategies:\")\n", "display_inline(strat1, strat2)\n", - "print(\"Circuit implementing both machines\")\n", - "m_vec = spot.vector_const_twa_graph()\n", - "m_vec.push_back(strat1)\n", - "m_vec.push_back(strat2)\n", - "aig = spot.mealy_machines_to_aig(m_vec, \"isop\")\n", + "print(\"Circuit implementing both machines:\")\n", + "aig = spot.mealy_machines_to_aig([strat1, strat2], \"isop\")\n", "display(aig)" ] }, { "cell_type": "markdown", + "id": "b3985f04", "metadata": {}, "source": [ "# Reading an AIGER-file\n", "\n", "Note that we do not support the full [AIGER syntax](http://fmv.jku.at/aiger/FORMAT.aiger). Our restrictions corresponds to the conventions used in the type of AIGER file we output:\n", "- Input variables start at index 2 and are consecutively numbered.\n", - "- Latch variables start at index (1 + #inputs) × 2 and are consecutively numbered.\n", + "- Latch variables start at index (1 + #inputs)×2 and are consecutively numbered.\n", "- If some inputs or outputs are named in comments, all of them have to be named.\n", "- Gate number $n$ can only connect to latches, inputs, or previously defined gates ($\n" ], "text/plain": [ - " >" + " >" ] }, "metadata": {}, @@ -4099,6 +3941,7 @@ { "cell_type": "code", "execution_count": 14, + "id": "0c418256", "metadata": {}, "outputs": [ { @@ -4127,6 +3970,7 @@ { "cell_type": "code", "execution_count": 15, + "id": "bd4b6aa2", "metadata": {}, "outputs": [ { @@ -4143,14 +3987,16 @@ }, { "cell_type": "markdown", + "id": "94fd22a1", "metadata": {}, "source": [ - "An aiger circuit can be transformed into a monitor. This can help for instance to verify that it does not intersect the negation of the specification." + "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." ] }, { "cell_type": "code", "execution_count": 16, + "id": "b157ec16", "metadata": {}, "outputs": [ { @@ -4204,7 +4050,7 @@ "\n" ], "text/plain": [ - " *' at 0x7f49481417b0> >" + " *' at 0x7fe9bc6a8330> >" ] }, "execution_count": 16, @@ -4233,7 +4079,7 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.8.10" + "version": "3.9.2" } }, "nbformat": 4,