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(
L: 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,
):
r"""Solve ``d|state>/dt = L|state>`` using the Crank-Nicolson 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=L.strategy)
def evolve_for_dt(
t: float,
state: MPS,
dt: float,
strategy: Strategy,
) -> MPS:
nonlocal A, B, last_dt
if last_dt != dt or A is None or B is None:
last_dt = dt
A = MPOSum(mpos=[id, L], weights=[1, -0.5 * dt], strategy=L.strategy).join(
strategy=L.strategy
)
B = MPOSum(mpos=[id, L], weights=[1, 0.5 * dt], strategy=L.strategy).join(
strategy=L.strategy
)
# TODO: Consider using dmrg_solve or other algorithm
state, _ = cgs_solve(
A,
B @ state,
guess=state,
tolerance=tol_cgs,
strategy=strategy,
maxiter=maxiter_cgs,
)
return state
return ode_solver(evolve_for_dt, time, state, steps, strategy, callback)