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.
Related
I am having trouble pulling out values out of a function that I am trying to optimize. The code looks similar to what follows. I want to minimize c by changing x through scipy.optimize.minimize, but am also trying to pull a and b out of the function as well. What is the best way to do this?
def function(x, inputs)
a = math
b = math
c = math
return(c)
You should be able to just compute a and b outside of the function. It doesn't need to be part of the function if the result is not optimized. Just use x, inputs, and c to compute a and b outside of the function.
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.
Why does the following code return a ValueError?
from scipy.optimize import fsolve
import numpy as np
def f(p,a=0):
x,y = p
return (np.dot(x,y)-a,np.outer(x,y)-np.ones((3,3)),x+y-np.array([1,2,3]))
x,y = fsolve(f,(np.ones(3),np.ones(3)),9)
ValueError: setting an array element with a sequence.
The basic problem here is that your function f does not satisfy the criteria required for fsolve to work. These criteria are described in the documentation - although arguably not very clearly.
The particular things that you need to be aware of are:
the input to the function that will be solved for must be an n-dimensional vector (referred to in the docs as ndarray), such that the value of x you want is the solution to f(x, *args) = 0.
the output of f must be the same shape as the x input to f.
Currently, your function takes a 2 member tuple of 1x3-arrays (in p) and a fixed scalar offset (in a). It returns a 3 member tuple of types (scalar,3x3 array, 1x3 array)
As you can see, neither condition 1 nor 2 is met.
It is hard to advise you on exactly how to fix this without being exactly sure of the equation you are trying to solve. It seems you are trying to solve some particular equation f(x,y,a) = 0 for x and y with x0 = (1,1,1) and y0 = (1,1,1) and a = 9 as a fixed value. You might be able to do this by passing in x and y concatenated (e.g. pass in p0 = (1,1,1,1,1,1) and in the function use x=p[:3] and y = p[3:] but then you must modify your function to output x and y concatenated into a 6-dimensional vector similarly. This depends on the exact function your are solving for and I can't work this out from the output of your existing f (i.e based on a dot product, outer product and sum based tuple).
Note that arguments that you don't pass in the vector (e.g. a in your case) will be treated as fixed values and won't be varied as part of the optimisation or returned as part of any solution.
Note for those who like the full story...
As the docs say:
fsolve is a wrapper around MINPACK’s hybrd and hybrj algorithms.
If we look at the MINPACK hybrd documentation, the conditions for the input and output vectors are more clearly stated. See the relevant bits below (I've cut some stuff out for clarity - indicated with ... - and added the comment to show that the input and output must be the same shape - indicated with <--)
1 Purpose.
The purpose of HYBRD is to find a zero of a system of N non-
linear functions in N variables by a modification of the Powell
hybrid method. The user must provide a subroutine which calcu-
lates the functions. The Jacobian is then calculated by a for-
ward-difference approximation.
2 Subroutine and type statements.
SUBROUTINE HYBRD(FCN,N,X, ...
...
FCN is the name of the user-supplied subroutine which calculates
the functions. FCN must be declared in an EXTERNAL statement
in the user calling program, and should be written as follows.
SUBROUTINE FCN(N,X,FVEC,IFLAG)
INTEGER N,IFLAG
DOUBLE PRECISION X(N),FVEC(N) <-- input X is an array length N, so is output FVEC
----------
CALCULATE THE FUNCTIONS AT X AND
RETURN THIS VECTOR IN FVEC.
----------
RETURN
END
N is a positive integer input variable set to the number of
functions and variables.
X is an array of length N. On input X must contain an initial
estimate of the solution vector. On output X contains the
final estimate of the solution vector.
Apologies for the simplicity of this question.
I would like to implement an equation in Python. In this equation, K_0 is the zeroth-order modifed Bessel function.
What is the best way of implementing K_0 in Python?
No need to implement it; it's included. See the docs for the scipy.special module, in particular the optimized common ones here:
>>> import scipy.special
>>> print scipy.special.k0.__doc__
k0(x[, out])
y=k0(x) returns the modified Bessel function of the second kind (sometimes called the third kind) of
order 0 at x.
>>> scipy.special.k0(1)
0.42102443824070823
or more generally:
>>> print scipy.special.kn.__doc__
kn(x1, x2[, out])
y=kn(n,x) returns the modified Bessel function of the second kind (sometimes called the third kind) for
integer order n at x.
>>> scipy.special.kn(0, 1)
0.42102443824070834
I have a fairly complex function f(x) that I want to optimize and I am using the fmin_bfgs function from the scipy.optimize module from Scipy. It forces me to give the function to minimize and the function of the gradient f'(x) separately, which is a pity because some of the computations for the gradient can be done when evaluating the function f(x).
Is there a way of combining both functions? I was considering saving the intermediate values required for both functions, but I don't know if the fmin_bfgs function guarantees that f(x) is evaluated before than f'(x).
Thank you
The scipy.optimize.minimize methods has a parameter called "jac". If set to True, minimize will expect the callable f(x) to both return the function value and it's derivatives.
You could use memoization to cache the intermediate values. Regardless of which function is called first, the second will be able to take advantage of the memoized value.
cache={}
def expensive_calc(x):
# If x is a numpy array, you need to convert x into something hashable so it
# can be used as a key in cache.
key=tuple(x)
try:
return cache[key]
except KeyError:
# do expensive calc for result
cache[key] = result
return result
def func(x):
y=expensive_calc(x)
return something_based_on_y
def func_der(x):
y=expensive_calc(x)
return something_else_based_on_y
def optimize_fmin_bfgs():
xopt = fmin_bfgs(func, x_guess, fprime=func_der)