Closed hmunozb closed 2 years ago
This new interface does not seem to allow users to easily define their own eigen decomposition routines for existing types like https://uscqserver.github.io/HOQSTTutorials.jl/html/hamiltonian/01-custom_eigen.html.
For the dense Hamiltonian, the only downside is that we cannot define a fix set of eigen values/vectors if the Hamiltonian is constant. However, for the sparse Hamiltonian (or other types), we will need to provide a robust and reliable routine for the generic case. I am not sure if there exist such a library.
I think that the case where a custom eigen routine is needed (and not addressed by an existing Hamiltonian type) might be better addressed by a user-defined implementation of AbstractHamiltonian. It is potentially a little more boilerplate, but you could for example wrap a DenseHamiltonian into a new Hamiltonian type and override the haml_eigs interface. Just to illustrate,
#### My custom two-qubit Hamiltonian
const sui=SMatrix{4,4,ComplexF64,16}((σx+σy)⊗σi)/sqrt(2.0)
const siu=SMatrix{4,4,ComplexF64,16}(σi⊗(σx+σy))/sqrt(2.0)
const szi=SMatrix{4,4,ComplexF64,16}(σz⊗σi)
const siz=SMatrix{4,4,ComplexF64,16}(σi⊗σz)
const szz=SMatrix{4,4,ComplexF64,16}(σz⊗σz)
struct My2QHamiltonian{S} <: AbstractHamiltonian{Float64}
sched::S
s0::Float64
hx::Float64
hz::Float64
H::DenseHamiltonian
end
function My2QHamiltonian(sched, s0, hx, hz)
H = DenseHamiltonian(
[ (s)-> hx*(s0-sched(s)), (s)-> 0.8*hx*(s0-sched(s)),
(s)-> 0.4*hz*sched(s), (s)-> 0.6*hz*sched(s),
(s)-> -1.0*hz*sched(s) ],
[ sui, siu, szi, siz, szz]; unit=:ħ
)
return My2QHamiltonian(sched, s0, hx, hz, H)
end
Base.size(H::My2QHamiltonian) = (4, 4)
Base.size(H::My2QHamiltonian, dim::T) where {T <: Integer} = 4
OpenQuantumBase.get_cache(H::My2QHamiltonian) = H.H.u_cache
function (H::My2QHamiltonian)(s::Real)
return Matrix(H.H(s))
end
function OpenQuantumBase.haml_eigs(H::My2QHamiltonian, t; lvl::Union{Int,Nothing}=nothing)
w,v = eigen(Hermitian(H(t)))
# or some other eigen decomposition
return real(w), v
end
I imagine that removing this level of indirection for the eigendecomposition would also improve the performance of code with custom eigen routines as well as the default one.
I agree. I think we can also define a new type of constant Hamiltonian if there is a need for it. Let me rewrite the tutorial and then merge the pull request.
Actually, if only the eigen routine needs to be overwritten with a user function while keeping all other behavior of Dense/SparseHamiltonian the same, then perhaps we can also include a wrapper class just for that. This would look something like this
struct CustomEigHamiltonian{T<:Number, Htype<:AbstractHamiltonian{T}} <: AbstractHamiltonian{T}
"Wrapped Hamiltonian "
H::Htype
"Custom eigen decomposition routine"
EIGS::Any
end
function CustomEigHamiltonian(H::Htype ; EIGS = EIGEN_DEFAULT) where { T<:Number, Htype<:AbstractHamiltonian{T}}
EIGS = EIGS(H.u_cache)
CustomEigHamiltonian{T, Htype}(H, EIGS)
end
function haml_eigs(H::CustomEigHamiltonian, t; lvl::Union{Int,Nothing}=nothing)
return H.EIGS(H, t, lvl)
end
# etc...
Merged pull request. Several TODOs before closing the issue:
Open new issues for tutorials and documentations #81
The use of a .EIGS member to diagonalize a dense Hamiltonian instead of an interface function is more bulky than necessary in time and allocations.
(Very rough) test of this with a 4 qubit system and a few different schedules suggest up to a ~25% improvement in the performance of the AME
This is simply with this replacement in
diffeq_liouvillian.jl
Some additional optimizations also look possible by making DiffEqLiouvillian and other other objects type-generic, e.g.