isympy -I: A saner interactive environment

August 31, 2012

As promised, here is another post describing a new feature in the upcoming SymPy 0.7.2.

Automatic Symbol Definition

While not as ground breaking as the feature I described in my last post, this feature is still quite useful. As you may know, SymPy is inherently a Python library, meaning that it lives by the rules of Python. If you want to use any name, whether it be a Symbol or a function (like cos), you need to define it (in the case of Symbols), or import it (in the case of functions that come with SymPy). We provide the script isympy with SymPy to assist with this. This script automatically runs IPython (if it’s installed), imports all names from sympy (from sympy import *), and defines common symbol names (like x, y, and z).

But if you want to use a Symbol that is not one of the ones predefined by isympy, you will get something like

In [1]: r*x
---------------------------------------------------------------------------
NameError                                 Traceback (most recent call last)
 in ()
----> 1 r*x

NameError: name 'r' is not defined

The best solution for this has been either to type var('r'), which will create the Symbol r and inject it into the namespace, or to wrap your text in a string and pass it to sympify(), like sympify("r*x"). Neither of these are very friendly in interactive mode.

In SymPy 0.7.2, isympy has a new command line option, isympy -a, which will enable a mechanism that will automatically define all undefined names as Symbols for you:

In [1]: r*x
Out[1]: r⋅x

There are some caveats to be aware of when using this feature:

  • Names must be undefined for isympy -a to work. If you type something like S*x, you’ll get:
    In [3]: S*x
    ---------------------------------------------------------------------------
    TypeError                                 Traceback (most recent call last)
    <ipython-input-3-6656a97ea7b0> in <module>()
    ----> 1 S*x
    
    TypeError: unsupported operand type(s) for *: 'SingletonRegistry' and 'Symbol'
    

    That’s because S is already defined (it’s the SingletonRegistry, and also a shortcut to sympify()). To use a name that’s already defined, either create it manually with var() or delete it using del.

  • This only works on the top level namespace. If you define a function with an undefined name, it will not automatically define that symbol when run.
  • This works by catching NameError, defining the name, and then re-running the expression. If you have a multiline statement, any lines before the undefined name will be run before the NameError will be caught. This usually won’t happen, but it’s a potential side-effect to be aware of. We plan to rewrite it using either ast or tokenize to avoid this issue.
  • Obviously, this is intended for interactive use only. If you copy code and put it in a script, or in some other place where someone might be expected to run it, but not necessarily from isympy -a, you should include symbol definitions.

Automatic int to Integer Conversion

A second thing that is annoying with Python and SymPy is that something like 1/2 will be interpreted completely by Python, without any SymPy. This means that something like 1/2 + x will give either 0 + x or 0.5 + x, depending on whether or not __future__.division has been imported. isympy has always ran from __future__ import division, so that you’ll get the latter, but we usually would prefer to get Rational(1, 2). Previously, the best way to do this was again to either run it through sympify() as a string, or to sympify at least one of the numbers (here the S() shortcut to sympify() is useful, because you can type just S(1)/2).

With SymPy 0.7.2, you can run isympy -i, and it will automatically wrap all integers literals with Integer(). The result is that 1/2 produces Rational(1, 2):

In [1]: 1/2 + x
Out[1]: x + 1/2

Again, there are a couple of caveats:

  • If you want to get Python style division, you just need to wrap both arguments in int():
    In [2]: int(1)/int(2)
    Out[2]: 0.5
    

    Of course, if you just want a floating point number, you can just use N() or .evalf()

  • This works by parsing the text and wrapping all integer literals with Integer(). This means that if you have a variable set to a Python int, it will still act like a Python int:
    In [6]: a = int(1)
    
    In [7]: b = int(2)
    
    In [8]: a/b
    Out[8]: 0.5
    

    Note that to even do that example, I had to manually make a and b Python ints by wrapping them in int(). If I had just done a = 1, it would have been parsed as a = Integer(1), and I would have gotten a SymPy Integer. But this can be an issue if you use the result of some function that returns an int (again, note that most functions in SymPy that return integers return Integer, not int).

  • The same as before: this will only work interactively. If you want to reuse your code outside of isympy -i, you should take care of any int/int by rewriting it as S(int)/int.

Since these are both useful features, we’ve added a way that you can get them both at once: by doing isympy -I (the “I” stands for “Interactive”). If we add similar features in the future, we will also add them to the -I shortcut (for example, we may add an option to allow ^ to automatically be replaced with **).


SymPy Live Sphinx Extension

August 21, 2012

I didn’t blog about SymPy all summer, so I thought I would write a post about my favorite feature of the upcoming SymPy 0.7.2 release.  In fact, this feature has got me more excited than any other feature from any version of SymPy.  Yeah, it’s that good.

The feature is the SymPy Live Sphinx extension.  To start, if you don’t know about it, check out SymPy Live.  This is a console that runs on the App Engine.  We’ve actually had this for quite some time, but this winter, it got a huge upgrade thanks to the contribution of some GCI students.  Basically, SymPy Live lets you try out SymPy in your browser completely for free, because it runs all the code on the App Engine.  Actually, the console is a full Python console, so you can actually run any valid Python command on it.  This past winter, GCI students upgraded the look of the site, added a mobile version (visit live.sympy.org on your phone), and added other neat features like search history and autocompletion.

Now, Sphinx is the documentation system that we use to generate SymPy’s html documentation. Last year, when I was at the SciPy Conference, Mateusz had an idea at the sprints to create an extension linking SymPy Live and Sphinx, so that the examples in Sphinx could be easily run in SymPy Live.  He didn’t finish the extension, but I’m happy to report that thanks to David Li, who was also one of the aforementioned GCI students, the extension is now complete, and is running live on our development docs.  When SymPy 0.7.2 is released (soon I promise), it will be part of the oficial documentation.

The best way to see how awesome this is is to visit the website and check it out.  You will need a modern browser (the latest version of Firefox, Safari, or Chrome will work, IE might work too).  Go to a page in the development docs with documentation examples, for example, http://docs.sympy.org/dev/tutorial.html#algebra, and click on one of the examples (or click on one of the green “Run code block in SymPy Live” buttons). You should see a console pop up from the bottom-right of the screen, and run your code.  For example:

Example of the SymPy Live Sphinx extension at http://docs.sympy.org/dev/tutorial.html#algebra. Click for larger image.

 

You can access or hide the console at any time by clicking on the green box at the bottom-right of the page.  If you click on “Settings”, you will see that you can change all the same settings as the regular SymPy Live console, such as the printer type, and the keys for execution and autocompletion.  Additionally, there is a new setting, “Evaluation Mode”, which changes how the Sphinx examples are evaluated.  The default is “Evaluate”.  In this mode, if you click on an example, it is executed immediately.  The other option is “Copy”.  In this mode, if you click an example, it is copied to the console, but not executed right away. This way, you can edit the code to try something different.  Remember, this is a full fledged Python console running SymPy, so you can try literally anything

So play with this and let us know what you think.  We would love to hear ways that we can improve the experience even further.  In particular, I think we should think about ways to make the “Copy” mode more user-friendly.  Suggestions welcome!  Also, please report any bugs.

And one word of warning:  even though these are the development docs, SymPy Live is still running SymPy 0.7.1.  So some examples may not work until 0.7.2 is released, at which point we will update SymPy Live.

I believe that this extension represents the future of interactive documentation. I hope you enjoy.