I'm trying to approximate the Bessel function with Runge-Kutta. I'm using RK4 here cause I don't know what the equations are for RK2 for a system of ODEs.....Basically it's not working, or at least it's not working very well, I don't know why.
Anyway it's written in python. Here is code:
from __future__ import division
from pylab import*
#This literally just makes a matrix
def listoflists(rows,cols):
return [[0]*cols for i in range(rows)]
def f(x,Jm,Zm,m):
def rm(x):
return (m**2 - x**2)/(x**2)
def q(x):
return 1/x
return rm(x)*Jm-q(x)*Zm
n = 100 #No Idea what to set this to really
m = 1 #Bessel function order; computes up to m (i.e. 0,1,2,...m)
interval = [.01, 10] #Interval in x
dx = (interval[1]-interval[0])/n #Step size
x = zeros(n+1)
Z = listoflists(m+1,n+1) #Matrix: Rows are Function order, Columns are integration step (i.e. function value at xn)
J = listoflists(m+1,n+1)
x[0] = interval[0]
x[n] = interval[1]
#This reproduces all the Runge-Kutta relations if you read 'i' as 'm' and 'j' as 'n'
for i in range(m+1):
#Initial Conditions, i is m
if i == 0:
J[i][0] = 1
Z[i][0] = 0
if i == 1:
J[i][0] = 0
Z[i][0] = 1/2
#Generate each Bessel function, j is n
for j in range(n):
x[j] = x[0] + j*dx
K1 = Z[i][j]
L1 = f(x[j],J[i][j],Z[i][j],i)
K2 = Z[i][j] + L1/2
L2 = f(x[j] + dx/2, J[i][j]+K1/2,Z[i][j]+L1/2,i)
K3 = Z[i][j] + L2/2
L3 = f(x[j] +dx/2, J[i][j] + K2/2, Z[i][j] + L2/2,i)
K4 = Z[i][j] + L3
L4 = f(x[j]+dx,J[i][j]+K3, Z[i][j]+L3,i)
J[i][j+1] = J[i][j]+(dx/6)*(K1+2*K2+2*K3+K4)
Z[i][j+1] = Z[i][j]+(dx/6)*(L1+2*L2+2*L3+L4)
plot(x,J[0][:])
show()
(To close this old question with at least a partial answer.) The immediate error is that the RK4 method is incorrectly implemented. For instance instead of
K2 = Z[i][j] + L1/2
L2 = f(x[j] + dx/2, J[i][j]+K1/2, Z[i][j]+L1/2, i)
it should be
K2 = Z[i][j] + L1*dx/2
L2 = f(x[j] + dx/2, J[i][j]+K1*dx/2, Z[i][j]+L1*dx/2, i)
that is, the slopes have to be scaled by the step size to get the correct update for the variables.
Related
Using MATLAB, I am trying to solve the equation using Newtons Method but keep printing the result "None". I am not sure where the mistake is as I do not want to tamper with the formula.
def newt(p):
maxIt = 1000
tol = 10^(-5)
for i in range(maxIt):
fp = 2*p**3 + p**2 - p + 1 #double * for exponent
fprimep = 6*p**2 + 2*p - 1 #f'p
p_new = p - fp/fprimep
if abs (p - p_new) < tol:
return p
p = p_new
#initial values
p0 = -1.2
p = newt(p0)
print(p)
The error in your code is due to a partial conversion from Matlab. You define tol = 10^(-5), but this is not exponentiation in Python, it is bitwise xor. Correcting that, you get the proper result:
def newt(p):
maxIt = 1000
tol = 1e-5 # Error was here
for i in range(maxIt):
fp = 2*p**3 + p**2 - p + 1 #double * for exponent
fprimep = 6*p**2 + 2*p - 1 #f'p
p_new = p - fp/fprimep
if abs (p - p_new) < tol:
return p
p = p_new
#initial values
p0 = -1.2
p = newt(p0)
print(p)
# -1.23375
As for the return value, your function returns None when the method does not converge. I'm not sure this was a intentional decision, but it is a good convention anyway, since it allows the user to know the method did not converge. A more robust method would be to throw an exception, maybe adding the guess at the time the iteration stopped and the convergence ratio.
Essentially, I am trying to solve the lapalcian using a random walk numerical solution. My domain is a circle, and the boundary condition is some function: f(phi) = cos(2phi). Essentially, I am trying to take a point within the 2D domain of my (unit) circle, randomly walk it until it meets the edge of the circle, so when x^2 + y+2 = 1. I am then going to take the x and y coordinate, find theta, then plug theta into my function to obtain a value. I will record and store this value, and repeat this process 'NumbRepeats' times. Then take the average of these values. This should give an approximate solution to the equation. I am not so concerned about the physics per se, I need help in what I have listed above the process I have described above. It may not necesarily be correct, but if I can get the program to do what I think is correct, then I will be happy. Thanks for the help. I will post my code below:
Note, my expertise is not coding, so apologies if this is difficult to understand. Any help is appreciated.
import matplotlib.pyplot as plt
import numpy as np
import random
#Python code for 2D random walk
# defining the number of steps
n = 100000
#creating two array for containing x and y coordinate
#of size equals to the number of size and filled up with 0's
x = np.zeros(n)
y = np.zeros(n)
NumbRepeats = 100
PotBoundVec = []
for j in range(NumbRepeats):
PotBound = 0
for i in range(1, n):
val = random.randint(0, 1)
if val == .25:
x[i] = x[i - 1] + 1
y[i] = y[i - 1]
elif val == .5:
x[i] = x[i - 1] - 1
y[i] = y[i - 1]
elif val == .75:
x[i] = x[i - 1]
y[i] = y[i - 1] + 1
else:
x[i] = x[i - 1]
y[i] = y[i - 1] - 1
if x[i]**2 + y[i]**2 == 1:
Theta = np.tan(x[i]/y[i])
PotBound = np.cos(2*Theta)
PotBoundVec.append(PotBound)
x = np.zeros(n)
y = np.zeros(n)
else:
pass
print(PotBoundVec)
# RK4
# disp = u0 # initial condition array
flag = True
#h = 0
timeee = 0.000
timemax = 5001 #ms
while(timeee<timemax):
predisp = np.zeros(N+4)
ddisp = np.zeros(N+2) # derivative of displacement
k1 = np.zeros(N)
K2 = np.zeros(N)
K3 = np.zeros(N)
K4 = np.zeros(N)
# stage 1
for l in range (0,N):
predisp[l+2] = u0[l]
predisp[N+1] = predisp[3]
predisp[N+2] = predisp[3]
predisp[N+3] = predisp[4]
predisp[3] = predisp[N]
predisp[0] = predisp[N-1]
for p in range (0,N):
ddisp[p+1] = uj_prime[p]
ddisp[N+1] = ddisp[3]
ddisp[N] = ddisp[3]
ddisp[0] = ddisp[N+1]
for i in range(1,N+1):
k1[i-1] = -c*ddisp[i]
k1[N-1] = k1[0]
for y in range (0,N-1):
u0[y] = predisp[y+2]+ 0.5*(dt/a_sec)*k1[y]
u0[N-1] = u0[0]
#Stage 2
u_initial_colmat = np.transpose(u0)
uj_prime = C # u_initial_colmat
for k in range(0,N):
ddisp[k+1] = uj_prime[k]
ddisp[N+1] = ddisp[3]
ddisp[N] = ddisp[3]
ddisp[0] = ddisp[N+1]
for i in range(1,N+1):
K2[i-1] = -c*ddisp[i]
K2[N-1] = K2[0]
for y in range(0,N-1):
u0[y] = predisp[y+2] + 0.5*(dt/a_sec)*K2[y]
u0[N-1] = u0[0]
#Stage 3
u_initial_colmat = np.transpose(u0)
uj_prime = C # u_initial_colmat
for k in range(0,N):
ddisp[k+1] = uj_prime[k]
ddisp[N+1] = ddisp[3]
ddisp[N] = ddisp[3]
ddisp[0] = ddisp[N+1]
for i in range(1,N+1):
K3[i-1] = -c*ddisp[i]
K3[N-1] = K3[0]
for y in range(0,N-1):
u0[y] = predisp[y+2] + 1.0*(dt/a_sec)*K3[y]
u0[N-1] = u0[0]
#Stage 4
u_initial_colmat = np.transpose(u0)
uj_prime = C # u_initial_colmat
for k in range(0,N):
ddisp[k+1] = uj_prime[k]
ddisp[N+1] = ddisp[3]
ddisp[N] = ddisp[3]
ddisp[0] = ddisp[N+1]
for i in range(1,N+1):
K4[i-1] = -c*ddisp[i]
K4[N-1] = K4[0]
for y in range(0,N-1):
u0[y] = predisp[y+2] + (1/6)*(dt/a_sec)*(k1[y] + 2*K2[y] + 2*K3[y] + K4[y])
u0[N-1] = u0[0]
milli_time = timeee/a_sec
uu = ((np.exp(alpha*((x-c*milli_time-x0)**2))*(np.cos(k0*(x-c*milli_time-x0))))) # Analytical Solution
timeee = timeee + dt
if (timeee >= timemax):
exit()
It is the part of the code where RK4 is used. Tangent hyperbolic function(double-sided) is used as the grid. I need to solve the 1D wave equation using Runge Kutta of order 4 for time discretisation with periodic boundary conditions. I know that while getting matrices for solving derivatives is correct and the periodic boundary conditions are applied properly. But during the application of the RK4 method, I am a bit unsure if the periodic conditions are applied properly. spatial discretisation algo have derivative terms considering three points on RHS: j-1, j and j+1
N is the number of nodes in the grid. The above image shows how the RK4 method is used in the code.
The derivative term is written as ddisp in the code. Any help is highly appreciated.
I'm trying to write an algorithm for my student work, it is working well. However, it takes a long time to calculate, especially with big arrays.
This part of code is slowing down all program.
Shapes: X.shape = mask.shape = logBN.shape = (500,500,1000),
F.shape = (20,20),
A.shape = (481,481),
s2 -- scalar.
How should I change this code to make it faster?
h = F.shape[0]
w = F.shape[1]
q = np.zeros((A.shape[0], A.shape[1], X.shape[2]))
for i in range(A.shape[0]):
for j in range(A.shape[1]):
mask[:,:,:] = 0
mask[i:i + h,j:j + w,:] = 1
q[i,j,:] = ((logBN*(1 - mask)).sum(axis=(0,1)) +
(np.log(norm._pdf((X[i:i + h,j:j + w,:]-F[:,:,np.newaxis])/s2)/s2)).sum(axis=(0,1))
After heavy juggling through algebraic operations of log, exp, power, it all came to this -
# Params
m,n = F.shape[:2]
k1 = 1.0/(s2*np.sqrt(2*np.pi))
k2 = -0.5/s2**2
k3 = np.log(k1)*m*n
out = np.zeros((A.shape[0], A.shape[1], X.shape[2]))
for i in range(A.shape[0]):
for j in range(A.shape[1]):
mask[:] = 1
mask[i:i + h,j:j + w,:] = 0
XF = (X[i:i + h,j:j + w,:]-F[:,:,np.newaxis])
p1 = np.einsum('ijk,ijk->k',logBN,mask)
p2 = k2*np.einsum('ijk,ijk->k',XF,XF)
out[i,j,:] = p1 + p2
out += k3
Few things used were -
1] norm._pdf is basically : norm.pdf(x) = exp(-x**2/2)/sqrt(2*pi). So, we could inline the implementation and optimize those at the script level.
2] The division by scalars won't be efficient, so those were replaced by multiplications by their reciprocals. So, as a pre-processing store their reciprocals before going into the loop.
Just trying to make sense of your inner loop
mask[:,:,:] = 0
mask[i:i + h,j:j + w,:] = 1
q[i,j,:] = ((logBN*(1 - mask)).sum(axis=(0,1)) +
(np.log(norm._pdf((X[i:i + h,j:j + w,:]-F[:,:,np.newaxis])/s2)/s2)).sum(axis=(0,1))
looks like
idx = (slice(i,i+h), slice(j,j_w), slice(None))
mask = np.zeros(X.shape)
mask(idx) = 1
mask = 1 - mask
# alt mask=np.ones(X.shape);mask[idx]=0
term1 = (logBN*mask).sum(axis=(0,1))
term2 = np.log(norm._pdf((X[idx] - F[...,None])/s2)/s2).sum(axis=(0,1))
q[i,j,:] = term1 + term2
So idx and mask define a subarray in A. You are using logBN outside the array; and term inside it. You are summing values on 1st 2 dim, so both term1 and term2 has shape X.shape[2], which you save in q.
That mask/window is 20x20.
As a first cut I'd try to calculate that term2 for all i,j at once. That looks like a typical sliding window problem. I'd also try to express the term1 as a subtraction - the whole logBN minus this window.
The program needs to compute define integral with a predetermined
accuracy (eps) with the Trapezoidal Rule and my function needs to return:
1.the approximate value of the integral.
2.the number of iterations.
My code:
from math import *
def f1(x):
return (x ** 2 - 1)**(-0.5)
def f2(x):
return (cos(x)/(x + 1))
def integral(f,a,b,eps):
n = 2
x = a
h = (b - a) / n
sum = 0.5 * (f(a) + f(b))
for i in range(n):
sum = sum + f(a + i * h)
sum_2 = h * sum
k = 0
flag = 1
while flag == 1:
n = n * 2
sum = 0
k = k + 1
x = a
h = (b - a) / n
sum = 0.5 * (f(a) + f(b))
for i in range(n):
sum = sum + f(a + i * h)
sum_new = h * sum
if eps > abs(sum_new - sum_2):
t1 = sum_new
t2 = k
return t1, t2
else:
sum_2 = sum_new
x1 = float(input("First-begin: "))
x2 = float(input("First-end: "))
y1 = float(input("Second-begin: "))
y2 = float(input("Second-end: "))
int_1 = integral(f1,x1,y1,1e-6)
int_2 = integral(f2,x2,y2,1e-6)
print(int_1)
print(int_2)
It doesn't work correct. Help, please!
You implemented the math wrong. The error is in the lines
for i in range(n):
sum = sum + f(a + i * h)
range(n) always starts at 0, so in your first iteration you just add the f(a) term again.
If you replace it with
for i in range(1, n):
sum = sum + f(a + i * h)
it works.
Also, you have a ton of redundant code; you basically coded the core of the integration algorithm twice. Try to follow the DRY-principle.
The trapezoidal rule of integration simply says that an approximation to the integral $\int_a^b f(x) dx$ is (b-a) (f(a)+f(b))/2. The error is proportional to (b-a)^2, so that it is possible to have a better estimate using the composite rule, i.e., subdividing the initial interval in a number of shorter intervals.
Is it possible to use shorter intervals and still reuse the function values previously computed, so minimizing the total number of function evaluation?
Yes, it is possible if we divide each interval in two equal parts, so that at stage 0 we use 1 intervals, at stage 1 2 equal intervals and in general, at stage n, we use 2n equal intervals.
Let's start with a simple problem and see if it possible to generalize the procedure…
a, b = 0, 32
L = b-a = 32
by the trapezoidal rule the initial approximation say I0, is given by
I0 = L * (f0+f1)/2
= L * S0
with S0 = (f0+f1)/2; a pictorial representation of the real axis, the coordinates of the interval extremes and the evaluated functions follows
x0 x1
01234567890123456789012345679012
f0 f1
Next, we divide the original interval in two,
L = L/2
x0 x2 x1
01234567890123456789012345679012
f0 f2 f1
and the new approximation, stage n=1, is obtained using two times the trapezoidal rule and applying a bit of algebra
I1 = L * (f0+f2)/2 + L * (f2+f1)/2
= L * [(f0+f1)/2 + f2]
= L * [S0 + S1]
with S1 = f2
Another subdivision, stage n=2, L = L/2 and
x0 x3 x2 x4 x1
012345678901234567890123456789012
f0 f3 f2 f4 f1
I2 = L * [(f0+f3) + (f3+f2) + (f2+f4) + (f4+f1)] / 2
= L * [(f0+f1)/2 + f2 + (f3+f4)]
= L * [S0+S1+S2]
with S2 = f3 + f4.
It is not difficult, given this picture,
x0 x5 x3 x6 x2 x7 x4 x8 x1
012345678901234567890123456789012
f0 f5 f3 f6 f2 f7 f4 f8 f1
to understand that our next approximation can be computed as follows
L = L/2
S3 = f5+f6+f7+f8
I3 = L*[S0+S1+S2+S3]
Now, we have to understand how to compute a generalization of Sn,
n = 1, … — for us, the pseudocode is
L_n = (b-a)/2**n
list_x_n = list(a + L_n + 2*Ln*j for j=0, …, 2**n-1)
Sn = Sum(f(xj) for each xj in list_x_n)
For n = 3, L = (b-a)/8 = 4, we have from the formula above list_x_n = [4, 12, 20, 28], please check with the picture...
Now we are ready to code our algorithm in Python
def trapaezia(f, a, b, tol):
"returns integ(f, (a,b)), estimated error and number of evaluations"
from math import fsum # controls accumulation of rounding errors in sums
L = b - a
S = (f(a)+f(b))/2
I = L*S
n = 1
while True:
L = L/2
new_points = (a+L+j*L for j in range(0, n+n, 2))
delta_S = fsum(f(x) for x in new_points)
new_S = S + delta_S
new_I = L*new_S
# error is estimated using Richardson extrapolation (REP)
err = (new_I - I) * 4/3
if abs(err) > tol:
n = n+n
S, I = new_S, new_I
else:
# we return a better estimate using again REP
return (4*new_I-I)/3, err, n+n+1
If you are curious about Richardson extrapolation, I recommend this document that deals exactly with the application of REP to the trapezoidal rule quadrature algorithm.
If you are curious about math.fsum, the docs don't say too much but the link to the original implementation that also includes an extended explanation of all the issues involved.