New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Plotting framework #673
Plotting framework #673
Conversation
At least on my computer, the matplotlib plots don't stay open with the test script, so I can't really see them. |
@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. |
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). |
Please, replace Out [1]: to |
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.) |
|
||
Examples: | ||
In [10]: str2tree(str(Integral(x, (x, 1, y)))) | ||
Out[10]: ('', ('Integral(', 'x', '(x, 1, y)'), ')') |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My console give me:
('', ('Integral(', 'x, (x, 1, y)'), ')')
The other examples are correct.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That should be fixed, you are right
On 14 November 2011 19:24, Alexey U. Gudchenko <
reply@reply.github.com
wrote:
- 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)'), ')')
My console give me:
('', ('Integral(', 'x, (x, 1, y)'), ')')
The other examples are correct.
Reply to this email directly or view it on GitHub:
https://github.com/sympy/sympy/pull/673/files#r227792
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. |
@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. |
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. |
The last commit should take care of the problems arising from not having matplotlib installed. |
I agree. The only thing that should be fixed before merging is a stable API, since that is annoying to change afterwords.
This is a problem SymPy should not require any dependencies by default. By the way, this branch cannot be cleanly merged over master. |
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. |
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. |
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. |
What is the status on this? |
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! |
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. |
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) Automatic review by SymPy Bot. |
@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: 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? |
if parent.legend: | ||
self.ax.legend() | ||
self.ax.legend_.set_visible(parent.legend) | ||
elif hasattr(self.ax, 'ledend_'): |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Typo?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I should be more specific :). In line 907, is it suppose to be legend_
or ledend_
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oh god! You are right. Thanks for spotting this. I will add also a test for deleting a legend.
I've only used the new Some functionality i would like to see (but there's no hurry, I wouldn't mind implementing it myself when i find the time):
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()
|
Feel free to add those to the issue tracker. That way they will not be It would be great help if you can run the tests on this (you will need On 28 January 2012 23:11, Miha Marolt
|
Should I add it to 2845 or create a new issue like "plotting enhancements".
Sure, I'll do it tomorrow. |
I would prefer a new issue (blocked on the current one). That way it On 28 January 2012 23:26, Miha Marolt
|
and len(args[4]) == 3): | ||
inst = ParametricSurfaceSeries(*args) | ||
else: | ||
raise ValueError('The supplied argument do not correspond to a' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
argument => arguments
So I've started playing around with this, now that I need to do some plotting for my homework. I get the following:
This does create the plot. It also never returns, unless I close the plot. Is this the intended behavior? It isn't very helpful. |
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). |
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. |
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.
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.
The tests were failing if numpy was not installed. A try except block was added to check for import errors.
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.
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).
This is discussion from a pull request against this branch
This should be discussed on the other pull request page, so the others can comment. Basically using |
Here is the ipython_config that I am using. |
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. |
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. |
@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 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. |
oops, this should have been "the coverage is ~90%". |
# wrapping in complex((...).evalf()) and returning the real | ||
# part. | ||
if self.failure: | ||
raise e |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My bad, the problem was somewhere else. Got why you raise the exception.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I do not understand the question. There is infinite recursion without
this. Adaptive sampling has nothing to do with it. Finally, if you
have an expression that raises this error there is a bug.
On 24 May 2012 14:54, Bharath M R
reply@reply.github.com
wrote:
- ('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.
Reply to this email directly or view it on GitHub:
https://github.com/sympy/sympy/pull/673/files#r874451
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.
🔴 There were test failures. In this module there is syntax unsupported in 2.5 |
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. |
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.
The fixes for 2.5 are in. I am waiting for new bugs or a review/merge. |
Calculator-like Interface | ||
========================= | ||
|
||
>>> p = Plot(visible=False) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
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()
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@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 *
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that this branch still does not permit plotting constant
expressions... I won't have time to work on this during this week.
I rebased, fixed the typos I mentioned, and committed this from here after also fixing a whitespace error. Are there any issues to close? |
I guess the only issue is plotting constants. I haven't found an non - ugly way to do it. |
Wow, thanks Chris, I am grateful for the help. I will update/close the related issues. |
On Mon, Jun 11, 2012 at 7:19 PM, Bharath M R
It's in. BTW, what is "plotting a constant"? |
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? |
@smichr |
On Mon, Jun 11, 2012 at 8:26 PM, Bharath M R
What should it do? Is this suppose to give a |
|
I will prepare something before the end of the day. |
The help page (meaning the dosctring that is not imported in sphinx) |
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):
free_symbols
for matrices)line_plot, surface_plot, contour_plot, implicit_plot, parametric_plot, list_plot, etc
if it makes sense. Or maybeplot_*
so tab completion works.DONE:
if
in the unit-tests)No more problems with array shapes.
Now one can plot lists of expressions.
No more problems with complex numbers or numpy.ufunc(Float(1))
Now axis_center 'auto' is a bit smarter
--pylab
NEW TODO
#1
Plot() should not do all this magic. All the magic should be in plot()
and Series().
its constructor.
is to be added it should be contained in Series() and plot()
Soc final fixed #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()):
ex: plot_surface(x+y, (x, 1, 2), (y, 1, 2))
result: obvious
ex: plot_surface([x+y, x-y], (x, 1, 2), (y, 1, 2))
result: plots two surfaces
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
Improve collect so that you can use Wild in the pattern #4
The ipython profile should be updated