Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Loading…

Plotting framework #673

Closed
wants to merge 26 commits into from

7 participants

@Krastanov
Collaborator

Most of the new lines are from the ipython notebooks in ./examples.

This proposal for a plotting framework easily supports multiple backends (the wish that Certik expressed on multiple occasions). For the moment only matplotlib and textplot have backends (simple ones).

See the docstrings for the module and for the Plot class for details. Example usage is present in example files in the root folder.

The base idea is to have simple representation for the data to be plotted (DataSeries), simple backends implementing some or all of the api and the class Plot that is a nice and simple interface for all this. For anything fancy the user should access directly the backend. For example the matplotlib backend will return the figure/axe/lines instances and the user can work on them.

TODO: current issues (many thanks to asmeurer and miham for raising them):

  • #673 (comment)
  • plotting matrices as 2d and 3d lines (needs free_symbols for matrices)
  • add line_plot, surface_plot, contour_plot, implicit_plot, parametric_plot, list_plot, etc if it makes sense. Or maybe plot_* so tab completion works.
  • saving of plots
  • plotting constants
  • #673 (comment)

DONE:

  • add a minimal version requirement for numpy and matplotlib (a warning? and an if in the unit-tests)
  • #673 (comment) No more problems with array shapes.
  • #673 (comment) - the first issue Now one can plot lists of expressions.
  • #673 (comment) No more problems with complex numbers or numpy.ufunc(Float(1))
  • #673 (comment) - the second note Now axis_center 'auto' is a bit smarter
  • change the number of points
  • #673 (comment)
  • interactive use without --pylab

NEW TODO

#1
Plot() should not do all this magic. All the magic should be in plot()
and Series().

  • Plot() should accept only instances of subclasses of BaseSeries in its constructor.
  • plot() should be fixed to work after the change to Plot()
  • the documentation should explicitly mention that if any heuristics is to be added it should be contained in Series() and plot()
  • the name of Series() should change to something like HeuristicSeries()

#2
Add explicit plot functions:
plot_line, plot_surface, plot_parametric_surface, etc
These again should require fully explicit syntax, no guessing like in plot()

#3
The api should be as follows (both for plot() and for the explicit
plot_something()):

  • plot_some_type_of_graph(expression, tuple_of_variable_and_range, ...)
    ex: plot_surface(x+y, (x, 1, 2), (y, 1, 2))
    result: obvious

  • plot_some_type_of_graph(list_of_expressions, tuple_of_variable_and_range, ...)
    ex: plot_surface([x+y, x-y], (x, 1, 2), (y, 1, 2))
    result: plots two surfaces

  • plot_some_type_of_graph(many_tuples_of(expression,
    tuple_of_variable_and_range, ...))
    ex: plot_surface((x+y, (x, 1, 2), (y, 1, 2)), (x-y, (x, 1, 2), (y, 1, 2)))
    result: the same as the previous example

#4
The ipython profile should be updated

@asmeurer
Owner

At least on my computer, the matplotlib plots don't stay open with the test script, so I can't really see them.

@Krastanov
Collaborator

@asmeurer, try it in interactive mode (-i). The test.py is meant more for copy-pasting.

Just to be clear (I'm adding it to the docstrings right now) the work flow I imagine is to use this module for making a not-too-advanced plot and then using directly the backend (mainly matplotlib) to do any fancy fine-tuning.

The proposition here may be way too simplistic or way to complicated when the stated goals are taken in account. In any case I would like to hear what you think.

@Krastanov
Collaborator

Please also look at the docstring of newplot.Plot - it explains all the options and aesthetics (the choice of names is based loosely on The Grammar of Graphics and the ggplot from R).

@goodok

Please, replace Out [1]: to >>> how it described in [1].
Thereafter thos doctests in the docstrings can be tested automatically. So I will be sure that they are written correctly and in the future development workflow they will not failed.

[1] https://github.com/sympy/sympy/wiki/Running-tests

@Krastanov
Collaborator

There is a reason those examples are not doctests - they were done in Ipython with Ipython's printing hooks, so they will not be the same as what comes from Cpython. As you can see I have not imported the needed objects either.

I suppose I'll rewrite those when I start writing tests. (At least I should.)

sympy/plotting/experimental_lambdify.py
((164 lines not shown))
+ return (numpy_dict, )
+
+##############################################################################
+# The translator functions, tree parsers, etc.
+##############################################################################
+
+def str2tree(exprstr):
+ """Converts an expression string to a tree.
+
+ Functions are represented by ('func_name(', tree_of_arguments).
+ Other expressions are (head_string, mid_tree, tail_str).
+ Expressions that do not contain functions are directly returned.
+
+ Examples:
+ In [10]: str2tree(str(Integral(x, (x, 1, y))))
+ Out[10]: ('', ('Integral(', 'x', '(x, 1, y)'), ')')
@goodok
goodok added a note

My console give me:
('', ('Integral(', 'x, (x, 1, y)'), ')')

The other examples are correct.

@Krastanov Collaborator
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@asmeurer
Owner

Yes, I would minimize those doctests that use IPython to only those that specifically are showing what happens in IPython. Because those doctests will never be tested.

On the other hand, doctests that generate plots will have to be skipped anyway, but even so, we should assume that the average user does not use IPython.

@Krastanov
Collaborator

@asmeurer, On the mailing list @certik proposed to merge this sooner so it can be used, tested and worked on by other people. I agree with him.

The documentation is already written (in docstrings, not on sphinx). The plot function you proposed is there BUT for the moment it just passes its argument to Plot. The old Plot was moved, the documentation for the old plot on sphinx says that there is an alternative and the location of the doctests in the doctests utility was updated.

from sympy import * will import textplot, plot and the old Plot with a warning added. The warning says how to import the OLD Plot without warnings and how to import the new Plot.

The polar, cylindrical and spherical stuff won't be written very soon. The experimental_lambdify should be reviewed in depth.

@Krastanov
Collaborator

One important thing: the change of backends is not explained and it's difficult at the moment (the backend must be imported). But it defaults to matplotlib so there should be no problem.

On the other hand if there is no matplotlib on the system "from sympy import all" will raise an error.

@Krastanov
Collaborator

The last commit should take care of the problems arising from not having matplotlib installed.

@asmeurer
Owner

@asmeurer, On the mailing list @certik proposed to merge this sooner so it can be used, tested and worked on by other people. I agree with him.

I agree. The only thing that should be fixed before merging is a stable API, since that is annoying to change afterwords.

On the other hand if there is no matplotlib on the system "from sympy import all" will raise an error.

This is a problem SymPy should not require any dependencies by default.

By the way, this branch cannot be cleanly merged over master.

@Krastanov
Collaborator

The code is mostly ready. Please check the init.py file. It seems that the plot function is not always imported.

Waiting for a review.

@asmeurer
Owner

Please check the init.py file. It seems that the plot function is not always imported.

This should be fixed so that it's always imported, but raises ImportError if you try to use it without matplotlib (or, it moves on to the next library). Take a look at import_module() in sympy/external/importtools.py. The importing of any external libraries (numpy, matplotlib, etc.) should use this.

@Krastanov
Collaborator

The importing of module is now done. But there is another bug - plot(cos(sqrt(x2+y2))) is not working, it's a problem with the experimental_lambdify function that I'm using. I'll fix it later.

@mrocklin
Collaborator

What is the status on this?

@Krastanov
Collaborator

Works if you have installed the dependencies (matplotlib). I'm currently using it. Has mostly stable api and will be finished after my exams (or GCI). You can start reviewing but there are some obvious things that will change (adding tests, making it not crash on import if matplotlib is not installed, and so on).

The documentation (comments and docstrings) must be reviewed!
The experimental_lambdify function must be reviewed (it's private).

@Krastanov Krastanov closed this
@Krastanov Krastanov reopened this
@Krastanov
Collaborator

I have simplified some parts of the code (the oldest parts that have not changed as the code evolved).

And I have added tests. In the test folder I have placed reference png files that are compared to newly generated files each time the test runs. I am not sure whether this will work on different versions of matplotlib or other OSes.

@Krastanov
Collaborator

SymPy Bot Summary: There were test failures.

@Krastanov: Please fix the test failures.

Test results html report: http://reviews.sympy.org/report/agZzeW1weTNyDAsSBFRhc2sYtYEKDA

Interpreter: /usr/bin/python (2.7.1-final-0)
Architecture: Linux (64-bit)
Cache: yes
Test command: setup.py test
master hash: 9258bb9
branch hash: 666e34e2caa89385fbd970db44b8b0da669fbff2

Automatic review by SymPy Bot.

@Krastanov
Collaborator

@smichr and @asmeurer , would you be able to test this on windows and mac. I have tested it on two different ubuntu installations and it works ok (the failures are in test of integration and another concerning issue 2863, not in my code).

Please run only the new plotting tests: ./bin/test sympy/plotting/tests

You will absolutely need matplotlib from git (there are bugs in the latest stable release): https://github.com/matplotlib/matplotlib (I'm not sure how difficult it is to compile for windows/mac, if it is too much work I will just disable the tests that do not work in the stable release)

And you must install PIL (python-imaging-library). (needed only for the tests, not for plotting)

The tests will also print out a sequence of True/False values. Could you post them if there are any False?

sympy/plotting/plot.py
((1,036 lines not shown))
+ pass
+ else:
+ self.ax.set_xscale(parent.xscale)
+ #XXX In matplotlib xscale resets xlim, so we must set xlim again.
+ self.ax.set_xlim(parent.xlim)
+ if parent.yscale:
+ if isinstance(self.ax, Axes3D):
+ pass
+ else:
+ self.ax.set_yscale(parent.yscale)
+ #XXX In matplotlib yscale resets ylim, so we must set ylim again.
+ self.ax.set_ylim(parent.ylim)
+ if parent.legend:
+ self.ax.legend()
+ self.ax.legend_.set_visible(parent.legend)
+ elif hasattr(self.ax, 'ledend_'):
@miham
miham added a note

Typo?

@Krastanov Collaborator

Nope, I do not think so. legend_ is created after calling legend() and consequent manipulations are done on it. But maybe there is a better and more OOP method to write this? I learned most of matplotlib while writing this module so there may be some anti-patterns present.

@miham
miham added a note

I should be more specific :). In line 907, is it suppose to be legend_ or ledend_?

@Krastanov Collaborator

Oh god! You are right. Thanks for spotting this. I will add also a test for deleting a legend.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@miham

I've only used the new plot function for a couple plots, but so far I have to say it feels really nice. The notebook examples are also a joy to play with.

Some functionality i would like to see (but there's no hurry, I wouldn't mind implementing it myself when i find the time):

  • Change default location of the legend when matplotlib is used to 'best' (plt.plot(..., loc='best')). That way the legend box tries not to cover the curves.
  • Automatically draw new lines with different colors. In the following example all lines are blue,
p = plot(besselj(0, x), (x, 0, 10), show=False)
for i in xrange(1,5):
    p.extend(plot(besselj(i, x), (x, 0, 10), show=False))
p.legend = True
p.show()
  • Implement __iadd__ for Plot objects, so += can be used instead of extend function.
@Krastanov
Collaborator
@miham

Feel free to add those to the issue tracker. That way they will not be lost. And I agree about each of those.

Should I add it to 2845 or create a new issue like "plotting enhancements".

It would be great help if you can run the tests on this (you will need matplotlib from git). For the moment they run well > on my machine but it is hard to test graphics so I imagine that there will be quite some changes before the testing
routines are ready. Those that are in the code at the moment are just a preliminary idea that must be tested.

Sure, I'll do it tomorrow.

@Krastanov
Collaborator
@asmeurer asmeurer commented on the diff
sympy/plotting/plot.py
((903 lines not shown))
+ and isinstance(args[1], Expr)
+ and isinstance(args[2], Expr)
+ and isinstance(args[3], Tuple)
+ and len(args[3]) == 3):
+ inst = Parametric3DLineSeries(*args)
+ elif (len(args) == 5
+ and isinstance(args[0], Expr)
+ and isinstance(args[1], Expr)
+ and isinstance(args[2], Expr)
+ and isinstance(args[3], Tuple)
+ and isinstance(args[4], Tuple)
+ and len(args[3]) == 3
+ and len(args[4]) == 3):
+ inst = ParametricSurfaceSeries(*args)
+ else:
+ raise ValueError('The supplied argument do not correspond to a'
@asmeurer Owner

argument => arguments

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@asmeurer
Owner

So I've started playing around with this, now that I need to do some plotting for my homework. I get the following:

In [4]: a = plot(sqrt(-alpha - 1))
<string>:1: RuntimeWarning: invalid value encountered in sqrt

This does create the plot.

It also never returns, unless I close the plot. Is this the intended behavior? It isn't very helpful.

@asmeurer
Owner

Next question: what's the easiest way to plot multiple equations at once? I tried passing a list, but it just gave me an error. I tried .extend, but this didn't work (possibly because I had to close the other plot to get control back).

@asmeurer
Owner

OK, I figured it out. You have to pass them as 1-element tuples. I guess this makes sense because you can combine different kinds of plots, though it still seems to me that passing a list of multiple equations should work. I'll have to think about it.

@asmeurer
Owner

So maybe I am wrong? This give me an error: plot((0,), ((alpha - 1)/alpha,)).

@asmeurer
Owner

Must be a bug. This works fine: plot((alpha,), ((alpha - 1)/alpha,)).

@Krastanov
Collaborator

About the first problem: "It returns only after closing the plot." This is because matplotlib creates an eventloop that does not play well with the interpreter. If you use Ipython try to start it with the --pylab argument. If it is still not working it is a bug in my code.

I suppose that this module should be smart enough to do this by itself, so I'll try to find out what --pylab does to separate the eventloop.

@Krastanov
Collaborator

The second problem about plotting (0,). It does not work because plot cannot decide if this must be a line or a surface or some kind of a parametric plot. Presumably (0, (x, 0, 10)) will work because there is enough information to decide to plot a line. Do you think that (0,) should automatically return a line? I do not have an opinion on this.

@asmeurer
Owner

OK, here's a stress test:

In [24]: plot(*zip(solve(x**4 - alpha*x**3 + 1, x)))
---------------------------------------------------------------------------
AttributeError                            Traceback (most recent call last)
/Users/aaronmeurer/Documents/Python/sympy/sympy/<ipython-input-24-68ccdc4354d8> in <module>()
----> 1 plot(*zip(solve(x**4 - alpha*x**3 + 1, x)))

/Users/aaronmeurer/Documents/Python/sympy/sympy/sympy/plotting/plot.pyc in plot(*args, **kwargs)
    313     p = Plot(*list_of_plots, **kwargs)
    314     if show:
--> 315         p.show()
    316     return p
    317 

/Users/aaronmeurer/Documents/Python/sympy/sympy/sympy/plotting/plot.pyc in show(self)
    194             self._backend.close()
    195         self._backend = self.backend(self)
--> 196         self._backend.show()
    197 
    198     def save(self, path):

/Users/aaronmeurer/Documents/Python/sympy/sympy/sympy/plotting/plot.pyc in show(self)
    913 
    914     def show(self):
--> 915         self.process_series()
    916         #TODO after fixing https://github.com/ipython/ipython/issues/1255

    917         # you can uncomment the next line and remove the pyplot.show() call


/Users/aaronmeurer/Documents/Python/sympy/sympy/sympy/plotting/plot.pyc in process_series(self)
    814             # Create the collections

    815             if s.is_2Dline:
--> 816                 collection = LineCollection(s.get_segments())
    817                 self.ax.add_collection(collection)
    818             elif s.is_contour:

/Users/aaronmeurer/Documents/Python/sympy/sympy/sympy/plotting/plot.pyc in get_segments(self)
    413 
    414     def get_segments(self):
--> 415         points = self.get_points()
    416         if self.steps == True:
    417             x = np.array((points[0],points[0])).T.flatten()[1:]

/Users/aaronmeurer/Documents/Python/sympy/sympy/sympy/plotting/plot.pyc in get_points(self)
    482             list_x = np.linspace(self.start, self.end, num=self.nb_of_points)
    483         f = vectorized_lambdify([self.var], self.expr)
--> 484         list_y = f(list_x)
    485         return (list_x, list_y)
    486 

/sw/lib/python2.7/site-packages/numpy/lib/function_base.pyc in __call__(self, *args)
   1860             for arg in args:
   1861                 newargs.append(asarray(arg).flat[0])
-> 1862             theout = self.thefunc(*newargs)
   1863             if isinstance(theout, tuple):
   1864                 self.nout = len(theout)

/Users/aaronmeurer/Documents/Python/sympy/sympy/<string> in <lambda>(alpha)

AttributeError: sqrt

The *zip is a work-around to plot a list of equations.

@Krastanov
Collaborator

It is a problem in the lambdify function that I am using. It is a private one in the module, not the one from utilities because I had to made some modifications.

@asmeurer
Owner

Thanks. --pylab worked.

@asmeurer
Owner

I see. All this automatic stuff is making me think that we should also create an interface that lets you be very explicit about what you want (2d or 3d, parametric or regular, etc.).

@asmeurer
Owner

I guess 0 should be a line if the rest of the plot is 2d and a surface if the rest of the plot is 3d. How does that sound?

@asmeurer
Owner

Here's another traceback. It showed the plot just fine:

In [8]: plot(x*y)
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
/Users/aaronmeurer/Documents/Python/sympy/sympy/<ipython-input-8-44dd698262d4> in <module>()
----> 1 plot(x*y)

/Users/aaronmeurer/Documents/Python/sympy/sympy/sympy/plotting/plot.pyc in plot(*args, **kwargs)
    313     p = Plot(*list_of_plots, **kwargs)
    314     if show:
--> 315         p.show()
    316     return p
    317 

/Users/aaronmeurer/Documents/Python/sympy/sympy/sympy/plotting/plot.pyc in show(self)
    194             self._backend.close()
    195         self._backend = self.backend(self)
--> 196         self._backend.show()
    197 
    198     def save(self, path):

/Users/aaronmeurer/Documents/Python/sympy/sympy/sympy/plotting/plot.pyc in show(self)
    913 
    914     def show(self):
--> 915         self.process_series()
    916         #TODO after fixing https://github.com/ipython/ipython/issues/1255

    917         # you can uncomment the next line and remove the pyplot.show() call


/Users/aaronmeurer/Documents/Python/sympy/sympy/sympy/plotting/plot.pyc in process_series(self)
    907         elif hasattr(self.ax, 'ledend_'):
    908             self.ax.legend_.set_visible(parent.legend)
--> 909         self.ax.set_autoscale_on(parent.autoscale)
    910         if parent.margin:
    911             self.ax.set_xmargin(parent.margin)

/sw/lib/python2.7/site-packages/mpl_toolkits/mplot3d/axes3d.pyc in set_autoscale_on(self, b)
    252         """
    253         Axes.set_autoscale_on(self, b)
--> 254         self.set_autoscalez_on(self, b)
    255 
    256     def set_autoscalez_on(self, b) :

TypeError: set_autoscalez_on() takes exactly 2 arguments (3 given)
@Krastanov
Collaborator

The second traceback (about the 3d plots) is a bug in matplotlib. It is fixed in their git master.

@asmeurer
Owner

Ah, OK. Will the bug affect me other than seeing the traceback?

@asmeurer
Owner

Is there a way to do implicit plots?

@Krastanov
Collaborator

About implicit plots: maybe for simple cases you can use nsolve (that is why I was pushing for symbolic expressions created from nsolve). But for the moment there is nothing implemented here that will create an implicit plot.

About the matplotlib bug: It is possible to have your plot strangely scaled. Or having an empty figure because the stuff was plotted outside of the viewing window. I was not tested.

@Krastanov
Collaborator

About using nsolve for implicit plots, check out the notebook in examples/beginner/plot_advanced.ipnb

@asmeurer
Owner

Any thoughts on what the syntax for them would be?

@Krastanov
Collaborator

I have not thought about implicit plots. And by the way because of all the automatic stuff it becomes hard to add new types of plots with automatic arguments (contour and 3D surface for example would have indistinguishable input).

Another thing that was not done is volume plots. Mathematica does them very nicely. A ball (not sphere) will be something like Plot[If[Sqrt(x2+y2+z**2)<1]]. There is no obvious way to do those in matplotlib. On the other hand something that would be nice (and easy to add) is vector field plots. I may do those soon as I need them for a class on dynamical systems.

And about the tests. There are already 2.2 MB of pngs for tests. I am not sure whether it is acceptable to have them in the code repository. They may grow and become a burden. Especially as a change in one of them will leave the old one in the git history. Should they be removed (or maybe saved with a much lower dpi)?

@Krastanov
Collaborator

About the first traceback reported by @asmeurer. The problem boils down to:

In [4]: np.sqrt(Float(2))
AttributeError: sqrt
@Krastanov
Collaborator

@asmeurer, your stress-test should be working now. A ton of warnings are raised because of complex values, but otherwise it plots nicely. Two bugs were discovered with that test. One of them is in the doctests now (regex stuff in lambdify). The other one will be added to the tests and examples later.

@Krastanov
Collaborator

There is another thing also. Now the private lambdify in this module uses from __future__ import division. Otherwise sqrt(sqrt(x)) is a constant.

@asmeurer
Owner

As far as making things work automatically, that's tricky. WolframAlpha does an implicit plot if you write "=0" at the end of the equation. I think we should just try to make things as smart as we can, even to the point of guessing, but, also provide an API that lets you define very explicitly what you want. The former would be for interactive use and the latter for scripts and so on.

Any thoughts on what the latter API would look like. I think we should either have separate functions for each type of plot (plot3d, plot_implicit, etc.), or allow specification via some kind of keyword arguments (like plot(x**3*y + 1, implicit=True)). There are good arguments for both ways. Maybe it should be discussed on the list.

@asmeurer
Owner

Another error.

In [29]: print solve(exp(alpha*x) - x**2, x)[0]
-2*LambertW(-alpha/2)/alpha

In [30]: plot(solve(exp(alpha*x) - x**2, x)[0])
---------------------------------------------------------------------------
ValueError                                Traceback (most recent call last)
/Users/aaronmeurer/Documents/Python/sympy/sympy/<ipython-input-30-0d6ce1622583> in <module>()
----> 1 plot(solve(exp(alpha*x) - x**2, x)[0])

/Users/aaronmeurer/Documents/Python/sympy/sympy/sympy/plotting/plot.pyc in plot(*args, **kwargs)
    313     p = Plot(*list_of_plots, **kwargs)
    314     if show:
--> 315         p.show()
    316     return p
    317 

/Users/aaronmeurer/Documents/Python/sympy/sympy/sympy/plotting/plot.pyc in show(self)
    194             self._backend.close()
    195         self._backend = self.backend(self)
--> 196         self._backend.show()
    197 
    198     def save(self, path):

/Users/aaronmeurer/Documents/Python/sympy/sympy/sympy/plotting/plot.pyc in show(self)
    913 
    914     def show(self):
--> 915         self.process_series()
    916         #TODO after fixing https://github.com/ipython/ipython/issues/1255

    917         # you can uncomment the next line and remove the pyplot.show() call


/Users/aaronmeurer/Documents/Python/sympy/sympy/sympy/plotting/plot.pyc in process_series(self)
    814             # Create the collections

    815             if s.is_2Dline:
--> 816                 collection = LineCollection(s.get_segments())
    817                 self.ax.add_collection(collection)
    818             elif s.is_contour:

/sw/lib/python2.7/site-packages/matplotlib/collections.pyc in __init__(self, segments, linewidths, colors, antialiaseds, linestyles, offsets, transOffset, norm, cmap, pickradius, **kwargs)
    895             **kwargs)
    896 
--> 897         self.set_segments(segments)
    898 
    899     def set_segments(self, segments):

/sw/lib/python2.7/site-packages/matplotlib/collections.pyc in set_segments(self, segments)
    904 
    905             if not np.ma.isMaskedArray(seg):
--> 906                 seg = np.asarray(seg, np.float_)
    907             _segments.append(seg)
    908         if self._uniform_offsets is not None:

/sw/lib/python2.7/site-packages/numpy/core/numeric.pyc in asarray(a, dtype, order)
    233 
    234     """
--> 235     return array(a, dtype, copy=False, order=order)
    236 
    237 def asanyarray(a, dtype=None, order=None):

/Users/aaronmeurer/Documents/Python/sympy/sympy/sympy/core/expr.pyc in __float__(self)
    159             return float(result)
    160         else:
--> 161             raise ValueError("Symbolic value, can't compute")
    162 
    163     def __complex__(self):

ValueError: Symbolic value, can't compute

and it opens an empty plot, which I guess is an additional bug.

@asmeurer
Owner

Regarding the stress test, is there a way to make it show the end points more smoothly? I noticed the problem with more simple square roots as well (try plot((sqrt(x),), (-sqrt(x),)).

Unrelated: I noticed a bug plotting plot((sqrt(x - 1),), (-sqrt(x - 1),)). The y-axis is drawn to the left of the plot. I can upload an image if you can't reproduce it. I still haven't been using the git matplotlib, so I would be surprised if it's that (or some other matplotlib bug, for that matter).

@asmeurer
Owner

Just for the record, I'm not a fan of how plotting a list of equations doesn't work, because it expects it to be parametric. Maple differentiates parametric plots by requiring a range (see http://www.maplesoft.com/support/help/Maple/view.aspx?path=plot/parametric). We could do the same, so that plot([sin(x), cos(x)]) plots sin(x) and cos(x) on top of each other, but plot([sin(x), cos(x), [x, 0, 2*pi]), plots a circle. We could allow just (x,) to compute the range automatically. What are your thoughts.

Continuing the discussion about automatic heuristics: since they are heuristics, perhaps we should just drop the pretense of having a set API of doing one thing gives one thing and another another. At some point, it becomes too hard to remember if you need a tuple of tuples or if it should be rezipped and if so with or without the * to unpack it, and so on... I think we should make the automatic API just try to be smart (it would be set, but not in such a way that it needs to be precise, or even rememberable by the user). This would be used in interactive mode, where you just send the function what you want and it (hopefully) gives it to you.

Then, there would be a set API, which is very precise, and which doesn't allow any room for confusion. I'm still debating whether this would be better done with separate functions, or if we should allow keyword arguments to plot(), which would override the "auto" settings when there is ambiguity. Perhaps we should move this discussion on list. Is there already a thread for it somewhere, or should I start a new one (sorry, I'm still way behind on email from GCI)?

@Krastanov
Collaborator
@Krastanov
Collaborator
@Krastanov
Collaborator
@Krastanov
Collaborator

Thanks for reporting this. I would look at this. I may have to install another version of numpy, as I don't get this error with my current system.

@asmeurer
Owner

If it turns out to be a numpy bug, you can require a certain version with import_module (I didn't check if you are, but you should be using that for all external dependencies).

@Krastanov
Collaborator

OK. I will check all those issues later this week (I have added a list with the raised issues in the PR summary). Thanks for the feedback.

@Krastanov
Collaborator

@miham , could you please run the tests once again sometime this week? Besides the FAILED/PASS message it should print some boolean values. I would like to know them as well.

@asmeurer , I have worked on most of the issues that you have raised. Now lists of expressions can be plotted and it works better with complex results. Some expressions containing calls to re or im still fail, but I will work on them later. Plotting of constant still fails if it is not explicit.

@mrocklin
Collaborator

Some thoughts.

In November there was discussion that this should be pushed sooner rather than later. I agree and would like to encourage this again, even if the PR has bugs. It will need to be tested by a large number of people before its ready. I suggest that a semi-buggy version be pushed into master but that the plot function isn't put into the general namespace (i.e. users would have to explicitly import plot from sympy.) This would keep general users away from it while the interface is volatile but allow those of us who know/care to play with it more. A future pull request could be set up to fix the discovered issues and then put plot into the general namespace.

My two cents anyway.

plot(cos(pix), sin(pix)) looks bad on my machine. Spacing between sample points should be decreased significantly. I don't think that this is anywhere near being computationally expensive.

NumPy dependence. Does this module fail entirely without numpy? I suspect that much of the code could be written in a numpy/list agnostic way. Lists should be able to quickly handle 90% of the use cases (i.e. one dimensional plotting). Is there a way to fall back on this? I suspect someone out there has made a pure-python numpy clone. An ndarray object with a list backend would be easy to do.

--pylab
This does more than we want. It pulls in lots of other functions that we don't particularly need or care about. I remember ipython used to have a -wthread option that would allow you to open up asynchronous plot windows without having to import all of the numpy/matplotlib functions. I can't find it in the new ipython version. Is there any way to get this multi-window functionality into the isympy executable?

Style
For some reason a simple plot like plot(x**2) looks aesthetically off. The functionality is wonderful but there is something about the pyglet and Mathematica style plots that just looked nicer. Perhaps its as simple as including a nicer border and changing linewidths/tick-sizes. I know that matplotlib has a decent way to specify a general plot style in a matplotlib.rc file. I wonder if, down the road, this is something that SymPy might want to look into. It would have made an excellent GCI project.

plot(x*y) generates an error for me. It will also plot (despite the error) using ipython --pylab but not in isympy.

In [6]: plot(x*y)

TypeError Traceback (most recent call last)
/home/mrocklin/workspace/sympy/ in ()
----> 1 plot(x*y)

/home/mrocklin/workspace/sympy/sympy/plotting/plot.pyc in plot(args, *kwargs)
351 p.extend(plot_argument)
352 if show:
--> 353 p.show()
354 return p
355

/home/mrocklin/workspace/sympy/sympy/plotting/plot.pyc in show(self)
195 self._backend.close()
196 self._backend = self.backend(self)
--> 197 self._backend.show()
198
199 def save(self, path):

/home/mrocklin/workspace/sympy/sympy/plotting/plot.pyc in show(self)
947
948 def show(self):
--> 949 self.process_series()
950 #TODO after fixing ipython/ipython#1255

951         # you can uncomment the next line and remove the pyplot.show() call

/home/mrocklin/workspace/sympy/sympy/plotting/plot.pyc in process_series(self)
915 if parent.ylim:
916 self.ax.set_ylim(parent.ylim)
--> 917 self.ax.set_autoscale_on(parent.autoscale)
918 if parent.axis_center:
919 val = parent.axis_center

/home/mrocklin/Software/epd-7.2-1-rh5-x86/lib/python2.7/site-packages/mpl_toolkits/mplot3d/axes3d.pyc in set_autoscale_on(self, b)
252 """
253 Axes.set_autoscale_on(self, b)
--> 254 self.set_autoscalez_on(self, b)
255
256 def set_autoscalez_on(self, b) :

TypeError: set_autoscalez_on() takes exactly 2 arguments (3 given)

@miham

@miham , could you please run the tests once again sometime this week? Besides the FAILED/PASS message it > should print some boolean values. I would like to know them as well.

Here is the full output of the test runner and matplotlib information: https://gist.github.com/1772811 There is a failure, but no boolean values.

@Krastanov
Collaborator
@miham

I can't spot a difference between the plots by eye, but there is a difference in file size: ref_advanced_fin_sum.png takes12.8 KB of space, while test_advanced_fin_sum.png takes 13.6 KB.

Edit: I didn't notice the above comment by Krastanov ;)

@Krastanov
Collaborator

plot(cos(pix), sin(pix)) looks bad on my machine. Spacing between sample points should be decreased significantly. I don't think that this is anywhere near being computationally expensive.

I will check those and add an option to change the number of points. In the future it would be done automatically but for the moment I will just set a bigger default value.

NumPy dependence. Does this module fail entirely without numpy?

If matplotlib is not installed it will use an ascii plot. If matplotlib is installed numpy will also be installed as a dependency. When new backends are written some part of the code should be reviewed in order not to use numpy but it would be straightforward.

--pylab
This does more than we want. It pulls in lots of other functions that we don't particularly need or care about. I remember ipython used to have a -wthread option that would allow you to open up asynchronous plot windows without having to import all of the numpy/matplotlib functions. I can't find it in the new ipython version. Is there any way to get this multi-window functionality into the isympy executable?

Yes, there is the ion function from matplotlib (meaning "interactive on"). But I have no idea of the details. I need to check this out.

Style
For some reason a simple plot like plot(x**2) looks aesthetically off.

In the backend class for matplotlib there is the process_series function. All style changes are hard-coded there for the moment.

plot(x*y) generates an error for me.

bug in matplotlib. fixed only in their master. I should add a workaround for this.

Thanks for the feedback.

@Krastanov
Collaborator

@miham, Could you send me the test_* file (on the gist or by mail) so I can check how sensitive the tests should be? The difference in size should not be a problem.

@miham

Here's the test plot: http://postimage.org/image/7a0wwhe9f/

How do you attach it to gist?

@Krastanov
Collaborator

Oh, I just saw the uploaded image. This should be sufficient. Thanks!

I'm not sure how to add it to a gist. Maybe by cloning it and pushing the image in.

@miham

I'm not sure how to add it to a gist. Maybe by cloning it and pushing the image in.

Good idea, it worked!

@Krastanov
Collaborator

@miham, I have increased the tolerance when comparing images. Now it should work. I would be interested to know if other images fail now. It would be great if you can run the tests again.

@miham

Now ref_colors_surface_arity3b.png is different. I uploaded the test version to gist.

@Krastanov
Collaborator

@miham, I increased the tolerance even more. I hope that it works now.

If it is still not working you may try to adjust it. It is in test_plot.py in the image comparison function.

@miham

For me the tests pass for tolerance >= 0.016.

sympy/plotting/tests/test_plot.py
((142 lines not shown))
+ s = summation(1/x**y,(x,1,oo))
+ p = plot(s, (2,10), show=False)
+ p.save('%s_advanced_inf_sum.png' % name)
+
+ p = plot(summation(1/x,(x,1,y)), (2,10), show=False)
+ p[0].only_integers = True
+ p[0].steps = True
+ p.save('%s_advanced_fin_sum.png' % name)
+
+
+def test_matplotlib():
+ try:
+ import matplotlib
+ import PIL
+ if matplotlib.__version__ < '1.2.0':
+ return
@miham
miham added a note

Raise an error here (line 157), because otherwise the tests pass when matplotlib < 1.2 is used.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@miham

plot() could also accept dictionary and plot(dict) would produce something similar to matplotlib's bar(dict.keys(), dict.values(), align='center'). A practical example:

from sympy.stats import *
import matplotlib.pyplot as plt
d1 = Die(6)
d2 = Die(6)
dic = Density(d1 + d2)
plt.bar(dic.keys(), dic.values(), align='center')
@miham

The xlabel doesn't work. Unlike xlim, xlabel doesn't even get it to backend.

p = plot(sin(x)/x, show=False)
p.xlabel = 'x axis'
p.show()
print p.xlabel
print p._backend.ax.get_xlabel()

Output is only one 'x axis'.

@Krastanov
Collaborator

Thanks for reporting. You probably have found another typo like the legend stuff. I will fix both of those when I have the time. And thanks for running the tests.

@Krastanov
Collaborator

I am doing some rebasing so I can remove the png images from the notebooks. I hope nobody is working on this branch at the moment. Sorry for the inconvenience.

@Krastanov
Collaborator

@asmeurer @mrocklin @miham I would like to push that for inclusion. The outstanding issues were cleared. Most importantly:

  • now it is interactive without --pylab
  • all tested expression were plotted correctly (including those containing imaginary parts) (a new lambdify function is used for them as the old one covers only a very small subset of sympy expressions)
  • list of expressions are correctly plotted
  • all big png files were removed from the branch history

I have the feeling that the default number of points per plot is too big but we will see.

@Krastanov
Collaborator

@asmeurer @mrocklin @miham I would like to push that for inclusion. The outstanding issues were cleared. Most importantly:

  • now it is interactive without --pylab
  • all tested expression were plotted correctly (including those containing imaginary parts) (a new lambdify function is used for them as the old one covers only a very small subset of sympy expressions)
  • list of expressions are correctly plotted
  • all big png files were removed from the branch history

I have the feeling that the default number of points per plot is too big but we will see.

@mrocklin
Collaborator

On my machine I get your repo with the latest updates, run bin/isympy, type plot(x**2).

An empty window appears. I still have control of the isympy session. I don't seem to be able to close the window. When I finally close isympy the plot renders and then immediately closes.

Even though matplotlib is interactive isympy still has only one thread and doesn't seem to be able to manage both the window and the isympy session simultaneously, at least on my machine. The --pylab flag, along with importing lots of numpy and matplotlib stuff, also gives ipython the ability to handle multiple windows at once. There used to be a -wthread flag, that allows multi-window use without flooding the namespace with numpy and matplotlib functions.

@Krastanov
Collaborator

@mrocklin You are right. I have removed the changes that switch automatically matplotlib to interactive mode. I was not at all the right solution.

Looking at http://matplotlib.sourceforge.net/users/shell.html it seems that matplotlib suggests that the problem should be solved in the shell and not in the library. That means that we should try to fix isympy and suggest some changes directly to ipython. Do you agree on this?

Now that the last commit was removed I again suggest getting this merged (or at least ripped down in criticism ;-) )

@Krastanov
Collaborator

About the interactive stuff, it seems that there is the pylab_import_all configuration option that defaults to True. I will see if I can make it work using that.

@Krastanov
Collaborator

@mrocklin adding app.pylab_import_all = False to the sympy profile in ipython fixes the interactive problem.
@asmeurer Do you guys consider this an appropriate fix?

With this option plotting is interactive in Ipython but not in Cpython. It is exactly what the matplotlib team has chosen to do in their case. Otherwise we will have to hack around every eventloop used by matplotlib and will just duplicate work done by the ipython guys.

@mrocklin
Collaborator

I'm not sure I entirely understand. What is the behavior of typing in
isympy
and what is the behavior of typing in
isympy -pylab

I think that ideally the first would enable threading and interactive sympy plot windows and that the second would do this and also import all the numpy/matplotlib stuff.

Regarding non-interactive plotting in standard python - isn't this (as you say) standard already? This isn't a problem we should expect ourselves to fix.

@Krastanov
Collaborator

isympy -pylab won't work as -pylab is not a flag for isympy.

When isympy uses python the plots wont be interactive or in a separate thread. When isympy uses ipython we have some options:

  • make ipython use --pylab : this will result in interactive plots in a separate thread but with a polluted namespace
  • make ipython use --pylab and pylab_import_all=False : interactive plots in a separate thread, np, matplotlib and pyplot get imported (just the modules, not a star import from them)
  • don't use --pylab : no interactive plots and no separate thread

What I tried to say was that making the plots interactive in the ordinary python interpreter is too much work for nothing - we will be just repeating all the work done by ipython. And this page http://matplotlib.sourceforge.net/users/shell.html suggests that this was quite a bit of effort (each of matplotlib's toolkit backends needs separate treatment).

@Krastanov
Collaborator

We can of course add the --pyplot or similar option to isympy but only after patching the ipython profile upstream in ipython.

@miham

@Krastanov, the problem with setting the limits of x and y axis that you mention in plot_intro.ipynb is not limited to IPython notebook. I tried to set the limits in QtConsole and in Python script and in both cases the values did come to the backend, but were not honoured in the actual plot. More precisely the limits are not honoured when they are outside of the default interval [-10, 10]. So setting xlim to [-3, 2] works, while setting it to [-18, 12] doesn't.

@Krastanov
Collaborator

@miham, I will check this. Thanks for reporting.

@Krastanov
Collaborator

@miham, I was unable to reproduce it outside of the notebook/qtconsole.

In their case I suppose it is a bug in the inline backend in Ipython. But if it happens also in a script it could be a bug in my code. Can you upload the script that you are using to a gist, so I can test it?

@miham

Here's the script: https://gist.github.com/1860594

I'll add the plot i get in a minute..

Edit: plot added.

@Krastanov
Collaborator

In both python and ipython I get the correct plot. Are you using some special flags when running the script? And again, if it fails in notebook/qtconsole it is because of the their inline backend.

One more thing. xlim and ylim are for the viewing window not for the interval over which to calculate. If you want those, you should specify them per expression. In this case it would be something like

p = plot(...)
p[0].start = ...
p[0].end = ...

Maybe it should be discussed whether this api is appropriate.

@Krastanov
Collaborator

I checked the gist. I consider the behavior correct as xlim and ylim are for the viewing window. If you and the others disagree we can change it, but what it would mean for example in the case of parametric plots?

@miham

You're right. I had strange expectations of xlim :)

Btw, are there going to be any tests for the plotting module? You removed the images, because they were too big, but other things can still be tested. For example if options come from Plot to the backend etc. Do you think it would be worth it?

@Krastanov
Collaborator

Yeah, testing those would be worth it. For the moment the ipython notebooks contain the same stuff that was in the tests, it is just not automatized. I will not have the time to write more detailed tests anytime soon. And of course I am keeping the old test but in another branch, so they can be easily ran before release, etc.

@asmeurer
Owner

I vote for the option that uses a separate thread. Doing otherwise is very annoying. We can even manually delete the module names from the namespace. Or see what IPython does and if we can just do it without importing at all (or patch something like that upstream if not).

@asmeurer
Owner

Plotting the real part of complex numbers doesn't seem to be a very good idea to me, other than as a workaround for very small imaginary parts that don't disappear. Consider for example plot((x - 1 + sqrt(x**2 + 2*x - 3))/2). Instead of plotting both parts separately, it connects the two parts with a line (representing the real part in that region). If I try to plot the algebraic curve plot((x - 1 + sqrt(x**2 + 2*x - 3))/2, plot((x - 1 - sqrt(x**2 + 2*x - 3))/2)), I get something that looks really wierd: the curve(s) with a line connecting the critical points.

And even something as simple as sqrt(x) will have a strange line coming out of it in the non-real part (see for example plot([sqrt(x) + 1, x])).

@asmeurer
Owner

I don't remember if I mentioned this before, but just plotting a number does not work.

@Krastanov
Collaborator

@asmeurer

Threads:
Using a separate thread without polluting the namespace is easy in Ipython and already implemented by them. The only thing that must be changed is the sympy profile (adding pylab_import_all=False). Doing it in the standard python interpreter is beyond what I plan to do (I don't think it would be useful, but if somebody needs it, he can always implement it (it seems that it is quite a bit of work)).

Complex numbers:
What would you prefer? Not plotting anything seems like the correct choice, but this will create another problem: the bifurcation point is not detected, so the actual point that is plotted will just be somewhere near. This will cause a missing part in the line. This will be fixed when somebody adds some adaptive choice for the number of points. We may add a flag for that. By the way, there was no need to chain those plot commands. The feature you requested (plotting lists of expressions) is already implemented, so you can just use plot([expr1, expr2]).

Constants:
You have mentioned it. There was the ambiguity of whether this should be a surface or a line, but we decided on some smart defaults. It still needs to be special cased. It should not be difficult at all.

I will not have the time to work on this in the near future. If somebody wants to help I can explain parts of the code as needed, but it should be sufficiently well commented to get a general idea without help.

@asmeurer
Owner

What would you prefer? Not plotting anything seems like the correct choice, but this will create another problem: the bifurcation point is not detected, so the actual point that is plotted will just be somewhere near. This will cause a missing part in the line.

Yes, I think this is what most other plotting solutions do. There are some things I can think of that we can try:

  • We can adaptively notice that the solution becomes nonreal and make the points more dense there, in an attempt to hit enough before the bifurcation to make it smooth.

  • We can try to do some things symbolically (e.g., if plotting y = f(x), try to solve for x = f^-1(y) near that point. This would potentially create the problem that we plot too much into another solution (e.g., we try to plot sqrt(x), do it by plotting y**2 on the reversed axes, and end up plotting a little bit of -sqrt(x)).

  • We can use symbolic solve(), but just to find the bifurcation points. We then use this information to increase the point density, or do some other smart things that I can't think of now. Note that this is just one kind of bifurcation point (an annihilation point), but there could be others, such as intersections (esp. when we do implicit plots). But it would probably help for those cases as well.

We need to be careful that there are no guarantees of any kind of the continuity of the equation we are plotting, but then again, plotting highly discontinuous functions will not come out very well anyway.

By the way, there was no need to chain those plot commands. The feature you requested (plotting lists of expressions) is already implemented, so you can just use plot([expr1, expr2]).

Oh, I guess I chained them accidentally. I didn't even know that would work.

You have mentioned it. There was the ambiguity of whether this should be a surface or a line, but we decided on some smart defaults. It still needs to be special cased. It should not be difficult at all.

I would just default to a line, e.g., for plot(1).

I will not have the time to work on this in the near future. If somebody wants to help I can explain parts of the code as needed, but it should be sufficiently well commented to get a general idea without help.

OK. I still haven't really made any progress toward a release, but I promise that this will get in before then.

@asmeurer
Owner

Found an error:

In [1]: plot([1, 2], [3, 4])
---------------------------------------------------------------------------
KeyError                                  Traceback (most recent call last)
/Users/aaronmeurer/Documents/Python/sympy/sympy/<ipython-input-1-24153d600024> in <module>()
----> 1 plot([1, 2], [3, 4])

/Users/aaronmeurer/Documents/Python/sympy/sympy/sympy/plotting/plot.py in plot(*args, **kwargs)
    316         new_args = [args[0],]
    317         new_args.extend(repeat(a) for a in args[1:])
--> 318         return plot(*zip(*new_args), **kwargs)
    319 
    320     #

/Users/aaronmeurer/Documents/Python/sympy/sympy/sympy/plotting/plot.py in plot(*args, **kwargs)
    343                     free_vars.discard(item[0])
    344                 elif len(item) == 2:
--> 345                     pl[index] = Tuple(free_vars.pop(), item[0], item[1])
    346         list_of_plots[pl_index] = Tuple(*pl)
    347 

KeyError: 'pop from an empty set'
@asmeurer
Owner

And if I try the first example in the docstring, I get ValueError: The supplied argument do not correspond to a valid plotable object.

@Krastanov
Collaborator

I suppose that this happened in the last few commits and as the tests were removed (I do not remember why, but it was discussed here) I did not detect the problem.

I will check it on Friday and also make public the backup branch with the tests.

@Krastanov
Collaborator

@asmeurer , I will remove the first example, because what it does clashes in syntax with your request to be able to plot lists of expressions. Obviously there will be a plot_list function to take its role.

@Krastanov
Collaborator

Now we have tests and doctests.

@Krastanov
Collaborator

Without installing numpy and matplotlib

SymPy Bot Summary: :red_circle: There were test failures.

@Krastanov: Please fix the test failures.

Test results html report: http://reviews.sympy.org/report/agZzeW1weTNyDAsSBFRhc2sYyvcUDA

Interpreter: /usr/bin/python (2.7.2-final-0)
Architecture: Linux (64-bit)
Cache: yes
Test command: setup.py test
master hash: 8f0a110
branch hash: 64dccd7a81d3f552386f06b7a4aa41527b9b8095

Automatic review by SymPy Bot.

@Krastanov
Collaborator

The error here seems to be unrelated to the plotting module.

@Krastanov
Collaborator

With numpy and matplotlib installed

SymPy Bot Summary: :eight_spoked_asterisk: All tests have passed.

Test results html report: http://reviews.sympy.org/report/agZzeW1weTNyDAsSBFRhc2sY_4gXDA

Interpreter: /home/stefan/scientific_python_stack/bin/python (2.7.2-final-0)
Architecture: Linux (64-bit)
Cache: yes
Test command: setup.py test
master hash: 8f0a110
branch hash: 64dccd7a81d3f552386f06b7a4aa41527b9b8095

Automatic review by SymPy Bot.

@asmeurer
Owner

That error is specific to that random seed. Can you open an issue for it?

@Krastanov
Collaborator

@asmeurer, here is the new issue for the geometry test http://code.google.com/p/sympy/issues/detail?id=3257

@catchmrbharath catchmrbharath commented on the diff
sympy/plotting/plot.py
((102 lines not shown))
+ supported values for an aesthetic are:
+ - None (the backend uses default values)
+ - a constant
+ - a function of one variable (the first coordinate or parameter)
+ - a function of two variables (the first and second coordinate or
+ parameters)
+ - a function of three variables (only in nonparametric 3D plots)
+ Their implementation depends on the backend so they may not work in some
+ backends.
+
+ If the plot is parametric and the arity of the aesthetic function permits
+ it the aesthetic is calculated over parameters and not over coordinates.
+ If the arity does not permit calculation over parameters the calculation is
+ done over coordinates.
+
+ Only cartesian coordinates are suported for the moment, but you can use

suported => supported

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@catchmrbharath catchmrbharath commented on the diff
sympy/plotting/plot.py
((103 lines not shown))
+ - None (the backend uses default values)
+ - a constant
+ - a function of one variable (the first coordinate or parameter)
+ - a function of two variables (the first and second coordinate or
+ parameters)
+ - a function of three variables (only in nonparametric 3D plots)
+ Their implementation depends on the backend so they may not work in some
+ backends.
+
+ If the plot is parametric and the arity of the aesthetic function permits
+ it the aesthetic is calculated over parameters and not over coordinates.
+ If the arity does not permit calculation over parameters the calculation is
+ done over coordinates.
+
+ Only cartesian coordinates are suported for the moment, but you can use
+ the paremetric plots to plot in polar, spherical and cylindrical

paremetric => parametric

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Krastanov and others added some commits
@Krastanov Krastanov Presenting newplot.py
A new plotting framework supporting numerous backends.

Two files are present:
plotting/newplot.py containing the module
test.py in the root containing some examples

See the docstring of newplot.py for implementation details.

It has 2d and 3d plots, parametric plots and contours.

Example:

a = Plot(x**2, x, 0, 10)
a.title = 'A Figure'
a.xlabel = 'The argument'

A big and dirty commit.

The current version of matplotlib (1.1.0) has a bug so some
of the 3D stuff does not work.
Concerning the bug - it's reported, but to fix it now try this:
/usr/lib/pymodules/python2.7/mpl_toolkits/mplot3d/axes3d.pyc in set_autoscale_on(self, b)
    252         """
    253         Axes.set_autoscale_on(self, b)
--> 254         self.set_autoscalez_on(self, b)
    255
    256     def set_autoscalez_on(self, b) :

TypeError: set_autoscalez_on() takes exactly 2 arguments (3 given)
I believe there is one self too much.
70f57a2
@Krastanov Krastanov Adding an experimental new lambdify to the plotting module.
Needed because of the limitation of the old lambdify:
No symbolic Sums or other nontrivial expression.
Mixing translated and non translated functions.

rewrite of lambdify - This stuff is not stable at all.

It is for internal use in the new plotting module.
It may (will! see the Q'n'A below) be rewritten.

  We parse the expression string into a tree that identifies functions. Then
 we translate the names of the functions and we translate also some strings
 that are not names of functions (all this according to translation
 dictionaries).
  If the translation goes to another module (like numpy) the
 module is imported and 'func' is translated to 'module.func'.
  If a function can not be translated, the inner nodes of that part of the
 tree are not translated. So if we have Integral(sqrt(x)), sqrt is not
 translated to np.sqrt and the Integral does not crash.

#  Please, if there is a bug, do not try to fix it here! Rewrite this by using
# the method proposed in the last Q'n'A below.
#  If you insist on fixing it here, look at the workarounds in the function
# sympy_expression_namespace and in lambdify.

# Q: Why are you not using python abstract syntax tree?
# A: Because it is more complicated and not much more powerful in this case.

# Q: What if I have Symbol('sin') or g=Function('f')?
# A: You will break the algorithm. We should use srepr to defend against this?
#  The problem with Symbol('sin') is that it will be printed as 'sin'. The
# parser will distinguish it from the function 'sin' because functions are
# detected thanks to the opening parenthesis, but the lambda expression won't
# understand the difference if we have also the sin function.
# The solution (complicated) is to use srepr and maybe ast.
#  The problem with the g=Function('f') is that it will be printed as 'f' but in
# the global namespace we have only 'g'. But as the same printer is used in the
# constructor of the namespace there will be no problem.

# Q: What if some of the printers are not printing as expected?
# A: The algorithm wont work. You must use srepr for those cases. But even
# srepr may not print well. All problems with printers should be considered
# bugs.

# Q: What about _imp_ functions?
# A: Those are taken care for by evalf.

# Q: Will ast fix all possible problems?
# A: No. You will always have to use some printer. Even srepr may not work in
# some cases. But if the printer does not work, that should be considered a
# bug.

# Q: Is there same way to fix all possible problems?
# A: Probably by constructing our strings ourself by traversing the (func,
# args) tree and creating the namespace at the same time. That actually sounds
# good.
1db4e97
@Krastanov Krastanov Removing lambdarepr from experimental_lambdify in newplot.py
For the restricted usage of experimental_lambify there is absolutely
no need to complicate the code and make it dependent on lambdarepr.

Now experimental_lambdify is completely self contained.

Also some additions to the documentation were done.
b9166b9
@Krastanov Krastanov Removing checks for ctypes.
There were checks about the ctypes module. As it's included in
python 2.5+ those are no longer needed.

Also removed a remark about ctypes from the documentation.
15a6d7d
@Krastanov Krastanov Moving the old pyglet plotting module and adding a warning
To make place for the new interface the old plotting module was
moved to another location. It was noted in examples, documentation
and tests.

The location has changed but the Plot function is still imported
from the same location. It's a proxy object giving the Plot from
the old pyglet module, but it adds a warning about the change.
Importing the pyglet Plot from the new location does not give a
warning.
87347ba
@goodok goodok plotting: enabled doctests for some utils. 159259a
@Krastanov Krastanov Adding plot function (not implemented)
The docstring for the plot function is ready. The function itself is not
ready. It just calls Plot with the same arguments.

Also now the default between a contour plot and a 3d plot is the 3d plot.

Also fixed some comments.
389c685
@Krastanov Krastanov Finishing touches in the new plotting module.
There are probably still many bugs, but the module should be ready for
review after this commit.

- Finishing the plot function.
   Now the plot function gives a great flexibility in how one
can define a plot. Multiple cartesian or parametric plots are supported.

- Fixing imports.

- Minor fixes to plotting/experimental_lambdify.py
 The sqrt function was not translated to the numpy sqrt. Now it is.
before the commit sqrt(x) -> sqrt(x).evalf()
after the commit sqrt(x) -> np.sqrt(x)
 Also experimental_lambdify now supports the print_lambda argument.
When true the function will print the lambda expression to the terminal
before creating the actual lambda function.

- Many other clean-ups and bug fixes.
15bc203
@Krastanov Krastanov Preparing examples of the new plotting module.
In the root folder two preliminary files with examples were created.
They should be moved.
4a7ec1a
@Krastanov Krastanov Bug fixes and examples concerning coloring of plots. a12ad7f
@Krastanov Krastanov Fixes to experimental_lambdify from the plotting module. e2951a6
@Krastanov Krastanov Stop raising warnings every single one missing option in the backend. 3f67e94
@Krastanov Krastanov Some new functionality for plotting only integer values and steps. 518854e
@Krastanov Krastanov Extensive simplifications of code for the plotting module.
All the setattr and getattr overrides were deleted. The _options and
_aesthetics dicts were deleted and all options and aesthetics are now
regular attributes.
e42244f
@Krastanov Krastanov All plotting examples are moved to ./examples be90077
@Krastanov Krastanov Add a rudimentary save() method to plot.
Currently there is only one argument (string path) that is directly
sent to the backend. No *args or **kwargs.
8199c50
@Krastanov Krastanov A multitude of changes concerning the plotting module.
1.
`plot` now handles lists of expressions, Plot objects and Series objects.

Examples of lists of expressions as arguments:
plot([x,x**2]) # with implicit default range
plot([x,x**2], (0,2)) # explicit range
plot([-abs(x*y)-10, abs(x*y)+10], (-5,5), (-5,5)) # 3D surface

Examples of Plot and Series objects as arguments:
a = plot((x,(0,2)), (x**2,(0,2))) # creating a Plot object
plot(a) # Plotting a Plot objects
plot(a[0]) # Plotting a Series object

2.
Extensive changes to experimental_lambdify.

Now there is a smart vectorized_lambdify function that tries to work around
all the problems of lambdifying.

The following translations are tried:
only numpy
    - on FloatingPointError (numpy tried to return a nan):
        take the real part and use complex arguments
    - on AttributeError (meaning that numpy can not uderstand the object)
      or on TypeError (unhashable type) raised by sympy:
        only python math and then vectorize float64
    - on ValueError (Symbolic value, can't compute):
        only python cmath and then vectorize complex128

When using python math/cmath there is no need for evalf or float/complex
because python math/cmath calls those.

This function never tries to mix numpy directly with evalf because numpy
does not understand sympy Float. If this is needed one can use the
float_wrap_evalf/complex_wrap_evalf options of experimental_lambdify or
better one can be explicit about the dtypes that numpy works with.

3.
Refactor the general options in the matplotlib backend.

The sequence of operations was refactored and simplified. Some of the
operations were redoing previous operations and this was not good.

Also a new auto option for axis_center was created as a consequence.
Now the axis do not go out of the viewing window.

4.
Small fix for np.array reshaping in the plotting module.

Resetting the shape attribute was working in numpy 1.5.1 but was raising
AttributeError: incompatible shape for a non-contiguous array
in numpy 1.6.1.

Now the reshape() method is used instead of changing the shape attribute.

5.
Small cosmetic changes
447c019
@Krastanov Krastanov Small fixes to the matplotlib plotting backend.
Removal of a useless `hasattr` call. It is not useful because the if branch
controled by it is never to be executed. Moreover it was spelled
erroneously.

Change the order of operations so the x and y label are actually plotted.
40c0ca7
@Krastanov Krastanov Add ipython notebooks with plotting examples.
The notebooks are not evaluated. That way the png images are not generated
and do not take up place.
4d38584
@Krastanov Krastanov matplotlib version check in the plotting module
If the version is less than 1.0.0 matplotlib is not imported and
ascii is used. If the version is less than 1.2.0 (not released yet)
the 3D surface coloring does not work.
9a59d6e
@Krastanov Krastanov Refactoring and new tests for the plotting module.
A number of regressions were fixed.

There are now tests and doctest, that check for correct instantialization and
saving to disk of figures. It checks only the `plot()` function.

The `Plot()` class was simplified, as it was trying to be too many things at the
same time.

The `Series()` class was simplified for the same reason and it is now named
`OverloadedSeriesFactory()`.

Most of the docstrings were updated, however there is still much to be wanted
from the documentation of the module.
94a9ca2
@Krastanov Krastanov Add check for the presence of numpy to the plotting module tests.
The tests were failing if numpy was not installed. A try except block was added
to check for import errors.
a9f8305
@Krastanov Krastanov Change the import location of DeprecationWarning used by plotting module
The SympyDeprecationWarning was moved from its original location. The change
was done in the master branch. The same change must be mirrored in this
development branch.
a10a3d1
@Krastanov Krastanov Additional tests for the plotting module.
The following tests concern the detection of complex numbers in the plotting
routines. The coverage of experimental_lambdify is better, however still not
100% due to bugs in the vectorization routine (it does not detect examples like
sin(LambertW(x)) where a complex sympy expression is given to a python math
function).
364c504
@Krastanov
Collaborator

This is discussion from a pull request against this branch

Krastanov#5

About the review, everything looks fine. I think it is ready to go in. The only problem is show() is blocking, which will prevent updating of plot on the go. Is there any problem with using ion() ?

This should be discussed on the other pull request page, so the others can comment. Basically using ion() is the responsability of ipython and isympy, not the plotting module. It was already explained a few times how to do it in ipython.

@Krastanov
Collaborator

Here is the ipython_config that I am using.

https://gist.github.com/2775867

@catchmrbharath

I forgot about the mention of ion() previously and all the discussion about it here. I think this is ready to go in. I will be able to add a basic adaptive sampling for 2D after this gets merged.

@catchmrbharath

Will it be better to plot the real part of a complex value, or not plot the complex value? This has to be discussed before we add this.

@Krastanov
Collaborator

@asmeurer, This has gone through numerous revisions and now has a nice amount of tests (the coverage is). I am sure that there are still issues and that even small changes to the api are possible, however these will be difficult to happen without more widespread use. Thus, as @catchmrbharath has review it, I would like to get this merged.

An important outstanding issue is the fact that show() blocks the eventloop. However I think that this is an issue to be solved in isympy and ipython. This is also the stance that the matplotlib team has according to their webpage.

Adaptive sampling is already in a pull request against this branch. There is also a pull request changing the way that complex values are plotted.

@Krastanov
Collaborator

oops, this should have been "the coverage is ~90%".

@catchmrbharath catchmrbharath commented on the diff
sympy/plotting/experimental_lambdify.py
((149 lines not shown))
+ ('Symbolic value, can\'t compute' in str(e) or 'math domain error' in str(e))):
+ # Almost all functions were translated to python math, but some
+ # were left as sympy functions. They produced complex numbers.
+ # float(a+I*b) raises "Symbolic value, can't compute"
+ # math.sqrt(-1) raises "math domain error"
+ # Solution: use cmath and vectorize the final lambda.
+ self.lambda_func = experimental_lambdify(self.args, self.expr, use_python_cmath=True)
+ self.vector_func = np.vectorize(self.lambda_func, otypes=[np.complex])
+ results = np.real(self.__call__(*args))
+ warnings.warn('Complex values encountered. Returning only the real part.')
+ else:
+ # Complete failure. One last try with no translations, only
+ # wrapping in complex((...).evalf()) and returning the real
+ # part.
+ if self.failure:
+ raise e

Is there any particular reason why we are raising the exception. Repeated calls to to a vectorized_lambdify function will raise the exception when adaptive sampling.

My bad, the problem was somewhere else. Got why you raise the exception.

@Krastanov Collaborator
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@Krastanov Krastanov [plotting] Fix for a change master concerning complex values.
This commit
commit cb3bf49
Author: Chris Smith <smichr@gmail.com>

Has changed the way a certain error in __float__ is raised. This is a small
change to experimental_lambdify in order to accomodate the new behaviour in
master. Test are provided.
401b12c
@Krastanov
Collaborator

:red_circle: There were test failures.

In this module there is syntax unsupported in 2.5

@Krastanov
Collaborator

There are more than one errors related to python2.5 (something about set.union that I do not understand). I won't be able to fix these during the weekend.

@Krastanov Krastanov Fixes for 2.5 compatibility in the plotting module
A keyword argument was used after unpacking of args. This was fixed by using
unpacking of kwargs.

set.union was called with variable number of arguments. In 2.5 only two
arguments are allowed. This was fixed by using reduce.
d5d920f
@Krastanov
Collaborator

The fixes for 2.5 are in. I am waiting for new bugs or a review/merge.

@smichr smichr commented on the diff
sympy/plotting/pygletplot/__init__.py
((56 lines not shown))
+ plot if you provide two or three functions. Similarly, the
+ arguments will be interpreted as a curve is one variable is
+ used, and a surface if two are used.
+
+ Supported mode names by number of variables:
+
+ 1: parametric, cartesian, polar
+ 2: parametric, cartesian, cylindrical = polar, spherical
+
+ >>> Plot(1, mode='spherical')
+
+
+ Calculator-like Interface
+ =========================
+
+ >>> p = Plot(visible=False)
@smichr Collaborator
smichr added a note

I just tried this and the plots appeared but some error appeared, too:

>>> p=Plot(visible=False)
sympy\plotting\proxy_pyglet.py:30: SymPyDeprecationWarning:

This interface will change in future versions of sympy. As a
precatuion use the plot() function (lowercase). See the docstring for
details.

  SymPyDeprecationWarning)
>>> f=x**2
>>> p[1]=x**2
>>> p[2]=f.diff(x)
>>> p[3]=f.diff(x).diff(x)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "sympy\plotting\pygletplot\plot.py", line 301, in __setitem__
    f = PlotMode(*args, **kwargs)
  File "sympy\plotting\pygletplot\plot_mode.py", line 75, in __new__
    subcls = PlotMode._get_mode(mode_arg, i, d)
  File "sympy\plotting\pygletplot\plot_mode.py", line 153, in _get_mode
    return PlotMode._get_default_mode(i, d)
  File "sympy\plotting\pygletplot\plot_mode.py", line 167, in _get_default_mode
    return PlotMode._mode_default_map[d][i]
KeyError: 0
>>> p.show()
>>> p.clear()
>>> p
<blank plot>
>>> p[1]=x**2+y**2
>>> p[1].style='solid'
>>> p[2]=-x**2-y**2
>>> p[2].style='wireframe'
>>> p[1].color=z,(0.4,.4,.9),(.9,.4,.4)
>>> p[1].style='both'
>>> p[2].style='both'
>>> p.close()
@Krastanov Collaborator

@smichr, You have been using the old plotting module that is working on pyglet. I have little idea how it works and I have not made changes to it (I have only moved it to a subfolder).

Check the docstrings of both from sympy import Plot (a proxy object to the old plotting module in order to have deprecation warnings) and from sympy import plot. Both of them should be imported with from sympy import *

@Krastanov Collaborator
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@smichr smichr commented on the diff
sympy/plotting/pygletplot/__init__.py
((42 lines not shown))
+
+ Coordinate Modes
+ ================
+
+ Plot supports several curvilinear coordinate modes, and
+ they independent for each plotted function. You can specify
+ a coordinate mode explicitly with the 'mode' named argument,
+ but it can be automatically determined for Cartesian or
+ parametric plots, and therefore must only be specified for
+ polar, cylindrical, and spherical modes.
+
+ Specifically, Plot(function arguments) and Plot[n] =
+ (function arguments) will interpret your arguments as a
+ Cartesian plot if you provide one function and a parametric
+ plot if you provide two or three functions. Similarly, the
+ arguments will be interpreted as a curve is one variable is
@smichr Collaborator
smichr added a note

is one -> if one

@Krastanov Collaborator

@smichr, as I mentioned earlier, these files are not part of my code (this is the old plotting module that you are looking at). I have indeed moved the files, however I have not done any changes to them and I would prefer not to touch them in this pull request as this is unrelated work. Though, it is true that there are issues to be fixed in the old plotting module.

@smichr Collaborator
smichr added a note
@Krastanov Collaborator
@smichr Collaborator
smichr added a note
@Krastanov Collaborator
@smichr Collaborator
smichr added a note
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
@smichr
Collaborator

I rebased, fixed the typos I mentioned, and committed this from here after also fixing a whitespace error. Are there any issues to close?

@smichr smichr closed this
@catchmrbharath

I guess the only issue is plotting constants. I haven't found an non - ugly way to do it.
Otherwise it is ready to be merged. It will be great if it gets merged so that I can send
the my gsoc work as pull requests and get it reviewed by the community.

@Krastanov
Collaborator

Wow, thanks Chris, I am grateful for the help. I will update/close the related issues.

@smichr
Collaborator
@mrocklin
Collaborator

Awesome. I think that an e-mail or blogpost should go out about this. The developer community should start using it to generate (and hopefully fix) some issues. Stefan, I know you've posted a few demos before in various places. Is there a single HTML page somewhere that documents how to do basic things with this module? Is there a good help page?

@catchmrbharath

@smichr
plot(1). This throws an error.

@smichr
Collaborator
@Krastanov
Collaborator
@Krastanov
Collaborator
@Krastanov
Collaborator
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Commits on May 19, 2012
  1. @Krastanov

    Presenting newplot.py

    Krastanov authored
    A new plotting framework supporting numerous backends.
    
    Two files are present:
    plotting/newplot.py containing the module
    test.py in the root containing some examples
    
    See the docstring of newplot.py for implementation details.
    
    It has 2d and 3d plots, parametric plots and contours.
    
    Example:
    
    a = Plot(x**2, x, 0, 10)
    a.title = 'A Figure'
    a.xlabel = 'The argument'
    
    A big and dirty commit.
    
    The current version of matplotlib (1.1.0) has a bug so some
    of the 3D stuff does not work.
    Concerning the bug - it's reported, but to fix it now try this:
    /usr/lib/pymodules/python2.7/mpl_toolkits/mplot3d/axes3d.pyc in set_autoscale_on(self, b)
        252         """
        253         Axes.set_autoscale_on(self, b)
    --> 254         self.set_autoscalez_on(self, b)
        255
        256     def set_autoscalez_on(self, b) :
    
    TypeError: set_autoscalez_on() takes exactly 2 arguments (3 given)
    I believe there is one self too much.
  2. @Krastanov

    Adding an experimental new lambdify to the plotting module.

    Krastanov authored
    Needed because of the limitation of the old lambdify:
    No symbolic Sums or other nontrivial expression.
    Mixing translated and non translated functions.
    
    rewrite of lambdify - This stuff is not stable at all.
    
    It is for internal use in the new plotting module.
    It may (will! see the Q'n'A below) be rewritten.
    
      We parse the expression string into a tree that identifies functions. Then
     we translate the names of the functions and we translate also some strings
     that are not names of functions (all this according to translation
     dictionaries).
      If the translation goes to another module (like numpy) the
     module is imported and 'func' is translated to 'module.func'.
      If a function can not be translated, the inner nodes of that part of the
     tree are not translated. So if we have Integral(sqrt(x)), sqrt is not
     translated to np.sqrt and the Integral does not crash.
    
    #  Please, if there is a bug, do not try to fix it here! Rewrite this by using
    # the method proposed in the last Q'n'A below.
    #  If you insist on fixing it here, look at the workarounds in the function
    # sympy_expression_namespace and in lambdify.
    
    # Q: Why are you not using python abstract syntax tree?
    # A: Because it is more complicated and not much more powerful in this case.
    
    # Q: What if I have Symbol('sin') or g=Function('f')?
    # A: You will break the algorithm. We should use srepr to defend against this?
    #  The problem with Symbol('sin') is that it will be printed as 'sin'. The
    # parser will distinguish it from the function 'sin' because functions are
    # detected thanks to the opening parenthesis, but the lambda expression won't
    # understand the difference if we have also the sin function.
    # The solution (complicated) is to use srepr and maybe ast.
    #  The problem with the g=Function('f') is that it will be printed as 'f' but in
    # the global namespace we have only 'g'. But as the same printer is used in the
    # constructor of the namespace there will be no problem.
    
    # Q: What if some of the printers are not printing as expected?
    # A: The algorithm wont work. You must use srepr for those cases. But even
    # srepr may not print well. All problems with printers should be considered
    # bugs.
    
    # Q: What about _imp_ functions?
    # A: Those are taken care for by evalf.
    
    # Q: Will ast fix all possible problems?
    # A: No. You will always have to use some printer. Even srepr may not work in
    # some cases. But if the printer does not work, that should be considered a
    # bug.
    
    # Q: Is there same way to fix all possible problems?
    # A: Probably by constructing our strings ourself by traversing the (func,
    # args) tree and creating the namespace at the same time. That actually sounds
    # good.
  3. @Krastanov

    Removing lambdarepr from experimental_lambdify in newplot.py

    Krastanov authored
    For the restricted usage of experimental_lambify there is absolutely
    no need to complicate the code and make it dependent on lambdarepr.
    
    Now experimental_lambdify is completely self contained.
    
    Also some additions to the documentation were done.
  4. @Krastanov

    Removing checks for ctypes.

    Krastanov authored
    There were checks about the ctypes module. As it's included in
    python 2.5+ those are no longer needed.
    
    Also removed a remark about ctypes from the documentation.
  5. @Krastanov

    Moving the old pyglet plotting module and adding a warning

    Krastanov authored
    To make place for the new interface the old plotting module was
    moved to another location. It was noted in examples, documentation
    and tests.
    
    The location has changed but the Plot function is still imported
    from the same location. It's a proxy object giving the Plot from
    the old pyglet module, but it adds a warning about the change.
    Importing the pyglet Plot from the new location does not give a
    warning.
  6. @goodok @Krastanov

    plotting: enabled doctests for some utils.

    goodok authored Krastanov committed
  7. @Krastanov

    Adding plot function (not implemented)

    Krastanov authored
    The docstring for the plot function is ready. The function itself is not
    ready. It just calls Plot with the same arguments.
    
    Also now the default between a contour plot and a 3d plot is the 3d plot.
    
    Also fixed some comments.
  8. @Krastanov

    Finishing touches in the new plotting module.

    Krastanov authored
    There are probably still many bugs, but the module should be ready for
    review after this commit.
    
    - Finishing the plot function.
       Now the plot function gives a great flexibility in how one
    can define a plot. Multiple cartesian or parametric plots are supported.
    
    - Fixing imports.
    
    - Minor fixes to plotting/experimental_lambdify.py
     The sqrt function was not translated to the numpy sqrt. Now it is.
    before the commit sqrt(x) -> sqrt(x).evalf()
    after the commit sqrt(x) -> np.sqrt(x)
     Also experimental_lambdify now supports the print_lambda argument.
    When true the function will print the lambda expression to the terminal
    before creating the actual lambda function.
    
    - Many other clean-ups and bug fixes.
  9. @Krastanov

    Preparing examples of the new plotting module.

    Krastanov authored
    In the root folder two preliminary files with examples were created.
    They should be moved.
  10. @Krastanov
  11. @Krastanov
  12. @Krastanov
  13. @Krastanov
  14. @Krastanov

    Extensive simplifications of code for the plotting module.

    Krastanov authored
    All the setattr and getattr overrides were deleted. The _options and
    _aesthetics dicts were deleted and all options and aesthetics are now
    regular attributes.
  15. @Krastanov
  16. @Krastanov

    Add a rudimentary save() method to plot.

    Krastanov authored
    Currently there is only one argument (string path) that is directly
    sent to the backend. No *args or **kwargs.
  17. @Krastanov

    A multitude of changes concerning the plotting module.

    Krastanov authored
    1.
    `plot` now handles lists of expressions, Plot objects and Series objects.
    
    Examples of lists of expressions as arguments:
    plot([x,x**2]) # with implicit default range
    plot([x,x**2], (0,2)) # explicit range
    plot([-abs(x*y)-10, abs(x*y)+10], (-5,5), (-5,5)) # 3D surface
    
    Examples of Plot and Series objects as arguments:
    a = plot((x,(0,2)), (x**2,(0,2))) # creating a Plot object
    plot(a) # Plotting a Plot objects
    plot(a[0]) # Plotting a Series object
    
    2.
    Extensive changes to experimental_lambdify.
    
    Now there is a smart vectorized_lambdify function that tries to work around
    all the problems of lambdifying.
    
    The following translations are tried:
    only numpy
        - on FloatingPointError (numpy tried to return a nan):
            take the real part and use complex arguments
        - on AttributeError (meaning that numpy can not uderstand the object)
          or on TypeError (unhashable type) raised by sympy:
            only python math and then vectorize float64
        - on ValueError (Symbolic value, can't compute):
            only python cmath and then vectorize complex128
    
    When using python math/cmath there is no need for evalf or float/complex
    because python math/cmath calls those.
    
    This function never tries to mix numpy directly with evalf because numpy
    does not understand sympy Float. If this is needed one can use the
    float_wrap_evalf/complex_wrap_evalf options of experimental_lambdify or
    better one can be explicit about the dtypes that numpy works with.
    
    3.
    Refactor the general options in the matplotlib backend.
    
    The sequence of operations was refactored and simplified. Some of the
    operations were redoing previous operations and this was not good.
    
    Also a new auto option for axis_center was created as a consequence.
    Now the axis do not go out of the viewing window.
    
    4.
    Small fix for np.array reshaping in the plotting module.
    
    Resetting the shape attribute was working in numpy 1.5.1 but was raising
    AttributeError: incompatible shape for a non-contiguous array
    in numpy 1.6.1.
    
    Now the reshape() method is used instead of changing the shape attribute.
    
    5.
    Small cosmetic changes
  18. @Krastanov

    Small fixes to the matplotlib plotting backend.

    Krastanov authored
    Removal of a useless `hasattr` call. It is not useful because the if branch
    controled by it is never to be executed. Moreover it was spelled
    erroneously.
    
    Change the order of operations so the x and y label are actually plotted.
  19. @Krastanov

    Add ipython notebooks with plotting examples.

    Krastanov authored
    The notebooks are not evaluated. That way the png images are not generated
    and do not take up place.
  20. @Krastanov

    matplotlib version check in the plotting module

    Krastanov authored
    If the version is less than 1.0.0 matplotlib is not imported and
    ascii is used. If the version is less than 1.2.0 (not released yet)
    the 3D surface coloring does not work.
  21. @Krastanov

    Refactoring and new tests for the plotting module.

    Krastanov authored
    A number of regressions were fixed.
    
    There are now tests and doctest, that check for correct instantialization and
    saving to disk of figures. It checks only the `plot()` function.
    
    The `Plot()` class was simplified, as it was trying to be too many things at the
    same time.
    
    The `Series()` class was simplified for the same reason and it is now named
    `OverloadedSeriesFactory()`.
    
    Most of the docstrings were updated, however there is still much to be wanted
    from the documentation of the module.
  22. @Krastanov

    Add check for the presence of numpy to the plotting module tests.

    Krastanov authored
    The tests were failing if numpy was not installed. A try except block was added
    to check for import errors.
  23. @Krastanov

    Change the import location of DeprecationWarning used by plotting module

    Krastanov authored
    The SympyDeprecationWarning was moved from its original location. The change
    was done in the master branch. The same change must be mirrored in this
    development branch.
  24. @Krastanov

    Additional tests for the plotting module.

    Krastanov authored
    The following tests concern the detection of complex numbers in the plotting
    routines. The coverage of experimental_lambdify is better, however still not
    100% due to bugs in the vectorization routine (it does not detect examples like
    sin(LambertW(x)) where a complex sympy expression is given to a python math
    function).
Commits on May 25, 2012
  1. @Krastanov

    [plotting] Fix for a change master concerning complex values.

    Krastanov authored
    This commit
    commit cb3bf49
    Author: Chris Smith <smichr@gmail.com>
    
    Has changed the way a certain error in __float__ is raised. This is a small
    change to experimental_lambdify in order to accomodate the new behaviour in
    master. Test are provided.
Commits on May 27, 2012
  1. @Krastanov

    Fixes for 2.5 compatibility in the plotting module

    Krastanov authored
    A keyword argument was used after unpacking of args. This was fixed by using
    unpacking of kwargs.
    
    set.union was called with variable number of arguments. In 2.5 only two
    arguments are allowed. This was fixed by using reduce.
Something went wrong with that request. Please try again.