Python code optimization using vectorizing iterative section - python

I am new to python and numpy there is this Second order PDE code which i want to vectorize to run in lesser time but since it uses function to fill each value in grid i m stuck.
def func(x, y):
return (-2 * np.pi ** 2) * np.sin(np.pi * x) * np.sin(np.pi * y)
def f1t(x, y):
return (np.sin(np.pi * x) * np.sin(np.pi * y))
def five_point(grid, i, j, h, grid_x, grid_y):
return ((grid[i + 1, j] + grid[i - 1, j] + grid[i, j + 1] + grid[i, j - 1]) / 4
- ((h ** 2) / 4) * func(grid_x[i, 0], grid_y[0, j]))
def five_point_fin_int(X=np.ones(1), Y=np.ones(1), n_x=32, K_max=1000,
tol=0.0001, tol_type="grid"):
import time;
t0 = time.clock()
h = 1 / n_x
X_max = int(X / h)
Y_max = int(Y / h)
grid_x, grid_y = np.mgrid[0:X + h:h, 0:Y + h:h]
grid_true = np.zeros((X_max + 1, Y_max + 1))
for i in range(1, X_max):
for j in range(1, Y_max):
grid_true[i, j] = f1t(grid_x[i, 0], grid_y[0, j])
grid = np.zeros((X_max + 1, Y_max + 1))
grid_err = np.zeros((X_max + 1, Y_max + 1))
count = 0
tol_max = False
while ((count < K_max) & (tol_max == False)):
count += 1
for i in range(1, X_max):
for j in range(1, Y_max):
grid[i, j] = five_point(grid, i, j, h, grid_x, grid_y)
grid_err[i, j] = (grid[i, j] - grid_true[i, j])
if (tol_type.lower() == "grid" ):
if (np.amax(abs(grid_err)) < tol):
tol_max = True
else:
if (abs(np.linalg.norm(grid) - np.linalg.norm(grid_true)) < tol):
tol_max = True
cpu_time = time.clock() - t0
In the end i print compute time since its nested for loops right now the time taken is a lot around 9 sec i would like to improvise on this.

numpy allows you to replace loops by vector calls. You can definitely do the following:
grid_true = np.zeros((X_max + 1, Y_max + 1))
grid_true[1:X_max,1:Y_max]=f1t(*np.meshgrid(grid_x[1:X_max,0], grid_y[0,1:Y_max]))
And you can also try the following:
grid = np.zeros((X_max + 1, Y_max + 1))
grid[1:-1, 1:-1] = five_point(grid, *np.meshgrid(np.arange(1,X_max), np.arange(1, Y_max)),
h, grid_x, grid_y)
However this is not pure "upstream" integration like the one you are doing, since you are essentialy calculating all the grid together in each step (your call!).
Probably a minimization routine could do better. There isn't much difference in performance between numpy and pure python for short loops or small vectors.

Related

Error 'RuntimeWarning: invalid value encountered in double_scalars' upon addition and multiplication of numpy array elements

I am trying to write an algorithm for simulating the steady flow in a windtunnel around a rectangle. I based my code heavily on the beam.py code in the book 'A survey of computational physics' by Landau.
The code in the book is the following:
import matplotlib.pylab as p;
from mpl_toolkits.mplot3d import Axes3D ;
from numpy import *;
import numpy;
print( "Working, look for figure window after 100 iterations")
Nxmax = 70; Nymax = 20; IL = 10; H = 8; T = 8; h = 1.
u = zeros( (Nxmax + 1, Nymax + 1), float) # Stream
w = zeros( (Nxmax + 1, Nymax + 1), float) # Vorticity
V0 = 1.0; omega = 0.1; nu = 1.; iter = 0; R = V0*h/nu # Renold #
def borders(): # Method borders: init & B.C
for i in range(0, Nxmax + 1): # Initialize stream function
for j in range(0, Nymax + 1 ): # And vorticity
w[i, j] = 0.
u[i, j] = j * V0
for i in range(0, Nxmax + 1 ): # Fluid surface
u[i, Nymax] = u[i, Nymax - 1] + V0 * h
w[i, Nymax - 1] = 0.
for j in range(0, Nymax + 1 ):
u[1, j] = u[0, j]
w[0, j] = 0. # Inlet
for i in range(0, Nxmax + 1 ): # Centerline
if i <= IL and i>= IL + T:
u[i, 0] = 0.
w[i, 0] = 0.
for j in range(1, Nymax ): # Outlet
w[Nxmax, j] = w[Nxmax - 1, j]
u[Nxmax, j] = u[Nxmax - 1, j] # Boundary conditions
def beam(): # Method beam; BC for beam
for j in range (0, H + 1): # Beam sides
w[IL, j] = - 2 * u[IL - 1, j]/(h*h) # Front side
w[IL + T, j] = - 2 * u[IL + T + 1, j]/(h*h) # Back side
for i in range(IL, IL + T + 1): w[i, H - 1] = - 2 * u[i, H]/(h*h);
for i in range(IL, IL + T + 1 ):
for j in range(0, H + 1):
u[IL, j] = 0. # Front
u[IL+T, j] = 0. # Back
u[i, H] = 0; # top
def relax(): # Method to relax stream
beam() # Reset conditions at beam
for i in range(1, Nxmax): # Relax stream function
for j in range (1, Nymax):
r1 = omega*((u[i+1,j]+u[i-1,j]+u[i,j+1]+u[i,j-1] + h*h*w[i,j])*0.25-u[i,j])
u[i, j] += r1
for i in range(1, Nxmax): # Relax vorticity
for j in range(1, Nymax):
a1 = w[i+1, j] + w[i-1,j] + w[i,j+1] + w[i,j-1]
a2 = (u[i,j+1] - u[i,j-1])*(w[i+1,j] - w[i - 1, j])
a3 = (u[i+1,j] - u[i-1,j])*(w[i,j+1] - w[i, j - 1])
r2 = omega *( (a1 - (R/4.)*(a2 - a3) )/4.0 - w[i,j])
w[i, j] += r2
borders()
while (iter <= 100):
iter += 1
if iter%10 == 0: print (iter)
relax()
for i in range (0, Nxmax + 1):
for j in range(0, Nymax + 1 ): u[i, j] = u[i, j]/(V0*h) # V0h units
x = range(0, Nxmax - 1); y = range(0, Nymax - 1) # returns stream flow to plot
# for several iterations
X, Y = p.meshgrid(x, y)
def functz(u): # Return transform
Z = u[X, Y]
return Z
Z = functz(u) # here the function is called
fig = p.figure() # creates the figure
ax = Axes3D(fig) # plots the axis for the figure
ax.plot_wireframe(X, Y, Z, color = 'r') # surface of wireframe in red
ax.set_xlabel('X') # label the three axes
ax.set_ylabel('Y')
ax.set_zlabel('Stream Function')
p.show()
I discovered that if I increase the value of V_0 above 2 or 3 then I get a RuntimeWarning: invalid value encountered in double_scalars error when r1 and r2 are calculated.
I want to simulate with much larger velocities and I couldn't find a way to fix this problem. I don't really understand why it is even an error when there are no divisions by really small numbers, close to 0.
Can anyone help me out and spot the problem?
Thanks in advance
I tried to look up the answer but only found special scipy libraries for certain operators but in this code none of them could be used.

Plot the multiple values returned by a function

My function returns 2 different values which I want to utilise in 2 different graphs using Matplotlib. How can I achieve it?
def option_value_european_put(T, m, r, sigma, mu, E):
cost_value_at_initial_t_put = []
portfolio_payoff_put = []
for e in E:
delta_t = T / m
u = (1 + (sigma * math.sqrt(delta_t)) * (math.sqrt(1 + ((mu ** 2) * delta_t) / math.pow(sigma, 2))))
v = 2 - u
option_stock_price_matrix_put = np.zeros((m + 1, m + 1))
sum = 0
k = m
start = m
for i in range(m + 1):
option_stock_price_matrix_put[i][start] = max(
(e - stock_price_binomial_model(
mu, sigma, T, m,
S
)[i][start], 0)
)
for j in range(m - 1, -1, -1):
for i in range(0, j + 1):
v_plus = option_stock_price_matrix_put[i][j + 1]
v_minus = option_stock_price_matrix_put[i + 1][j + 1]
v_t = ((((v_plus - v_minus) / (u - v)) * (1 + r * delta_t)) + (u * v_minus - v * v_plus) / (u - v)) / (
1 + r * delta_t)
option_stock_price_matrix_put[i][j] = v_t
cost_value_at_initial_t_put.append(option_stock_price_matrix_put[0][0])
for i in range(0, m+1):
sum = sum + option_stock_price_matrix_put[k][i]
portfolio_return_average = math.average(sum)
portfolio_payoff_put.append(portfolio_return_average-option_stock_price_matrix_put[0][0] )
return cost_value_at_initial_t_put, portfolio_payoff_put
I want to use cost_value_at_initial_t_put in 1 Matplotlib plot and the other value in another plot. How can I use it?
Supposing that cost_value_at_initial_t_put and portfolio_payoff_cut are both lists you can create subplots:
import matplotlib.pyplot as plt
fig, (ax_cost, ax_payoff) = plt.subplots(nrows=2)
ax_cost.plot(cost_value_at_initial_t_put)
ax_payoff.plot(portfolio_payoff_cut)

Speeding up custom adaptive smoothen filter with opencv in Python

I am new in Image Processing and trying to implement a custom filter I found in bibliography. I need to implement this in Python with opencv, but my implementation, though correct, is running really slowly. The algorithm can be described as follows:
Calculate normalized chromatic distances in a 3x3 neighborhood around a centre point
d_i = (|R_centre-R_i|+|G_centre-G_i|+|B_centre-B_i|)/(3x255) where centre point is the current pixel that iterates through whole picture, and i are the 8 points around it
Having calculated values d_0 through d_7 we calculate an array of 8 elements called c_i = (1-d_i)^10 which will be the values used in our convolution box
Our final convolution mask will be (1/sum(c))*([c_0, c_1, c_2; c_3, 0 , c_4; c_5, c_6, c_7]) so basically c calues will be around and the centre point will be 0, and this matrix will be multiplied with the 9 pixels in the original image.
Finally repeat the whole process for as many as iterations variables declares, usually around 10
For an image of size 677x450 it takes around 1 minute, which I think is too much. What could change to take better advantage of numpy's functionality, or anything else that would help speed it up?
As we can see from the photos the result is a lot more smooth, even after only 2 iterations
The code is as follows:
def adaptive_smoothening(img, iterations):
M, N, K = img.shape # M: HEIGHT, N:WIDTH, K:CHANNELS
F = np.zeros([M, N, K], dtype=np.uint8)
for p in range(iterations):
print("Iteration ", p + 1)
r = img[:, :, 2]
g = img[:, :, 1]
b = img[:, :, 0]
m, n = r.shape # m: height/channel , n: width/channel
c = np.zeros([8, 1])
d = np.zeros([8, 1])
for i in range(1, m):
for j in range(1, n):
# Creating Neighborhood Indices
ip = i + 1
im = i - 1
jm = j - 1
jp = j + 1
if im < 1:
im = i
elif ip > m - 1:
ip = i - 1
if jm < 1:
jm = j
elif jp > n - 1:
jp = j - 1
d[0] = (abs(int(r[i, j]) - int(r[i, jm])) + abs(int(g[i, j]) - int(g[i, jm])) + abs(
int(b[i, j] - int(b[i, jm])))) / (3 * 255)
d[1] = (abs(int(r[i, j]) - int(r[i, jp])) + abs(int(g[i, j]) - int(g[i, jp])) + abs(
int(b[i, j] - int(b[i, jp])))) / (3 * 255)
d[2] = (abs(int(r[i, j]) - int(r[ip, j])) + abs(int(g[i, j]) - int(g[ip, j])) + abs(
int(b[i, j] - int(b[ip, j])))) / (3 * 255)
d[3] = (abs(int(r[i, j]) - int(r[im, j])) + abs(int(g[i, j]) - int(g[im, j])) + abs(
int(b[i, j] - int(b[im, j])))) / (3 * 255)
d[4] = (abs(int(r[i, j]) - int(r[im, jm])) + abs(int(g[i, j]) - int(g[im, jm])) + abs(
int(b[i, j] - int(b[im, jm])))) / (3 * 255)
d[5] = (abs(int(r[i, j]) - int(r[ip, jm])) + abs(int(g[i, j]) - int(g[ip, jm])) + abs(
int(b[i, j] - int(b[ip, jm])))) / (3 * 255)
d[6] = (abs(int(r[i, j]) - int(r[ip, jp])) + abs(int(g[i, j]) - int(g[ip, jp])) + abs(
int(b[i, j] - int(b[ip, jp])))) / (3 * 255)
d[7] = (abs(int(r[i, j]) - int(r[im, jp])) + abs(int(g[i, j]) - int(g[im, jp])) + abs(
int(b[i, j] - int(b[im, jp])))) / (3 * 255)
c = [pow(1 - float(x), 10) for x in d]
sum2 = sum(c)
F[i, j, 2] = (1 / sum2) * (
c[0] * r[i, jm] + c[1] * r[i, jp] + c[2] * r[ip, j] + c[3] * r[im, j] + c[4] * r[im, jm] +
c[5] * r[ip, jm] + c[6] * r[ip, jp] + c[7] * r[im, jp])
F[i, j, 1] = (1 / sum2) * (
c[0] * g[i, jm] + c[1] * g[i, jp] + c[2] * g[ip, j] + c[3] * g[im, j] + c[4] * g[im, jm] +
c[5] * g[ip, jm] + c[6] * g[ip, jp] + c[7] * g[im, jp])
F[i, j, 0] = (1 / sum2) * (
c[0] * b[i, jm] + c[1] * b[i, jp] + c[2] * b[ip, j] + c[3] * b[im, j] + c[4] * b[im, jm] +
c[5] * b[ip, jm] + c[6] * b[ip, jp] + c[7] * b[im, jp])
img=F
return F
image = cv2.imread("HorVerTextDoc 001.jpg", 1 )
cv2.imshow("Original", resize(image, 0.5) )
result = adaptive_smoothening(resize(image,0.5),2)
cv2.imshow("Result",result)
You do not really have to slide over your images for your task. Instead, you can create all the shifted version of your image, and do your calculations over them. Basically, you will be doing the same calculations at one-shot instead of iterating through all the pixels, which takes some much time. Here is an approach:
import numpy as np
import cv2
import matplotlib.pyplot as plt
def adaptive_smooth(img):
img = np.pad(img,((1,1),(1,1),(0,0)),mode='reflect') # add some padding so translations do not make any problem
rows,cols = img.shape[:2]
F = np.zeros_like(img)
# Translations
img_lt = cv2.warpAffine(img,np.float32([[1,0,-1],[0,1,-1]]),(cols,rows)) # top left
img_t = cv2.warpAffine(img,np.float32([[1,0,0],[0,1,-1]]),(cols,rows)) # top
img_rt = cv2.warpAffine(img,np.float32([[1,0,1],[0,1,-1]]),(cols,rows)) # right top
img_l = cv2.warpAffine(img,np.float32([[1,0,-1],[0,1,0]]),(cols,rows)) # left
img_r = cv2.warpAffine(img,np.float32([[1,0,1],[0,1,0]]),(cols,rows)) # right
img_lb = cv2.warpAffine(img,np.float32([[1,0,-1],[0,1,1]]),(cols,rows)) # left bottom
img_b = cv2.warpAffine(img,np.float32([[1,0,0],[0,1,1]]),(cols,rows)) # bottom
img_rb = cv2.warpAffine(img,np.float32([[1,0,1],[0,1,1]]),(cols,rows)) # right bottom
c_0 = (1-(np.sum(np.abs(img-img_lt),axis=2)/(255*3)))**10
c_1 = (1-(np.sum(np.abs(img-img_t),axis=2)/(255*3)))**10
c_2 = (1-(np.sum(np.abs(img-img_rt),axis=2)/(255*3)))**10
c_3 = (1-(np.sum(np.abs(img-img_l),axis=2)/(255*3)))**10
c_4 = (1-(np.sum(np.abs(img-img_r),axis=2)/(255*3)))**10
c_5 = (1-(np.sum(np.abs(img-img_lb),axis=2)/(255*3)))**10
c_6 = (1-(np.sum(np.abs(img-img_b),axis=2)/(255*3)))**10
c_7 = (1-(np.sum(np.abs(img-img_rb),axis=2)/(255*3)))**10
# fig,ax=plt.subplots(1,2,sharex=True,sharey=True)
# ax[0].imshow(c_0,cmap='gray')
# ax[1].imshow(c_7,cmap='gray')
# plt.show()
sum2 = c_0 + c_1 + c_2 + c_3 + c_4 + c_5 + c_6 + c_7
F = (1/np.dstack((sum2,sum2,sum2)))*(img_lt*np.dstack((c_0,c_0,c_0))+
img_t*np.dstack((c_1,c_1,c_1))+
img_rt*np.dstack((c_2,c_2,c_2))+
img_l*np.dstack((c_3,c_3,c_3))+
img_r*np.dstack((c_4,c_4,c_4))+
img_lb*np.dstack((c_5,c_5,c_5))+
img_b*np.dstack((c_6,c_6,c_6))+
img_rb*np.dstack((c_7,c_7,c_7)))
return F[1:-1,1:-1,:] # remove padding
img = cv2.cvtColor(cv2.imread("my_image.jpg", 1),cv2.COLOR_BGR2RGB)
F = adaptive_smooth(img) # 1st iteration
for _ in range(3): # 3 more iterations
F = adaptive_smooth(F)
F[:,:,0] = F[:,:,0]/F[:,:,0].max() # Normalize channels
F[:,:,1] = F[:,:,1]/F[:,:,1].max()
F[:,:,2] = F[:,:,2]/F[:,:,2].max()
fig,ax=plt.subplots(1,2,sharex=True,sharey=True)
ax[0].imshow(img,cmap='gray')
ax[1].imshow((F*255).astype(int),cmap='gray')
plt.show()
This image is smoothed 4 times. I hope the code is self explanatory.
EDIT: There we go. I made a mistake with sum2 in the previous version. Also I removed the data type switches in your solution, e.g., int to float at the arrays c and d. Please add them in case this is necessary for your solution.

Applying Modified Euler to solve a Pendulum ODE in Python

So I am trying to implement some numerical methods into python and I am having some issues where all of my functions output more or less the same thing as the regular euler method. I assume this is because I am messing up in some way when I am implementing the method into code.
My pendulum is defined as this:
def func(y,t):
### Simplified the Function to remove friction since it cancelled out
x,v = y[:3],y[3:6]
grav = np.array([0., 0., -9.8 ])
lambd = (grav.dot(x)+v.dot(v))/x.dot(x)
return np.concatenate([v, grav - lambd*x] )
def dF_matrix(y):
n=y.size
dF=np.zeros((6,6))
xp=np.array([y[1],y[2],y[3]])[np.newaxis]
mass=1.
F1=0.
F2=0.
F3=-mass*9.8
F=np.array([F1,F2,F3])[np.newaxis]
phix=2.*y[0]
phiy=2.*y[4]
phiz=2.*y[5]
G=np.array([phix,phiy,phiz])[np.newaxis]
H=2.*np.eye(3)
lambd=(mass*np.dot(xp,np.dot(H,xp.T))+np.dot(F,G.T))/np.dot(G,G.T)
dF[0,3]=1
dF[1,4]=1
dF[2,5]=1
dF[3,0]=(y[0]*F1+2*lambd)/mass
dF[3,1]=(y[0]*F2)/mass
dF[3,2]=(y[0]*F3)/mass
dF[3,3]=phix*y[1]
dF[3,4]=phix*y[2]
dF[3,5]=phix*y[3]
dF[4,0]=(y[4]*F1)/mass
dF[4,1]=(y[4]*F2+2*lambd)/mass
dF[4,2]=(y[4]*F3)/mass
dF[4,3]=phiy*y[1]
dF[4,4]=phiy*y[2]
dF[4,5]=phiy*y[3]
dF[5,0]=(y[5]*F1)/mass
dF[5,1]=(y[5]*F2)/mass
dF[5,2]=(y[5]*F3+2*lambd)/mass
dF[5,3]=phiz*y[1]
dF[5,4]=phiz*y[2]
dF[5,5]=phiz*y[3]
return dF
The functions that I have made to integrate the ODE function are as follows (with help from others in previous a thread):
from scipy.integrate import odeint
from mpl_toolkits import mplot3d
import matplotlib.pyplot as plt
Forward Euler Method
def forward_Euler(function, y_matrix, time):
y = np.zeros((np.size(time), np.size(y_matrix)))
y[0, :] = y_matrix
for i in range(len(time) - 1):
dt = time[i + 1] - time[i]
y[i + 1, :] = y[i, :] + np.asarray(function(y[i, :], time[i])) * dt
return y
Modified Euler Method
ERROR STARTS HERE
The error I am getting is:
RuntimeWarning: invalid value encountered in double_scalars
lambd = (grav.dot(x)+v.dot(v))/x.dot(x)
def modified_Euler(function, y_matrix, time):
y = np.zeros((np.size(time), np.size(y_matrix))) # creates the matrix that we will fill
y[0, :] = y_matrix # sets the initial values of the matrix
for i in range(len(time) - 1): # apply the Euler
dt = time[i + 1] - time[i] # Step size
k1 = np.asarray(function(y[i, :], time[i])*dt)
k2 = np.asarray(function(y[i] + k1, time[i+1])*dt)
y[i + 1, :] = y[i, :] + .5 * (k1 + k2)
return y
Adams-Bashforth 2nd order
def Adams_Bash_2nd(function, y_matrix, time):
y = np.zeros((np.size(time), np.size(y_matrix)))
y[0, :] = y_matrix
dt = time[1] - time[0]
f_0 = function(y[0], time[0])
y[1] = y[0] + dt * f_0
y[1] = y[0] + 0.5*dt * (f_0+function(y[1], time[1]))
for i in range(len(time) - 1):
dt = time[i + 1] - time[i]
f_1 = function(y[i, :], time[i])
f_2 = function(f_1-1, time[i-1])
y[i + 1] = y[i] + 0.5 * dt * (3 * f_1 - f_2)
return y
Adams Bashforth Moulton Method
def Adams_Moulton(function, y_matrix, time):
y = np.zeros((np.size(time), np.size(y_matrix)))
y[0, :] = y_matrix
### predictor formula
for i in range(len(time) - 1):
dt = time[i + 1] - time[i]
f_1 = function(y[i, :], time[i])
f_2 = function(f_1-1, time[i-1])
y[i + 1, :] = y[i, :] + dt * f_1 + ((dt**2)/2) * f_2
### Corrector formula
for i in range(len(time) - 1):
dt = time[i + 1] - time[i]
k_1 = 9 * (function(y[i, :], time[i+1]))
k_2 = 19 * (function(y[i, :], time[i]))
k_3 = 5 * (function(y[i, :], time[i-1]))
k_4 = (function(y[i, :], time[i-2]))
y[i + 1, :] = y[i] + (dt/24) * (k_1 + k_2 - k_3 + k_4)
return y
RK4 step to use in next function
def RK4_step(f,y,t,dt, N=1):
dt /= N;
for k in range(N):
k1=f(y,t)*dt; k2=f(y+k1/2,t+dt/2)*dt; k3=f(y+k2/2,t+dt/2)*dt; k4=f(y+k3,t+dt)*dt;
y, t = y+(k1+2*(k2+k3)+k4)/6, t+dt
return y
Adams-Bashforth Moulton Method 4th order
def Adams_Moulton_4th(function, y_matrix, time):
y = np.zeros((np.size(time), np.size(y_matrix)))
y[0] = y_matrix
### bootstrap steps with 4th order one-step method
dt = time[4] - time[0]
y[4] = RK4_step(function, y[0], time[0], dt, N=4)
y[5] = RK4_step(function, y[4], time[4], dt, N=4)
y[1] = RK4_step(function, y[5], time[5], dt, N=4)
f_m2 = function(y[0], time[0])
f_m1 = function(y[4], time[4])
f_0 = function(y[5], time[5])
f_1 = function(y[1], time[1])
for i in range(3, len(time) - 1):
### predictor formula 4th order [ 55/24, -59/24, 37/24, -3/8 ]
f_m3, f_m2, f_m1, f_0 = f_m2, f_m1, f_0, f_1
y[i + 1] = y[i] + (dt / 24) * (55 * f_0 - 59 * f_m1 + 37 * f_m2 - 9 * f_m3)
f_1 = function(y[i + 1], time[i + 1])
### Corrector formula 4th order [ 3/8, 19/24, -5/24, 1/24 ]
y[i + 1] = y[i] + (dt / 24) * (9 * f_1 + 19 * f_0 - 5 * f_m1 + f_m2)
f_1 = function(y[i + 1], time[i + 1])
return y
I decided to program the way I am testing the functions into a with a function eliminating a good amount of lines from the previous iteration
# initial condition
y0 = np.array([0.0, 1.0, 0.0, 0.8, 0.0, 1.2])
def test_function(test_function):
print(test_function.__name__ + "...")
nt = 2500
time_start = process_time()
# time points
t = np.linspace(0, 25, nt)
# solve ODE
y1 = test_function(func, y0, t)
time_elapsed = (process_time() - time_start)
print('elapsed time', time_elapsed)
# compute residual:
r = y1[:, 0] ** 2 + y1[:, 1] ** 2 + y1[:, 2] ** 2 - 1
rmax1 = np.max(np.abs(r))
print('error', rmax1)
fig = plt.figure()
ax = plt.axes(projection='3d')
ax.plot3D(y1[:, 0], y1[:, 1], y1[:, 2], 'gray')
plt.show()
test_function(odeint)
test_function(forward_Euler)
test_function(modified_Euler)
test_function(Adams_Bash_2nd)
test_function(Adams_Moulton)
test_function(Adams_Moulton_4th)
The modified Euler method Does Not access points outside the step i -> i+1, there is no i-1 (note that in your source document the step, in the python code, not the formulas, is i-1 -> i with the loops starting at an appropriately increased index). It simply is (as you can find everywhere the mod. Euler or Heun method is discussed)
k1 = f(y[i] , t[i ])*dt;
k2 = f(y[i]+k1, t[i+1])*dt;
y[i+1] = y[i] + 0.5*(k1+k2);
In contrast, the Adams-Bashford method of order 2 and Adams-Moulton methods of order greater 2 Do access points from before the step i -> i+1, formally one has in AB2
y[i+1] = y[i] + 0.5*dt * (3*f[i] - f[i-1])
For a first implementation it would make sense to declare the f array the same way as the y array to implement this formula verbatim. It can be more economical to only keep a short array of f values that is shifted or rotated to give access to the last few f values.
Note that you need to initialize y[1] and f[1] with some other method of similar or higher order. Or if you want to have a "pure" run of the method, you need to initialize y[-1] and f[-1] and further back so that y[1] can be computed with the method formula.

Hermite interpolation in Python

I have this program for calculating Hermite interpolation.
Problem is, that its behave really bad.
This is chart for 35 Chebyshev nodes. If I put more points, peak on the beginning will be higher(its about 10^7 with this amount of nodes).
I interpolated this same function using Lagrange method (green, its shifted so it can be seen) and as you can see it looks fine.
Here is the code:
def hermit_interpolate(input): #input is list of tuples [(x1,y1),(x2,y2)...] xi are Chebyshev nodes
points = [(input[0][0], input[0][1] - 0), (input[0][0], calculate_f_p_x(input[0][0]))] #"input[0][1] - 0" this is just to change type of second element
#calculate_f_p_x returns value of derivative
for k in range(1, len(input)): #Divided differences and derivatives in one list alternately
points.append((input[k][0], (input[k][1] - input[k - 1][1]) / (
input[k][0] - input[k - 1][0])))
points.append((input[k][0], calculate_f_p_x(input[k][0])))
x, c = zip(*points)
x = list(x)
c = list(c)
n = len(points)
for i in range(2, n): #calculating factors
for j in range(n - 1, i - 1, -1):
c[j] = (c[j] - c[j - 1]) / (x[j] - x[j - i])
def result_polynomial(xpoint): #here is function to calculate value for given x
val = c[0]
factor = 1.0
for l in range(1, n):
factor *= (xpoint - x[l - 1])
val += (c[l] * factor)
return val
return result_polynomial
I can't seen what's wrong here.
Thanks!
This code actually works:
def hermit_interpolate(input): #input is list of tuples [(x1,y1),(x2,y2),...,(xn,yn)] xi are Chebyshev nodes
n = len(input)
points = numpy.zeros(shape=(2 * n + 1, 2 * n + 1))
X, Y = zip(*input)
X = list(X)
Y = list(Y)
for i in range(0, 2 * n, 2):
points[i][0] = X[i / 2]
points[i + 1][0] = X[i / 2]
points[i][1] = Y[i / 2]
points[i + 1][1] = Y[i / 2]
for i in range(2, 2 * n + 1):
for j in range(1 + (i - 2), 2 * n):
if i == 2 and j % 2 == 1:
points[j][i] = calculate_f_p_x(X[j / 2]);
else:
points[j][i] = (points[j][i - 1] - points[j - 1][i - 1]) / (
points[j][0] - points[(j - 1) - (i - 2)][0])
def result_polynomial(xpoint): #here is function to calculate value for given x
val = 0
for i in range(0, 2 * n):
factor = 1.
j = 0
while j < i:
factor *= (xpoint - X[j / 2])
if j + 1 != i:
factor *= (xpoint - X[j / 2])
j += 1
j += 1
val += factor * points[i][i + 1]
return val
return result_polynomia

Categories

Resources