Sympy Function substitution - python

I use things like
x = Function('x',real=True)(t)
which gives x(t) as function variables in all my calculations. I can now differentiate with respect to time, solve (not nonlinsolve or solveset, because they cannot subs these functions) and so on.
However, I want to export my results to MATLAB. So I need the (t) gone. I can fill a dict with d = {x(t):x}, but I think there is a more elegant way to do this for all Functions, like y(t),z(t),...
My desired result is for the input
expr = x(t)-y(t)
output = x-y
where x and y can be "Function" as known to Sympy, but there shall not be (t) in the output. And I do want to avoid introducing all my variables again without the dependence on time!

Here is a loop that walks through the expression tree and replaces any function whose argument is t by a symbol matching the name of that function. The substitutions are made in another copy of the expression, to avoid modifying the expression we are walking through.
Example: if expr is x(t) - y(t), then expr1 is x - y.
expr1 = expr
for f in preorder_traversal(expr):
if f.args == (t,):
expr1 = expr1.subs(f, Symbol(str(f.func)))
print(expr1)

Related

Finding coefficients of x in a string

I am trying to make an algebra solver, so I have to find the coefficients of x in an expression. Currently this is the code I have to take an input:
equation = input("Enter equation: ")
LHS, RHS = equation.split("=")[0], equation.split("=")[1]
##Remove whitespaces
LHS, RHS = LHS.replace(" ",""), RHS.replace(" ","")
Now I want to get the coefficients of x in the expressions LHS and RHS, how can I do this?
And also, the coefficients may not be just numbers but they may be of the form (2*3+4)x. I think this can be done using the eval() function but I don't know how to apply it.
Note: I want to do this with plain python, without using any modules which are not built-in.
In case it's only one variable and thus only one coefficient you could divide the respective variable/site by x.
Taking your example:
(2*3+4)x
dividing by x gives us the coefficient:
(2*3.4)
If that's not the approach you want to take, you could also convert the expression into a string and exclude "x", as such:
coefficientLHS = LHS.replace("x", "")
However, you should use this cautious as you might have algebra expressions in front of your variable, so regex implementation would be advisable.
Another approach would be to use the module Poly:
x = symbols("x")
coefLHS = Poly(LHS, x)
coefLHS.coeffs()

After substituting Functions in sympy, the evaluation fails to do straightforward derivatives

Last Day I have been bothering with this problem. At first I made a question but after comment of needing a simplified example, I deleted the question and I finally found the "source" of the problem.
I fail to evaluate expressions AFTER substituting Functions by expressions: Following example will show you what I mean:
xx = sy.Symbol('x',real=True)
yy = sy.Symbol('y',real=True)
FuncT = sy.Function('F')
TestExp= sy.cos(sy.diff(FuncT(xx,yy),xx)+xx+yy*xx+yy)
print(TestExp.subs({FuncT(xx,yy):xx})).subs({xx:1,yy:0.1})
which results
How can it replace dx/dx = 1 ?
Just doit:
>>> TestExp.subs({FuncT(xx,yy):xx}).subs({xx:1,yy:0.1}).doit()
-0.588501117255346
How to know to use doit?
When I print (not pprint) the expressions I see
cos(Subs(Derivative(x, x), x, 1) + 1.2)
I don't want Subs there but I don't know much about Subs so I
ask for help and read the following:
>>> help(Subs)
...
There's no automatic expansion - use the method .doit() to effect all
possible substitutions of the object and also of objects inside the
expression.
...

Replacing numbers with parameters in sympy

I have some SymPy expressions of the form: 2*x1**2+7*cos(8*x2)+2*Pi (mine are longer and more complicated, but this should be enough for my question). How can I turn all the numbers appearing in this expression into parameters, something like this: a*x1**2+b*cos(c*x2)+d. Basically, I have a program which is able to give me an approximate function able to fit some data, but the parameters are integers or some well known numbers, such as pi or e (this is the first expression I provided). Then, I want to take that expression and fine tune these numbers (using gradient descent), such that to obtain the actual parameters (one can assume that the functional form is right, just the parameters need to be adjusted). For example, in the end, the right equation could be: 2.87*x1**2+6.95*cos(8.05*x2)+6.27. Is there a way to do this? Thank you!
It's a little tricky because you say "all numbers" but you are ignoring exponents. In your example you are only replacing numerical factors in a term with new symbols. To do that (and to get you on your way with a possible solution) try using replace, telling it you are looking for a Mul and then telling it what you want to do with the Mul when you have it:
from sympy import *
from sympy.abc import x,y
eq=2*x**2+7*cos(8*y)+2*pi
def nfact2dum(m):
assert m.is_Mul
nonnum = sift(m.args, lambda i:i.is_number, binary=True)[1]
return Mul(*([Dummy()] + nonnum))
deq = eq.replace(
lambda x:x.is_Mul,
lambda x: nfact2dum(x))
print(
deq.subs(list(zip(deq.atoms(Dummy),numbered_symbols('c')))))
output: c0*x**2 + c2*cos(c1*y) + c3

Integrating a function with Python (sympy, quad) where the result is another function I want to plot

I want to integrate a function using python, where the output is a new function rather than a numerical value. For example, I have the equation (from Arnett 1982 -- analytical description of a supernova):
def A(z,tm,tni):
y=tm/(2*tni)
tm=8.8 # diffusion parameter
tni=8.77 # efolding time of Ni56
return 2*z*np.exp((-2*z*y)+(z**2))
I want to then find the integral of A, and then plot the results. First, I naively tried scipy.quad:
def Arnett(t,z,tm,tni,tco,Mni,Eni,Eco):
x=t/tm
Eni=3.90e+10 # Heating from Ni56 decay
Eco=6.78e+09 # Heating from Co56 decay
tni=8.77 # efolding time of Ni56
tco=111.3 # efolding time of Co56
tm=8.8 # diffusion parameter
f=integrate.quad(A(z,tm,tni),0,x) #integral of A
h=integrate.quad(B(z,tm,tni,tco),0,x) #integral of B
g=np.exp((-(x/tm)**2))
return Mni*g*((Eni-Eco)*f+Eco*h)
Where B is also a pre-defined function (not presented here). Both A and B are functions of z, however the final equation is a function of time, t. (I believe that it is herein I am causing my code to fail.)
The integrals of A and B run from zero to x, where x is a function of time t. Attempting to run the code as it stands gives me an error: "ValueError: The truth value of an array with more than one element is ambiguous. Use a.any() or a.all()".
So after a short search I thought that maybe sympy would be the way to go. However I am failing with this as well.
I wonder if anyone has a helpful suggestion how to complete this task please?
Many thanks,
Zach
You can integrate A analytically. Assuming I'm not missing something silly due to being up way too late, does the following help?
import sympy as sy
sys.displayhook = sy.pprint
A, y, z, tm, t, tni = sy.symbols('A, y, z, tm, t, tni')
A = 2*z*sy.exp(-2*z*y + z**2)
expr = sy.integrate(A, (z,0,t)) # patience - this takes a while
expr
# check:
(sy.diff(expr,t).simplify() - A.replace(z,t)).simplify()
# thus, the result:
expr.replace(y,tm/(2*tni)).replace(t,t/tm)
The last line yields the integral of your A function in analytic form, though it does require evaluating the imaginary error function (which you can do with scipy.special.erfi()).
I think what you are looking for are lambda expression (if i understood correctly what you said.. see here for extra information and some examples on lambda functions).
What they allow you to do is define an anonymous function in A and return it so that you get your B function, should work something like this:
def A(parameters):
return lambda x: x * parameters # for simplicity i applied a multiplication
# but you can apply anything you want to x
B = A(args)
x = B(2)
Hope I could provide you with a decent response!
I think the error you get comes from an incorrect call to scipy.integrate.quad:
The first argument needs to be just the function name, integration is then performed over the first variable of this function. The values of the other variables can be passed to the function via the args keyword.
The output of scipy.integrate.quad contains not only the value of the integral, but also an error estimate. So a tuple of 2 values is returned!
In the end the following function should work:
def Arnett(t, z, Mni, tm=8.8, tni=8.77, tco=111.3, Eni=3.90e+10,
Eco=6.78e+09):
x=t/tm
f,err=integrate.quad(A,0,x,args=(tm,tni)) #integral of A
h,err=integrate.quad(B,0,x,args=(tm,tni,tco)) #integral of B
g=np.exp((-(x/tm)**2))
return Mni*g*((Eni-Eco)*f+Eco*h)
But an even better solution would probably be integrating A and B analytically and then evaluating the expression as murison suggested.

Optimizing Python polynomial evaluation

I have a function which evaluates terms of a polynomial in several variables. The inputs are lists of powers of each variable. For example, for two variables and 2nd order it looks like this,
def f(x,y):
return [1, x[1], y[1], x[1]*y[1], x[2], y[2]]
x = [2**0, 2**1, 2**2]
y = [3**0, 3**1, 3**2]
>>> f(x,y)
[1,2,3,6,4,9]
In reality the function is higher order and has many variables so on average there are a few thousand terms (in fact, I create the function at run time with an eval statement, but that's not important). The function is on an inner most loop and is currently a speed bottleneck. The profiler tells me I spend most of the time in __times__.
Short of creating a C extension module, can anyone see any room for optimization?
Edit: The example above is trying to evaulate 1 + x + y + xy + x^2 + y^2 with x = 2and y = 3, except without adding them, just putting each term in a list.
Adding them is fine (with some coefficients A, B, ...) i.e. all I'm trying to do is compute:
A + B*x + C*y + D*x*y + E*x^2 + F*y^2.
I'm not sure from which version, but numpy should have a polyval2d(x,y,c) function into the polynomial module, that will perfectly apply to your example.
You seemed interested in expanding your example to a much higher dimension.
In the same module there's a polyval3d(x,y,z,c), if that's not enought I'd suggest (as I guess you're already doing) to look at the source code. It shouldn't be too hard to implement what best suits your needs, and you can always ask here on SO :)
The function is on an inner most loop and is currently a speed
bottleneck.
You could try to get rid of the loop altogether, by using NumPy and replacing your variables with arrays of higher dimension.

Categories

Resources