Hi list,
as you might have noticed, I am trying to improve the syntax and semantics for symbolic math in Python. Until now, I have to say, my ideas were not that well received, but I learned from the discussion and maybe this time I come up with something people like.
To frame the problem, let us try to solve the equation x ** 2 == 1/2 using sympy:
>>> from sympy import Eq, solve, symbols, S >>> x = symbols("x") >>> solve(Eq(x**2, S(1)/2)) [-sqrt(2)/2, sqrt(2)/2]
that worked well, but actually we would like to write the last line simply as
>>> solve(x**2 == 1/2)
as you might notice, this is fully legal Python syntax. Unfortunately the semantics is such that sympy has no way to determine what is actually going on, this is why they invented all those helper functions shown above.
My idea is now to start at the line above, "x = symbols('x')". I propose a new syntax, "symbolic x", which tells the parser that x is a symbolic variable, and expressions should not be executed, but handed over as such to the calling functions. To stay with the example, the code would look like this (this is fake, I did not prototype this yet):
>>> from sympy import solve >>> symbolic x >>> solve(x**2 == 1/2) [-sqrt(2)/2, sqrt(2)/2]
Now to the details. The scope that "symbolic" acts on should be the same as the scope of "global". Note that "symbolic x" is currently illegal syntax, so we would not even need to make "symbolic" a keyword.
Expressions that contain a symbolic variable are not executed, but instead the expression should be given to the function as a tuple, so in the example above, solve would be given ('==', ('**', ('x', 2)), ('/', 1, 2)). If that looks like LISP to you, then you are not completely wrong. The boundaries of expressions are function calls, assignments, getitems and getattrs, as they can be overloaded much easier by other means. To give an example with gory details (again, this is fake):
>>> symbolic x >>> d = {"a" : 5} >>> c = 7 # not symbolic! >>> print(c * x + d["a"]) ('+', ('*', 7, 'x'), 5)
Maybe we would want to have a new class "expressiontuple" which simply acts as a pretty-printer, then the last lines would become
>>> print(x + d["a"]) 7 * x + 5
What sympy does with this tuple would be fully at its discretion.
I think that other libraries could also benefit from this proposal. As an example, in a numerics library (numpy? scipy?) one could improve numerical integration as in
>>> symbolic x >>> integrate(sin(x), x, 0, pi)
then the integrator could even compile the expression to machine code for faster integration.
numpy could also compile expressions to machine code, making it even faster than it is now, as in
>>> symbolic x >>> f = compile(5 * x + 7 * y, (x, y)) >>> f(matrix_a, matrix_b)
Let's see how well this proposal fares.
Cheers
Martin
On Wed, 19 May 2021 at 07:41, Martin Teichmann martin.teichmann@gmail.com wrote:
Hi list,
To frame the problem, let us try to solve the equation x ** 2 == 1/2 using sympy:
>>> from sympy import Eq, solve, symbols, S >>> x = symbols("x") >>> solve(Eq(x**2, S(1)/2)) [-sqrt(2)/2, sqrt(2)/2]
that worked well, but actually we would like to write the last line simply as
>>> solve(x**2 == 1/2)
as you might notice, this is fully legal Python syntax. Unfortunately the semantics is such that sympy has no way to determine what is actually going on, this is why they invented all those helper functions shown above.
Numpy override __eq__ liberally to allow this kind of behaviour (though not root searching, but returning ndarray instead of a boolean for == so things remain matrices. As a toy example to show that it is definitely possible to let the x "poison the well":
``` class Symbolic: def __init__(self, expression): self.expression = expression
def __pow__(self, power): return Symbolic(f"{self.expression} ** {power}")
def __eq__(self, other): return Symbolic(f"{self.expression} == {other}")
def __truediv__(self, other): return Symbolic(f"{self.expression} / {other}")
def __str__(self): return self.expression
def __repr__(self): return f"{self.__class__}({self.expression})"
x = Symbolic("x") print(x ** 2 == 1 / 2) print(x ** 2 == Symbolic(1) / 2) ```
Which gives output:
``` x ** 2 == 0.5 x ** 2 == 1 / 2 ```
Now, it hasn't done root solving, but it could (or return an object that could), so to say that "Unfortunately the semantics is such that sympy has no way to determine what is actually going on" is an unsound foundation for your idea.
My idea is now to start at the line above, "x = symbols('x')". I propose a new syntax, "symbolic x", which tells the parser that x is a symbolic variable, and expressions should not be executed, but handed over as such to the calling functions.
So... what you're suggesting can basically already be done, though as I did above, might require some additional "poisoning" so that all roots are poisoned, just like when there was the thread about float literals (e.g. 1F / 3 to become Float(1, 3)).
On Wed, 19 May 2021 at 07:41, Martin Teichmann martin.teichmann@gmail.com wrote:
that worked well, but actually we would like to write the last line simply as
>>> solve(x**2 == 1/2)
as you might notice, this is fully legal Python syntax. Unfortunately the semantics is such that sympy has no way to determine what is actually going on, this is why they invented all those helper functions shown above.
It's almost possible to do this right now. Using the compiler and ast module, you can write a version of solve that works like
>>> solve("x**2 == 1/2")
You have to quote the argument, and yes that probably means your editor/IDE won't help you as much with the expression syntax, but otherwise this could be made to work exactly as you want with no language changes.
People don't often do this, so presumably there's a reason people don't like having to quote "stuff that's basically Python code". But it *is* technically possible.
My idea is now to start at the line above, "x = symbols('x')". I propose a new syntax, "symbolic x", which tells the parser that x is a symbolic variable, and expressions should not be executed, but handed over as such to the calling functions. To stay with the example, the code would look like this (this is fake, I did not prototype this yet):
>>> from sympy import solve >>> symbolic x >>> solve(x**2 == 1/2) [-sqrt(2)/2, sqrt(2)/2]
Now to the details.
Before you get to details of implementation, you should explain:
1. Why sympy doesn't have a solve("x**2 == 1/2") function taking a string like I described above? Why isn't that a good solution here? 2. Why new syntax is any more likely to be useful for sympy than a string-based function.
The barrier for language changes, and in particular new syntax, is high (as you've discovered). So proposals need to be pretty thorough in examining ways to solve a problem within the existing language before proposing a new language feature. Particularly if the feature is focused on improving things just for a subset of the Python user base.
You really should look at the history of the matrix multiplication operator in Python - start with PEP 465, https://www.python.org/dev/peps/pep-0465/, but look back at the history of all the proposals that *didn't* work leading up to that - to get a feel for how much work it needs to get a feature focused on the needs of a specialist community into the core language. And as others have pointed out, symbolic maths and sympy is nowhere near as big an audience as the numeric community.
Paul
Martin wrote:
as you might have noticed, I am trying to improve the syntax and semantics
for symbolic math in Python. Until now, I have to say, my ideas were not that well received, but I learned from the discussion and maybe this time I come up with something people like.
For about 10 years I've used PARI/gp for computer algebra, mainly for integer linear algebra and polynomials. One day I might use its number theory features. http://pari.math.u-bordeaux.fr/
Lately it's acquired good Python bindings, and most likely for my next project I'll start using them. https://pari.math.u-bordeaux.fr/Events/PARI2019/talks/jeroen.html https://pypi.org/project/cypari2/ https://cypari2.readthedocs.io/en/latest/
Regarding semantics, I'm very happy to go along with PARI/gp. This is in part because of its deep roots in computational number theory and the community it has around it.
See also: Integer Factorization Software: PARI/GP, Mathematica, and SymPy https://dzone.com/articles/integer-factorization-software-parigp-mathematica
Regarding syntax, I'd be pleased to see a paritools package that makes it easier to use the cypari2 bindings for common purposes. This perhaps could become part of sympy. It's also worth looking at sage. https://doc.sagemath.org/html/en/reference/libs/sage/libs/pari.html
This is what I would like. Other people will most likely have different wishes for improving symbolic math in Python.
It's a neat idea but I agree with the others that as proposed, it's probably too focused on turning python into a better mathematics tool, rather than turning python into a better programming language.
That being said, from a more generalist standpoint I wonder if it would be useful in many connects other than symbolic math to have a way to "get to" and work with the actual written code in a module from inside that same module. Essentially to parse actual lines of code, not strings containing code, into an AST, and work with it. The "poison the well" approach is an interesting idea from that standpoint.
On Wed, May 19, 2021, 6:32 AM Jonathan Fine jfine2358@gmail.com wrote:
Martin wrote:
as you might have noticed, I am trying to improve the syntax and semantics
for symbolic math in Python. Until now, I have to say, my ideas were not that well received, but I learned from the discussion and maybe this time I come up with something people like.
For about 10 years I've used PARI/gp for computer algebra, mainly for integer linear algebra and polynomials. One day I might use its number theory features. http://pari.math.u-bordeaux.fr/
Lately it's acquired good Python bindings, and most likely for my next project I'll start using them. https://pari.math.u-bordeaux.fr/Events/PARI2019/talks/jeroen.html https://pypi.org/project/cypari2/ https://cypari2.readthedocs.io/en/latest/
Regarding semantics, I'm very happy to go along with PARI/gp. This is in part because of its deep roots in computational number theory and the community it has around it.
See also: Integer Factorization Software: PARI/GP, Mathematica, and SymPy
https://dzone.com/articles/integer-factorization-software-parigp-mathematica
Regarding syntax, I'd be pleased to see a paritools package that makes it easier to use the cypari2 bindings for common purposes. This perhaps could become part of sympy. It's also worth looking at sage. https://doc.sagemath.org/html/en/reference/libs/sage/libs/pari.html
This is what I would like. Other people will most likely have different wishes for improving symbolic math in Python.
-- Jonathan
Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/EOOMLH... Code of Conduct: http://python.org/psf/codeofconduct/
On Wed, May 19, 2021 at 8:14 AM Ricky Teachey ricky@teachey.org wrote:
It's a neat idea but I agree with the others that as proposed, it's probably too focused on turning python into a better mathematics tool, rather than turning python into a better programming language.
That being said, from a more generalist standpoint I wonder if it would be useful in many connects other than symbolic math to have a way to "get to" and work with the actual written code in a module from inside that same module. Essentially to parse actual lines of code, not strings containing code, into an AST, and work with it.
You can essentially do this using import hooks as I have mentioned many
times on this list. A good example of what one can accomplish by transforming and AST is given by MacroPy: https://macropy3.readthedocs.io/en/latest/
I also gave various examples of using import hooks to modify Python's syntax using https://github.com/aroberge/ideas which I wrote to facilitate such experimentation -- although I consider this more of a toy than anything resembling a professional and robust package.
André Roberge
I'm +1 on the idea but -1 on the proposed syntax. Yes it would be kind of nice *but* it's not how Python arithmetic or grammar works. And why should Python have that functionality? AFAICT Python doesn't have a module in stdlib for symbolic math. This looks like a feature request to third party modules that support symbolic math like sympy. People usually doesn't use symbolic math in Python but those who do know the drawbacks and have already come up with hacks to overcome those.
To frame the problem, let us try to solve the equation x ** 2 == 1/2 using sympy:
>>> from sympy import Eq, solve, symbols, S >>> x = symbols("x") >>> solve(Eq(x**2, S(1)/2)) [-sqrt(2)/2, sqrt(2)/2]
that worked well, but actually we would like to write the last line simply as
>>> solve(x**2 == 1/2)
This is essentially how this would be written in sagemath (a CAS exposing various FOSS math software behind a unified python-based interface). More about sagemath can be found at https://www.sagemath.org/
In sage, this would be written
solve([x**2 == 1/2], x)
The additional "x" is because sage also accepts things like
y = var('y') solve([x**2 == y], x) # solve for x in terms of other variables
Large amounts of sage are written in python, including essentially the whole symbolic mathematics stack. I'm personally content with using a CAS like sage when I want to manipulate mathematics and keeping symbolic math separate from my default python interpreter.
- DLD
SUB
Hello everyone, what is the usual way to "like" a mail in the maillist and subscribing to the thread ?
By sending a message it adds me to the "Participants" in the webapp which is neat (I can then search for my messages)
I could do it in the webapp but not everybody will see it
Le mer. 19 mai 2021 à 17:02, David Lowry-Duda david@lowryduda.com a écrit :
To frame the problem, let us try to solve the equation x ** 2 == 1/2 using sympy:
>>> from sympy import Eq, solve, symbols, S >>> x = symbols("x") >>> solve(Eq(x**2, S(1)/2)) [-sqrt(2)/2, sqrt(2)/2]
that worked well, but actually we would like to write the last line
simply as
>>> solve(x**2 == 1/2)
This is essentially how this would be written in sagemath (a CAS exposing various FOSS math software behind a unified python-based interface). More about sagemath can be found at https://www.sagemath.org/
In sage, this would be written
solve([x**2 == 1/2], x)
The additional "x" is because sage also accepts things like
y = var('y') solve([x**2 == y], x) # solve for x in terms of other variables
Large amounts of sage are written in python, including essentially the whole symbolic mathematics stack. I'm personally content with using a CAS like sage when I want to manipulate mathematics and keeping symbolic math separate from my default python interpreter.
- DLD
Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/PMUPJJ... Code of Conduct: http://python.org/psf/codeofconduct/
solve(x**2 == 1/2)
pip install funcoperators
solve(x ** 2 |Equals| 1/2)
<<< Equals = infix(Eq) <<< from sympy import Eq
Le mer. 19 mai 2021 à 08:40, Martin Teichmann martin.teichmann@gmail.com a écrit :
Hi list,
as you might have noticed, I am trying to improve the syntax and semantics for symbolic math in Python. Until now, I have to say, my ideas were not that well received, but I learned from the discussion and maybe this time I come up with something people like.
To frame the problem, let us try to solve the equation x ** 2 == 1/2 using sympy:
>>> from sympy import Eq, solve, symbols, S >>> x = symbols("x") >>> solve(Eq(x**2, S(1)/2)) [-sqrt(2)/2, sqrt(2)/2]
that worked well, but actually we would like to write the last line simply as
>>> solve(x**2 == 1/2)
as you might notice, this is fully legal Python syntax. Unfortunately the semantics is such that sympy has no way to determine what is actually going on, this is why they invented all those helper functions shown above.
My idea is now to start at the line above, "x = symbols('x')". I propose a new syntax, "symbolic x", which tells the parser that x is a symbolic variable, and expressions should not be executed, but handed over as such to the calling functions. To stay with the example, the code would look like this (this is fake, I did not prototype this yet):
>>> from sympy import solve >>> symbolic x >>> solve(x**2 == 1/2) [-sqrt(2)/2, sqrt(2)/2]
Now to the details. The scope that "symbolic" acts on should be the same as the scope of "global". Note that "symbolic x" is currently illegal syntax, so we would not even need to make "symbolic" a keyword.
Expressions that contain a symbolic variable are not executed, but instead the expression should be given to the function as a tuple, so in the example above, solve would be given ('==', ('**', ('x', 2)), ('/', 1, 2)). If that looks like LISP to you, then you are not completely wrong. The boundaries of expressions are function calls, assignments, getitems and getattrs, as they can be overloaded much easier by other means. To give an example with gory details (again, this is fake):
>>> symbolic x >>> d = {"a" : 5} >>> c = 7 # not symbolic! >>> print(c * x + d["a"]) ('+', ('*', 7, 'x'), 5)
Maybe we would want to have a new class "expressiontuple" which simply acts as a pretty-printer, then the last lines would become
>>> print(x + d["a"]) 7 * x + 5
What sympy does with this tuple would be fully at its discretion.
I think that other libraries could also benefit from this proposal. As an example, in a numerics library (numpy? scipy?) one could improve numerical integration as in
>>> symbolic x >>> integrate(sin(x), x, 0, pi)
then the integrator could even compile the expression to machine code for faster integration.
numpy could also compile expressions to machine code, making it even faster than it is now, as in
>>> symbolic x >>> f = compile(5 * x + 7 * y, (x, y)) >>> f(matrix_a, matrix_b)
Let's see how well this proposal fares.
Cheers
Martin _______________________________________________ Python-ideas mailing list -- python-ideas@python.org To unsubscribe send an email to python-ideas-leave@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python-ideas@python.org/message/ZMUV4O... Code of Conduct: http://python.org/psf/codeofconduct/
People always come up with hacks like Ir. Robert Vanden Eynde has come up with the vertical bar hack. This is why I feel there's no need to have additional performance decrease and new syntax only to solve a little problem that occurs in some third party like sympy.
On Wed, 19 May 2021 at 07:41, Martin Teichmann martin.teichmann@gmail.com wrote:
Hi list,
as you might have noticed, I am trying to improve the syntax and semantics for symbolic math in Python. Until now, I have to say, my ideas were not that well received, but I learned from the discussion and maybe this time I come up with something people like.
I think that the discussion could be more productive if it started on the SymPy mailing list. Some of the things you refer to can be improved in SymPy itself without needing changes to core Python.
-- Oscar
Aside from the type conversion dunders (e.g. `__bool__`), and the `and`, `or` and `in` operators, this is possible right now: You can create a `Symbol` class that returns another `Symbol` in the compatible operator dunder methods, while internally maintaining a list of (operator, args, kwargs). Then you can "replay" all the operations when you evaluate the symbol by in some way binding it to a value. You could even go as far as to build an AST from the operations list, and compile it to a function or lambda (its argument is the value you bind to), so you avoid the (significant) overhead of looping through the operations and applying then.
It's unfortunate that there is no way to have e.g. `'spam' in my_symbolic_set` evaluate to something else than a boolean. Also, this approach will not work with everything else for which there is no dunder method, e.g. `math.sin(my_symbolic_value)` cannot be tricked into returning a Symbol.
On Mon, May 24, 2021 at 09:49:13PM -0000, Joren wrote:
It's unfortunate that there is no way to have e.g. `'spam' in my_symbolic_set` evaluate to something else than a boolean. Also, this approach will not work with everything else for which there is no dunder method, e.g. `math.sin(my_symbolic_value)` cannot be tricked into returning a Symbol.
Monkey-patching to the rescue.
import math from math import sin as _sin
def my_sin(x): if isinstance(x, Symbol): ... else: return _sin(x)
math.sin = my_sin
Obligatory link to:
https://avdi.codes/why-monkeypatching-is-destroying-ruby/
solve(x**2 == 1/2)
as you might notice, this is fully legal Python syntax. Unfortunately the semantics is such that sympy has no way to determine what is actually going on, this is why they invented all those helper functions shown above.
This is a SymPy special "solution". Nothing in Python prevents from returning a non-boolean from __eq__(): https://docs.python.org/3/reference/datamodel.html#object.__eq__
For example, instead you could use singletons. In fact, every object in symbolic math is a singleton. x**2 is such, (x + 1)**2 - 2*x - 1 - is another, different one. To test structural equality in this approach - you can use "is", instead of "==".
On Tue, 25 May 2021 at 13:31, Sergey B Kirpichev skirpichev@gmail.com wrote:
solve(x**2 == 1/2)
as you might notice, this is fully legal Python syntax. Unfortunately the semantics is such that sympy has no way to determine what is actually going on, this is why they invented all those helper functions shown above.
This is a SymPy special "solution". Nothing in Python prevents from returning a non-boolean from __eq__(): https://docs.python.org/3/reference/datamodel.html#object.__eq__
For example, instead you could use singletons. In fact, every object in symbolic math is a singleton. x**2 is such, (x + 1)**2 - 2*x - 1 - is another, different one. To test structural equality in this approach - you can use "is", instead of "==".
Although nothing in Python prevents defining __eq__() how you want and having it return other values can still be problematic to do so because of the way that __eq__() is tied to __hash__() and used in dicts and sets. Python the language made the decision that the == operator is also used for equality in dicts and sets. If x**2 == 1/2 were to return a symbolic equation object then it would imply that symbolic objects could not be used in Python's important container types.
The overloaded use of == in Python also leads to a tension between wanting e.g. 1 == 1.0 when == is used explicitly but perhaps not wanting 1 and 1.0 to be interchangeable in a dict/set:
{1.0, 1}
{1.0}
{1, 1.0}
{1}
On that basis I would say that unless you are sure you have no need to use objects as dict keys or in sets then you should define __eq__() so that a==b returns a bool. Also a==b should only return True in the cases where a and b are interchangeable and you genuinely don't care which of the two objects you would get.
-- Oscar
Although nothing in Python prevents defining __eq__() how you want and having it return other values can still be problematic to do so because of the way that __eq__() is tied to __hash__() and used in dicts and sets.
I think, this can work if you guarantee there are no hash collisions. This is doable, if you restrict yourself by some symbolic class hierarchy: e.g. you can enumerate symbolic objects within the session. Probably, id() will work: remember - all your objects are singletons!
But you will not be able to mix built-in data types and your symbolic objects in same set, for example. In fact, this might be not a too high price: e.g. SymPy already uses Integer's and Rational's instead of built-in types. So, every "external" object must be converted to your class hierarchy.
Also a==b should only return True
That's an easy thing, but I doubt that the docs enforce us to do this.
Have you considered using the JAX library's trick of decorating functions to provide automatic symbols?
For example, your problem could be done in current Python with an appropriately-coded library:
@symbolic def f(x, a, b, c): return a * x ** 2 + b * x + c
sentinel = Sentinel()
solve(f(sentinel, 1, 0, -1 / 2), solve_for=sentinel)
The idea is that f(sentinel) would produce the expression graph by propagating tracer objects throughout the tree. Then, solve would identify the appropriate tracer to be solved.
JAX uses this trick to accomplish three tasks: JIT compilation of mathematical expressions, automatic calculation of gradients and related functions, and automatic generation of vectorized versions of functions.
To be fair, JAX's design is brilliant. I don't fault the SymPy people for not coming up with it, but the problem that you're trying to solved could be done with a better-designed SymPy. You don't need new syntax.
Neil On Wednesday, May 19, 2021 at 2:39:18 AM UTC-4 Martin Teichmann wrote:
Hi list,
as you might have noticed, I am trying to improve the syntax and semantics for symbolic math in Python. Until now, I have to say, my ideas were not that well received, but I learned from the discussion and maybe this time I come up with something people like.
To frame the problem, let us try to solve the equation x ** 2 == 1/2 using sympy:
from sympy import Eq, solve, symbols, S x = symbols("x") solve(Eq(x**2, S(1)/2))
[-sqrt(2)/2, sqrt(2)/2]
that worked well, but actually we would like to write the last line simply as
solve(x**2 == 1/2)
as you might notice, this is fully legal Python syntax. Unfortunately the semantics is such that sympy has no way to determine what is actually going on, this is why they invented all those helper functions shown above.
My idea is now to start at the line above, "x = symbols('x')". I propose a new syntax, "symbolic x", which tells the parser that x is a symbolic variable, and expressions should not be executed, but handed over as such to the calling functions. To stay with the example, the code would look like this (this is fake, I did not prototype this yet):
from sympy import solve symbolic x solve(x**2 == 1/2)
[-sqrt(2)/2, sqrt(2)/2]
Now to the details. The scope that "symbolic" acts on should be the same as the scope of "global". Note that "symbolic x" is currently illegal syntax, so we would not even need to make "symbolic" a keyword.
Expressions that contain a symbolic variable are not executed, but instead the expression should be given to the function as a tuple, so in the example above, solve would be given ('==', ('**', ('x', 2)), ('/', 1, 2)). If that looks like LISP to you, then you are not completely wrong. The boundaries of expressions are function calls, assignments, getitems and getattrs, as they can be overloaded much easier by other means. To give an example with gory details (again, this is fake):
symbolic x d = {"a" : 5} c = 7 # not symbolic! print(c * x + d["a"])
('+', ('*', 7, 'x'), 5)
Maybe we would want to have a new class "expressiontuple" which simply acts as a pretty-printer, then the last lines would become
print(x + d["a"])
7 * x + 5
What sympy does with this tuple would be fully at its discretion.
I think that other libraries could also benefit from this proposal. As an example, in a numerics library (numpy? scipy?) one could improve numerical integration as in
symbolic x integrate(sin(x), x, 0, pi)
then the integrator could even compile the expression to machine code for faster integration.
numpy could also compile expressions to machine code, making it even faster than it is now, as in
symbolic x f = compile(5 * x + 7 * y, (x, y)) f(matrix_a, matrix_b)
Let's see how well this proposal fares.
Cheers
Martin _______________________________________________ Python-ideas mailing list -- python...@python.org To unsubscribe send an email to python-id...@python.org https://mail.python.org/mailman3/lists/python-ideas.python.org/ Message archived at https://mail.python.org/archives/list/python...@python.org/message/ZMUV4OUDU... https://mail.python.org/archives/list/python-ideas@python.org/message/ZMUV4OUDUX3NSROM2KRUIQKSIBUCZOCD/ Code of Conduct: http://python.org/psf/codeofconduct/
On Mon, 31 May 2021 at 13:10, Neil Girdhar mistersheik@gmail.com wrote:
Have you considered using the JAX library's trick of decorating functions to provide automatic symbols?
For example, your problem could be done in current Python with an appropriately-coded library:
@symbolic def f(x, a, b, c): return a * x ** 2 + b * x + c
sentinel = Sentinel()
solve(f(sentinel, 1, 0, -1 / 2), solve_for=sentinel)
The idea is that f(sentinel) would produce the expression graph by propagating tracer objects throughout the tree. Then, solve would identify the appropriate tracer to be solved.
JAX uses this trick to accomplish three tasks: JIT compilation of mathematical expressions, automatic calculation of gradients and related functions, and automatic generation of vectorized versions of functions.
To be fair, JAX's design is brilliant. I don't fault the SymPy people for not coming up with it, but the problem that you're trying to solved could be done with a better-designed SymPy. You don't need new syntax.
Hi Neil,
Thanks for the suggestion but I'm not sure exactly what you mean.
It's not clear to me what problem you think this would solve for sympy. If the problem is to do with needing to declare symbols then this solution seems awkward because it only works if the variables are function parameters so that a decorator can be used on the function. The current situation is this:
a, b, c, x = symbols('a:c, x') eq = a*x**2 + b*x + c
Your suggestion seems to require defining an otherwise unnecessary function like this:
@symbolic def equation(a, b, c, x): return a*x**2 + b*x + c eq = equation()
That doesn't seem like an improvement to me (maybe I've misunderstood...).
Oscar