Skip to content

Conversation

Davide-sd
Copy link
Contributor

@Davide-sd Davide-sd commented Aug 16, 2023

References to other Issues or PRs

Another PR related to Issue #23036

Brief description of what is fixed or changed

  • The label keyword argument can now deal with multiple expressions.
  • Added a new keyword argument: rendering_kw. If provided, this dictionary of commands will be passed to the plotting library's function responsible to generate the visualization. It allows to customize the appearance by leveraging the full capabilities of a plotting library.
  • Added support for label and rendering_kw optional arguments to plotting functions.

A couple of new keyword arguments are introduced. This will be properly documented on a future PR.

Back-compatibility is maintained.

Other comments

Up to SymPy 1.12, the label keyword argument was unable to set custom labels when plotting multiple expressions with the same function call. The following shows an example of before (up to sympy 1.12) and after (this PR):

from sympy import *
var("x")
plot(sin(x), cos(x), (x, -pi, pi), legend=True, legend=["a", "b"])

image

This PR also extend the number of optional arguments that any plotting function can receive. Up to SymPy 1.12, a generic plotting function had the following form:

plot_func(expr, range1 [tuple, optional], range2 [tuple, optional], ..., **kwargs)

With this PR it is extended to:

plot_func(expr, range1 [tuple, optional], ..., label [str, optional], rendering_kw [dict, optional], **kwargs)

This allows more refinement when plotting and customizing multiple expressions. For example:

plot(
    (sin(x), (x, -pi, pi), "a"),
    (cos(x), (x, 0, pi), {"linestyle": "--"})
)

Here, a custom label is set on the first expression and custom rendering options are set on the second expression. Note that this plot was generated with spb because sympy's MatplotlibBackend is currently unaware of the rendering_kw dictionary: this will be implemented in the next PR.

image

Release Notes

  • plotting
    • Improved support for custom labels and rendering options in plotting functions

@sympy-bot
Copy link

sympy-bot commented Aug 16, 2023

Hi, I am the SymPy bot. I'm here to help you write a release notes entry. Please read the guide on how to write release notes.

Your release notes are in good order.

Here is what the release notes will look like:

  • plotting
    • Improved support for custom labels and rendering options in plotting functions (#25531 by @Davide-sd)

This will be added to https://github.com/sympy/sympy/wiki/Release-Notes-for-1.13.

Click here to see the pull request description that was parsed.
<!-- Your title above should be a short description of what
was changed. Do not include the issue number in the title. -->

#### References to other Issues or PRs
<!-- If this pull request fixes an issue, write "Fixes #NNNN" in that exact
format, e.g. "Fixes #1234" (see
https://tinyurl.com/auto-closing for more information). Also, please
write a comment on that issue linking back to this pull request once it is
open. -->

Another PR related to Issue #23036 

#### Brief description of what is fixed or changed

* The `label` keyword argument can now deal with multiple expressions.
* Added a new keyword argument: `rendering_kw`. If provided, this dictionary of commands will be passed to the plotting library's function responsible to generate the visualization. It allows to customize the appearance by leveraging the full capabilities of a plotting library.
* Added support for `label` and `rendering_kw` optional arguments to plotting functions.

A couple of new keyword arguments are introduced. This will be properly documented on a future PR.

Back-compatibility is maintained.

#### Other comments

Up to SymPy 1.12, the `label` keyword argument was unable to set custom labels when plotting multiple expressions with the same function call. The following shows an example of before (up to sympy 1.12) and after (this PR):

```py
from sympy import *
var("x")
plot(sin(x), cos(x), (x, -pi, pi), legend=True, legend=["a", "b"])
```

![image](https://github.com/sympy/sympy/assets/9921777/88c6cf7b-2135-4fb9-a814-884e9ce8724c)

This PR also extend the number of optional arguments that any plotting function can receive. Up to SymPy 1.12, a generic plotting function had the following form: 

`plot_func(expr, range1 [tuple, optional], range2 [tuple, optional], ..., **kwargs`)

With this PR it is extended to: 

`plot_func(expr, range1 [tuple, optional], ..., label [str, optional], rendering_kw [dict, optional], **kwargs`)

This allows more refinement when plotting and customizing multiple expressions. For example:

```py
plot(
    (sin(x), (x, -pi, pi), "a"),
    (cos(x), (x, 0, pi), {"linestyle": "--"})
)
```
Here, a custom label is set on the first expression and custom rendering options are set on the second expression. Note that this plot was generated with [spb](https://github.com/Davide-sd/sympy-plot-backends/) because sympy's `MatplotlibBackend` is currently unaware of the `rendering_kw` dictionary: this will be implemented in the next PR.

![image](https://github.com/sympy/sympy/assets/9921777/4e0dbb03-8d82-4e6b-9287-cf8fcedd1178) 


#### Release Notes

<!-- Write the release notes for this release below between the BEGIN and END
statements. The basic format is a bulleted list with the name of the subpackage
and the release note for this PR. For example:

* solvers
  * Added a new solver for logarithmic equations.

* functions
  * Fixed a bug with log of integers. Formerly, `log(-x)` incorrectly gave `-log(x)`.

* physics.units
  * Corrected a semantical error in the conversion between volt and statvolt which
    reported the volt as being larger than the statvolt.

or if no release note(s) should be included use:

NO ENTRY

See https://github.com/sympy/sympy/wiki/Writing-Release-Notes for more
information on how to write release notes. The bot will check your release
notes automatically to see if they are formatted correctly. -->

<!-- BEGIN RELEASE NOTES -->

* plotting
  * Improved support for custom labels and rendering options in plotting functions

<!-- END RELEASE NOTES -->

Update

The release notes on the wiki have been updated.

@sympy-bot
Copy link

sympy-bot commented Aug 16, 2023

🟠

Hi, I am the SymPy bot. I've noticed that some of your commits add or delete files. Since this is sometimes done unintentionally, I wanted to alert you about it.

This is an experimental feature of SymPy Bot. If you have any feedback on it, please comment at sympy/sympy-bot#75.

The following commits add new files:

  • 2a08057:
    • sympy/plotting/tests/test_utils.py
  • 31a43c8:
    • sympy/plotting/tests/test_utils.py
  • 221b50d:
    • doc/src/modules/physics/mechanics/api/actuator.rst
    • doc/src/modules/physics/mechanics/api/pathway.rst
    • sympy/external/tests/test_gmpy.py
    • sympy/physics/_biomechanics/_mixin.py
    • sympy/physics/_biomechanics/tests/test_mixin.py
    • sympy/physics/mechanics/pathway.py
    • sympy/solvers/simplex.py
    • sympy/solvers/tests/test_simplex.py

The following commits delete files:

  • 221b50d:
    • sympy/physics/mechanics/_pathway.py

If these files were added/deleted on purpose, you can ignore this message.

@Davide-sd
Copy link
Contributor Author

@oscarbenjamin tests are failing for things that I haven't touched. Do I need to rebase or something?

@oscarbenjamin
Copy link
Collaborator

The other failure is mypy and I'm not sure what about this PR has caused that to change but I do see these errors regularly when running mypy over specific modules (but not the whole codebase):

sympy/vector/parametricregion.py:4: error: Name "trigsimp" already defined (by an import)  [no-redef]
sympy/vector/implicitregion.py:11: error: Name "solveset" already defined (by an import)  [no-redef]
sympy/vector/implicitregion.py:11: error: Name "diophantine" already defined (by an import)  [no-redef]

The functions that it complains about are all functions that have the same name as the module that contains them.

Actually these imports are ambiguous:

from sympy.simplify import trigsimp

The ambiguity is here is whether that means the module or the sympy.simplify.trigsimp or the function trigsimp that is defined in that module. It is a poor design in sympy that package __init__ files import things that overwrite submodule names.

The solution is to make these imports fully explicit like

from sympy.simplify.trigsimp import trigsimp

@sylee957
Copy link
Member

The documentation needs to be carefully reviewed for this,
Do you have written the documentation yet?

@sylee957
Copy link
Member

sylee957 commented Aug 17, 2023

Also, I'd question the API design if label should be consistently inside the dictionary, than being positional argument, because it is optional.

plot(
    (sin(x), (x, -pi, pi), "a"),
    (cos(x), (x, 0, pi), {"linestyle": "--"})
)

@Davide-sd
Copy link
Contributor Author

The documentation needs to be carefully reviewed for this, Do you have written the documentation yet?

Of course there is, you can find it here.

@Davide-sd
Copy link
Contributor Author

Also, I'd question the API design if label should be consistently inside the dictionary, than being positional argument, because it is optional.

plot(
    (sin(x), (x, -pi, pi), "a"),
    (cos(x), (x, 0, pi), {"linestyle": "--"})
)

There is nothing stopping you from executing something like:

plot(
    (sin(x), (x, -pi, pi), "a"),
    (cos(x), (x, 0, pi), {"linestyle": "--", "label": "b"})
)

It is pretty clear that you save some typing if you use the positional argument, which is why it was created. In my experience, it is often necessary to change the label, whereas changing other rendering options like color, linestyle, ..., is less frequent.

@sylee957
Copy link
Member

I also have question that label and rendering_kw can be top-level option plot(..., label=['f', 'g']) as well as option for individual plot (sin(x), ..., 'f'), ....
This can be redundant and have consistency issues if both are specified.

@Davide-sd
Copy link
Contributor Author

I also have question that label and rendering_kw can be top-level option plot(..., label=['f', 'g']) as well as option for individual plot (sin(x), ..., 'f'), .... This can be redundant and have consistency issues if both are specified.

There is no reason for using both at the same time: either one or the other. Someone is comfortable with the positional arguments, others are comfortable with keyword arguments. In any case, if keywords arguments are specified, they have the priority because they are applied after the data series have been generated.

In my opinion, positional arguments have the advantage of making code cleaner. Consider again this code that uses positional arguments:

plot(
    (sin(x), (x, -pi, pi), "a"),
    (cos(x), (x, 0, pi), {"linestyle": "--"})
)

Here, anyone reading the code can immediately understand that the label "a" is associated to sin(x) and that {"linestyle": "--"} is applied to cos(x).

A similar result can be achieved with keyword arguments:

plot(
    (sin(x), (x, -pi, pi)),
    (cos(x), (x, 0, pi)),
    label=["a", None],
    rendering_kw=[None, {"linestyle": "--"}]
)

Here, things are separated into keyword arguments. However, someone reading this code needs to be careful in order to understand that there is a one-to-one correspondence between the symbolic expression and what is specified on the label and rendering_kw. In my opinion, this code looks messy, especially when comparing more than 2 symbolic expressions.

Moreover, now you have specified None as the label of the second expression, hence it won't be visible in the legend.

@sylee957
Copy link
Member

I don't favor the plot with multiple expressions though.
It just makes the options extend 2-dimensionally, with different expressions and different styles.
I'm not sure if we have to extend the plot arguments in that way,
if there is only one expression for one plot, things will be a lot simpler and more consistent.

@Davide-sd
Copy link
Contributor Author

I don't favor the plot with multiple expressions though. It just makes the options extend 2-dimensionally, with different expressions and different styles. I'm not sure if we have to extend the plot arguments in that way, if there is only one expression for one plot, things will be a lot simpler and more consistent.

If you allows to plot only one expression for each function call, you break back-compatibility. If you allows a plotting function to plot multiple expressions (as is the case right now), users expects further customization (#22609 just to name one)...

From what I understand you would like only one way to obtain some result. Correct me if I'm wrong, but you'd prefer something like this:

from sympy import *
from spb import *
var("x")

p1 = plot(cos(x + pi / 2) * exp(-x / 4), (x, 0, 2 * pi),
    label="oscillator",
    ylim=(-1.25, 1.25), show=False)
p2 = plot(exp(-x / 4), (x, 0, 2 * pi),
    label="upper limit",
    rendering_kw={"linestyle": ":"}, show=False)
p3 = plot(-exp(-x / 4), (x, 0, 2 * pi),
    label="lower limit",
    rendering_kw={"linestyle": ":"}, show=False)
p1.extend(p2)
p1.extend(p3)
p1.show()

image

On the other hand, I would like to do it like this:

from sympy import *
from spb import *
var("x")

plot(
    (cos(x + pi / 2) * exp(-x / 4), "oscillator"),
    (exp(-x / 4), "upper limit", {"linestyle": ":"}),
    (-exp(-x / 4), "upper limit", {"linestyle": ":"}),
    (x, 0, 2 * pi),
    ylim=(-1.25, 1.25)
)

Make no mistake, there is absolutely no way in the entire universe that you'll be able to convince me that the first code block is better than the second, which achieve the same result. But feel free to try.

Luckily, this PR enables both ways so you'll be able to use the former, while I'll be able to use the latter, and everyone lives happily ever after.

@sylee957
Copy link
Member

Luckily, this PR enables both ways so you'll be able to use the former, while I'll be able to use the latter, and everyone lives happily ever after.

My opinion is that this makes the logic much more complicated and have maintainance problem later.
I don't have the strong preference between passing the options imperatively, or passing the options declaratively like in tree, but I rather try to opinionate in one way if that doesn't make some things less general.

For example, if we are going to stick to lists of expressions, it may be better to force people to always pass the list, even for length-1.
We can keep the backward compatibility, by sorting out the trivial case,
but I'm not sure we have to extend the backward behavior though.

(cos(x + pi / 2) * exp(-x / 4), "oscillator")

However, I don't favor the arguments like this because the IDE poorly gives argument hints and autocompletion.
These kinds of things are best with type annotations, or wrapped inside custom objects, functions with explicit arguments or type annotations. (both is best).
However, usually designing everything with flat arguments are favored because we get free lunch of type inference, if we are not using type annotations heavily anyway.

return series


def _set_labels(series, labels, rendering_kw):
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we have to tie the labels and options to series?

I thought Series should only be implementing the geometry, or at least it could split into something that specifically implements the geometry and styles.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Back-compatibility.

A series is used to generate numerical data and store a few style-related attributes, like label, or line_color, or surface_color inherited from the old implementation, and now the more general rendering_kw.

After a series has been created, sooner or later it will be processed by a renderer: it is just a function which extract the numerical data and styling options from the series and adds them to the chart.

Copy link
Member

@sylee957 sylee957 Aug 18, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No. Series was more simple before #25469, and you started to add a bunch of things there.
Well, I start to believe that label or line_color makes sense.
Because line_color can be similar to 'vertex colors' which is often nontrivial with geometry
And label makes sense in some regards because there is no big deal to consider that as 'name' of a geometry.
But I didn't have a roadmap for that to extend to have much more attributes, if that is not contextually necessary, and if Series could be a public objects that can be inherited by users.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I read your comment 3 times, and I still don't understand what you are trying to say?!?!?!?!?!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don’t be personal
It does not intimidate me.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@sylee957 Please, consider writing in your native language. Then, I'll use Google Translate or something similar. Because right now, I don't understand what kind of grammar you are using. At least 3 out of 4 of your sentences doesn't make any sense to me... It's like reading a text generated from a really old chat-bot: I don't understand you! So, don't take it personal. A communication is successful if two or more people understands each other. I'm unable to understand what you are writing.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please, consider writing in your native language. Then, I'll use Google Translate or something similar. Because right now, I don't understand what kind of grammar you are using. At least 3 out of 4 of your sentences doesn't make any sense to me... It's like reading a text generated from a really old chat-bot:

I have web archives of your comments

@oscarbenjamin
Copy link
Collaborator

I don't favor the plot with multiple expressions though.

I very often want to plot multiple expressions usually to compare them. The current interface is not very good for doing this though.

Often though I might want to overlay different kinds of plot e.g. plot vs plot_implicit. I don't know that the approach here works for that.

In general I have to say that I have never been happy with the interface for any plotting library be that matplotlib, Matlab etc. The obscure layers of configuring properties are one issue and then the mix of stateful and declarative things are another.

Probably the main problem here is that show defaults to True. I think what I really want is perhaps more like:

show(
    plot(cos(x + pi / 2) * exp(-x / 4), "oscillator"),
    plot_implicit(x**2 + y**2 - 1, "circle"),
    xlim=(-2, 2)
)

Is there a way to do that? Perhaps we could use different names for these rather than plot and plot_implicit like:

plot(
    line(cos(x + pi / 2) * exp(-x / 4), "oscillator"),
    line_implicit(x**2 + y**2 - 1, "circle"),
    xlim = (-2, 2),
    axes = (x, y),
)

@Davide-sd
Copy link
Contributor Author

My opinion is that this makes the logic much more complicated and have maintainance problem later.

New capabilities are always going to introduce new level of complexities. In my opinion, this is not as "much more" complicated as you would like to think.

The real question is: are the new capabilities worth the the complexity? I really think they do!

For example, if we are going to stick to lists of expressions, it may be better to force people to always pass the list, even for length-1. We can keep the backward compatibility, by sorting out the trivial case, but I'm not sure we have to extend the backward behavior though.

(cos(x + pi / 2) * exp(-x / 4), "oscillator")

However, I don't favor the arguments like this because the IDE poorly gives argument hints and autocompletion. These kinds of things are best with type annotations, or wrapped inside custom objects, functions with explicit arguments or type annotations. (both is best). However, usually designing everything with flat arguments are favored because we get free lunch of type inference, if we are not using type annotations heavily anyway.

To be clear, everything you mentioned is also valid for SymPy 1.12:

from sympy import *
var("x")
plot((sin(x), (x, 0, 5)), (cos(x), (x, -5, 0))

Here, users can provide arguments of the form (expr, range). So, you still have tuple of elements.

All plotting functions into SymPy have this signature: (*args, show=True, **kwargs). If you want to add type hinting, you have to replace *args and **kwargs: that's a job for someone with a good knowledge about type hinting and back-compatibility.

I know nothing about type hinting, but I know that I invested a lot of time to make sure these new capabilities are back-compatible. If I need to type help(plot) or open a browser to the official documentation in order to understand how to use a function, that's a tradeoff I'm willing to take. And that's how things work up to SymPy 1.12: I don't intend to change that.

You are free to invest your time in order to improve type hinting on the plotting module. You are clearly more qualified than me. I honestly don't believe that it is possible to achieve what you have in mind while maintaining back-compatibility!

@Davide-sd
Copy link
Contributor Author

Often though I might want to overlay different kinds of plot e.g. plot vs plot_implicit. I don't know that the approach here works for that.

Probably the main problem here is that show defaults to True. I think what I really want is perhaps more like:

show(
    plot(cos(x + pi / 2) * exp(-x / 4), "oscillator"),
    plot_implicit(x**2 + y**2 - 1, "circle"),
    xlim=(-2, 2)
)

Right now, spb allows you to do something like that with:

p1 = plot(cos(x + pi / 2) * exp(-x / 4), "oscillator", show=False)
p2 = plot_implicit(x**2 + y**2 - 1, "circle", show=False)
(p1+p2).show()

It's kind of a PITA to add show=False to each plot command. However, I don't think it would be too difficult to implement your proposal. I'll add it to the todo list.

@sylee957
Copy link
Member

I favor the direction to go

plot(
    line(cos(x + pi / 2) * exp(-x / 4), "oscillator"),
    line_implicit(x**2 + y**2 - 1, "circle"),
    xlim = (-2, 2),
)

I think that it is best to make plots defined by some 'layer' than composite arguments.
It is proven to be one of the most general approaches to UI designs and graphics.

@smichr
Copy link
Member

smichr commented Aug 17, 2023

I am not sure why we have to tell sympy to do something explicity when that is the only way it can be done: so line_implicit(x**2 + y**2 - 1, "circle") or plot_implicit(x**2 + y**2 - 1, "circle") with defaults seem like it should pick the right wrapper. So if an expression is encountered like (x**2 + y**2 - 2, "circle") it just gets some type of implict wrapper; if the user wants to fine-tune it then they can wrap it explicity and provide the desired fine-tuning.

@sylee957
Copy link
Member

I am not sure why we have to tell sympy to do something explicity when that is the only way it can be done

The problem is that the style options may likely be very different depending on the types of geometry (2D, 3D, plot_implicit vs plot)
and a lot of styles that works in one side may not make sense if the plot is decided to be different.

@Davide-sd
Copy link
Contributor Author

I favor the direction to go

plot(
    line(cos(x + pi / 2) * exp(-x / 4), "oscillator"),
    line_implicit(x**2 + y**2 - 1, "circle"),
    xlim = (-2, 2),
)

This break back-compatibility. A new name must be define, maybe graphics instead of plot. Then, line or line_implicit will just be wrappers to the existing plot or plot_implicit, which set show=False.

Which brings us back to the scope of this PR, extending the arguments to accept label and styling arguments.

@sylee957
Copy link
Member

sylee957 commented Aug 18, 2023

This break back-compatibility.

I don't really get what you mean by backward compatibility here. I don't request to delete any new features before.
But making the existing code immutable and giving different new function doesn't break anything for users.

plot with extended tuple arguments looks ugly for me.
I'm not sure if you think that is beautiful, if you do, please give at least one reason for defense.

@Davide-sd
Copy link
Contributor Author

This break back-compatibility.

I don't really get what you mean by backward compatibility here.

Maybe you can achieve that in a back compatible way, but Just because you don't find the tuple arguments nice, you are willing to add a new mode of operation to an already existing function, plot, making it more confusing for the users and harder to document. It's simply brilliant.

plot with extended tuple arguments looks ugly for me. I'm not sure if you think that is beautiful, if you do, please give at least one reason for defense

I find them to be extremely nice, compact and friendly to use! I have already provided a very nice example for you to debate, and you purposely avoided it!!!!!!!!!

I'm warning you: this is the kind of discussions that enrages me, because you are avoiding to provide counter arguments to the specific problem that this PR addresses, basing exclusively on "tuple arguments looks ugly for me". Let me give you a brief recap: tuple arguments already exists, this PR extends them for good reasons.

As for a new plotting interface: open a new issue, collects opinion and start working on that. This PR is part of #23036 , I want to emphasize merging: the development is already completed!

@sylee957
Copy link
Member

sylee957 commented Aug 18, 2023

I'm warning you: this is the kind of discussions that enrages me,

I am not furious about anything,
and not offended by anything if the code isn't opinionated towards my idea or yours,
but just wanted to check that you have supplied one 'reason' for each questions I have, (and others may have) before merging

@Davide-sd Davide-sd requested a review from moorepants as a code owner August 18, 2023 22:05
@Davide-sd
Copy link
Contributor Author

Ops, sorry guys, I typed the wrong command...

@github-actions
Copy link

github-actions bot commented Aug 18, 2023

Benchmark results from GitHub Actions

Lower numbers are good, higher numbers are bad. A ratio less than 1
means a speed up and greater than 1 means a slowdown. Green lines
beginning with + are slowdowns (the PR is slower then master or
master is slower than the previous release). Red lines beginning
with - are speedups.

Significantly changed benchmark results (PR vs master)

Significantly changed benchmark results (master vs previous release)

       before           after         ratio
     [8059df73]       [002bf92c]
     <sympy-1.12^0>                 
-         116±6ms         76.6±1ms     0.66  integrate.TimeIntegrationRisch02.time_doit(10)
-         114±4ms         75.3±2ms     0.66  integrate.TimeIntegrationRisch02.time_doit_risch(10)
-      9.39±0.3ms       5.21±0.2ms     0.56  logic.LogicSuite.time_load_file
-     2.70±0.07ms         817±30μs     0.30  polys.TimePREM_LinearDenseQuadraticGCD.time_op(3, 'sparse')
-      13.8±0.2ms      2.40±0.05ms     0.17  polys.TimePREM_LinearDenseQuadraticGCD.time_op(5, 'sparse')
-        438±10μs          101±3μs     0.23  polys.TimePREM_QuadraticNonMonicGCD.time_op(1, 'sparse')
-      6.78±0.1ms         446±10μs     0.07  polys.TimePREM_QuadraticNonMonicGCD.time_op(3, 'sparse')
-      15.0±0.2ms      1.31±0.01ms     0.09  polys.TimePREM_QuadraticNonMonicGCD.time_op(5, 'sparse')
-      8.15±0.1ms       4.89±0.1ms     0.60  polys.TimeSUBRESULTANTS_LinearDenseQuadraticGCD.time_op(2, 'sparse')
-      35.2±0.9ms       15.1±0.2ms     0.43  polys.TimeSUBRESULTANTS_LinearDenseQuadraticGCD.time_op(3, 'sparse')
-     8.13±0.08ms      1.48±0.03ms     0.18  polys.TimeSUBRESULTANTS_QuadraticNonMonicGCD.time_op(1, 'sparse')
-      19.5±0.6ms       11.6±0.3ms     0.60  polys.TimeSUBRESULTANTS_QuadraticNonMonicGCD.time_op(2, 'sparse')
-         270±4ms         85.6±1ms     0.32  polys.TimeSUBRESULTANTS_QuadraticNonMonicGCD.time_op(3, 'sparse')
-      8.87±0.1ms         677±20μs     0.08  polys.TimeSUBRESULTANTS_SparseGCDHighDegree.time_op(3, 'sparse')
-        37.6±1ms      1.04±0.05ms     0.03  polys.TimeSUBRESULTANTS_SparseGCDHighDegree.time_op(5, 'sparse')
-        775±30μs         270±10μs     0.35  polys.TimeSUBRESULTANTS_SparseNonMonicQuadratic.time_op(1, 'sparse')
-      9.18±0.2ms         289±10μs     0.03  polys.TimeSUBRESULTANTS_SparseNonMonicQuadratic.time_op(3, 'sparse')
-        24.1±1ms          283±4μs     0.01  polys.TimeSUBRESULTANTS_SparseNonMonicQuadratic.time_op(5, 'sparse')
-         221±7μs          128±2μs     0.58  solve.TimeMatrixOperations.time_rref(3, 0)
-        409±10μs          155±3μs     0.38  solve.TimeMatrixOperations.time_rref(4, 0)
-        41.5±2ms      17.4±0.09ms     0.42  solve.TimeSolveLinSys189x49.time_solve_lin_sys

Full benchmark results can be found as artifacts in GitHub Actions
(click on checks at the top of the PR).

Comment on lines +966 to +973
# single argument: lambda function
f = lambda t: t
p = plot(lambda t: t)
assert isinstance(p[0], LineOver1DRangeSeries)
assert callable(p[0].expr)
assert p[0].ranges[0][1:] == (-10, 10)
assert p[0].get_label(False) == ""
assert p[0].rendering_kw == {}
Copy link
Member

@sylee957 sylee957 Aug 19, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I also notice that lambda function can be used instead of SymPy function.
But I raise another question that if lambda is used, does it never need SymPy at all at that cod path, or does it still depends on the SymPy.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

lambda functions supporting vectorized operations can be used with the plotting module, but don't support all functionalities. Obviously, sympy is required to run the plotting module.

@sylee957
Copy link
Member

Ops, sorry guys, I typed the wrong command...

I'm starting to think if it is really not sure that we should acquire the project by merging to SymPy core,
than opening repository in SymPy community as external dependency.
Such frustration gives some skepticism if this is the right direction to do this

@smichr
Copy link
Member

smichr commented Aug 20, 2023

Ops, sorry guys, I typed the wrong command...

This happens to me frequently -- I think there is a keyboard shortcut (or an Enter pressed when that action is highlighted on the page). It's annoying to have this happen so undesirably easily.

@smichr
Copy link
Member

smichr commented Aug 20, 2023

If the tuple discussion refers to here I would add this obsevation:

In either case you can "store a plot" to be used in another plot. You are either storing them as "p" values or as tuples.

p1 = plot(cos(x + pi / 2) * exp(-x / 4), (x, 0, 2 * pi),
    label="oscillator",
    ylim=(-1.25, 1.25), show=False)
p2 = plot(exp(-x / 4), (x, 0, 2 * pi),
    label="upper limit",
    rendering_kw={"linestyle": ":"}, show=False)
p3 = plot(-exp(-x / 4), (x, 0, 2 * pi),
    label="lower limit",
    rendering_kw={"linestyle": ":"}, show=False)
p1.extend(p2)
p1.extend(p3)
p1.show()

vs

t1 = (cos(x + pi / 2) * exp(-x / 4), "oscillator")
t2 = (exp(-x / 4), "upper limit", {"linestyle": ":"})
t3 = (-exp(-x / 4), "upper limit", {"linestyle": ":"})
plot(t1, t2, t3,
    (x, 0, 2 * pi),
    ylim=(-1.25, 1.25)
)
  • The one-by-one extending of the plot feels awkward to me--couldn't it support the plot(p1, p2, p3) syntax? It feels like manually drawing something on the page in TeX.
  • I also agree that needing to put "show=False" on each stored plot feels clunky (but I understand the reason for it).
  • I like that the limit is clearly seen on the tuple style. If no x-range were given for the plot case (i.e. omitted from p3) then would default limits be used?
  • Best of all, a user can choose whichever format is preferred. I think we need to keep the first form form backward compatibility; I like the addition of the tuple form and would never look back to the other form. But I don't mind keeping both camps happy.

@Davide-sd
Copy link
Contributor Author

* I like that the limit is clearly seen on the tuple style. If no x-range were given for the plot case (i.e. omitted from p3) then would default limits be used?

Yes, default limits will be used.

@smichr
Copy link
Member

smichr commented Aug 21, 2023

@sylee957 , do you have any objection to merging? I know you raised some points but I am not following intently enough to know where you are at with any concerns.

@sylee957
Copy link
Member

No, but I would not merge this to petition code-of-conduct violation issues

Copy link
Member

@sylee957 sylee957 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@smichr
Copy link
Member

smichr commented Aug 22, 2023

Probably the main problem here is that show defaults to True. I think what I really want is perhaps more like:

What about using the SymPy caps vs lower syntax: plot defaults to show=True and Plot defaults to show=False?

@smichr
Copy link
Member

smichr commented Aug 22, 2023

No, but I would not merge this to petition code-of-conduct violation issues

Although @asmeurer or @certik are the ones to assess this, I did send some thoughts offline to you and Davide.

@smichr smichr merged commit 6b71094 into sympy:master Aug 23, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

5 participants