function _noninstant_operators(state, state_indices, operation, backgrounds)
    base = basis(state)
    iscomposite = base isa CompositeBasis
    lindbladians = _embedded_lindblad_operators(state, state_indices, backgrounds)
    ham = express(operation.hamiltonian, QOR)
    ham = iscomposite ? embed(base, state_indices, ham) : ham
    ham, lindbladians
end

function apply_noninstant!(state::Operator, state_indices::Vector{Int},
                           operation::ConstantHamiltonianEvolution, backgrounds)
    ham, lindbladians = _noninstant_operators(
        state, state_indices, operation, backgrounds
    )
    _, sol = timeevolution.master(
        [0, operation.duration], state, ham, lindbladians
    )
    sol[end]
end

function apply_noninstant!(state::Ket, state_indices::Vector{Int}, operation::ConstantHamiltonianEvolution, backgrounds)
    apply_noninstant!(dm(state), state_indices, operation, backgrounds)
end

function apply_noninstant!(state::MCKet, state_indices::Vector{Int},
                           operation::ConstantHamiltonianEvolution, backgrounds)
    ham, lindbladians = _noninstant_operators(
        state, state_indices, operation, backgrounds
    )
    times = [0, operation.duration]
    if isempty(lindbladians)
        _, sol = timeevolution.schroedinger(times, state.ket, ham)
    else
        _, sol = timeevolution.mcwf(times, state.ket, ham, lindbladians)
    end
    MCKet(sol[end])
end

"""
For a given background noise type, provide the corresponding Lindblad collapse operator, in a QuantumOptics.jl representation.

See also: [`paulinoise`](@ref), [`krausops`](@ref)
"""
function lindbladop end

function lindbladop(b::AbstractBackground, basis) # shortcircuit for backgrounds that work on a single basis
    lindbladop(b)
end

"`1/√T₁ |0⟩⟨1|`"
function lindbladop(T1::T1Decay)
    [1/√T1.t1 * _lh]
end

"`1/√τ â`"
function lindbladop(d::AmplitudeDamping, basis)
    [1/√d.τ * destroy(basis)]
end

"`1/√(2T₂) Z`"
function lindbladop(T2::T2Dephasing)
    [1 / √(2*T2.t2) * _z]
end

function lindbladop(D::Depolarization)
    error("we do not have lindblad operators implemented for Depolarization")
end

function lindbladop(P::PauliNoise)
    error("we do not have lindblad operators implemented for PauliNoise")
end

"""
Lindblad operators for combined T₁ and T₂ noise.

Returns a list of Lindblad operators:
- `L₁ = (1/√T₁) |0⟩⟨1|` for amplitude damping
- `L₂ = (1/√(2Tᵩ)) Z` for pure dephasing (if T₂ < 2T₁)

where `1/Tᵩ = 1/T₂ - 1/(2T₁)`

Of note, this is **not** the same as having "on top of each other"
T₁ noise and then an additional "dephasing" noise. As you can see from
the formula above, T₁ is causing dephasing of its own.
Thus, T₂ (transverse relaxation time) includes dephasing from T₁ and pure dephasing Tᵩ.
See https://qiskit-community.github.io/qiskit-experiments/manuals/characterization/tphi.html for more.
"""
function lindbladop(T1T2::T1T2Noise)
    Tᵩ_inv = 1/T1T2.t2 - 1/(2*T1T2.t1)

    if Tᵩ_inv <= 0 # no pure dephasing, so same as T1 implementation above
        return [1/√T1T2.t1 * _lh]
    end

    Tᵩ = 1/Tᵩ_inv
    [1/√T1T2.t1 * _lh, 1/√(2*Tᵩ) * _z]
end
