diff --git a/spot/twaalgos/dot.cc b/spot/twaalgos/dot.cc index 15ee5bd08..aa808d97f 100644 --- a/spot/twaalgos/dot.cc +++ b/spot/twaalgos/dot.cc @@ -699,13 +699,13 @@ namespace spot } } auto si = - std::unique_ptr((opt_scc_ && !aut->is_alternating()) - ? new scc_info(aut) : nullptr); + std::unique_ptr(opt_scc_ ? new scc_info(aut) : nullptr); start(); if (si) { - si->determine_unknown_acceptance(); + if (!aut->is_alternating()) + si->determine_unknown_acceptance(); unsigned sccs = si->scc_count(); for (unsigned i = 0; i < sccs; ++i) diff --git a/spot/twaalgos/sccinfo.cc b/spot/twaalgos/sccinfo.cc index d4d079a1f..c806552da 100644 --- a/spot/twaalgos/sccinfo.cc +++ b/spot/twaalgos/sccinfo.cc @@ -60,173 +60,211 @@ namespace spot // Values < 0 number states that are part of incomplete SCCs being // completed. 0 denotes non-visited states. - int num_; // Number of visited nodes, negated. + int num_ = 0; // Number of visited nodes, negated. - typedef twa_graph::graph_t::const_iterator iterator; - typedef std::pair pair_state_iter; - std::stack todo_; // DFS stack. Holds (STATE, - // ITERATOR) pairs where - // ITERATOR is an iterator over - // the successors of STATE. - // ITERATOR should always be - // freed when TODO is popped, - // but STATE should not because - // it is used as a key in H. + struct stack_item { + unsigned src; + unsigned out_edge; + unsigned univ_pos; + }; + // DFS stack. Holds (STATE, TRANS, UNIV_POS) pairs where TRANS is + // the current outgoing transition of STATE, and UNIV_POS is used + // when the transition is universal to iterate over all possible + // destinations. + std::stack todo_; + auto& gr = aut->get_graph(); - // Setup depth-first search from the initial state. - unsigned init = aut->get_init_state_number(); - num_ = -1; - h_[init] = num_; - root_.emplace_back(num_, 0U); - todo_.emplace(init, aut->out(init).begin()); - live.emplace_back(init); - - while (!todo_.empty()) + // Setup depth-first search from the initial state. But we may + // have a conjunction of initial state in alternating automata. + for (unsigned init: aut->univ_dests(aut->get_init_state_number())) { - // We are looking at the next successor in SUCC. - iterator succ = todo_.top().second; - - // If there is no more successor, backtrack. - if (!succ) - { - // We have explored all successors of state CURR. - unsigned curr = todo_.top().first; - - // Backtrack TODO_. - todo_.pop(); - - // When backtracking the root of an SCC, we must also - // remove that SCC from the ARC/ROOT stacks. We must - // discard from H all reachable states from this SCC. - assert(!root_.empty()); - if (root_.back().index == h_[curr]) - { - unsigned num = node_.size(); - auto acc = root_.back().acc; - bool triv = root_.back().trivial; - node_.emplace_back(acc, triv); - - // Move all elements of this SCC from the live stack - // to the the node. - auto i = std::find(live.rbegin(), live.rend(), curr); - assert(i != live.rend()); - ++i; // Because base() does -1 - auto& nbs = node_.back().states_; - nbs.insert(nbs.end(), i.base(), live.end()); - live.erase(i.base(), live.end()); - - std::set dests; - unsigned np1 = num + 1; - for (unsigned s: nbs) - { - sccof_[s] = num; - h_[s] = np1; - } - // Gather all successor SCCs - for (unsigned s: nbs) - for (auto& t: aut->out(s)) - { - unsigned n = sccof_[t.dst]; - assert(n != -1U); - if (n == num) - continue; - dests.insert(n); - } - auto& succ = node_.back().succ_; - succ.insert(succ.end(), dests.begin(), dests.end()); - bool accept = !triv && root_.back().accepting; - node_.back().accepting_ = accept; - bool reject = triv || !aut->acc().inf_satisfiable(acc); - // If the SCC acceptance is indeterminate, but has - // only one state and one transition, it is - // necessarily rejecting, otherwise we would have - // found it to be accepting. - if (!accept && !reject && nbs.size() == 1) - { - unsigned selfloop = 0; - for (const auto& e: aut->out(nbs.front())) - if (e.src == e.dst) - { - ++selfloop; - if (selfloop > 1) - break; - } - reject = selfloop <= 1; - } - node_.back().rejecting_ = reject; - root_.pop_back(); - } - continue; - } - - // We have a successor to look at. - // Fetch the values we are interested in... - unsigned dest = succ->dst; - auto acc = succ->acc; - ++todo_.top().second; - - // We do not need SUCC from now on. - - // Are we going to a new state? - int spi = h_[dest]; - if (spi == 0) - { - // Yes. Number it, stack it, and register its successors - // for later processing. - h_[dest] = --num_; - root_.emplace_back(num_, acc); - todo_.emplace(dest, aut->out(dest).begin()); - live.emplace_back(dest); - continue; - } - - // We already know the state. - - // Have we reached a maximal SCC? + int spi = h_[init]; if (spi > 0) continue; + assert(spi == 0); + h_[init] = --num_; + root_.emplace_back(num_, 0U); + todo_.emplace(stack_item{init, gr.state_storage(init).succ, 0}); + live.emplace_back(init); - // Now this is the most interesting case. We have reached a - // state S1 which is already part of a non-dead SCC. Any such - // non-dead SCC has necessarily been crossed by our path to - // this state: there is a state S2 in our path which belongs - // to this SCC too. We are going to merge all states between - // this S1 and S2 into this SCC.. - // - // This merge is easy to do because the order of the SCC in - // ROOT is descending: we just have to merge all SCCs from the - // top of ROOT that have an index lesser than the one of - // the SCC of S2 (called the "threshold"). - int threshold = spi; - bool is_accepting = false; - // If this is a self-loop, check its acceptance alone. - if (dest == succ->src) - is_accepting = aut->acc().accepting(acc); - - assert(!root_.empty()); - while (threshold > root_.back().index) + while (!todo_.empty()) { - acc |= root_.back().acc; - acc |= root_.back().in_acc; - is_accepting |= root_.back().accepting; - root_.pop_back(); + // We are looking at the next successor in SUCC. + unsigned tr_succ = todo_.top().out_edge; + + // If there is no more successor, backtrack. + if (!tr_succ) + { + // We have explored all successors of state CURR. + unsigned curr = todo_.top().src; + + // Backtrack TODO_. + todo_.pop(); + + // When backtracking the root of an SCC, we must also + // remove that SCC from the ARC/ROOT stacks. We must + // discard from H all reachable states from this SCC. + assert(!root_.empty()); + if (root_.back().index == h_[curr]) + { + unsigned num = node_.size(); + auto acc = root_.back().acc; + bool triv = root_.back().trivial; + node_.emplace_back(acc, triv); + + // Move all elements of this SCC from the live stack + // to the the node. + auto i = std::find(live.rbegin(), live.rend(), curr); + assert(i != live.rend()); + ++i; // Because base() does -1 + auto& nbs = node_.back().states_; + nbs.insert(nbs.end(), i.base(), live.end()); + live.erase(i.base(), live.end()); + + std::set dests; + unsigned np1 = num + 1; + for (unsigned s: nbs) + { + sccof_[s] = num; + h_[s] = np1; + } + // Gather all successor SCCs + for (unsigned s: nbs) + for (auto& t: aut->out(s)) + for (unsigned d: aut->univ_dests(t)) + { + unsigned n = sccof_[d]; + assert(n != -1U); + if (n == num) + continue; + dests.insert(n); + } + auto& succ = node_.back().succ_; + succ.insert(succ.end(), dests.begin(), dests.end()); + bool accept = !triv && root_.back().accepting; + node_.back().accepting_ = accept; + bool reject = triv || !aut->acc().inf_satisfiable(acc); + // If the SCC acceptance is indeterminate, but has + // only self-loops with the same mark, it is + // necessarily rejecting, otherwise we would have + // found it to be accepting. + if (!accept && !reject && nbs.size() == 1) + { + acc_cond::mark_t selfacc = 0; + bool first = true; + reject = true; + for (const auto& e: aut->out(nbs.front())) + for (unsigned d: aut->univ_dests(e)) + if (e.src == d) + { + if (first) + { + selfacc = e.acc; + first = false; + } + else if (selfacc != e.acc) + { + reject = false; + goto break2; + } + } + } + break2: + node_.back().rejecting_ = reject; + root_.pop_back(); + } + continue; + } + + // We have a successor to look at. + // Fetch the values we are interested in... + auto& e = gr.edge_storage(tr_succ); + unsigned dest = e.dst; + if ((int) dest < 0) + { + // Iterate over all destinations of an universal edge. + if (todo_.top().univ_pos == 0) + todo_.top().univ_pos = ~dest + 1; + const auto& v = gr.dests_vector(); + dest = v[todo_.top().univ_pos]; + // Last universal destination? + if (~e.dst + v[~e.dst] == todo_.top().univ_pos) + { + todo_.top().out_edge = e.next_succ; + todo_.top().univ_pos = 0; + } + else + { + ++todo_.top().univ_pos; + } + } + else + { + todo_.top().out_edge = e.next_succ; + } + + auto acc = e.acc; + + // Are we going to a new state? + int spi = h_[dest]; + if (spi == 0) + { + // Yes. Number it, stack it, and register its successors + // for later processing. + h_[dest] = --num_; + root_.emplace_back(num_, acc); + todo_.emplace(stack_item{dest, gr.state_storage(dest).succ, 0}); + live.emplace_back(dest); + continue; + } + + // We already know the state. + + // Have we reached a maximal SCC? + if (spi > 0) + continue; + + // Now this is the most interesting case. We have reached a + // state S1 which is already part of a non-dead SCC. Any such + // non-dead SCC has necessarily been crossed by our path to + // this state: there is a state S2 in our path which belongs + // to this SCC too. We are going to merge all states between + // this S1 and S2 into this SCC.. + // + // This merge is easy to do because the order of the SCC in + // ROOT is descending: we just have to merge all SCCs from the + // top of ROOT that have an index lesser than the one of + // the SCC of S2 (called the "threshold"). + int threshold = spi; + bool is_accepting = false; + // If this is a self-loop, check its acceptance alone. + if (dest == e.src) + is_accepting = aut->acc().accepting(acc); + assert(!root_.empty()); + while (threshold > root_.back().index) + { + acc |= root_.back().acc; + acc |= root_.back().in_acc; + is_accepting |= root_.back().accepting; + root_.pop_back(); + assert(!root_.empty()); + } + + // Note that we do not always have + // threshold == root_.back().index + // after this loop, the SCC whose index is threshold might have + // been merged with a higher SCC. + + // Accumulate all acceptance conditions, states, SCC + // successors, and conditions into the merged SCC. + root_.back().acc |= acc; + root_.back().accepting |= is_accepting + || aut->acc().accepting(root_.back().acc); + // This SCC is no longer trivial. + root_.back().trivial = false; } - - // Note that we do not always have - // threshold == root_.back().index - // after this loop, the SCC whose index is threshold might have - // been merged with a higher SCC. - - // Accumulate all acceptance conditions, states, SCC - // successors, and conditions into the merged SCC. - root_.back().acc |= acc; - root_.back().accepting |= is_accepting - || aut->acc().accepting(root_.back().acc); - // This SCC is no longer trivial. - root_.back().trivial = false; } - determine_usefulness(); } @@ -315,6 +353,9 @@ namespace spot void scc_info::determine_unknown_acceptance() { + if (aut_->is_alternating()) + throw std::runtime_error("scc_info::determine_unknown_acceptance() " + "does not support alternating automata"); std::vector k; unsigned n = scc_count(); bool changed = false; diff --git a/spot/twaalgos/sccinfo.hh b/spot/twaalgos/sccinfo.hh index f75e29a47..15f4017e8 100644 --- a/spot/twaalgos/sccinfo.hh +++ b/spot/twaalgos/sccinfo.hh @@ -24,6 +24,18 @@ namespace spot { + /// \brief Compute an SCC map and gather assorted information. + /// + /// This takes twa_graph as input and compute its SCCs. This + /// class maps all input states to their SCCs, and vice-versa. + /// It allows iterating over all SCCs of the automaton, and check + /// their acceptance or non-acceptance. + /// + /// Additionally this class can be used on alternating automata, but + /// in this case, universal transitions are handled like existential + /// transitions. It still make sense to check which states belong + /// to the same SCC, but the acceptance information computed by + /// this class is meaningless. class SPOT_API scc_info { public: diff --git a/tests/Makefile.am b/tests/Makefile.am index cb7715cdb..8d9e5a1c3 100644 --- a/tests/Makefile.am +++ b/tests/Makefile.am @@ -323,6 +323,7 @@ TESTS_ipython = \ # with a _. TESTS_python = \ python/_aux.ipynb \ + python/_altscc.ipynb \ python/accparse2.py \ python/alarm.py \ python/alternating.py \ diff --git a/tests/core/alternating.test b/tests/core/alternating.test index 718f77351..49eedb9b9 100644 --- a/tests/core/alternating.test +++ b/tests/core/alternating.test @@ -53,7 +53,6 @@ State: 6 "t" --END-- EOF -# The 's' option should be ignored for alternating automata autfilt --dot=bans alt.hoa >alt.dot cat >expect.dot <,width=0,height=0,shape=none] -11 -> 0 -11 -> 2 + subgraph cluster_0 { + color=green + label="" + 6 [label="t"] + } + subgraph cluster_1 { + color=red + label="" + 4 [label="F(b)\n⓿"] + } + subgraph cluster_2 { + color=green + label="" + 3 [label="GF(b)"] + } + subgraph cluster_3 { + color=green + label="" + 2 [label="G(a)"] + } + subgraph cluster_4 { + color=red + label="" + 1 [label="FG(a)\n⓿"] + } + subgraph cluster_5 { + color=red + label="" + 5 [label="((a) U (b))\n⓿"] + } + subgraph cluster_6 { + color=black + label="" 0 [label="((((a) U (b)) && GF(b)) && FG(a))"] + } 0 -> -1 [label="b", dir=none] -1 [label=<>,width=0,height=0,shape=none] -1 -> 3 @@ -76,24 +109,18 @@ digraph G { -4 -> 5 [style=bold, color="#F15854"] -4 -> 3 [style=bold, color="#F15854"] -4 -> 1 [style=bold, color="#F15854"] - 1 [label="FG(a)\n⓿"] 1 -> 2 [label="a"] 1 -> 1 [label="1"] - 2 [label="G(a)"] 2 -> 2 [label="a"] - 3 [label="GF(b)"] 3 -> 3 [label="b"] 3 -> -8 [label="!b", style=bold, color="#FAA43A", dir=none] -8 [label=<>,width=0,height=0,shape=none] -8 -> 4 [style=bold, color="#FAA43A"] -8 -> 3 [style=bold, color="#FAA43A"] - 4 [label="F(b)\n⓿"] 4 -> 6 [label="b"] 4 -> 4 [label="!b"] - 5 [label="((a) U (b))\n⓿"] 5 -> 6 [label="b"] 5 -> 5 [label="a & !b"] - 6 [label="t"] 6 -> 6 [label="1"] } EOF diff --git a/tests/python/_altscc.ipynb b/tests/python/_altscc.ipynb new file mode 100644 index 000000000..d448119ef --- /dev/null +++ b/tests/python/_altscc.ipynb @@ -0,0 +1,603 @@ +{ + "metadata": { + "kernelspec": { + "display_name": "Python 3", + "language": "python", + "name": "python3" + }, + "language_info": { + "codemirror_mode": { + "name": "ipython", + "version": 3 + }, + "file_extension": ".py", + "mimetype": "text/x-python", + "name": "python", + "nbconvert_exporter": "python", + "pygments_lexer": "ipython3", + "version": "3.5.2+" + }, + "name": "" + }, + "nbformat": 3, + "nbformat_minor": 0, + "worksheets": [ + { + "cells": [ + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "These examples are tests for scc_info on alternating automata." + ] + }, + { + "cell_type": "code", + "collapsed": true, + "input": [ + "import spot\n", + "spot.setup(show_default='.bas')" + ], + "language": "python", + "metadata": {}, + "outputs": [], + "prompt_number": 2 + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "spot.automaton('''\n", + "HOA: v1\n", + "States: 2\n", + "Start: 0&1\n", + "AP: 2 \"a\" \"b\"\n", + "acc-name: Buchi\n", + "Acceptance: 1 Inf(0)\n", + "--BODY--\n", + "State: 0\n", + "[0] 0\n", + "[!0] 1\n", + "State: 1\n", + "[1] 1 {0}\n", + "--END--\n", + "''')" + ], + "language": "python", + "metadata": {}, + "outputs": [ + { + "metadata": {}, + "output_type": "pyout", + "prompt_number": 12, + "svg": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "G\n", + "\n", + "Inf(\n", + "\u24ff\n", + ")\n", + "cluster_0\n", + "\n", + "\n", + "cluster_1\n", + "\n", + "\n", + "\n", + "\n", + "-1\n", + "\n", + "\n", + "\n", + "I->-1\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0\n", + "\n", + "\n", + "-1->0\n", + "\n", + "\n", + "\n", + "\n", + "1\n", + "\n", + "1\n", + "\n", + "\n", + "-1->1\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "a\n", + "\n", + "\n", + "0->1\n", + "\n", + "\n", + "!a\n", + "\n", + "\n", + "1->1\n", + "\n", + "\n", + "b\n", + "\u24ff\n", + "\n", + "\n", + "\n" + ], + "text": [ + " *' at 0x7f76dc5d7090> >" + ] + } + ], + "prompt_number": 12 + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "universal edges are handled as if they were many distinct existencial edges from the point of view of `scc_info`, so the acceptance / rejection status is not always meaningful." + ] + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "spot.automaton('''\n", + "HOA: v1\n", + "States: 2\n", + "Start: 0&1\n", + "AP: 2 \"a\" \"b\"\n", + "Acceptance: 1 Fin(0)\n", + "--BODY--\n", + "State: 0\n", + "[0] 0&1 {0}\n", + "State: 1\n", + "[1] 1\n", + "--END--\n", + "''')" + ], + "language": "python", + "metadata": {}, + "outputs": [ + { + "metadata": {}, + "output_type": "pyout", + "prompt_number": 17, + "svg": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "G\n", + "\n", + "Fin(\n", + "\u24ff\n", + ")\n", + "cluster_0\n", + "\n", + "\n", + "cluster_1\n", + "\n", + "\n", + "\n", + "\n", + "-4\n", + "\n", + "\n", + "\n", + "I->-4\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0\n", + "\n", + "\n", + "-4->0\n", + "\n", + "\n", + "\n", + "\n", + "1\n", + "\n", + "1\n", + "\n", + "\n", + "-4->1\n", + "\n", + "\n", + "\n", + "\n", + "-1\n", + "\n", + "\n", + "\n", + "0->-1\n", + "\n", + "a\n", + "\u24ff\n", + "\n", + "\n", + "1->1\n", + "\n", + "\n", + "b\n", + "\n", + "\n", + "-1->0\n", + "\n", + "\n", + "\n", + "\n", + "-1->1\n", + "\n", + "\n", + "\n", + "\n", + "\n" + ], + "text": [ + " *' at 0x7f76dc5d71b0> >" + ] + } + ], + "prompt_number": 17 + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "spot.automaton('''\n", + "HOA: v1\n", + "States: 2\n", + "Start: 0&1\n", + "AP: 2 \"a\" \"b\"\n", + "Acceptance: 1 Fin(0)\n", + "--BODY--\n", + "State: 0\n", + "[0] 0 {0}\n", + "[!0] 1\n", + "State: 1\n", + "[1] 1&0\n", + "--END--\n", + "''')" + ], + "language": "python", + "metadata": {}, + "outputs": [ + { + "metadata": {}, + "output_type": "pyout", + "prompt_number": 16, + "svg": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "G\n", + "\n", + "Fin(\n", + "\u24ff\n", + ")\n", + "cluster_0\n", + "\n", + "\n", + "\n", + "\n", + "-4\n", + "\n", + "\n", + "\n", + "I->-4\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0\n", + "\n", + "\n", + "-4->0\n", + "\n", + "\n", + "\n", + "\n", + "1\n", + "\n", + "1\n", + "\n", + "\n", + "-4->1\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "a\n", + "\u24ff\n", + "\n", + "\n", + "0->1\n", + "\n", + "\n", + "!a\n", + "\n", + "\n", + "-1\n", + "\n", + "\n", + "\n", + "1->-1\n", + "\n", + "b\n", + "\n", + "\n", + "-1->0\n", + "\n", + "\n", + "\n", + "\n", + "-1->1\n", + "\n", + "\n", + "\n", + "\n", + "\n" + ], + "text": [ + " *' at 0x7f76dc5d7180> >" + ] + } + ], + "prompt_number": 16 + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "spot.automaton('''\n", + "HOA: v1\n", + "States: 2\n", + "Start: 0\n", + "AP: 2 \"a\" \"b\"\n", + "Acceptance: 1 Fin(0)\n", + "--BODY--\n", + "State: 0\n", + "[0] 0\n", + "[!0] 1 {0}\n", + "State: 1\n", + "[1] 1&0\n", + "--END--\n", + "''')" + ], + "language": "python", + "metadata": {}, + "outputs": [ + { + "metadata": {}, + "output_type": "pyout", + "prompt_number": 30, + "svg": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "G\n", + "\n", + "Fin(\n", + "\u24ff\n", + ")\n", + "cluster_0\n", + "\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "a\n", + "\n", + "\n", + "1\n", + "\n", + "1\n", + "\n", + "\n", + "0->1\n", + "\n", + "\n", + "!a\n", + "\u24ff\n", + "\n", + "\n", + "-1\n", + "\n", + "\n", + "\n", + "1->-1\n", + "\n", + "b\n", + "\n", + "\n", + "-1->0\n", + "\n", + "\n", + "\n", + "\n", + "-1->1\n", + "\n", + "\n", + "\n", + "\n", + "\n" + ], + "text": [ + " *' at 0x7f76dc5d7de0> >" + ] + } + ], + "prompt_number": 30 + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "spot.automaton('''\n", + "HOA: v1\n", + "States: 2\n", + "Start: 0\n", + "AP: 2 \"a\" \"b\"\n", + "Acceptance: 1 Fin(0)\n", + "--BODY--\n", + "State: 0\n", + "[0] 0 {0}\n", + "[!0] 1 \n", + "State: 1\n", + "[1] 1&0 {0}\n", + "--END--\n", + "''')" + ], + "language": "python", + "metadata": {}, + "outputs": [ + { + "metadata": {}, + "output_type": "pyout", + "prompt_number": 31, + "svg": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "G\n", + "\n", + "Fin(\n", + "\u24ff\n", + ")\n", + "cluster_0\n", + "\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "a\n", + "\u24ff\n", + "\n", + "\n", + "1\n", + "\n", + "1\n", + "\n", + "\n", + "0->1\n", + "\n", + "\n", + "!a\n", + "\n", + "\n", + "-1\n", + "\n", + "\n", + "\n", + "1->-1\n", + "\n", + "b\n", + "\u24ff\n", + "\n", + "\n", + "-1->0\n", + "\n", + "\n", + "\n", + "\n", + "-1->1\n", + "\n", + "\n", + "\n", + "\n", + "\n" + ], + "text": [ + " *' at 0x7f76dc5d7f00> >" + ] + } + ], + "prompt_number": 31 + }, + { + "cell_type": "code", + "collapsed": true, + "input": [], + "language": "python", + "metadata": {}, + "outputs": [], + "prompt_number": null + } + ], + "metadata": {} + } + ] +}