I am learning Numpy as a substitute to Matlab. I am having problem mapping Matlab function to Numpy. Program is to add two signals using Matlab
Matlab version :
function [y.d = sigadd(xl,nl,x2,n2)
% implements y(n) = xi(n)+x2(n)
% [y,nl - sigadd(xi,nl,x2,n2)
X
% xi = first sequence over nl
% x2 - second sequence over n2 (n2 can be different from nl)
%
n = min(min(n1) ,min(n2)) :max(max(nl) ,max(n2)) ; X duration of y(n)
yl - zeros(l,length(n)); y2 = yl;
yl(find((n>=min(nl))&(n<cmar(nl))-l))lxl;
y2(find((n>=min(n2))&(n<=max(n2))==1))=x2;
y = yl+y2;
I have tried the following in Python :
def SigAdd(x1,n1,x2,n2):
n_l = min(min(n1),min(n2))
n_h= max(max(n1),max(n2))
n = arange(n_l,n_h+1)
y1 = zeros([1,len(n)],int)
y2 = y1
y1 = (argwhere((n>=min(n1))&(n<=max(n1)))==1)
y1 = x1
y2 = (argwhere((n>=min(n2))&(n<=max(n2)))==1)
y2 = x2
y = y1 + y2
return y,n
Expected Results:
Example1: Arrays of unequal length
x1 = array([1,1,1,1,1])
x2 = array([1,1,1,1])
n1 = array([1,2,3,4,5])
n2 = array([1,2,3,4])
y,n = SigAdd(x1,n1,x2,n2)
>> y
array[2,2,2,2,1]
>>n
array[1,2,3,4,5]
Example2:Arrays of equal length
x1 = array([1,1,1])
x2 = array([1,1,1])
n1 = array([1,2,3])
n2 = array([3,4,5])
y,n = SigAdd(x1,n1,x2,n2)
>> y
array[1,1,2,1,1]
>>n
array[1,2,3,4,5]
It works fine if both the arrays are of equal length, but won't work for unequal arrays. I know the problem is i am overriding the y1 (which i initially created with zeros) with value of x1 causing problem. I have used argwhere command in Numpy which is equivalent to Find command of Matlab, but if I use it like Matlab program shown above, it shows error that value can't be assigned to callable function.
Basically in Matlab program, unequal arrays are filled with zero's. Matlab version handles condition even if two signals are of equal length but are at different positions. I want to use Python instead of Matlab but these conversion problems are causing pain.
Some Modifications : (But is not working, Indexing Error : Out of bound coming)
def SigAdd(x1,n1,x2,n2):
n_l = min(min(n1),min(n2))
n_h= max(max(n1),max(n2))
n = arange(n_l,n_h+1)
y1 = zeros([1,len(n)],int)
y2 = y1
y11 = argwhere((n>=min(n1))&(n<=max(n1)))
q = 0
for w in y11:
y1[w]= x1[q]
q = q + 1
y22 = argwhere((n>=min(n2))&(n<=max(n2)))
q = 0
for w in y22:
y2[w]= x2[q]
q = q + 1
y = y1 + y2
return y
You can assign to a slice:
def SigAdd(x1,n1,x2,n2):
n_l = min(n1[0], n2[0])
n_h = max(n1[-1], n2[-1])
n = arange(n_l, n_h+1)
y = zeros(len(n), int)
i = n1[0] - n[0]
y[i:i+len(x1)] = x1
i = n2[0] - n[0]
y[i:i+len(x2)] += x2
return y
Using fancy indexing:
import numpy as np
high = max(n2[-1], n1[-1])
low = min(n2[0], n1[0])
n1_ = n1 - low # use 0-based indexing
n2_ = n2 - low
y = np.zeros(high - low + 1, dtype=x1.dtype)
y[n1_] += x1
y[n2_] += x2
This helped me for a similar problem. The idea is to first make the vectors equal length (with 0 padding the shorter at the end). Disadvantages are the conversion to lists and the memory overhead with those lists. However for quick and dirty tasks it has served well.
def MakeVectorsSameSize(v1,v2):
"""given two vectors zero pad smaller to match length of bigger"""
#make the vectors length of longest 0 pad
l1 = len(v1)
l2 = len(v2)
if l1 < l2:
#put l2-l1 elements into v1
z1 = numpy.ones(l2-l1)*0
v1 = list(v1)
v1.extend(z1)
v1 = numpy.array(v1)
elif l1 > l2:
#put l1-l2 elements into v2
z2 = numpy.ones(l1-l2)*0
v2 = list(v2)
v2.extend(z2)
v2 = numpy.array(vTemp)
return v1,v2
Related
I want to plot the motion of a double pendulum with a spring in python. I need to plot the theta1, theta2, r, and their first derivatives. I have found my equations for the motion, which are second-order ODEs so I then converted them to first-order ODEs where x1=theta1, x2=theta1-dot, y1=theta2, y2=theta2-dot, z1=r, and z2=r-dot. Here is a picture of the double pendulum problem: enter image description here
Here is my code:
from scipy.integrate import solve_ivp
from numpy import pi, sin, cos, linspace
g = 9.806 #Gravitational acceleration
l0 = 1 #Natural length of spring is 1
k = 2 #K value for spring is 2
OA = 2 #Length OA is 2
m = 1 #Mass of the particles is 1
def pendulumDynamics1(t, x): #Function to solve for theta-1 double-dot
x1 = x[0]
x2 = x[1]
y1 = y[0]
y2 = y[1]
z1 = z[0]
z2 = z[1]
Fs = -k*(z1-l0)
T = m*(x2**2)*OA + m*g*cos(x1) + Fs*cos(y1-x1)
x1dot = x2
x2dot = (Fs*sin(y1-x1) - m*g*sin(x1))/(m*OA) # angles are in radians
return [x1dot,x2dot]
def pendulumDynamics2(t, y): #Function to solve for theta-2 double-dot
x1 = x[0]
x2 = x[1]
y1 = y[0]
y2 = y[1]
z1 = z[0]
z2 = z[1]
Fs = -k*(z1-l0)
y1dot = y2
y2dot = (-g*sin(y1) - (Fs*cos(y1-x1)*sin(x1))/m + g*cos(y1-x1)*sin(x1) - x2*z1*sin(x1))/z1
return [y1dot,y2dot]
def pendulumDynamics3(t, z): #Function to solve for r double-dot (The length AB which is the spring)
x1 = x[0]
x2 = x[1]
y1 = y[0]
y2 = y[1]
z1 = z[0]
z2 = z[1]
Fs = -k*(z1-l0)
z1dot = z2
z2dot = g*cos(y1) - Fs/m + (y2**2)*z1 + x2*OA*cos(y1-x1) - (Fs*(sin(y1-x1))**2)/m + g*sin(x1)*sin(y1-x1)
return [z1dot,z2dot]
# Define initial conditions, etc
d2r = pi/180
x0 = [30*d2r, 0] # start from 30 deg, with zero velocity
y0 = [60*d2r, 0] # start from 60 deg, with zero velocity
z0 = [1, 0] #Start from r=1
t0 = 0
tf = 10
#Integrate dynamics, initial value problem
sol1 = solve_ivp(pendulumDynamics1,[t0,tf],x0,dense_output=True) # Save as a continuous solution
sol2 = solve_ivp(pendulumDynamics2,[t0,tf],y0,dense_output=True) # Save as a continuous solution
sol3 = solve_ivp(pendulumDynamics3,[t0,tf],z0,dense_output=True) # Save as a continuous solution
t = linspace(t0,tf,200) # determine solution at these times
dt = t[1]-t[0]
x = sol1.sol(t)
y = sol2.sol(t)
z = sol3.sol(t)
I have 3 functions in my code, each to solve for x, y, and z. I then use solve_ivp function to solve for x, and y, and z. The error in the code is:
`File "C:\Users\omora\OneDrive\Dokument\AERO 211\project.py", line 13, in pendulumDynamics1
y1 = y[0]
NameError: name 'y' is not defined`
I don't understand why it is saying that y is not defined, because I defined it in my functions.
Your system is closed without friction, thus can be captured by the Lagrange or Hamiltonian formalism. You have 3 position variables, thus a 6-dimensional dynamical state, complemented either by the velocities or the impulses.
Let q_k be theta_1, theta_2, r, Dq_k their time derivatives and p_k the impulse variables to q_k, then the dynamics can be realized by
def DoublePendulumSpring(u,t,params):
m_1, l_1, m_2, l_2, k, g = params
q_1,q_2,q_3 = u[:3]
p = u[3:]
A = [[l_1**2*(m_1 + m_2), l_1*m_2*q_3*cos(q_1 - q_2), -l_1*m_2*sin(q_1 - q_2)],
[l_1*m_2*q_3*cos(q_1 - q_2), m_2*q_3**2, 0],
[-l_1*m_2*sin(q_1 - q_2), 0, m_2]]
Dq = np.linalg.solve(A,p)
Dq_1,Dq_2,Dq_3 = Dq
T1 = Dq_2*q_3*sin(q_1 - q_2) + Dq_3*cos(q_1 - q_2)
T3 = Dq_1*l_1*cos(q_1 - q_2) + Dq_2*q_3
Dp = [-l_1*(m_2*Dq_1*T1 + g*(m_1+m_2)*sin(q_1)),
l_1*m_2*Dq_1*T1 - g*m_2*q_3*sin(q_2),
m_2*Dq_2*T3 + g*m_2*cos(q_2) + k*(l_2 - q_3) ]
return [*Dq, *Dp]
For a derivation see the Euler-Lagrange equations and their connection to the Hamilton equations. You might get asked about such a derivation.
This, after suitable defining the parameter tuple and initial conditions, can be fed to odeint and produces a solution that can then be plotted, animated or otherwise examined. The lower bob traces a path like the one below, not periodic and not very deterministic. (The fulcrum and the arc of the upper bob are also inserted, but less interesting.)
def pendulumDynamics1(t, x):
x1 = x[0]
x2 = x[1]
y1 = y[0]
y2 = y[1]
z1 = z[0]
z2 = z[1]
You only pass x as a parameter. The code inside the function has no idea what y and z refer to.
You will need to change the function call to also include those variables.
def pendulumDynamics1(t, x, y, z):
How to make my code nice as I am sure there must be neater solution. I have tried to solve it with pandas apply (but it does not use updated values but keeps the old values in memory). Shall I use some kind of function returning generator object e.g. iterrows? Maybe I could solve it somehow with cumsum or cumprod?
import pandas as pd
df = pd.DataFrame(data={'val':[0, 0.2, 0.1, 0.5, -0.6]})
prev = 100
val = []
for i in df['val'].tolist():
prev = prev * i + prev
val.append(prev)
df['val'] = val
I want to update column values according to the equation:
n = given value e.g. 100
v0 = 0, v1 = 0.2 etc.
y0 = n * v0 + n
y1 = y0 * v1 + y0
y2 = y1 * v2 + y1
etc.
You can use:
df['val'] = 100 * (df['val'] + 1).cumprod()
The reason this works is because your expression can be rewritten:
for i in df['val'].tolist():
prev *= (i + 1)
I'm trying to solve the following problem of coupled ODEs using odeint() from scipy. The system looks like this:
X'_k = mean(Y_k) + F
Y'_{k,j} = X_k - Y_{k,j}
This is a system with 3 X variables, and for each X variable, there are other 3 Y variables.
From what I read from the documentation, and the examples here and here, I can pass the system of equations as a list. And that is what I tried in the following example:
import numpy as np
from scipy.integrate import odeint
def dZdt(Z, t):
X = Z[0]
Y = Z[1]
F = 4
d_x = np.zeros(3)
d_y = np.zeros(3*3).reshape(3,3)
# Compute the Y values
for k in range(3):
for j in range(3):
d_y[k][j] = X[k] - Y[k][j]
# X values
d_x[k] = Y[k].mean() + F
d = [d_x, d_y]
return d
# Initial conditions
X0 = np.random.uniform(size=3)
Y0 = np.random.uniform(size = 3*3).reshape(3,3)
Z0 = [X0, Y0]
t = range(20)
Z = odeint(dZdt, Z0, t)
Where k, j = (1,2,3) and Z = [X,Y]
But I'm afraid I'm getting the following error:
ValueError: could not broadcast input array from shape (3,3) into shape (3)
My real problem is more complex, because j, and k, can be bigger than 3 (they go from 1 to j_max, and K_max, respectively) so I cannot write the 12 variables one by one.
My guessing is that somewhere in the code, Y variables are tried to fill in X shape... but no clue about where.
Any idea of what I'm doing wrong?
You are trying to represent an unknown function by two arrays inside of a list. It must be a one-dimensional array. So, instead of 3 X-variables and 9 Y-variables it must be a flat list of 12 variables. Like this:
def dZdt(Z, t):
X = Z[:3]
Y = Z[3:].reshape(3, 3)
F = 4
d_x = np.zeros(3)
d_y = np.zeros((3, 3))
# Compute the Y values
for k in range(3):
for j in range(3):
d_y[k, j] = X[k] - Y[k, j]
# X values
d_x[k] = Y[k].mean() + F
d = np.concatenate((d_x.ravel(), d_y.ravel()))
return d
# Initial conditions
X0 = np.random.uniform(size=3)
Y0 = np.random.uniform(size=(3, 3))
Z0 = np.concatenate((X0.ravel(), Y0.ravel()))
t = range(20)
Z = odeint(dZdt, Z0, t)
NumPy arrays are indexed as Y[k, j], not Y[k][j]. And there are ample vectorization opportunities that would eliminate the loops in the computation of dZdt. Like this:
def dZdt(Z, t):
X = Z[:3]
Y = Z[3:].reshape(3, 3)
F = 4
d_y = X[:, None] - Y
d_x = Y.mean(axis=1) + F
d = np.concatenate((d_x.ravel(), d_y.ravel()))
return d
I have a program I've been working on all weekend and it contains the correct output, however it outputs a list that is not callable, and I need to be able to build a plot from it. How do I modify the list to make it callable?
Homework #3 - Convolution
import numpy as np
#import matplotlib.patches as mpatches
import matplotlib.pyplot as plt
np.set_printoptions(threshold=np.nan)
#04-01 Convolution
def conv395(x,h):
#Function to convolve x input signal and h impulse response
M = len(h)-1
L = len(x)
Ly = M+L
LOL = []
def ConV(x,h,n):
y = (h[m]*x[(n-1)-m])
return (y)
for n in range(0,Ly+1):
mx = n-L+1
if mx < 1:
n = n+1
#print("SPACE")
Y1 = 0
if n <= M+1:
L1 = []
for m in range(n):
#print(n)
y = ConV(x,h,n)
Y1 = y + Y1
L1.append(Y1)
LOL.append(str(L1))
#print Y,
###LOL = np.int32(LOL)
if n > M+1:
#print("Space")
L2 = []
N = n-1
n = M+1
Y2 = 0
for m in range(n):
y2 = h[m]*x[(N-m)]
Y2 = Y2 + y2
L2.append(Y2)
LOL.append(str(L2))
if mx > 1:
#print("space")
L3 = []
if n > M+1:
Y3 = 0
for m in range(n-L,M+1):
y3 = h[m]*x[m-(n-L)]
Y3 = Y3 + y3
L3.append(Y3)
LOL.append(str(L3))
return(LOL)
h = [1,2,-1,1]
x = [1,1,2,1,2,2,1,1]
Y = conv395(x,h)
print(callable(Y))
The OUTPUT shows that the list is not callable, and I the output looks like this ['[1]', '[3]', '[3]', '[5]', '[3]', '[7]', '[4]', '[3]', '[3]', '[0]', '[1]']
I've tried every form of conversion I could find on the internet and it won't budge. Suggestions?
I have a 300 x 4 matrix called X created by the odeint function. In the second column are y-values and I would like to cut the matrix when the y-value dips below 0. As a first step I was attempting to create a function that would read the second column and spit out the row number where the column first dips below 0.
X = odeint(func, X0, t)
Yval = X[:,1]
def indexer():
i = 0
if Yval[i] > 0:
i = i + 1
if Yval[i] < 0:
return i
Which is not working and conceptually I know this is wrong, I just couldn't think of another way to do this. Is there a way to cut out all the rows that contain and follow the first <0 y value?
This is my entire code:
import numpy as np
import math
from scipy.integrate import odeint
g = 9.8
theta = (45 * math.pi)/180
v0 = 10.0
k = 0.3
x0 = 0
y0 = 0
vx0 = v0*math.sin(theta)
vy0 = v0*math.cos(theta)
def func(i_state,time):
f = np.zeros(4)
f[0] = i_state[2]
f[1] = i_state[3]
f[2] = -k*(f[0]**2 + f[1]**2)**(.5)*f[0]
f[3] = -g - k*(f[0]**2 + f[1]**2)**(.5)*f[1]
return f
X0 = [x0, y0, vx0, vy0]
t0 = 0
tf = 3
timestep = 0.01
nsteps = (tf - t0)/timestep
t = np.linspace(t0, tf, num = nsteps)
X = odeint(func, X0, t)
Yval = X[:,1]
def indexer():
i = 0
if Yval[i] > 0:
i = i + 1
if Yval[i] < 0:
return i
Maybe you could use the takewhile function from the itertools package:
from itertools import takewhile
first_elements = list(takewhile(lambda x: x[1] >= 0, X))
Where X is your matrix. I used x[1] in the lambda predicate to compare the numbers in the second column.
Here, first_elements will be the rows of the matrix before the first row that contains a value less than zero. You can use len(first_elements) to know what the cutoff point was.
I converted it to a list but you don't have to if you are just going to iterate through the result.
I hope this works.
You could do something like this:
newVals = []
i = 0
while( i < len(X) and X[i][1] >= 0):
newVals.append(X[i])
i += 1
This would go through X and append values to the list newVals until you either reach the end of the list (i < len(X)) or you reach your condition (X[i][1] >= 0).