Magnus expansion

U(t,t₀) = exp(Ω(t,t₀)) with

Ω₁ = Σₐ [fₐ] H⁽ᵃ⁾
Ω₂ = ½ Σₐᵦ [fₐfᵦ] [H⁽ᵃ⁾, H⁽ᵇ⁾]
Ω₃ = ⅙ Σₐᵦ𝚌 [fₐfᵦf𝚌] ( [[H⁽ᵃ⁾,H⁽ᵇ⁾],H⁽ᶜ⁾] + [H⁽ᵃ⁾,[H⁽ᵇ⁾,H⁽ᶜ⁾]] )
ψ = time_evolve([(1.0, Hzz), (ramp, Hx)], ψ0, 0.0, 10.0;
                alg = "magnus", order = 2, nsteps = 100)

# ...or the driver directly
Ω = magnus_generator(channels, t0, t1; order = 2)
ψ = magnus_evolve(
    channels, ψ0, 0.0, 10.0;
    nsteps = 100, order = 2,
    tdvp_kwargs = (; cutoff = 1e-10, maxdim = 128),
)

Because the brackets carry the factors of -i, Ω is anti-Hermitian and exp(Ω) is unitary — so unlike the Dyson driver, magnus_evolve preserves the norm exactly. The exponential is applied as tdvp(Ω, 1.0, ψ): the generator already carries the whole step, so the TDVP "time" is 1, matching the paper's exponentiation of Ω at τ = 1.

The commutator structure is what keeps each term extensive — the non-overlapping ("disjoint") contributions cancel between AB and BA.

Orders

Orders 1–3 are supported; order 2 is the right choice (order 3 converges no faster and costs more). See Why Dyson improves order-by-order but Magnus doesn't for why. A fourth-order Ω₄ is not offered: reaching genuine 6th order requires Ω₃ and Ω₄ together in a nested-commutator basis that is nontrivial to get right, so CFET is the route to higher order here — it is both cheaper and more extensible, since commutator-free schemes are specified by coefficient tables rather than commutator algebra.

Reference

ITensorTDMPO.magnus_generatorFunction
magnus_generator(channels::DrivingChannels, t0, t; order = 2, kwargs...)

The Magnus generator Ω(t, t0) up to order, as an MPO, such that U(t, t0) ≈ exp(Ω(t, t0)).

Following Vanthilt et al., writing H(t) = Σₐ fₐ(t) H^{(a)}, each Magnus term is a linear combination of (nested) commutators of the time-independent channel operators, with scalar prefactors given by the time-ordered integrals of the driving functions:

\[\Omega_1 = \sum_a [f_a] H^{(a)}, \qquad \Omega_2 = \tfrac{1}{2} \sum_{ab} [f_a f_b] \, [H^{(a)}, H^{(b)}]\]

The commutator structure is what keeps each term extensive: the non-overlapping ("disjoint") contributions cancel between AB and BA. The […] brackets carry the factors of -i, so Ω is anti-Hermitian for Hermitian channel operators and real driving functions, and exp(Ω) is unitary.

Orders

Supported orders are 1 through 3; 2 is the right default:

ordermeasured convergence ordernote
12exponential midpoint-like
2~3.9the recommended setting
3~3.8no better than 2, and costs more per step

Orders 2 and 3 coincide because the truncated Magnus expansion is time-symmetric, and time-symmetric methods have even order — so the odd term Ω₃ buys nothing. Reaching sixth order would need Ω₄; a partial implementation of it measured ~4th order rather than the expected 6th and was removed rather than shipped unverified. For higher order, prefer cfet_evolve, which reaches fourth order more cheaply and extends to higher-order commutator-free schemes.

prefactor (default -im) sets the factor carried per order; pass -1 for imaginary time, which makes Ω Hermitian and exp(Ω) a non-unitary contraction.

Scope

Commutators are formed by direct MPO multiplication rather than through the paper's finite-state-machine encoding. See Scope and limitations.

source
ITensorTDMPO.magnus_termsFunction
magnus_terms(channels::DrivingChannels, t0, t; order, cutoff, maxdim, npoints, prefactor)

The individual terms of the Magnus generator Ω(t, t0), as a vector of MPOs. Summing them gives magnus_generator.

source
ITensorTDMPO.magnus_evolveFunction
magnus_evolve(channels, ψ0, times; order = 2, kwargs...)
magnus_evolve(channels, ψ0, t_start, t_stop; dt = nothing, nsteps = nothing, kwargs...)

Time-evolve ψ0 under the time-dependent Hamiltonian carried by channels by building the Magnus generator Ω on each interval of times and applying exp(Ω) to the state with TDVP.

For real-time evolution Ω is anti-Hermitian, so exp(Ω) is unitary and the norm is preserved. As with the Dyson driver, the time dependence within each step is captured to the given order rather than frozen.

The exponential is applied as tdvp(Ω, 1.0, ψ): the generator already carries the full step, so the TDVP "time" is 1, exactly as in Sec. IV of Vanthilt et al. where Ω is exponentiated with a Taylor MPO at τ = 1.

Keywords

  • order = 2: order of the Magnus expansion on each step (1–3); see magnus_generator for what each order buys.
  • generator_kwargs = (;): forwarded to magnus_generator, including prefactor for imaginary time.
  • tdvp_kwargs = (; cutoff = 1e-10): forwarded to tdvp. Increase nsteps here if the exponentiation of Ω itself needs refining.
  • normalize = false: renormalize after each step. Leave false for real time, where the evolution is already unitary; set true for imaginary time, where exp(Ω) contracts the state.
  • (step_observer!) = nothing: callback step_observer!(; step, t_start, t_stop, state) after each step.
  • outputlevel = 0: set >= 1 to print progress.

Returns the final evolved MPS.

source
magnus_evolve([(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