Solve complex matrix differential equation with Odeint - python

I want to solve a matrix differential equation, like this one:
import numpy as np
from scipy.integrate import odeint
def deriv(A, t, Ab):
return np.dot(Ab, A)
Ab = np.array([[-0.25, 0, 0],
[ 0.25, -0.2, 0],
[ 0, 0.2, -0.1]])
time = np.linspace(0, 25, 101)
A0 = np.array([10, 20, 30])
MA = odeint(deriv, A0, time, args=(Ab,))
However, this does not work in the case of having complex matrix elements. I am looking for something similar to scipy.integrate.complex_ode but for odeint. If this is not possible, what other library should I use to perform the integration? I appreciate your help!

odeintw wrapper for odeint must be used in the same fashion as in the question. However, the initial value A0 must be complex-valued vector.

Related

Numpy: sigma notion over two 2D arrays

I have the following 2D Numpy arrays that can take an arbitrary shape (d, c), with d and c being equal to each other :
import numpy as np
arr_a = np.array([[1, 2, 3], [4, 5, 6]])
arr_b = np.array([[10, 20, 30], [40, 50, 60]])
I want to solve for t (a float) by taking the summation of these 2D arrays as follows:
I believe np.add(arr_a, arr_b) returns a matrix, so that wouldn't apply here.
Are there any native Numpy functions that can do this? Or, would I have to iterate through arr_a and arr_b?
And, if iteration is required here, how would I apply the summation logic above to the iteration?
(part of my confusion is understanding the i-th and j-th elements)
Thanks in advance for your help!
If A and B are square matrices, equation in the picture just translates to 2*np.sum(a)
import numpy as np
A = np.random.rand(100,100) ##
B = np.random.rand(100,100) ##
your_required_answer = 2*np.sum(A) ## np.sum(A-B) + np.sum(A.T+B.T)

How to determine two vectors are linearly dependent or independent in python?

Take in two 3 dimensional vectors, each represented as an array, and tell whether they are linearly independent. I tried to use np.linalg.solve() to get the solution of x, and tried to find whether x is trivial or nontrivial. But it shows 'LinAlgError: Last 2 dimensions of the array must be square'. Can anyone help me how to figure that out?
from sympy import *
import numpy as np
from scipy import linalg
from numpy import linalg
v1 = np.array([0, 5, 0])
v2 = np.array([0, -10, 0])
a = np.array([v1,v2])
b = np.zeros(3)
x = np.linalg.solve(a, b)
As your final matrix will be in a rectangular form, a simple approach of EigenValues will not work. You need to use the library of sympy
import sympy
import numpy as np
matrix = np.array([
[0, 5, 0],
[0, -10, 0]
])
_, indexes = sympy.Matrix(matrix).T.rref() # T is for transpose
print(indexes)
This will print the indexes of linearly independent rows. To further print them from the matrix, use
print(matrix[indexes,:])
To answer your specific question, check if two vectors are linearly dependant or not. You can most definitely use an if statement afterwards if it is the two vectors you are always going to check.
if len(indexes) == 2:
print("linearly independant")
else:
print("linearly dependant")
If one eigenvalue of the matrix is zero, its corresponding eigenvector is linearly dependent.
So the following code would work for simple case:
from sympy import *
import numpy as np
from scipy import linalg
from numpy import linalg
matrix = np.array([[0, 1, 0, 0], [0, 0, 1, 0], [0, 1, 1, 0], [1, 0, 0,
1]])
(lambdas, V) = np.linalg.eig(matrix.T)
print matrix[lambdas == 0, :]
Output: [[0 1 1 0]]

Lagrange multipliers with scipy.optimize.linprog

Is it possible to retriev the Lagrange multipliers from scipy linprog like in Matlab linprog? If so how?
I read the documentation but I didn't find it. There is a return parameter call slack but I think this is something different because it is only related to the inequality constraint:
slack: 1D array
The (nominally positive) values of the slack variables, b_ub - A_ub # x.
Thanks for the help!
Not implemented yet. See How to get Lagrange / lambda multipliers out of 'linprog' optimize subroutine in scipy module ? #11848.
Although my question was already answered by Arraval. I found a work around that I want to share, also using scipy. Linprog hasn't implemented yet but the minimize function can return the Lagrange multipliers when utilizing the method='trust-constr':
I hope this helps.
Starting from scipy 1.7.0, one can also receive the Lagrangian multipliers (also known as dual values or shadow prices) by using the HiGHS dual simplex solver:
import numpy as np
from scipy.optimize import linprog
c = -1*np.array([300, 500])
A_ub = np.array([[1, 2], [1, 1], [0, 3]])
b_ub = np.array([170, 150, 180])
A_eq = np.array([[1, 1]])
b_eq = np.array([80])
# solve c'x s.t. A_ub*x <= b_ub, A_eq*x == b_eq, x >= 0
result = linprog(c=c, A_ub=A_ub, b_ub=b_ub, method="highs-ds")
# lagrangian multipliers
λ_ineq = result['ineqlin']['marginals']
λ_eq = result['eqlin']['marginals']

Calculate state transition matrix in python

Is there a direct way to calculate state transition matrix(i.e. e^(A*t), where A is a matrix)?
I planned to calculate it in this way:
but failed:
And if I directly calculate A*t first and then use expm(), it still cannot work since there should be no variable in expm().
I hope I illustrate my problem clearly :)
EDIT: Here is the code I think should be useful to solve my problem:
import numpy as np
import sympy
import scipy
from scipy.integrate import quad
Ts=0.02
s=sympy.symbols('s')
t=sympy.symbols('t')
T0=np.matrix([[1,0,0],
[0,1,0],
[0,-1,1]])
M0=np.matrix([[1.735,0.15851,0.042262],
[0.123728,0.07019322,0.02070838],
[0.042262,0.0243628,0.014375212]])
F0=np.matrix([[-22.915,0,0],
[0,-0.00969,0.00264],
[0,0.00264,-0.00264]])
N0=np.matrix([[0,0,0],
[0,1.553398,0],
[0,0,0.4141676]])
G0=np.matrix([[11.887],[0],[0]])
Ky=np.matrix([1.0121,4.5728,6.3652,0.9117,1.5246,0.9989])
A21=T0*(M0.I)*N0*(T0.I)
A22=T0*(M0.I)*F0*(T0.I)
Z=np.zeros((3,3))
Y=(np.matrix([0,0,0])).T
by1=np.row_stack((Z,A21))
by2=np.row_stack((np.identity(3),A22))
A=np.column_stack((by1,by2))
G=scipy.linalg.expm(A*Ts)
B2=T0*(M0.I)*G0
B=np.row_stack((Y,B2))
S1=sympy.Matrix((s*np.identity(6))-A)
S2=S1.inv()
S=S2
for (i,j), orinm in scipy.ndenumerate(S2):
S[i,j]=sympy.inverse_laplace_transform(orinm, s, t)
#integral
H=np.zeros(S2.shape, dtype=float)
for (i,j),func_sympy in scipy.ndenumerate(S2):
func = sympy.lambdify( (t),func_sympy, 'math')
H[i,j] = quad(func, 0, 0.02)[0]
print(H)
You can directly calculate the matrix exponential using scipy.
import numpy as np
from scipy.linalg import expm
A = np.random.random((10, 10))
exp_A = expm(A)
The documentation for this is here It uses the Pade approximation.
Here is an example using the 2x2 identity matrix.
>>> expm(np.eye(2))
array([[2.71828183, 0. ],
[0. , 2.71828183]])
If you need the matrix exponential of a symbolic matrix (as per your comment) then you can do this with Sympy:
t = sympy.symbols('t')
A = sympy.Matrix([[t, 0], [0, t]])
>>> sympy.exp(A)
Matrix([
[expt(t), 0],
[0, exp(t)]])

Initial values in scipy.integrate.solve_ivp

I'm trying to use solve_ivp but I don't understand how it deals with the initial values in the argument. The documentation on solve_ivp states:
scipy.integrate.solve_ivp(fun, t_span, y0, method='RK45', t_eval=None, dense_output=False, events=None, vectorized=False, **options)
with
y0 : array_like, shape (n,)
Initial state. For problems in the complex domain, pass y0 with a complex data type (even if the initial guess is purely real)
However, I don't understand the example
>>> from scipy.integrate import solve_ivp
>>> def exponential_decay(t, y): return -0.5 * y
>>> sol = solve_ivp(exponential_decay, [0, 10], [2, 4, 8])
>>> print(sol.t)
[ 0. 0.11487653 1.26364188 3.06061781 4.85759374
6.65456967 8.4515456 10. ]
>>> print(sol.y)
[[2. 1.88836035 1.06327177 0.43319312 0.17648948 0.0719045
0.02929499 0.01350938]
[4. 3.7767207 2.12654355 0.86638624 0.35297895 0.143809
0.05858998 0.02701876]
[8. 7.5534414 4.25308709 1.73277247 0.7059579 0.287618
0.11717996 0.05403753]]
Why do they give an array of 3 initial values here when the differential equation only has one component?
the differential equation only has one component
It doesn't. The function exponential_decay can accept an array as y, and perform operations on that array in a vectorized fashion, as is typical in NumPy.
The initial value determines how many components the unknown function has. In this case, three.

Categories

Resources