diff --git a/docs/src/resources/api.md b/docs/src/resources/api.md index ff890fea..1728fbb7 100644 --- a/docs/src/resources/api.md +++ b/docs/src/resources/api.md @@ -11,6 +11,9 @@ CurrentModule = QuantumToolbox ## [Quantum object (Qobj) and type](@id doc-API:Quantum-object-and-type) ```@docs +Space +Dimensions +GeneralDimensions AbstractQuantumObject BraQuantumObject Bra diff --git a/docs/src/tutorials/lowrank.md b/docs/src/tutorials/lowrank.md index d7084600..2e1dddbe 100644 --- a/docs/src/tutorials/lowrank.md +++ b/docs/src/tutorials/lowrank.md @@ -41,7 +41,7 @@ M = latt.N + 1 # Number of states in the LR basis Define lr states. Take as initial state all spins up. All other N states are taken as those with miniman Hamming distance to the initial state. ```@example lowrank -ϕ = Vector{QuantumObject{Vector{ComplexF64},KetQuantumObject,M-1}}(undef, M) +ϕ = Vector{QuantumObject{Vector{ComplexF64},KetQuantumObject,Dimensions{M-1}}}(undef, M) ϕ[1] = kron(fill(basis(2, 1), N_modes)...) i = 1 diff --git a/ext/QuantumToolboxCUDAExt.jl b/ext/QuantumToolboxCUDAExt.jl index 7d379c3a..91544157 100644 --- a/ext/QuantumToolboxCUDAExt.jl +++ b/ext/QuantumToolboxCUDAExt.jl @@ -10,35 +10,36 @@ import SparseArrays: SparseVector, SparseMatrixCSC If `A.data` is a dense array, return a new [`QuantumObject`](@ref) where `A.data` is in the type of `CUDA.CuArray` for gpu calculations. """ -CuArray(A::QuantumObject{Tq}) where {Tq<:Union{Vector,Matrix}} = QuantumObject(CuArray(A.data), A.type, A.dims) +CuArray(A::QuantumObject{Tq}) where {Tq<:Union{Vector,Matrix}} = QuantumObject(CuArray(A.data), A.type, A._dims) @doc raw""" CuArray{T}(A::QuantumObject) If `A.data` is a dense array, return a new [`QuantumObject`](@ref) where `A.data` is in the type of `CUDA.CuArray` with element type `T` for gpu calculations. """ -CuArray{T}(A::QuantumObject{Tq}) where {T,Tq<:Union{Vector,Matrix}} = QuantumObject(CuArray{T}(A.data), A.type, A.dims) +CuArray{T}(A::QuantumObject{Tq}) where {T,Tq<:Union{Vector,Matrix}} = QuantumObject(CuArray{T}(A.data), A.type, A._dims) @doc raw""" CuSparseVector(A::QuantumObject) If `A.data` is a sparse vector, return a new [`QuantumObject`](@ref) where `A.data` is in the type of `CUDA.CUSPARSE.CuSparseVector` for gpu calculations. """ -CuSparseVector(A::QuantumObject{<:SparseVector}) = QuantumObject(CuSparseVector(A.data), A.type, A.dims) +CuSparseVector(A::QuantumObject{<:SparseVector}) = QuantumObject(CuSparseVector(A.data), A.type, A._dims) @doc raw""" CuSparseVector{T}(A::QuantumObject) If `A.data` is a sparse vector, return a new [`QuantumObject`](@ref) where `A.data` is in the type of `CUDA.CUSPARSE.CuSparseVector` with element type `T` for gpu calculations. """ -CuSparseVector{T}(A::QuantumObject{<:SparseVector}) where {T} = QuantumObject(CuSparseVector{T}(A.data), A.type, A.dims) +CuSparseVector{T}(A::QuantumObject{<:SparseVector}) where {T} = + QuantumObject(CuSparseVector{T}(A.data), A.type, A._dims) @doc raw""" CuSparseMatrixCSC(A::QuantumObject) If `A.data` is in the type of `SparseMatrixCSC`, return a new [`QuantumObject`](@ref) where `A.data` is in the type of `CUDA.CUSPARSE.CuSparseMatrixCSC` for gpu calculations. """ -CuSparseMatrixCSC(A::QuantumObject{<:SparseMatrixCSC}) = QuantumObject(CuSparseMatrixCSC(A.data), A.type, A.dims) +CuSparseMatrixCSC(A::QuantumObject{<:SparseMatrixCSC}) = QuantumObject(CuSparseMatrixCSC(A.data), A.type, A._dims) @doc raw""" CuSparseMatrixCSC{T}(A::QuantumObject) @@ -46,14 +47,14 @@ CuSparseMatrixCSC(A::QuantumObject{<:SparseMatrixCSC}) = QuantumObject(CuSparseM If `A.data` is in the type of `SparseMatrixCSC`, return a new [`QuantumObject`](@ref) where `A.data` is in the type of `CUDA.CUSPARSE.CuSparseMatrixCSC` with element type `T` for gpu calculations. """ CuSparseMatrixCSC{T}(A::QuantumObject{<:SparseMatrixCSC}) where {T} = - QuantumObject(CuSparseMatrixCSC{T}(A.data), A.type, A.dims) + QuantumObject(CuSparseMatrixCSC{T}(A.data), A.type, A._dims) @doc raw""" CuSparseMatrixCSR(A::QuantumObject) If `A.data` is in the type of `SparseMatrixCSC`, return a new [`QuantumObject`](@ref) where `A.data` is in the type of `CUDA.CUSPARSE.CuSparseMatrixCSR` for gpu calculations. """ -CuSparseMatrixCSR(A::QuantumObject{<:SparseMatrixCSC}) = QuantumObject(CuSparseMatrixCSR(A.data), A.type, A.dims) +CuSparseMatrixCSR(A::QuantumObject{<:SparseMatrixCSC}) = QuantumObject(CuSparseMatrixCSR(A.data), A.type, A._dims) @doc raw""" CuSparseMatrixCSR(A::QuantumObject) @@ -61,7 +62,7 @@ CuSparseMatrixCSR(A::QuantumObject{<:SparseMatrixCSC}) = QuantumObject(CuSparseM If `A.data` is in the type of `SparseMatrixCSC`, return a new [`QuantumObject`](@ref) where `A.data` is in the type of `CUDA.CUSPARSE.CuSparseMatrixCSR` with element type `T` for gpu calculations. """ CuSparseMatrixCSR{T}(A::QuantumObject{<:SparseMatrixCSC}) where {T} = - QuantumObject(CuSparseMatrixCSR{T}(A.data), A.type, A.dims) + QuantumObject(CuSparseMatrixCSR{T}(A.data), A.type, A._dims) @doc raw""" cu(A::QuantumObject; word_size::Int=64) diff --git a/src/correlations.jl b/src/correlations.jl index 5b7068d9..ada51d3d 100644 --- a/src/correlations.jl +++ b/src/correlations.jl @@ -51,8 +51,7 @@ function correlation_3op_2t( ψ0 = steadystate(L) end - allequal((L.dims, ψ0.dims, A.dims, B.dims, C.dims)) || - throw(DimensionMismatch("The quantum objects are not of the same Hilbert dimension.")) + check_dimensions(L, ψ0, A, B, C) kwargs2 = merge((saveat = collect(tlist),), (; kwargs...)) ρt_list = mesolve(L, ψ0, tlist; kwargs2...).states @@ -137,7 +136,7 @@ function correlation_2op_2t( HOpType<:Union{OperatorQuantumObject,SuperOperatorQuantumObject}, StateOpType<:Union{KetQuantumObject,OperatorQuantumObject}, } - C = eye(prod(H.dims), dims = H.dims) + C = eye(prod(H.dimensions), dims = H.dimensions) if reverse corr = correlation_3op_2t(H, ψ0, tlist, τlist, c_ops, A, B, C; kwargs...) else diff --git a/src/negativity.jl b/src/negativity.jl index 26d574ae..d3f2cb6b 100644 --- a/src/negativity.jl +++ b/src/negativity.jl @@ -39,7 +39,7 @@ julia> round(negativity(ρ, 2), digits=2) ``` """ function negativity(ρ::QuantumObject, subsys::Int; logarithmic::Bool = false) - mask = fill(false, length(ρ.dims)) + mask = fill(false, length(ρ.dimensions)) try mask[subsys] = true catch @@ -68,17 +68,17 @@ Return the partial transpose of a density matrix ``\rho``, where `mask` is an ar # Returns - `ρ_pt::QuantumObject`: The density matrix with the selected subsystems transposed. """ -function partial_transpose(ρ::QuantumObject{T,OperatorQuantumObject}, mask::Vector{Bool}) where {T} - if length(mask) != length(ρ.dims) +function partial_transpose(ρ::QuantumObject{DT,OperatorQuantumObject}, mask::Vector{Bool}) where {DT} + if length(mask) != length(ρ.dimensions) throw(ArgumentError("The length of \`mask\` should be equal to the length of \`ρ.dims\`.")) end return _partial_transpose(ρ, mask) end # for dense matrices -function _partial_transpose(ρ::QuantumObject{<:AbstractArray,OperatorQuantumObject}, mask::Vector{Bool}) - isa(ρ.dims, CompoundDimensions) && - (ρ.to != ρ.from) && +function _partial_transpose(ρ::QuantumObject{DT,OperatorQuantumObject}, mask::Vector{Bool}) where {DT<:AbstractArray} + isa(ρ.dimensions, GeneralDimensions) && + (get_dimensions_to(ρ) != get_dimensions_from(ρ)) && throw(ArgumentError("Invalid partial transpose for dims = $(ρ.dims)")) mask2 = [1 + Int(i) for i in mask] @@ -87,27 +87,27 @@ function _partial_transpose(ρ::QuantumObject{<:AbstractArray,OperatorQuantumObj # 2 - the subsystem need be transposed nsys = length(mask2) - dimslist = dims_to_list(ρ.to) + dims = dimensions_to_dims(get_dimensions_to(ρ)) pt_dims = reshape(Vector(1:(2*nsys)), (nsys, 2)) pt_idx = [ - [pt_dims[n, mask2[n]] for n in 1:nsys] # origin value in mask2 - [pt_dims[n, 3-mask2[n]] for n in 1:nsys] # opposite value in mask2 (1 -> 2, and 2 -> 1) + [pt_dims[n, mask2[n]] for n in 1:nsys] # origin value in mask2 + [pt_dims[n, 3-mask2[n]] for n in 1:nsys] # opposite value in mask2 (1 -> 2, and 2 -> 1) ] return QuantumObject( - reshape(permutedims(reshape(ρ.data, (dimslist..., dimslist...)), pt_idx), size(ρ)), + reshape(permutedims(reshape(ρ.data, (dims..., dims...)), pt_idx), size(ρ)), Operator, - Dimensions(ρ.dims.to), + Dimensions(ρ.dimensions.to), ) end # for sparse matrices function _partial_transpose(ρ::QuantumObject{<:AbstractSparseArray,OperatorQuantumObject}, mask::Vector{Bool}) - isa(ρ.dims, CompoundDimensions) && - (ρ.to != ρ.from) && + isa(ρ.dimensions, GeneralDimensions) && + (get_dimensions_to(ρ) != get_dimensions_from(ρ)) && throw(ArgumentError("Invalid partial transpose for dims = $(ρ.dims)")) M, N = size(ρ) - dimsTuple = Tuple(dims_to_list(ρ.to)) + dimsTuple = Tuple(dimensions_to_dims(get_dimensions_to(ρ))) colptr = ρ.data.colptr rowval = ρ.data.rowval nzval = ρ.data.nzval @@ -139,5 +139,5 @@ function _partial_transpose(ρ::QuantumObject{<:AbstractSparseArray,OperatorQuan end end - return QuantumObject(sparse(I_pt, J_pt, V_pt, M, N), Operator, ρ.dims) + return QuantumObject(sparse(I_pt, J_pt, V_pt, M, N), Operator, ρ.dimensions) end diff --git a/src/qobj/arithmetic_and_attributes.jl b/src/qobj/arithmetic_and_attributes.jl index 6af167c6..2e2fdd1e 100644 --- a/src/qobj/arithmetic_and_attributes.jl +++ b/src/qobj/arithmetic_and_attributes.jl @@ -13,23 +13,23 @@ Base.broadcastable(x::QuantumObject) = x.data for op in (:(+), :(-), :(*), :(/), :(^)) @eval begin function Base.Broadcast.broadcasted(::typeof($op), x::QuantumObject, y::QuantumObject) - return QuantumObject(broadcast($op, x.data, y.data), x.type, x.dims) + return QuantumObject(broadcast($op, x.data, y.data), x.type, x.dimensions) end function Base.Broadcast.broadcasted(::typeof($op), x::QuantumObject, y::Number) - return QuantumObject(broadcast($op, x.data, y), x.type, x.dims) + return QuantumObject(broadcast($op, x.data, y), x.type, x.dimensions) end function Base.Broadcast.broadcasted(::typeof($op), x::Number, y::QuantumObject) - return QuantumObject(broadcast($op, x, y.data), y.type, y.dims) + return QuantumObject(broadcast($op, x, y.data), y.type, y.dimensions) end function Base.Broadcast.broadcasted(::typeof($op), x::QuantumObject, y::AbstractArray) - return QuantumObject(broadcast($op, x.data, y), x.type, x.dims) + return QuantumObject(broadcast($op, x.data, y), x.type, x.dimensions) end function Base.Broadcast.broadcasted(::typeof($op), x::AbstractArray, y::QuantumObject) - return QuantumObject(broadcast($op, x, y.data), y.type, y.dims) + return QuantumObject(broadcast($op, x, y.data), y.type, y.dimensions) end end end @@ -37,39 +37,39 @@ end for op in (:(+), :(-), :(*)) @eval begin function LinearAlgebra.$op(A::AbstractQuantumObject, B::AbstractQuantumObject) - check_dims(A, B) + check_dimensions(A, B) QType = promote_op_type(A, B) - return QType($(op)(A.data, B.data), A.type, A.dims) + return QType($(op)(A.data, B.data), A.type, A.dimensions) end - LinearAlgebra.$op(A::AbstractQuantumObject) = get_typename_wrapper(A)($(op)(A.data), A.type, A.dims) + LinearAlgebra.$op(A::AbstractQuantumObject) = get_typename_wrapper(A)($(op)(A.data), A.type, A.dimensions) LinearAlgebra.$op(n::T, A::AbstractQuantumObject) where {T<:Number} = - get_typename_wrapper(A)($(op)(n * I, A.data), A.type, A.dims) + get_typename_wrapper(A)($(op)(n * I, A.data), A.type, A.dimensions) LinearAlgebra.$op(A::AbstractQuantumObject, n::T) where {T<:Number} = - get_typename_wrapper(A)($(op)(A.data, n * I), A.type, A.dims) + get_typename_wrapper(A)($(op)(A.data, n * I), A.type, A.dimensions) end end -function check_mul_dims(from::SVector{NA,AbstractSpace}, to::SVector{NB,AbstractSpace}) where {NA,NB} +function check_mul_dimensions(from::NTuple{NA,AbstractSpace}, to::NTuple{NB,AbstractSpace}) where {NA,NB} (from != to) && throw( DimensionMismatch( - "The quantum object with dims = $from can not multiply a quantum object with dims = $to on the right-hand side.", + "The quantum object with (right) dims = $(dimensions_to_dims(from)) can not multiply a quantum object with (left) dims = $(dimensions_to_dims(to)) on the right-hand side.", ), ) return nothing end -for ADimType in (:Dimensions, :CompoundDimensions) - for BDimType in (:Dimensions, :CompoundDimensions) +for ADimType in (:Dimensions, :GeneralDimensions) + for BDimType in (:Dimensions, :GeneralDimensions) if ADimType == BDimType == :Dimensions @eval begin function LinearAlgebra.:(*)( A::AbstractQuantumObject{DT1,OperatorQuantumObject,$ADimType{NA}}, B::AbstractQuantumObject{DT2,OperatorQuantumObject,$BDimType{NB}}, ) where {DT1,DT2,NA,NB} - check_dims(A, B) + check_dimensions(A, B) QType = promote_op_type(A, B) - return QType(A.data * B.data, Operator, A.dims) + return QType(A.data * B.data, Operator, A.dimensions) end end else @@ -78,9 +78,13 @@ for ADimType in (:Dimensions, :CompoundDimensions) A::AbstractQuantumObject{DT1,OperatorQuantumObject,$ADimType{NA}}, B::AbstractQuantumObject{DT2,OperatorQuantumObject,$BDimType{NB}}, ) where {DT1,DT2,NA,NB} - check_mul_dims(A.from, B.to) + check_mul_dimensions(get_dimensions_from(A), get_dimensions_to(B)) QType = promote_op_type(A, B) - return QType(A.data * B.data, Operator, CompoundDimensions(A.to, B.from)) + return QType( + A.data * B.data, + Operator, + GeneralDimensions{NA}(get_dimensions_to(A), get_dimensions_from(B)), + ) end end end @@ -89,64 +93,64 @@ end function LinearAlgebra.:(*)( A::AbstractQuantumObject{DT1,OperatorQuantumObject}, - B::QuantumObject{DT2,KetQuantumObject}, -) where {DT1,DT2} - check_mul_dims(A.from, B.to) - return QuantumObject(A.data * B.data, Ket, Dimensions(A.to)) + B::QuantumObject{DT2,KetQuantumObject,Dimensions{N}}, +) where {DT1,DT2,N} + check_mul_dimensions(get_dimensions_from(A), get_dimensions_to(B)) + return QuantumObject(A.data * B.data, Ket, Dimensions{N}(get_dimensions_to(A))) end function LinearAlgebra.:(*)( - A::QuantumObject{DT1,BraQuantumObject}, + A::QuantumObject{DT1,BraQuantumObject,Dimensions{N}}, B::AbstractQuantumObject{DT2,OperatorQuantumObject}, -) where {DT1,DT2} - check_mul_dims(A.from, B.to) - return QuantumObject(A.data * B.data, Bra, Dimensions(B.from)) +) where {DT1,DT2,N} + check_mul_dimensions(get_dimensions_from(A), get_dimensions_to(B)) + return QuantumObject(A.data * B.data, Bra, Dimensions{N}(get_dimensions_from(B))) end function LinearAlgebra.:(*)( A::QuantumObject{DT1,KetQuantumObject}, B::QuantumObject{DT2,BraQuantumObject}, ) where {DT1,DT2} - check_dims(A, B) - return QuantumObject(A.data * B.data, Operator, A.dims) # to align with QuTiP, don't use kron(A, B) to do it. + check_dimensions(A, B) + return QuantumObject(A.data * B.data, Operator, A.dimensions) # to align with QuTiP, don't use kron(A, B) to do it. end function LinearAlgebra.:(*)( A::QuantumObject{DT1,BraQuantumObject}, B::QuantumObject{DT2,KetQuantumObject}, ) where {DT1,DT2} - check_dims(A, B) + check_dimensions(A, B) return A.data * B.data end function LinearAlgebra.:(*)( A::AbstractQuantumObject{DT1,SuperOperatorQuantumObject}, B::QuantumObject{DT2,OperatorQuantumObject}, ) where {DT1,DT2} - check_dims(A, B) - return QuantumObject(vec2mat(A.data * mat2vec(B.data)), Operator, A.dims) + check_dimensions(A, B) + return QuantumObject(vec2mat(A.data * mat2vec(B.data)), Operator, A.dimensions) end function LinearAlgebra.:(*)( A::QuantumObject{DT1,OperatorBraQuantumObject}, B::QuantumObject{DT2,OperatorKetQuantumObject}, ) where {DT1,DT2} - check_dims(A, B) + check_dimensions(A, B) return A.data * B.data end function LinearAlgebra.:(*)( A::AbstractQuantumObject{DT1,SuperOperatorQuantumObject}, B::QuantumObject{DT2,OperatorKetQuantumObject}, ) where {DT1,DT2} - check_dims(A, B) - return QuantumObject(A.data * B.data, OperatorKet, A.dims) + check_dimensions(A, B) + return QuantumObject(A.data * B.data, OperatorKet, A.dimensions) end function LinearAlgebra.:(*)( A::QuantumObject{<:AbstractArray{T1},OperatorBraQuantumObject}, B::AbstractQuantumObject{<:AbstractArray{T2},SuperOperatorQuantumObject}, ) where {T1,T2} - check_dims(A, B) - return QuantumObject(A.data * B.data, OperatorBra, A.dims) + check_dimensions(A, B) + return QuantumObject(A.data * B.data, OperatorBra, A.dimensions) end -LinearAlgebra.:(^)(A::QuantumObject{DT}, n::T) where {DT,T<:Number} = QuantumObject(^(A.data, n), A.type, A.dims) +LinearAlgebra.:(^)(A::QuantumObject{DT}, n::T) where {DT,T<:Number} = QuantumObject(^(A.data, n), A.type, A.dimensions) LinearAlgebra.:(/)(A::AbstractQuantumObject{DT}, n::T) where {DT,T<:Number} = - get_typename_wrapper(A)(A.data / n, A.type, A.dims) + get_typename_wrapper(A)(A.data / n, A.type, A.dimensions) @doc raw""" A ⋅ B @@ -163,7 +167,7 @@ function LinearAlgebra.dot( A::QuantumObject{DT1,OpType}, B::QuantumObject{DT2,OpType}, ) where {DT1,DT2,OpType<:Union{KetQuantumObject,OperatorKetQuantumObject}} - A.dims != B.dims && throw(DimensionMismatch("The quantum objects are not of the same Hilbert dimension.")) + check_dimensions(A, B) return LinearAlgebra.dot(A.data, B.data) end @@ -185,8 +189,7 @@ function LinearAlgebra.dot( A::AbstractQuantumObject{DT2,OperatorQuantumObject}, j::QuantumObject{DT3,KetQuantumObject}, ) where {DT1,DT2,DT3} - ((i.dims != A.dims) || (A.dims != j.dims)) && - throw(DimensionMismatch("The quantum objects are not of the same Hilbert dimension.")) + check_dimensions(i, A, j) return LinearAlgebra.dot(i.data, A.data, j.data) end function LinearAlgebra.dot( @@ -194,8 +197,7 @@ function LinearAlgebra.dot( A::AbstractQuantumObject{DT2,SuperOperatorQuantumObject}, j::QuantumObject{DT3,OperatorKetQuantumObject}, ) where {DT1,DT2,DT3} - ((i.dims != A.dims) || (A.dims != j.dims)) && - throw(DimensionMismatch("The quantum objects are not of the same Hilbert dimension.")) + check_dimensions(i, A, j) return LinearAlgebra.dot(i.data, A.data, j.data) end @@ -204,7 +206,7 @@ end Return a similar [`AbstractQuantumObject`](@ref) with `dims` and `type` are same as `A`, but `data` is a zero-array. """ -Base.zero(A::AbstractQuantumObject) = get_typename_wrapper(A)(zero(A.data), A.type, A.dims) +Base.zero(A::AbstractQuantumObject) = get_typename_wrapper(A)(zero(A.data), A.type, A.dimensions) @doc raw""" one(A::AbstractQuantumObject) @@ -216,14 +218,14 @@ Note that `A` must be [`Operator`](@ref) or [`SuperOperator`](@ref). Base.one( A::AbstractQuantumObject{DT,OpType}, ) where {DT,OpType<:Union{OperatorQuantumObject,SuperOperatorQuantumObject}} = - get_typename_wrapper(A)(one(A.data), A.type, A.dims) + get_typename_wrapper(A)(one(A.data), A.type, A.dimensions) @doc raw""" conj(A::AbstractQuantumObject) Return the element-wise complex conjugation of the [`AbstractQuantumObject`](@ref). """ -Base.conj(A::AbstractQuantumObject) = get_typename_wrapper(A)(conj(A.data), A.type, A.dims) +Base.conj(A::AbstractQuantumObject) = get_typename_wrapper(A)(conj(A.data), A.type, A.dimensions) @doc raw""" transpose(A::AbstractQuantumObject) @@ -233,7 +235,7 @@ Lazy matrix transpose of the [`AbstractQuantumObject`](@ref). LinearAlgebra.transpose( A::AbstractQuantumObject{DT,OpType}, ) where {DT,OpType<:Union{OperatorQuantumObject,SuperOperatorQuantumObject}} = - get_typename_wrapper(A)(transpose(A.data), A.type, transpose(A.dims)) + get_typename_wrapper(A)(transpose(A.data), A.type, transpose(A.dimensions)) @doc raw""" A' @@ -248,15 +250,15 @@ Lazy adjoint (conjugate transposition) of the [`AbstractQuantumObject`](@ref) LinearAlgebra.adjoint( A::AbstractQuantumObject{DT,OpType}, ) where {DT,OpType<:Union{OperatorQuantumObject,SuperOperatorQuantumObject}} = - get_typename_wrapper(A)(adjoint(A.data), A.type, transpose(A.dims)) + get_typename_wrapper(A)(adjoint(A.data), A.type, adjoint(A.dimensions)) LinearAlgebra.adjoint(A::QuantumObject{DT,KetQuantumObject}) where {DT} = - QuantumObject(adjoint(A.data), Bra, transpose(A.dims)) + QuantumObject(adjoint(A.data), Bra, adjoint(A.dimensions)) LinearAlgebra.adjoint(A::QuantumObject{DT,BraQuantumObject}) where {DT} = - QuantumObject(adjoint(A.data), Ket, transpose(A.dims)) + QuantumObject(adjoint(A.data), Ket, adjoint(A.dimensions)) LinearAlgebra.adjoint(A::QuantumObject{DT,OperatorKetQuantumObject}) where {DT} = - QuantumObject(adjoint(A.data), OperatorBra, transpose(A.dims)) + QuantumObject(adjoint(A.data), OperatorBra, adjoint(A.dimensions)) LinearAlgebra.adjoint(A::QuantumObject{DT,OperatorBraQuantumObject}) where {DT} = - QuantumObject(adjoint(A.data), OperatorKet, transpose(A.dims)) + QuantumObject(adjoint(A.data), OperatorKet, adjoint(A.dimensions)) @doc raw""" inv(A::AbstractQuantumObject) @@ -266,13 +268,13 @@ Matrix inverse of the [`AbstractQuantumObject`](@ref). If `A` is a [`QuantumObje LinearAlgebra.inv( A::AbstractQuantumObject{DT,OpType}, ) where {DT,OpType<:Union{OperatorQuantumObject,SuperOperatorQuantumObject}} = - QuantumObject(sparse(inv(Matrix(A.data))), A.type, A.dims) + QuantumObject(sparse(inv(Matrix(A.data))), A.type, A.dimensions) LinearAlgebra.Hermitian( A::QuantumObject{DT,OpType}, uplo::Symbol = :U, ) where {DT,OpType<:Union{OperatorQuantumObject,SuperOperatorQuantumObject}} = - QuantumObject(Hermitian(A.data, uplo), A.type, A.dims) + QuantumObject(Hermitian(A.data, uplo), A.type, A.dimensions) @doc raw""" tr(A::QuantumObject) @@ -373,9 +375,9 @@ Also, see [`norm`](@ref) about its definition for different types of [`QuantumOb LinearAlgebra.normalize( A::QuantumObject{<:AbstractArray{T},ObjType}, p::Real = 2, -) where {T,ObjType<:Union{KetQuantumObject,BraQuantumObject}} = QuantumObject(A.data / norm(A, p), A.type, A.dims) +) where {T,ObjType<:Union{KetQuantumObject,BraQuantumObject}} = QuantumObject(A.data / norm(A, p), A.type, A.dimensions) LinearAlgebra.normalize(A::QuantumObject{<:AbstractArray{T},OperatorQuantumObject}, p::Real = 1) where {T} = - QuantumObject(A.data / norm(A, p), A.type, A.dims) + QuantumObject(A.data / norm(A, p), A.type, A.dimensions) @doc raw""" normalize!(A::QuantumObject, p::Real) @@ -407,12 +409,12 @@ LinearAlgebra.triu( A::QuantumObject{<:AbstractArray{T},OpType}, k::Integer = 0, ) where {T,OpType<:Union{OperatorQuantumObject,SuperOperatorQuantumObject}} = - QuantumObject(triu(A.data, k), A.type, A.dims) + QuantumObject(triu(A.data, k), A.type, A.dimensions) LinearAlgebra.tril( A::QuantumObject{<:AbstractArray{T},OpType}, k::Integer = 0, ) where {T,OpType<:Union{OperatorQuantumObject,SuperOperatorQuantumObject}} = - QuantumObject(tril(A.data, k), A.type, A.dims) + QuantumObject(tril(A.data, k), A.type, A.dimensions) LinearAlgebra.lmul!(a::Number, B::QuantumObject{<:AbstractArray}) = (lmul!(a, B.data); B) LinearAlgebra.rmul!(B::QuantumObject{<:AbstractArray}, a::Number) = (rmul!(B.data, a); B) @@ -430,7 +432,7 @@ Matrix square root of [`QuantumObject`](@ref) `√(A)` (where `√` can be typed by tab-completing `\sqrt` in the REPL) is a synonym of `sqrt(A)`. """ LinearAlgebra.sqrt(A::QuantumObject{<:AbstractArray{T}}) where {T} = - QuantumObject(sqrt(sparse_to_dense(A.data)), A.type, A.dims) + QuantumObject(sqrt(sparse_to_dense(A.data)), A.type, A.dimensions) @doc raw""" log(A::QuantumObject) @@ -442,7 +444,7 @@ Note that this function only supports for [`Operator`](@ref) and [`SuperOperator LinearAlgebra.log( A::QuantumObject{<:AbstractMatrix{T},ObjType}, ) where {T,ObjType<:Union{OperatorQuantumObject,SuperOperatorQuantumObject}} = - QuantumObject(log(sparse_to_dense(A.data)), A.type, A.dims) + QuantumObject(log(sparse_to_dense(A.data)), A.type, A.dimensions) @doc raw""" exp(A::QuantumObject) @@ -454,11 +456,11 @@ Note that this function only supports for [`Operator`](@ref) and [`SuperOperator LinearAlgebra.exp( A::QuantumObject{<:AbstractMatrix{T},ObjType}, ) where {T,ObjType<:Union{OperatorQuantumObject,SuperOperatorQuantumObject}} = - QuantumObject(dense_to_sparse(exp(A.data)), A.type, A.dims) + QuantumObject(dense_to_sparse(exp(A.data)), A.type, A.dimensions) LinearAlgebra.exp( A::QuantumObject{<:AbstractSparseMatrix{T},ObjType}, ) where {T,ObjType<:Union{OperatorQuantumObject,SuperOperatorQuantumObject}} = - QuantumObject(_spexp(A.data), A.type, A.dims) + QuantumObject(_spexp(A.data), A.type, A.dimensions) function _spexp(A::SparseMatrixCSC{T,M}; threshold = 1e-14, nonzero_tol = 1e-20) where {T,M} m = checksquare(A) # Throws exception if not square @@ -587,7 +589,7 @@ function ptrace(QO::QuantumObject{<:AbstractArray,KetQuantumObject}, sel::Union{ if n_s == 0 # return full trace for empty sel return tr(ket2dm(QO)) else - n_d = length(QO.dims) + n_d = length(QO.dimensions) (any(>(n_d), sel) || any(<(1), sel)) && throw( ArgumentError("Invalid indices in `sel`: $(sel), the given QuantumObject only have $(n_d) sub-systems"), @@ -596,17 +598,16 @@ function ptrace(QO::QuantumObject{<:AbstractArray,KetQuantumObject}, sel::Union{ (n_d == 1) && return ket2dm(QO) # ptrace should always return Operator end - dimslist = dims_to_list(QO.dims) _sort_sel = sort(SVector{length(sel),Int}(sel)) - ρtr, dkeep = _ptrace_ket(QO.data, dimslist, _sort_sel) + ρtr, dkeep = _ptrace_ket(QO.data, QO.dims, _sort_sel) return QuantumObject(ρtr, type = Operator, dims = Dimensions(dkeep)) end ptrace(QO::QuantumObject{<:AbstractArray,BraQuantumObject}, sel::Union{AbstractVector{Int},Tuple}) = ptrace(QO', sel) function ptrace(QO::QuantumObject{<:AbstractArray,OperatorQuantumObject}, sel::Union{AbstractVector{Int},Tuple}) - isa(QO.dims, CompoundDimensions) && - (QO.to != QO.from) && + isa(QO.dimensions, GeneralDimensions) && + (get_dimensions_to(QO) != get_dimensions_from(QO)) && throw(ArgumentError("Invalid partial trace for dims = $(QO.dims)")) _non_static_array_warning("sel", sel) @@ -615,7 +616,7 @@ function ptrace(QO::QuantumObject{<:AbstractArray,OperatorQuantumObject}, sel::U if n_s == 0 # return full trace for empty sel return tr(QO) else - n_d = length(QO.dims) + n_d = length(QO.dimensions) (any(>(n_d), sel) || any(<(1), sel)) && throw( ArgumentError("Invalid indices in `sel`: $(sel), the given QuantumObject only have $(n_d) sub-systems"), @@ -624,9 +625,9 @@ function ptrace(QO::QuantumObject{<:AbstractArray,OperatorQuantumObject}, sel::U (n_d == 1) && return QO end - dimslist = dims_to_list(QO.to) + dims = dimensions_to_dims(get_dimensions_to(QO)) _sort_sel = sort(SVector{length(sel),Int}(sel)) - ρtr, dkeep = _ptrace_oper(QO.data, dimslist, _sort_sel) + ρtr, dkeep = _ptrace_oper(QO.data, dims, _sort_sel) return QuantumObject(ρtr, type = Operator, dims = Dimensions(dkeep)) end ptrace(QO::QuantumObject, sel::Int) = ptrace(QO, SVector(sel)) @@ -713,7 +714,7 @@ purity(ρ::QuantumObject{<:AbstractArray{T},OperatorQuantumObject}) where {T} = Given a [`QuantumObject`](@ref) `A`, check the real and imaginary parts of each element separately. Remove the real or imaginary value if its absolute value is less than `tol`. """ tidyup(A::QuantumObject{<:AbstractArray{T}}, tol::T2 = 1e-14) where {T,T2<:Real} = - QuantumObject(tidyup(A.data, tol), A.type, A.dims) + QuantumObject(tidyup(A.data, tol), A.type, A.dimensions) tidyup(A::AbstractArray{T}, tol::T2 = 1e-14) where {T,T2<:Real} = tidyup!(copy(A), tol) @doc raw""" @@ -737,7 +738,7 @@ tidyup!(A::AbstractArray{T}, tol::T2 = 1e-14) where {T,T2<:Real} = Returns the data of a [`AbstractQuantumObject`](@ref). """ -get_data(A::AbstractQuantumObject) = A.data +get_data(A::AbstractQuantumObject) = getfield(A, :data) @doc raw""" get_coherence(ψ::QuantumObject) @@ -747,7 +748,7 @@ Get the coherence value ``\alpha`` by measuring the expectation value of the des It returns both ``\alpha`` and the corresponding state with the coherence removed: ``\ket{\delta_\alpha} = \exp ( \alpha^* \hat{a} - \alpha \hat{a}^\dagger ) \ket{\psi}`` for a pure state, and ``\hat{\rho_\alpha} = \exp ( \alpha^* \hat{a} - \alpha \hat{a}^\dagger ) \hat{\rho} \exp ( -\bar{\alpha} \hat{a} + \alpha \hat{a}^\dagger )`` for a density matrix. These states correspond to the quantum fluctuations around the coherent state ``\ket{\alpha}`` or ``|\alpha\rangle\langle\alpha|``. """ function get_coherence(ψ::QuantumObject{<:AbstractArray,KetQuantumObject}) - a = destroy(prod(ψ.dims)) + a = destroy(prod(ψ.dimensions)) α = expect(a, ψ) D = exp(α * a' - conj(α) * a) @@ -755,7 +756,7 @@ function get_coherence(ψ::QuantumObject{<:AbstractArray,KetQuantumObject}) end function get_coherence(ρ::QuantumObject{<:AbstractArray,OperatorQuantumObject}) - a = destroy(prod(ρ.dims)) + a = destroy(prod(ρ.dimensions)) α = expect(a, ρ) D = exp(α * a' - conj(α) * a) @@ -793,7 +794,7 @@ function permute( A::QuantumObject{<:AbstractArray{T},ObjType}, order::Union{AbstractVector{Int},Tuple}, ) where {T,ObjType<:Union{KetQuantumObject,BraQuantumObject,OperatorQuantumObject}} - (length(order) != length(A.dims)) && + (length(order) != length(A.dimensions)) && throw(ArgumentError("The order list must have the same length as the number of subsystems (A.dims)")) !isperm(order) && throw(ArgumentError("$(order) is not a valid permutation of the subsystems (A.dims)")) @@ -803,31 +804,28 @@ function permute( order_svector = SVector{length(order),Int}(order) # convert it to SVector for performance # obtain the arguments: dims for reshape; perm for PermutedDimsArray - dimslist = dims_to_list(A.dims) - dims, perm = _dims_and_perm(A.type, dimslist, order_svector, length(order_svector)) + dims, perm = _dims_and_perm(A.type, A.dims, order_svector, length(order_svector)) - return QuantumObject( - reshape(permutedims(reshape(A.data, dims...), Tuple(perm)), size(A)), - A.type, - Dimensions(A.dims.to[order_svector]), - ) + order_dimensions = _order_dimensions(A.dimensions, order_svector) + + return QuantumObject(reshape(permutedims(reshape(A.data, dims...), Tuple(perm)), size(A)), A.type, order_dimensions) end _dims_and_perm( ::ObjType, - dimslist::SVector{N,Int}, + dims::SVector{N,Int}, order::AbstractVector{Int}, L::Int, -) where {ObjType<:Union{KetQuantumObject,BraQuantumObject},N} = reverse(dimslist), reverse((L + 1) .- order) +) where {ObjType<:Union{KetQuantumObject,BraQuantumObject},N} = reverse(dims), reverse((L + 1) .- order) # if dims originates from Dimensions -_dims_and_perm(::OperatorQuantumObject, dimslist::SVector{N,Int}, order::AbstractVector{Int}, L::Int) where {N} = - reverse(vcat(dimslist, dimslist)), reverse((2 * L + 1) .- vcat(order, order .+ L)) +_dims_and_perm(::OperatorQuantumObject, dims::SVector{N,Int}, order::AbstractVector{Int}, L::Int) where {N} = + reverse(vcat(dims, dims)), reverse((2 * L + 1) .- vcat(order, order .+ L)) -# if dims originates from CompoundDimensions -_dims_and_perm( - ::OperatorQuantumObject, - dimslist::SVector{2,SVector{N,Int}}, - order::AbstractVector{Int}, - L::Int, -) where {N} = reverse(vcat(dimslist[1], dimslist[2])), reverse((2 * L + 1) .- vcat(order, order .+ L)) +# if dims originates from GeneralDimensions +_dims_and_perm(::OperatorQuantumObject, dims::SVector{2,SVector{N,Int}}, order::AbstractVector{Int}, L::Int) where {N} = + reverse(vcat(dims[1], dims[2])), reverse((2 * L + 1) .- vcat(order, order .+ L)) + +_order_dimensions(dimensions::Dimensions{N}, order::AbstractVector{Int}) where {N} = Dimensions{N}(dimensions.to[order]) +_order_dimensions(dimensions::GeneralDimensions{N}, order::AbstractVector{Int}) where {N} = + GeneralDimensions{N}(dimensions.to[order], dimensions.from[order]) diff --git a/src/qobj/block_diagonal_form.jl b/src/qobj/block_diagonal_form.jl index cc1f97ea..ca3e89a6 100644 --- a/src/qobj/block_diagonal_form.jl +++ b/src/qobj/block_diagonal_form.jl @@ -54,7 +54,7 @@ function block_diagonal_form( A::QuantumObject{DT,OpType}, ) where {DT<:AbstractSparseMatrix,OpType<:Union{OperatorQuantumObject,SuperOperatorQuantumObject}} bdf = block_diagonal_form(A.data) - B = QuantumObject(bdf.B, type = A.type, dims = A.dims) - P = QuantumObject(bdf.P, type = A.type, dims = A.dims) + B = QuantumObject(bdf.B, type = A.type, dims = A.dimensions) + P = QuantumObject(bdf.P, type = A.type, dims = A.dimensions) return BlockDiagonalForm(B, P, bdf.blocks, bdf.block_sizes) end diff --git a/src/qobj/dimensions.jl b/src/qobj/dimensions.jl index a9aeb886..041456b1 100644 --- a/src/qobj/dimensions.jl +++ b/src/qobj/dimensions.jl @@ -1,39 +1,50 @@ -export AbstractDimensions, Dimensions, CompoundDimensions +#= +This file defines the Dimensions structures, which can describe composite Hilbert spaces. +=# + +export AbstractDimensions, Dimensions, GeneralDimensions abstract type AbstractDimensions{N} end -# this show function is for printing AbstractDimensions -function Base.show(io::IO, svec::SVector{N,AbstractSpace}) where {N} - print(io, "[") - join(io, string.(svec), ", ") - return print(io, "]") -end +@doc raw"""" + struct Dimensions{N} <: AbstractDimensions{N} + to::NTuple{N,AbstractSpace} + end +A structure that describes the Hilbert [`Space`](@ref) of each subsystems. +""" struct Dimensions{N} <: AbstractDimensions{N} - to::SVector{N,AbstractSpace} + to::NTuple{N,AbstractSpace} end function Dimensions(dims::Union{AbstractVector{T},NTuple{N,T}}) where {T<:Integer,N} _non_static_array_warning("dims", dims) L = length(dims) (L > 0) || throw(DomainError(dims, "The argument dims must be of non-zero length")) - return Dimensions{L}(SVector{L,AbstractSpace}(Space.(dims))) + return Dimensions{L}(NTuple{L,Space}(Space.(dims))) end -Dimensions(dims::Int) = Dimensions(SVector{1,Int}(dims)) +Dimensions(dims::Int) = Dimensions(Space(dims)) +Dimensions(dims::DimType) where {DimType<:AbstractSpace} = Dimensions(NTuple{1,DimType}((dims,))) Dimensions(dims::Any) = throw( ArgumentError( "The argument dims must be a Tuple or a StaticVector of non-zero length and contain only positive integers.", ), ) -Base.show(io::IO, D::Dimensions) = print(io, D.to) +@doc raw"""" + struct GeneralDimensions{N} <: AbstractDimensions{N} + to::NTuple{N,AbstractSpace} + from::NTuple{N,AbstractSpace} + end -struct CompoundDimensions{N} <: AbstractDimensions{N} +A structure that describes the left-hand side (`to`) and right-hand side (`from`) Hilbert [`Space`](@ref) of an [`Operator`](@ref). +""" +struct GeneralDimensions{N} <: AbstractDimensions{N} # note that the number `N` should be the same for both `to` and `from` - to::SVector{N,AbstractSpace} # space acting on the left - from::SVector{N,AbstractSpace} # space acting on the right + to::NTuple{N,AbstractSpace} # space acting on the left + from::NTuple{N,AbstractSpace} # space acting on the right end -function CompoundDimensions(dims::Union{AbstractVector{T},NTuple{N,T}}) where {T<:Union{AbstractVector,NTuple},N} +function GeneralDimensions(dims::Union{AbstractVector{T},NTuple{N,T}}) where {T<:Union{AbstractVector,NTuple},N} (length(dims) != 2) && throw(ArgumentError("Invalid dims = $dims")) _non_static_array_warning("dims[1]", dims[1]) @@ -48,27 +59,20 @@ function CompoundDimensions(dims::Union{AbstractVector{T},NTuple{N,T}}) where {T ), ) - return CompoundDimensions{L1}( - SVector{L1,AbstractSpace}(Space.(dims[1])), - SVector{L1,AbstractSpace}(Space.(dims[2])), - ) + return GeneralDimensions{L1}(NTuple{L1,Space}(Space.(dims[1])), NTuple{L1,Space}(Space.(dims[2]))) end -Base.show(io::IO, D::CompoundDimensions) = print(io, "[", D.to, ", ", D.from, "]") - -_gen_dims(dims::AbstractDimensions) = dims -_gen_dims(dims::Union{AbstractVector{T},NTuple{N,T}}) where {T<:Integer,N} = Dimensions(dims) -_gen_dims(dims::Union{AbstractVector{T},NTuple{N,T}}) where {T<:Union{AbstractVector,NTuple},N} = - CompoundDimensions(dims) -_gen_dims(dims::Any) = Dimensions(dims) +_gen_dimensions(dims::AbstractDimensions) = dims +_gen_dimensions(dims::Union{AbstractVector{T},NTuple{N,T}}) where {T<:Integer,N} = Dimensions(dims) +_gen_dimensions(dims::Union{AbstractVector{T},NTuple{N,T}}) where {T<:Union{AbstractVector,NTuple},N} = + GeneralDimensions(dims) +_gen_dimensions(dims::Any) = Dimensions(dims) # obtain dims in the type of SVector with integers -dims_to_list(dimsvec::SVector{N,AbstractSpace}) where {N} = SVector{N,Int}(ntuple(i -> dimsvec[i].size, Val(N))) -dims_to_list(dims::Dimensions) = dims_to_list(dims.to) -dims_to_list(dims::CompoundDimensions) = SVector{2}(dims_to_list(dims.to), dims_to_list(dims.from)) - -Base.:(==)(vect::AbstractVector{T}, dims::AbstractDimensions) where {T} = vect == dims_to_list(dims) -Base.:(==)(dims::AbstractDimensions, vect::AbstractVector{T}) where {T} = vect == dims +dimensions_to_dims(dimensions::NTuple{N,AbstractSpace}) where {N} = vcat(map(dimensions_to_dims, dimensions)...) +dimensions_to_dims(dimensions::Dimensions) = dimensions_to_dims(dimensions.to) +dimensions_to_dims(dimensions::GeneralDimensions) = + (dimensions_to_dims(dimensions.to), dimensions_to_dims(dimensions.from)) # tuple Base.length(::AbstractDimensions{N}) where {N} = N @@ -76,7 +80,8 @@ Base.length(::AbstractDimensions{N}) where {N} = N # otherwise the type of `prod(::Dimensions)` will be unstable _get_space_size(s::AbstractSpace)::Int = s.size Base.prod(dims::Dimensions) = prod(dims.to) -Base.prod(spaces::SVector{N,AbstractSpace}) where {N} = prod(_get_space_size, spaces) +Base.prod(spaces::NTuple{N,AbstractSpace}) where {N} = prod(_get_space_size, spaces) -LinearAlgebra.transpose(dims::Dimensions) = dims -LinearAlgebra.transpose(dims::CompoundDimensions) = CompoundDimensions(dims.from, dims.to) # switch `to` and `from` +LinearAlgebra.transpose(dimensions::Dimensions) = dimensions +LinearAlgebra.transpose(dimensions::GeneralDimensions) = GeneralDimensions(dimensions.from, dimensions.to) # switch `to` and `from` +LinearAlgebra.adjoint(dimensions::AbstractDimensions) = transpose(dimensions) diff --git a/src/qobj/eigsolve.jl b/src/qobj/eigsolve.jl index 929673ef..ca8b6f18 100644 --- a/src/qobj/eigsolve.jl +++ b/src/qobj/eigsolve.jl @@ -23,11 +23,14 @@ A struct containing the eigenvalues, the eigenvectors, and some information from - `values::AbstractVector`: the eigenvalues - `vectors::AbstractMatrix`: the transformation matrix (eigenvectors) - `type::Union{Nothing,QuantumObjectType}`: the type of [`QuantumObject`](@ref), or `nothing` means solving eigen equation for general matrix -- `dims::AbstractDimensions`: the `dims` of [`QuantumObject`](@ref) +- `dimensions::AbstractDimensions`: the `dimensions` of [`QuantumObject`](@ref) - `iter::Int`: the number of iteration during the solving process - `numops::Int` : number of times the linear map was applied in krylov methods - `converged::Bool`: Whether the result is converged +!!! note "`dims` property" + For a given `res::EigsolveResult`, `res.dims` or `getproperty(res, :dims)` returns its `dimensions` in the type of integer-vector. + # Examples One can obtain the eigenvalues and the corresponding [`QuantumObject`](@ref)-type eigenvectors by: ```jldoctest @@ -50,7 +53,7 @@ julia> λ 1.0 + 0.0im julia> ψ -2-element Vector{QuantumObject{Vector{ComplexF64}, KetQuantumObject, 1}}: +2-element Vector{QuantumObject{Vector{ComplexF64}, KetQuantumObject, Dimensions{1}}}: Quantum Object: type=Ket dims=[2] size=(2,) 2-element Vector{ComplexF64}: -0.7071067811865475 + 0.0im @@ -75,19 +78,23 @@ struct EigsolveResult{ values::T1 vectors::T2 type::ObjType - dims::DimType + dimensions::DimType iter::Int numops::Int converged::Bool end +Base.getproperty(res::EigsolveResult, key::Symbol) = getproperty(res, makeVal(key)) +Base.getproperty(res::EigsolveResult, ::Val{:dims}) = dimensions_to_dims(getfield(res, :dimensions)) +Base.getproperty(res::EigsolveResult, ::Val{K}) where {K} = getfield(res, K) + Base.iterate(res::EigsolveResult) = (res.values, Val(:vector_list)) Base.iterate(res::EigsolveResult{T1,T2,Nothing}, ::Val{:vector_list}) where {T1,T2} = ([res.vectors[:, k] for k in 1:length(res.values)], Val(:vectors)) Base.iterate(res::EigsolveResult{T1,T2,OperatorQuantumObject}, ::Val{:vector_list}) where {T1,T2} = - ([QuantumObject(res.vectors[:, k], Ket, res.dims) for k in 1:length(res.values)], Val(:vectors)) + ([QuantumObject(res.vectors[:, k], Ket, res.dimensions) for k in 1:length(res.values)], Val(:vectors)) Base.iterate(res::EigsolveResult{T1,T2,SuperOperatorQuantumObject}, ::Val{:vector_list}) where {T1,T2} = - ([QuantumObject(res.vectors[:, k], OperatorKet, res.dims) for k in 1:length(res.values)], Val(:vectors)) + ([QuantumObject(res.vectors[:, k], OperatorKet, res.dimensions) for k in 1:length(res.values)], Val(:vectors)) Base.iterate(res::EigsolveResult, ::Val{:vectors}) = (res.vectors, Val(:done)) Base.iterate(res::EigsolveResult, ::Val{:done}) = nothing @@ -281,7 +288,7 @@ function eigsolve( A.data; v0 = v0, type = A.type, - dims = A.dims, + dims = A.dimensions, sigma = sigma, k = k, krylovdim = krylovdim, @@ -296,7 +303,7 @@ function eigsolve( A; v0::Union{Nothing,AbstractVector} = nothing, type::Union{Nothing,OperatorQuantumObject,SuperOperatorQuantumObject} = nothing, - dims = Dimensions{0}(SVector{0,AbstractSpace}()), + dims = Dimensions{0}(NTuple{0,AbstractSpace}()), sigma::Union{Nothing,Real} = nothing, k::Int = 1, krylovdim::Int = max(20, 2 * k + 1), @@ -332,7 +339,7 @@ function eigsolve( vals = @. (1 + sigma * res.values) / res.values end - return EigsolveResult(vals, res.vectors, res.type, res.dims, res.iter, res.numops, res.converged) + return EigsolveResult(vals, res.vectors, res.type, res.dimensions, res.iter, res.numops, res.converged) end @doc raw""" @@ -392,7 +399,7 @@ function eigsolve_al( prob = mesolveProblem( L_evo, - QuantumObject(ρ0, type = Operator, dims = H.dims), + QuantumObject(ρ0, type = Operator, dims = H.dimensions), [zero(T), T]; params = params, progress_bar = Val(false), @@ -404,7 +411,7 @@ function eigsolve_al( Lmap = ArnoldiLindbladIntegratorMap(eltype(DT1), size(L_evo), integrator) - res = _eigsolve(Lmap, mat2vec(ρ0), L_evo.type, L_evo.dims, k, krylovdim, maxiter = maxiter, tol = eigstol) + res = _eigsolve(Lmap, mat2vec(ρ0), L_evo.type, L_evo.dimensions, k, krylovdim, maxiter = maxiter, tol = eigstol) # finish!(prog) vals = similar(res.values) @@ -416,7 +423,7 @@ function eigsolve_al( @. vecs[:, i] = vec * exp(-1im * angle(vec[1])) end - return EigsolveResult(vals, vecs, res.type, res.dims, res.iter, res.numops, res.converged) + return EigsolveResult(vals, vecs, res.type, res.dimensions, res.iter, res.numops, res.converged) end @doc raw""" @@ -460,7 +467,7 @@ function LinearAlgebra.eigen( E::mat2vec(sparse_to_dense(MT)) = F.values U::sparse_to_dense(MT) = F.vectors - return EigsolveResult(E, U, A.type, A.dims, 0, 0, true) + return EigsolveResult(E, U, A.type, A.dimensions, 0, 0, true) end @doc raw""" diff --git a/src/qobj/functions.jl b/src/qobj/functions.jl index b2da348c..49825b29 100644 --- a/src/qobj/functions.jl +++ b/src/qobj/functions.jl @@ -105,7 +105,7 @@ variance(O::QuantumObject{DT1,OperatorQuantumObject}, ψ::Vector{<:QuantumObject Converts a sparse QuantumObject to a dense QuantumObject. """ -sparse_to_dense(A::QuantumObject{<:AbstractVecOrMat}) = QuantumObject(sparse_to_dense(A.data), A.type, A.dims) +sparse_to_dense(A::QuantumObject{<:AbstractVecOrMat}) = QuantumObject(sparse_to_dense(A.data), A.type, A.dimensions) sparse_to_dense(A::MT) where {MT<:AbstractSparseArray} = Array(A) for op in (:Transpose, :Adjoint) @eval sparse_to_dense(A::$op{T,<:AbstractSparseMatrix}) where {T<:BlasFloat} = Array(A) @@ -132,7 +132,7 @@ sparse_to_dense(::Type{M}) where {M<:AbstractMatrix} = M Converts a dense QuantumObject to a sparse QuantumObject. """ dense_to_sparse(A::QuantumObject{<:AbstractVecOrMat}, tol::Real = 1e-10) = - QuantumObject(dense_to_sparse(A.data, tol), A.type, A.dims) + QuantumObject(dense_to_sparse(A.data, tol), A.type, A.dimensions) function dense_to_sparse(A::MT, tol::Real = 1e-10) where {MT<:AbstractMatrix} idxs = findall(@. abs(A) > tol) row_indices = getindex.(idxs, 1) @@ -183,12 +183,12 @@ function LinearAlgebra.kron( B::AbstractQuantumObject{DT2,OpType,Dimensions{NB}}, ) where {DT1,DT2,OpType<:Union{KetQuantumObject,BraQuantumObject,OperatorQuantumObject},NA,NB} QType = promote_op_type(A, B) - return QType(kron(A.data, B.data), A.type, Dimensions{NA + NB}(vcat(A.dims.to, B.dims.to))) + return QType(kron(A.data, B.data), A.type, Dimensions{NA + NB}((A.dimensions.to..., B.dimensions.to...))) end -# if A and B are both Operator but either one of them has CompoundDimensions -for ADimType in (:Dimensions, :CompoundDimensions) - for BDimType in (:Dimensions, :CompoundDimensions) +# if A and B are both Operator but either one of them has GeneralDimensions +for ADimType in (:Dimensions, :GeneralDimensions) + for BDimType in (:Dimensions, :GeneralDimensions) if !(ADimType == BDimType == :Dimensions) # not for this case because it's already implemented @eval begin function LinearAlgebra.kron( @@ -199,7 +199,10 @@ for ADimType in (:Dimensions, :CompoundDimensions) return QType( kron(A.data, B.data), Operator, - CompoundDimensions{NA + NB}(vcat(A.to, B.to), vcat(A.from, B.from)), + GeneralDimensions{NA + NB}( + (get_dimensions_to(A)..., get_dimensions_to(B)...), + (get_dimensions_from(A)..., get_dimensions_from(B)...), + ), ) end end @@ -207,7 +210,7 @@ for ADimType in (:Dimensions, :CompoundDimensions) end end -# if A and B are different type (must return Operator with CompoundDimensions) +# if A and B are different type (must return Operator with GeneralDimensions) for AOpType in (:KetQuantumObject, :BraQuantumObject, :OperatorQuantumObject) for BOpType in (:KetQuantumObject, :BraQuantumObject, :OperatorQuantumObject) if (AOpType != BOpType) @@ -220,7 +223,10 @@ for AOpType in (:KetQuantumObject, :BraQuantumObject, :OperatorQuantumObject) return QType( kron(A.data, B.data), Operator, - CompoundDimensions(vcat(A.to, B.to), vcat(A.from, B.from)), + GeneralDimensions( + (get_dimensions_to(A)..., get_dimensions_to(B)...), + (get_dimensions_from(A)..., get_dimensions_from(B)...), + ), ) end end @@ -250,7 +256,7 @@ end Convert a quantum object from vector ([`OperatorKetQuantumObject`](@ref)-type) to matrix ([`OperatorQuantumObject`](@ref)-type) """ vec2mat(A::QuantumObject{<:AbstractArray{T},OperatorKetQuantumObject}) where {T} = - QuantumObject(vec2mat(A.data), Operator, A.dims) + QuantumObject(vec2mat(A.data), Operator, A.dimensions) @doc raw""" mat2vec(A::QuantumObject) @@ -258,7 +264,7 @@ vec2mat(A::QuantumObject{<:AbstractArray{T},OperatorKetQuantumObject}) where {T} Convert a quantum object from matrix ([`OperatorQuantumObject`](@ref)-type) to vector ([`OperatorKetQuantumObject`](@ref)-type) """ mat2vec(A::QuantumObject{<:AbstractArray{T},OperatorQuantumObject}) where {T} = - QuantumObject(mat2vec(A.data), OperatorKet, A.dims) + QuantumObject(mat2vec(A.data), OperatorKet, A.dimensions) @doc raw""" mat2vec(A::AbstractMatrix) diff --git a/src/qobj/quantum_object.jl b/src/qobj/quantum_object.jl index f480a01b..08943fb0 100644 --- a/src/qobj/quantum_object.jl +++ b/src/qobj/quantum_object.jl @@ -12,9 +12,12 @@ export QuantumObject struct QuantumObject{DataType<:AbstractArray,ObjType<:QuantumObjectType,DimType<:AbstractDimensions} <: AbstractQuantumObject{DataType,ObjType,DimType} data::DataType type::ObjType - dims::DimType + dimensions::DimType end +!!! note "`dims` property" + For a given `H::QuantumObject`, `H.dims` or `getproperty(H, :dims)` returns its `dimensions` in the type of integer-vector. + Julia struct representing any quantum objects. # Examples @@ -37,15 +40,15 @@ struct QuantumObject{DataType<:AbstractArray,ObjType<:QuantumObjectType,DimType< AbstractQuantumObject{DataType,ObjType,DimType} data::DataType type::ObjType - dims::DimType + dimensions::DimType function QuantumObject(data::MT, type::ObjType, dims) where {MT<:AbstractArray,ObjType<:QuantumObjectType} - _dims = _gen_dims(dims) + dimensions = _gen_dimensions(dims) _size = _get_size(data) - _check_QuantumObject(type, _dims, _size[1], _size[2]) + _check_QuantumObject(type, dimensions, _size[1], _size[2]) - return new{MT,ObjType,typeof(_dims)}(data, type, _dims) + return new{MT,ObjType,typeof(dimensions)}(data, type, dimensions) end end @@ -81,7 +84,7 @@ function QuantumObject( elseif type isa OperatorQuantumObject dims = (_size[1] == _size[2]) ? Dimensions(_size[1]) : - CompoundDimensions(SVector{2}(SVector{1}(_size[1]), SVector{1}(_size[2]))) + GeneralDimensions(SVector{2}(SVector{1}(_size[1]), SVector{1}(_size[2]))) elseif type isa SuperOperatorQuantumObject || type isa OperatorBraQuantumObject dims = Dimensions(isqrt(_size[2])) end @@ -124,12 +127,12 @@ end function QuantumObject( A::QuantumObject{<:AbstractArray{T,N}}; type::ObjType = A.type, - dims = A.dims, + dims = A.dimensions, ) where {T,N,ObjType<:QuantumObjectType} _size = N == 1 ? (length(A), 1) : size(A) - _dims = _gen_dims(dims) - _check_QuantumObject(type, _dims, _size[1], _size[2]) - return QuantumObject(copy(A.data), type, _dims) + dimensions = _gen_dimensions(dims) + _check_QuantumObject(type, dimensions, _size[1], _size[2]) + return QuantumObject(copy(A.data), type, dimensions) end function Base.show( @@ -166,15 +169,16 @@ function Base.show(io::IO, QO::QuantumObject) return show(io, MIME("text/plain"), op_data) end -Base.real(x::QuantumObject) = QuantumObject(real(x.data), x.type, x.dims) -Base.imag(x::QuantumObject) = QuantumObject(imag(x.data), x.type, x.dims) +Base.real(x::QuantumObject) = QuantumObject(real(x.data), x.type, x.dimensions) +Base.imag(x::QuantumObject) = QuantumObject(imag(x.data), x.type, x.dimensions) -SparseArrays.sparse(A::QuantumObject{<:AbstractArray{T}}) where {T} = QuantumObject(sparse(A.data), A.type, A.dims) +SparseArrays.sparse(A::QuantumObject{<:AbstractArray{T}}) where {T} = + QuantumObject(sparse(A.data), A.type, A.dimensions) SparseArrays.nnz(A::QuantumObject{<:AbstractSparseArray}) = nnz(A.data) SparseArrays.nonzeros(A::QuantumObject{<:AbstractSparseArray}) = nonzeros(A.data) SparseArrays.rowvals(A::QuantumObject{<:AbstractSparseArray}) = rowvals(A.data) SparseArrays.droptol!(A::QuantumObject{<:AbstractSparseArray}, tol::Real) = (droptol!(A.data, tol); return A) -SparseArrays.dropzeros(A::QuantumObject{<:AbstractSparseArray}) = QuantumObject(dropzeros(A.data), A.type, A.dims) +SparseArrays.dropzeros(A::QuantumObject{<:AbstractSparseArray}) = QuantumObject(dropzeros(A.data), A.type, A.dimensions) SparseArrays.dropzeros!(A::QuantumObject{<:AbstractSparseArray}) = (dropzeros!(A.data); return A) @doc raw""" @@ -191,7 +195,7 @@ SciMLOperators.cache_operator( L::AbstractQuantumObject{DT,OpType}, u::AbstractVector, ) where {DT,OpType<:Union{OperatorQuantumObject,SuperOperatorQuantumObject}} = - get_typename_wrapper(L)(cache_operator(L.data, sparse_to_dense(similar(u))), L.type, L.dims) + get_typename_wrapper(L)(cache_operator(L.data, sparse_to_dense(similar(u))), L.type, L.dimensions) function SciMLOperators.cache_operator( L::AbstractQuantumObject{DT1,OpType}, @@ -202,7 +206,7 @@ function SciMLOperators.cache_operator( OpType<:Union{OperatorQuantumObject,SuperOperatorQuantumObject}, SType<:Union{KetQuantumObject,OperatorKetQuantumObject}, } - check_dims(L, u) + check_dimensions(L, u) if isoper(L) && isoperket(u) throw(ArgumentError("The input state `u` must be a Ket if `L` is an Operator.")) @@ -213,14 +217,17 @@ function SciMLOperators.cache_operator( end # data type conversions -Base.Vector(A::QuantumObject{<:AbstractVector}) = QuantumObject(Vector(A.data), A.type, A.dims) -Base.Vector{T}(A::QuantumObject{<:AbstractVector}) where {T<:Number} = QuantumObject(Vector{T}(A.data), A.type, A.dims) -Base.Matrix(A::QuantumObject{<:AbstractMatrix}) = QuantumObject(Matrix(A.data), A.type, A.dims) -Base.Matrix{T}(A::QuantumObject{<:AbstractMatrix}) where {T<:Number} = QuantumObject(Matrix{T}(A.data), A.type, A.dims) -SparseArrays.SparseVector(A::QuantumObject{<:AbstractVector}) = QuantumObject(SparseVector(A.data), A.type, A.dims) +Base.Vector(A::QuantumObject{<:AbstractVector}) = QuantumObject(Vector(A.data), A.type, A.dimensions) +Base.Vector{T}(A::QuantumObject{<:AbstractVector}) where {T<:Number} = + QuantumObject(Vector{T}(A.data), A.type, A.dimensions) +Base.Matrix(A::QuantumObject{<:AbstractMatrix}) = QuantumObject(Matrix(A.data), A.type, A.dimensions) +Base.Matrix{T}(A::QuantumObject{<:AbstractMatrix}) where {T<:Number} = + QuantumObject(Matrix{T}(A.data), A.type, A.dimensions) +SparseArrays.SparseVector(A::QuantumObject{<:AbstractVector}) = + QuantumObject(SparseVector(A.data), A.type, A.dimensions) SparseArrays.SparseVector{T}(A::QuantumObject{<:SparseVector}) where {T<:Number} = - QuantumObject(SparseVector{T}(A.data), A.type, A.dims) + QuantumObject(SparseVector{T}(A.data), A.type, A.dimensions) SparseArrays.SparseMatrixCSC(A::QuantumObject{<:AbstractMatrix}) = - QuantumObject(SparseMatrixCSC(A.data), A.type, A.dims) + QuantumObject(SparseMatrixCSC(A.data), A.type, A.dimensions) SparseArrays.SparseMatrixCSC{T}(A::QuantumObject{<:SparseMatrixCSC}) where {T<:Number} = - QuantumObject(SparseMatrixCSC{T}(A.data), A.type, A.dims) + QuantumObject(SparseMatrixCSC{T}(A.data), A.type, A.dimensions) diff --git a/src/qobj/quantum_object_base.jl b/src/qobj/quantum_object_base.jl index 22e3d82b..f0911765 100644 --- a/src/qobj/quantum_object_base.jl +++ b/src/qobj/quantum_object_base.jl @@ -152,16 +152,20 @@ Returns the length of the matrix or vector corresponding to the [`AbstractQuantu Base.length(A::AbstractQuantumObject) = length(A.data) Base.isequal(A::AbstractQuantumObject, B::AbstractQuantumObject) = - isequal(A.type, B.type) && isequal(A.dims, B.dims) && isequal(A.data, B.data) + isequal(A.type, B.type) && isequal(A.dimensions, B.dimensions) && isequal(A.data, B.data) Base.isapprox(A::AbstractQuantumObject, B::AbstractQuantumObject; kwargs...) = - isequal(A.type, B.type) && isequal(A.dims, B.dims) && isapprox(A.data, B.data; kwargs...) + isequal(A.type, B.type) && isequal(A.dimensions, B.dimensions) && isapprox(A.data, B.data; kwargs...) Base.:(==)(A::AbstractQuantumObject, B::AbstractQuantumObject) = - (A.type == B.type) && (A.dims == B.dims) && (A.data == B.data) + (A.type == B.type) && (A.dimensions == B.dimensions) && (A.data == B.data) -function check_dims(A::AbstractQuantumObject, B::AbstractQuantumObject) - A.dims != B.dims && throw(DimensionMismatch("The two quantum objects don't have the same Hilbert dimension.")) +function check_dimensions(dimensions_list::NTuple{N,AbstractDimensions}) where {N} + allequal(dimensions_list) || + throw(DimensionMismatch("The quantum objects should have the same Hilbert `dimensions`.")) return nothing end +check_dimensions(Qobj_tuple::NTuple{N,AbstractQuantumObject}) where {N} = + check_dimensions(getfield.(Qobj_tuple, :dimensions)) +check_dimensions(A::AbstractQuantumObject...) = check_dimensions(A) function _check_QuantumObject(type::KetQuantumObject, dims::Dimensions, m::Int, n::Int) (n != 1) && throw(DomainError((m, n), "The size of the array is not compatible with Ket")) @@ -181,7 +185,7 @@ function _check_QuantumObject(type::OperatorQuantumObject, dims::Dimensions, m:: return nothing end -function _check_QuantumObject(type::OperatorQuantumObject, dims::CompoundDimensions, m::Int, n::Int) +function _check_QuantumObject(type::OperatorQuantumObject, dims::GeneralDimensions, m::Int, n::Int) ((m == 1) || (n == 1)) && throw(DomainError((m, n), "The size of the array is not compatible with Operator")) ((prod(dims.to) != m) || (prod(dims.from) != n)) && throw(DimensionMismatch("Operator with dims = $(dims) does not fit the array size = $((m, n)).")) @@ -209,26 +213,31 @@ function _check_QuantumObject(type::OperatorBraQuantumObject, dims::Dimensions, return nothing end -Base.getproperty(A::AbstractQuantumObject, key::Symbol) = getproperty(A, Val{key}()) +Base.getproperty(A::AbstractQuantumObject, key::Symbol) = getproperty(A, makeVal(key)) + +# support `AbstractQuantumObject.dims` +Base.getproperty(A::AbstractQuantumObject, ::Val{:dims}) = dimensions_to_dims(getfield(A, :dimensions)) Base.getproperty(A::AbstractQuantumObject, ::Val{K}) where {K} = getfield(A, K) -# support `AbstractQuantumObject.to` and `AbstractQuantumObject.from` -Base.getproperty(A::AbstractQuantumObject{DT,KetQuantumObject,<:Dimensions}, ::Val{:to}) where {DT} = A.dims.to -Base.getproperty(A::AbstractQuantumObject{DT,KetQuantumObject,Dimensions{N}}, ::Val{:from}) where {DT,N} = Field_list(N) -Base.getproperty(A::AbstractQuantumObject{DT,BraQuantumObject,Dimensions{N}}, ::Val{:to}) where {DT,N} = Field_list(N) -Base.getproperty(A::AbstractQuantumObject{DT,BraQuantumObject,<:Dimensions}, ::Val{:from}) where {DT} = A.dims.to -Base.getproperty(A::AbstractQuantumObject{DT,OperatorQuantumObject}, ::Val{:to}) where {DT} = A.dims.to -Base.getproperty(A::AbstractQuantumObject{DT,OperatorQuantumObject,<:Dimensions}, ::Val{:from}) where {DT} = A.dims.to -Base.getproperty(A::AbstractQuantumObject{DT,OperatorQuantumObject,<:CompoundDimensions}, ::Val{:from}) where {DT} = - A.dims.from -Base.getproperty( - A::AbstractQuantumObject{DT,ObjType,<:Dimensions}, - ::KeyType, -) where { - DT, - ObjType<:Union{SuperOperatorQuantumObject,OperatorBraQuantumObject,OperatorKetQuantumObject}, - KeyType<:Union{Val{:to},Val{:from}}, -} = A.dims.to +get_dimensions_to(A::AbstractQuantumObject{DT,KetQuantumObject,Dimensions{N}}) where {DT,N} = A.dimensions.to +get_dimensions_to(A::AbstractQuantumObject{DT,BraQuantumObject,Dimensions{N}}) where {DT,N} = space_one_list(N) +get_dimensions_to(A::AbstractQuantumObject{DT,OperatorQuantumObject,Dimensions{N}}) where {DT,N} = A.dimensions.to +get_dimensions_to(A::AbstractQuantumObject{DT,OperatorQuantumObject,GeneralDimensions{N}}) where {DT,N} = + A.dimensions.to +get_dimensions_to( + A::AbstractQuantumObject{DT,ObjType,Dimensions{N}}, +) where {DT,ObjType<:Union{SuperOperatorQuantumObject,OperatorBraQuantumObject,OperatorKetQuantumObject},N} = + A.dimensions.to + +get_dimensions_from(A::AbstractQuantumObject{DT,KetQuantumObject,Dimensions{N}}) where {DT,N} = space_one_list(N) +get_dimensions_from(A::AbstractQuantumObject{DT,BraQuantumObject,Dimensions{N}}) where {DT,N} = A.dimensions.to +get_dimensions_from(A::AbstractQuantumObject{DT,OperatorQuantumObject,Dimensions{N}}) where {DT,N} = A.dimensions.to +get_dimensions_from(A::AbstractQuantumObject{DT,OperatorQuantumObject,GeneralDimensions{N}}) where {DT,N} = + A.dimensions.from +get_dimensions_from( + A::AbstractQuantumObject{DT,ObjType,Dimensions{N}}, +) where {DT,ObjType<:Union{SuperOperatorQuantumObject,OperatorBraQuantumObject,OperatorKetQuantumObject},N} = + A.dimensions.to # functions for getting Float or Complex element type _FType(A::AbstractQuantumObject) = _FType(eltype(A)) diff --git a/src/qobj/quantum_object_evo.jl b/src/qobj/quantum_object_evo.jl index 933924ea..08fb2f02 100644 --- a/src/qobj/quantum_object_evo.jl +++ b/src/qobj/quantum_object_evo.jl @@ -8,16 +8,21 @@ export QuantumObjectEvolution struct QuantumObjectEvolution{DataType<:AbstractSciMLOperator,ObjType<:QuantumObjectType,DimType<:AbstractDimensions} <: AbstractQuantumObject{DataType,ObjType,DimType} data::DataType type::ObjType - dims::DimType + dimensions::DimType end +!!! note "`dims` property" + For a given `H::QuantumObjectEvolution`, `H.dims` or `getproperty(H, :dims)` returns its `dimensions` in the type of integer-vector. + Julia struct representing any time-dependent quantum object. The `data` field is a `AbstractSciMLOperator` object that represents the time-dependent quantum object. It can be seen as ```math \hat{O}(t) = \sum_{i} c_i(p, t) \hat{O}_i ``` -where ``c_i(p, t)`` is a function that depends on the parameters `p` and time `t`, and ``\hat{O}_i`` are the operators that form the quantum object. The `type` field is the type of the quantum object, and the `dims` field is the dimensions of the quantum object. For more information about `type` and `dims`, see [`QuantumObject`](@ref). For more information about `AbstractSciMLOperator`, see the [SciML](https://docs.sciml.ai/SciMLOperators/stable/) documentation. +where ``c_i(p, t)`` is a function that depends on the parameters `p` and time `t`, and ``\hat{O}_i`` are the operators that form the quantum object. + +For more information about `AbstractSciMLOperator`, see the [SciML](https://docs.sciml.ai/SciMLOperators/stable/) documentation. # Examples This operator can be initialized in the same way as the QuTiP `QobjEvo` object. For example @@ -103,7 +108,7 @@ struct QuantumObjectEvolution{ } <: AbstractQuantumObject{DataType,ObjType,DimType} data::DataType type::ObjType - dims::DimType + dimensions::DimType function QuantumObjectEvolution( data::DT, @@ -113,12 +118,12 @@ struct QuantumObjectEvolution{ (type == Operator || type == SuperOperator) || throw(ArgumentError("The type $type is not supported for QuantumObjectEvolution.")) - _dims = _gen_dims(dims) + dimensions = _gen_dimensions(dims) _size = _get_size(data) - _check_QuantumObject(type, _dims, _size[1], _size[2]) + _check_QuantumObject(type, dimensions, _size[1], _size[2]) - return new{DT,ObjType,typeof(_dims)}(data, type, _dims) + return new{DT,ObjType,typeof(dimensions)}(data, type, dimensions) end end @@ -155,7 +160,7 @@ function QuantumObjectEvolution(data::AbstractSciMLOperator; type::QuantumObject if type isa OperatorQuantumObject dims = (_size[1] == _size[2]) ? Dimensions(_size[1]) : - CompoundDimensions(SVector{2}(SVector{1}(_size[1]), SVector{1}(_size[2]))) + GeneralDimensions(SVector{2}(SVector{1}(_size[1]), SVector{1}(_size[2]))) elseif type isa SuperOperatorQuantumObject dims = Dimensions(isqrt(_size[2])) end @@ -257,7 +262,7 @@ function QuantumObjectEvolution( type::Union{Nothing,QuantumObjectType} = nothing, ) op, data = _QobjEvo_generate_data(op_func_list, α) - dims = op.dims + dims = op.dimensions if type isa Nothing type = op.type end @@ -320,7 +325,7 @@ function QuantumObjectEvolution( if type isa Nothing type = op.type end - return QuantumObjectEvolution(_make_SciMLOperator(op, α), type, op.dims) + return QuantumObjectEvolution(_make_SciMLOperator(op, α), type, op.dimensions) end function QuantumObjectEvolution( @@ -338,9 +343,9 @@ function QuantumObjectEvolution( ) end if α isa Nothing - return QuantumObjectEvolution(op.data, type, op.dims) + return QuantumObjectEvolution(op.data, type, op.dimensions) end - return QuantumObjectEvolution(α * op.data, type, op.dims) + return QuantumObjectEvolution(α * op.data, type, op.dimensions) end #= @@ -378,7 +383,7 @@ Parse the `op_func_list` and generate the data for the `QuantumObjectEvolution` op = :(op_func_list[$i][1]) data_type = op_type.parameters[1] - dims_expr = (dims_expr..., :($op.dims)) + dims_expr = (dims_expr..., :($op.dimensions)) func_methods_expr = (func_methods_expr..., :(methods(op_func_list[$i][2], [Any, Real]))) # [Any, Real] means each func must accept 2 arguments if i == 1 first_op = :($op) @@ -390,7 +395,7 @@ Parse the `op_func_list` and generate the data for the `QuantumObjectEvolution` throw(ArgumentError("The element must be a Operator or SuperOperator.")) data_type = op_type.parameters[1] - dims_expr = (dims_expr..., :(op_func_list[$i].dims)) + dims_expr = (dims_expr..., :(op_func_list[$i].dimensions)) if i == 1 first_op = :(op_func_list[$i]) end @@ -486,8 +491,8 @@ function (A::QuantumObjectEvolution)( p, t, ) where {DT1,DT2,QobjType<:Union{KetQuantumObject,OperatorKetQuantumObject}} - check_dims(ψout, ψin) - check_dims(ψout, A) + check_dimensions(ψout, ψin) + check_dimensions(ψout, A) if isoper(A) && isoperket(ψin) throw(ArgumentError("The input state must be a Ket if the QuantumObjectEvolution object is an Operator.")) @@ -514,7 +519,7 @@ function (A::QuantumObjectEvolution)( p, t, ) where {DT,QobjType<:Union{KetQuantumObject,OperatorKetQuantumObject}} - ψout = QuantumObject(similar(ψ.data), ψ.type, ψ.dims) + ψout = QuantumObject(similar(ψ.data), ψ.type, ψ.dimensions) return A(ψout, ψ, p, t) end @@ -533,7 +538,7 @@ Calculate the time-dependent [`QuantumObjectEvolution`](@ref) object `A` at time function (A::QuantumObjectEvolution)(p, t) # We put 0 in the place of `u` because the time-dependence doesn't depend on the state update_coefficients!(A.data, 0, p, t) - return QuantumObject(concretize(A.data), A.type, A.dims) + return QuantumObject(concretize(A.data), A.type, A.dimensions) end (A::QuantumObjectEvolution)(t) = A(nothing, t) diff --git a/src/qobj/space.jl b/src/qobj/space.jl index e5485cf5..b7d948f4 100644 --- a/src/qobj/space.jl +++ b/src/qobj/space.jl @@ -1,28 +1,41 @@ -export AbstractSpace, Field, Space +#= +This file defines the Hilbert space structure. +=# -abstract type AbstractSpace end +export AbstractSpace, Space -# this replaces Space(1), so that we don't need to store the value `1` -struct Field <: AbstractSpace end -Base.getproperty(s::Field, key::Symbol) = getproperty(s, Val{key}()) -Base.getproperty(s::Field, ::Val{:size}) = 1 -Base.string(s::Field) = "1" # this is only used when printing AbstractDimensions +abstract type AbstractSpace end -# this creates a list of Field, it is used to generate `from` for Ket, and `to` for Bra) -Field_list(N::Int) = SVector{N,AbstractSpace}(ntuple(i -> Field(), Val(N))) +@doc raw""" + struct Space <: AbstractSpace + size::Int + end +A structure that describes a single Hilbert space. +""" struct Space <: AbstractSpace size::Int function Space(size::Int) - # (put this comment here to avoid bad JuliaFormatter syntax) - if size > 1 - return new(size) - elseif size == 1 - return Field() - else - throw(DomainError(size, "The size of Space must be positive integer (≥ 1).")) - end + (size < 1) && throw(DomainError(size, "The size of Space must be positive integer (≥ 1).")) + return new(size) end end -Base.string(s::Space) = string(s.size) # this is only used when printing AbstractDimensions + +dimensions_to_dims(s::Space) = SVector{1,Int}(s.size) + +# this creates a list of Space(1), it is used to generate `from` for Ket, and `to` for Bra) +space_one_list(N::Int) = ntuple(i -> Space(1), Val(N)) + +# TODO: introduce energy restricted space +#= +struct EnrSpace{N} <: AbstractSpace + size::Int + dims::SVector{N,Int} + n_excitations::Int + state2idx + idx2state +end + +dimensions_to_dims(s::EnrSpace) = s.dims +=# diff --git a/src/qobj/superoperators.jl b/src/qobj/superoperators.jl index 01085512..68297e74 100644 --- a/src/qobj/superoperators.jl +++ b/src/qobj/superoperators.jl @@ -77,7 +77,7 @@ The optional argument `Id_cache` can be used to pass a precomputed identity matr See also [`spost`](@ref) and [`sprepost`](@ref). """ spre(A::AbstractQuantumObject{DT,OperatorQuantumObject}, Id_cache = I(size(A, 1))) where {DT} = - get_typename_wrapper(A)(_spre(A.data, Id_cache), SuperOperator, A.dims) + get_typename_wrapper(A)(_spre(A.data, Id_cache), SuperOperator, A.dimensions) @doc raw""" spost(B::AbstractQuantumObject, Id_cache=I(size(B,1))) @@ -96,7 +96,7 @@ The optional argument `Id_cache` can be used to pass a precomputed identity matr See also [`spre`](@ref) and [`sprepost`](@ref). """ spost(B::AbstractQuantumObject{DT,OperatorQuantumObject}, Id_cache = I(size(B, 1))) where {DT} = - get_typename_wrapper(B)(_spost(B.data, Id_cache), SuperOperator, B.dims) + get_typename_wrapper(B)(_spost(B.data, Id_cache), SuperOperator, B.dimensions) @doc raw""" sprepost(A::AbstractQuantumObject, B::AbstractQuantumObject) @@ -116,8 +116,8 @@ function sprepost( A::AbstractQuantumObject{DT1,OperatorQuantumObject}, B::AbstractQuantumObject{DT2,OperatorQuantumObject}, ) where {DT1,DT2} - check_dims(A, B) - return promote_op_type(A, B)(_sprepost(A.data, B.data), SuperOperator, A.dims) + check_dimensions(A, B) + return promote_op_type(A, B)(_sprepost(A.data, B.data), SuperOperator, A.dimensions) end @doc raw""" @@ -135,7 +135,7 @@ The optional argument `Id_cache` can be used to pass a precomputed identity matr See also [`spre`](@ref), [`spost`](@ref), and [`sprepost`](@ref). """ lindblad_dissipator(O::AbstractQuantumObject{DT,OperatorQuantumObject}, Id_cache = I(size(O, 1))) where {DT} = - get_typename_wrapper(O)(_lindblad_dissipator(O.data, Id_cache), SuperOperator, O.dims) + get_typename_wrapper(O)(_lindblad_dissipator(O.data, Id_cache), SuperOperator, O.dimensions) # It is already a SuperOperator lindblad_dissipator(O::AbstractQuantumObject{DT,SuperOperatorQuantumObject}, Id_cache = nothing) where {DT} = O @@ -162,7 +162,7 @@ See also [`spre`](@ref), [`spost`](@ref), and [`lindblad_dissipator`](@ref). function liouvillian( H::AbstractQuantumObject{DT,OpType}, c_ops::Union{Nothing,AbstractVector,Tuple} = nothing, - Id_cache = I(prod(H.dims)), + Id_cache = I(prod(H.dimensions)), ) where {DT,OpType<:Union{OperatorQuantumObject,SuperOperatorQuantumObject}} L = liouvillian(H, Id_cache) if !(c_ops isa Nothing) @@ -181,7 +181,7 @@ function liouvillian( return L end -liouvillian(H::AbstractQuantumObject{DT,OperatorQuantumObject}, Id_cache::Diagonal = I(prod(H.dims))) where {DT} = - get_typename_wrapper(H)(_liouvillian(H.data, Id_cache), SuperOperator, H.dims) +liouvillian(H::AbstractQuantumObject{DT,OperatorQuantumObject}, Id_cache::Diagonal = I(prod(H.dimensions))) where {DT} = + get_typename_wrapper(H)(_liouvillian(H.data, Id_cache), SuperOperator, H.dimensions) liouvillian(H::AbstractQuantumObject{DT,SuperOperatorQuantumObject}, Id_cache::Diagonal) where {DT} = H diff --git a/src/spectrum.jl b/src/spectrum.jl index a62a69ca..af4d08b1 100644 --- a/src/spectrum.jl +++ b/src/spectrum.jl @@ -84,8 +84,7 @@ function _spectrum( solver::ExponentialSeries; kwargs..., ) where {T1,T2,T3} - allequal((L.dims, A.dims, B.dims)) || - throw(DimensionMismatch("The quantum objects are not of the same Hilbert dimension.")) + check_dimensions(L, A, B) rates, vecs, ρss = _spectrum_get_rates_vecs_ss(L, solver) @@ -111,8 +110,7 @@ function _spectrum( solver::PseudoInverse; kwargs..., ) where {T1,T2,T3} - allequal((L.dims, A.dims, B.dims)) || - throw(DimensionMismatch("The quantum objects are not of the same Hilbert dimension.")) + check_dimensions(L, A, B) ωList = convert(Vector{_FType(L)}, ωlist) # Convert it to support GPUs and avoid type instabilities Length = length(ωList) @@ -123,7 +121,7 @@ function _spectrum( b = (spre(B) * ρss).data # multiply by operator A on the left (spre) and then perform trace operation - D = prod(L.dims) + D = prod(L.dimensions) _tr = SparseVector(D^2, [1 + n * (D + 1) for n in 0:(D-1)], ones(_CType(L), D)) # same as vec(system_identity_matrix) _tr_A = transpose(_tr) * spre(A).data diff --git a/src/spin_lattice.jl b/src/spin_lattice.jl index c46695af..99f356df 100644 --- a/src/spin_lattice.jl +++ b/src/spin_lattice.jl @@ -51,7 +51,7 @@ function MultiSiteOperator(dims::Union{AbstractVector,Tuple}, pairs::Pair{<:Inte sites, ops = _get_unique_sites_ops(_sites, _ops) - _dims[sites] == [op.to[1].size for op in ops] || throw(ArgumentError("The dimensions of the operators do not match the dimensions of the lattice.")) + _dims[sites] == [get_dimensions_to(op)[1].size for op in ops] || throw(ArgumentError("The dimensions of the operators do not match the dimensions of the lattice.")) data = kron(I(prod(_dims[1:sites[1]-1])), ops[1].data) for i in 2:length(sites) diff --git a/src/steadystate.jl b/src/steadystate.jl index 606b0907..43b23a2c 100644 --- a/src/steadystate.jl +++ b/src/steadystate.jl @@ -96,7 +96,7 @@ function _steadystate( kwargs..., ) where {T} L_tmp = L.data - N = prod(L.dims) + N = prod(L.dimensions) weight = norm(L_tmp, 1) / length(L_tmp) v0 = _get_dense_similar(L_tmp, N^2) @@ -126,7 +126,7 @@ function _steadystate( ρss = reshape(ρss_vec, N, N) ρss = (ρss + ρss') / 2 # Hermitianize - return QuantumObject(ρss, Operator, L.dims) + return QuantumObject(ρss, Operator, L.dimensions) end function _steadystate( @@ -134,7 +134,7 @@ function _steadystate( solver::SteadyStateEigenSolver; kwargs..., ) where {T} - N = prod(L.dims) + N = prod(L.dimensions) kwargs = merge((sigma = 1e-8, k = 1), (; kwargs...)) @@ -142,7 +142,7 @@ function _steadystate( ρss = reshape(ρss_vec, N, N) ρss /= tr(ρss) ρss = (ρss + ρss') / 2 # Hermitianize - return QuantumObject(ρss, Operator, L.dims) + return QuantumObject(ρss, Operator, L.dimensions) end function _steadystate( @@ -150,7 +150,7 @@ function _steadystate( solver::SteadyStateDirectSolver, ) where {T} L_tmp = L.data - N = prod(L.dims) + N = prod(L.dimensions) weight = norm(L_tmp, 1) / length(L_tmp) v0 = _get_dense_similar(L_tmp, N^2) @@ -170,7 +170,7 @@ function _steadystate( ρss_vec = L_tmp \ v0 # This is still not supported on GPU, yet ρss = reshape(ρss_vec, N, N) ρss = (ρss + ρss') / 2 # Hermitianize - return QuantumObject(ρss, Operator, L.dims) + return QuantumObject(ρss, Operator, L.dimensions) end _steadystate( @@ -411,13 +411,13 @@ function _steadystate_floquet( ρ0 = reshape(ρtot[offset1+1:offset2], Ns, Ns) ρ0_tr = tr(ρ0) ρ0 = ρ0 / ρ0_tr - ρ0 = QuantumObject((ρ0 + ρ0') / 2, type = Operator, dims = L_0.dims) + ρ0 = QuantumObject((ρ0 + ρ0') / 2, type = Operator, dims = L_0.dimensions) ρtot = ρtot / ρ0_tr ρ_list = [ρ0] for i in 0:n_max-1 ρi_m = reshape(ρtot[offset1-(i+1)*N+1:offset1-i*N], Ns, Ns) - ρi_m = QuantumObject(ρi_m, type = Operator, dims = L_0.dims) + ρi_m = QuantumObject(ρi_m, type = Operator, dims = L_0.dimensions) push!(ρ_list, ρi_m) end @@ -434,8 +434,7 @@ function _steadystate_floquet( tol::R = 1e-8, kwargs..., ) where {R<:Real} - ((L_0.dims == L_p.dims) && (L_0.dims == L_m.dims)) || - throw(DimensionMismatch("The quantum objects are not of the same Hilbert dimension.")) + check_dimensions(L_0, L_p, L_m) L_eff = liouvillian_floquet(L_0, L_p, L_m, ωd; n_max = n_max, tol = tol) diff --git a/src/time_evolution/lr_mesolve.jl b/src/time_evolution/lr_mesolve.jl index 8b71e94d..f8a62cb8 100644 --- a/src/time_evolution/lr_mesolve.jl +++ b/src/time_evolution/lr_mesolve.jl @@ -401,7 +401,7 @@ function lr_mesolveProblem( opt::NamedTuple = lr_mesolve_options_default, kwargs..., ) where {DT1,T2} - Hdims = H.dims + Hdims = H.dimensions # Formulation of problem H -= 0.5im * mapreduce(op -> op' * op, +, c_ops) diff --git a/src/time_evolution/mcsolve.jl b/src/time_evolution/mcsolve.jl index db8ab031..99b5d3fa 100644 --- a/src/time_evolution/mcsolve.jl +++ b/src/time_evolution/mcsolve.jl @@ -299,7 +299,7 @@ function mcsolveEnsembleProblem( ensemble_prob = TimeEvolutionProblem( EnsembleProblem(prob_mc.prob, prob_func = _prob_func, output_func = _output_func[1], safetycopy = false), prob_mc.times, - prob_mc.dims, + prob_mc.dimensions, (progr = _output_func[2], channel = _output_func[3]), ) @@ -472,7 +472,7 @@ function mcsolve( ) sol = _mcsolve_solve_ens(ens_prob_mc, alg, ensemble_method, ntraj) - dims = ens_prob_mc.dims + dims = ens_prob_mc.dimensions _sol_1 = sol[:, 1] _expvals_sol_1 = _mcsolve_get_expvals(_sol_1) diff --git a/src/time_evolution/mesolve.jl b/src/time_evolution/mesolve.jl index 2d9f42ef..d119785b 100644 --- a/src/time_evolution/mesolve.jl +++ b/src/time_evolution/mesolve.jl @@ -73,7 +73,7 @@ function mesolveProblem( tlist = convert(Vector{_FType(ψ0)}, tlist) # Convert it to support GPUs and avoid type instabilities for OrdinaryDiffEq.jl L_evo = _mesolve_make_L_QobjEvo(H, c_ops) - check_dims(L_evo, ψ0) + check_dimensions(L_evo, ψ0) T = Base.promote_eltype(L_evo, ψ0) ρ0 = sparse_to_dense(_CType(T), mat2vec(ket2dm(ψ0).data)) # Convert it to dense vector with complex element type @@ -89,7 +89,7 @@ function mesolveProblem( tspan = (tlist[1], tlist[end]) prob = ODEProblem{getVal(inplace),FullSpecialize}(L, ρ0, tspan, params; kwargs3...) - return TimeEvolutionProblem(prob, tlist, L_evo.dims) + return TimeEvolutionProblem(prob, tlist, L_evo.dimensions) end @doc raw""" @@ -179,7 +179,7 @@ end function mesolve(prob::TimeEvolutionProblem, alg::OrdinaryDiffEqAlgorithm = Tsit5()) sol = solve(prob.prob, alg) - ρt = map(ϕ -> QuantumObject(vec2mat(ϕ), type = Operator, dims = prob.dims), sol.u) + ρt = map(ϕ -> QuantumObject(vec2mat(ϕ), type = Operator, dims = prob.dimensions), sol.u) return TimeEvolutionSol( prob.times, diff --git a/src/time_evolution/sesolve.jl b/src/time_evolution/sesolve.jl index 28562e28..341a2cc5 100644 --- a/src/time_evolution/sesolve.jl +++ b/src/time_evolution/sesolve.jl @@ -1,8 +1,8 @@ export sesolveProblem, sesolve _sesolve_make_U_QobjEvo(H::QuantumObjectEvolution{<:MatrixOperator}) = - QobjEvo(MatrixOperator(-1im * H.data.A), dims = H.dims, type = Operator) -_sesolve_make_U_QobjEvo(H::QuantumObject) = QobjEvo(MatrixOperator(-1im * H.data), dims = H.dims, type = Operator) + QobjEvo(MatrixOperator(-1im * H.data.A), dims = H.dimensions, type = Operator) +_sesolve_make_U_QobjEvo(H::QuantumObject) = QobjEvo(MatrixOperator(-1im * H.data), dims = H.dimensions, type = Operator) _sesolve_make_U_QobjEvo(H::Union{QuantumObjectEvolution,Tuple}) = QobjEvo(H, -1im) @doc raw""" @@ -62,7 +62,7 @@ function sesolveProblem( H_evo = _sesolve_make_U_QobjEvo(H) # Multiply by -i isoper(H_evo) || throw(ArgumentError("The Hamiltonian must be an Operator.")) - check_dims(H_evo, ψ0) + check_dimensions(H_evo, ψ0) T = Base.promote_eltype(H_evo, ψ0) ψ0 = sparse_to_dense(_CType(T), get_data(ψ0)) # Convert it to dense vector with complex element type @@ -78,7 +78,7 @@ function sesolveProblem( tspan = (tlist[1], tlist[end]) prob = ODEProblem{getVal(inplace),FullSpecialize}(U, ψ0, tspan, params; kwargs3...) - return TimeEvolutionProblem(prob, tlist, H_evo.dims) + return TimeEvolutionProblem(prob, tlist, H_evo.dimensions) end @doc raw""" @@ -152,7 +152,7 @@ end function sesolve(prob::TimeEvolutionProblem, alg::OrdinaryDiffEqAlgorithm = Tsit5()) sol = solve(prob.prob, alg) - ψt = map(ϕ -> QuantumObject(ϕ, type = Ket, dims = prob.dims), sol.u) + ψt = map(ϕ -> QuantumObject(ϕ, type = Ket, dims = prob.dimensions), sol.u) return TimeEvolutionSol( prob.times, diff --git a/src/time_evolution/ssesolve.jl b/src/time_evolution/ssesolve.jl index 2dc7fac1..cdd2de9b 100644 --- a/src/time_evolution/ssesolve.jl +++ b/src/time_evolution/ssesolve.jl @@ -166,8 +166,8 @@ function ssesolveProblem( H_eff_evo = _mcsolve_make_Heff_QobjEvo(H, sc_ops) isoper(H_eff_evo) || throw(ArgumentError("The Hamiltonian must be an Operator.")) - check_dims(H_eff_evo, ψ0) - dims = H_eff_evo.dims + check_dimensions(H_eff_evo, ψ0) + dims = H_eff_evo.dimensions ψ0 = sparse_to_dense(_CType(ψ0), get_data(ψ0)) diff --git a/src/time_evolution/time_evolution.jl b/src/time_evolution/time_evolution.jl index dfa20750..d14af0c4 100644 --- a/src/time_evolution/time_evolution.jl +++ b/src/time_evolution/time_evolution.jl @@ -15,16 +15,23 @@ A Julia constructor for handling the `ODEProblem` of the time evolution of quant - `prob::AbstractSciMLProblem`: The `ODEProblem` of the time evolution. - `times::Abstractvector`: The time list of the evolution. -- `dims::AbstractDimensions`: The dimensions of the Hilbert space. +- `dimensions::AbstractDimensions`: The dimensions of the Hilbert space. - `kwargs::KWT`: Generic keyword arguments. + +!!! note "`dims` property" + For a given `prob::TimeEvolutionProblem`, `prob.dims` or `getproperty(prob, :dims)` returns its `dimensions` in the type of integer-vector. """ struct TimeEvolutionProblem{PT<:AbstractSciMLProblem,TT<:AbstractVector,DT<:AbstractDimensions,KWT} prob::PT times::TT - dims::DT + dimensions::DT kwargs::KWT end +Base.getproperty(prob::TimeEvolutionProblem, key::Symbol) = getproperty(prob, makeVal(key)) +Base.getproperty(prob::TimeEvolutionProblem, ::Val{:dims}) = dimensions_to_dims(getfield(prob, :dimensions)) +Base.getproperty(prob::TimeEvolutionProblem, ::Val{K}) where {K} = getfield(prob, K) + TimeEvolutionProblem(prob, times, dims) = TimeEvolutionProblem(prob, times, dims, nothing) @doc raw""" @@ -210,9 +217,7 @@ function liouvillian_floquet( n_max::Int = 3, tol::Real = 1e-15, ) where {T1,T2,T3} - ((L₀.dims == Lₚ.dims) && (L₀.dims == Lₘ.dims)) || - throw(DimensionMismatch("The quantum objects are not of the same Hilbert dimension.")) - + check_dimensions(L₀, Lₚ, Lₘ) return _liouvillian_floquet(L₀, Lₚ, Lₘ, ω, n_max, tol) end @@ -253,11 +258,11 @@ function liouvillian_generalized( ) where {MT<:AbstractMatrix} (length(fields) == length(T_list)) || throw(DimensionMismatch("The number of fields, ωs and Ts must be the same.")) - dims = (N_trunc isa Nothing) ? H.dims : SVector(N_trunc) + dims = (N_trunc isa Nothing) ? H.dimensions : SVector(N_trunc) final_size = prod(dims) result = eigen(H) E = real.(result.values[1:final_size]) - U = QuantumObject(result.vectors, result.type, result.dims) + U = QuantumObject(result.vectors, result.type, result.dimensions) H_d = QuantumObject(Diagonal(complex(E)), type = Operator, dims = dims) @@ -328,6 +333,6 @@ function _liouvillian_floquet( T = -(L_0 + 1im * n_i * ω * I + L_p * T) \ L_m_dense end - tol == 0 && return QuantumObject(L_0 + L_m * S + L_p * T, SuperOperator, L₀.dims) - return QuantumObject(dense_to_sparse(L_0 + L_m * S + L_p * T, tol), SuperOperator, L₀.dims) + tol == 0 && return QuantumObject(L_0 + L_m * S + L_p * T, SuperOperator, L₀.dimensions) + return QuantumObject(dense_to_sparse(L_0 + L_m * S + L_p * T, tol), SuperOperator, L₀.dimensions) end diff --git a/src/time_evolution/time_evolution_dynamical.jl b/src/time_evolution/time_evolution_dynamical.jl index 650f406e..81cf3260 100644 --- a/src/time_evolution/time_evolution_dynamical.jl +++ b/src/time_evolution/time_evolution_dynamical.jl @@ -149,7 +149,7 @@ function dfd_mesolveProblem( tol_list::Vector{<:Number} = fill(1e-8, length(maxdims)), kwargs..., ) where {DT1,T2<:Integer,StateOpType<:Union{KetQuantumObject,OperatorQuantumObject}} - length(ψ0.dims) != length(maxdims) && + length(ψ0.dimensions) != length(maxdims) && throw(DimensionMismatch("'dim_list' and 'maxdims' do not have the same dimension.")) dim_list = MVector(ψ0.dims) @@ -362,7 +362,7 @@ function dsf_mesolveProblem( op_l_vec = map(op -> mat2vec(get_data(op)'), op_list) # Create the Krylov subspace with kron(H₀.data, H₀.data) just for initialize expv_cache = arnoldi(kron(H₀.data, H₀.data), mat2vec(ket2dm(ψ0).data), krylov_dim) - dsf_identity = I(prod(H₀.dims)) + dsf_identity = I(prod(H₀.dimensions)) dsf_displace_cache_left = sum(op -> ScalarOperator(one(T)) * MatrixOperator(kron(op.data, dsf_identity)), op_list) dsf_displace_cache_left_dag = sum(op -> ScalarOperator(one(T)) * MatrixOperator(kron(sparse(op.data'), dsf_identity)), op_list) diff --git a/test/core-test/quantum_objects.jl b/test/core-test/quantum_objects.jl index e9acf6fb..9b0771f3 100644 --- a/test/core-test/quantum_objects.jl +++ b/test/core-test/quantum_objects.jl @@ -77,7 +77,7 @@ a = sprand(ComplexF64, 100, 100, 0.1) a2 = Qobj(a) a3 = Qobj(a, type = SuperOperator) - a4 = Qobj(sprand(ComplexF64, 100, 10, 0.1)) # CompoundDimensions + a4 = Qobj(sprand(ComplexF64, 100, 10, 0.1)) # GeneralDimensions @test isket(a2) == false @test isbra(a2) == false @test isoper(a2) == true @@ -344,7 +344,7 @@ end UnionType2 = Union{ - QuantumObject{Matrix{T},OperatorQuantumObject,CompoundDimensions{1}}, + QuantumObject{Matrix{T},OperatorQuantumObject,GeneralDimensions{1}}, QuantumObject{Matrix{T},OperatorQuantumObject,Dimensions{1}}, } a = rand(T, N, N) @@ -653,7 +653,7 @@ ρ1_ptr = ptrace(ρ, 1) ρ2_ptr = ptrace(ρ, 2) - # use CompoundDimensions to do partial trace + # use GeneralDimensions to do partial trace ρ1_compound = Qobj(zeros(ComplexF64, 2, 2), dims = ((2, 1), (2, 1))) basis2 = [tensor(eye(2), basis(2, i)) for i in 0:1] for b in basis2 diff --git a/test/core-test/quantum_objects_evo.jl b/test/core-test/quantum_objects_evo.jl index c783e521..87997b2e 100644 --- a/test/core-test/quantum_objects_evo.jl +++ b/test/core-test/quantum_objects_evo.jl @@ -130,7 +130,7 @@ for T in [ComplexF32, ComplexF64] a = MatrixOperator(rand(T, N, N)) UnionType = Union{ - QuantumObjectEvolution{typeof(a),OperatorQuantumObject,CompoundDimensions{1}}, + QuantumObjectEvolution{typeof(a),OperatorQuantumObject,GeneralDimensions{1}}, QuantumObjectEvolution{typeof(a),OperatorQuantumObject,Dimensions{1}}, } @inferred UnionType QobjEvo(a) diff --git a/test/runtests.jl b/test/runtests.jl index b79500b6..9081ad84 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -9,7 +9,7 @@ const testdir = dirname(@__FILE__) core_tests = [ "block_diagonal_form.jl", "correlations_and_spectrum.jl", - #"dynamical_fock_dimension_mesolve.jl", # TODO: fix tests + "dynamical_fock_dimension_mesolve.jl", "dynamical-shifted-fock.jl", "eigenvalues_and_operators.jl", "entanglement.jl",