On Sun, 11 Apr 2021 at 16:26, David Bailey <[email protected]> wrote:
>
> Dear group,
>
> Recently Bruce Allen discussed a problem that he had after pickling a
> list. However, I think he revealed a deeper problem that is nothing to
> do with pickling.
>
> w=sin(Symbol("x", positive=True))*cos(Symbol("x"))
>
> sin(x)*cos(x)
>
> test=Integral(w,,x)
>
> Integral(sin(x)*cos(x), x)
>
> test.doit()
>
> sin(x)*sin(x)
>
> OK, that was a bit contrived, but it does show how worryingly easy it is
> to generate multiple symbols with the same name in the same Python context.
The solution is straight-forward: just call Symbol once for each
symbol that you want to use e.g.:
x = Symbol('x')
w = sin(x)*cos(x)
These kinds of problems come from wanting to construct the symbol in
different places and have it be the same symbol like in:
def func1():
x = Symbol('x')
expr = x**2 + 1
return func2(expr)
def func2(expr):
x = Symbol('x')
return expr.subs(x, 2)
Although that approach sometimes works it won't always work with
assumptions and it's bad practice anyway. The robust solution here is
to create the symbol once and pass the symbol from func1 to func2:
def func1():
x = Symbol('x')
expr = x**2 + 1
return func2(expr, x)
def func2(expr, x):
return expr.subs(x, 2)
While it can be convenient in interactive use I never write code that
depends on being able to reconstruct a symbol (although this is widely
used in the sympy test suite e.g. for the integration constants
returned by dsolve).
> Clearly when processing the first statement, SymPy inserts x into its
> data base as a symbol with an assumption. After that it creates a
> distinct symbol x without that assumption!
Don't think of there being any "database". There is a cache but it is
not supposed to have any noticeable effect except when you compare
objects like "a is b". Demonstration:
In [11]: x1 = Symbol('x')
In [12]: other_symbols = symbols('y:10000') # create other symbols to
blow the cache
In [13]: x2 = Symbol('x')
In [14]: x1 is x2
Out[14]: False
In [15]: x1 == x2
Out[15]: True
Here x1 and x2 are distinct Python objects (x1 is x2 -> False). They
compare as equal in SymPy (x1 == x2 -> True) because they have the
same name and assumptions. SymPy does not keep any registry of the
symbols created, it just compares their properties.
> I would have thought it would be kinder to cause an exception in that
> case to prevent the ensuing confusion.
I'm not sure when you would expect that the exception would be raised
but I don't think there's any way to implement this without causing a
lot of unjustifiable breakage for code that uses sympy as a library.
Suppose I make a library that depends on sympy and defines a function
called do_stuff which internally creates symbols. The do_stuff
function might be like:
def do_stuff():
x = Symbol('x')
r1, r2 = solve(x**2 - 2, x)
return r1, r2
Now a user of this library might do something like:
from sympy import Symbol
from library import do_stuff
x = Symbol('x', positive=True)
roots = do_stuff()
Now at the point when the do_stuff function calls Symbol there already
exists a Symbol with the same name. However these symbols are used in
different parts of the code and have no possibility of interacting. We
couldn't raise an exception at the moment Symbol('x') is created
because that would break all kinds of things.
Alternatively there could be a check for whether symbols with the same
name but different assumptions are present in a given expression.
Checking for that as part of every arithmetic operation would slow
things down considerably though.
Oscar
--
You received this message because you are subscribed to the Google Groups
"sympy" group.
To unsubscribe from this group and stop receiving emails from it, send an email
to [email protected].
To view this discussion on the web visit
https://groups.google.com/d/msgid/sympy/CAHVvXxTrSPV5p8hcZ4fSoPqhCDppmtbbVapDJch-TT5DFJye8g%40mail.gmail.com.