I’m trying to solve a n-Body problem using hamilton equations of motion; here is what I’v done:
1.) First I define random initial values for momentums and the vectors positions:
import numpy as np
import matplotlib.pyplot as plt
from scipy import integrate
n= 3 # of bodys
G = 1 # constant
m = np.random.random_sample((1,n)) # random values for masses
#----- Random Values for momentums and positions
P_0 = np.random.random_sample(size=(3,n)) # each column of the matrix represents one vector
R_0= np.random.random_sample(size=(3,n))
Y_0 = np.array([P_0,R_0])
2.) then I write the hamilton equations for a k-th particle:
# Hamilton equations:
p_kDot = lambda t,r_k,r_j,m_k,m_j: G*(m_k*m_j/np.linalg.norm(r_k-r_j)**3)*np.subtract(r_k-r_j) # Equation for the momentum
r_kDot = lambda t,p_k,m_k: (1/m_k)*p_k # Equation for the vectors positions.
3.)Then I sum over all the particles and define the function U, which is gonna be pass to
scipy.integrate.solve_ivp:
def U(t,Y,m):
partial_Ham_r = np.array( [p_kDot(t,Y[1][k],Y[1][j],m[k],m[j]) for k in range(0,len(Y[1]))
for j in range(0,k)] )
partial_Ham_p = np.array( [r_kDot(t,Y[0][i],m[i]) for i in range(0,len(Y[0]))] )
return (partial_Ham_r,partial_Ham_p)
4.) Call the scipy integrator, but, as documentation say, Y_0 must be a vector, but i dont find a way to express my initial conditions as a n-dimensional vector!:
Sol = scipy.integrate.solve_ivp(U,t_span=
[0,10],y0=Y_0,args=m,dense_output=True,vectorized=True)
obviously outcomes the an error saying that Y_0 must be 1-Dimensional.
Is there a way to express Y_0 so that the code works? or should I express the position and moment vectors in 1 dimensional arrays?
I do not know if you care at all, but just like you I took upon the task to write vectorized version of a "many-body" dynamics and wrote a couple of versions. In case you might be interested, I will share what I wrote. I tested it on the two body problem and tried to even make some rudimentary animation.
'''
vectorized many body
'''
import math
import numpy as np
from scipy.integrate import solve_ivp
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
'''
vectorization of the gravitational acceleration field
'''
def G_accel(positions, mass):
q = positions.reshape((int(len(positions)/dim), dim))
q = q.T
Dq = - q[:, :, np.newaxis] + q[:, np.newaxis, :]
D = np.linalg.norm(Dq, axis = 0) + np.identity(Dq.shape[1])
D = 1/D
D = D - np.identity(D.shape[1])
D = mass[:, np.newaxis].dot(mass[np.newaxis, :]) * D**3 # column times row times elemntwise
Dq = D * Dq
Dq = Dq.sum(axis=-1) / mass[np.newaxis,:]
Dq = Dq.T
return Dq.reshape((1, q.shape[0]*q.shape[1]))[0]
'''
sum the matrix along the last dimension, i.e. a tensor contraction along the third index
'''
'''
vectorized right-hand side of the Newtonian ODEs
'''
def f(t, y, masses):
n = int(len(y)/2)
positions = y[0:n]
velocities = y[n:]
return np.concatenate( ( velocities, G_accel(positions, masses) ))
'''
vectorized set up for the initial values, so that momentum is conserved and it is 0
'''
def initialize(positions, velocities, masses):
momenta = masses[:, np.newaxis] * velocities
velocities[-1,:] = - np.sum(momenta[0:-1, :], axis=0) / masses[-1]
n3 = positions.shape[0]*positions.shape[1]
pos = positions.reshape((1, n3))[0]
vel = velocities.reshape((1, n3))[0]
return np.concatenate((pos, vel))
'''
Test with a two-body version in the x,y plane and z = 0 component
'''
dim = 3
masses = np.array([2, 1])
q = np.array([[ -1, 0, 0],
[1.3, 0, 0]])
v = np.array([[0, 0.5, 0],
[0, 0, 0]])
n_particles = q.shape[0]
t_start = 0
t_step = 0.1
n_steps = 5000
t_stop = n_steps * t_step
t_nodes = np.linspace(t_start, t_stop, n_steps)
y_initial = initialize(q, v, masses)
solution = solve_ivp(fun = lambda t, y : f(t, y, masses),
t_span=[t_start, t_stop],
y0=y_initial,
t_eval=t_nodes,
method='Radau')
pos = solution.y
pos = pos[0: int(pos.shape[0]/2)]
'''
animation plot of time-evolution:
'''
plt.style.use('seaborn-whitegrid')
fig = plt.figure()
ax = plt.axes()
ax.set_xlim(-10, 10)
ax.set_ylim(-10, 10)
line = np.empty(n_particles, dtype=object)
line[0], = ax.plot(pos[0, 0], pos[1, 0])
line[1], = ax.plot(pos[3, 0], pos[4, 0])
def animate(i):
'''
update plot
'''
line[0].set_xdata(pos[0, 0:i])
line[0].set_ydata(pos[1, 0:i])
line[1].set_xdata(pos[3, 0:i])
line[1].set_ydata(pos[4, 0:i])
return line
anim = FuncAnimation(fig, animate, frames=2000, interval=10)
ax.set_aspect( 1 )
plt.show()
I think I solved it, the code works when the initial conditions are such that there are no collisions between the particles (in which case the algorithm diverges):
import numpy as np
import matplotlib.pyplot as plt
from scipy import integrate
n= 3 # of bodys
G = 1 # constant
#m = np.random.randint(low=1,high=100,size=(n,)) # random values for masses
m = [1,1,1]
#----- Random Values for momentums and positions
P_0 = np.random.random_sample(size=(3,n)) # each column of the matrix represents
one vector
#R_0= np.random.random_sample(size=(3,n))
R_0= np.array([[0,0,0],[0,1,0],[0,1,0.96]]
Y_0 = np.concatenate((P_0,R_0),axis=1)
#----------------------------------------------
# Hamilton equations:
r_kDot = lambda t,p_k,m_k: (1/m_k)*p_k
p_kDot = lambda t,r_k,r_j,m_k,m_j: -G*(m_k*m_j/np.linalg.norm(r_k-
r_j)**3)*(r_k-
r_j)
def U(t,Y):
Y = Y.reshape(3,2*n)
Y = [Y[:,:n],Y[:,n:]]
partial_Ham_p = np.array( [r_kDot(t,Y[0][i],m[i]) for i in
range(0,len(Y[0]))] )
partial_Ham_r = np.array( [p_kDot(t,Y[1][k],Y[1][j],m[k],m[j]) for k in
range(0,len(Y[1])) for j in range(0,k)] )
return np.concatenate((partial_Ham_r,partial_Ham_p),axis=1)
I just reshaped the Y arg into the U function for have the sime shape as the Y_0 concatenated matrix, a minus sign was missing as someone commented
#%matplotlib notebook
Sol = integrate.solve_ivp(U,y0=Y_0.flatten(),t_span=
[0,10],dense_output=True,vectorized=True,t_eval=np.linspace(0,10,9999))
print (np.shape(Sol.y))
print (Sol.y)
r_vectors = Sol.y[9:,:]
fig , axes = plt.subplots(subplot_kw={ "projection":"3d" })
axes.plot(r_vectors[6,:],r_vectors [7,:],r_vectors [8,:])
axes.plot(r_vectors[0,:],r_vectors [1,:],r_vectors [2,:])
axes.plot(r_vectors[3,:],r_vectors [4,:],r_vectors [5,:])
I also generalized it to n bodies:
m = np.random.randint(low=1,high=100,size=(n,)) # random values for masses
m = np.ones((n,))
m[0]=2
#----- Random Values for momentums and positions
P_0 = np.random.random_sample(size=(3,n)) # each column of the matrix represents
one vector
R_0= np.random.random_sample(size=(3,n))
Y_0 = np.concatenate((P_0,R_0),axis=1)
Sol = integrate.solve_ivp(U,y0=Y_0.flatten(),t_span=
[0,10],dense_output=True,vectorized=True,t_eval=np.linspace(0,10,9999))
TrayX = Sol.y[3*n::3,:]
TrayY = Sol.y[3*n+1::3,:]
TrayZ = Sol.y[3*n+2::3,:]
print(np.shape(TrayX))
print(np.shape(TrayY))
print(np.shape(TrayZ))
fig , axes = plt.subplots(subplot_kw={ "projection":"3d" })
for j in range(n):
axes.plot(TrayX[j],TrayY[j],TrayZ[j])
perhaps some of you may know how to prevent the code from failing when there are particle collisions ...
Overall, I want to calculate a fourier transform of a given data set and filter out some of the frequencies with the biggest absolute values. So:
1) Given a data array D with accompanying times t, 2) find the k biggest fourier coefficients and 3) remove those coefficients from the data, in order to filter out certain signals from the original data.
Something goes wrong in the end when plotting the filtered data set over the given times. I'm not exactly sure, where the error is. The final 'filtered data' plot doesn't look even slightly smoothened and somehow changes its position compared with the original data. Is my code completely bad?
Part 1):
n = 1000
limit_low = 0
limit_high = 0.48
D = np.random.normal(0, 0.5, n) + np.abs(np.random.normal(0, 2, n) * np.sin(np.linspace(0, 3*np.pi, n))) + np.sin(np.linspace(0, 5*np.pi, n))**2 + np.sin(np.linspace(1, 6*np.pi, n))**2
scaling = (limit_high - limit_low) / (max(D) - min(D))
D = D * scaling
D = D + (limit_low - min(D)) # given data
t = linspace(0,D.size-1,D.size) # times
Part 2):
from numpy import linspace
import numpy as np
from scipy import fft, ifft
D_f = fft.fft(D) # fft of D-dataset
#---extract the k biggest coefficients out of D_f ---
k = 15
I, bigvals = [], []
for i in np.argsort(-D_f):
if D_f[i] not in bigvals:
bigvals.append(D_f[i])
I.append(i)
if len(I) == k:
break
bigcofs = np.zeros(len(D_f))
bigcofs[I] = D_f[I] # array with only zeros in in except for the k maximal coefficients
Part 3):
D_filter = fft.ifft(bigcofs)
D_new = D - D_filter
p1=plt.plot(t,D,'r')
p2=plt.plot(t,D_new,'b');
plt.legend((p1[0], p2[0]), ('original data', 'filtered data'))
I appreciate your help, thanks in advance.
There were two issues I noticed:
you likely want the components with largest absolute value, so np.argsort(-np.abs(D_f)) instead of np.argsort(-D_f).
More subtly: bigcofs = np.zeros(len(D_f)) is of type float64 and was discarding the imaginary part at the line bigcofs[I] = D_f[I]. You can fix this with bigcofs = np.zeros(len(D_f), dtype=complex)
I've improved your code slightly below to get desired results:
import numpy as np
from scipy import fft, ifft
import matplotlib.pyplot as plt
n = 1000
limit_low = 0
limit_high = 0.48
N_THRESH = 10
D = 0.5*np.random.normal(0, 0.5, n) + 0.5*np.abs(np.random.normal(0, 2, n) * np.sin(np.linspace(0, 3*np.pi, n))) + np.sin(np.linspace(0, 5*np.pi, n))**2 + np.sin(np.linspace(1, 6*np.pi, n))**2
scaling = (limit_high - limit_low) / (max(D) - min(D))
D = D * scaling
D = D + (limit_low - min(D)) # given data
t = np.linspace(0,D.size-1,D.size) # times
# transformed data
D_fft = fft.fft(D)
# Create boolean mask for N largest indices
idx_sorted = np.argsort(-np.abs(D_fft))
idx = idx_sorted[0:N_THRESH]
mask = np.zeros(D_fft.shape).astype(bool)
mask[idx] = True
# Split fft above, below N_THRESH points:
D_below = D_fft.copy()
D_below[mask] = 0
D_above = D_fft.copy()
D_above[~mask] = 0
#inverse separated functions
D_above = fft.ifft(D_above)
D_below = fft.ifft(D_below)
# plot
plt.ion()
f, (ax1, ax2, ax3) = plt.subplots(3,1)
l1, = ax1.plot(t, D, c="r", label="original")
l2, = ax2.plot(t, D_above, c="g", label="top {} coeff. signal".format(N_THRESH))
l3, = ax3.plot(t, D_below, c="b", label="remaining signal")
f.legend(handles=[l1,l2,l3])
plt.show()
So i was trying this and I find it really unfeasible. I am not that aware about smart ways to do the following. Can somebody help ? Also inputs of lists are quite big.
This task was to create an image from the values generated by me.
center_star contains list of [x,y] pairs which are centers of various point like objects.
1800 value represents that image to be generated is of 1800x1800 pixel.
Sigma variable has value 2 by default.
final=[[0]*1800]*1800
for i in range(len(center_stars)):
xi=center_stars[i][0]
yi=center_stars[i][1]
print(i)
for j in range(1800):
for k in range(1800):
final[j][k]+=gauss_amplitude[i]*(math.e**((-1*((xi-j)**2+(yi-k)**2))/2*sigma*sigma))
Is there a smarter way to save time using some of the numpy operation and execute this piece of code in less time?
Something like that:
import math
import numpy as np
N = 1800
final = np.empty((N, N))
final1 = np.empty((N, N))
j = np.arange(N)
k = np.arange(N)
jj, kk = np.meshgrid(j, k)
sigma = 2.
s = 0.5 / (sigma * sigma)
for i in range(len(center_stars)):
xi = center_stars[i][0]
yi = center_stars[i][1]
final += gauss_amplitude[i] * np.exp(- ((xi - jj.T)**2 + (yi - kk.T)**2) * s)
# Code below is for comparison:
for j in range(N):
for k in range(N):
final1[j][k]+=gauss_amplitude[i] * (math.e** (-((xi-j)**2+(yi-k)**2)/(2*sigma*sigma)))
Besides, I assume you missed brackets around 2*sigma*sigma
you can try to compact your code like this:
Gauss=lambda i,j,k,xi,yi:gauss_amplitude[i]*(math.e**((-((xi-j)**2+(yi-k)**2))/(2*sigma*sigma)))
final=[[Gauss(i,j,k,x[0],x[1]) for j in range(1800) for k in range(1800)] for i,x in enumerate(center_starts)]
If your sigmas are all the same, you can achieve this with out any loop by using
scipy.signal.convolve2d.
import numpy as np
import matplotlib.pyplot as plt
from scipy.signal import convolve2d
from scipy.stats import multivariate_normal
sigma = 3
width = 200 # smaller than yours so you can see the single pixels
n_stars = 50
# draw some random stars
star_row = np.random.randint(0, width, n_stars)
star_col = np.random.randint(0, width, n_stars)
star_amplitude = np.random.normal(50, 10, n_stars)
# assign amplitudes to center pixel of stars
amplitudes = np.zeros((width, width))
amplitudes[star_row, star_col] = star_amplitude
# create 2d gaussian kernel
row = col = np.arange(-4 * sigma, 4 * sigma + 1)
grid = np.stack(np.meshgrid(row, col)).T
kernel = multivariate_normal(
[0, 0],
[[sigma**2, 0], [0, sigma**2]]
).pdf(grid)
kernel /= kernel.sum()
# convolve with 2d gaussian
final = convolve2d(amplitudes, kernel, mode='same')
fig, (ax1, ax2, ax3) = plt.subplots(1, 3, figsize=(9, 3))
img = ax1.imshow(amplitudes)
fig.colorbar(img, ax=ax1)
ax1.set_label('Before Convolution')
img = ax2.imshow(kernel)
fig.colorbar(img, ax=ax2)
ax2.set_label('Convolution Kernel')
img = ax3.imshow(final)
fig.colorbar(img, ax=ax3)
ax3.set_label('After Convolution')
fig.tight_layout()
fig.savefig('conv2d.png', dpi=300)
Result:
If the sigmas differ, you get a way with a single loop over the possible sigmas.
I'm attempting to implement the frequency domain ReLu as detailed in: http://cs231n.stanford.edu/reports/2015/pdfs/tema8_final.pdf
The formula that is confusing me is on the bottom left of page 4. I am not confident that I am computing the sum of the FFT of the dirac function properly. Am I interpreting this formula incorrectly?
import numpy as np
import matplotlib.pyplot as plt
img = np.array([[-1.0,2.3],[5,7.8]])
# Dirac is essentially the shifting matrix
# So create the 2d shifting matrix values
N = 2
x = np.arange(0, N, 1)
y = np.arange(0, N, 1)
xm, ym = np.meshgrid(x, y)
shiftMat = np.exp(1j * ((2.0 * np.pi)) * (xm + ym))
# Set equal to shift mat
# In this trivial example I know that [0,0] is only negative position
# So set to 0 and compute sum of all positions in which f(x) > 0 as detailed in paper
freqRelu = shiftMat
freqRelu[0,0] = 0
freqRelu = np.sum(freqRelu)
# Fourier Convolution and IFFT
imgFFT = np.fft.fft2(img)
freqR = np.multiply(imgFFT,freqRelu)
reluedFreq = np.real(np.fft.ifft2(freqR))
# Spatial Relu For Comparision
reluedImg = img
reluedImg[0,0] = 0
plt.subplot(121)
plt.imshow(reluedImg)
plt.subplot(122)
plt.imshow(reluedFreq)
plt.show()
print(np.allclose(reluedFreq,reluedImg))
print(reluedFreq)
print(reluedImg)
For reference this question was answered on signal stack exchange here: https://dsp.stackexchange.com/questions/49023/sum-of-diracs-in-frequency-domain
linspace generates a linear space. How can I generate a grid using an arbitrary density function?
Say, I would like to have a grid from 0 to 1, with 100 grid points, and where the density of points is given by (x - 0.5)**2 - how would I create such a grid in Python?
That is, I want many grid-points where the function (x - 0.5)**2) is large, and few points where the function is small. I do not want a grid that has values according to this function.
For example like this:
x = (np.linspace(0.5,1.5,100)-0.5)**2
The start and end values have to be chosen so that f(start) = 0 and f(end)=1.
In that case the following solution should work. Be sure that func is positive throughout the range...
import numpy as np
from matplotlib import pyplot as plt
def func(x):
return (x-0.5)**2
start = 0
end = 1
npoints = 100
x = np.linspace(start,end,npoints)
fx = func(x)
# take density (or intervals) as inverse of fx
# g in [0,1] controls how much warping you want.
# g = 0: fully warped
# g = 1: linearly spaced
g = 0
density = (1+g*(fx-1))/fx
# sum the intervals to get new grid
x_density = np.cumsum(density)
# rescale to match old range
x_density -= x_density.min()
x_density/= x_density.max()
x_density *= (end-start)
x_density += start
fx_density = func(x_density)
plt.plot(x,fx,'ok',ms = 10,label = 'linear')
plt.plot(x_density,fx_density,'or',ms = 10,label = 'warped')
plt.legend(loc = 'upper center')
plt.show()