Piecewise-constant TDVP

The simplest algorithm: freeze H(t) once per interval and run an ordinary TDVP sweep across it.

using ITensorMPS, ITensorTDMPO

sites = siteinds("S=1/2", 20)
Hzz = MPO(OpSum() + ..., sites)          # coupling
Hx  = MPO(OpSum() + ..., sites)          # transverse field
ramp = Ramp(SmoothstepRamp(), 0.0, 10.0, 0.0, 2.0)

ψ = time_evolve(
    [(1.0, Hzz), (ramp, Hx)], ψ0, 0.0, 10.0;
    alg = "piecewise_constant", nsteps = 200, cutoff = 1e-10, maxdim = 128,
)

The driver piecewise_constant_tdvp also accepts an H0/Ht pair directly, which is the more general form when the time dependence is not channel-separable:

piecewise_constant_tdvp(Hzz, t -> ramp(t) * Hx, ψ0, 0.0, 10.0; nsteps = 200)

Note the sign convention: generator_prefactor defaults to -im, so each step applies exp(-i·dt·H). This differs from bare ITensorMPS.tdvp, where you pass -im*t yourself. Pass generator_prefactor = -1 for imaginary-time evolution.

Step calibration

A schedule(t, ψ) -> TDVPStepSpec callback chooses, per step, between 1-site and 2-site TDVP and whether to do a global Krylov subspace expansion first (needed to grow the bond dimension under 1-site TDVP):

schedule = (t, ψ) -> maxlinkdim(ψ) < 128 ?
    TDVPStepSpec(; nsite = 2) :
    TDVPStepSpec(; nsite = 1, expand_krylov = true,
                   expand_kwargs = (; krylovdim = 2, cutoff = 1e-8))
Warning

Never select nsite = 1 without expand_krylov = true when the state cannot already represent the target entanglement. One-site TDVP cannot grow the bond dimension, so starting from a product state it stays at bond dimension 1 and the result is simply wrong — measured error 0.82 (trace distance), independent of step count, on a case where two-site TDVP reaches 1e-7. The default schedule uses nsite = 2.

Other keywords: eval_at (:midpoint or :start), combine_kwargs, step_observer!(; step, t_start, t_stop, state), outputlevel. An explicit non-uniform time grid can be passed in place of t_start, t_stop.

Reference

ITensorTDMPO.piecewise_constant_tdvpFunction
piecewise_constant_tdvp(H0, Ht, ψ0, times; kwargs...)
piecewise_constant_tdvp(H0, Ht, ψ0, t_start, t_stop; dt=nothing, nsteps=nothing, kwargs...)

Time-evolve ψ0 under the time-dependent Hamiltonian H(t) = H0 + Ht(t) (or just Ht(t) if H0 === nothing) using TDVP, approximating H(t) as piecewise constant on each interval of times (or of the regular grid from t_start to t_stop built from dt/nsteps, following the same conventions as ITensorMPS.tdvp).

This is the simplest general driver for adiabatic ramps and other time-dependent Hamiltonians: H(t) is frozen at one evaluation point per interval and the state is evolved across that interval with an ordinary (time-independent) TDVP sweep.

Arguments

  • H0::Union{MPO,Nothing}: the time-independent part of the Hamiltonian, or nothing if the full Hamiltonian is time dependent.
  • Ht: a function Ht(t) -> MPO giving the time-dependent part of the Hamiltonian (or the full Hamiltonian, if H0 === nothing).
  • ψ0::MPS: the initial state.
  • times: the interval endpoints of the piecewise-constant time grid.

Keywords

  • schedule = default_tdvp_schedule: a function schedule(t, ψ) -> TDVPStepSpec calibrating, for the interval evaluated at time t with current state ψ, whether to use 1-site or 2-site TDVP and whether to perform a global Krylov subspace expansion beforehand (needed to grow the bond dimension under 1-site TDVP). See TDVPStepSpec.
  • eval_at = :midpoint: whether to freeze H(t) at the :midpoint or the :start of each interval.
  • generator_prefactor = -im: each step evolves the state by exp(generator_prefactor * dt * H(t)), matching the convention of ITensorMPS.tdvp. The default -im gives real-time Schrödinger evolution; pass -1 for imaginary-time evolution.
  • tdvp_kwargs = (;): keyword arguments forwarded to ITensorMPS.tdvp on every step, e.g. cutoff, maxdim, updater_backend, order.
  • combine_kwargs = (; alg = "directsum"): keyword arguments used to combine H0 and Ht(t), forwarded to +(H0, Ht(t); combine_kwargs...).
  • (step_observer!) = nothing: an optional callback step_observer!(; step, t_start, t_stop, state) invoked after each interval.
  • outputlevel = 0: set to >= 1 to print progress after each step.

Returns the final evolved MPS.

source
piecewise_constant_tdvp(channels::DrivingChannels, ψ0::MPS, times; kwargs...)
piecewise_constant_tdvp(channels, ψ0, t_start, t_stop; dt, nsteps, kwargs...)

Evolve ψ0 with H(t) frozen on each interval, taking the Hamiltonian from a DrivingChannels decomposition rather than an H0/Ht pair. This is the form shared with dyson_evolve and magnus_evolve, so the same Hamiltonian description drives every method; see time_evolve for the common entry point.

operator_cutoff/operator_maxdim control the truncation used when assembling H(t) = Σₐ fₐ(t) H^{(a)} at each evaluation point; all other keywords are as for the H0/Ht method above.

source
piecewise_constant_tdvp([(f1, H1), (f2, H2), ...], ψ0, args...; kwargs...)

Convenience form taking the channels as a plain list of (driving, MPO) tuples or pairs (see DrivingChannels), for calling this driver directly without building the DrivingChannels object yourself.

source
ITensorTDMPO.TDVPStepSpecType
TDVPStepSpec(; nsite, expand_krylov = false, expand_kwargs = (;))

Describes how a single piecewise-constant TDVP step should be carried out: whether to use 1-site or 2-site updates (nsite), and whether to perform a global Krylov subspace expansion (see ITensorMPS.expand with alg = "global_krylov") before the step. Subspace expansion is only needed when nsite == 1, since 1-site TDVP cannot grow the MPS bond dimension on its own.

source
ITensorTDMPO.default_tdvp_scheduleFunction
default_tdvp_schedule(t, ψ)

Default TDVP calibration function: always use 2-site TDVP and never perform a global Krylov expansion, since 2-site TDVP grows the bond dimension on its own. t is the time at which the step is evaluated and ψ is the current MPS state.

source
ITensorTDMPO.DrivenHamiltonianType
DrivenHamiltonian(H0, Ht; combine_kwargs = (; alg = "directsum"))

Bundles a time-independent MPO H0 with a function Ht(t) -> MPO giving the time-dependent part of the Hamiltonian. Calling H(t) returns the total Hamiltonian at time t as an MPO, H0 + Ht(t).

H0 can be nothing if the full Hamiltonian is time dependent, in which case H(t) === Ht(t).

combine_kwargs are passed to +(H0, Ht(t); combine_kwargs...) and default to an exact (non-truncating) direct sum, appropriate for Hamiltonian MPOs which typically already have a small bond dimension.

source