Problem using lambda expression to be integrated using quad from mpmath - python

I am trying to integrate an oscillatory expression with mpmath.quad. The integral only has one variable but I need to evaluate it in a 2D space. I need the resulting function from the integral along kx to be evaluated for a group of coordinated in x and y that defined a plane. That's why I evaluate integral(x,y) as a test in a single point of that 2D array.
This is the code I am using:
import numpy as np
from numpy.lib.scimath import sqrt
from mpmath import *
import matplotlib.pyplot as plt
mp.dps = 15
f = 8500
rho = 1.225
c0 = 343
Omega = 2*np.pi*f
k = Omega/c0
Z = -426
integrandReal = lambda kx, x, y: np.real(((2*sqrt(k**2 - kx**2)*Z)/(sqrt(k**2 - kx**2)*Z + Omega*rho))*((np.exp(1j*(kx*x + sqrt(k**2 - kx**2)*y)))/(sqrt(k**2 - kx**2))))
integrandImag = lambda kx, x, y: np.imag(((2*sqrt(k**2 - kx**2)*Z)/(sqrt(k**2 - kx**2)*Z + Omega*rho))*((np.exp(1j*(kx*x + sqrt(k**2 - kx**2)*y)))/(sqrt(k**2 - kx**2))))
integral = lambda x, y: quad(integrandReal, [-100*k, 100*k], maxdegree=10)[0] + 1j*quad(integrandImag, [-100*k, 100*k], maxdegree=10)[0]
G = integral(0.1,0.1)
I am getting the following errors when I try to evaluate the integral in an arbitrary point:
Traceback (most recent call last):
File "<ipython-input-87-901e6e5dc630>", line 1, in <module>
integral(1,1)
File "/d/dfg/Documents/ImpedanceModel/GreenImpedance.py", line 55, in <lambda>
integral = lambda x, y: quad(integrandReal, [-100*k, 100*k], maxdegree=10)[0] + 1j*quad(integrandImag, [-100*k, 100*k], maxdegree=10)[0]
File "/opt/tools/anaconda/2020.11/lib/python3.8/site-packages/mpmath/calculus/quadrature.py", line 742, in quad
v, err = rule.summation(f, points[0], prec, epsilon, m, verbose)
File "/opt/tools/anaconda/2020.11/lib/python3.8/site-packages/mpmath/calculus/quadrature.py", line 232, in summation
results.append(self.sum_next(f, nodes, degree, prec, results, verbose))
File "/opt/tools/anaconda/2020.11/lib/python3.8/site-packages/mpmath/calculus/quadrature.py", line 304, in sum_next
S += self.ctx.fdot((w,f(x)) for (x,w) in nodes)
File "/opt/tools/anaconda/2020.11/lib/python3.8/site-packages/mpmath/ctx_mp_python.py", line 944, in fdot
for a, b in A:
File "/opt/tools/anaconda/2020.11/lib/python3.8/site-packages/mpmath/calculus/quadrature.py", line 304, in <genexpr>
S += self.ctx.fdot((w,f(x)) for (x,w) in nodes)
TypeError: <lambda>() missing 2 required positional arguments: 'x' and 'y'
I thought the error was because I wasn't defining the arguments in the integration as it has to be done with scipy.integrate.quad (as you can see in one of my last posts) but with mpmath I don't know what is generating this error.
Thanks in advance for any help!

You need to pass x and y to your lambdas (integrandReal and integrandImag). Also, there is another issue with your example as you try to subscript the mpc object returned by quad. I removed it to get a result, it shows you how to pass x and y through the calls, but I cannot assure you the numerical result is correct:
integral = lambda x, y: quad(integrandReal, [-100*k, 100*k], [x], [y], maxdegree=10) + 1j*quad(integrandImag, [-100*k, 100*k], [x], [y], maxdegree=10)
G = integral(0.1,0.1)
# Returns: mpc(real='0.0', imag='0.0')

Related

Cant plot an equation properly in Python

I am trying to plot a 3rd rank polynomial that I calculated through Lagrance with a given set of x,y.(x=P_Maz , y=H_Maz).I can't really find what I am doing wrong.Even some plots I tried would show me wrong graph(Because x=0 wouldnt give y=25).
The equation is as follows:
3 2
3.395e-06 x - 0.001528 x + 1.976 x + 25
The part of my function and plot is this one:
P_Maz = np.array([120, 180, 270, 300]) # x
H_Maz = np.array([246, 351, 514, 572]) # y
def Lagrange(Lx, Ly):
x = symbols('x')
y = 0
for k in range(len(Lx)):
p = 1
for j in range(len(Lx)):
if j != k:
p = p * ((x - Lx[j]) / (Lx[k] - Lx[j]))
y += p * Ly[k]
return y
poly1=simplify(Lagrange(P_Maz,H_Maz))
plot(P_Maz,poly1(H_Maz))
grid()
show()
I get the following error:
Traceback (most recent call last):
File "C:\Users\Egw\Desktop\Analysh\Askhsh1\askhsh5.py", line 36, in <module>
plot(P_Maz,poly1(H_Maz))
TypeError: 'Add' object is not callable
Full code is as follows:
import numpy as np
from matplotlib.pyplot import plot, show, grid
import numpy.polynomial.polynomial as poly
import matplotlib.pyplot as plt
from scipy.interpolate import lagrange
from sympy import symbols, Eq,simplify
P_Maz = np.array([120, 180, 270, 300]) # x
H_Maz = np.array([246, 351, 514, 572]) # y
P_Lig = np.array([150, 215, 285, 300]) # x
H_Lig = np.array([307, 427, 563, 594]) # y
def Lagrange(Lx, Ly):
x = symbols('x')
y = 0
for k in range(len(Lx)):
p = 1
for j in range(len(Lx)):
if j != k:
p = p * ((x - Lx[j]) / (Lx[k] - Lx[j]))
y += p * Ly[k]
return y
poly1=simplify(Lagrange(P_Maz,H_Maz))
poly2=simplify(Lagrange(P_Lig,H_Lig))
print(Lagrange(P_Maz, H_Maz)) #Morfh Klasmatwn
print(poly1) #Telikh eksiswsh
print(lagrange(P_Maz,H_Maz)) #Telikh eksiswsh me built in method
print(Lagrange(P_Lig, H_Lig)) #Morfh Klasmatwn
print(poly2) #Telikh eksiswsh
print(lagrange(P_Lig,H_Lig)) #Telikh eksiswsh me built in method
plot(P_Maz,poly1(P_Maz))
grid()
show()
With simplify, you get an expression, but then you need to evaluate that expression for all the desired values.
You can do that through lambdify:
from sympy import lambdify
f = lambdify(x, simplify(Lagrange(P_Maz,H_Maz)), "numpy")
# above, just copied your simplify call with your variables and used in the lambdify
plot(P_Maz, f(P_Maz)) # use the created lambdify function, f, not the unevaluated expression
Mind the warning in the documentation (docs): "lambdify uses eval. Don’t use it on unsanitized input."

How to solve this time-dependent PDE in Python?

Is there a way to numerically solve the following PDE in Python?
The second term on the RHS has a derivative with respect to time as well as space.
I tried using Py-PDE package in Python, it solves only the form dy(x,t)/dt = f(y(x,t)) so I tried to use a root finding algorithm similar to scipy fzero to get the solution to dy(x,t)/dt - f(y(x,t),dy(x,t)/dt) = 0 (solving for dy(x,t)/dt).
class nonlinearPDE(pde.PDEBase):
def __init__(self, bc={"derivative":0}):
self.bc = bc #boundary conditions for operators
def _make_pde_rhs_numba(self, state):
"""numba-compiled implementation of the PDE"""
laplace = state.grid.make_operator("laplace", bc=self.bc)
def findroot(f, df, x0, nmax):
"""Newton–Raphson method"""
for i in range(nmax):
x0 = x0 - f(x0)/df(x0)
return x0
#jit
def pde_rhs(y, t):
func = lambda dydt : dydt - a*laplace(y) - b*laplace(dydt)
dydt = findroot(func, lambda x : 1, 0, 1)
return dydt
return pde_rhs
However, when the program tries to solve the PDE it throws an error:
File "...\anaconda3\lib\site-packages\pde\solvers\controller.py", line 191, in run
t = stepper(state, t, t_break)
File "...\anaconda3\lib\site-packages\pde\solvers\scipy.py", line 82, in stepper
sol = integrate.solve_ivp(
File "...\anaconda3\lib\site-packages\scipy\integrate\_ivp\ivp.py", line 542, in solve_ivp
solver = method(fun, t0, y0, tf, vectorized=vectorized, **options)
File "...\anaconda3\lib\site-packages\scipy\integrate\_ivp\rk.py", line 94, in __init__
self.f = self.fun(self.t, self.y)
File "...\anaconda3\lib\site-packages\scipy\integrate\_ivp\base.py", line 138, in fun
return self.fun_single(t, y)
File "...\anaconda3\lib\site-packages\scipy\integrate\_ivp\base.py", line 20, in fun_wrapped
return np.asarray(fun(t, y), dtype=dtype)
File "...\anaconda3\lib\site-packages\pde\solvers\scipy.py", line 74, in rhs_helper
return rhs(state_flat.reshape(shape), t).flat # type: ignore
File "...\anaconda3\lib\site-packages\numba\core\dispatcher.py", line 420, in _compile_for_args
error_rewrite(e, 'typing')
File "...\anaconda3\lib\site-packages\numba\core\dispatcher.py", line 361, in error_rewrite
raise e.with_traceback(None)
TypingError: Cannot capture the non-constant value associated with variable 'y' in a function that will escape.
Since no one has posted an answer yet, I managed to get a minimal working example by using scipy odeint with a method of lines to solve the PDE, that is, by discretizing the Laplace operator, and then wrapping the differential equation inside fsolve to get dydt:
import numpy as np
from scipy.integrate import odeint
from scipy.optimize import fsolve
a=1
b=1
t = np.linspace(0,1,100)
y0 = np.asarray([1,0.5,0]) #starting values
N=len(y0)
def laplace(y):
""" Laplace operator in cartesian coordinate system """
d2y_0 = [2*(y[1] - y[0])] #at i=0
d2y_N = [2*(y[-2] - y[-1])] #at i=N
d2y_i = [y[i+2] - 2*y[i+1] + y[i] for i in range(N-2)] #elsewhere
return np.asarray(d2y_0 + d2y_i + d2y_N)
def rhs(y, dydt, t):
""" RHS of PDE including dydt term """
return a*laplace(y) + b*laplace(dydt)
def pde(y, t):
""" Function that solves for dydt using root finding """
return fsolve(lambda dydt : dydt - rhs(y, dydt, t),np.zeros(N))
#calculate
sol = odeint(pde, y0, t)
#plot odeint with fsolve
import matplotlib.pyplot as plt
plt.figure()
plt.plot(t,sol)
plt.grid(ls='--')
plt.xlabel('$t$')
plt.ylabel('$y_i$')
plt.title('odeint with fsolve')
plt.legend(["$i=$"+str(i) for i in range(N)])
#plot theoretical
u10=y0[0]
u20=y0[1]
u30=y0[2]
E1=np.exp(-2*a*t/(1+2*b)) #1st exponential term
E2=np.exp(-4*a*t/(1+4*b)) #2nd exponential term
u1 = 1/4*((1+2*E1+E2)*u10 - 2*(E2-1)*u20 + (1-2*E1+E2)*u30)
u2 = 1/4*((1-E2)*u10 + 2*(E2+1)*u20 + (1-E2)*u30)
u3 = 1/4*((1-2*E1+E2)*u10 - 2*(E2-1)*u20 + (1+2*E1+E2)*u30)
plt.figure()
plt.plot(t,u1,t,u2,t,u3)
plt.grid(ls='--')
plt.xlabel('$t$')
plt.ylabel('$y_i$')
plt.title('Theoretical')
plt.legend(["$i=$"+str(i) for i in range(N)])
Note that the same Laplace discretization method allows us to solve a system of ODEs to get the exact analytical solution with which we verify our numerical calculation (for a N=3 size system):
>>> np.allclose(sol[:,0],u1)
True
>>> np.allclose(sol[:,1],u2)
True
>>> np.allclose(sol[:,2],u3)
True
Seems it works as intended.

Optimizing a function where one of the parameters is an array

I want to optimize a function by varying the parameters where two of the parameters are actually arrays. I've tried to do
...
# initial parameters
params0 = np.array([p1, p2, ... , p_array1, p_array2])
p_min = minimize(myfunc, params0, args)
...
where the pj's are scalars and p_array1 and p_array2 are arrays of the same length, but this gave me an error saying
ValueError: setting an array element with a sequence.
I've also tried passing p_array1 and p_array2 as scalars into myfunc and then create predetermined arrays from those two inside myfunc (e.g. setting p_array1 = p_array1*np.arange(6) and similarly for p_array2), eliminating the error, but I don't want them to be predetermined -- instead I want 'minimize' to figure out what they should be.
Is there any way that I can utilize one of Scipy's optimization functions without getting this error while still keeping p_array1 and p_array2 as arrays and not scalars?
EDIT
Sorry for being very broad but here is my code:
NOTE: 'myfunc' here is actually norm_residual .
import pandas as pd
import numpy as np
def f(yvec, t, a, b, c, d, M, theta):
# the system of ODEs to be solved
x, y = yvec
dydt = [ a*x - b*y**2 + 1, -c*x - d*x*y + np.sum(M * np.cos(theta*t)) ]
return dydt
ni = 3 # the number of periodic forcing functions to add to the DE system
M = 0.56*np.random.rand(ni) # the initial amplitudes of forcing functions
theta = np.pi/6*np.arange(ni) # the initial coefficients of the forcing functions
# initialize the parameters
params0 = [0.75, 0.23, 1.0, 0.2, M, theta]
# grabbing the data to be used later
data = pd.read_csv('data.csv')
y_data = data['Y']
N = y_data.shape[0] #20
t = np.linspace(0, N, N) # array of t values to integrate over
yvec0 = [0.3, 0.34] # initial conditions for x and y respectively
def norm_residual(params, *args):
"""
Computes the L^2 norm of the residual of y and the data (y as defined above).
Input: params = array of parameters (scalars or arrays) for the DE system
args = other arguments to pass into the function f or to use
to compute the residual.
Output: err = L^2 error of the solution vector (scalar).
"""
data, yvec0, t = args
a, b, c, d, M, theta = params
sol = odeint(f, yvec0, t, args=(a, b, c, d, M, theta))
x = sol[:, 0]; y = sol[:, 1]
res = data - y
err = np.linalg.norm(res, 2)
return err
from scipy.optimize import minimize
p_min = minimize(norm_residual, params0, args=(y_data, yvec0, t))
print(p_min)
And the traceback
Traceback (most recent call last):
File "model_ex_1.py", line 62, in <module>
p_min = minimize(norm_residual, params0, args=(y_anom, yvec0, t))
File "/usr/lib/python2.7/dist-packages/scipy/optimize/_minimize.py", line 354, in minimize
x0 = np.asarray(x0)
File "/usr/lib/python2.7/dist-packages/numpy/core/numeric.py", line 482, in asarray
return array(a, dtype, copy=False, order=order)
ValueError: setting an array element with a sequence.
You cannot put a list in a numpy array if the other elements are scalars.
>>> import numpy as np
>>> foo_array = np.array([1,2,3,[5,6,7]])
Traceback (most recent call last):
File "<pyshell#1>", line 1, in <module>
foo_array = np.array([1,2,3,[5,6,7]])
ValueError: setting an array element with a sequence.
It would be helpful if you post myfunc
but you can do this -
def foo():
return [p0,p1,p2..pn]
params0 = numpy.array([foo(), p_array1, p_array2])
p_min = minimize(myfunc, params0, args)
OR from Multiple variables in SciPy's optimize.minimize
import scipy.optimize as optimize
def f(params):
# print(params) # <-- you'll see that params is a NumPy array
a, b, c = params # <-- for readability you may wish to assign names to the component variables
return a**2 + b**2 + c**2
initial_guess = [1, 1, 1]
result = optimize.minimize(f, initial_guess)
if result.success:
fitted_params = result.x
print(fitted_params)
else:
raise ValueError(result.message)
I figured it out! The solution that I found to work was to change
params0 = [0.75, 0.23, 1.0, 0.2, M, theta]
in line 6 to
params0 = np.array([ 0.75, 0.23, 1.0, 0.2, *M, *theta], dtype=np.float64)
and in my function definition of my system of ODEs to be solved, instead of having
def f(yvec, t, a, b, c, d, M, theta):
x, y = yvec
dydt = [ a*x - b*y**2 + 1, -c*x - d*x*y + np.sum(M * np.cos(theta*t)) ]
return dydt
I now have
def f(yvec, t, myparams):
x, y = yvec
a, b, c, d = myparams[:4]
ni = (myparams[4:].shape[0])//2 # halved b/c M and theta are of the same shape
M = myparams[4:ni+4]
theta = myparams[ni+4:]
dydt = [ a*x - b*y**2 + 1, -c*x - d*x*y + np.sum(M * np.cos(theta*t)) ]
return dydt
NOTE: I had to add "dtype=np.float64" for 'params0' because I was getting the error
AttributeError: 'numpy.float64' object has no attribute 'cos'
when I did not have it there and it appears that 'cos' does not know how to handle 'ndarray' objects. The workaround can be found here.
Thanks everyone for the suggestions!

curve_fit with ODE of unknown coefficients

I'm trying to solve the equation Iy'' + b|y'|y' + ky = 0 and fit the coefficients to data.
This is the code I have so far (ready to run):
import numpy as np
import matplotlib.pyplot as plt
from scipy.integrate import odeint
import pandas as pd
from scipy.optimize import curve_fit
# Define derivatives of function
def f(y, t, I, b, k):
theta, omega = y
derivs = [omega, -b / I * omega * abs(omega) - k / I * theta]
return derivs
# integrate the function
def yint(t, I, b, k, y0, y1):
y = odeint(f, [y0, y1], t, args=(I, b, k))
return y.ravel()
# define t and y to fit
y0 = [.5, 0]
t = np.arange(0, 3, .01)
y = np.cos(t)*np.e**(-.01*t)
# fit
vals, cov = curve_fit(yint, t, y, p0=[0.002245, 1e-5, 0.2492, y0[0], y[1]])
However, when I run the function, I get the error:
Traceback (most recent call last):
File "---", line 24, in <module>
vals, cov = curve_fit(yint, t, y, p0=[0.002245, 1e-5, 0.2492, y0[0], y[1]])
File "---.py", line 578, in curve_fit
res = leastsq(func, p0, args=args, full_output=1, **kw)
File "---.py", line 371, in leastsq
shape, dtype = _check_func('leastsq', 'func', func, x0, args, n)
File "---.py", line 20, in _check_func
res = atleast_1d(thefunc(*((x0[:numinputs],) + args)))
File "---.py", line 447, in _general_function
return function(xdata, *params) - ydata
ValueError: operands could not be broadcast together with shapes (600,) (300,)
Any thoughts on how to fix this?
The problem is that function yint returns an array of shape (600,) for the argument of shape (300,). Think again about yint: it solves a second-order differential equation by representing it as a system of two first-order equations. So the result of y = odeint(...) has two columns, one for the solution itself, the second for its derivative. Its shape is (300,2). Mashing the solution and its derivative together with ravel does not make sense. Instead, you should only take the actual solution, that's the thing you are fitting.
So, replace
return y.ravel()
with
return y[:, 0]

Finding roots of function in Python

I'm trying to calculate the roots for a function using the scipy function fsolve, but an error keeps flagging:
TypeError: 'numpy.array' object is not callable
I assume it's probably easier to define the equation as a function but I've tried that a few times to no avail.
Code:
import scipy
import numpy as np
import matplotlib.pyplot as plt
from scipy import optimize
# Constants
wavelength = 0.6328
ncore = 1.462420
nclad = 1.457420
a = 8.335
# Mode Order
l = 0
# Mode parameters
V = (2 * np.pi * a / wavelength) * np.sqrt(ncore**2 - nclad**2)
U = np.arange(0, V, 0.01)
W = np.sqrt(V**2-U**2)
func = U * scipy.special.jv(l+1, U) / scipy.special.jv(l, U) - W * scipy.special.kv(l+1, W) / scipy.special.kv(l, W)
from scipy.optimize import fsolve
x = fsolve(func,0)
print x
StackTrace:
Traceback (most recent call last):
File "<ipython-input-52-081a9cc9c0ea>", line 1, in <module>
runfile('/home/luke/Documents/PythonPrograms/ModeSolver_StepIndex/ModeSolver_StepIndex.py', wdir='/home/luke/Documents/PythonPrograms/ModeSolver_StepIndex')
File "/usr/lib/python2.7/site-packages/spyderlib/widgets/externalshell/sitecustomize.py", line 580, in runfile
execfile(filename, namespace)
File "/home/luke/Documents/PythonPrograms/ModeSolver_StepIndex/ModeSolver_StepIndex.py", line 52, in <module>
x = fsolve(func,0)
File "/usr/lib64/python2.7/site-packages/scipy/optimize/minpack.py", line 140, in fsolve
res = _root_hybr(func, x0, args, jac=fprime, **options)
File "/usr/lib64/python2.7/site-packages/scipy/optimize/minpack.py", line 197, in _root_hybr
shape, dtype = _check_func('fsolve', 'func', func, x0, args, n, (n,))
File "/usr/lib64/python2.7/site-packages/scipy/optimize/minpack.py", line 20, in _check_func
res = atleast_1d(thefunc(*((x0[:numinputs],) + args)))
TypeError: 'numpy.ndarray' object is not callable
That is because fsolve takes a function as argument.
Try this, Note you still will encounter some runtime error , you will have to check if your return from func is properly constructed, I will leave that for you to figure out.
import scipy
import numpy as np
import matplotlib.pyplot as plt
from scipy import optimize
# Constants
wavelength = 0.6328
ncore = 1.462420
nclad = 1.457420
a = 8.335
# Mode Order
# l = 0
# Mode parameters
V = (2 * np.pi * a / wavelength) * np.sqrt(ncore**2 - nclad**2)
U = np.arange(0, V, 0.01)
W = np.sqrt(V**2-U**2)
def func(l):
return U * scipy.special.jv(l+1, U) / scipy.special.jv(l, U) - W * scipy.special.kv(l+1, W) / scipy.special.kv(l, W)
from scipy.optimize import fsolve
x = fsolve(func,0)
print x
You need to pass a function to fsolve not an array.
If I just print your func:
func
array([ -1.04882076e+01, -1.04881526e+01, -1.04879876e+01,
-1.04877125e+01, -1.04873274e+01, -1.04868321e+01,
-1.04862266e+01, -1.04855109e+01, -1.04846847e+01,
-1.04837481e+01, -1.04827008e+01, -1.04815428e+01,
-1.04802738e+01, -1.04788938e+01, -1.04774024e+01,
-1.04757996e+01, -1.04740850e+01, -1.04722585e+01,
-1.04703198e+01, -1.04682686e+01, -1.04661046e+01,
-1.04638275e+01, -1.04614371e+01, -1.04589330e+01,
-1.04563147e+01, -1.04535820e+01, -1.04507345e+01,
-1.04477718e+01, -1.04446934e+01, -1.04414988e+01,
... ]
that's an array but you want a function. Something like this works:
def linear(x):
return 2*x+4
fsolve(linear, 0)
don't know how one could define your function, though.

Categories

Resources