Sympify string expression depending on a real valued argument - python

I expect the following code evaluates derivative of sin(t)
import sympy as sy
t = sy.Symbol('t', real=True)
expr = sy.sympify('sin(t)')
dexpr = sy.diff(expr, t)
print dexpr
But actually it prints 0. If I change the first line by t = sy.Symbol('t'), it works well. It looks like sympy thinks there are 2 different t.
The question: how to say to sympy that my string expression depends on the real valued argument t, and how to sympify this string correctly?

If you declare t as a real variable, it will be considered a different variable to Symbol('t') (with no assumptions).
Try this way:
In [1]: treal = Symbol('t', real=True)
In [2]: t = Symbol('t')
In [3]: expr = sympify('sin(t)')
In [4]: expr.diff(treal)
Out[4]: 0
In [5]: expr.diff(t)
Out[5]: cos(t)
In [6]: treal == t
Out[6]: False
In [7]: expr_real = expr.subs(t, treal)
In [8]: expr_real.diff(treal)
Out[8]: cos(t)
In input [6] you may see that the two variables are considered different, even if both print as t. If you differentiate by the real variable your sympified expression (input [4]), your expression will be considered a constant, because the two ts do not identify as the same variable.
In input [7] I replaced t with treal, so that in input [8] I was able to correctly derive the expression.
EDIT
A fast form to import is by specifying in sympify a mapping between string-variables and local-variables:
In [1]: t = Symbol('t', real=True)
In [2]: expr = sympify('sin(t)', locals={'t': t})
In [3]: expr
Out[3]: sin(t)
In [4]: expr.diff(t)
Out[4]: cos(t)
In this way, t will be set to the variable defined in input [1].

Related

Sympy evalf for real symbol

This Sympy code works as I expect:
>>> x = sp.Symbol("x")
>>> y = sp.Symbol("y")
>>> z = sp.Symbol("z")
>>> (x+y+z).evalf(subs={"x":1, "y":2, "z":3})
6.0
However, if I use real-valued Symbols instead, the expression isn't simplified:
>>> x = sp.Symbol("x", real=True)
>>> y = sp.Symbol("y", real=True)
>>> z = sp.Symbol("z", real=True)
>>> (x+y+z).evalf(subs={"x":1, "y":2, "z":3})
x + y + z
I was unable to find an explanation for this by searching with keywords like sympy symbol real evalf - I only get unrelated results.
Why isn't the expression simplified in the second case? How can I substitute in values for real-valued Symbols and evaluate the expression?
Use the symbols as keys for the dictionary of substitutions, rather than string names:
>>> (x + y + z).evalf(subs={x: 1, y: 2, z: 3})
6.00000000000000
There appears to be an inconsistency between how complex and real symbols are treated:
>>> x = sp.Symbol('x')
>>> x.subs(x, 1)
1
>>> x.subs('x', 1)
1
>>> x = sp.Symbol('x', real=True)
>>> x.subs(x, 1)
1
>>> x.subs('x', 1)
x
I can't find anything relevant about this in the documentation, and the built-in help text isn't useful either. My best guess is that a string 'x' is naively converted using sp.Symbol, and the resulting symbol is always a complex-valued symbol that doesn't match the real-valued one with the same name.
I would consider this behaviour a bug and file a bug report (or look for an existing report). IMO, if a string is usable at all, it should match any symbol with that name; and an expression shouldn't be able to contain two different variables with the same name and different types; and trying to substitute in a variable with a matching name and incompatible type should probably raise an exception:
>>> x = sp.Symbol('x')
>>> # why allow this?
>>> broken = sp.Symbol('x', real=True) + x
>>> broken # if the types matched, it would simplify to 2*x
x + x
>>> # surely 2 is the only value that makes sense?
>>> broken.subs('x', 1)
x + 1
>>> x.subs('x', 1)
1
>>> # If this is allowed at all, surely the result should be 1?
>>> x.subs(sp.Symbol('x', real=True), 1)
x

Using diff on a SymPy Function object returns 1

I set up an undefined function and a variable to go with it:
import sympy as sp
f = sp.Function('f')
t = sp.Symbol('t')
If I differentiate f(t) with respect to t, it works fine:
f(t).diff(t)
# Derivative(f(t), t)
However, if I differentiate f itself with respect to t, I get this:
f.diff(t)
# 1
I'm not exactly sure what I expected here (probably an error), but I certainly didn't expect 1. What's going on?
A SymPy Function object is actually a Python class:
In [15]: f = Function('f')
In [16]: type(f(t))
Out[16]: f
In [17]: isinstance(f(t), f)
Out[17]: True
This is analogous to cos(t) being an instance of the cos class. Consequently f.diff is equivalent to Expr.diff i.e. an unbound class method:
In [18]: f.diff
Out[18]: <function sympy.core.expr.Expr.diff(self, *symbols, **assumptions)>
In [19]: Expr.diff
Out[19]: <function sympy.core.expr.Expr.diff(self, *symbols, **assumptions)>
In [20]: Expr.diff(t)
Out[20]: 1
This is equivalent to:
In [21]: t.diff()
Out[21]: 1
Note that the diff method allows not specifying the differentiation variable if the expression only has one symbol:
In [22]: sin(t).diff(t)
Out[22]: cos(t)
In [23]: sin(t).diff()
Out[23]: cos(t)

solving equation in python with SymPy takes forever

I tried to solve this equation but still running.
I gave the symbol and the equation is "Eq((1-(1+ x )(-60))/ x+32*(1+x)(-60) , 41.81)".
The way solve and solveset usual work is to split an expression into numerator and denominator, and return solutions for the one that are not in the other.
Let's define a helper function to put the solutions from nsolve into a FiniteSet and one to give the final solution:
>>> from sympy import FiniteSet, nsolve, Add, Eq
>>> from sympy.abc import x
>>> rr = lambda x: FiniteSet(*[i[0] for i in real_roots(x, multiple=False)])
>>> sol = lambda n, d: list(rr(n) - rr(d))
>>> go = lambda eq: sol(*eq.rewrite(Add).as_numer_denom())
Now we try this out on your original expression:
>>> eq = Eq(32/(x + 1)**60 + (1 - 1/(x + 1)**60)/x, 41.81)
>>> fsol = go(eq) # very slow
>>> [i.n(3) for i in fsol]
[-3.33, -2.56, -1.44, -0.568, -0.228, 0.0220]
If you check those out by substituting into the original expression (written as an expression) you will find that only the last one is valid
>>> expr = eq.rewrite(Add)
>>> [expr.subs(x, i).n(3) for i in fsol]
[-42.1, -42.2, 4.72e+22, 2.64e+23, 1.97e+8, 1.31e-15]
Now let's replace that Float with a Rational and get solutions:
>>> req = nsimplify(eq, rational=True); req
Eq(32/(x + 1)**60 + (1 - 1/(x + 1)**60)/x, 4181/100)
>>> rsol = go(_) # pretty fast
>>> [i.n(3) for i in rsol]
[-2.00, 0.0220]
We know the 2nd solution is right; let's check the first:
>>> req.subs(x, rsol[0]).rewrite(Add).n(3)
-0.e-114
So both solutions appear to be valid and you don't get any spurious solutions which (by the way) I wasn't expecting from nsolve.
An exact analytic solution to this is unlikely but you can get numeric solutions e.g.:
In [18]: nsolve(eq, x, -2)
Out[18]: -1.99561339048822
Since this can be transformed into a polynomial you can find all real solutions like:
In [20]: p = Poly(nsimplify(eq).rewrite(Add).as_numer_denom()[0])
In [21]: [r[0].n() for r in p.real_roots(multiple=False)]
Out[21]: [-1.99561339048822, -1.0, 0, 0.0219988833527669]
Using as_numer_denom like this can potentially introduce spurious solutions though so you should check them (e.g. by plotting the function around each root). For example 0 is not actually a root.

Prevent evaluation of exponentials in SymPy

I'm using SymPy as a code generator for functions that contain many exponentials. Hence, it is important for numerical stability that the arguments of the exponentials are not evaluated. I want to prevent this:
>>> import sympy as sp
>>> x, y = sp.symbols('x y')
>>> expr = sp.exp(5.*x - 10.)
>>> print(expr)
4.53999297624849e-5*exp(5.0*x)
As it can lead to numerically inaccurate results.
I can prevent the evaluation of the exponentials as follows:
>>> expr = sp.exp(5.*x - 10., evaluate=False)
>>> print(expr)
exp(5.0*x - 10.0)
However, when I perform operations like a substitution or differentiation on the expression, the exponential is evaluated again:
>>> expr = sp.exp(5.*x - 10., evaluate=False)
>>> expr.subs(x, y)
4.53999297624849e-5*exp(5.0*y)
>>> expr.diff(x, 1)
5.0*(4.53999297624849e-5*exp(5.0*x))
What is the correct way in SymPy to prevent the evaluation of the exponential under such operations?
The most obvious point is that you are using float for integer values e.g.:
In [8]: exp(5*x - 10)
Out[8]:
5⋅x - 10
ℯ
In [9]: exp(5.*x - 10.)
Out[9]:
5.0⋅x
4.53999297624849e-5⋅ℯ
Maybe in your real problem you want to work with non-integers. Again rationals should be used for exact calculations:
In [10]: exp(Rational(1, 3)*x - S(3)/2)
Out[10]:
x 3
─ - ─
3 2
ℯ
Perhaps your input numbers are not really rational and you have them as Python floats but you want to keep them from evaluating. You can use symbols and then only substitute for them when evaluating:
In [13]: exp(a*x + b).evalf(subs={a:5.0, b:10.})
Out[13]:
a⋅x + b
ℯ
In [14]: exp(a*x + b).evalf(subs={x:1, a:5.0, b:10.})
Out[14]: 3269017.37247211
In [15]: exp(a*x + b).subs({a:5.0, b:10.})
Out[15]:
5.0⋅x
22026.4657948067⋅ℯ
If all of these seems awkward and you really do just want to stuff floats in and prevent evaluation then you can use UnevaluatedExpr:
In [21]: e = exp(UnevaluatedExpr(5.0)*x - UnevaluatedExpr(10.))
In [22]: e
Out[22]:
x⋅5.0 - 10.0
ℯ
In [23]: e.doit() # doit triggers evaluation
Out[23]:
5.0⋅x
4.53999297624849e-5⋅ℯ

Sympy "global" substitution

I have a number of symbolic expressions in sympy, and I may come to realize that one of the coefficients is zero. I would think, perhaps because I am used to mathematica, that the following makes sense:
from sympy import Symbol
x = Symbol('x')
y = Symbol('y')
f = x + y
x = 0
f
Surprisingly, what is returned is x + y. Is there any way, aside from explicitly calling "subs" on every equation, for f to return just y?
I think subs is the only way to do this. It looks like a sympy expression is something unto itself. It does not reference the pieces that made it up. That is f only has the expression x+y, but doesn't know it has any link back to the python objects x and y. Consider the code below:
from sympy import Symbol
x = Symbol('x')
y = Symbol('y')
z = Symbol('z')
f1 = x + y
f2 = z + f1
f1 = f1.subs(x,0)
print(f1)
print(f2)
The output from this is
y
x + y + z
So even though f1 has changed f2 hasn't. To my knowledge subs is the only way to get done what you want.
I don't think there is a way to do that automatically (or at least no without modifying SymPy).
The following question from SymPy's FAQ explains why:
Why doesn't changing one variable change another that depends it?
The short answer is "because it doesn't depend on it." :-) Even though
you are working with equations, you are still working with Python
objects. The equations you are typing use the values present at the
time of creation to "fill in" values, just like regular python
definitions. They are not altered by changes made afterwards. Consider
the following:
>>> a = Symbol('a') # create an object with name 'a' for variable a to point to
>>> b = a + 1; b # create another object that refers to what 'a' refers to
a + 1
>>> a = 4; a # a now points to the literal integer 4, not Symbol('a')
4
>>> b # but b is still pointing at Symbol('a')
a + 1
Changing quantity a does not change b; you are not working with a set
of simultaneous equations. It might be helpful to remember that the
string that gets printed when you print a variable refering to a sympy
object is the string that was give to it when it was created; that
string does not have to be the same as the variable that you assign it
to:
>>> r, t, d = symbols('rate time short_life')
>>> d = r*t; d
rate*time
>>> r=80; t=2; d # we haven't changed d, only r and t
rate*time
>>> d=r*t; d # now d is using the current values of r and t
160
Maybe this is not what you're looking for (as it was already explained by others), but this is my solution to substitute several values at once.
def GlobalSubs(exprNames, varNames, values=[]):
if ( len(values) == 0 ): # Get the values from the
for varName in varNames: # variables when not defined
values.append( eval(varName) ) # as argument.
# End for.
# End if.
for exprName in exprNames: # Create a temp copy
expr = eval(exprName) # of each expression
for i in range(len(varNames)): # and substitute
expr = expr.subs(varNames[i], values[i]) # each variable.
# End for.
yield expr # Return each expression.
# End for.
It works even for matrices!
>>> x, y, h, k = symbols('x, y, h, k')
>>> A = Matrix([[ x, -h],
... [ h, x]])
>>> B = Matrix([[ y, k],
... [-k, y]])
>>> x = 2; y = 4; h = 1; k = 3
>>> A, B = GlobalSubs(['A', 'B'], ['x', 'h', 'y', 'k'])
>>> A
Matrix([
[2, -1],
[1, 2]])
>>> B
Matrix([
[ 4, 3],
[-3, 4]])
But don't try to make a module with this. It won't work. This will only work when the expressions, the variables and the function are defined into the same file, so everything is global for the function and it can access them.

Categories

Resources