Solve ode in python with complex matrix as initial value - python

I have a von Neumann equation which looks like:
dr/dt = - i [H, r], where r and H are square matricies of complex numbers and I need to find r(t) using python script.
Is there any standart instruments to integrate such equations?
When I was solving another aquation with a vector as initial value, like Schrodinger equation:
dy/dt = - i H y, I used scipy.integrate.ode function ('zvode'), but trying to use the same function for von Neumann equation gives me the following error:
**scipy/integrate/_ode.py:869: UserWarning: zvode: Illegal input detected. (See printed message.)
ZVODE-- ZWORK length needed, LENZW (=I1), exceeds LZW (=I2)
self.messages.get(istate, 'Unexpected istate=%s' % istate))
In above message, I1 = 72 I2 = 24**
Here is the code:
def integrate(r, t0, t1, dt):
e = linspace(t0, t1, (t1 - t0) / dt + 10)
g = linspace(t0, t1, (t1 - t0) / dt + 10)
u = linspace(t0, t1, (t1 - t0) / dt + 10)
while r.successful() and r.t < t1:
r.integrate(r.t + dt)
e[r.t / dt] = abs(r.y[0][0]) ** 2
g[r.t / dt] = abs(r.y[1][1]) ** 2
u[r.t / dt] = abs(r.y[2][2]) ** 2
return e, g, u
# von Neumann equation's
def right_part(t, rho):
hamiltonian = (h / 2) * array(
[[delta, omega_s, omega_p / 2.0 * sin(t * w_p)],
[omega_s, 0.0, 0.0],
[omega_p / 2.0 * sin(t * w_p), 0.0, 0.0]],
dtype=complex128)
return (dot(hamiltonian, rho) - dot(rho, hamiltonian)) / (1j * h)
def create_integrator():
r = ode(right_part).set_integrator('zvode', method='bdf', with_jacobian=False)
psi_init = array([[1.0, 0.0, 0.0],
[0.0, 0.0, 0.0],
[0.0, 0.0, 0.0]], dtype=complex128)
t0 = 0
r.set_initial_value(psi_init, t0)
return r, t0
def main():
r, t0 = create_integrator()
t1 = 10 ** -6
dt = 10 ** -11
e, g, u = integrate(r, t0, t1, dt)
main()

I have created a wrapper of scipy.integrate.odeint called odeintw that can handle complex matrix equations such as this. See How to plot the Eigenvalues when solving matrix coupled differential equations in PYTHON? for another question involving a matrix differential equation.
Here's a simplified version of your code that shows how you could use it. (For simplicity, I got rid of most of the constants from your example).
import numpy as np
from odeintw import odeintw
def right_part(rho, t, w_p):
hamiltonian = (1. / 2) * np.array(
[[0.1, 0.01, 1.0 / 2.0 * np.sin(t * w_p)],
[0.01, 0.0, 0.0],
[1.0 / 2.0 * np.sin(t * w_p), 0.0, 0.0]],
dtype=np.complex128)
return (np.dot(hamiltonian, rho) - np.dot(rho, hamiltonian)) / (1j)
psi_init = np.array([[1.0, 0.0, 0.0],
[0.0, 0.0, 0.0],
[0.0, 0.0, 0.0]], dtype=np.complex128)
t = np.linspace(0, 10, 101)
sol = odeintw(right_part, psi_init, t, args=(0.25,))
sol will be a complex numpy array with shape (101, 3, 3), holding the solution rho(t). The first index is the time index, and the other two indices are the 3x3 matrix.

QuTiP has some nice integrators for doing just this, using things like Master equations and Lindblad damping terms. QuTiP itself is only a thin wrapper around scipy.odeint, but it makes a lot of the mechanics much nicer, particularly since it has great documentation.

Related

Python - Different regular/analytic functions

To perform the derivative, I have developed the following code:
import matplotlib.pyplot as plt
import numpy as np
from math import *
xi = jnp.linspace(-3,3)
def f(x):
a = x**3+5
return a
g1i = jax.vmap(jax.grad(f))(xi)
g2i = jax.vmap(jax.grad(jax.grad(f)))(xi)
g3i = jax.vmap(jax.grad(jax.grad(jax.grad(f))))(xi)
plt.plot(xi,yi, label = "f")
plt.plot(xi,g1i, label = "f'")
plt.plot(xi,g2i, label = "f''")
plt.plot(xi,g3i, label = "f'''")
plt.legend()
This code works, but now I am interested in apply the following code to compute the first derivative of a Call price, with respect to the underlying asset (i.e. delta), trying with the following, but it does not works:
import scipy.stats as si
import sympy as sy
import sys
xi = jnp.linspace(1,1.5)
def analytical_call(s0):
T=1.
q=0.
r=0.
k=1.
sigma=0.4
Kt = k*exp((q-r)*T)
d = (log(Kt/s0)+(sigma**2)/2*T)/sigma
result = (Kt * si.norm.cdf((d / sqrt(T)), 0.0, 1.0) - s0 * si.norm.cdf(((d - sigma * T) / sqrt(T)), 0.0, 1.0) ) * exp(-q * T) + exp(-q * T) * (s0 - Kt)
return result
print(analytical_call(1))
g1i = jax.vmap(jax.grad(analytical_call))(xi)
g2i = jax.vmap(jax.grad(jax.grad(analytical_call)))(xi)
plt.plot(xi,yi, label = "f")
plt.plot(xi,g1i, label = "f'")
plt.legend()
Have you some hints? Thanks in advance!
As already mentioned in the comments, you can't use methods outside the jax library like scipy.stats.norm.cdf. Use jax.scipy.stats instead. Similarly, replace exp and sqrt with their jax equivalents jnp.exp and jnp.sqrt:
from jax import jit, grad, vmap
import jax.numpy as jnp
from jax.scipy.stats.norm import cdf
def analytical_call(s0):
T, q, r, k, sigma = 1.0, 0.0, 0.0, 1.0, 0.4
Kt = k*jnp.exp((q-r)*T)
d = (jnp.log(Kt/s0)+(sigma**2)/2*T)/sigma
result = (Kt * cdf((d / jnp.sqrt(T)), 0.0, 1.0) - s0 * cdf(((d - sigma * T) / jnp.sqrt(T)), 0.0, 1.0) ) * jnp.exp(-q * T) + jnp.exp(-q * T) * (s0 - Kt)
return result
g = vmap(grad(analytical_call))
h = vmap(grad(grad(analytical_call)))
xi = jnp.linspace(1,1.5)
Then, you can evaluate g(xi) and h(xi).

How calculate a double integral accurately using python

I'm trying to calculate a double integral given by :
import scipy.special as sc
from numpy.lib.scimath import sqrt as csqrt
from scipy.integrate import dblquad
def g_re(alpha, beta, k, m):
psi = csqrt(alpha ** 2 + beta ** 2 - k ** 2)
return np.real(
sc.jv(m, alpha)
* sc.jv(m, beta)
* sc.jv(m, alpha)
* np.sin(beta)
* sc.jv(m, -1j * psi)
* np.exp(-psi)
/ (alpha ** 2 * psi)
)
def g_im(alpha, beta, k, m):
psi = csqrt(alpha ** 2 + beta ** 2 - k ** 2)
return np.imag(
sc.jv(m, alpha)
* sc.jv(m, beta)
* sc.jv(m, alpha)
* np.sin(beta)
* sc.jv(m, -1j * psi)
* np.exp(-psi)
/ (alpha ** 2 * psi)
)
k = 5
m = 0
tuple_args = (k, m)
ans = dblquad(g_re, 0.0, np.inf, 0, np.inf, args=tuple_args)[0]
ans += 1j * dblquad(g_im, 0.0, np.inf, 0, np.inf, args=tuple_args)[0]
The integration intervals are along the positive real axes ([0, np.inf[). When calculating I got the following warning :
/tmp/a.py:10: RuntimeWarning: invalid value encountered in multiply
sc.jv(m, alpha)
g/home/nschloe/.local/lib/python3.9/site-packages/scipy/integrate/quadpack.py:879: IntegrationWarning: The maximum number of subdivisions (50) has been achieved.
If increasing the limit yields no improvement it is advised to analyze
the integrand in order to determine the difficulties. If the position of a
local difficulty can be determined (singularity, discontinuity) one will
probably gain from splitting up the interval and calling the integrator
on the subranges. Perhaps a special-purpose integrator should be used.
quad_r = quad(f, low, high, args=args, full_output=self.full_output,
I subdivided the domain of integration but I still got the same warning. Could you help me please.

Numpy: efficiently apply function that takes surrounding cells

I have a numpy array (Potential) and I would like to compute the electromagnetic field. Right now it is the bottleneck of my program.
I have an array V dimension n+2, m+2. I want to create an Array E dimension n,m. The calculation of each cell is to do cell is ~:
sqrt((Cell_left-Cell_right)^2+(Cell_top-Cell_bottom)^2)
I would like to know if there is a way to apply a function to the whole array to avoid the expensive computation of "for loop" :)
right now my code is :
def set_e(self):
pass
for i in range(0, n):
for j in range(0, m):
self.E[i, j] = self.get_local_e(i, j)
def get_local_e(self, i, j):
return (
((self.solution[i + 2, j + 1] - self.solution[i, j + 1]) / unt_y) ** 2
+ ((self.solution[i + 1, j + 2] - self.solution[i + 1, j]) / unt_x) ** 2
) ** 0.5
Thanks
For the people that are interested in this issue, It is possible to do array calculation that way :
def set_e(self):
y_tmp = np.power((self.solution[:-2, 1:-1] - self.solution[2:, 1:-1]) / unt_y, 2)
x_tmp = np.power((self.solution[1:-1, :-2] - self.solution[1:-1, 2:]) / unt_x, 2)
self.E = np.power(x_tmp + y_tmp, 0.5)
It solved my issue
Let's work on this here.
There is something strange about your equation, as only computes the gradient along one row, see y_tmp.
The gradient function calculates along all rows and columns, that's what the shape of the input is the same as with the output.
import numpy as np
solution = np.array([[1.0, 2.0, 3.0, 4.0],
[3.0, 5.0, 7.0, 9.0],
[5.0, 8.0, 11.0, 14.0],
[7.0, 11.0, 15.0, 19.0]])
unt_y = 1
unt_x = 1
g = np.gradient(solution, unt_y, unt_x)
print(g)
a,b = g
c = np.power(a+b, 2.0)
print(c)
def set_e():
y_tmp = np.power((solution[:-2, 1:-1] - solution[2:, 1:-1]) / unt_y, 2)
print('y_tmp', y_tmp)
x_tmp = np.power((solution[1:-1, :-2] - solution[1:-1, 2:]) / unt_x, 2)
E = np.power(x_tmp + y_tmp, 0.5)
print(E)
set_e()

Fitting data with a custom distribution using scipy.stats

So I noticed that there is no implementation of the Skewed generalized t distribution in scipy. It would be useful for me to fit this is distribution to some data I have. Unfortunately fit doesn't seem to be working in this case for me. To explain further I have implemented it like so
import numpy as np
import pandas as pd
import scipy.stats as st
from scipy.special import beta
class sgt(st.rv_continuous):
def _pdf(self, x, mu, sigma, lam, p, q):
v = q ** (-1 / p) * \
((3 * lam ** 2 + 1) * (
beta(3 / p, q - 2 / p) / beta(1 / p, q)) - 4 * lam ** 2 *
(beta(2 / p, q - 1 / p) / beta(1 / p, q)) ** 2) ** (-1 / 2)
m = 2 * v * sigma * lam * q ** (1 / p) * beta(2 / p, q - 1 / p) / beta(
1 / p, q)
fx = p / (2 * v * sigma * q ** (1 / p) * beta(1 / p, q) * (
abs(x - mu + m) ** p / (q * (v * sigma) ** p) * (
lam * np.sign(x - mu + m) + 1) ** p + 1) ** (
1 / p + q))
return fx
def _argcheck(self, mu, sigma, lam, p, q):
s = sigma > 0
l = -1 < lam < 1
p_bool = p > 0
q_bool = q > 0
all_bool = s & l & p_bool & q_bool
return all_bool
This all works fine and I can generate random variables with given parameters no problem. The _argcheck is required as a simple positive params only check is not suitable.
sgt_inst = sgt(name='sgt')
vars = sgt_inst.rvs(mu=1, sigma=3, lam = -0.1, p = 2, q = 50, size = 100)
However, when I try fit these parameters I get an error
sgt_inst.fit(vars)
RuntimeWarning: invalid value encountered in subtract
numpy.max(numpy.abs(fsim[0] - fsim[1:])) <= fatol):
and it just returns
What I find strange is that when I implement the example custom Gaussian distribution as shown in the docs, it has no problem running the fit method.
Any ideas?
As fit docstring says,
Starting estimates for the fit are given by input arguments; for any arguments not provided with starting estimates, self._fitstart(data) is called to generate such.
Calling sgt_inst._fitstart(data) returns (1.0, 1.0, 1.0, 1.0, 1.0, 0, 1) (the first five are shape parameters, the last two are loc and scale). Looks like _fitstart is not a sophisticated process. The parameter l it picks does not meet your argcheck requirement.
Conclusion: provide your own starting parameters for fit, e.g.,
sgt_inst.fit(data, 0.5, 0.5, -0.5, 2, 10)
returns (1.4587093459289049, 5.471769032259468, -0.02391466905874927, 7.07289326147152
4, 0.741434497805832, -0.07012808188413872, 0.5308181287869771) for my random data.

Changing order of DE in python (scipy.integrate.odeint)

I am facing a problem with differential equation (python):
This is my DE:
m*x"(t) + d*x'(t) + k*x(t) = y(t)
Where y(t) = Y * sin(w*t)
Data:
m=3
d=79
k=200000
w=152
Y=0.05
t=np.linspace(t_0,t_1, n) # not that important.
I have to get numpy.array of x(t) using scipy.integrate.odeint and i am having really big issues transforming my DE of 2. order to DE of 1. order.
What i am looking for is func in scipy.integrate.odeint
https://docs.scipy.org/doc/scipy/reference/generated/scipy.integrate.odeint.html
based on my equations. And primarily and need to find np.array of x(t), then x'(t) and x''(t).
The usual procedure is to set v=x' so that then
x' = v
v' = ( y(t) - d*v - k*x) / m
or
def derivs(u,t):
x,v = u
return [ v, ( y(t) - d*v - k*x) / m ]
def y(t): return Y*np.sin(w*t)
t_0, t_1, n = 0, 1, 501
t=np.linspace(t_0,t_1, n)
x0, v0 = 0.0, 0.0
u0 = [ x0, v0 ]
u = odeint(derivs, u0, t)
plt.subplot(2,1,1); plt.title("x(t)"); plt.plot(t, u[:,0])
plt.subplot(2,1,2); plt.title("x'(t)"); plt.plot(t, u[:,1])
plt.show()
with the result

Categories

Resources