Defining a integral of multi variable function as a second function python - python

Defining a integral of multi variable function as a second function python
I am using python to integrate a multivariable function over only one of the variables (it is a function of x and theta and I am integrating over theta from 0 to 2*pi, hence the result is a function of x). I have attempted the following:
import numpy as np
import scipy.integrate as inte
d=10.0
xvals=np.linspace(-d,d,1000)
def aIntegrand(theta,x):
return 1/(2*np.pi)*np.sin(2*np.pi*x*np.sin(theta)/d)**2
def A(x):
return (inte.quad(aIntegrand,0,2*np.pi,args=(x,))[0])**(1/2)
plt.plot(xvals,A(xvals))
plt.xlabel("x")
plt.ylabel("A(x)")
plt.show()
and I get the following error:
TypeError: only size-1 arrays can be converted to Python scalars
I assume this is because the result of the quad integrator is an array with two elements and python doesn't like defining the function based on an indexed array? That is a complete guess of the problem though. If anyone knows how I can fix this and could let me know, that'd be great :)
A second attempt
I have managed to get the plot of the integral successfully using the following code:
import numpy as np
import scipy.integrate as inte
import matplotlib.pyplot as plt
d=10.0
xvals=np.linspace(-d,d,1000)
thetavals=np.linspace(0.0,2*np.pi,1000)
def aIntegrand(theta,x):
return 1/(2*np.pi)*np.sin(2*np.pi*x*np.sin(theta)/d)**2
def A(x):
result=np.zeros(len(x))
for i in range(len(x)):
result[i]=(inte.quad(aIntegrand,0,2*np.pi,args=(x[i],))[0])**(1/2)
return result
def f(x,theta):
return x**2* np.sin(theta)
plt.plot(xvals,A(xvals))
plt.xlabel("x")
plt.ylabel("A(x)")
plt.show()
But this does not give A(x) as a function, due to the way I have defined it, it requires an array form input. I need the function to be of the same form as aIntegrand, where when given parameters returns a single value & so the function can be integrated repeatedly.

I do not think that what you seek exists within Scipy. However, you have at least two alternatives.
First, you can create an interpolator using interp1d. This means that the range of x values you initially give determines the range for which you will be able to call the interpolator. This interpolator can then be called for any value of x in this interval.
You can perform the second integral even though you do not have a callable. The function simps only requires values and their location to provide an estimate of the integration process. Otherwise, you can perform the double integration in one call with dblquad.

First note that your integral can be calculated analytically. It is
0.5 * (1 - J0(4 * pi * x / d))
where J0 is the Bessel function of the first kind.
Second, you could use quadpy (one of my projects); it has fully vectorized computation.
import numpy as np
import quadpy
import matplotlib.pyplot as plt
import scipy.special
d = 10.0
x = np.linspace(-d, d, 1000)
def aIntegrand(theta):
return (
1
/ (2 * np.pi)
* np.sin(2 * np.pi * np.multiply.outer(x, np.sin(theta)) / d) ** 2
)
Ax2, err = quadpy.quad(aIntegrand, 0, 2 * np.pi)
Ax = np.sqrt(Ax2)
plt.plot(x, Ax, label="quadpy")
plt.xlabel("x")
plt.ylabel("A(x)")
plt.plot(x, np.sqrt(0.5 * (1 - scipy.special.jv(0, 4*np.pi*x/d))), label="bessel")
# plt.show()
plt.savefig("out.png", transparent=True, bbox_inches="tight")

Related

Solving dynamical ODE System with Python

I am trying to solve a dynamical system with three state variables V1,V2,I3 and then plot these in a 3d Plot. My code so far looks as follows:
from scipy.integrate import ode
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
import math
def ID(V,a,b):
return a*(math.exp(b*V)-math.exp(-b*V))
def dynamical_system(t,z,C1,C2,L,R1,R2,R3,RN,a,b):
V1,V2,I3 = z
f = [(1/C1)*(V1*(1/RN-1/R1)-ID(V1-V2,a,b)-(V1-V2)/R2),(1/C2)*(ID(V1-V2,a,b)+(V1-V2)/R2-I3),(1/L)*(-I3*R3+V2)]
return f
# Create an `ode` instance to solve the system of differential
# equations defined by `dynamical_system`, and set the solver method to 'dopri5'.
solver = ode(dynamical_system)
solver.set_integrator('dopri5')
# Set the initial value z(0) = z0.
C1=10
C2=100
L=0.32
R1=22
R2=14.5
R3=100
RN=6.9
a=2.295*10**(-5)
b=3.0038
solver.set_f_params(C1,C2,L,R1,R2,R3,RN,a,b)
t0 = 0.0
z0 = [-3, 0.5, 0.25] #here you can set the inital values V1,V2,I3
solver.set_initial_value(z0, t0)
# Create the array `t` of time values at which to compute
# the solution, and create an array to hold the solution.
# Put the initial value in the solution array.
t1 = 25
N = 200 #number of iterations
t = np.linspace(t0, t1, N)
sol = np.empty((N, 3))
sol[0] = z0
# Repeatedly call the `integrate` method to advance the
# solution to time t[k], and save the solution in sol[k].
k = 1
while solver.successful() and solver.t < t1:
solver.integrate(t[k])
sol[k] = solver.y
k += 1
xlim = (-4,1)
ylim= (-1,1)
zlim=(-1,1)
fig=plt.figure()
ax=fig.gca(projection='3d')
#ax.view_init(35,-28)
ax.set_xlim(xlim)
ax.set_ylim(ylim)
ax.set_zlim(zlim)
print sol[:,0]
print sol[:,1]
print sol[:,2]
ax.plot3D(sol[:,0], sol[:,1], sol[:,2], 'gray')
plt.show()
Printing the arrays that should hold the solutions sol[:,0] etc. shows that apparently it constantly fills it with the initial value. Can anyone help? Thanks!
Use from __future__ import division.
I can't reproduce your problem: I see a gradual change from -3 to -2.46838127, from 0.5 to 0.38022886 and from 0.25 to 0.00380239 (with a sharp change from 0.25 to 0.00498674 in the first step). This is with Python 3.7.0, NumPy version 1.15.3 and SciPy version 1.1.0.
Given that you are using Python 2.7, integer division may be the culprit here. Quite a number of your constants are integer, and you have a bunch of 1/<constant> integer divisions in your equation.
Indeed, if I replace / with // in my version (for Python 3), I can reproduce your problem.
Simply add from __future__ import division at the top of your script to solve your problem.
Also add from __future__ import print_function at the top, replace print <something> with print(<something>) and your script is fully Python 3 and 2 compatible).

How to pass the source term when solving ODE using odeint

Differential equation of forced harmonic oscillator is given as Mx''+Lx'+(w^2)x=F(t). Here F(t) is a source term. To solve this problem I wrote a code where I define the differential equation in a function 'diff'. I wrote another function 'generate_pulse' that gives the F(t).
Then I use 'odeint', which solves the differential equation by calling the 'diff' function along with other parameters. Now I don't get any error message if I put F=0 inside the 'diff' function (i.e., ignore any F(t) term). Please have a look inside the 'diff' function:
F=0 #No error detected if I put F=0 here. Comment out this line to see the error
Once I keep the F(t), I get an error message 'ValueError: setting an array element with a sequence.'
How to solve the problem?
Code:
import numpy as np
from scipy.integrate import odeint
import matplotlib.pyplot as plt
import math
import itertools
def diff(y, t,param):
M=param[0]
L=param[1]
w2=param[2]
F=param[3]
F=0 #No error detected if I put F=0 here. Comment out this line to see the error
x,v = y
dydt = [v, (F-L*v - w2*x)/M]
return dydt
def generate_pulse(t,Amp,init_delay,rtime,PW):
L=len(t)
t0=t[0:math.ceil(init_delay*L/100)]
t1=t[len(t0):len(t0)+math.ceil(rtime*L/100)]
t2=t[len(t0)+len(t1):len(t0)+len(t1)+math.ceil(PW*L/100)]
t3=t[len(t0)+len(t1)+len(t2):len(t0)+len(t1)+len(t2)+2*math.ceil(rtime*L/100)]
t4=t[len(t0)+len(t1)+len(t2)+len(t3):len(t0)+len(t1)+len(t2)+len(t3)+math.ceil(PW*L/100)]
t5=t[len(t0)+len(t1)+len(t2)+len(t3)+len(t4):len(t0)+len(t1)+len(t2)+len(t3)+len(t4)+math.ceil(rtime*L/100)]
t6=t[len(t0)+len(t1)+len(t2)+len(t3)+len(t4)+len(t5):]
s0=0*t0
s1=(Amp/(t1[-1]-t1[0]))*(t1-t1[0])
s2=np.full((1,len(t2)),Amp)
s2=list(itertools.chain.from_iterable(s2)) #The 'tuple' is converted into array
s3=-Amp/(t1[-1]-t1[0])*(t3-t3[0])+Amp
s4=np.full((1,len(t4)),-Amp)
s4=list(itertools.chain.from_iterable(s4)) #The 'tuple' is converted into array
s5=(Amp/(t5[-1]-t5[0]))*(t5-t5[0])-Amp
s6=np.full((1,len(t6)),0)
s6=list(itertools.chain.from_iterable(s6)) #The 'tuple' is converted into array
s=[s0,s1,s2,s3,s4,s5,s6]
s=list(itertools.chain.from_iterable(s))
return s
###############################################################################
# Main code from here
t = np.linspace(0, 30, 200)
y0 = [- 10, 0.0]
M=5
L = 1
w2 = 15.0
Amp=5
init_delay=10
rtime=10
PW=10
F=generate_pulse(t,Amp,init_delay,rtime,PW)
Param=([M,L,w2,F],) #Making the 'Param' a tuple. Because args of odeint takes tuple as argument.
sol = odeint(diff, y0, t, args=Param)
plt.plot(t, sol[:, 0], 'b', label='x(t)')
plt.plot(t,F,'g',label='Force(t)')
plt.legend(loc='best')
plt.show()
You get the error because the value of F that you pass is the array that you generated.
Use the interpolation functions of numpy or scipy to make an actual function out of the arrays. Then evaluate that function at time t. Or directly implement the forcing term as a piecewise defined function of the scalar t.
Also note that the list of sampling times you give in t to odeint has (almost) nothing to do with the times at which odeint calls the ODE function diff. If you want to control that you would have to implement your own fixed-step method, probably not Runge-Kutta but some multi-step method.

Integrating special function and plotting

I am trying to plot an integration of a special (eg. Bessel) function and my minimal code is the following.
#!/usr/bin/env python
import matplotlib.pyplot as plt
import numpy as np
import scipy.integrate as integrate
import scipy.special as sp
from scipy.special import jn
#x = np.arange(0.0, 10.0, 0.1)
U = np.linspace(0,10,1000)
#Delta = U**2
#Delta = U-4+8*integrate.quad(lambda x: sp.jv(1,x)/(x*(1.0+np.exp(U*x*0.5))), 0, 100)
Delta = U-4+8*integrate.quad(lambda x: jn(1,x)/(x*(1.0+np.exp(U*x*0.5))), 0.1, 1000)
plt.plot(U,Delta)
plt.xlabel('U')
plt.ylabel('$\Delta$')
plt.show()
However, this gives me several an error messages saying quadpack.error: Supplied function does not return a valid float whereas the function gets easily plotted in Mathematica. Do Python's Bessel's functions have limitations?
I have used this documentation for my plotting.
It is difficult to provide an answer that solves the problem before understanding what exactly you are trying to do. However, let me list a number of issues and provide an example that may not achieve what you are trying to do but at least it will provide a path forward.
Because your lambda function multiplies x by an array U, it returns an array instead of a number. A function that needs to be integrated should return a single number. You could fix this, for example, by replacing U by u:
f = lambda x, u: jn(1,x)/(x*(1.0+np.exp(u*x*0.5)))
Make Delta a function of u AND make quad pass additional argument u to f (defined in the previous point) AND extract only the value of the integral from the returned tuple from quad (quad returns a tuple of several values: the integral, the error, etc.):
Delta = lambda u: -4+8*integrate.quad(f, 0.1, 1000, args=(u,))[0]
Compute Delta for each u:
deltas = np.array(map(Delta, U))
plot the data:
plt.plot(U, deltas)

python scipy minimize ValueError

I'm trying to use scipy minimize and running into a ValueError error that just I can't seem to figure out.
I have an objective function f as defined below, which takes two inputs. If I define the two inputs with random numbers, and call the function, it seems to evaluate just fine.
import numpy as np
import pandas as pd
from scipy.optimize import minimize
sigma = pd.DataFrame(np.random.randn(7, 7), columns=list('ABCDEFG'))
x0 = np.random.randn(7)
def f(x, sigma):
result = 0.0
sigmaX = sigma.dot(x)
for i in range(len(x)):
for j in range(len(x)):
result = result + (x[i] * sigmaX[i] - x[j]*sigmaX[j])**2
return result
In [387]: f(x0, sigma)
Out[387]: 371.67951578983951
But when I try to use minimize, I get a ValueError:
In [389]: minimize(f, x0, args=(sigma))
ValueError: Wrong number of items passed 1, placement implies 7
I can't figure out whether minimize is upset about the shape/size of x0 or sigma. Any help would be very much appreciated!
Eric
Copied from my comment, so it can be marked as closed
Try args=(sigma,). That is make sure you give args a tuple. Without the , the () does nothing.

Plot arbitrary 2-D function in python/pyplot like Matlab's Ezplot

I'm looking for a way to generate a plot similar to how ezplot works in MATLAB in that I can type:
ezplot('x^2 + y^2 = y + 5')
and get a graph ready to go for any arbitrary function. I'm only worrying about the case where I have both a x and a y.
I only have the function, and I'd really rather not go about trying to calculate all the y values for some given x range if I didn't have to.
The few solutions I've seen suggested are either about decision boundaries (which this is not. There is no test data or anything, just an arbitrary function) or are all for functions already defined as y = some x equation which doesn't really help me.
I would somewhat accept if there was a good way to mimic Wolfram|Alpha in their solve functionality("solve x^2 + y^2 = y + 5 for y" will give me two functions I could then graph separately), but rather prefer the ezplot as that's more or less instant within MATLAB.
I think you could use sympy plotting and parse_expr for this For your example, this would work as follows
from sympy.plotting import plot_implicit
from sympy.parsing.sympy_parser import parse_expr
def ezplot(s):
#Parse doesn't parse = sign so split
lhs, rhs = s.replace("^","**").split("=")
eqn_lhs = parse_expr(lhs)
eqn_rhs = parse_expr(rhs)
plot_implicit(eqn_lhs-eqn_rhs)
ezplot('x^2 + y^2 = y + 5')
This can be made as general as needed
You could use sympy to solve the equation and then use the resulting functions for plotting y over x:
import sympy
x=sympy.Symbol('x')
y=sympy.Symbol('y')
f = sympy.solve(x**2 + y**2 - y - 5, [y])
print f
xpts = (numpy.arange(10.)-5)/10
ypts = sympy.lambdify(x, f, 'numpy')(xpts)
# then e.g.: pylab.scatter(xpts, ypts)
#EdSmith solution works fine. Nevertheless, I have another suggestion. You can use plot a contour. You can rewrite your function as f(x, y)=0, and then use this code
from numpy import mgrid, pi
import matplotlib.pyplot as plt
def ezplot(f):
x, y = mgrid[-2*pi:2*pi:51, -2*pi:2*pi:51]
z = f(x, y)
ezplt = plt.contour(x, y, f, 0, colors='k')
return ezplt
That's the main idea. Of course, you can generalize it as the function in MATLAB, like general intervals of x and y, passing the function as a string, etc.

Categories

Resources