Plot orbit of three bodies using rk4 - python

I have been trying to plot the trajectories of three particles using the RK4 method. I haven't been able to produce an array of results over the time period as it brings up the following error message:
File "C:\Users\Local\Runge-Kutta 4 Code.py", line 65, in <module>
solution.step()
File "F:\Anaconda3\lib\site-packages\scipy\integrate\_ivp\base.py", line 170, in step
raise RuntimeError("Attempt to step on a failed or finished "
RuntimeError: Attempt to step on a failed or finished solver.
I suspect that there is a problem with the initial "y_0" value that I have but I could be wrong.
Any help at all would be greatly appreciated. My code is as follows:
import numpy as np
import matplotlib.pyplot as plt
import scipy as scipy
from scipy import integrate
from numpy import asarray
from numpy import savetxt
# Physical constants
mass_vector = np.array([1, 1, 1])
r_vec_1 = np.array([0, 0])
v_vec_1 = np.array([-np.sqrt(2), -np.sqrt(2)])
r_vec_2 = np.array([-1, 0])
v_vec_2 = np.array([np.sqrt(2) / 2, np.sqrt(2) / 2])
r_vec_3 = np.array([1, 0])
v_vec_3 = np.array([np.sqrt(2) / 2, np.sqrt(2) / 2])
#Initial x acceleration ODE's
def x1_double_dot(y, mass_vector):
#G can be omitted for scale purposes (should not be compared with realistic data)
return ((mass_vector[1]*(y[4]-y[0])/((y[0]-y[4])**2 + (y[1]-y[5])**2)**(3/2)) +
(mass_vector[2]*(y[8]-y[0])/((y[0]-y[8])**2 + (y[1]-y[9])**2)**(3/2)))
def x2_double_dot(y, mass_vector):
return ((mass_vector[0]*(y[0]-y[4])/((y[4]-y[0])**2 + (y[5]-y[1])**2)**(3/2)) +
(mass_vector[2]*(y[8]-y[4])/((y[4]-y[8])**2 + (y[5]-y[9])**2)**(3/2)))
def x3_double_dot(y, mass_vector):
return ((mass_vector[0]*(y[0]-y[8])/((y[8]-y[0])**2 + (y[9]-y[1])**2)**(3/2)) +
(mass_vector[1]*(y[4]-y[8])/((y[8]-y[4])**2 + (y[9]-y[5])**2)**(3/2)))
#Initial y acceleration ODE's
def y1_double_dot(y, mass_vector):
return ((mass_vector[1]*(y[5]-y[1])/((y[0]-y[4])**2 + (y[1]-y[5]**2)**(3/2))) +
(mass_vector[2]*(y[9]-y[1])/((y[0]-y[8])**2 + (y[1]-y[9])**2)**(3/2)))
def y2_double_dot(y, mass_vector):
return ((mass_vector[0]*(y[1]-y[5])/((y[4]-y[0])**2 + (y[5]-y[1]**2)**(3/2))) +
(mass_vector[2]*(y[9]-y[5])/((y[4]-y[8])**2 + (y[5]-y[9])**2)**(3/2)))
def y3_double_dot(y, mass_vector):
return ((mass_vector[0]*(y[1]-y[9])/((y[8]-y[0])**2 + (y[9]-y[1]**2)**(3/2))) +
(mass_vector[1]*(y[5]-y[9])/((y[8]-y[4])**2 + (y[9]-y[5])**2)**(3/2)))
#This is my X(t) at time zero
y_0 = np.concatenate((r_vec_1, v_vec_1, r_vec_2, v_vec_2, r_vec_3, v_vec_3))
y = y_0
#This is my F(X) at time zero
def fun(t,y):
return np.array([y[2], y[3], x1_double_dot(y, mass_vector), y1_double_dot(y, mass_vector),
y[6], y[7], x2_double_dot(y, mass_vector), y2_double_dot(y, mass_vector),
y[10], y[11], x3_double_dot(y, mass_vector), y3_double_dot(y, mass_vector)])
# collect data
t_values = []
y_values = []
#Time start, step, and finish point
t0,tf,t_step = 0, 2, 0.1
nsteps = int((tf - t0)/t_step)
solution = integrate.RK45(fun, t0, y_0, tf, first_step=t_step)
#The loop for running the Runge-Kutta method over some time period.
for step in range(nsteps):
solution.step()
y_values.append(solution.y[0])
# break loop after modeling is finished
if solution.status == 'finished':
break

I condensed the setup and derivatives computation to
masses = [1,1,1]
r1,v1 = [ 0,0], [-2**0.5,-2**0.5]
r2,v2 = [-1,0], [0.5**0.5, 0.5**0.5]
r3,v3 = [ 1,0], [0.5**0.5, 0.5**0.5]
G = 1
def odesys(t,u):
def force(a): return G*a/sum(a**2)**1.5
r1,v1,r2,v2,r3,v3 = u.reshape([-1,2])
m1,m2,m3 = masses
f12, f13, f23 = force(r1-r2), force(r1-r3), force(r2-r3)
a1,a2,a3 = -m2*f12-m3*f13, m1*f12-m3*f23, m1*f13+m2*f23
return np.concatenate([v1,a1,v2,a2,v3,a3])
Then the execution I essentially copied your code, just adding some options that are nice for the graph
from scipy import integrate
#Time start, step, and finish point
t0,tf,t_step = 0, 2, 0.1
nsteps = int((tf - t0)/t_step)
u0 = np.concatenate([r1,v1,r2,v2,r3,v3])
solution = integrate.RK45(odesys, t0, u0, tf, first_step=0.2*t_step, max_step=t_step)
# collect data
t_values = [t0]
u_values = [u0]
#The loop for running the Runge-Kutta method over some time period.
for step in range(nsteps):
solution.step()
t_values.append(solution.t)
u_values.append(solution.y)
# break loop after modeling is finished
if solution.status == 'finished':
break
There were no errors reported, and the solution plots as
My scipy version is 1.4.1.
The plot is obtained via
u = np.asarray(u_values).T
# x1,y1 = u[0], u[1], x2,y2 = u[4],u[5]
plt.plot(u[0],u[1],'-o',lw=1, ms=3, label="body 1")
plt.plot(u[4],u[5],'-x',lw=1, ms=3, label="body 2")
plt.plot(u[8],u[9],'-s',lw=1, ms=3, label="body 3")
Instead of, e.g., u[4] after the transformation of the view of the data in u_values one could also have used u_values[:][4] using the original result data structure.
With changed data so that the center-of-mass is largely constant, and the first body small to the other two, and an increased gravity constant
masses = [0.01, 1, 1]
r1,v1 = [ 0,0], [-2**0.5,-2**0.5]
r2,v2 = [-1,0], [ 0.5**0.5, 0.5**0.5]
r3,v3 = [ 1,0], [-0.5**0.5,-0.5**0.5]
G = 4
The resulting dynamic is

Related

Getting the elbow/knee of a list of 2d points [duplicate]

i have a list of points which are the inertia values of a kmeans algorithm.
To determine the optimum amount of clusters i need to find the point, where this curve starts to flatten.
Data example
Here is how my list of values is created and filled:
sum_squared_dist = []
K = range(1,50)
for k in K:
km = KMeans(n_clusters=k, random_state=0)
km = km.fit(normalized_modeling_data)
sum_squared_dist.append(km.inertia_)
print(sum_squared_dist)
How can i find a point, where the pitch of this curve increases (the curve is falling, so the first derivation is negative)?
My approach
derivates = []
for i in range(len(sum_squared_dist)):
derivates.append(sum_squared_dist[i] - sum_squared_dist[i-1])
I want to find the optimum number of clusters any given data using the elbow method. Could someone help me how i can find the point where the list of the inertia values starts to flatten?
Edit
Datapoints:
[7342.1301373073857, 6881.7109460930769, 6531.1657905495022,
6356.2255554679778, 6209.8382535595829, 6094.9052166741121,
5980.0191582610196, 5880.1869867848218, 5779.8957906367368,
5691.1879324562778, 5617.5153566271356, 5532.2613232619951,
5467.352265375117, 5395.4493783888756, 5345.3459908298091,
5290.6769823693812, 5243.5271656371888, 5207.2501206569532,
5164.9617535255456]
Graph:
I worked on a Python package modeled after the Kneedle algorithm. It finds x=5 as the point where the curve starts to flatten. The documentation and the paper discuss the algorithm for choosing the knee point in more detail.
y = [7342.1301373073857, 6881.7109460930769, 6531.1657905495022,
6356.2255554679778, 6209.8382535595829, 6094.9052166741121,
5980.0191582610196, 5880.1869867848218, 5779.8957906367368,
5691.1879324562778, 5617.5153566271356, 5532.2613232619951,
5467.352265375117, 5395.4493783888756, 5345.3459908298091,
5290.6769823693812, 5243.5271656371888, 5207.2501206569532,
5164.9617535255456]
x = range(1, len(y)+1)
from kneed import KneeLocator
kn = KneeLocator(x, y, curve='convex', direction='decreasing')
print(kn.knee)
5
import matplotlib.pyplot as plt
plt.xlabel('number of clusters k')
plt.ylabel('Sum of squared distances')
plt.plot(x, y, 'bx-')
plt.vlines(kn.knee, plt.ylim()[0], plt.ylim()[1], linestyles='dashed')
For all those who want to do this on their own, here is a little and basic implementation.
It is highly adapted to my use case (200 clusters as border for the calculation) and the calculation of the distance is very basic and based to point->point in a 2D space, but it can be adapted to any other amount of figures.
I think Kevin's library is technically more up to date and better implemented.
import KMeansClusterer
from math import sqrt, fabs
from matplotlib import pyplot as plp
import multiprocessing as mp
import numpy as np
class ClusterCalculator:
m = 0
b = 0
sum_squared_dist = []
derivates = []
distances = []
line_coordinates = []
def __init__(self, calc_border, data):
self.calc_border = calc_border
self.data = data
def calculate_optimum_clusters(self, option_parser):
if(option_parser.multiProcessing):
self.calc_mp()
else:
self.calculate_squared_dist()
self.init_opt_line()
self.calc_distances()
self.calc_line_coordinates()
opt_clusters = self.get_optimum_clusters()
print("Evaluated", opt_clusters, "as optimum number of clusters")
self.plot_results()
return opt_clusters
def calculate_squared_dist(self):
for k in range(1, self.calc_border):
print("Calculating",k, "of", self.calc_border, "\n", (self.calc_border - k), "to go!")
kmeans = KMeansClusterer.KMeansClusterer(k, self.data)
ine = kmeans.calc_custom_params(self.data, k).inertia_
print("inertia in round", k, ": ", ine)
self.sum_squared_dist.append(ine)
def init_opt_line(self):
self. m = (self.sum_squared_dist[0] - self.sum_squared_dist[-1]) / (1 - self.calc_border)
self.b = (1 * self.sum_squared_dist[0] - self.calc_border*self.sum_squared_dist[0]) / (1 - self.calc_border)
def calc_y_value(self, x_calc):
return self.m * x_calc + self.b
def calc_line_coordinates(self):
for i in range(0, len(self.sum_squared_dist)):
self.line_coordinates.append(self.calc_y_value(i))
def calc_distances(self):
for i in range(0, self.calc_border):
y_value = self.calc_y_value(i)
d = sqrt(fabs(self.sum_squared_dist[i] - self.calc_y_value(i)))
length_list = len(self.sum_squared_dist)
self.distances.append(sqrt(fabs(self.sum_squared_dist[i] - self.calc_y_value(i))))
print("For border", self.calc_border, ", calculated the following distances: \n", self.distances)
def get_optimum_clusters(self):
return self.distances.index((max(self.distances)))
def plot_results(self):
plp.plot(range(0, self.calc_border), self.sum_squared_dist, "bx-")
plp.plot(range(0, self.calc_border), self.line_coordinates, "bx-")
plp.xlabel("Number of clusters")
plp.ylabel("Sum of squared distances")
plp.show()
def calculate_squared_dist_sliced_data(self,output, proc_numb, start, end):
temp = []
for k in range(start, end + 1):
kmeans = KMeansClusterer.KMeansClusterer(k, self.data)
ine = kmeans.calc_custom_params(self.data, k).inertia_
print("Process", proc_numb,"had the CPU,", "calculated", ine, "in round", k)
temp.append(ine)
output.put((proc_numb, temp))
def sort_result_queue(self, result):
result.sort()
result = [r[1] for r in result]
flat_list= [item for sl in result for item in sl]
return flat_list
def calc_mp(self):
output = mp.Queue()
processes = []
processes.append(mp.Process(target=self.calculate_squared_dist_sliced_data, args=(output, 1, 1, 50)))
processes.append(mp.Process(target=self.calculate_squared_dist_sliced_data, args=(output, 2, 51, 100)))
processes.append(mp.Process(target=self.calculate_squared_dist_sliced_data, args=(output, 3, 101, 150)))
processes.append(mp.Process(target=self.calculate_squared_dist_sliced_data, args=(output, 4, 151, 200)))
for p in processes:
p.start()
#lock code and wait for all processes to finsish
for p in processes:
p.join()
results = [output.get() for p in processes]
self.sum_squared_dist = self.sort_result_queue(results)

scipy.integrate.solve_ivp diverges on a state space simulation well finished in MATLAB

I tried to simulate a state space model with MATLAB ode45, then I tried the same work in Python with scipy.integrate.solve_ivp. As it is obviously shown in this post pictures. Python simulation diverges for no good reason. The solvers message is "Required step size is less than spacing between numbers." but adding time steps is not a solution.
Here is the MATLAB code for the time interval of half a second following a link for the plot of 173rd state:
[1]: https://i.stack.imgur.com/mMdNQ.png
C_static=csvread('C_static.csv');
M_static=csvread('M_static.csv');
B_static=csvread('B_static.csv');
CY_static=csvread('CY_static.csv');
DY_static=csvread('DY_static.csv');
dynamoterm = csvread('dynamoterm.csv');
C_static=0*C_static;
n2panto=dynamoterm(6,1);
n2cw=dynamoterm(6,2);
k_dynamic = KCdyna(0,dynamoterm);
K_total = K_static;
K_total(n2cw+1:n2panto+1,n2cw+1:n2panto+1)=K_total(n2cw+1:n2panto+1,n2cw+1:n2panto+1)+k_dynamic;
A_static = [0*K_static, eye(length(B_static));
-M_static\K_static, -M_static\C_static];
Bu = [0*B_static;
M_static\B_static];
inc0 = -A_static\Bu;
M_in=inv(M_static);
M_cwp=M_in(:,n2cw+1:n2panto+1);
timer=tic;
[T, Y] = ode45(#(t,X) Asol(X,t,A_static,M_cwp,Bu,n2cw,n2panto,dynamoterm),[0,0.5],inc0);
output=[CY_static,0*CY_static]*Y'+DY_static*ones(1,length(T));
figure
plot(T,output(173,:));
stopwatch=toc(timer);
function dx=Asol(X,t,A_static,M_cwp,Bu,n2cw,n2panto,dynamoterm)
[k_dynamic]=KCdyna(t,dynamoterm);
A=A_static;
A(n2panto+4:2*(n2panto+3),n2cw+1:n2panto+1)=A_static(n2panto+4:2*(n2panto+3),n2cw+1:n2panto+1)-M_cwp*k_dynamic;
dx=A*X+Bu;
end
[![MATLAB simulation plot of the 173rd state][1]][1]
Here is my similar work in Python for the time interval of half a second following a link for the plot of 173rd state:
[2]: https://i.stack.imgur.com/LOg2j.png
from KCdyna2 import K_dyn
import matplotlib.pyplot as plt
from scipy.integrate import solve_ivp
# Imports matrices via .csv file
M = np.genfromtxt('Excel\dyn\M_static.csv', delimiter=',')
C = np.genfromtxt('Excel\dyn\C_static.csv', delimiter=',')
C = np.zeros(np.shape(C))
K_static = np.genfromtxt('Excel\dyn\K_static.csv', delimiter=',')
B = np.genfromtxt('Excel\dyn\B_static.csv', delimiter=',')
dyn_trm = np.genfromtxt('Excel\dyn\dynamoterm.csv', delimiter=',')
# Slice addresses
n2cw = int(dyn_trm[5, 1]) # Slice beginning
n2panto = int(dyn_trm[5, 0]) # Slice finishing
# Time interval for solution
time_interval = [0, 0.5]
times = np.linspace(time_interval[0], time_interval[1], 50000)
M_inv = np.linalg.inv(M)
K_total = K_static
K_total[n2cw:n2panto + 1,
n2cw:n2panto + 1] += K_dyn(0, dyn_trm)
# System dynamics matrix
A_static = np.block([[np.zeros((len(M_inv), len(M_inv)), dtype='uint8'), np.eye(len(M_inv), dtype='uint8')],
[np.matmul(-M_inv, K_total), np.matmul(-M_inv, C)]])
Bu = np.block([[np.zeros((len(B), 1), dtype='uint8')],
[np.matmul(M_inv, B).reshape(len(B), 1)]])
inc0 = np.matmul(-np.linalg.inv(A_static), Bu)
M_inv = np.linalg.inv(M)
M_cwp = M_inv[:, n2cw:n2panto + 1]
def SttSpcEq(t, x, M_cwp, A_st, Bu, dynamoterm, n2cw,n2panto):
K_dynamic = K_dyn(t, dynamoterm)
A = A_st
A[n2panto + 3:2*(n2panto+3),
n2cw:n2panto + 1] -= np.matmul(M_cwp, K_dynamic)
return (np.matmul(A, x.reshape(len(Bu), 1)) + Bu).reshape(len(Bu), )
soln = solve_ivp(SttSpcEq,
time_interval,
inc0.reshape(len(inc0),),
method='RK45', t_eval=times, args=(M_cwp, A_static, Bu, dyn_trm, n2cw, n2panto))
print(soln.message)
plt.plot(soln.t, soln.y[172])
plt.show() ```
[![Python simulation plot of the 173rd state][2]][2]

Python's fsolve not working

I'm currently trying to find the intercept of 2 equations from my code (pasted below). I'm using fsolve and have used it successfully in one part but I can't get it to work for the second.
Confusingly it's not showing up an error, if you paste this code into your notebook and run it you'll see 2 grphs, on the first graph there's a line at an angle which should be stopping at the eqm line.
The section which wont work is def q_eqm(x_q). Thank you for your help
import numpy as np
import scipy.optimize as opt
import matplotlib.pyplot as plt
AC_LK = np.array([4.02232,1206.53,220.291])
AC_HK = np.array([4.0854,1348.77,219.976])
P_Tot = 1 # Bara
N_Size = 11 # 1001 = 0.1% accuracy for xA
xf = 0.7
q = 0.7
xA = np.linspace(0,1,N_Size)
yA = np.linspace(0.00,0.00,N_Size)
T = np.linspace(0.00,0.00,N_Size)
x = np.array([xA[0:N_Size],yA[0:N_Size],T[0:N_Size]]) # x[xA,yA,T]
F = np.empty((1))
def xA_T(N):
xA_Ant = x[0,N]
def P_Ant(T):
PA = pow(10,AC_LK[0]-(AC_LK[1]/(T+AC_LK[2])))*xA_Ant
PB = pow(10,AC_HK[0]-(AC_HK[1]/(T+AC_HK[2])))*(1-xA_Ant)
F[0] = P_Tot - (PA + PB)
return F
return x
TGuess = [100]
T = opt.fsolve(P_Ant,TGuess)
x[2,N] = T
return x
for N in range(0,len(xA)):
xA_T(N)
x[1,N] = pow(10,AC_LK[0]-(AC_LK[1]/(x[2,N]+AC_LK[2])))*x[0,N]/P_Tot
q_int = ((-q*0)/(1-q)) + (xf/(1-q))
Eqm_Poly = np.polyfit(x[0,0:N_Size], x[1,0:N_Size], 6)
q_Poly = np.polyfit([xf,0], [xf,q_int], 1)
F = np.empty((1))
def q_Eqm(x_q):
y_q = q_Poly[0]*x_q + q_Poly[1]
eqm_y = (Eqm_Poly[0]*pow(x_q,6)+Eqm_Poly[1]*pow(x_q,5)+Eqm_Poly[2]*pow(x_q,4)+Eqm_Poly[3]*pow(x_q,3)+Eqm_Poly[4]*pow(x_q,2)+Eqm_Poly[5]*pow(x_q,1)+Eqm_Poly[6]*pow(x_q,0))
F[0] = y_q - eqm_y
return F
x_qGuess = [0]
x_q = opt.fsolve(q_Eqm,x_qGuess)
print(x,Eqm_Poly,x_q,q_int)
plt.plot(x[0,0:N_Size],x[1,0:N_Size],'k-',linewidth=1)
plt.plot([xf,xf],[0,xf],'b-',linewidth=1)
plt.plot([xf,x_q],[xf,(q_Poly[0]*x_q + q_Poly[1])],'r-',linewidth=1)
plt.legend(['Eqm','Feed'])
plt.xlabel('xA')
plt.ylabel('yA')
plt.xlim([0.00, 1])
plt.ylim([0.00, 1])
plt.savefig('x.png')
plt.savefig('x.eps')
plt.show()
plt.plot(x[0,0:N_Size],x[2,0:N_Size],'r--',linewidth=3)
plt.plot(x[1,0:N_Size],x[2,0:N_Size],'b--',linewidth=3)
plt.legend(['xA','yA'])
plt.xlabel('Mol Frac')
plt.ylabel('Temp degC')
plt.xlim([0, 1])
plt.savefig('Txy.png')
plt.savefig('Txy.eps')
plt.show()
The answer turns out to be relatively simple:
#F = np.empty((1)) # remove this
def q_Eqm(x_q):
y_q = q_Poly[0]*x_q + q_Poly[1]
eqm_y = (Eqm_Poly[0]*pow(x_q,6)+Eqm_Poly[1]*pow(x_q,5)+Eqm_Poly[2]*pow(x_q,4)+Eqm_Poly[3]*pow(x_q,3)+Eqm_Poly[4]*pow(x_q,2)+Eqm_Poly[5]*pow(x_q,1)+Eqm_Poly[6]*pow(x_q,0))
return y_q - eqm_y
The original code defines a global F, which is modified in the function and then returned. So in each iteration the function returns different values but they are the same object. This seems to confuse fsolve (I guess it internally stores references to the results rather than values). Removing this F and simply returning the result of the subtraction resolves the problem.

Frequency Response Scipy.signal

I'm learning digital signal processing to implement filters and am using python to easily implement a test ideas. So I just started using the scipy.signal library to find the impulse response and frequency response of different filters.
Currently I am working through the book "Digital Signals, Processors and Noise by Paul A. Lynn (1992)" (and finding it an amazing resource for learning this stuff). In this book they have a filter with the transfer functions shown below:
I divided the numerator and denominator by in order to get the following equation:
I then implemented this with Scipy using:
NumeratorZcoefs = [1, -1, 1, -1]
DenominatorZcoefs = [1, 0.54048, -0.62519, -0.66354, 0.60317, 0.69341]
FreqResponse = scipy.signal.freqz(NumeratorZcoefs, DenominatorZcoefs)
fig = plt.figure(figsize = [8, 6])
ax = fig.add_subplot(111)
ax.plot(FreqResponse[0], abs(np.array(FreqResponse[1])))
ax.set_xlim(0, 2*np.pi)
ax.set_xlabel("$\Omega$")
and produce the plot shown below:
However in the book the frequency response is shown to be the following:
They are the same shape but the ratio of the peaks at ~2.3 and 0.5 are very different for the 2 plots, could someone suggest why this is?
Edit:
To add to this, I've just implemented a function to calculate the frequency response by hand (by calculating the distance from the poles and zeros of the function) and I get a similar ratio to the plot generated by scipy.signal, however the numbers are not the same, does anyone know why this might by?
Implementation is as follows:
def H(omega):
z1 = np.array([0,0]) # zero at 0, 0
z2 = np.array([0,0]) # Another zero at 0, 0
z3 = np.array([0, 1]) # zero at i
z4 = np.array([0, -1]) # zero at -i
z5 = np.array([1, 0]) # zero at 1
z = np.array([z1, z2, z3, z4, z5])
p1 = np.array([-0.8, 0])
p = cmath.rect(0.98, np.pi/4)
p2 = np.array([p.real, p.imag])
p = cmath.rect(0.98, -np.pi/4)
p3 = np.array([p.real, p.imag])
p = cmath.rect(0.95, 5*np.pi/6)
p4 = np.array([p.real, p.imag])
p = cmath.rect(0.95, -5*np.pi/6)
p5 = np.array([p.real, p.imag])
p = np.array([p1, p2, p3, p4, p5])
a = cmath.rect(1,omega)
a_2dvector = np.array([a.real, a.imag])
dz = z-a_2dvector
dp = p-a_2dvector
dzmag = []
for dis in dz:
dzmag.append(np.sqrt(dis.dot(dis)))
dpmag = []
for dis in dp:
dpmag.append(np.sqrt(dis.dot(dis)))
return(np.product(dzmag)/np.product(dpmag))
I then plot the frequency response like so:
omegalist = np.linspace(0,2*np.pi,5000)
Hlist = []
for omega in omegalist:
Hlist.append(H(omega))
fig = plt.figure()
ax = fig.add_subplot(111)
ax.plot(omegalist, Hlist)
ax.set_xlabel("$\Omega$")
ax.set_ylabel("$|H(\Omega)|$")
and get the following plot:
The SciPy generated frequency response is correct. In any case, I wouldn't trust the book's figure which appears to have been drawn by hand.
If you want to find the frequency response "manually", this can be simply done by defining a function returning the original Z-transform and evaluating it on the unit circle as follows
def H(z):
num = z**5 - z**4 + z**3 - z**2
denom = z**5 + 0.54048*z**4 - 0.62519*z**3 - 0.66354*z**2 + 0.60317*z + 0.69341
return num/denom
import numpy as np
import matplotlib.pyplot as plt
w_range = np.linspace(0, 2*np.pi, 1000)
plt.plot(w_range, np.abs(H(np.exp(1j*w_range))))
The result is exactly the same as SciPy.

Using numpy vectorize

I'm trying to do some bayesian probit code using data augmentation. I can get it to work if I loop over the rows of the output matrix, but I'd like to vectorize it and do it all in one shot (presumably that's faster).
import numpy as np
from numpy import random
import statsmodels.api as sm
from scipy import stats
from scipy.stats import norm, truncnorm
##################################
### Create some simulated data ###
num_leg = 50
num_bills = 20
a = np.random.uniform(-1,1,num_bills).reshape(num_bills, 1)
b = np.random.uniform(-2,2,num_bills).reshape(num_bills, 1)
x = np.random.standard_normal(num_leg).reshape(num_leg, 1)
ystar_base = a + np.dot(b,x.T)
epsilon = np.random.standard_normal(num_leg * num_bills).reshape(num_bills, num_leg)
ystar = ystar_base + epsilon
y = 1*(ystar >0)
### Initialize some stuff I need ###
avec = [0]*num_bills # These are bill parameters
bvec = [0]*num_bills
betavec = [np.matrix(zip(avec,bvec))]
xvec = [0]*num_leg # these are legislator parameters
_ones = np.ones(num_leg)
def init_y(mat): # initialize a latent y matrix
if mat==1: return truncnorm.rvs(0,10000)
else: return truncnorm.rvs(-10000,0)
vectorize_y = np.vectorize(init_y)
latent_y = np.matrix(vectorize_y(y))
burn = 500 # How long to run the MCMC
runs = 500
### define the functions ###
def sample_params(xnow,ynow): # This is the function I'd like to vectorize
if type(xnow) == list:
xnow = np.array(xnow)
if type(ynow) == list:
ynow = np.array(ynow)
ynow = ynow.T #reshape(ynow.shape[0],1)
sigma = np.linalg.inv(np.dot(xnow.T,xnow)) ###This is the line that produces an error###
xy = np.dot(xnow.T,ynow)
mu = np.dot(sigma, xy) # this is just (x'x)inv x'y
return np.random.multivariate_normal(np.array(mu).flatten(), sigma)
vecparams = np.vectorize(sample_params)
def get_mu(xnow, bnow): # getting the updated mean to draw the latent ys
if type(xnow) == list:
xnow = np.array(xnow)
if type(bnow) == list:
bnow = np.array(bnow)
mu = np.dot(xnow,bnow.T)
mu = np.matrix(mu)
return mu
def sample_y(mu, ynow): # generate latent y matrix
if ynow==1:
a, b = (0 - mu),(10000-mu)
else:
a, b = (-10000 - mu),(0-mu)
return truncnorm.rvs(a,b)
vector_sample = np.vectorize(sample_y) # I'd like to be able to do something like this
### Here's the MCMC loop with the internal loop over rows(bills)
for i in range(burn+runs):
this_beta = []
this_x = []
this_y = []
for j in range(num_bills): #I'd like to get rid of this loop
ex = zip(x_ones, x)
newbeta = sample_params(ex, latent_y[j])
this_beta.append(newbeta)
#ex = np.array(zip(x_ones, x))
#this_beta = vecparams(ex, latent_y[:,]) # and call the vectorized function here
betavec.append(this_beta)
#Note, I can vectorize the latent outputs easily enough here
mean = get_mu(ex, betavec[-1])
latent_y = np.matrix(vector_sample(mean, np.matrix(y).T).T.reshape(latent_y.shape[0], latent_y.shape[1]))
### Now a bit of code to check to see if I've recovered what I want ###
test_beta = [zip(*(z)) for z in betavec[burn:]]
test_a = np.array([z[0] for z in test_beta])
test_b = np.array([z[1] for z in test_beta])
amean = test_a.sum(axis = 0)/float(runs)
bmean = test_b.sum(axis = 0)/float(runs)
print 'a mean'
print np.corrcoef([amean, np.array(a)])
print
print 'b mean'
print np.corrcoef([bmean, np.array(b)])
If I comment out the loop and use the commented out lines just above, I get the following error at the line I indicated earlier (the one that defines sigma):
LinAlgError: 0-dimensional array given. Array must be at least two-dimensional

Categories

Resources