Source code for seemps.evolution.crank_nicolson
from __future__ import annotations
import numpy as np
from ..analysis.operators import id_mpo
from ..solve import cgs_solve
from ..operators import MPO, MPOSum
from ..state import DEFAULT_STRATEGY, MPS, Strategy
from .common import ODECallback, TimeSpan, ode_solver
[docs]
def crank_nicolson(
H: MPO,
time: TimeSpan,
state: MPS,
steps: int = 1000,
tol_cgs: float = 1e-7,
maxiter_cgs: int = 50,
strategy: Strategy = DEFAULT_STRATEGY,
callback: ODECallback | None = None,
itime: bool = False,
):
r"""Solve a Schrodinger equation using a fourth order Runge-Kutta method.
See :func:`seemps.evolution.euler` for a description of the
missing function arguments and the function's output.
Parameters
----------
tol_cgs: float
Tolerance of the CGS algorithm.
maxiter_cgs: int
Maximum number of iterations of the CGS algorithm.
"""
A: MPO | None = None
B: MPO | None = None
last_dt: float = np.inf
id = id_mpo(state.size, strategy=H.strategy)
def evolve_for_dt(
t: float,
state: MPS,
factor: complex | float,
dt: float,
normalize_strategy: Strategy,
) -> MPS:
nonlocal A, B, last_dt
if last_dt != dt or A is None or B is None:
last_dt = dt
idt = factor * dt
A = MPOSum(mpos=[id, H], weights=[1, 0.5 * idt], strategy=H.strategy).join(
strategy=H.strategy
)
B = MPOSum(mpos=[id, H], weights=[1, -0.5 * idt], strategy=H.strategy).join(
strategy=H.strategy
)
# TODO: Consider using dmrg_solve or other algorithm
state, _ = cgs_solve(
A,
B @ state,
guess=state,
tolerance=tol_cgs,
strategy=normalize_strategy,
maxiter=maxiter_cgs,
)
return state
return ode_solver(evolve_for_dt, time, state, steps, strategy, callback, itime)