jupyter | ||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
Arne L. Grimsmo
Université de Sherbrooke
[email protected]
The "Transfer Tensor Method" was introduced by Cerrillo and Cao in Phys. Rev. Lett 112, 110401 (2014) (arXiv link), as a general method for evolving non-Markovian open quantum systems.
The method takes as input a set of dynamical maps
for an intial set of times
import numpy as np
import qutip as qt
import qutip.solver.nonmarkov.transfertensor as ttm
from qutip.ipynbtools import version_table
%matplotlib inline
import matplotlib.pyplot as plt
As a simple example, we consider the Jaynes-Cummings mode, and the non-Markovian dynamics of the qubit when the cavity is traced out. In this example, the dynamical maps
$$ \mathcal{E}k \rho = {\rm tr}{\rm cav} \left[ {\rm e}^{\mathcal{L} t_k} \rho \otimes \rho_{0,{\rm cav}} \right], $$
where
kappa = 1.0 # cavity decay rate
wc = 0.0 * kappa # cavity frequency
wa = 0.0 * kappa # qubit frequency
g = 10.0 * kappa # coupling strength
N = 3 # size of cavity basis
# intial state
psi0c = qt.basis(N, 0)
rho0c = qt.ket2dm(psi0c)
rho0a = qt.ket2dm(qt.basis(2, 0))
rho0 = qt.tensor(rho0a, rho0c)
rho0avec = qt.operator_to_vector(rho0a)
# identity superoperator
Id = qt.tensor(qt.qeye(2), qt.qeye(N))
E0 = qt.sprepost(Id, Id)
# partial trace over the cavity, reprsented as a superoperator
ptracesuper = qt.tensor_contract(E0, (1, 3))
# intial state of the cavity, represented as a superoperator
superrho0cav = qt.sprepost(
qt.tensor(qt.qeye(2), psi0c), qt.tensor(qt.qeye(2), psi0c.dag())
)
# operators
a = qt.tensor(qt.qeye(2), qt.destroy(N))
sm = qt.tensor(qt.sigmam(), qt.qeye(N))
sz = qt.tensor(qt.sigmaz(), qt.qeye(N))
# Hamiltonian
H = wc * a.dag() * a + wa * sm.dag() * sm + g * (a.dag() * sm + a * sm.dag())
c_ops = [np.sqrt(kappa) * a]
The function dynmap
generates an exact timepropagator for the qubit
prop = qt.Propagator(qt.liouvillian(H, c_ops))
def dynmap(t):
# reduced dynamical map for the qubit at time t
return ptracesuper @ prop(t) @ superrho0cav
dynmap(1.0)
exacttimes = np.arange(0, 5, 0.02)
exactsol = qt.mesolve(H, rho0, exacttimes, c_ops, [sz])
times = np.arange(0, 5, 0.05) # total extrapolation time
ttmsols = []
maxlearningtimes = [0.5, 2.0] # maximal learning times
for T in maxlearningtimes:
learningtimes = np.arange(0, T, 0.05)
# generate exact dynamical maps to learn from
learningmaps = [
dynmap(t) for t in learningtimes
]
# extrapolate using TTM
ttmsols.append(ttm.ttmsolve(learningmaps, rho0a, times))
fig, ax = plt.subplots(figsize=(10, 7))
ax.plot(exactsol.times, exactsol.expect[0], "-b", linewidth=3.0)
style = ["og", "or"]
for i, ttmsol in enumerate(ttmsols):
ax.plot(
ttmsol.times,
qt.expect(qt.sigmaz(), ttmsol.states),
style[i],
linewidth=1.5,
)
ax.legend(["exact", str(maxlearningtimes[0]), str(maxlearningtimes[1])])
ax.set_xlabel(r"$\kappa t$", fontsize=20)
ax.set_ylabel(r"$\sigma_z$", fontsize=20)
The figure above illustrates how the transfer tensor method needs a sufficiently long set of learning times to get good results. The green dots show results for learning times
version_table()