Is there a way to define a function through symbolic derivation? - python

Is there a way to define a function through symbolic derivation? For example,
def f(x):return x**x
def df(x) : return diff(f(x),x)
This code doesn't work, since df(1) would not be possible (diff(f(1),1) doesn't make sense.) But is there a way to take advantage of the result of "diff"? When printing df(x), there is a pretty function form with variable x, so neat.

Don't need Maple or Mathematica. Python has sympy.
import numpy as np
from sympy import symbols, diff, lambdify
x = symbols('x')
def f(x):
return x**x
def df(x):
return diff(f(x))
print(df(x))
This returns the symbolic derivative, x**x*(log(x) + 1). Now, it depends on what you want to do with that. It seems like you wanted to calculate numeric values without necessarily knowing that the derivative is x**x*(log(x) + 1). sympy's recommended way for calculating many numeric values is to use its lambdify.
df_numeric = lambdify(x, df(x), modules=['numpy'])
df_numeric(5)
This gives 8154.4934763565643. You can check it against a regular, manual function:
def df_manual(n):
return n**n*(np.log(n) + 1)
df_manual(5)

Related

Using a matrix multiplication operator in a sympy expression

I am trying to implement a function using a Sympy expression with multiple parameters. For example, I used the following code:
import sympy
a = sympy.symbols("a")
ad = sympy.symbols("ad")
x = sympy.symbols("x")
c = sympy.symbols("c")
f = (ad*a)*c + x
func = sympy.lambdify((a,ad,x,c),f)
And what I would like to evaluate is the following:
func(M_A,M_B,0,1)
When I use two matrices M_A and M_B, the function performs just an element-wise multiplication, but I need it to be a matrix multiplication for the objects a and ad. I do know that it is possible to do so when I define the variables using MatrixSymbol instead of symbols, but this is not possible in my case as I have implemented a scenario which uses diagonal matrices where element-wise or matrix multiplication would not make a difference. Further, it is also possible to do something like this with normal symbols
x_vars = [symbols("x"+i) for i in range(1,4)]
trans_mat = np.random.random([3,3])
y_vars = trans_mat.dot(x_vars)
which just does not seem to work when I am using MatrixSymbol.
So, I was thinking if I could just compute the expression and perform all the manipulations using the regular symbols and at the end replace all the multiplication operators with numpy.matmul. Please let me know if this is possible somehow, or any other suggestion which can help is also welcome.
Thanks!
Doing help(func) we can see the code produced by lambdify:
Help on function _lambdifygenerated:
_lambdifygenerated(a, ad, x, c)
Created with lambdify. Signature:
func(a, ad, x, c)
Expression:
a*ad*c + x
Source code:
def _lambdifygenerated(a, ad, x, c):
return a*ad*c + x
That's a straightforward translation to python/numpy. Sounds like you want (a#ad)*c+x.

Getting coefficient of term in sympy

I need to find the coefficient of a term in a rather long, nasty expansion. I have a polynomial, say f(x) = (x+x^2)/2 and then a function that is defined recursively: g_k(x,y) = y*f(g_{k-1}(x,y)) with g_0(x,y)=yx.
I want to know, say, the coefficient of x^2y^4 in g_10(x,y)
I've coded this up as
import sympy
x, y = sympy.symbols('x y')
def f(x):
return (x+x**2)/2
def g(x,y,k):
if k==0:
return y*x
else:
return y*f(g(x,y,k-1))
fxn = g(x,y,2)
fxn.expand().coeff(x**2).coeff(y**4)
> 1/4
So far so good.
But now I want to find a coefficient for k = 10. Now fxn = g(x,y,10) and then fxn.expand() is very slow. Obviously there are a lot of steps going on, so it's not a surprise. But my knowledge of sympy is rudimentary - I've only started using it specifically because I need to be able to find these coefficients. I could imagine that there may be a way to get sympy to recognize that everything is a polynomial and so it can more quickly find a particular coefficient, but I haven't been able to find examples doing that.
Is there another approach through sympy to get this coefficient, or anything I can do to speed it up?
I assume you are only interested in the coefficients given and not the whole polynomial g(x,y,10). So you can redefine your function g to get rid of higher orders in every step of the recursion. This will significantly speed up your calculation.
def g(x,y,k):
if k==0:
return y*x
else:
temp = y*f(g(x,y,k-1)) + sympy.O(y**5) + sympy.O(x**3)
return temp.expand().removeO()
Works as follows: First everything of the order O(y**5), O(x**3) (and higher) will be grouped and then discarded. Keep in mind you loose lots of information!
Also have a look here: Sympy: Drop higher order terms in polynomial

Function that returns an equation

I am writing a script to calculate the definite integral of an equation. I'm writing a helper function that would take in coefficients as parameters and return a function of x.
def eqn(x, k, c, a):
return ((k*x + c**(1-a))
Next, I define a function that calculates the definite integral, using quad imported from scipy:
from scipy.integrate import quad
def integral(eqn, c_i, y_i):
integral_i, integral_err = quad(eqn, c_i, y_i)
print integral_i
Then I call the function by passing in parameters
k = calc_k(7511675,1282474,0,38,2)
eqn = carbon_path_eqn(x, k, 7511675, 2)
carbon_path_def_int(eqn,0,38)
However, I get an error saying that 'name x is not defined'. I understand that x isn't defined globally, but I'm wondering how I can write a helper function, that takes in parameters, and still returns a function of x that can be used in quad?
Thank you!
PS -
#bpachev, this is a follow up from the other post
The mistake here is that the function 'eqn' does not return a function, it returns the value of a function at some point x, given parameters k,c,a.
quad should be passed a function (in your case, eqn) where the first argument (in your case, x) is assumed to be the variable over which the function is integrated. You also need to pass quad a tuple of the remaining parameters (in your case (k,c,a)) and two limits (in your case, c_i,y_i). In other words, call quad like this:
quad(eqn,c_i,y_i,args=(k,c,a))
This is all explained in the scipy documentation http://docs.scipy.org/doc/scipy/reference/generated/scipy.integrate.quad.html.
This is not what you asked. However, as someone else has mentioned, sympy would probably make your life much easier. For instance, suppose you need to be able to evaluate integrals of functions of x such as f in the code below, where a and b are arbitrary constants. Here's how you can use sympy to do that.
Define the function, integrate it with respect to x and save the result, then evaluate the result for values of a, b and x.

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.

Is there a vectorized way to calculate the gradient in sympy?

How does one calculate the (symbolic) gradient of a multivariate function in sympy?
Obviously I could calculate separately the derivative for each variable, but is there a vectorized operation that does this?
For example
m=sympy.Matrix(sympy.symbols('a b c d'))
Now for i=0..3 I can do:
sympy.diff(np.sum(m*m.T),m[i])
which will work, but I rather do something like:
sympy.diff(np.sum(m*m.T),m)
Which does not work ("AttributeError: ImmutableMatrix has no attribute _diff_wrt").
Just use a list comprehension over m:
[sympy.diff(sum(m*m.T), i) for i in m]
Also, don't use np.sum unless you are working with numeric values. The builtin sum is better.
Here is an alternative to #asmeurer. I prefer this way because it returns a SymPy object instead of a Python list.
def gradient(scalar_function, variables):
matrix_scalar_function = Matrix([scalar_function])
return matrix_scalar_function.jacobian(variables)
mf = sum(m*m.T)
gradient(mf, m)

Categories

Resources