A system of two multivariable coupled ODEs - python

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

Related

Using a list of floats for a loop

I'm trying to run a Runge-Kutta algorithm to approximate a differential equation. I want to go through a list of values for a constant variable, A, in the function and have the algorithm loop go through for each item in the list and produce a graph. I keep getting an error saying "list indices must be integers or slices but not a float". I tried to convert the numbers in the list to being integer fractions of each other but that didn't work either. I'm mostly unsure on how to circumvent this error as some fixes I found didn't work, here is my code:
import numpy as np
import matplotlib.pyplot as plt
from math import pi
from numpy import arange
from matplotlib.pyplot import plot,show
wo = 1
w = 2 #defining wo, w, g1, Amplitude and steps
h = 0.001
g1 = 0.2
A = [0.1,0.25,0.5,0.7,0.75,0.85,0.95,1.00,1.02,1.031,1.033,1.035,1.05]
for item in list(A): #Converting list items into Float values
[float(i) for i in A]
xpoints = arange(0,100,h)
tpoints = []
zpoints = []
t=0
x = 0
z = pi/2
for i in A: #Calls for items in Amplitude list to run algorighm
def F(t, z, x): #Defining the differential equation
return -g1 * z - (wo ** 2 + 2 * A[i] * np.cos(w * t)) * np.sin(x)
for x in xpoints:
tpoints.append(t)
zpoints.append(z)
m1 = z*h
k1 = h*F(t,z,x) #setting up the runge-kutta algorithm
m2 = h*(z+(k1/2))
k2 = h*F(t+0.5*m1,z+0.5*m1,x+0.5*h)
m3 = h*(z+0.5*k2)
k3 = h*F(t+0.5*m2,z+0.5*m2,x+0.5*h)
m4 = h*(z+0.5*k3)
k4 = h*F(t+0.5*m3,z+0.5*m3,x+0.5*h)
t += (m1+2*m2+2*m3+m4)/6
z += (k1+2*k2+2*k3+k4)/6
A += 1
plot(xpoints,zpoints)
The problem isn't that the numbers themselves need to be converted. Note how you iterate with for i in A:. This means that i is the actual value and not the index. So where you use A[i], you're trying to go to the 0.1 index of A. Instead, just replace A[i] with i in the line at the bottom of this snippet.
A = [0.1,0.25,0.5,0.7,0.75,0.85,0.95,1.00,1.02,1.031,1.033,1.035,1.05]
...
for i in A:
def F(t, z, x):
return -g1 * z - (wo ** 2 + 2 * A[i] * np.cos(w * t)) * np.sin(x)
Because the value of i is an element of A. If you want to loop index by index in A list:
for i in range(len(A))
this works.
This time, you get an error in A + = 1. I think this place will be i + = 1.

On the division and multiplication of matrices in python

Here the question with details and I think it's clearer,
suppose I have a matrix h of size 4 x 4 , and a vector of x of size 4 x 1, if we have y is the output of multiplication between h and x which means y = h * x; whose size is 1 x 4. So when I multiply again the inverse of every column in h by vector y, I should be able to get a vector equivalent of vector x which means $x = h^{-1} * y $. But unfortunately, I can't get that in python.
for example, let's first do that in MATLAB:
clear all
clc
h = (randn(4,4) + 1j*randn(4,4)); %any matrix of 4 x 4
x = [1 + 1j ; 0; 0 ; 0]; % a vector of 4 x 1
y = h * x ; % y is the output of multiplication
x2 = [];
for ii = 1 : 4
x1 = pinv(h(:,ii))*y; %multiply every column of h^(-1) with y
x2 = [x2 x1]; % the output
end
in that case, the output x2 is as expected, a vector 1 x 4 as below:
x2 =
1.0000 + 1.0000i 0.7249 + 0.5054i -0.0202 + 0.0104i 0.2429 + 0.0482i
In MATLAB, that's ok.
Now let's do that in python:
import numpy as np
h = np.random.randn(4,4) + 1j*np.random.randn(4,4)
x = [[1+1j],[0+0j],[0+0j],[0+0j]]
y = h.dot(x)
x2 = []
for ii in range(4):
x1 = np.divide(y, h[:,ii])
x2.append(x1)
print(x2)
Although x2 is supposed to be a vector of dimension 1 x 4 similar as in output of above MATLAB code, but in that case, I get x2 a matrix of size 4 x 4 !!
please any help.
There are two issues here:
np.divide() is for element-wise division, you may be looking for np.linalg.pinv() instead.
MATLAB is col major (FORTRAN-style), while NumPy is row major (C-style) so getting a list as a NumPy array will get you to a shape (n,) with n the length of the list and not an object of size (1, n) as MATLAB would.
The Python code equivalent (sort of, I'll do preallocation) to your MATLAB one, would be:
import numpy as np
h = np.random.randn(4, 4) + 1j * np.random.randn(4, 4)
x = np.array([[1 + 1j], [0 + 0j], [0 + 0j], [0 + 0j]])
# y = h.dot(x) <-- now NumPy supports also `#` in place of `np.dot()`
y = h # x
x2 = np.zeros((1, 4), dtype=np.complex)
for i in range(4):
x2[0, i] = np.linalg.pinv(h[:, i:i + 1]) # y
as you can see, the shape of the output is enforced right away.

Solve underdetermined linear equation Ax + By = C where y is constrained by x

I am new to optimization and have been struggling to solve for variable x and y in linear equation Ax +By = C, while y is constrained by the solution of x.
An example of the problem can be:
A = np.random.rand(20,100)
B = np.random.rand(20,200)
C = np.random.rand(20)
Solve for x and y so that Ax +By = C, with constraints that x is non-negative and -0.7*x0 < y0,y1 <0.7*x0, -0.7*x1 < y2,y3 <0.7*x1... ( -0.7x[i] < y[2i],y[2i+1]<0.7x[i] )
I would really appreciate if someone can recommend me a python package that solve this problem, or some way to transform my problem into a more conventional format that can be solved directly with libraries like Scipy.optimize
I solved the problem using CVXPY:
import cvxpy as cp
import numpy as np
m = 20
n = 100
A = np.random.rand(m,n)
B = np.random.rand(m,n)
C = np.random.rand(m,n)
d = np.random.rand(m)
# construct the problem.
x = cp.Variable(n)
y = cp.Variable(n)
z = cp.Variable(n)
objective = cp.Minimize(cp.sum_squares(A*x + B*y + C*z -d))
constaints = [0 <= x, x <= 1, y <= 0.7*x, y >= -0.7*x, z <= 0.7*x, z >= -0.7*x]
prob = cp.Problem(objective, constraints)
result = prob.solve()
It depends on what do you mean by solve, what you have described have multiple solutions and it is the interior of a polyhedral.
It is a linear programming problem if you are willing to convert the problem to say
-0.7x_0 <=y_0 <= 0.7x_0
Suppose not, consider introducing a small positive number m to make a linear program.
-0.7x_0 + m <=y_0 <= 0.7x_0 - m
You can then use scipy.optimize.linprog to solve a linear program.
If you are just interested in a particular solution, you can just set c in the objective function to be zero.
Edit:
import numpy as np
from scipy.optimize import linprog
m_size = 20
bound = 0.7
x_size = 100
y_size = 2 * x_size
small_m = 0
A = np.random.rand(m_size, x_size)
B = np.random.rand(m_size, y_size)
C = np.random.rand(m_size)
A_eq = np.hstack((A, B))
b_eq = C
sub_block = np.array([-bound, -bound ])
sub_block.shape = (2,1)
block = np.kron(np.eye(x_size), sub_block)
upper_block = np.hstack((block, - np.eye(y_size)))
lower_block = np.hstack((block, np.eye(y_size)))
A_ub = np.vstack((upper_block, lower_block))
b_ub = -small_m * np.ones( 2 * y_size)
bounds = tuple([tuple([0, None]) for i in range(x_size)] + [tuple([None, None]) for i in range(y_size)])
c = [0 for i in range(x_size+y_size)]
res = linprog(c, A_ub = A_ub, b_ub = b_ub, A_eq = A_eq, b_eq = b_eq, bounds = bounds)
x_part = res.x[:x_size]
y_part = res.x[x_size:]

Is there a method to load a 2D array into three 1D arrays?

Is there a more efficient way or even a method to load a 2D numpy array data[n,m] into three 1D arrays X[n*m], Y[n*m], and Z[n*m] than looping over the indices? What I did is:
n = len(data[:,0])
m = len(data[0,:])
X = zeros(n*m)
Y = zeros(n*m)
Z = zeros(n*m)
c = 0
for i in range(n):
for j in range(m):
X[c] = i
Y[c] = j
Z[c] = data[i,j]
c += 1
If your codes actually does what you intended. This should be the equivalent.
X,Y = np.indices(data.shape)
Z = data.ravel()
X = X.ravel()
Y = Y.ravel()

Fast math operations on an array in python

I have a fairly simple math operation I'd like to perform on a array. Let me write out the example:
A = numpy.ndarray((255, 255, 3), dtype=numpy.single)
# ..
for i in range(A.shape[0]):
for j in range(A.shape[1]):
x = simple_func1(i)
y = simple_func2(j)
A[i, j] = (alpha * x * y + beta * x**2 + gamma * y**2, 1, 0)
So basically, there's a mapping between (i, j) and the 3 values of that value (this is for visualization).
I'd like to roll this up and somehow vectorize this, but I'm not sure how to or if I can. Thanks.
Here is the vectorized version:
i = arange(255)
j = arange(255)
x = simple_func1(i)
y = simple_func2(j)
y = y.reshape(-1,1)
A = alpha * x * y + beta * x**2 + gamma * y**2 # broadcasting is your friend here
If you want to fill the last coordinates with 1 and 0:
B = empty(A.shape+(3,))
B[:,:,0] = A
B[:,:,1] = 1 # broadcasting again
B[:,:,2] = 0
You have to change simple_funcN so that they take arrays as input, and create arrays as output. After that, you could look into the numpy.meshgrid() or the cartesian() function here to build coordinate arrays. After that, you should be able to use the coordinate array(s) to fill A with a one-liner.

Categories

Resources