From 19a273929cd664733c8f38ea8a63f26dec51ff53 Mon Sep 17 00:00:00 2001 From: Alexandre Duret-Lutz Date: Fri, 15 May 2015 23:50:19 +0200 Subject: [PATCH] python: rewrite translate() to deal with unambiguous and sbacc and make it easier to extend and use. * src/twaalgos/postproc.hh, src/twaalgos/translate.cc, src/twaalgos/translate.hh: Incorporate the Unambiguous option with the other preferences. It's cleaner this way, and the previous setup did not work well with Python. * src/bin/ltl2tgba.cc: Adjust to this change. * wrap/python/spot.py (translate): Rewrite. * wrap/python/tests/automata.ipynb: Adjust existing cases, and add more as well as some comments. --- src/bin/ltl2tgba.cc | 8 +- src/twaalgos/postproc.hh | 10 +- src/twaalgos/translate.cc | 12 +- src/twaalgos/translate.hh | 15 +- wrap/python/spot.py | 168 +++++--- wrap/python/tests/automata.ipynb | 643 +++++++++++++++++++++++++------ 6 files changed, 666 insertions(+), 190 deletions(-) diff --git a/src/bin/ltl2tgba.cc b/src/bin/ltl2tgba.cc index 829b450a0..df4534140 100644 --- a/src/bin/ltl2tgba.cc +++ b/src/bin/ltl2tgba.cc @@ -83,7 +83,7 @@ const struct argp_child children[] = }; static spot::option_map extra_options; -bool unambiguous = false; +static spot::postprocessor::output_pref unambig = 0; static int parse_opt(int key, char* arg, struct argp_state*) @@ -98,7 +98,7 @@ parse_opt(int key, char* arg, struct argp_state*) type = spot::postprocessor::Monitor; break; case 'U': - unambiguous = true; + unambig = spot::postprocessor::Unambiguous; break; case 'x': { @@ -186,11 +186,9 @@ main(int argc, char** argv) program_name); spot::translator trans(&extra_options); - trans.set_pref(pref | comp | sbacc); + trans.set_pref(pref | comp | sbacc | unambig); trans.set_type(type); trans.set_level(level); - if (unambiguous) - trans.set_pref(spot::translator::Unambiguous); try { diff --git a/src/twaalgos/postproc.hh b/src/twaalgos/postproc.hh index 93f23d528..8c4d3bfc7 100644 --- a/src/twaalgos/postproc.hh +++ b/src/twaalgos/postproc.hh @@ -74,13 +74,11 @@ namespace spot enum { Any = 0, - Small = 1, - Deterministic = 2, - // 3 reserved for unambiguous - // Combine Complete as 'Small | Complete' or 'Deterministic | Complete' + Small = 1, // Small and Deterministic + Deterministic = 2, // are exclusive choices. Complete = 4, - // Likewise. State-based acceptance. - SBAcc = 8, + SBAcc = 8, // State-based acceptance. + Unambiguous = 16, }; typedef int output_pref; diff --git a/src/twaalgos/translate.cc b/src/twaalgos/translate.cc index 496b8ca3c..db915f9bf 100644 --- a/src/twaalgos/translate.cc +++ b/src/twaalgos/translate.cc @@ -28,7 +28,6 @@ namespace spot void translator::setup_opt(const option_map* opt) { comp_susp_ = early_susp_ = skel_wdba_ = skel_simul_ = 0; - unambiguous_ = false; if (!opt) return; @@ -64,12 +63,13 @@ namespace spot twa_graph_ptr translator::run(const ltl::formula** f) { - if (unambiguous_ && type_ == postprocessor::Monitor) + bool unambiguous = (pref_ & postprocessor::Unambiguous); + if (unambiguous && type_ == postprocessor::Monitor) { // Deterministic monitor are unambiguous, so the unambiguous // option is not really relevant for monitors. - unambiguous_ = false; - set_pref(postprocessor::Deterministic); + unambiguous = false; + set_pref(pref_ | postprocessor::Deterministic); } const ltl::formula* r = simpl_->simplify(*f); @@ -94,9 +94,9 @@ namespace spot } else { - bool exprop = unambiguous_ || level_ == postprocessor::High; + bool exprop = unambiguous || level_ == postprocessor::High; aut = ltl_to_tgba_fm(r, simpl_->get_dict(), exprop, - true, false, false, 0, 0, unambiguous_); + true, false, false, 0, 0, unambiguous); } aut = this->postprocessor::run(aut, r); return aut; diff --git a/src/twaalgos/translate.hh b/src/twaalgos/translate.hh index 25bf04d4f..dc00aa2f7 100644 --- a/src/twaalgos/translate.hh +++ b/src/twaalgos/translate.hh @@ -1,6 +1,6 @@ // -*- coding: utf-8 -*- -// Copyright (C) 2013, 2014 Laboratoire de Recherche et Développement -// de l'Epita (LRDE). +// Copyright (C) 2013, 2014, 2015 Laboratoire de Recherche et +// Développement de l'Epita (LRDE). // // This file is part of Spot, a model checking library. // @@ -82,22 +82,12 @@ namespace spot this->postprocessor::set_type(type); } - typedef postprocessor::output_pref output_pref; - enum output_pref_extra { Unambiguous }; - void set_pref(output_pref pref) { this->postprocessor::set_pref(pref); } - void - set_pref(output_pref_extra) - { - unambiguous_ = true; - } - - typedef postprocessor::optimization_level optimization_level; void @@ -130,7 +120,6 @@ namespace spot int early_susp_; int skel_wdba_; int skel_simul_; - bool unambiguous_; }; /// @} } diff --git a/wrap/python/spot.py b/wrap/python/spot.py index fd6e367d8..a46d0c57d 100644 --- a/wrap/python/spot.py +++ b/wrap/python/spot.py @@ -219,65 +219,135 @@ def automaton(filename): except StopIteration: raise RuntimeError("Failed to read automaton from {}".format(filename)) -def translate(formula, output='tgba', pref='small', level='high', - complete=False): +def translate(formula, *args): """Translate a formula into an automaton. Keep in mind that pref expresses just a preference that may not be satisfied. - Keyword arguments: - output -- the type of automaton to build ('tgba', 'ba', 'monitor') - pref -- prefered characteristic of the produced automaton - ('small', 'deterministic', 'any') - level -- level of optimizations ('low', 'medium', 'high') - complete -- whether to produce a complete automaton (True, False) + The optional arguments should be strings among the following: + - at most one in 'TGBA', 'BA', or 'Monitor' + (type of automaton to build) + - at most one in 'Small', 'Deterministic', 'Any' + (perefered characteristics of the produced automaton) + - at most one in 'Low', 'Medium', 'High' + (optimization level) + - any combination of 'Complete', 'Unambiguous', and + 'StateBasedAcceptance' (or 'SBAcc' for short) + + The default correspond to 'tgba', 'small' and 'high'. """ + + type_ = None + pref_ = None + optm_ = None + comp_ = 0 + unam_ = 0 + sbac_ = 0 + + def type_set(val): + nonlocal type_ + if type_ != None and type_ != val: + raise ValueError("type cannot be both {} and {}" + .format(type_, val)) + elif val == 'tgba': + type_ = postprocessor.TGBA + elif val == 'ba': + type_ = postprocessor.BA + else: + assert(val == 'monitor') + type_ = postprocessor.Monitor + + def pref_set(val): + nonlocal pref_ + if pref_ != None and pref_ != val: + raise ValueError("preference cannot be both {} and {}" + .format(pref_, val)) + elif val == 'small': + pref_ = postprocessor.Small + elif val == 'deterministic': + pref_ = postprocessor.Deterministic + else: + assert(val == 'any') + pref_ = postprocessor.Any + + def optm_set(val): + nonlocal optm_ + if optm_ != None and optm_ != val: + raise ValueError("optimization level cannot be both {} and {}" + .format(optm_, val)) + if optm_ == 'high': + optm_ = postprocessor.High + elif optm_.startswith('med'): + optm_ = postprocessor.Medium + else: + assert(level_ == 'low') + optm_ = postprocessor.Low + + def misc_set(val): + nonlocal comp_, unam_, sbac_ + if val == 'complete': + comp_ = postprocessor.Complete + elif val == 'sbacc' or val == 'state-based-acceptance': + sbac_ = postprocessor.SBAcc + else: + assert(val == 'unambiguous') + unam_ = postprocessor.Unambiguous + + options = { + 'tgba': type_set, + 'ba': type_set, + 'monitor': type_set, + 'small': pref_set, + 'deterministic': pref_set, + 'any': pref_set, + 'high': optm_set, + 'medium': optm_set, + 'low': optm_set, + 'complete': misc_set, + 'unambiguous': misc_set, + 'statebasedacceptance': misc_set, + 'sbacc': misc_set, + } + + for arg in args: + arg = arg.lower() + fn = options.get(arg) + if fn: + fn(arg) + else: + # arg is not an know option, but maybe it is a prefix of + # one of them + compat = [] + f = None + for key, fn in options.items(): + if key.startswith(arg): + compat.append(key) + f = fn + lc = len(compat) + if lc == 1: + f(compat[0]) + elif lc < 1: + raise ValueError("unknown option '{}'".format(arg)) + else: + raise ValueError("ambiguous option '{}' is prefix of {}" + .format(arg, str(compat))) + + if type_ == None: + type_ = postprocessor.TGBA + if pref_ == None: + pref_ = postprocessor.Small + if optm_ == None: + optm_ = postprocessor.High + if type(formula) == str: formula = parse_formula(formula) + a = translator(_bdd_dict) - - if type(output) == str: - output_ = output.lower() - if output_ == 'tgba': - output = postprocessor.TGBA - elif output_ == 'ba': - output = postprocessor.BA - elif output_.startswith('mon'): - output = postprocessor.Monitor - else: - raise ValueError("unknown output type: " + output) - a.set_type(output) - - if complete: - complete = postprocessor.Complete - else: - complete = 0 - - if type(pref) == str: - pref_ = pref.lower() - if pref_.startswith('sm'): - pref = postprocessor.Small - elif pref_.startswith('det'): - pref = postprocessor.Deterministic - elif pref_ == 'any': - pref = postprocessor.Any - else: - raise ValueError("unknown output preference: " + pref) - a.set_pref(pref | complete) - - if type(level) == str: - level_ = level.lower() - if level_ == 'high': - level = postprocessor.High - elif level_.startswith('med'): - level = postprocessor.Medium - elif level_ == 'low': - level = postprocessor.Low - else: - raise ValueError("unknown optimization level: " + level) - a.set_level(level) + a.set_type(type_) + a.set_pref(pref_ | comp_ | unam_ | sbac_) + a.set_level(optm_) return a.run(formula) diff --git a/wrap/python/tests/automata.ipynb b/wrap/python/tests/automata.ipynb index 4d99552a4..afede8f1f 100644 --- a/wrap/python/tests/automata.ipynb +++ b/wrap/python/tests/automata.ipynb @@ -15,10 +15,9 @@ "name": "python", "nbconvert_exporter": "python", "pygments_lexer": "ipython3", - "version": "3.4.2" + "version": "3.4.3+" }, - "name": "", - "signature": "sha256:fe7ef6b55c6362768f225bdba28a93036807ecfa50449826c1020f3a80e585f6" + "name": "" }, "nbformat": 3, "nbformat_minor": 0, @@ -37,11 +36,18 @@ "outputs": [], "prompt_number": 1 }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "To build an automaton, simply call `translate()` with a formula, and a list of options to characterize the automaton you want (those options have the same name as the long options name of the `ltl2tgba` tool, and they can be abbreviated)." + ] + }, { "cell_type": "code", "collapsed": false, "input": [ - "a = spot.translate('(a U b) & GFc & GFd', 'BA', complete=True); a" + "a = spot.translate('(a U b) & GFc & GFd', 'BA', 'complete'); a" ], "language": "python", "metadata": {}, @@ -170,17 +176,24 @@ "\n" ], "text": [ - " *' at 0x7fd09041e6c0> >" + " *' at 0x7f44bdf3f8d0> >" ] } ], "prompt_number": 2 }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The call the `spot.setup()` in the first cells has installed a default style for the graphviz output. If you want to change this style temporarily, you can call the `show(style)` method explicitely. For instance here is a vertical layout with the default font of GraphViz." + ] + }, { "cell_type": "code", "collapsed": false, "input": [ - "a.show(\"\")" + "a.show(\"v\")" ], "language": "python", "metadata": {}, @@ -190,124 +203,131 @@ "output_type": "pyout", "prompt_number": 3, "svg": [ - "\n", - "\n", + "\n", + "\n", "G\n", - "\n", + "\n", "\n", "\n", "0\n", - "\n", - "0\n", + "\n", + "0\n", "\n", "\n", "I->0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "0->0\n", - "\n", - "\n", - "a & !b\n", + "\n", + "\n", + "a & !b\n", "\n", "\n", "1\n", - "\n", - "\n", - "1\n", + "\n", + "\n", + "1\n", "\n", "\n", "0->1\n", - "\n", - "\n", - "b\n", + "\n", + "\n", + "b\n", "\n", "\n", "4\n", - "\n", - "4\n", + "\n", + "4\n", "\n", "\n", "0->4\n", - "\n", - "\n", - "!a & !b\n", + "\n", + "\n", + "!a & !b\n", "\n", "\n", "1->1\n", - "\n", - "\n", - "c & d\n", + "\n", + "\n", + "c & d\n", "\n", "\n", "2\n", - "\n", - "2\n", + "\n", + "2\n", "\n", "\n", "1->2\n", - "\n", - "\n", - "!c & d\n", + "\n", + "\n", + "!c & d\n", "\n", "\n", "3\n", - "\n", - "3\n", + "\n", + "3\n", "\n", "\n", "1->3\n", - "\n", - "\n", - "!d\n", + "\n", + "\n", + "!d\n", "\n", "\n", "4->4\n", - "\n", - "\n", - "1\n", + "\n", + "\n", + "1\n", "\n", "\n", "2->1\n", - "\n", - "\n", - "c\n", + "\n", + "\n", + "c\n", "\n", "\n", "2->2\n", - "\n", - "\n", - "!c\n", + "\n", + "\n", + "!c\n", "\n", "\n", "3->1\n", - "\n", - "\n", - "c & d\n", + "\n", + "\n", + "c & d\n", "\n", "\n", "3->2\n", - "\n", - "\n", - "!c & d\n", + "\n", + "\n", + "!c & d\n", "\n", "\n", "3->3\n", - "\n", - "\n", - "!d\n", + "\n", + "\n", + "!d\n", "\n", "\n", "" ], "text": [ - "" + "" ] } ], "prompt_number": 3 }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "If you want to add some style options to the existing one, pass a dot to the `show()` function in addition to your own style options:" + ] + }, { "cell_type": "code", "collapsed": false, @@ -448,12 +468,19 @@ "" ], "text": [ - "" + "" ] } ], "prompt_number": 4 }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The `translate()` function can also be called with a formula object. Either as a function, or as a method." + ] + }, { "cell_type": "code", "collapsed": false, @@ -541,7 +568,7 @@ "\n" ], "text": [ - " *' at 0x7fd090415330> >" + " *' at 0x7f44bdff74b0> >" ] } ], @@ -611,12 +638,19 @@ "\n" ], "text": [ - " *' at 0x7fd090415450> >" + " *' at 0x7f44bdff7510> >" ] } ], "prompt_number": 7 }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "When used as a method, all the arguments are translation options. Here is a monitor:" + ] + }, { "cell_type": "code", "collapsed": false, @@ -680,12 +714,19 @@ "\n" ], "text": [ - " *' at 0x7fd090415270> >" + " *' at 0x7f44bdff73c0> >" ] } ], "prompt_number": 8 }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "The following three cells show a formulas for which it makes a difference to select `'small'` or `'deterministic'`." + ] + }, { "cell_type": "code", "collapsed": false, @@ -723,80 +764,80 @@ "output_type": "pyout", "prompt_number": 10, "svg": [ - "\n", + "\n", "\n", "G\n", - "\n", + "\n", "\n", "\n", "0\n", - "\n", - "0\n", + "\n", + "0\n", "\n", "\n", "I->0\n", - "\n", - "\n", + "\n", + "\n", "\n", "\n", "1\n", - "\n", - "\n", - "1\n", + "\n", + "\n", + "1\n", "\n", "\n", "0->1\n", - "\n", - "\n", - "a\n", + "\n", + "\n", + "a\n", "\n", "\n", "2\n", - "\n", - "\n", - "2\n", + "\n", + "\n", + "2\n", "\n", "\n", "0->2\n", - "\n", - "\n", - "b\n", + "\n", + "\n", + "b\n", "\n", "\n", "3\n", - "\n", - "\n", - "3\n", + "\n", + "\n", + "3\n", "\n", "\n", "0->3\n", - "\n", - "\n", - "c\n", + "\n", + "\n", + "c\n", "\n", "\n", "1->1\n", - "\n", - "\n", - "a\n", + "\n", + "\n", + "a\n", "\n", "\n", "2->2\n", - "\n", - "\n", - "b\n", + "\n", + "\n", + "b\n", "\n", "\n", "3->3\n", - "\n", - "\n", - "c\n", + "\n", + "\n", + "c\n", "\n", "\n", "" ], "text": [ - "" + "" ] } ], @@ -986,17 +1027,24 @@ "" ], "text": [ - "" + "" ] } ], "prompt_number": 11 }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Here is how to build an unambiguous automaton:" + ] + }, { "cell_type": "code", "collapsed": false, "input": [ - "a = spot.translate('F(a & X(!a &Xb))', pref=\"any\"); a" + "spot.translate('GFa -> GFb', 'unambig')" ], "language": "python", "metadata": {}, @@ -1005,6 +1053,372 @@ "metadata": {}, "output_type": "pyout", "prompt_number": 12, + "svg": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "G\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "1\n", + "\n", + "1\n", + "\n", + "\n", + "0->1\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "2\n", + "\n", + "2\n", + "\n", + "\n", + "0->2\n", + "\n", + "\n", + "!a & !b\n", + "\n", + "\n", + "3\n", + "\n", + "3\n", + "\n", + "\n", + "0->3\n", + "\n", + "\n", + "a | b\n", + "\n", + "\n", + "4\n", + "\n", + "4\n", + "\n", + "\n", + "0->4\n", + "\n", + "\n", + "!a & !b\n", + "\n", + "\n", + "1->1\n", + "\n", + "\n", + "b\n", + "\u24ff\n", + "\n", + "\n", + "1->1\n", + "\n", + "\n", + "!b\n", + "\n", + "\n", + "2->2\n", + "\n", + "\n", + "!a & !b\n", + "\u24ff\n", + "\n", + "\n", + "3->2\n", + "\n", + "\n", + "!a & !b\n", + "\n", + "\n", + "3->3\n", + "\n", + "\n", + "a | b\n", + "\n", + "\n", + "3->4\n", + "\n", + "\n", + "!a & !b\n", + "\n", + "\n", + "4->3\n", + "\n", + "\n", + "a | b\n", + "\n", + "\n", + "4->4\n", + "\n", + "\n", + "!a & !b\n", + "\n", + "\n", + "\n" + ], + "text": [ + " *' at 0x7f44bdff7420> >" + ] + } + ], + "prompt_number": 12 + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Compare with the standard translation:" + ] + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "spot.translate('GFa -> GFb')" + ], + "language": "python", + "metadata": {}, + "outputs": [ + { + "metadata": {}, + "output_type": "pyout", + "prompt_number": 13, + "svg": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "G\n", + "\n", + "\n", + "\n", + "1\n", + "\n", + "1\n", + "\n", + "\n", + "I->1\n", + "\n", + "\n", + "\n", + "\n", + "1->1\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "0\n", + "\n", + "0\n", + "\n", + "\n", + "1->0\n", + "\n", + "\n", + "!a\n", + "\n", + "\n", + "2\n", + "\n", + "2\n", + "\n", + "\n", + "1->2\n", + "\n", + "\n", + "b\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "!a\n", + "\u24ff\n", + "\n", + "\n", + "2->2\n", + "\n", + "\n", + "b\n", + "\u24ff\n", + "\n", + "\n", + "2->2\n", + "\n", + "\n", + "!b\n", + "\n", + "\n", + "\n" + ], + "text": [ + " *' at 0x7f44bdff72d0> >" + ] + } + ], + "prompt_number": 13 + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "And here is the automaton above with state-based acceptance:" + ] + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "spot.translate('GFa -> GFb', 'sbacc')" + ], + "language": "python", + "metadata": {}, + "outputs": [ + { + "metadata": {}, + "output_type": "pyout", + "prompt_number": 14, + "svg": [ + "\n", + "\n", + "\n", + "\n", + "\n", + "\n", + "G\n", + "\n", + "\n", + "\n", + "0\n", + "\n", + "0\n", + "\n", + "\n", + "I->0\n", + "\n", + "\n", + "\n", + "\n", + "0->0\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "1\n", + "\n", + "\n", + "0->1\n", + "\n", + "\n", + "!a\n", + "\n", + "\n", + "2\n", + "\n", + "\n", + "2\n", + "\n", + "\n", + "0->2\n", + "\n", + "\n", + "b\n", + "\n", + "\n", + "1->1\n", + "\n", + "\n", + "!a\n", + "\n", + "\n", + "2->2\n", + "\n", + "\n", + "b\n", + "\n", + "\n", + "3\n", + "\n", + "3\n", + "\n", + "\n", + "2->3\n", + "\n", + "\n", + "!b\n", + "\n", + "\n", + "3->2\n", + "\n", + "\n", + "b\n", + "\n", + "\n", + "3->3\n", + "\n", + "\n", + "!b\n", + "\n", + "\n", + "\n" + ], + "text": [ + " *' at 0x7f44bdff7360> >" + ] + } + ], + "prompt_number": 14 + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Some example of running the self-loopization algorithm on an automaton:" + ] + }, + { + "cell_type": "code", + "collapsed": false, + "input": [ + "a = spot.translate('F(a & X(!a &Xb))', \"any\"); a" + ], + "language": "python", + "metadata": {}, + "outputs": [ + { + "metadata": {}, + "output_type": "pyout", + "prompt_number": 15, "svg": [ "\n", "\n" ], "text": [ - " *' at 0x7fd090415390> >" + " *' at 0x7f44bdff7450> >" ] } ], - "prompt_number": 12 + "prompt_number": 15 }, { "cell_type": "code", @@ -1097,7 +1511,7 @@ { "metadata": {}, "output_type": "pyout", - "prompt_number": 13, + "prompt_number": 16, "svg": [ "\n", "\n" ], "text": [ - " *' at 0x7fd090415480> >" + " *' at 0x7f44bdfd9ba0> >" ] } ], - "prompt_number": 13 + "prompt_number": 16 }, { "cell_type": "code", @@ -1571,13 +1985,20 @@ { "metadata": {}, "output_type": "pyout", - "prompt_number": 14, + "prompt_number": 17, "text": [ "False" ] } ], - "prompt_number": 14 + "prompt_number": 17 + }, + { + "cell_type": "markdown", + "metadata": {}, + "source": [ + "Reading from file (see `automaton-io.ipynb` for more examples)." + ] }, { "cell_type": "code", @@ -1616,7 +2037,7 @@ ] } ], - "prompt_number": 15 + "prompt_number": 18 }, { "cell_type": "code", @@ -1631,7 +2052,7 @@ { "metadata": {}, "output_type": "pyout", - "prompt_number": 16, + "prompt_number": 19, "svg": [ "\n", "\n" ], "text": [ - " *' at 0x7fd0904153c0> >" + " *' at 0x7f44c5f30cc0> >" ] } ], - "prompt_number": 16 + "prompt_number": 19 }, { "cell_type": "code", @@ -1764,7 +2185,7 @@ "language": "python", "metadata": {}, "outputs": [], - "prompt_number": 17 + "prompt_number": 20 } ], "metadata": {}