diff --git a/.github/ISSUE_TEMPLATE/bug_report.yaml b/.github/ISSUE_TEMPLATE/bug_report.yaml index cc2245f6e..e2f33b540 100644 --- a/.github/ISSUE_TEMPLATE/bug_report.yaml +++ b/.github/ISSUE_TEMPLATE/bug_report.yaml @@ -22,7 +22,7 @@ body: attributes: label: Code Output description: Please paste the relevant output here (automatically formatted) - placeholder: "Quantum Object: type=Operator dims=[2] size=(2, 2) ishermitian=true\n2×2 Diagonal{ComplexF64, Vector{ComplexF64}}:\n 1.0+0.0im ⋅ \n ⋅ 1.0+0.0im" + placeholder: "Quantum Object: type=Operator() dims=[2] size=(2, 2) ishermitian=true\n2×2 Diagonal{ComplexF64, Vector{ComplexF64}}:\n 1.0+0.0im ⋅ \n ⋅ 1.0+0.0im" render: shell - type: textarea id: expected-behaviour diff --git a/CHANGELOG.md b/CHANGELOG.md index 8a9f46f76..d949e3651 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased](https://github.com/qutip/QuantumToolbox.jl/tree/main) - Return `sesolve` when `mesolve` allows it. ([#455]) +- Simplify structure of `QuantumObjectType`s. ([#456]) ## [v0.30.1] Release date: 2025-04-24 @@ -215,3 +216,4 @@ Release date: 2024-11-13 [#450]: https://github.com/qutip/QuantumToolbox.jl/issues/450 [#453]: https://github.com/qutip/QuantumToolbox.jl/issues/453 [#455]: https://github.com/qutip/QuantumToolbox.jl/issues/455 +[#456]: https://github.com/qutip/QuantumToolbox.jl/issues/456 diff --git a/docs/src/getting_started/type_stability.md b/docs/src/getting_started/type_stability.md index 2b5153665..bd22d3481 100644 --- a/docs/src/getting_started/type_stability.md +++ b/docs/src/getting_started/type_stability.md @@ -158,7 +158,7 @@ and its type is obj_type = typeof(σx_2) ``` -This is exactly what the Julia compiler sees: it is a [`QuantumObject`](@ref), composed by a field of type `SparseMatrixCSC{ComplexF64, Int64}` (i.e., the 8x8 matrix containing the Pauli matrix, tensored with the identity matrices of the other two qubits). Then, we can also see that it is a [`OperatorQuantumObject`](@ref), with `3` subsystems in total. Hence, just looking at the type of the object, the compiler has all the information it needs to generate a specialized version of the functions. +This is exactly what the Julia compiler sees: it is a [`QuantumObject`](@ref), composed by a field of type `SparseMatrixCSC{ComplexF64, Int64}` (i.e., the 8x8 matrix containing the Pauli matrix, tensored with the identity matrices of the other two qubits). Then, we can also see that it is a [`Operator`](@ref), with `3` subsystems in total. Hence, just looking at the type of the object, the compiler has all the information it needs to generate a specialized version of the functions. Let's see more in the details all the internal fields of the [`QuantumObject`](@ref) type: @@ -174,7 +174,6 @@ fieldnames(obj_type) σx_2.type ``` -[`Operator`](@ref) is a synonym for [`OperatorQuantumObject`](@ref). ```@example type-stability σx_2.dims @@ -184,7 +183,7 @@ The `dims` field contains the dimensions of the subsystems (in this case, three ```@example type-stability function reshape_operator_data(dims) - op = Qobj(randn(prod(dims), prod(dims)), type=Operator, dims=dims) + op = Qobj(randn(prod(dims), prod(dims)), type=Operator(), dims=dims) op_dims = op.dims op_data = op.data return reshape(op_data, vcat(op_dims, op_dims)...) @@ -235,7 +234,7 @@ function my_fock(N::Int, j::Int = 0; sparse::Bool = false) array = zeros(ComplexF64, N) array[j+1] = 1 end - return QuantumObject(array; type = Ket) + return QuantumObject(array; type = Ket()) end @show my_fock(2, 1) @show my_fock(2, 1; sparse = true) @@ -263,7 +262,7 @@ function my_fock_good(N::Int, j::Int = 0; sparse::Val = Val(false)) else array = sparsevec([j + 1], [1.0 + 0im], N) end - return QuantumObject(array; type = Ket) + return QuantumObject(array; type = Ket()) end @show my_fock_good(2, 1) @show my_fock_good(2, 1; sparse = Val(true)) diff --git a/docs/src/resources/api.md b/docs/src/resources/api.md index fc1d9fc7c..b9df5dbd9 100644 --- a/docs/src/resources/api.md +++ b/docs/src/resources/api.md @@ -21,17 +21,11 @@ Space Dimensions GeneralDimensions AbstractQuantumObject -BraQuantumObject Bra -KetQuantumObject Ket -OperatorQuantumObject Operator -OperatorBraQuantumObject OperatorBra -OperatorKetQuantumObject OperatorKet -SuperOperatorQuantumObject SuperOperator QuantumObject QuantumObjectEvolution diff --git a/docs/src/users_guide/QuantumObject/QuantumObject.md b/docs/src/users_guide/QuantumObject/QuantumObject.md index 0ac2a6fb7..8ca8104e0 100644 --- a/docs/src/users_guide/QuantumObject/QuantumObject.md +++ b/docs/src/users_guide/QuantumObject/QuantumObject.md @@ -54,7 +54,7 @@ Qobj(M, dims = SVector(2, 2)) # dims as StaticArrays.SVector (recommended) Please note that here we put the `dims` as a tuple `(2, 2)`. Although it supports also `Vector` type (`dims = [2, 2]`), it is recommended to use `Tuple` or `SVector` from [`StaticArrays.jl`](https://github.com/JuliaArrays/StaticArrays.jl) to improve performance. For a brief explanation on the impact of the type of `dims`, see the Section [The Importance of Type-Stability](@ref doc:Type-Stability). ```@example Qobj -Qobj(rand(4, 4), type = SuperOperator) +Qobj(rand(4, 4), type = SuperOperator()) ``` !!! note "Difference between `dims` and `size`" diff --git a/docs/src/users_guide/extensions/cuda.md b/docs/src/users_guide/extensions/cuda.md index 228a8acf1..eacef8083 100644 --- a/docs/src/users_guide/extensions/cuda.md +++ b/docs/src/users_guide/extensions/cuda.md @@ -36,7 +36,7 @@ V = fock(2, 0) # CPU dense vector ``` ``` -Quantum Object: type=Ket dims=[2] size=(2,) +Quantum Object: type=Ket() dims=[2] size=(2,) 2-element Vector{ComplexF64}: 1.0 + 0.0im 0.0 + 0.0im @@ -47,7 +47,7 @@ cu(V) ``` ``` -Quantum Object: type=Ket dims=[2] size=(2,) +Quantum Object: type=Ket() dims=[2] size=(2,) 2-element CuArray{ComplexF64, 1, CUDA.DeviceMemory}: 1.0 + 0.0im 0.0 + 0.0im @@ -58,7 +58,7 @@ cu(V; word_size = 32) ``` ``` -Quantum Object: type=Ket dims=[2] size=(2,) +Quantum Object: type=Ket() dims=[2] size=(2,) 2-element CuArray{ComplexF32, 1, CUDA.DeviceMemory}: 1.0 + 0.0im 0.0 + 0.0im @@ -69,7 +69,7 @@ M = Qobj([1 2; 3 4]) # CPU dense matrix ``` ``` -Quantum Object: type=Operator dims=[2] size=(2, 2) ishermitian=false +Quantum Object: type=Operator() dims=[2] size=(2, 2) ishermitian=false 2×2 Matrix{Int64}: 1 2 3 4 @@ -80,7 +80,7 @@ cu(M) ``` ``` -Quantum Object: type=Operator dims=[2] size=(2, 2) ishermitian=false +Quantum Object: type=Operator() dims=[2] size=(2, 2) ishermitian=false 2×2 CuArray{Int64, 2, CUDA.DeviceMemory}: 1 2 3 4 @@ -91,7 +91,7 @@ cu(M; word_size = 32) ``` ``` -Quantum Object: type=Operator dims=[2] size=(2, 2) ishermitian=false +Quantum Object: type=Operator() dims=[2] size=(2, 2) ishermitian=false 2×2 CuArray{Int32, 2, CUDA.DeviceMemory}: 1 2 3 4 @@ -104,7 +104,7 @@ V = fock(2, 0; sparse=true) # CPU sparse vector ``` ``` -Quantum Object: type=Ket dims=[2] size=(2,) +Quantum Object: type=Ket() dims=[2] size=(2,) 2-element SparseVector{ComplexF64, Int64} with 1 stored entry: [1] = 1.0+0.0im ``` @@ -114,7 +114,7 @@ cu(V) ``` ``` -Quantum Object: type=Ket dims=[2] size=(2,) +Quantum Object: type=Ket() dims=[2] size=(2,) 2-element CuSparseVector{ComplexF64, Int32} with 1 stored entry: [1] = 1.0+0.0im ``` @@ -124,7 +124,7 @@ cu(V; word_size = 32) ``` ``` -Quantum Object: type=Ket dims=[2] size=(2,) +Quantum Object: type=Ket() dims=[2] size=(2,) 2-element CuSparseVector{ComplexF32, Int32} with 1 stored entry: [1] = 1.0+0.0im ``` @@ -134,7 +134,7 @@ M = sigmax() # CPU sparse matrix ``` ``` -Quantum Object: type=Operator dims=[2] size=(2, 2) ishermitian=true +Quantum Object: type=Operator() dims=[2] size=(2, 2) ishermitian=true 2×2 SparseMatrixCSC{ComplexF64, Int64} with 2 stored entries: ⋅ 1.0+0.0im 1.0+0.0im ⋅ @@ -145,7 +145,7 @@ cu(M) ``` ``` -Quantum Object: type=Operator dims=[2] size=(2, 2) ishermitian=true +Quantum Object: type=Operator() dims=[2] size=(2, 2) ishermitian=true 2×2 CuSparseMatrixCSC{ComplexF64, Int32} with 2 stored entries: ⋅ 1.0+0.0im 1.0+0.0im ⋅ @@ -156,7 +156,7 @@ cu(M; word_size = 32) ``` ``` -Quantum Object: type=Operator dims=[2] size=(2, 2) ishermitian=true +Quantum Object: type=Operator() dims=[2] size=(2, 2) ishermitian=true 2×2 CuSparseMatrixCSC{ComplexF32, Int32} with 2 stored entries: ⋅ 1.0+0.0im 1.0+0.0im ⋅ diff --git a/docs/src/users_guide/states_and_operators.md b/docs/src/users_guide/states_and_operators.md index b50814ede..4795756c3 100644 --- a/docs/src/users_guide/states_and_operators.md +++ b/docs/src/users_guide/states_and_operators.md @@ -389,7 +389,7 @@ B = Qobj(rand(ComplexF64, N, N)) mat2vec(A * ρ * B) ≈ spre(A) * spost(B) * mat2vec(ρ) ≈ sprepost(A, B) * mat2vec(ρ) ``` -In addition, dynamical generators on this extended space, often called Liouvillian superoperators, can be created using the [`liouvillian`](@ref) function. Each of these takes a Hamiltonian along with a list of collapse operators, and returns a [`type=SuperOperator`](@ref SuperOperator) object that can be exponentiated to find the superoperator for that evolution. +In addition, dynamical generators on this extended space, often called Liouvillian superoperators, can be created using the [`liouvillian`](@ref) function. Each of these takes a Hamiltonian along with a list of collapse operators, and returns a [`QuantumObject`](@ref) of type [`SuperOperator`](@ref) that can be exponentiated to find the superoperator for that evolution. ```@example states_and_operators H = 10 * sigmaz() diff --git a/ext/QuantumToolboxMakieExt.jl b/ext/QuantumToolboxMakieExt.jl index 3beb577da..41aefe2b9 100644 --- a/ext/QuantumToolboxMakieExt.jl +++ b/ext/QuantumToolboxMakieExt.jl @@ -54,7 +54,7 @@ function QuantumToolbox.plot_wigner( location::Union{GridPosition,Nothing} = nothing, colorbar::Bool = false, kwargs..., -) where {OpType<:Union{BraQuantumObject,KetQuantumObject,OperatorQuantumObject}} +) where {OpType<:Union{Bra,Ket,Operator}} QuantumToolbox.getVal(projection) == :two_dim || QuantumToolbox.getVal(projection) == :three_dim || throw(ArgumentError("Unsupported projection: $projection")) @@ -84,7 +84,7 @@ function _plot_wigner( location::Union{GridPosition,Nothing}, colorbar::Bool; kwargs..., -) where {OpType<:Union{BraQuantumObject,KetQuantumObject,OperatorQuantumObject}} +) where {OpType<:Union{Bra,Ket,Operator}} fig, location = _getFigAndLocation(location) lyt = GridLayout(location) @@ -117,7 +117,7 @@ function _plot_wigner( location::Union{GridPosition,Nothing}, colorbar::Bool; kwargs..., -) where {OpType<:Union{BraQuantumObject,KetQuantumObject,OperatorQuantumObject}} +) where {OpType<:Union{Bra,Ket,Operator}} fig, location = _getFigAndLocation(location) lyt = GridLayout(location) @@ -148,7 +148,7 @@ end unit_y_range::Bool = true, location::Union{GridPosition,Nothing} = nothing, kwargs... - ) where {SType<:Union{KetQuantumObject,OperatorQuantumObject}} + ) where {SType<:Union{Ket,Operator}} Plot the [Fock state](https://en.wikipedia.org/wiki/Fock_state) distribution of `ρ`. @@ -178,7 +178,7 @@ function QuantumToolbox.plot_fock_distribution( unit_y_range::Bool = true, location::Union{GridPosition,Nothing} = nothing, kwargs..., -) where {SType<:Union{BraQuantumObject,KetQuantumObject,OperatorQuantumObject}} +) where {SType<:Union{Bra,Ket,Operator}} return _plot_fock_distribution( library, ρ; @@ -196,7 +196,7 @@ function _plot_fock_distribution( unit_y_range::Bool = true, location::Union{GridPosition,Nothing} = nothing, kwargs..., -) where {SType<:Union{BraQuantumObject,KetQuantumObject,OperatorQuantumObject}} +) where {SType<:Union{Bra,Ket,Operator}} ρ = ket2dm(ρ) D = prod(ρ.dims) isapprox(tr(ρ), 1, atol = 1e-4) || (@warn "The input ρ should be normalized.") diff --git a/src/correlations.jl b/src/correlations.jl index 0df4375f2..3eae092b5 100644 --- a/src/correlations.jl +++ b/src/correlations.jl @@ -29,14 +29,11 @@ function correlation_3op_2t( tlist::AbstractVector, τlist::AbstractVector, c_ops::Union{Nothing,AbstractVector,Tuple}, - A::QuantumObject{OperatorQuantumObject}, - B::QuantumObject{OperatorQuantumObject}, - C::QuantumObject{OperatorQuantumObject}; + A::QuantumObject{Operator}, + B::QuantumObject{Operator}, + C::QuantumObject{Operator}; kwargs..., -) where { - HOpType<:Union{OperatorQuantumObject,SuperOperatorQuantumObject}, - StateOpType<:Union{KetQuantumObject,OperatorQuantumObject}, -} +) where {HOpType<:Union{Operator,SuperOperator},StateOpType<:Union{Ket,Operator}} # check tlist and τlist _check_correlation_time_list(tlist) _check_correlation_time_list(τlist) @@ -78,14 +75,11 @@ function correlation_3op_1t( ψ0::Union{Nothing,QuantumObject{StateOpType}}, τlist::AbstractVector, c_ops::Union{Nothing,AbstractVector,Tuple}, - A::QuantumObject{OperatorQuantumObject}, - B::QuantumObject{OperatorQuantumObject}, - C::QuantumObject{OperatorQuantumObject}; + A::QuantumObject{Operator}, + B::QuantumObject{Operator}, + C::QuantumObject{Operator}; kwargs..., -) where { - HOpType<:Union{OperatorQuantumObject,SuperOperatorQuantumObject}, - StateOpType<:Union{KetQuantumObject,OperatorQuantumObject}, -} +) where {HOpType<:Union{Operator,SuperOperator},StateOpType<:Union{Ket,Operator}} corr = correlation_3op_2t(H, ψ0, [0], τlist, c_ops, A, B, C; kwargs...) return corr[1, :] # 1 means tlist[1] = 0 @@ -114,14 +108,11 @@ function correlation_2op_2t( tlist::AbstractVector, τlist::AbstractVector, c_ops::Union{Nothing,AbstractVector,Tuple}, - A::QuantumObject{OperatorQuantumObject}, - B::QuantumObject{OperatorQuantumObject}; + A::QuantumObject{Operator}, + B::QuantumObject{Operator}; reverse::Bool = false, kwargs..., -) where { - HOpType<:Union{OperatorQuantumObject,SuperOperatorQuantumObject}, - StateOpType<:Union{KetQuantumObject,OperatorQuantumObject}, -} +) where {HOpType<:Union{Operator,SuperOperator},StateOpType<:Union{Ket,Operator}} C = eye(prod(H.dimensions), dims = H.dimensions) if reverse corr = correlation_3op_2t(H, ψ0, tlist, τlist, c_ops, A, B, C; kwargs...) @@ -153,14 +144,11 @@ function correlation_2op_1t( ψ0::Union{Nothing,QuantumObject{StateOpType}}, τlist::AbstractVector, c_ops::Union{Nothing,AbstractVector,Tuple}, - A::QuantumObject{OperatorQuantumObject}, - B::QuantumObject{OperatorQuantumObject}; + A::QuantumObject{Operator}, + B::QuantumObject{Operator}; reverse::Bool = false, kwargs..., -) where { - HOpType<:Union{OperatorQuantumObject,SuperOperatorQuantumObject}, - StateOpType<:Union{KetQuantumObject,OperatorQuantumObject}, -} +) where {HOpType<:Union{Operator,SuperOperator},StateOpType<:Union{Ket,Operator}} corr = correlation_2op_2t(H, ψ0, [0], τlist, c_ops, A, B; reverse = reverse, kwargs...) return corr[1, :] # 1 means tlist[1] = 0 diff --git a/src/deprecated.jl b/src/deprecated.jl index 56e5d04e7..f0a095bf0 100644 --- a/src/deprecated.jl +++ b/src/deprecated.jl @@ -33,15 +33,12 @@ correlation_3op_2t( ψ0::QuantumObject{StateOpType}, t_l::AbstractVector, τ_l::AbstractVector, - A::QuantumObject{OperatorQuantumObject}, - B::QuantumObject{OperatorQuantumObject}, - C::QuantumObject{OperatorQuantumObject}, + A::QuantumObject{Operator}, + B::QuantumObject{Operator}, + C::QuantumObject{Operator}, c_ops::Union{Nothing,AbstractVector,Tuple} = nothing; kwargs..., -) where { - HOpType<:Union{OperatorQuantumObject,SuperOperatorQuantumObject}, - StateOpType<:Union{KetQuantumObject,OperatorQuantumObject}, -} = error( +) where {HOpType<:Union{Operator,SuperOperator},StateOpType<:Union{Ket,Operator}} = error( "The parameter order of `correlation_3op_2t` has been changed, please use `?correlation_3op_2t` to check the updated docstring.", ) @@ -49,15 +46,12 @@ correlation_3op_1t( H::QuantumObject{HOpType}, ψ0::QuantumObject{StateOpType}, τ_l::AbstractVector, - A::QuantumObject{OperatorQuantumObject}, - B::QuantumObject{OperatorQuantumObject}, - C::QuantumObject{OperatorQuantumObject}, + A::QuantumObject{Operator}, + B::QuantumObject{Operator}, + C::QuantumObject{Operator}, c_ops::Union{Nothing,AbstractVector,Tuple} = nothing; kwargs..., -) where { - HOpType<:Union{OperatorQuantumObject,SuperOperatorQuantumObject}, - StateOpType<:Union{KetQuantumObject,OperatorQuantumObject}, -} = error( +) where {HOpType<:Union{Operator,SuperOperator},StateOpType<:Union{Ket,Operator}} = error( "The parameter order of `correlation_3op_1t` has been changed, please use `?correlation_3op_1t` to check the updated docstring.", ) @@ -66,15 +60,12 @@ correlation_2op_2t( ψ0::QuantumObject{StateOpType}, t_l::AbstractVector, τ_l::AbstractVector, - A::QuantumObject{OperatorQuantumObject}, - B::QuantumObject{OperatorQuantumObject}, + A::QuantumObject{Operator}, + B::QuantumObject{Operator}, c_ops::Union{Nothing,AbstractVector,Tuple} = nothing; reverse::Bool = false, kwargs..., -) where { - HOpType<:Union{OperatorQuantumObject,SuperOperatorQuantumObject}, - StateOpType<:Union{KetQuantumObject,OperatorQuantumObject}, -} = error( +) where {HOpType<:Union{Operator,SuperOperator},StateOpType<:Union{Ket,Operator}} = error( "The parameter order of `correlation_2op_2t` has been changed, please use `?correlation_2op_2t` to check the updated docstring.", ) @@ -82,15 +73,12 @@ correlation_2op_1t( H::QuantumObject{HOpType}, ψ0::QuantumObject{StateOpType}, τ_l::AbstractVector, - A::QuantumObject{OperatorQuantumObject}, - B::QuantumObject{OperatorQuantumObject}, + A::QuantumObject{Operator}, + B::QuantumObject{Operator}, c_ops::Union{Nothing,AbstractVector,Tuple} = nothing; reverse::Bool = false, kwargs..., -) where { - HOpType<:Union{OperatorQuantumObject,SuperOperatorQuantumObject}, - StateOpType<:Union{KetQuantumObject,OperatorQuantumObject}, -} = error( +) where {HOpType<:Union{Operator,SuperOperator},StateOpType<:Union{Ket,Operator}} = error( "The parameter order of `correlation_2op_1t` has been changed, please use `?correlation_2op_1t` to check the updated docstring.", ) diff --git a/src/entropy.jl b/src/entropy.jl index bdf057d86..575ea3209 100644 --- a/src/entropy.jl +++ b/src/entropy.jl @@ -22,7 +22,7 @@ Pure state: ```jldoctest julia> ψ = fock(2,0) -Quantum Object: type=Ket dims=[2] size=(2,) +Quantum Object: type=Ket() dims=[2] size=(2,) 2-element Vector{ComplexF64}: 1.0 + 0.0im 0.0 + 0.0im @@ -35,7 +35,7 @@ Mixed state: ```jldoctest julia> ρ = maximally_mixed_dm(2) -Quantum Object: type=Operator dims=[2] size=(2, 2) ishermitian=true +Quantum Object: type=Operator() dims=[2] size=(2, 2) ishermitian=true 2×2 Diagonal{ComplexF64, Vector{ComplexF64}}: 0.5-0.0im ⋅ ⋅ 0.5-0.0im @@ -44,11 +44,7 @@ julia> entropy_vn(ρ, base=2) 1.0 ``` """ -function entropy_vn( - ρ::QuantumObject{ObjType}; - base::Int = 0, - tol::Real = 1e-15, -) where {ObjType<:Union{KetQuantumObject,OperatorQuantumObject}} +function entropy_vn(ρ::QuantumObject{ObjType}; base::Int = 0, tol::Real = 1e-15) where {ObjType<:Union{Ket,Operator}} T = eltype(ρ) vals = eigenenergies(ket2dm(ρ)) indexes = findall(x -> abs(x) > tol, vals) @@ -78,10 +74,7 @@ function entropy_relative( σ::QuantumObject{ObjType2}; base::Int = 0, tol::Real = 1e-15, -) where { - ObjType1<:Union{KetQuantumObject,OperatorQuantumObject}, - ObjType2<:Union{KetQuantumObject,OperatorQuantumObject}, -} +) where {ObjType1<:Union{Ket,Operator},ObjType2<:Union{Ket,Operator}} check_dimensions(ρ, σ) # the logic of this code follows the detail given in the reference of the docstring @@ -131,8 +124,7 @@ Calculates the quantum linear entropy ``S_L = 1 - \textrm{Tr} \left[ \hat{\rho}^ Note that `ρ` can be either a [`Ket`](@ref) or an [`Operator`](@ref). """ -entropy_linear(ρ::QuantumObject{ObjType}) where {ObjType<:Union{KetQuantumObject,OperatorQuantumObject}} = - 1.0 - purity(ρ) # use 1.0 to make sure it always return value in Float-type +entropy_linear(ρ::QuantumObject{ObjType}) where {ObjType<:Union{Ket,Operator}} = 1.0 - purity(ρ) # use 1.0 to make sure it always return value in Float-type @doc raw""" entropy_mutual(ρAB::QuantumObject, selA, selB; kwargs...) @@ -153,7 +145,7 @@ function entropy_mutual( selA::Union{Int,AbstractVector{Int},Tuple}, selB::Union{Int,AbstractVector{Int},Tuple}; kwargs..., -) where {ObjType<:Union{KetQuantumObject,OperatorQuantumObject},N} +) where {ObjType<:Union{Ket,Operator},N} # check if selA and selB matches the dimensions of ρAB sel_A_B = (selA..., selB...) (length(sel_A_B) != N) && throw( @@ -185,8 +177,7 @@ entropy_conditional( ρAB::QuantumObject{ObjType,<:AbstractDimensions{N,N}}, selB::Union{Int,AbstractVector{Int},Tuple}; kwargs..., -) where {ObjType<:Union{KetQuantumObject,OperatorQuantumObject},N} = - entropy_vn(ρAB; kwargs...) - entropy_vn(ptrace(ρAB, selB); kwargs...) +) where {ObjType<:Union{Ket,Operator},N} = entropy_vn(ρAB; kwargs...) - entropy_vn(ptrace(ρAB, selB); kwargs...) @doc raw""" entanglement(ρ::QuantumObject, sel; kwargs...) @@ -203,7 +194,7 @@ function entanglement( ρ::QuantumObject{OpType}, sel::Union{Int,AbstractVector{Int},Tuple}, kwargs..., -) where {OpType<:Union{KetQuantumObject,OperatorQuantumObject}} +) where {OpType<:Union{Ket,Operator}} p = purity(ρ) isapprox(p, 1; atol = 1e-2) || throw( ArgumentError( @@ -229,7 +220,7 @@ Calculate the [concurrence](https://en.wikipedia.org/wiki/Concurrence_(quantum_c - [Hill-Wootters1997](@citet) """ -function concurrence(ρ::QuantumObject{OpType}) where {OpType<:Union{KetQuantumObject,OperatorQuantumObject}} +function concurrence(ρ::QuantumObject{OpType}) where {OpType<:Union{Ket,Operator}} (ρ.dimensions == Dimensions((Space(2), Space(2)))) || throw( ArgumentError( "The `concurrence` only works for a two-qubit state, invalid dims = $(_get_dims_string(ρ.dimensions)).", diff --git a/src/metrics.jl b/src/metrics.jl index d9462682f..90c22b01a 100644 --- a/src/metrics.jl +++ b/src/metrics.jl @@ -16,14 +16,14 @@ Here, the definition is from [Nielsen-Chuang2011](@citet). It is the square root Note that `ρ` and `σ` must be either [`Ket`](@ref) or [`Operator`](@ref). """ -function fidelity(ρ::QuantumObject{OperatorQuantumObject}, σ::QuantumObject{OperatorQuantumObject}) +function fidelity(ρ::QuantumObject{Operator}, σ::QuantumObject{Operator}) sqrt_ρ = sqrt(ρ) eigval = abs.(eigvals(sqrt_ρ * σ * sqrt_ρ)) return sum(sqrt, eigval) end -fidelity(ρ::QuantumObject{OperatorQuantumObject}, ψ::QuantumObject{KetQuantumObject}) = sqrt(abs(expect(ρ, ψ))) -fidelity(ψ::QuantumObject{KetQuantumObject}, σ::QuantumObject{OperatorQuantumObject}) = fidelity(σ, ψ) -fidelity(ψ::QuantumObject{KetQuantumObject}, ϕ::QuantumObject{KetQuantumObject}) = abs(dot(ψ, ϕ)) +fidelity(ρ::QuantumObject{Operator}, ψ::QuantumObject{Ket}) = sqrt(abs(expect(ρ, ψ))) +fidelity(ψ::QuantumObject{Ket}, σ::QuantumObject{Operator}) = fidelity(σ, ψ) +fidelity(ψ::QuantumObject{Ket}, ϕ::QuantumObject{Ket}) = abs(dot(ψ, ϕ)) @doc raw""" tracedist(ρ::QuantumObject, σ::QuantumObject) @@ -36,10 +36,7 @@ Note that `ρ` and `σ` must be either [`Ket`](@ref) or [`Operator`](@ref). tracedist( ρ::QuantumObject{ObjType1}, σ::QuantumObject{ObjType2}, -) where { - ObjType1<:Union{KetQuantumObject,OperatorQuantumObject}, - ObjType2<:Union{KetQuantumObject,OperatorQuantumObject}, -} = norm(ket2dm(ρ) - ket2dm(σ), 1) / 2 +) where {ObjType1<:Union{Ket,Operator},ObjType2<:Union{Ket,Operator}} = norm(ket2dm(ρ) - ket2dm(σ), 1) / 2 @doc raw""" hilbert_dist(ρ::QuantumObject, σ::QuantumObject) @@ -55,10 +52,7 @@ Note that `ρ` and `σ` must be either [`Ket`](@ref) or [`Operator`](@ref). function hilbert_dist( ρ::QuantumObject{ObjType1}, σ::QuantumObject{ObjType2}, -) where { - ObjType1<:Union{KetQuantumObject,OperatorQuantumObject}, - ObjType2<:Union{KetQuantumObject,OperatorQuantumObject}, -} +) where {ObjType1<:Union{Ket,Operator},ObjType2<:Union{Ket,Operator}} check_dimensions(ρ, σ) A = ket2dm(ρ) - ket2dm(σ) @@ -79,10 +73,7 @@ Note that `ρ` and `σ` must be either [`Ket`](@ref) or [`Operator`](@ref). function hellinger_dist( ρ::QuantumObject{ObjType1}, σ::QuantumObject{ObjType2}, -) where { - ObjType1<:Union{KetQuantumObject,OperatorQuantumObject}, - ObjType2<:Union{KetQuantumObject,OperatorQuantumObject}, -} +) where {ObjType1<:Union{Ket,Operator},ObjType2<:Union{Ket,Operator}} # Ket (pure state) doesn't need to do square root sqrt_ρ = isket(ρ) ? ket2dm(ρ) : sqrt(ρ) sqrt_σ = isket(σ) ? ket2dm(σ) : sqrt(σ) diff --git a/src/negativity.jl b/src/negativity.jl index eefaab3d3..e19afa341 100644 --- a/src/negativity.jl +++ b/src/negativity.jl @@ -8,7 +8,7 @@ where ``\hat{\rho}^{\Gamma}`` is the partial transpose of ``\hat{\rho}`` with re and ``\Vert \hat{X} \Vert_1=\textrm{Tr}\sqrt{\hat{X}^\dagger \hat{X}}`` is the trace norm. # Arguments -- `ρ::QuantumObject`: The density matrix (`ρ.type` must be [`OperatorQuantumObject`](@ref)). +- `ρ::QuantumObject`: The density matrix (`ρ.type` must be [`Operator`](@ref)). - `subsys::Int`: an index that indicates which subsystem to compute the negativity for. - `logarithmic::Bool`: choose whether to calculate logarithmic negativity or not. Default as `false` @@ -20,7 +20,7 @@ and ``\Vert \hat{X} \Vert_1=\textrm{Tr}\sqrt{\hat{X}^\dagger \hat{X}}`` is the t ```jldoctest julia> Ψ = bell_state(0, 0) -Quantum Object: type=Ket dims=[2, 2] size=(4,) +Quantum Object: type=Ket() dims=[2, 2] size=(4,) 4-element Vector{ComplexF64}: 0.7071067811865475 + 0.0im 0.0 + 0.0im @@ -29,7 +29,7 @@ Quantum Object: type=Ket dims=[2, 2] size=(4,) julia> ρ = ket2dm(Ψ) -Quantum Object: type=Operator dims=[2, 2] size=(4, 4) ishermitian=true +Quantum Object: type=Operator() dims=[2, 2] size=(4, 4) ishermitian=true 4×4 Matrix{ComplexF64}: 0.5+0.0im 0.0+0.0im 0.0+0.0im 0.5+0.0im 0.0+0.0im 0.0+0.0im 0.0+0.0im 0.0+0.0im @@ -64,13 +64,13 @@ end Return the partial transpose of a density matrix ``\rho``, where `mask` is an array/vector with length that equals the length of `ρ.dims`. The elements in `mask` are boolean (`true` or `false`) which indicates whether or not the corresponding subsystem should be transposed. # Arguments -- `ρ::QuantumObject`: The density matrix (`ρ.type` must be [`OperatorQuantumObject`](@ref)). +- `ρ::QuantumObject`: The density matrix (`ρ.type` must be [`Operator`](@ref)). - `mask::Vector{Bool}`: A boolean vector selects which subsystems should be transposed. # Returns - `ρ_pt::QuantumObject`: The density matrix with the selected subsystems transposed. """ -function partial_transpose(ρ::QuantumObject{OperatorQuantumObject}, mask::Vector{Bool}) +function partial_transpose(ρ::QuantumObject{Operator}, mask::Vector{Bool}) if length(mask) != length(ρ.dimensions) throw(ArgumentError("The length of \`mask\` should be equal to the length of \`ρ.dims\`.")) end @@ -78,7 +78,7 @@ function partial_transpose(ρ::QuantumObject{OperatorQuantumObject}, mask::Vecto end # for dense matrices -function _partial_transpose(ρ::QuantumObject{OperatorQuantumObject}, mask::Vector{Bool}) +function _partial_transpose(ρ::QuantumObject{Operator}, mask::Vector{Bool}) isa(ρ.dimensions, GeneralDimensions) && (get_dimensions_to(ρ) != get_dimensions_from(ρ)) && throw(ArgumentError("Invalid partial transpose for dims = $(_get_dims_string(ρ.dimensions))")) @@ -97,14 +97,14 @@ function _partial_transpose(ρ::QuantumObject{OperatorQuantumObject}, mask::Vect ] return QuantumObject( reshape(permutedims(reshape(ρ.data, (dims..., dims...)), pt_idx), size(ρ)), - Operator, + Operator(), Dimensions(ρ.dimensions.to), ) end # for sparse matrices function _partial_transpose( - ρ::QuantumObject{OperatorQuantumObject,DimsType,<:AbstractSparseArray}, + ρ::QuantumObject{Operator,DimsType,<:AbstractSparseArray}, mask::Vector{Bool}, ) where {DimsType<:AbstractDimensions} isa(ρ.dimensions, GeneralDimensions) && @@ -144,5 +144,5 @@ function _partial_transpose( end end - return QuantumObject(sparse(I_pt, J_pt, V_pt, M, N), Operator, ρ.dimensions) + 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 a18a383c8..8eecc5acd 100644 --- a/src/qobj/arithmetic_and_attributes.jl +++ b/src/qobj/arithmetic_and_attributes.jl @@ -64,25 +64,25 @@ for ADimType in (:Dimensions, :GeneralDimensions) if ADimType == BDimType == :Dimensions @eval begin function Base.:(*)( - A::AbstractQuantumObject{OperatorQuantumObject,<:$ADimType}, - B::AbstractQuantumObject{OperatorQuantumObject,<:$BDimType}, + A::AbstractQuantumObject{Operator,<:$ADimType}, + B::AbstractQuantumObject{Operator,<:$BDimType}, ) check_dimensions(A, B) QType = promote_op_type(A, B) - return QType(A.data * B.data, Operator, A.dimensions) + return QType(A.data * B.data, Operator(), A.dimensions) end end else @eval begin function Base.:(*)( - A::AbstractQuantumObject{OperatorQuantumObject,<:$ADimType}, - B::AbstractQuantumObject{OperatorQuantumObject,<:$BDimType}, + A::AbstractQuantumObject{Operator,<:$ADimType}, + B::AbstractQuantumObject{Operator,<:$BDimType}, ) check_mul_dimensions(get_dimensions_from(A), get_dimensions_to(B)) QType = promote_op_type(A, B) return QType( A.data * B.data, - Operator, + Operator(), GeneralDimensions(get_dimensions_to(A), get_dimensions_from(B)), ) end @@ -91,37 +91,37 @@ for ADimType in (:Dimensions, :GeneralDimensions) end end -function Base.:(*)(A::AbstractQuantumObject{OperatorQuantumObject}, B::QuantumObject{KetQuantumObject,<:Dimensions}) +function Base.:(*)(A::AbstractQuantumObject{Operator}, B::QuantumObject{Ket,<:Dimensions}) check_mul_dimensions(get_dimensions_from(A), get_dimensions_to(B)) - return QuantumObject(A.data * B.data, Ket, Dimensions(get_dimensions_to(A))) + return QuantumObject(A.data * B.data, Ket(), Dimensions(get_dimensions_to(A))) end -function Base.:(*)(A::QuantumObject{BraQuantumObject,<:Dimensions}, B::AbstractQuantumObject{OperatorQuantumObject}) +function Base.:(*)(A::QuantumObject{Bra,<:Dimensions}, B::AbstractQuantumObject{Operator}) check_mul_dimensions(get_dimensions_from(A), get_dimensions_to(B)) - return QuantumObject(A.data * B.data, Bra, Dimensions(get_dimensions_from(B))) + return QuantumObject(A.data * B.data, Bra(), Dimensions(get_dimensions_from(B))) end -function Base.:(*)(A::QuantumObject{KetQuantumObject}, B::QuantumObject{BraQuantumObject}) +function Base.:(*)(A::QuantumObject{Ket}, B::QuantumObject{Bra}) 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. + return QuantumObject(A.data * B.data, Operator(), A.dimensions) # to align with QuTiP, don't use kron(A, B) to do it. end -function Base.:(*)(A::QuantumObject{BraQuantumObject}, B::QuantumObject{KetQuantumObject}) +function Base.:(*)(A::QuantumObject{Bra}, B::QuantumObject{Ket}) check_dimensions(A, B) return A.data * B.data end -function Base.:(*)(A::AbstractQuantumObject{SuperOperatorQuantumObject}, B::QuantumObject{OperatorQuantumObject}) +function Base.:(*)(A::AbstractQuantumObject{SuperOperator}, B::QuantumObject{Operator}) check_dimensions(A, B) - return QuantumObject(vec2mat(A.data * mat2vec(B.data)), Operator, A.dimensions) + return QuantumObject(vec2mat(A.data * mat2vec(B.data)), Operator(), A.dimensions) end -function Base.:(*)(A::QuantumObject{OperatorBraQuantumObject}, B::QuantumObject{OperatorKetQuantumObject}) +function Base.:(*)(A::QuantumObject{OperatorBra}, B::QuantumObject{OperatorKet}) check_dimensions(A, B) return A.data * B.data end -function Base.:(*)(A::AbstractQuantumObject{SuperOperatorQuantumObject}, B::QuantumObject{OperatorKetQuantumObject}) +function Base.:(*)(A::AbstractQuantumObject{SuperOperator}, B::QuantumObject{OperatorKet}) check_dimensions(A, B) - return QuantumObject(A.data * B.data, OperatorKet, A.dimensions) + return QuantumObject(A.data * B.data, OperatorKet(), A.dimensions) end -function Base.:(*)(A::QuantumObject{OperatorBraQuantumObject}, B::AbstractQuantumObject{SuperOperatorQuantumObject}) +function Base.:(*)(A::QuantumObject{OperatorBra}, B::AbstractQuantumObject{SuperOperator}) check_dimensions(A, B) - return QuantumObject(A.data * B.data, OperatorBra, A.dimensions) + return QuantumObject(A.data * B.data, OperatorBra(), A.dimensions) end Base.:(^)(A::QuantumObject, n::T) where {T<:Number} = QuantumObject(^(A.data, n), A.type, A.dimensions) @@ -138,10 +138,7 @@ Note that `A` and `B` should be [`Ket`](@ref) or [`OperatorKet`](@ref) !!! note `A ⋅ B` (where `⋅` can be typed by tab-completing `\cdot` in the REPL) is a synonym of `dot(A, B)`. """ -function LinearAlgebra.dot( - A::QuantumObject{OpType}, - B::QuantumObject{OpType}, -) where {OpType<:Union{KetQuantumObject,OperatorKetQuantumObject}} +function LinearAlgebra.dot(A::QuantumObject{OpType}, B::QuantumObject{OpType}) where {OpType<:Union{Ket,OperatorKet}} check_dimensions(A, B) return LinearAlgebra.dot(A.data, B.data) end @@ -159,18 +156,14 @@ Supports the following inputs: !!! note `matrix_element(i, A, j)` is a synonym of `dot(i, A, j)`. """ -function LinearAlgebra.dot( - i::QuantumObject{KetQuantumObject}, - A::AbstractQuantumObject{OperatorQuantumObject}, - j::QuantumObject{KetQuantumObject}, -) +function LinearAlgebra.dot(i::QuantumObject{Ket}, A::AbstractQuantumObject{Operator}, j::QuantumObject{Ket}) check_dimensions(i, A, j) return LinearAlgebra.dot(i.data, A.data, j.data) end function LinearAlgebra.dot( - i::QuantumObject{OperatorKetQuantumObject}, - A::AbstractQuantumObject{SuperOperatorQuantumObject}, - j::QuantumObject{OperatorKetQuantumObject}, + i::QuantumObject{OperatorKet}, + A::AbstractQuantumObject{SuperOperator}, + j::QuantumObject{OperatorKet}, ) check_dimensions(i, A, j) return LinearAlgebra.dot(i.data, A.data, j.data) @@ -190,7 +183,7 @@ Return a similar [`AbstractQuantumObject`](@ref) with `dims` and `type` are same Note that `A` must be [`Operator`](@ref) or [`SuperOperator`](@ref). """ -Base.one(A::AbstractQuantumObject{OpType}) where {OpType<:Union{OperatorQuantumObject,SuperOperatorQuantumObject}} = +Base.one(A::AbstractQuantumObject{OpType}) where {OpType<:Union{Operator,SuperOperator}} = get_typename_wrapper(A)(one(A.data), A.type, A.dimensions) @doc raw""" @@ -205,9 +198,7 @@ Base.conj(A::AbstractQuantumObject) = get_typename_wrapper(A)(conj(A.data), A.ty Lazy matrix transpose of the [`AbstractQuantumObject`](@ref). """ -Base.transpose( - A::AbstractQuantumObject{OpType}, -) where {OpType<:Union{OperatorQuantumObject,SuperOperatorQuantumObject}} = +Base.transpose(A::AbstractQuantumObject{OpType}) where {OpType<:Union{Operator,SuperOperator}} = get_typename_wrapper(A)(transpose(A.data), A.type, transpose(A.dimensions)) @doc raw""" @@ -220,29 +211,22 @@ Lazy adjoint (conjugate transposition) of the [`AbstractQuantumObject`](@ref) !!! note `A'` and `dag(A)` are synonyms of `adjoint(A)`. """ -Base.adjoint(A::AbstractQuantumObject{OpType}) where {OpType<:Union{OperatorQuantumObject,SuperOperatorQuantumObject}} = +Base.adjoint(A::AbstractQuantumObject{OpType}) where {OpType<:Union{Operator,SuperOperator}} = get_typename_wrapper(A)(adjoint(A.data), A.type, adjoint(A.dimensions)) -Base.adjoint(A::QuantumObject{KetQuantumObject}) = QuantumObject(adjoint(A.data), Bra, adjoint(A.dimensions)) -Base.adjoint(A::QuantumObject{BraQuantumObject}) = QuantumObject(adjoint(A.data), Ket, adjoint(A.dimensions)) -Base.adjoint(A::QuantumObject{OperatorKetQuantumObject}) = - QuantumObject(adjoint(A.data), OperatorBra, adjoint(A.dimensions)) -Base.adjoint(A::QuantumObject{OperatorBraQuantumObject}) = - QuantumObject(adjoint(A.data), OperatorKet, adjoint(A.dimensions)) +Base.adjoint(A::QuantumObject{Ket}) = QuantumObject(adjoint(A.data), Bra(), adjoint(A.dimensions)) +Base.adjoint(A::QuantumObject{Bra}) = QuantumObject(adjoint(A.data), Ket(), adjoint(A.dimensions)) +Base.adjoint(A::QuantumObject{OperatorKet}) = QuantumObject(adjoint(A.data), OperatorBra(), adjoint(A.dimensions)) +Base.adjoint(A::QuantumObject{OperatorBra}) = QuantumObject(adjoint(A.data), OperatorKet(), adjoint(A.dimensions)) @doc raw""" inv(A::AbstractQuantumObject) Matrix inverse of the [`AbstractQuantumObject`](@ref). If `A` is a [`QuantumObjectEvolution`](@ref), the inverse is computed at the last computed time. """ -LinearAlgebra.inv( - A::AbstractQuantumObject{OpType}, -) where {OpType<:Union{OperatorQuantumObject,SuperOperatorQuantumObject}} = +LinearAlgebra.inv(A::AbstractQuantumObject{OpType}) where {OpType<:Union{Operator,SuperOperator}} = QuantumObject(sparse(inv(Matrix(A.data))), A.type, A.dimensions) -LinearAlgebra.Hermitian( - A::QuantumObject{OpType}, - uplo::Symbol = :U, -) where {OpType<:Union{OperatorQuantumObject,SuperOperatorQuantumObject}} = +LinearAlgebra.Hermitian(A::QuantumObject{OpType}, uplo::Symbol = :U) where {OpType<:Union{Operator,SuperOperator}} = QuantumObject(Hermitian(A.data, uplo), A.type, A.dimensions) @doc raw""" @@ -257,7 +241,7 @@ Note that this function only supports for [`Operator`](@ref) and [`SuperOperator ```jldoctest julia> a = destroy(20) -Quantum Object: type=Operator dims=[20] size=(20, 20) ishermitian=false +Quantum Object: type=Operator() dims=[20] size=(20, 20) ishermitian=false 20×20 SparseMatrixCSC{ComplexF64, Int64} with 19 stored entries: ⎡⠈⠢⡀⠀⠀⠀⠀⠀⠀⠀⎤ ⎢⠀⠀⠈⠢⡀⠀⠀⠀⠀⠀⎥ @@ -269,11 +253,10 @@ julia> tr(a' * a) 190.0 + 0.0im ``` """ -LinearAlgebra.tr(A::QuantumObject{OpType}) where {OpType<:Union{OperatorQuantumObject,SuperOperatorQuantumObject}} = - tr(A.data) +LinearAlgebra.tr(A::QuantumObject{OpType}) where {OpType<:Union{Operator,SuperOperator}} = tr(A.data) LinearAlgebra.tr( A::QuantumObject{OpType,DimsType,<:Union{<:Hermitian{TF},Symmetric{TR}}}, -) where {OpType<:OperatorQuantumObject,DimsType,TF<:BlasFloat,TR<:Real} = real(tr(A.data)) +) where {OpType<:Operator,DimsType,TF<:BlasFloat,TR<:Real} = real(tr(A.data)) @doc raw""" svdvals(A::QuantumObject) @@ -295,7 +278,7 @@ Return the standard vector `p`-norm or [Schatten](https://en.wikipedia.org/wiki/ ```jldoctest julia> ψ = fock(10, 2) -Quantum Object: type=Ket dims=[10] size=(10,) +Quantum Object: type=Ket() dims=[10] size=(10,) 10-element Vector{ComplexF64}: 0.0 + 0.0im 0.0 + 0.0im @@ -312,15 +295,9 @@ julia> norm(ψ) 1.0 ``` """ -LinearAlgebra.norm( - A::QuantumObject{OpType}, - p::Real = 2, -) where {OpType<:Union{KetQuantumObject,BraQuantumObject,OperatorKetQuantumObject,OperatorBraQuantumObject}} = +LinearAlgebra.norm(A::QuantumObject{OpType}, p::Real = 2) where {OpType<:Union{Ket,Bra,OperatorKet,OperatorBra}} = norm(A.data, p) -function LinearAlgebra.norm( - A::QuantumObject{OpType}, - p::Real = 1, -) where {OpType<:Union{OperatorQuantumObject,SuperOperatorQuantumObject}} +function LinearAlgebra.norm(A::QuantumObject{OpType}, p::Real = 1) where {OpType<:Union{Operator,SuperOperator}} p == 2.0 && return norm(A.data, 2) return norm(svdvals(A), p) end @@ -340,11 +317,9 @@ Support for the following types of [`QuantumObject`](@ref): Also, see [`norm`](@ref) about its definition for different types of [`QuantumObject`](@ref). """ -LinearAlgebra.normalize( - A::QuantumObject{ObjType}, - p::Real = 2, -) where {ObjType<:Union{KetQuantumObject,BraQuantumObject}} = QuantumObject(A.data / norm(A, p), A.type, A.dimensions) -LinearAlgebra.normalize(A::QuantumObject{OperatorQuantumObject}, p::Real = 1) = +LinearAlgebra.normalize(A::QuantumObject{ObjType}, p::Real = 2) where {ObjType<:Union{Ket,Bra}} = + QuantumObject(A.data / norm(A, p), A.type, A.dimensions) +LinearAlgebra.normalize(A::QuantumObject{Operator}, p::Real = 1) = QuantumObject(A.data / norm(A, p), A.type, A.dimensions) @doc raw""" @@ -358,29 +333,17 @@ Support for the following types of [`QuantumObject`](@ref): Also, see [`norm`](@ref) about its definition for different types of [`QuantumObject`](@ref). """ -LinearAlgebra.normalize!( - A::QuantumObject{ObjType}, - p::Real = 2, -) where {ObjType<:Union{KetQuantumObject,BraQuantumObject}} = (rmul!(A.data, 1 / norm(A, p)); A) -LinearAlgebra.normalize!(A::QuantumObject{OperatorQuantumObject}, p::Real = 1) = (rmul!(A.data, 1 / norm(A, p)); A) - -LinearAlgebra.triu!( - A::QuantumObject{OpType}, - k::Integer = 0, -) where {OpType<:Union{OperatorQuantumObject,SuperOperatorQuantumObject}} = (triu!(A.data, k); A) -LinearAlgebra.tril!( - A::QuantumObject{OpType}, - k::Integer = 0, -) where {OpType<:Union{OperatorQuantumObject,SuperOperatorQuantumObject}} = (tril!(A.data, k); A) -LinearAlgebra.triu( - A::QuantumObject{OpType}, - k::Integer = 0, -) where {OpType<:Union{OperatorQuantumObject,SuperOperatorQuantumObject}} = +LinearAlgebra.normalize!(A::QuantumObject{ObjType}, p::Real = 2) where {ObjType<:Union{Ket,Bra}} = + (rmul!(A.data, 1 / norm(A, p)); A) +LinearAlgebra.normalize!(A::QuantumObject{Operator}, p::Real = 1) = (rmul!(A.data, 1 / norm(A, p)); A) + +LinearAlgebra.triu!(A::QuantumObject{OpType}, k::Integer = 0) where {OpType<:Union{Operator,SuperOperator}} = + (triu!(A.data, k); A) +LinearAlgebra.tril!(A::QuantumObject{OpType}, k::Integer = 0) where {OpType<:Union{Operator,SuperOperator}} = + (tril!(A.data, k); A) +LinearAlgebra.triu(A::QuantumObject{OpType}, k::Integer = 0) where {OpType<:Union{Operator,SuperOperator}} = QuantumObject(triu(A.data, k), A.type, A.dimensions) -LinearAlgebra.tril( - A::QuantumObject{OpType}, - k::Integer = 0, -) where {OpType<:Union{OperatorQuantumObject,SuperOperatorQuantumObject}} = +LinearAlgebra.tril(A::QuantumObject{OpType}, k::Integer = 0) where {OpType<:Union{Operator,SuperOperator}} = QuantumObject(tril(A.data, k), A.type, A.dimensions) LinearAlgebra.lmul!(a::Number, B::QuantumObject) = (lmul!(a, B.data); B) @@ -406,7 +369,7 @@ Matrix logarithm of [`QuantumObject`](@ref) Note that this function only supports for [`Operator`](@ref) and [`SuperOperator`](@ref) """ -Base.log(A::QuantumObject{ObjType}) where {ObjType<:Union{OperatorQuantumObject,SuperOperatorQuantumObject}} = +Base.log(A::QuantumObject{ObjType}) where {ObjType<:Union{Operator,SuperOperator}} = QuantumObject(log(to_dense(A.data)), A.type, A.dimensions) @doc raw""" @@ -416,14 +379,11 @@ Matrix exponential of [`QuantumObject`](@ref) Note that this function only supports for [`Operator`](@ref) and [`SuperOperator`](@ref) """ -Base.exp( - A::QuantumObject{ObjType,DimsType,<:AbstractMatrix}, -) where {ObjType<:Union{OperatorQuantumObject,SuperOperatorQuantumObject},DimsType} = +Base.exp(A::QuantumObject{ObjType,DimsType,<:AbstractMatrix}) where {ObjType<:Union{Operator,SuperOperator},DimsType} = QuantumObject(to_sparse(exp(A.data)), A.type, A.dimensions) Base.exp( A::QuantumObject{ObjType,DimsType,<:AbstractSparseMatrix}, -) where {ObjType<:Union{OperatorQuantumObject,SuperOperatorQuantumObject},DimsType} = - QuantumObject(_spexp(A.data), A.type, A.dimensions) +) where {ObjType<:Union{Operator,SuperOperator},DimsType} = QuantumObject(_spexp(A.data), A.type, A.dimensions) function _spexp(A::SparseMatrixCSC{T,M}; threshold = 1e-14, nonzero_tol = 1e-20) where {T<:Number,M<:Int} m = checksquare(A) # Throws exception if not square @@ -465,7 +425,7 @@ Matrix sine of [`QuantumObject`](@ref), defined as Note that this function only supports for [`Operator`](@ref) and [`SuperOperator`](@ref) """ -Base.sin(A::QuantumObject{ObjType}) where {ObjType<:Union{OperatorQuantumObject,SuperOperatorQuantumObject}} = +Base.sin(A::QuantumObject{ObjType}) where {ObjType<:Union{Operator,SuperOperator}} = (exp(1im * A) - exp(-1im * A)) / 2im @doc raw""" @@ -477,8 +437,7 @@ Matrix cosine of [`QuantumObject`](@ref), defined as Note that this function only supports for [`Operator`](@ref) and [`SuperOperator`](@ref) """ -Base.cos(A::QuantumObject{ObjType}) where {ObjType<:Union{OperatorQuantumObject,SuperOperatorQuantumObject}} = - (exp(1im * A) + exp(-1im * A)) / 2 +Base.cos(A::QuantumObject{ObjType}) where {ObjType<:Union{Operator,SuperOperator}} = (exp(1im * A) + exp(-1im * A)) / 2 @doc raw""" diag(A::QuantumObject, k::Int=0) @@ -487,18 +446,16 @@ Return the `k`-th diagonal elements of a matrix-type [`QuantumObject`](@ref) Note that this function only supports for [`Operator`](@ref) and [`SuperOperator`](@ref) """ -LinearAlgebra.diag( - A::QuantumObject{ObjType}, - k::Int = 0, -) where {ObjType<:Union{OperatorQuantumObject,SuperOperatorQuantumObject}} = diag(A.data, k) +LinearAlgebra.diag(A::QuantumObject{ObjType}, k::Int = 0) where {ObjType<:Union{Operator,SuperOperator}} = + diag(A.data, k) @doc raw""" proj(ψ::QuantumObject) Return the projector for a [`Ket`](@ref) or [`Bra`](@ref) type of [`QuantumObject`](@ref) """ -proj(ψ::QuantumObject{KetQuantumObject}) = ψ * ψ' -proj(ψ::QuantumObject{BraQuantumObject}) = ψ' * ψ +proj(ψ::QuantumObject{Ket}) = ψ * ψ' +proj(ψ::QuantumObject{Bra}) = ψ' * ψ @doc raw""" ptrace(QO::QuantumObject, sel) @@ -513,7 +470,7 @@ Two qubits in the state ``\ket{\psi} = \ket{e,g}``: ```jldoctest julia> ψ = kron(fock(2,0), fock(2,1)) -Quantum Object: type=Ket dims=[2, 2] size=(4,) +Quantum Object: type=Ket() dims=[2, 2] size=(4,) 4-element Vector{ComplexF64}: 0.0 + 0.0im 1.0 + 0.0im @@ -522,7 +479,7 @@ Quantum Object: type=Ket dims=[2, 2] size=(4,) julia> ptrace(ψ, 2) -Quantum Object: type=Operator dims=[2] size=(2, 2) ishermitian=true +Quantum Object: type=Operator() dims=[2] size=(2, 2) ishermitian=true 2×2 Matrix{ComplexF64}: 0.0+0.0im 0.0+0.0im 0.0+0.0im 1.0+0.0im @@ -532,7 +489,7 @@ or in an entangled state ``\ket{\psi} = \frac{1}{\sqrt{2}} \left( \ket{e,e} + \k ```jldoctest julia> ψ = 1 / √2 * (kron(fock(2,0), fock(2,0)) + kron(fock(2,1), fock(2,1))) -Quantum Object: type=Ket dims=[2, 2] size=(4,) +Quantum Object: type=Ket() dims=[2, 2] size=(4,) 4-element Vector{ComplexF64}: 0.7071067811865475 + 0.0im 0.0 + 0.0im @@ -541,13 +498,13 @@ Quantum Object: type=Ket dims=[2, 2] size=(4,) julia> ptrace(ψ, 1) -Quantum Object: type=Operator dims=[2] size=(2, 2) ishermitian=true +Quantum Object: type=Operator() dims=[2] size=(2, 2) ishermitian=true 2×2 Matrix{ComplexF64}: 0.5+0.0im 0.0+0.0im 0.0+0.0im 0.5+0.0im ``` """ -function ptrace(QO::QuantumObject{KetQuantumObject}, sel::Union{AbstractVector{Int},Tuple}) +function ptrace(QO::QuantumObject{Ket}, sel::Union{AbstractVector{Int},Tuple}) _non_static_array_warning("sel", sel) if length(sel) == 0 # return full trace for empty sel @@ -564,12 +521,12 @@ function ptrace(QO::QuantumObject{KetQuantumObject}, sel::Union{AbstractVector{I _sort_sel = sort(SVector{length(sel),Int}(sel)) ρtr, dkeep = _ptrace_ket(QO.data, QO.dims, _sort_sel) - return QuantumObject(ρtr, type = Operator, dims = Dimensions(dkeep)) + return QuantumObject(ρtr, type = Operator(), dims = Dimensions(dkeep)) end -ptrace(QO::QuantumObject{BraQuantumObject}, sel::Union{AbstractVector{Int},Tuple}) = ptrace(QO', sel) +ptrace(QO::QuantumObject{Bra}, sel::Union{AbstractVector{Int},Tuple}) = ptrace(QO', sel) -function ptrace(QO::QuantumObject{OperatorQuantumObject}, sel::Union{AbstractVector{Int},Tuple}) +function ptrace(QO::QuantumObject{Operator}, sel::Union{AbstractVector{Int},Tuple}) # TODO: support for special cases when some of the subsystems have same `to` and `from` space isa(QO.dimensions, GeneralDimensions) && (get_dimensions_to(QO) != get_dimensions_from(QO)) && @@ -592,7 +549,7 @@ function ptrace(QO::QuantumObject{OperatorQuantumObject}, sel::Union{AbstractVec dims = dimensions_to_dims(get_dimensions_to(QO)) _sort_sel = sort(SVector{length(sel),Int}(sel)) ρtr, dkeep = _ptrace_oper(QO.data, dims, _sort_sel) - return QuantumObject(ρtr, type = Operator, dims = Dimensions(dkeep)) + return QuantumObject(ρtr, type = Operator(), dims = Dimensions(dkeep)) end ptrace(QO::QuantumObject, sel::Int) = ptrace(QO, SVector(sel)) @@ -668,8 +625,8 @@ Calculate the purity of a [`QuantumObject`](@ref): ``\textrm{Tr}(\rho^2)`` Note that this function only supports for [`Ket`](@ref), [`Bra`](@ref), and [`Operator`](@ref) """ -purity(ρ::QuantumObject{ObjType}) where {ObjType<:Union{KetQuantumObject,BraQuantumObject}} = sum(abs2, ρ.data) -purity(ρ::QuantumObject{OperatorQuantumObject}) = real(tr(ρ.data^2)) +purity(ρ::QuantumObject{ObjType}) where {ObjType<:Union{Ket,Bra}} = sum(abs2, ρ.data) +purity(ρ::QuantumObject{Operator}) = real(tr(ρ.data^2)) @doc raw""" tidyup(A::QuantumObject, tol::Real=1e-14) @@ -709,7 +666,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{KetQuantumObject}) +function get_coherence(ψ::QuantumObject{Ket}) a = destroy(prod(ψ.dimensions)) α = expect(a, ψ) D = exp(α * a' - conj(α) * a) @@ -717,7 +674,7 @@ function get_coherence(ψ::QuantumObject{KetQuantumObject}) return α, D' * ψ end -function get_coherence(ρ::QuantumObject{OperatorQuantumObject}) +function get_coherence(ρ::QuantumObject{Operator}) a = destroy(prod(ρ.dimensions)) α = expect(a, ρ) D = exp(α * a' - conj(α) * a) @@ -755,7 +712,7 @@ true function SparseArrays.permute( A::QuantumObject{ObjType}, order::Union{AbstractVector{Int},Tuple}, -) where {ObjType<:Union{KetQuantumObject,BraQuantumObject,OperatorQuantumObject}} +) where {ObjType<:Union{Ket,Bra,Operator}} (length(order) != length(A.dimensions)) && throw(ArgumentError("The order list must have the same length as the number of subsystems (A.dims)")) @@ -773,19 +730,15 @@ function SparseArrays.permute( return QuantumObject(reshape(permutedims(reshape(A.data, dims...), Tuple(perm)), size(A)), A.type, order_dimensions) end -_dims_and_perm( - ::ObjType, - dims::SVector{N,Int}, - order::AbstractVector{Int}, - L::Int, -) where {ObjType<:Union{KetQuantumObject,BraQuantumObject},N} = reverse(dims), reverse((L + 1) .- order) +_dims_and_perm(::ObjType, dims::SVector{N,Int}, order::AbstractVector{Int}, L::Int) where {ObjType<:Union{Ket,Bra},N} = + reverse(dims), reverse((L + 1) .- order) # if dims originates from Dimensions -_dims_and_perm(::OperatorQuantumObject, dims::SVector{N,Int}, order::AbstractVector{Int}, L::Int) where {N} = +_dims_and_perm(::Operator, 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 GeneralDimensions -_dims_and_perm(::OperatorQuantumObject, dims::SVector{2,SVector{N,Int}}, order::AbstractVector{Int}, L::Int) where {N} = +_dims_and_perm(::Operator, dims::SVector{2,SVector{N,Int}}, order::AbstractVector{Int}, L::Int) where {N} = reverse(vcat(dims[2], dims[1])), reverse((2 * L + 1) .- vcat(order, order .+ L)) _order_dimensions(dimensions::Dimensions, order::AbstractVector{Int}) = Dimensions(dimensions.to[order]) diff --git a/src/qobj/block_diagonal_form.jl b/src/qobj/block_diagonal_form.jl index 118189492..13367477b 100644 --- a/src/qobj/block_diagonal_form.jl +++ b/src/qobj/block_diagonal_form.jl @@ -50,9 +50,7 @@ Return the block-diagonal form of a [`QuantumObject`](@ref). This is very useful # Returns The [`BlockDiagonalForm`](@ref) of `A`. """ -function block_diagonal_form( - A::QuantumObject{OpType}, -) where {OpType<:Union{OperatorQuantumObject,SuperOperatorQuantumObject}} +function block_diagonal_form(A::QuantumObject{OpType}) where {OpType<:Union{Operator,SuperOperator}} bdf = block_diagonal_form(A.data) B = QuantumObject(bdf.B, type = A.type, dims = A.dimensions) P = QuantumObject(bdf.P, type = A.type, dims = A.dimensions) diff --git a/src/qobj/boolean_functions.jl b/src/qobj/boolean_functions.jl index 935d1b3d9..9a5cafa20 100644 --- a/src/qobj/boolean_functions.jl +++ b/src/qobj/boolean_functions.jl @@ -8,55 +8,55 @@ export isunitary @doc raw""" isbra(A) -Checks if the [`QuantumObject`](@ref) `A` is a [`BraQuantumObject`](@ref). Default case returns `false` for any other inputs. +Checks if the [`QuantumObject`](@ref) `A` is a [`Bra`](@ref). Default case returns `false` for any other inputs. """ isbra(A::QuantumObject) = isbra(typeof(A)) -isbra(::Type{<:QuantumObject{BraQuantumObject,N}}) where {N} = true +isbra(::Type{<:QuantumObject{Bra,N}}) where {N} = true isbra(A) = false # default case @doc raw""" isket(A) -Checks if the [`QuantumObject`](@ref) `A` is a [`KetQuantumObject`](@ref). Default case returns `false` for any other inputs. +Checks if the [`QuantumObject`](@ref) `A` is a [`Ket`](@ref). Default case returns `false` for any other inputs. """ isket(A::QuantumObject) = isket(typeof(A)) -isket(::Type{<:QuantumObject{KetQuantumObject,N}}) where {N} = true +isket(::Type{<:QuantumObject{Ket,N}}) where {N} = true isket(A) = false # default case @doc raw""" isoper(A) -Checks if the [`AbstractQuantumObject`](@ref) `A` is a [`OperatorQuantumObject`](@ref). Default case returns `false` for any other inputs. +Checks if the [`AbstractQuantumObject`](@ref) `A` is a [`Operator`](@ref). Default case returns `false` for any other inputs. """ isoper(A::AbstractQuantumObject) = isoper(typeof(A)) -isoper(::Type{<:AbstractQuantumObject{OperatorQuantumObject,N}}) where {N} = true +isoper(::Type{<:AbstractQuantumObject{Operator,N}}) where {N} = true isoper(A) = false # default case @doc raw""" isoperbra(A) -Checks if the [`QuantumObject`](@ref) `A` is a [`OperatorBraQuantumObject`](@ref). Default case returns `false` for any other inputs. +Checks if the [`QuantumObject`](@ref) `A` is a [`OperatorBra`](@ref). Default case returns `false` for any other inputs. """ isoperbra(A::QuantumObject) = isoperbra(typeof(A)) -isoperbra(::Type{<:QuantumObject{OperatorBraQuantumObject,N}}) where {N} = true +isoperbra(::Type{<:QuantumObject{OperatorBra,N}}) where {N} = true isoperbra(A) = false # default case @doc raw""" isoperket(A) -Checks if the [`QuantumObject`](@ref) `A` is a [`OperatorKetQuantumObject`](@ref). Default case returns `false` for any other inputs. +Checks if the [`QuantumObject`](@ref) `A` is a [`OperatorKet`](@ref). Default case returns `false` for any other inputs. """ isoperket(A::QuantumObject) = isoperket(typeof(A)) -isoperket(::Type{<:QuantumObject{OperatorKetQuantumObject,N}}) where {N} = true +isoperket(::Type{<:QuantumObject{OperatorKet,N}}) where {N} = true isoperket(A) = false # default case @doc raw""" issuper(A) -Checks if the [`AbstractQuantumObject`](@ref) `A` is a [`SuperOperatorQuantumObject`](@ref). Default case returns `false` for any other inputs. +Checks if the [`AbstractQuantumObject`](@ref) `A` is a [`SuperOperator`](@ref). Default case returns `false` for any other inputs. """ issuper(A::AbstractQuantumObject) = issuper(typeof(A)) -issuper(::Type{<:AbstractQuantumObject{SuperOperatorQuantumObject,N}}) where {N} = true +issuper(::Type{<:AbstractQuantumObject{<:SuperOperatorType,N}}) where {N} = true issuper(A) = false # default case @doc raw""" diff --git a/src/qobj/eigsolve.jl b/src/qobj/eigsolve.jl index 986a25321..1d8f256e3 100644 --- a/src/qobj/eigsolve.jl +++ b/src/qobj/eigsolve.jl @@ -27,7 +27,7 @@ A struct containing the eigenvalues, the eigenvectors, and some information from One can obtain the eigenvalues and the corresponding [`QuantumObject`](@ref)-type eigenvectors by: ```jldoctest julia> result = eigenstates(sigmax()) -EigsolveResult: type=Operator dims=[2] +EigsolveResult: type=Operator() dims=[2] values: 2-element Vector{ComplexF64}: -1.0 + 0.0im @@ -45,14 +45,14 @@ julia> λ 1.0 + 0.0im julia> ψ -2-element Vector{QuantumObject{KetQuantumObject, Dimensions{1, Tuple{Space}}, Vector{ComplexF64}}}: +2-element Vector{QuantumObject{Ket, Dimensions{1, Tuple{Space}}, Vector{ComplexF64}}}: -Quantum Object: type=Ket dims=[2] size=(2,) +Quantum Object: type=Ket() dims=[2] size=(2,) 2-element Vector{ComplexF64}: -0.7071067811865475 + 0.0im 0.7071067811865475 + 0.0im -Quantum Object: type=Ket dims=[2] size=(2,) +Quantum Object: type=Ket() dims=[2] size=(2,) 2-element Vector{ComplexF64}: 0.7071067811865475 + 0.0im 0.7071067811865475 + 0.0im @@ -66,7 +66,7 @@ julia> U struct EigsolveResult{ T1<:Vector{<:Number}, T2<:AbstractMatrix{<:Number}, - ObjType<:Union{Nothing,OperatorQuantumObject,SuperOperatorQuantumObject}, + ObjType<:Union{Nothing,Operator,SuperOperator}, DimType<:Union{Nothing,AbstractDimensions}, } values::T1 @@ -90,10 +90,10 @@ end 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.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.dimensions) for k in 1:length(res.values)], Val(:vectors)) +Base.iterate(res::EigsolveResult{T1,T2,Operator}, ::Val{:vector_list}) where {T1,T2} = + ([QuantumObject(res.vectors[:, k], Ket(), res.dimensions) for k in 1:length(res.values)], Val(:vectors)) +Base.iterate(res::EigsolveResult{T1,T2,SuperOperator}, ::Val{:vector_list}) where {T1,T2} = + ([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 @@ -170,7 +170,7 @@ function _eigsolve( m::Int = max(20, 2 * k + 1); tol::Real = 1e-8, maxiter::Int = 200, -) where {T<:BlasFloat,ObjType<:Union{Nothing,OperatorQuantumObject,SuperOperatorQuantumObject}} +) where {T<:BlasFloat,ObjType<:Union{Nothing,Operator,SuperOperator}} n = size(A, 2) V = similar(b, n, m + 1) H = zeros(T, m + 1, m) @@ -312,7 +312,7 @@ end function eigsolve( A; v0::Union{Nothing,AbstractVector} = nothing, - type::Union{Nothing,OperatorQuantumObject,SuperOperatorQuantumObject} = nothing, + type::Union{Nothing,Operator,SuperOperator} = nothing, dimensions = nothing, sigma::Union{Nothing,Real} = nothing, eigvals::Int = 1, @@ -404,11 +404,11 @@ function eigsolve_al( maxiter::Int = 200, eigstol::Real = 1e-6, kwargs..., -) where {HOpType<:Union{OperatorQuantumObject,SuperOperatorQuantumObject}} +) where {HOpType<:Union{Operator,SuperOperator}} L_evo = _mesolve_make_L_QobjEvo(H, c_ops) prob = mesolveProblem( L_evo, - QuantumObject(ρ0, type = Operator, dims = H.dimensions), + QuantumObject(ρ0, type = Operator(), dims = H.dimensions), [zero(T), T]; params = params, progress_bar = Val(false), @@ -447,7 +447,7 @@ julia> H = a + a'; julia> using LinearAlgebra; julia> E, ψ, U = eigen(H) -EigsolveResult: type=Operator dims=[5] +EigsolveResult: type=Operator() dims=[5] values: 5-element Vector{ComplexF64}: -2.8569700138728 + 0.0im @@ -467,10 +467,7 @@ julia> expect(H, ψ[1]) ≈ E[1] true ``` """ -function LinearAlgebra.eigen( - A::QuantumObject{OpType}; - kwargs..., -) where {OpType<:Union{OperatorQuantumObject,SuperOperatorQuantumObject}} +function LinearAlgebra.eigen(A::QuantumObject{OpType}; kwargs...) where {OpType<:Union{Operator,SuperOperator}} MT = typeof(A.data) F = eigen(to_dense(A.data); kwargs...) # This fixes a type inference issue. But doesn't work for GPU arrays @@ -485,10 +482,8 @@ end Same as [`eigen(A::QuantumObject; kwargs...)`](@ref) but for only the eigenvalues. """ -LinearAlgebra.eigvals( - A::QuantumObject{OpType}; - kwargs..., -) where {OpType<:Union{OperatorQuantumObject,SuperOperatorQuantumObject}} = eigvals(to_dense(A.data); kwargs...) +LinearAlgebra.eigvals(A::QuantumObject{OpType}; kwargs...) where {OpType<:Union{Operator,SuperOperator}} = + eigvals(to_dense(A.data); kwargs...) @doc raw""" eigenenergies(A::QuantumObject; sparse::Bool=false, kwargs...) @@ -507,7 +502,7 @@ function eigenenergies( A::QuantumObject{OpType}; sparse::Bool = false, kwargs..., -) where {OpType<:Union{OperatorQuantumObject,SuperOperatorQuantumObject}} +) where {OpType<:Union{Operator,SuperOperator}} if !sparse return eigvals(A; kwargs...) else @@ -532,7 +527,7 @@ function eigenstates( A::QuantumObject{OpType}; sparse::Bool = false, kwargs..., -) where {OpType<:Union{OperatorQuantumObject,SuperOperatorQuantumObject}} +) where {OpType<:Union{Operator,SuperOperator}} if !sparse return eigen(A; kwargs...) else diff --git a/src/qobj/functions.jl b/src/qobj/functions.jl index 694e6b05f..820c20c30 100644 --- a/src/qobj/functions.jl +++ b/src/qobj/functions.jl @@ -12,9 +12,9 @@ export vec2mat, mat2vec Transform the ket state ``\ket{\psi}`` into a pure density matrix ``\hat{\rho} = |\psi\rangle\langle\psi|``. """ -ket2dm(ψ::QuantumObject{KetQuantumObject}) = ψ * ψ' +ket2dm(ψ::QuantumObject{Ket}) = ψ * ψ' -ket2dm(ρ::QuantumObject{OperatorQuantumObject}) = ρ +ket2dm(ρ::QuantumObject{Operator}) = ρ @doc raw""" expect(O::Union{AbstractQuantumObject,Vector{AbstractQuantumObject}}, ψ::Union{QuantumObject,Vector{QuantumObject}}) @@ -52,34 +52,33 @@ julia> round.(expect([a' * a, a' + a, a], [ψ1, ψ2]), digits = 1) 0.0+0.0im 0.6+0.8im ``` """ -expect(O::AbstractQuantumObject{OperatorQuantumObject}, ψ::QuantumObject{KetQuantumObject}) = - dot(ψ.data, O.data, ψ.data) -expect(O::AbstractQuantumObject{OperatorQuantumObject}, ψ::QuantumObject{BraQuantumObject}) = expect(O, ψ') -expect(O::QuantumObject{OperatorQuantumObject}, ρ::QuantumObject{OperatorQuantumObject}) = tr(O * ρ) +expect(O::AbstractQuantumObject{Operator}, ψ::QuantumObject{Ket}) = dot(ψ.data, O.data, ψ.data) +expect(O::AbstractQuantumObject{Operator}, ψ::QuantumObject{Bra}) = expect(O, ψ') +expect(O::QuantumObject{Operator}, ρ::QuantumObject{Operator}) = tr(O * ρ) expect( - O::QuantumObject{OperatorQuantumObject,DimsType,<:Union{<:Hermitian{TF},<:Symmetric{TR}}}, - ψ::QuantumObject{KetQuantumObject}, + O::QuantumObject{Operator,DimsType,<:Union{<:Hermitian{TF},<:Symmetric{TR}}}, + ψ::QuantumObject{Ket}, ) where {DimsType<:AbstractDimensions,TF<:Number,TR<:Real} = real(dot(ψ.data, O.data, ψ.data)) expect( - O::QuantumObject{OperatorQuantumObject,DimsType,<:Union{<:Hermitian{TF},<:Symmetric{TR}}}, - ψ::QuantumObject{BraQuantumObject}, + O::QuantumObject{Operator,DimsType,<:Union{<:Hermitian{TF},<:Symmetric{TR}}}, + ψ::QuantumObject{Bra}, ) where {DimsType<:AbstractDimensions,TF<:Number,TR<:Real} = real(expect(O, ψ')) expect( - O::QuantumObject{OperatorQuantumObject,DimsType,<:Union{<:Hermitian{TF},<:Symmetric{TR}}}, - ρ::QuantumObject{OperatorQuantumObject}, + O::QuantumObject{Operator,DimsType,<:Union{<:Hermitian{TF},<:Symmetric{TR}}}, + ρ::QuantumObject{Operator}, ) where {DimsType<:AbstractDimensions,TF<:Number,TR<:Real} = real(tr(O * ρ)) expect( - O::AbstractVector{<:AbstractQuantumObject{OperatorQuantumObject,DimsType,<:Union{<:Hermitian{TF},<:Symmetric{TR}}}}, + O::AbstractVector{<:AbstractQuantumObject{Operator,DimsType,<:Union{<:Hermitian{TF},<:Symmetric{TR}}}}, ρ::QuantumObject, ) where {DimsType<:AbstractDimensions,TF<:Number,TR<:Real} = expect.(O, Ref(ρ)) -function expect(O::AbstractVector{<:AbstractQuantumObject{OperatorQuantumObject}}, ρ::QuantumObject) +function expect(O::AbstractVector{<:AbstractQuantumObject{Operator}}, ρ::QuantumObject) result = Vector{ComplexF64}(undef, length(O)) result .= expect.(O, Ref(ρ)) return result end -expect(O::AbstractQuantumObject{OperatorQuantumObject}, ρ::AbstractVector{<:QuantumObject}) = expect.(Ref(O), ρ) +expect(O::AbstractQuantumObject{Operator}, ρ::AbstractVector{<:QuantumObject}) = expect.(Ref(O), ρ) function expect( - O::AbstractVector{<:AbstractQuantumObject{OperatorQuantumObject,DimsType,<:Union{<:Hermitian{TF},<:Symmetric{TR}}}}, + O::AbstractVector{<:AbstractQuantumObject{Operator,DimsType,<:Union{<:Hermitian{TF},<:Symmetric{TR}}}}, ρ::AbstractVector{<:QuantumObject}, ) where {DimsType<:AbstractDimensions,TF<:Number,TR<:Real} N_ops = length(O) @@ -89,7 +88,7 @@ function expect( end return result end -function expect(O::AbstractVector{<:AbstractQuantumObject{OperatorQuantumObject}}, ρ::AbstractVector{<:QuantumObject}) +function expect(O::AbstractVector{<:AbstractQuantumObject{Operator}}, ρ::AbstractVector{<:QuantumObject}) N_ops = length(O) result = Matrix{ComplexF64}(undef, N_ops, length(ρ)) for i in 1:N_ops @@ -109,8 +108,8 @@ The function returns a real number if `O` is hermitian, and returns a complex nu Note that `ψ` can also be given as a list of [`QuantumObject`](@ref), it returns a list of expectation values. """ -variance(O::QuantumObject{OperatorQuantumObject}, ψ::QuantumObject) = expect(O^2, ψ) - expect(O, ψ)^2 -variance(O::QuantumObject{OperatorQuantumObject}, ψ::Vector{<:QuantumObject}) = expect(O^2, ψ) .- expect(O, ψ) .^ 2 +variance(O::QuantumObject{Operator}, ψ::QuantumObject) = expect(O^2, ψ) - expect(O, ψ)^2 +variance(O::QuantumObject{Operator}, ψ::Vector{<:QuantumObject}) = expect(O^2, ψ) .- expect(O, ψ) .^ 2 @doc raw""" to_dense(A::QuantumObject) @@ -170,7 +169,7 @@ Returns the [Kronecker product](https://en.wikipedia.org/wiki/Kronecker_product) ```jldoctest julia> a = destroy(20) -Quantum Object: type=Operator dims=[20] size=(20, 20) ishermitian=false +Quantum Object: type=Operator() dims=[20] size=(20, 20) ishermitian=false 20×20 SparseMatrixCSC{ComplexF64, Int64} with 19 stored entries: ⎡⠈⠢⡀⠀⠀⠀⠀⠀⠀⠀⎤ ⎢⠀⠀⠈⠢⡀⠀⠀⠀⠀⠀⎥ @@ -190,7 +189,7 @@ julia> a.dims, O.dims function Base.kron( A::AbstractQuantumObject{OpType,<:Dimensions}, B::AbstractQuantumObject{OpType,<:Dimensions}, -) where {OpType<:Union{KetQuantumObject,BraQuantumObject,OperatorQuantumObject}} +) where {OpType<:Union{Ket,Bra,Operator}} QType = promote_op_type(A, B) _lazy_tensor_warning(A.data, B.data) return QType(kron(A.data, B.data), A.type, Dimensions((A.dimensions.to..., B.dimensions.to...))) @@ -202,14 +201,14 @@ for ADimType in (:Dimensions, :GeneralDimensions) if !(ADimType == BDimType == :Dimensions) # not for this case because it's already implemented @eval begin function Base.kron( - A::AbstractQuantumObject{OperatorQuantumObject,<:$ADimType}, - B::AbstractQuantumObject{OperatorQuantumObject,<:$BDimType}, + A::AbstractQuantumObject{Operator,<:$ADimType}, + B::AbstractQuantumObject{Operator,<:$BDimType}, ) QType = promote_op_type(A, B) _lazy_tensor_warning(A.data, B.data) return QType( kron(A.data, B.data), - Operator, + Operator(), GeneralDimensions( (get_dimensions_to(A)..., get_dimensions_to(B)...), (get_dimensions_from(A)..., get_dimensions_from(B)...), @@ -222,8 +221,8 @@ for ADimType in (:Dimensions, :GeneralDimensions) end # if A and B are different type (must return Operator with GeneralDimensions) -for AOpType in (:KetQuantumObject, :BraQuantumObject, :OperatorQuantumObject) - for BOpType in (:KetQuantumObject, :BraQuantumObject, :OperatorQuantumObject) +for AOpType in (:Ket, :Bra, :Operator) + for BOpType in (:Ket, :Bra, :Operator) if (AOpType != BOpType) @eval begin function Base.kron(A::AbstractQuantumObject{$AOpType}, B::AbstractQuantumObject{$BOpType}) @@ -231,7 +230,7 @@ for AOpType in (:KetQuantumObject, :BraQuantumObject, :OperatorQuantumObject) _lazy_tensor_warning(A.data, B.data) return QType( kron(A.data, B.data), - Operator, + Operator(), GeneralDimensions( (get_dimensions_to(A)..., get_dimensions_to(B)...), (get_dimensions_from(A)..., get_dimensions_from(B)...), @@ -263,23 +262,23 @@ end vec2mat(A::QuantumObject) vector_to_operator(A::QuantumObject) -Convert a quantum object from vector ([`OperatorKetQuantumObject`](@ref)-type) to matrix ([`OperatorQuantumObject`](@ref)-type) +Convert a quantum object from vector ([`OperatorKet`](@ref)-type) to matrix ([`Operator`](@ref)-type) !!! note `vector_to_operator` is a synonym of `vec2mat`. """ -vec2mat(A::QuantumObject{OperatorKetQuantumObject}) = QuantumObject(vec2mat(A.data), Operator, A.dimensions) +vec2mat(A::QuantumObject{OperatorKet}) = QuantumObject(vec2mat(A.data), Operator(), A.dimensions) @doc raw""" mat2vec(A::QuantumObject) operator_to_vector(A::QuantumObject) -Convert a quantum object from matrix ([`OperatorQuantumObject`](@ref)-type) to vector ([`OperatorKetQuantumObject`](@ref)-type) +Convert a quantum object from matrix ([`Operator`](@ref)-type) to vector ([`OperatorKet`](@ref)-type) !!! note `operator_to_vector` is a synonym of `mat2vec`. """ -mat2vec(A::QuantumObject{OperatorQuantumObject}) = QuantumObject(mat2vec(A.data), OperatorKet, A.dimensions) +mat2vec(A::QuantumObject{Operator}) = QuantumObject(mat2vec(A.data), OperatorKet(), A.dimensions) @doc raw""" mat2vec(A::AbstractMatrix) diff --git a/src/qobj/operators.jl b/src/qobj/operators.jl index d7c460fdb..fb0c2041f 100644 --- a/src/qobj/operators.jl +++ b/src/qobj/operators.jl @@ -48,7 +48,7 @@ function rand_unitary(dimensions::Union{Dimensions,AbstractVector{Int},Tuple}, : # Because inv(Λ) ⋅ R has real and strictly positive elements, Q · Λ is therefore Haar distributed. Λ = diag(R) # take the diagonal elements of R Λ ./= abs.(Λ) # rescaling the elements - return QuantumObject(to_dense(Q * Diagonal(Λ)); type = Operator, dims = dimensions) + return QuantumObject(to_dense(Q * Diagonal(Λ)); type = Operator(), dims = dimensions) end function rand_unitary(dimensions::Union{Dimensions,AbstractVector{Int},Tuple}, ::Val{:exp}) N = prod(dimensions) @@ -57,7 +57,7 @@ function rand_unitary(dimensions::Union{Dimensions,AbstractVector{Int},Tuple}, : Z = randn(ComplexF64, N, N) # generate Hermitian matrix - H = QuantumObject((Z + Z') / 2; type = Operator, dims = dimensions) + H = QuantumObject((Z + Z') / 2; type = Operator(), dims = dimensions) return to_dense(exp(-1.0im * H)) end @@ -73,8 +73,7 @@ Return the commutator (or `anti`-commutator) of the two [`QuantumObject`](@ref): Note that `A` and `B` must be [`Operator`](@ref) """ -commutator(A::QuantumObject{OperatorQuantumObject}, B::QuantumObject{OperatorQuantumObject}; anti::Bool = false) = - A * B - (-1)^anti * B * A +commutator(A::QuantumObject{Operator}, B::QuantumObject{Operator}; anti::Bool = false) = A * B - (-1)^anti * B * A @doc raw""" destroy(N::Int) @@ -88,7 +87,7 @@ This operator acts on a fock state as ``\hat{a} \ket{n} = \sqrt{n} \ket{n-1}``. ```jldoctest julia> a = destroy(20) -Quantum Object: type=Operator dims=[20] size=(20, 20) ishermitian=false +Quantum Object: type=Operator() dims=[20] size=(20, 20) ishermitian=false 20×20 SparseMatrixCSC{ComplexF64, Int64} with 19 stored entries: ⎡⠈⠢⡀⠀⠀⠀⠀⠀⠀⠀⎤ ⎢⠀⠀⠈⠢⡀⠀⠀⠀⠀⠀⎥ @@ -100,7 +99,7 @@ julia> fock(20, 3)' * a * fock(20, 4) 2.0 + 0.0im ``` """ -destroy(N::Int) = QuantumObject(spdiagm(1 => Array{ComplexF64}(sqrt.(1:(N-1)))), Operator, N) +destroy(N::Int) = QuantumObject(spdiagm(1 => Array{ComplexF64}(sqrt.(1:(N-1)))), Operator(), N) @doc raw""" create(N::Int) @@ -114,7 +113,7 @@ This operator acts on a fock state as ``\hat{a}^\dagger \ket{n} = \sqrt{n+1} \ke ```jldoctest julia> a_d = create(20) -Quantum Object: type=Operator dims=[20] size=(20, 20) ishermitian=false +Quantum Object: type=Operator() dims=[20] size=(20, 20) ishermitian=false 20×20 SparseMatrixCSC{ComplexF64, Int64} with 19 stored entries: ⎡⠢⡀⠀⠀⠀⠀⠀⠀⠀⠀⎤ ⎢⠀⠈⠢⡀⠀⠀⠀⠀⠀⠀⎥ @@ -126,7 +125,7 @@ julia> fock(20, 4)' * a_d * fock(20, 3) 2.0 + 0.0im ``` """ -create(N::Int) = QuantumObject(spdiagm(-1 => Array{ComplexF64}(sqrt.(1:(N-1)))), Operator, N) +create(N::Int) = QuantumObject(spdiagm(-1 => Array{ComplexF64}(sqrt.(1:(N-1)))), Operator(), N) @doc raw""" displace(N::Int, α::Number) @@ -167,7 +166,7 @@ Bosonic number operator with Hilbert space cutoff `N`. This operator is defined as ``\hat{N}=\hat{a}^\dagger \hat{a}``, where ``\hat{a}`` is the bosonic annihilation operator. """ -num(N::Int) = QuantumObject(spdiagm(0 => Array{ComplexF64}(0:(N-1))), Operator, N) +num(N::Int) = QuantumObject(spdiagm(0 => Array{ComplexF64}(0:(N-1))), Operator(), N) @doc raw""" position(N::Int) @@ -223,7 +222,7 @@ function phase(N::Int, ϕ0::Real = 0) N_list = collect(0:(N-1)) ϕ = ϕ0 .+ (2 * π / N) .* N_list states = [exp.((1.0im * ϕ[m]) .* N_list) ./ sqrt(N) for m in 1:N] - return QuantumObject(sum([ϕ[m] * states[m] * states[m]' for m in 1:N]); type = Operator, dims = N) + return QuantumObject(sum([ϕ[m] * states[m] * states[m]' for m in 1:N]); type = Operator(), dims = N) end @doc raw""" @@ -244,21 +243,21 @@ Note that if the parameter `which` is not specified, returns a set of Spin-`j` o ```jldoctest julia> jmat(0.5, :x) -Quantum Object: type=Operator dims=[2] size=(2, 2) ishermitian=true +Quantum Object: type=Operator() dims=[2] size=(2, 2) ishermitian=true 2×2 SparseMatrixCSC{ComplexF64, Int64} with 2 stored entries: ⋅ 0.5+0.0im 0.5+0.0im ⋅ julia> jmat(0.5, Val(:-)) -Quantum Object: type=Operator dims=[2] size=(2, 2) ishermitian=false +Quantum Object: type=Operator() dims=[2] size=(2, 2) ishermitian=false 2×2 SparseMatrixCSC{ComplexF64, Int64} with 1 stored entry: ⋅ ⋅ 1.0+0.0im ⋅ julia> jmat(1.5, Val(:z)) -Quantum Object: type=Operator dims=[4] size=(4, 4) ishermitian=true +Quantum Object: type=Operator() dims=[4] size=(4, 4) ishermitian=true 4×4 SparseMatrixCSC{ComplexF64, Int64} with 4 stored entries: 1.5+0.0im ⋅ ⋅ ⋅ ⋅ 0.5+0.0im ⋅ ⋅ @@ -277,7 +276,7 @@ function jmat(j::Real, ::Val{:x}) throw(ArgumentError("The spin quantum number (j) must be a non-negative integer or half-integer.")) σ = _jm(j) - return QuantumObject((σ' + σ) / 2, Operator, Int(J)) + return QuantumObject((σ' + σ) / 2, Operator(), Int(J)) end function jmat(j::Real, ::Val{:y}) J = 2 * j + 1 @@ -285,28 +284,28 @@ function jmat(j::Real, ::Val{:y}) throw(ArgumentError("The spin quantum number (j) must be a non-negative integer or half-integer.")) σ = _jm(j) - return QuantumObject((σ' - σ) / 2im, Operator, Int(J)) + return QuantumObject((σ' - σ) / 2im, Operator(), Int(J)) end function jmat(j::Real, ::Val{:z}) J = 2 * j + 1 ((floor(J) != J) || (j < 0)) && throw(ArgumentError("The spin quantum number (j) must be a non-negative integer or half-integer.")) - return QuantumObject(_jz(j), Operator, Int(J)) + return QuantumObject(_jz(j), Operator(), Int(J)) end function jmat(j::Real, ::Val{:+}) J = 2 * j + 1 ((floor(J) != J) || (j < 0)) && throw(ArgumentError("The spin quantum number (j) must be a non-negative integer or half-integer.")) - return QuantumObject(adjoint(_jm(j)), Operator, Int(J)) + return QuantumObject(adjoint(_jm(j)), Operator(), Int(J)) end function jmat(j::Real, ::Val{:-}) J = 2 * j + 1 ((floor(J) != J) || (j < 0)) && throw(ArgumentError("The spin quantum number (j) must be a non-negative integer or half-integer.")) - return QuantumObject(_jm(j), Operator, Int(J)) + return QuantumObject(_jm(j), Operator(), Int(J)) end jmat(j::Real, ::Val{T}) where {T} = throw(ArgumentError("Invalid spin operator: $(T)")) @@ -429,13 +428,9 @@ Note that `type` can only be either [`Operator`](@ref) or [`SuperOperator`](@ref !!! note `qeye` is a synonym of `eye`. """ -function eye( - N::Int; - type::ObjType = Operator, - dims = nothing, -) where {ObjType<:Union{OperatorQuantumObject,SuperOperatorQuantumObject}} +function eye(N::Int; type = Operator(), dims = nothing) if dims isa Nothing - dims = isa(type, OperatorQuantumObject) ? N : isqrt(N) + dims = isa(type, Operator) ? N : isqrt(N) end return QuantumObject(Diagonal(ones(ComplexF64, N)); type = type, dims = dims) end @@ -478,9 +473,9 @@ Note that we put ``\hat{\sigma}_{-} = \begin{pmatrix} 0 & 0 \\ 1 & 0 \end{pmatri """ fcreate(N::Union{Int,Val}, j::Int) = _Jordan_Wigner(N, j, sigmam()) -_Jordan_Wigner(N::Int, j::Int, op::QuantumObject{OperatorQuantumObject}) = _Jordan_Wigner(Val(N), j, op) +_Jordan_Wigner(N::Int, j::Int, op::QuantumObject{Operator}) = _Jordan_Wigner(Val(N), j, op) -function _Jordan_Wigner(::Val{N}, j::Int, op::QuantumObject{OperatorQuantumObject}) where {N} +function _Jordan_Wigner(::Val{N}, j::Int, op::QuantumObject{Operator}) where {N} (N < 1) && throw(ArgumentError("The total number of sites (N) cannot be less than 1")) ((j > N) || (j < 1)) && throw(ArgumentError("The site index (j) should satisfy: 1 ≤ j ≤ N")) @@ -490,7 +485,7 @@ function _Jordan_Wigner(::Val{N}, j::Int, op::QuantumObject{OperatorQuantumObjec S = 2^(N - j) I_tensor = sparse((1.0 + 0.0im) * LinearAlgebra.I, S, S) - return QuantumObject(kron(Z_tensor, op.data, I_tensor); type = Operator, dims = ntuple(i -> 2, Val(N))) + return QuantumObject(kron(Z_tensor, op.data, I_tensor); type = Operator(), dims = ntuple(i -> 2, Val(N))) end @doc raw""" @@ -499,7 +494,7 @@ end Generates the projection operator ``\hat{O} = |i \rangle\langle j|`` with Hilbert space dimension `N`. """ projection(N::Int, i::Int, j::Int) = - QuantumObject(sparse([i + 1], [j + 1], [1.0 + 0.0im], N, N), type = Operator, dims = N) + QuantumObject(sparse([i + 1], [j + 1], [1.0 + 0.0im], N, N), type = Operator(), dims = N) @doc raw""" tunneling(N::Int, m::Int=1; sparse::Union{Bool,Val{<:Bool}}=Val(false)) @@ -522,9 +517,9 @@ function tunneling(N::Int, m::Int = 1; sparse::Union{Bool,Val} = Val(false)) data = ones(ComplexF64, N - m) if getVal(sparse) - return QuantumObject(spdiagm(m => data, -m => data); type = Operator, dims = N) + return QuantumObject(spdiagm(m => data, -m => data); type = Operator(), dims = N) else - return QuantumObject(diagm(m => data, -m => data); type = Operator, dims = N) + return QuantumObject(diagm(m => data, -m => data); type = Operator(), dims = N) end end @@ -555,9 +550,9 @@ where ``\omega = \exp(\frac{2 \pi i}{N})``. !!! warning "Beware of type-stability!" It is highly recommended to use `qft(dimensions)` with `dimensions` as `Tuple` or `SVector` from [StaticArrays.jl](https://github.com/JuliaArrays/StaticArrays.jl) to keep type stability. See the [related Section](@ref doc:Type-Stability) about type stability for more details. """ -qft(dimensions::Int) = QuantumObject(_qft_op(dimensions), Operator, dimensions) +qft(dimensions::Int) = QuantumObject(_qft_op(dimensions), Operator(), dimensions) qft(dimensions::Union{Dimensions,AbstractVector{Int},Tuple}) = - QuantumObject(_qft_op(prod(dimensions)), Operator, dimensions) + QuantumObject(_qft_op(prod(dimensions)), Operator(), dimensions) function _qft_op(N::Int) ω = exp(2.0im * π / N) arr = 0:(N-1) diff --git a/src/qobj/quantum_object.jl b/src/qobj/quantum_object.jl index 763113776..053b9b050 100644 --- a/src/qobj/quantum_object.jl +++ b/src/qobj/quantum_object.jl @@ -25,7 +25,7 @@ Julia structure representing any time-independent quantum objects. For time-depe ```jldoctest julia> a = destroy(20) -Quantum Object: type=Operator dims=[20] size=(20, 20) ishermitian=false +Quantum Object: type=Operator() dims=[20] size=(20, 20) ishermitian=false 20×20 SparseMatrixCSC{ComplexF64, Int64} with 19 stored entries: ⎡⠈⠢⡀⠀⠀⠀⠀⠀⠀⠀⎤ ⎢⠀⠀⠈⠢⡀⠀⠀⠀⠀⠀⎥ @@ -50,9 +50,11 @@ struct QuantumObject{ObjType<:QuantumObjectType,DimType<:AbstractDimensions,Data type::ObjType dimensions::DimType - function QuantumObject(data::DT, type::ObjType, dims) where {DT<:AbstractArray,ObjType<:QuantumObjectType} + function QuantumObject(data::DT, type, dims) where {DT<:AbstractArray} dimensions = _gen_dimensions(dims) + ObjType = _check_type(type) + _size = _get_size(data) _check_QuantumObject(type, dimensions, _size[1], _size[2]) @@ -69,31 +71,29 @@ Generate [`QuantumObject`](@ref) with a given `A::AbstractArray` and specified ` !!! note `Qobj` is a synonym of `QuantumObject`. """ -function QuantumObject( - A::AbstractMatrix{T}; - type::ObjType = nothing, - dims = nothing, -) where {T,ObjType<:Union{Nothing,QuantumObjectType}} +function QuantumObject(A::AbstractMatrix{T}; type = nothing, dims = nothing) where {T} _size = _get_size(A) + _check_type(type) + if type isa Nothing - type = (_size[1] == 1 && _size[2] > 1) ? Bra : Operator # default type - elseif type != Operator && type != SuperOperator && type != Bra && type != OperatorBra + type = (_size[1] == 1 && _size[2] > 1) ? Bra() : Operator() # default type + elseif !(type isa Operator) && !(type isa SuperOperator) && !(type isa Bra) && !(type isa OperatorBra) throw( ArgumentError( - "The argument type must be Operator, SuperOperator, Bra or OperatorBra if the input array is a matrix.", + "The argument type must be Operator(), SuperOperator(), Bra() or OperatorBra() if the input array is a matrix.", ), ) end if dims isa Nothing - if type isa BraQuantumObject + if type isa Bra dims = Dimensions(_size[2]) - elseif type isa OperatorQuantumObject + elseif type isa Operator dims = (_size[1] == _size[2]) ? Dimensions(_size[1]) : GeneralDimensions(SVector{2}(SVector{1}(_size[1]), SVector{1}(_size[2]))) - elseif type isa SuperOperatorQuantumObject || type isa OperatorBraQuantumObject + elseif type isa SuperOperator || type isa OperatorBra dims = Dimensions(isqrt(_size[2])) end end @@ -101,22 +101,19 @@ function QuantumObject( return QuantumObject(A, type, dims) end -function QuantumObject( - A::AbstractVector{T}; - type::ObjType = nothing, - dims = nothing, -) where {T,ObjType<:Union{Nothing,QuantumObjectType}} +function QuantumObject(A::AbstractVector{T}; type = nothing, dims = nothing) where {T} + _check_type(type) if type isa Nothing - type = Ket # default type - elseif type != Ket && type != OperatorKet - throw(ArgumentError("The argument type must be Ket or OperatorKet if the input array is a vector.")) + type = Ket() # default type + elseif !(type isa Ket) && !(type isa OperatorKet) + throw(ArgumentError("The argument type must be Ket() or OperatorKet() if the input array is a vector.")) end if dims isa Nothing _size = _get_size(A) - if type isa KetQuantumObject + if type isa Ket dims = Dimensions(_size[1]) - elseif type isa OperatorKetQuantumObject + elseif type isa OperatorKet dims = Dimensions(isqrt(_size[1])) end end @@ -124,17 +121,14 @@ function QuantumObject( return QuantumObject(A, type, dims) end -function QuantumObject( - A::AbstractArray{T,N}; - type::ObjType = nothing, - dims = nothing, -) where {T,N,ObjType<:Union{Nothing,QuantumObjectType}} +function QuantumObject(A::AbstractArray{T,N}; type = nothing, dims = nothing) where {T,N} throw(DomainError(size(A), "The size of the array is not compatible with vector or matrix.")) end -function QuantumObject(A::QuantumObject; type::ObjType = A.type, dims = A.dimensions) where {ObjType<:QuantumObjectType} +function QuantumObject(A::QuantumObject; type = A.type, dims = A.dimensions) _size = _get_size(A.data) dimensions = _gen_dimensions(dims) + _check_type(type) _check_QuantumObject(type, dimensions, _size[1], _size[2]) return QuantumObject(copy(A.data), type, dimensions) end @@ -142,15 +136,7 @@ end function Base.show( io::IO, QO::QuantumObject{OpType}, -) where { - OpType<:Union{ - BraQuantumObject, - KetQuantumObject, - OperatorBraQuantumObject, - OperatorKetQuantumObject, - SuperOperatorQuantumObject, - }, -} +) where {OpType<:Union{Bra,Ket,OperatorBra,OperatorKet,SuperOperator}} op_data = QO.data println( io, @@ -204,16 +190,13 @@ Here, `u` can be in either the following types: SciMLOperators.cache_operator( L::AbstractQuantumObject{OpType}, u::AbstractVector, -) where {OpType<:Union{OperatorQuantumObject,SuperOperatorQuantumObject}} = +) where {OpType<:Union{Operator,SuperOperator}} = get_typename_wrapper(L)(cache_operator(L.data, to_dense(similar(u))), L.type, L.dimensions) function SciMLOperators.cache_operator( L::AbstractQuantumObject{OpType}, u::QuantumObject{SType}, -) where { - OpType<:Union{OperatorQuantumObject,SuperOperatorQuantumObject}, - SType<:Union{KetQuantumObject,OperatorKetQuantumObject}, -} +) where {OpType<:Union{Operator,SuperOperator},SType<:Union{Ket,OperatorKet}} check_dimensions(L, u) if isoper(L) && isoperket(u) diff --git a/src/qobj/quantum_object_base.jl b/src/qobj/quantum_object_base.jl index b43dd06d4..08c3de46b 100644 --- a/src/qobj/quantum_object_base.jl +++ b/src/qobj/quantum_object_base.jl @@ -4,14 +4,7 @@ This file defines the AbstractQuantumObject structure, all the type structures f =# export AbstractQuantumObject -export QuantumObjectType, - BraQuantumObject, - KetQuantumObject, - OperatorQuantumObject, - OperatorBraQuantumObject, - OperatorKetQuantumObject, - SuperOperatorQuantumObject -export Bra, Ket, Operator, OperatorBra, OperatorKet, SuperOperator +export QuantumObjectType, SuperOperatorType, Bra, Ket, Operator, OperatorBra, OperatorKet, SuperOperator @doc raw""" abstract type AbstractQuantumObject{ObjType,DimType,DataType} @@ -28,95 +21,49 @@ abstract type AbstractQuantumObject{ObjType,DimType,DataType} end abstract type QuantumObjectType end -@doc raw""" - BraQuantumObject <: QuantumObjectType - -Constructor representing a bra state ``\langle\psi|``. -""" -struct BraQuantumObject <: QuantumObjectType end -Base.show(io::IO, ::BraQuantumObject) = print(io, "Bra") +abstract type SuperOperatorType <: QuantumObjectType end @doc raw""" - const Bra = BraQuantumObject() + Bra <: QuantumObjectType -A constant representing the type of [`BraQuantumObject`](@ref): a bra state ``\langle\psi|`` +Constructor representing a bra state ``\langle\psi|``. """ -const Bra = BraQuantumObject() +struct Bra <: QuantumObjectType end @doc raw""" - KetQuantumObject <: QuantumObjectType + Ket <: QuantumObjectType Constructor representing a ket state ``|\psi\rangle``. """ -struct KetQuantumObject <: QuantumObjectType end -Base.show(io::IO, ::KetQuantumObject) = print(io, "Ket") +struct Ket <: QuantumObjectType end @doc raw""" - const Ket = KetQuantumObject() - -A constant representing the type of [`KetQuantumObject`](@ref): a ket state ``|\psi\rangle`` -""" -const Ket = KetQuantumObject() - -@doc raw""" - OperatorQuantumObject <: QuantumObjectType + Operator <: QuantumObjectType Constructor representing an operator ``\hat{O}``. """ -struct OperatorQuantumObject <: QuantumObjectType end -Base.show(io::IO, ::OperatorQuantumObject) = print(io, "Operator") - -@doc raw""" - const Operator = OperatorQuantumObject() - -A constant representing the type of [`OperatorQuantumObject`](@ref): an operator ``\hat{O}`` -""" -const Operator = OperatorQuantumObject() +struct Operator <: QuantumObjectType end @doc raw""" - SuperOperatorQuantumObject <: QuantumObjectType + SuperOperator <: SuperOperatorType Constructor representing a super-operator ``\hat{\mathcal{O}}`` acting on vectorized density operator matrices. """ -struct SuperOperatorQuantumObject <: QuantumObjectType end -Base.show(io::IO, ::SuperOperatorQuantumObject) = print(io, "SuperOperator") +struct SuperOperator <: SuperOperatorType end @doc raw""" - const SuperOperator = SuperOperatorQuantumObject() - -A constant representing the type of [`SuperOperatorQuantumObject`](@ref): a super-operator ``\hat{\mathcal{O}}`` acting on vectorized density operator matrices -""" -const SuperOperator = SuperOperatorQuantumObject() - -@doc raw""" - OperatorBraQuantumObject <: QuantumObjectType + OperatorBra <: QuantumObjectType Constructor representing a bra state in the [`SuperOperator`](@ref) formalism ``\langle\langle\rho|``. """ -struct OperatorBraQuantumObject <: QuantumObjectType end -Base.show(io::IO, ::OperatorBraQuantumObject) = print(io, "OperatorBra") +struct OperatorBra <: QuantumObjectType end @doc raw""" - const OperatorBra = OperatorBraQuantumObject() - -A constant representing the type of [`OperatorBraQuantumObject`](@ref): a bra state in the [`SuperOperator`](@ref) formalism ``\langle\langle\rho|``. -""" -const OperatorBra = OperatorBraQuantumObject() - -@doc raw""" - OperatorKetQuantumObject <: QuantumObjectType + OperatorKet <: QuantumObjectType Constructor representing a ket state in the [`SuperOperator`](@ref) formalism ``|\rho\rangle\rangle``. """ -struct OperatorKetQuantumObject <: QuantumObjectType end -Base.show(io::IO, ::OperatorKetQuantumObject) = print(io, "OperatorKet") - -@doc raw""" - const OperatorKet = OperatorKetQuantumObject() - -A constant representing the type of [`OperatorKetQuantumObject`](@ref): a ket state in the [`SuperOperator`](@ref) formalism ``|\rho\rangle\rangle`` -""" -const OperatorKet = OperatorKetQuantumObject() +struct OperatorKet <: QuantumObjectType end @doc raw""" size(A::AbstractQuantumObject) @@ -172,22 +119,14 @@ _check_QuantumObject( dimensions::GeneralDimensions, m::Int, n::Int, -) where { - ObjType<:Union{ - KetQuantumObject, - BraQuantumObject, - SuperOperatorQuantumObject, - OperatorBraQuantumObject, - OperatorKetQuantumObject, - }, -} = throw( +) where {ObjType<:Union{Ket,Bra,SuperOperator,OperatorBra,OperatorKet}} = throw( DomainError( _get_dims_string(dimensions), "The given `dims` is not compatible with type = $type, should be a single list of integers.", ), ) -function _check_QuantumObject(type::KetQuantumObject, dimensions::Dimensions, m::Int, n::Int) +function _check_QuantumObject(type::Ket, dimensions::Dimensions, m::Int, n::Int) (n != 1) && throw(DomainError((m, n), "The size of the array is not compatible with Ket")) (prod(dimensions) != m) && throw( DimensionMismatch("Ket with dims = $(_get_dims_string(dimensions)) does not fit the array size = $((m, n))."), @@ -195,7 +134,7 @@ function _check_QuantumObject(type::KetQuantumObject, dimensions::Dimensions, m: return nothing end -function _check_QuantumObject(type::BraQuantumObject, dimensions::Dimensions, m::Int, n::Int) +function _check_QuantumObject(type::Bra, dimensions::Dimensions, m::Int, n::Int) (m != 1) && throw(DomainError((m, n), "The size of the array is not compatible with Bra")) (prod(dimensions) != n) && throw( DimensionMismatch("Bra with dims = $(_get_dims_string(dimensions)) does not fit the array size = $((m, n))."), @@ -203,7 +142,7 @@ function _check_QuantumObject(type::BraQuantumObject, dimensions::Dimensions, m: return nothing end -function _check_QuantumObject(type::OperatorQuantumObject, dimensions::Dimensions, m::Int, n::Int) +function _check_QuantumObject(type::Operator, dimensions::Dimensions, m::Int, n::Int) L = prod(dimensions) (L == m == n) || throw( DimensionMismatch( @@ -213,7 +152,7 @@ function _check_QuantumObject(type::OperatorQuantumObject, dimensions::Dimension return nothing end -function _check_QuantumObject(type::OperatorQuantumObject, dimensions::GeneralDimensions, m::Int, n::Int) +function _check_QuantumObject(type::Operator, dimensions::GeneralDimensions, m::Int, n::Int) ((m == 1) || (n == 1)) && throw(DomainError((m, n), "The size of the array is not compatible with Operator")) ((prod(dimensions.to) != m) || (prod(dimensions.from) != n)) && throw( DimensionMismatch( @@ -223,7 +162,7 @@ function _check_QuantumObject(type::OperatorQuantumObject, dimensions::GeneralDi return nothing end -function _check_QuantumObject(type::SuperOperatorQuantumObject, dimensions::Dimensions, m::Int, n::Int) +function _check_QuantumObject(type::SuperOperator, dimensions::Dimensions, m::Int, n::Int) (m != n) && throw(DomainError((m, n), "The size of the array is not compatible with SuperOperator")) (prod(dimensions) != sqrt(m)) && throw( DimensionMismatch( @@ -233,7 +172,7 @@ function _check_QuantumObject(type::SuperOperatorQuantumObject, dimensions::Dime return nothing end -function _check_QuantumObject(type::OperatorKetQuantumObject, dimensions::Dimensions, m::Int, n::Int) +function _check_QuantumObject(type::OperatorKet, dimensions::Dimensions, m::Int, n::Int) (n != 1) && throw(DomainError((m, n), "The size of the array is not compatible with OperatorKet")) (prod(dimensions) != sqrt(m)) && throw( DimensionMismatch( @@ -243,7 +182,7 @@ function _check_QuantumObject(type::OperatorKetQuantumObject, dimensions::Dimens return nothing end -function _check_QuantumObject(type::OperatorBraQuantumObject, dimensions::Dimensions, m::Int, n::Int) +function _check_QuantumObject(type::OperatorBra, dimensions::Dimensions, m::Int, n::Int) (m != 1) && throw(DomainError((m, n), "The size of the array is not compatible with OperatorBra")) (prod(dimensions) != sqrt(n)) && throw( DimensionMismatch( @@ -253,6 +192,11 @@ function _check_QuantumObject(type::OperatorBraQuantumObject, dimensions::Dimens return nothing end +_check_type(::T) where {T<:Union{Nothing,<:QuantumObjectType}} = T +_check_type(::Type{T}) where {T} = + throw(ArgumentError("The argument `$T` is not valid. You may probably want to use `$T()` instead.")) +_check_type(t) = throw(ArgumentError("The argument $t is not valid. It should be a subtype of `QuantumObjectType`.")) + function Base.getproperty(A::AbstractQuantumObject, key::Symbol) # a comment here to avoid bad render by JuliaFormatter if key === :dims @@ -263,22 +207,22 @@ function Base.getproperty(A::AbstractQuantumObject, key::Symbol) end # this returns `to` in GeneralDimensions representation -get_dimensions_to(A::AbstractQuantumObject{KetQuantumObject,<:Dimensions}) = A.dimensions.to -get_dimensions_to(A::AbstractQuantumObject{BraQuantumObject,<:Dimensions{N}}) where {N} = space_one_list(N) -get_dimensions_to(A::AbstractQuantumObject{OperatorQuantumObject,<:Dimensions}) = A.dimensions.to -get_dimensions_to(A::AbstractQuantumObject{OperatorQuantumObject,<:GeneralDimensions}) = A.dimensions.to +get_dimensions_to(A::AbstractQuantumObject{Ket,<:Dimensions}) = A.dimensions.to +get_dimensions_to(A::AbstractQuantumObject{Bra,<:Dimensions{N}}) where {N} = space_one_list(N) +get_dimensions_to(A::AbstractQuantumObject{Operator,<:Dimensions}) = A.dimensions.to +get_dimensions_to(A::AbstractQuantumObject{Operator,<:GeneralDimensions}) = A.dimensions.to get_dimensions_to( A::AbstractQuantumObject{ObjType,<:Dimensions}, -) where {ObjType<:Union{SuperOperatorQuantumObject,OperatorBraQuantumObject,OperatorKetQuantumObject}} = A.dimensions.to +) where {ObjType<:Union{SuperOperator,OperatorBra,OperatorKet}} = A.dimensions.to # this returns `from` in GeneralDimensions representation -get_dimensions_from(A::AbstractQuantumObject{KetQuantumObject,<:Dimensions{N}}) where {N} = space_one_list(N) -get_dimensions_from(A::AbstractQuantumObject{BraQuantumObject,<:Dimensions}) = A.dimensions.to -get_dimensions_from(A::AbstractQuantumObject{OperatorQuantumObject,<:Dimensions}) = A.dimensions.to -get_dimensions_from(A::AbstractQuantumObject{OperatorQuantumObject,<:GeneralDimensions}) = A.dimensions.from +get_dimensions_from(A::AbstractQuantumObject{Ket,<:Dimensions{N}}) where {N} = space_one_list(N) +get_dimensions_from(A::AbstractQuantumObject{Bra,<:Dimensions}) = A.dimensions.to +get_dimensions_from(A::AbstractQuantumObject{Operator,<:Dimensions}) = A.dimensions.to +get_dimensions_from(A::AbstractQuantumObject{Operator,<:GeneralDimensions}) = A.dimensions.from get_dimensions_from( A::AbstractQuantumObject{ObjType,<:Dimensions}, -) where {ObjType<:Union{SuperOperatorQuantumObject,OperatorBraQuantumObject,OperatorKetQuantumObject}} = A.dimensions.to +) where {ObjType<:Union{SuperOperator,OperatorBra,OperatorKet}} = 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 62b15b0b5..3e8edf301 100644 --- a/src/qobj/quantum_object_evo.jl +++ b/src/qobj/quantum_object_evo.jl @@ -29,7 +29,7 @@ This operator can be initialized in the same way as the QuTiP `QobjEvo` object. ```jldoctest qobjevo julia> a = tensor(destroy(10), qeye(2)) -Quantum Object: type=Operator dims=[10, 2] size=(20, 20) ishermitian=false +Quantum Object: type=Operator() dims=[10, 2] size=(20, 20) ishermitian=false 20×20 SparseMatrixCSC{ComplexF64, Int64} with 18 stored entries: ⎡⠀⠑⢄⠀⠀⠀⠀⠀⠀⠀⎤ ⎢⠀⠀⠀⠑⢄⠀⠀⠀⠀⠀⎥ @@ -42,7 +42,7 @@ coef1 (generic function with 1 method) julia> op = QobjEvo(a, coef1) -Quantum Object Evo.: type=Operator dims=[10, 2] size=(20, 20) ishermitian=true isconstant=false +Quantum Object Evo.: type=Operator() dims=[10, 2] size=(20, 20) ishermitian=true isconstant=false ScalarOperator(0.0 + 0.0im) * MatrixOperator(20 × 20) ``` @@ -51,7 +51,7 @@ If there are more than 2 operators, we need to put each set of operator and coef ```jldoctest qobjevo julia> σm = tensor(qeye(10), sigmam()) -Quantum Object: type=Operator dims=[10, 2] size=(20, 20) ishermitian=false +Quantum Object: type=Operator() dims=[10, 2] size=(20, 20) ishermitian=false 20×20 SparseMatrixCSC{ComplexF64, Int64} with 10 stored entries: ⎡⠂⡀⠀⠀⠀⠀⠀⠀⠀⠀⎤ ⎢⠀⠀⠂⡀⠀⠀⠀⠀⠀⠀⎥ @@ -64,7 +64,7 @@ coef2 (generic function with 1 method) julia> op1 = QobjEvo(((a, coef1), (σm, coef2))) -Quantum Object Evo.: type=Operator dims=[10, 2] size=(20, 20) ishermitian=true isconstant=false +Quantum Object Evo.: type=Operator() dims=[10, 2] size=(20, 20) ishermitian=true isconstant=false (ScalarOperator(0.0 + 0.0im) * MatrixOperator(20 × 20) + ScalarOperator(0.0 + 0.0im) * MatrixOperator(20 × 20)) ``` @@ -72,7 +72,7 @@ We can also concretize the operator at a specific time `t` ```jldoctest qobjevo julia> op1(0.1) -Quantum Object: type=Operator dims=[10, 2] size=(20, 20) ishermitian=false +Quantum Object: type=Operator() dims=[10, 2] size=(20, 20) ishermitian=false 20×20 SparseMatrixCSC{ComplexF64, Int64} with 28 stored entries: ⎡⠂⡑⢄⠀⠀⠀⠀⠀⠀⠀⎤ ⎢⠀⠀⠂⡑⢄⠀⠀⠀⠀⠀⎥ @@ -91,7 +91,7 @@ coef2 (generic function with 1 method) julia> op1 = QobjEvo(((a, coef1), (σm, coef2))) -Quantum Object Evo.: type=Operator dims=[10, 2] size=(20, 20) ishermitian=true isconstant=false +Quantum Object Evo.: type=Operator() dims=[10, 2] size=(20, 20) ishermitian=true isconstant=false (ScalarOperator(0.0 + 0.0im) * MatrixOperator(20 × 20) + ScalarOperator(0.0 + 0.0im) * MatrixOperator(20 × 20)) julia> p = (ω1 = 1.0, ω2 = 0.5) @@ -99,7 +99,7 @@ julia> p = (ω1 = 1.0, ω2 = 0.5) julia> op1(p, 0.1) -Quantum Object: type=Operator dims=[10, 2] size=(20, 20) ishermitian=false +Quantum Object: type=Operator() dims=[10, 2] size=(20, 20) ishermitian=false 20×20 SparseMatrixCSC{ComplexF64, Int64} with 28 stored entries: ⎡⠂⡑⢄⠀⠀⠀⠀⠀⠀⠀⎤ ⎢⠀⠀⠂⡑⢄⠀⠀⠀⠀⠀⎥ @@ -109,7 +109,7 @@ Quantum Object: type=Operator dims=[10, 2] size=(20, 20) ishermitian=fal ``` """ struct QuantumObjectEvolution{ - ObjType<:Union{OperatorQuantumObject,SuperOperatorQuantumObject}, + ObjType<:Union{Operator,SuperOperator}, DimType<:AbstractDimensions, DataType<:AbstractSciMLOperator, } <: AbstractQuantumObject{ObjType,DimType,DataType} @@ -117,12 +117,9 @@ struct QuantumObjectEvolution{ type::ObjType dimensions::DimType - function QuantumObjectEvolution( - data::DT, - type::ObjType, - dims, - ) where {DT<:AbstractSciMLOperator,ObjType<:QuantumObjectType} - (type == Operator || type == SuperOperator) || + function QuantumObjectEvolution(data::DT, type, dims) where {DT<:AbstractSciMLOperator} + ObjType = _check_type(type) + (type isa Operator || type isa SuperOperator) || throw(ArgumentError("The type $type is not supported for QuantumObjectEvolution.")) dimensions = _gen_dimensions(dims) @@ -153,22 +150,23 @@ function Base.show(io::IO, QO::QuantumObjectEvolution) end @doc raw""" - QobjEvo(data::AbstractSciMLOperator; type::QuantumObjectType = Operator, dims = nothing) - QuantumObjectEvolution(data::AbstractSciMLOperator; type::QuantumObjectType = Operator, dims = nothing) + QobjEvo(data::AbstractSciMLOperator; type = Operator(), dims = nothing) + QuantumObjectEvolution(data::AbstractSciMLOperator; type = Operator(), dims = nothing) Generate a [`QuantumObjectEvolution`](@ref) object from a [`SciMLOperator`](https://github.com/SciML/SciMLOperators.jl), in the same way as [`QuantumObject`](@ref) for `AbstractArray` inputs. Note that `QobjEvo` is a synonym of `QuantumObjectEvolution` """ -function QuantumObjectEvolution(data::AbstractSciMLOperator; type::QuantumObjectType = Operator, dims = nothing) +function QuantumObjectEvolution(data::AbstractSciMLOperator; type = Operator(), dims = nothing) _size = _get_size(data) + _check_type(type) if dims isa Nothing - if type isa OperatorQuantumObject + if type isa Operator dims = (_size[1] == _size[2]) ? Dimensions(_size[1]) : GeneralDimensions(SVector{2}(SVector{1}(_size[1]), SVector{1}(_size[2]))) - elseif type isa SuperOperatorQuantumObject + elseif type isa SuperOperator dims = Dimensions(isqrt(_size[2])) end end @@ -177,8 +175,8 @@ function QuantumObjectEvolution(data::AbstractSciMLOperator; type::QuantumObject end @doc raw""" - QobjEvo(op_func_list::Union{Tuple,AbstractQuantumObject}, α::Union{Nothing,Number}=nothing; type::Union{Nothing, QuantumObjectType}=nothing) - QuantumObjectEvolution(op_func_list::Union{Tuple,AbstractQuantumObject}, α::Union{Nothing,Number}=nothing; type::Union{Nothing, QuantumObjectType}=nothing) + QobjEvo(op_func_list::Union{Tuple,AbstractQuantumObject}, α::Union{Nothing,Number}=nothing; type=nothing) + QuantumObjectEvolution(op_func_list::Union{Tuple,AbstractQuantumObject}, α::Union{Nothing,Number}=nothing; type=nothing) Generate [`QuantumObjectEvolution`](@ref). @@ -199,7 +197,7 @@ This operator can be initialized in the same way as the QuTiP `QobjEvo` object. ```jldoctest qobjevo julia> a = tensor(destroy(10), qeye(2)) -Quantum Object: type=Operator dims=[10, 2] size=(20, 20) ishermitian=false +Quantum Object: type=Operator() dims=[10, 2] size=(20, 20) ishermitian=false 20×20 SparseMatrixCSC{ComplexF64, Int64} with 18 stored entries: ⎡⠀⠑⢄⠀⠀⠀⠀⠀⠀⠀⎤ ⎢⠀⠀⠀⠑⢄⠀⠀⠀⠀⠀⎥ @@ -209,7 +207,7 @@ Quantum Object: type=Operator dims=[10, 2] size=(20, 20) ishermitian=fal julia> σm = tensor(qeye(10), sigmam()) -Quantum Object: type=Operator dims=[10, 2] size=(20, 20) ishermitian=false +Quantum Object: type=Operator() dims=[10, 2] size=(20, 20) ishermitian=false 20×20 SparseMatrixCSC{ComplexF64, Int64} with 10 stored entries: ⎡⠂⡀⠀⠀⠀⠀⠀⠀⠀⠀⎤ ⎢⠀⠀⠂⡀⠀⠀⠀⠀⠀⠀⎥ @@ -225,7 +223,7 @@ coef2 (generic function with 1 method) julia> op1 = QobjEvo(((a, coef1), (σm, coef2))) -Quantum Object Evo.: type=Operator dims=[10, 2] size=(20, 20) ishermitian=true isconstant=false +Quantum Object Evo.: type=Operator() dims=[10, 2] size=(20, 20) ishermitian=true isconstant=false (ScalarOperator(0.0 + 0.0im) * MatrixOperator(20 × 20) + ScalarOperator(0.0 + 0.0im) * MatrixOperator(20 × 20)) ``` @@ -233,7 +231,7 @@ We can also concretize the operator at a specific time `t` ```jldoctest qobjevo julia> op1(0.1) -Quantum Object: type=Operator dims=[10, 2] size=(20, 20) ishermitian=false +Quantum Object: type=Operator() dims=[10, 2] size=(20, 20) ishermitian=false 20×20 SparseMatrixCSC{ComplexF64, Int64} with 28 stored entries: ⎡⠂⡑⢄⠀⠀⠀⠀⠀⠀⠀⎤ ⎢⠀⠀⠂⡑⢄⠀⠀⠀⠀⠀⎥ @@ -252,7 +250,7 @@ coef2 (generic function with 1 method) julia> op1 = QobjEvo(((a, coef1), (σm, coef2))) -Quantum Object Evo.: type=Operator dims=[10, 2] size=(20, 20) ishermitian=true isconstant=false +Quantum Object Evo.: type=Operator() dims=[10, 2] size=(20, 20) ishermitian=true isconstant=false (ScalarOperator(0.0 + 0.0im) * MatrixOperator(20 × 20) + ScalarOperator(0.0 + 0.0im) * MatrixOperator(20 × 20)) julia> p = (ω1 = 1.0, ω2 = 0.5) @@ -260,7 +258,7 @@ julia> p = (ω1 = 1.0, ω2 = 0.5) julia> op1(p, 0.1) -Quantum Object: type=Operator dims=[10, 2] size=(20, 20) ishermitian=false +Quantum Object: type=Operator() dims=[10, 2] size=(20, 20) ishermitian=false 20×20 SparseMatrixCSC{ComplexF64, Int64} with 28 stored entries: ⎡⠂⡑⢄⠀⠀⠀⠀⠀⠀⠀⎤ ⎢⠀⠀⠂⡑⢄⠀⠀⠀⠀⠀⎥ @@ -269,13 +267,11 @@ Quantum Object: type=Operator dims=[10, 2] size=(20, 20) ishermitian=fal ⎣⠀⠀⠀⠀⠀⠀⠀⠀⠂⡑⎦ ``` """ -function QuantumObjectEvolution( - op_func_list::Tuple, - α::Union{Nothing,Number} = nothing; - type::Union{Nothing,QuantumObjectType} = nothing, -) +function QuantumObjectEvolution(op_func_list::Tuple, α::Union{Nothing,Number} = nothing; type = nothing) op, data = _QobjEvo_generate_data(op_func_list, α) dims = op.dimensions + _check_type(type) + if type isa Nothing type = op.type end @@ -288,15 +284,12 @@ function QuantumObjectEvolution( end # this is a extra method if user accidentally specify `QuantumObjectEvolution( (op, func) )` or `QuantumObjectEvolution( ((op, func)) )` -QuantumObjectEvolution( - op_func::Tuple{QuantumObject,Function}, - α::Union{Nothing,Number} = nothing; - type::Union{Nothing,QuantumObjectType} = nothing, -) = QuantumObjectEvolution((op_func,), α; type = type) +QuantumObjectEvolution(op_func::Tuple{QuantumObject,Function}, α::Union{Nothing,Number} = nothing; type = nothing) = + QuantumObjectEvolution((op_func,), α; type = type) @doc raw""" - QuantumObjectEvolution(op::QuantumObject, f::Function, α::Union{Nothing,Number}=nothing; type::Union{Nothing,QuantumObjectType} = nothing) - QobjEvo(op::QuantumObject, f::Function, α::Union{Nothing,Number}=nothing; type::Union{Nothing,QuantumObjectType} = nothing) + QuantumObjectEvolution(op::QuantumObject, f::Function, α::Union{Nothing,Number}=nothing; type = nothing) + QobjEvo(op::QuantumObject, f::Function, α::Union{Nothing,Number}=nothing; type = nothing) Generate [`QuantumObjectEvolution`](@ref). @@ -308,7 +301,7 @@ Generate [`QuantumObjectEvolution`](@ref). ```jldoctest julia> a = tensor(destroy(10), qeye(2)) -Quantum Object: type=Operator dims=[10, 2] size=(20, 20) ishermitian=false +Quantum Object: type=Operator() dims=[10, 2] size=(20, 20) ishermitian=false 20×20 SparseMatrixCSC{ComplexF64, Int64} with 18 stored entries: ⎡⠀⠑⢄⠀⠀⠀⠀⠀⠀⠀⎤ ⎢⠀⠀⠀⠑⢄⠀⠀⠀⠀⠀⎥ @@ -321,33 +314,23 @@ coef (generic function with 1 method) julia> op = QobjEvo(a, coef) -Quantum Object Evo.: type=Operator dims=[10, 2] size=(20, 20) ishermitian=true isconstant=false +Quantum Object Evo.: type=Operator() dims=[10, 2] size=(20, 20) ishermitian=true isconstant=false ScalarOperator(0.0 + 0.0im) * MatrixOperator(20 × 20) ``` """ -QuantumObjectEvolution( - op::QuantumObject, - f::Function, - α::Union{Nothing,Number} = nothing; - type::Union{Nothing,QuantumObjectType} = nothing, -) = QuantumObjectEvolution(((op, f),), α; type = type) - -function QuantumObjectEvolution( - op::QuantumObject, - α::Union{Nothing,Number} = nothing; - type::Union{Nothing,QuantumObjectType} = nothing, -) +QuantumObjectEvolution(op::QuantumObject, f::Function, α::Union{Nothing,Number} = nothing; type = nothing) = + QuantumObjectEvolution(((op, f),), α; type = type) + +function QuantumObjectEvolution(op::QuantumObject, α::Union{Nothing,Number} = nothing; type = nothing) + _check_type(type) if type isa Nothing type = op.type end return QuantumObjectEvolution(_make_SciMLOperator(op, α), type, op.dimensions) end -function QuantumObjectEvolution( - op::QuantumObjectEvolution, - α::Union{Nothing,Number} = nothing; - type::Union{Nothing,QuantumObjectType} = nothing, -) +function QuantumObjectEvolution(op::QuantumObjectEvolution, α::Union{Nothing,Number} = nothing; type = nothing) + _check_type(type) if type isa Nothing type = op.type elseif type != op.type @@ -482,7 +465,7 @@ Apply the time-dependent [`QuantumObjectEvolution`](@ref) object `A` to the inpu # Arguments - `ψout::QuantumObject`: The output state. It must have the same type as `ψin`. -- `ψin::QuantumObject`: The input state. It must be either a [`KetQuantumObject`](@ref) or a [`OperatorKetQuantumObject`](@ref). +- `ψin::QuantumObject`: The input state. It must be either a [`Ket`](@ref) or a [`OperatorKet`](@ref). - `p`: The parameters of the time-dependent coefficients. - `t`: The time at which the coefficients are evaluated. @@ -493,7 +476,7 @@ Apply the time-dependent [`QuantumObjectEvolution`](@ref) object `A` to the inpu ```jldoctest julia> a = destroy(20) -Quantum Object: type=Operator dims=[20] size=(20, 20) ishermitian=false +Quantum Object: type=Operator() dims=[20] size=(20, 20) ishermitian=false 20×20 SparseMatrixCSC{ComplexF64, Int64} with 19 stored entries: ⎡⠈⠢⡀⠀⠀⠀⠀⠀⠀⠀⎤ ⎢⠀⠀⠈⠢⡀⠀⠀⠀⠀⠀⎥ @@ -509,7 +492,7 @@ coef2 (generic function with 1 method) julia> A = QobjEvo(((a, coef1), (a', coef2))) -Quantum Object Evo.: type=Operator dims=[20] size=(20, 20) ishermitian=true isconstant=false +Quantum Object Evo.: type=Operator() dims=[20] size=(20, 20) ishermitian=true isconstant=false (ScalarOperator(0.0 + 0.0im) * MatrixOperator(20 × 20) + ScalarOperator(0.0 + 0.0im) * MatrixOperator(20 × 20)) julia> ψ1 = fock(20, 3); @@ -525,7 +508,7 @@ function (A::QuantumObjectEvolution)( ψin::QuantumObject{QobjType}, p, t, -) where {QobjType<:Union{KetQuantumObject,OperatorKetQuantumObject}} +) where {QobjType<:Union{Ket,OperatorKet}} check_dimensions(A, ψout, ψin) if isoper(A) && isoperket(ψin) @@ -548,11 +531,7 @@ end Apply the time-dependent [`QuantumObjectEvolution`](@ref) object `A` to the input state `ψ` at time `t` with parameters `p`. Out-of-place version of [`(A::QuantumObjectEvolution)(ψout, ψin, p, t)`](@ref). The output state is stored in a new [`QuantumObject`](@ref) object. This function mimics the behavior of a `AbstractSciMLOperator` object. """ -function (A::QuantumObjectEvolution)( - ψ::QuantumObject{QobjType}, - p, - t, -) where {QobjType<:Union{KetQuantumObject,OperatorKetQuantumObject}} +function (A::QuantumObjectEvolution)(ψ::QuantumObject{QobjType}, p, t) where {QobjType<:Union{Ket,OperatorKet}} ψout = QuantumObject(similar(ψ.data), ψ.type, ψ.dimensions) return A(ψout, ψ, p, t) end diff --git a/src/qobj/states.jl b/src/qobj/states.jl index 4474ebd16..b9bfe7ba6 100644 --- a/src/qobj/states.jl +++ b/src/qobj/states.jl @@ -19,9 +19,9 @@ The `dimensions` can be either the following types: !!! warning "Beware of type-stability!" It is highly recommended to use `zero_ket(dimensions)` with `dimensions` as `Tuple` or `SVector` from [StaticArrays.jl](https://github.com/JuliaArrays/StaticArrays.jl) to keep type stability. See the [related Section](@ref doc:Type-Stability) about type stability for more details. """ -zero_ket(dimensions::Int) = QuantumObject(zeros(ComplexF64, dimensions), Ket, dimensions) +zero_ket(dimensions::Int) = QuantumObject(zeros(ComplexF64, dimensions), Ket(), dimensions) zero_ket(dimensions::Union{Dimensions,AbstractVector{Int},Tuple}) = - QuantumObject(zeros(ComplexF64, prod(dimensions)), Ket, dimensions) + QuantumObject(zeros(ComplexF64, prod(dimensions)), Ket(), dimensions) @doc raw""" fock(N::Int, j::Int=0; dims::Union{Int,AbstractVector{Int},Tuple}=N, sparse::Union{Bool,Val}=Val(false)) @@ -39,7 +39,7 @@ function fock(N::Int, j::Int = 0; dims::Union{Int,AbstractVector{Int},Tuple} = N else array = [i == (j + 1) ? 1.0 + 0im : 0.0 + 0im for i in 1:N] end - return QuantumObject(array; type = Ket, dims = dims) + return QuantumObject(array; type = Ket(), dims = dims) end @doc raw""" @@ -79,7 +79,7 @@ rand_ket(dimensions::Int) = rand_ket(SVector(dimensions)) function rand_ket(dimensions::Union{Dimensions,AbstractVector{Int},Tuple}) N = prod(dimensions) ψ = rand(ComplexF64, N) .- (0.5 + 0.5im) - return QuantumObject(normalize!(ψ); type = Ket, dims = dimensions) + return QuantumObject(normalize!(ψ); type = Ket(), dims = dimensions) end @doc raw""" @@ -130,9 +130,9 @@ function thermal_dm(N::Int, n::Real; sparse::Union{Bool,Val} = Val(false)) N_list = Array{Float64}(0:(N-1)) data = exp.(-β .* N_list) if getVal(sparse) - return QuantumObject(spdiagm(0 => data ./ sum(data)), Operator, N) + return QuantumObject(spdiagm(0 => data ./ sum(data)), Operator(), N) else - return QuantumObject(diagm(0 => data ./ sum(data)), Operator, N) + return QuantumObject(diagm(0 => data ./ sum(data)), Operator(), N) end end @@ -148,10 +148,11 @@ The `dimensions` can be either the following types: !!! warning "Beware of type-stability!" If you want to keep type stability, it is recommended to use `maximally_mixed_dm(dimensions)` with `dimensions` as `Tuple` or `SVector` from [StaticArrays.jl](https://github.com/JuliaArrays/StaticArrays.jl) to keep type stability. See the [related Section](@ref doc:Type-Stability) about type stability for more details. """ -maximally_mixed_dm(dimensions::Int) = QuantumObject(I(dimensions) / complex(dimensions), Operator, SVector(dimensions)) +maximally_mixed_dm(dimensions::Int) = + QuantumObject(I(dimensions) / complex(dimensions), Operator(), SVector(dimensions)) function maximally_mixed_dm(dimensions::Union{Dimensions,AbstractVector{Int},Tuple}) N = prod(dimensions) - return QuantumObject(I(N) / complex(N), Operator, dimensions) + return QuantumObject(I(N) / complex(N), Operator(), dimensions) end @doc raw""" @@ -181,7 +182,7 @@ function rand_dm(dimensions::Union{Dimensions,AbstractVector{Int},Tuple}; rank:: X = _Ginibre_ensemble(N, rank) ρ = X * X' ρ /= tr(ρ) - return QuantumObject(ρ; type = Operator, dims = dimensions) + return QuantumObject(ρ; type = Operator(), dims = dimensions) end @doc raw""" @@ -254,7 +255,7 @@ Here, `x = 1` (`z = 1`) means applying Pauli-``X`` ( Pauli-``Z``) unitary transf ```jldoctest julia> bell_state(0, 0) -Quantum Object: type=Ket dims=[2, 2] size=(4,) +Quantum Object: type=Ket() dims=[2, 2] size=(4,) 4-element Vector{ComplexF64}: 0.7071067811865475 + 0.0im 0.0 + 0.0im @@ -263,7 +264,7 @@ Quantum Object: type=Ket dims=[2, 2] size=(4,) julia> bell_state(Val(1), Val(0)) -Quantum Object: type=Ket dims=[2, 2] size=(4,) +Quantum Object: type=Ket() dims=[2, 2] size=(4,) 4-element Vector{ComplexF64}: 0.0 + 0.0im 0.7071067811865475 + 0.0im @@ -275,10 +276,10 @@ Quantum Object: type=Ket dims=[2, 2] size=(4,) If you want to keep type stability, it is recommended to use `bell_state(Val(x), Val(z))` instead of `bell_state(x, z)`. See [this link](https://docs.julialang.org/en/v1/manual/performance-tips/#man-performance-value-type) and the [related Section](@ref doc:Type-Stability) for more details. """ bell_state(x::Int, z::Int) = bell_state(Val(x), Val(z)) -bell_state(::Val{0}, ::Val{0}) = QuantumObject(ComplexF64[1, 0, 0, 1] / sqrt(2), Ket, (2, 2)) -bell_state(::Val{0}, ::Val{1}) = QuantumObject(ComplexF64[1, 0, 0, -1] / sqrt(2), Ket, (2, 2)) -bell_state(::Val{1}, ::Val{0}) = QuantumObject(ComplexF64[0, 1, 1, 0] / sqrt(2), Ket, (2, 2)) -bell_state(::Val{1}, ::Val{1}) = QuantumObject(ComplexF64[0, 1, -1, 0] / sqrt(2), Ket, (2, 2)) +bell_state(::Val{0}, ::Val{0}) = QuantumObject(ComplexF64[1, 0, 0, 1] / sqrt(2), Ket(), (2, 2)) +bell_state(::Val{0}, ::Val{1}) = QuantumObject(ComplexF64[1, 0, 0, -1] / sqrt(2), Ket(), (2, 2)) +bell_state(::Val{1}, ::Val{0}) = QuantumObject(ComplexF64[0, 1, 1, 0] / sqrt(2), Ket(), (2, 2)) +bell_state(::Val{1}, ::Val{1}) = QuantumObject(ComplexF64[0, 1, -1, 0] / sqrt(2), Ket(), (2, 2)) bell_state(::Val{T1}, ::Val{T2}) where {T1,T2} = throw(ArgumentError("Invalid Bell state: $(T1), $(T2)")) @doc raw""" @@ -286,7 +287,7 @@ bell_state(::Val{T1}, ::Val{T2}) where {T1,T2} = throw(ArgumentError("Invalid Be Return the two particle singlet state: ``\frac{1}{\sqrt{2}} ( |01\rangle - |10\rangle )`` """ -singlet_state() = QuantumObject(ComplexF64[0, 1, -1, 0] / sqrt(2), Ket, (2, 2)) +singlet_state() = QuantumObject(ComplexF64[0, 1, -1, 0] / sqrt(2), Ket(), (2, 2)) @doc raw""" triplet_states() @@ -299,9 +300,9 @@ Return a list of the two particle triplet states: """ function triplet_states() return QuantumObject[ - QuantumObject(ComplexF64[0, 0, 0, 1], Ket, (2, 2)), - QuantumObject(ComplexF64[0, 1, 1, 0] / sqrt(2), Ket, (2, 2)), - QuantumObject(ComplexF64[1, 0, 0, 0], Ket, (2, 2)), + QuantumObject(ComplexF64[0, 0, 0, 1], Ket(), (2, 2)), + QuantumObject(ComplexF64[0, 1, 1, 0] / sqrt(2), Ket(), (2, 2)), + QuantumObject(ComplexF64[1, 0, 0, 0], Ket(), (2, 2)), ] end @@ -320,7 +321,7 @@ Returns the `n`-qubit [W-state](https://en.wikipedia.org/wiki/W_state): function w_state(::Val{n}) where {n} nzind = 2 .^ (0:(n-1)) .+ 1 nzval = fill(ComplexF64(1 / sqrt(n)), n) - return QuantumObject(SparseVector(2^n, nzind, nzval), Ket, ntuple(x -> 2, Val(n))) + return QuantumObject(SparseVector(2^n, nzind, nzval), Ket(), ntuple(x -> 2, Val(n))) end w_state(n::Int) = w_state(Val(n)) @@ -341,6 +342,6 @@ Here, `d` specifies the dimension of each qudit. Default to `d=2` (qubit). function ghz_state(::Val{n}; d::Int = 2) where {n} nzind = collect((0:(d-1)) .* Int((d^n - 1) / (d - 1)) .+ 1) nzval = ones(ComplexF64, d) / sqrt(d) - return QuantumObject(SparseVector(d^n, nzind, nzval), Ket, ntuple(x -> d, Val(n))) + return QuantumObject(SparseVector(d^n, nzind, nzval), Ket(), ntuple(x -> d, Val(n))) end ghz_state(n::Int; d::Int = 2) = ghz_state(Val(n), d = d) diff --git a/src/qobj/superoperators.jl b/src/qobj/superoperators.jl index affc0a980..6bc9a71c3 100644 --- a/src/qobj/superoperators.jl +++ b/src/qobj/superoperators.jl @@ -77,8 +77,8 @@ The optional argument `Id_cache` can be used to pass a precomputed identity matr See also [`spost`](@ref) and [`sprepost`](@ref). """ -spre(A::AbstractQuantumObject{OperatorQuantumObject}, Id_cache = I(size(A, 1))) = - get_typename_wrapper(A)(_spre(A.data, Id_cache), SuperOperator, A.dimensions) +spre(A::AbstractQuantumObject{Operator}, Id_cache = I(size(A, 1))) = + get_typename_wrapper(A)(_spre(A.data, Id_cache), SuperOperator(), A.dimensions) @doc raw""" spost(B::AbstractQuantumObject, Id_cache=I(size(B,1))) @@ -96,8 +96,8 @@ The optional argument `Id_cache` can be used to pass a precomputed identity matr See also [`spre`](@ref) and [`sprepost`](@ref). """ -spost(B::AbstractQuantumObject{OperatorQuantumObject}, Id_cache = I(size(B, 1))) = - get_typename_wrapper(B)(_spost(B.data, Id_cache), SuperOperator, B.dimensions) +spost(B::AbstractQuantumObject{Operator}, Id_cache = I(size(B, 1))) = + get_typename_wrapper(B)(_spost(B.data, Id_cache), SuperOperator(), B.dimensions) @doc raw""" sprepost(A::AbstractQuantumObject, B::AbstractQuantumObject) @@ -113,9 +113,9 @@ Since the density matrix is vectorized in [`OperatorKet`](@ref) form: ``|\hat{\r See also [`spre`](@ref) and [`spost`](@ref). """ -function sprepost(A::AbstractQuantumObject{OperatorQuantumObject}, B::AbstractQuantumObject{OperatorQuantumObject}) +function sprepost(A::AbstractQuantumObject{Operator}, B::AbstractQuantumObject{Operator}) check_dimensions(A, B) - return promote_op_type(A, B)(_sprepost(A.data, B.data), SuperOperator, A.dimensions) + return promote_op_type(A, B)(_sprepost(A.data, B.data), SuperOperator(), A.dimensions) end @doc raw""" @@ -132,11 +132,11 @@ 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{OperatorQuantumObject}, Id_cache = I(size(O, 1))) = - get_typename_wrapper(O)(_lindblad_dissipator(O.data, Id_cache), SuperOperator, O.dimensions) +lindblad_dissipator(O::AbstractQuantumObject{Operator}, Id_cache = I(size(O, 1))) = + get_typename_wrapper(O)(_lindblad_dissipator(O.data, Id_cache), SuperOperator(), O.dimensions) # It is already a SuperOperator -lindblad_dissipator(O::AbstractQuantumObject{SuperOperatorQuantumObject}, Id_cache = nothing) = O +lindblad_dissipator(O::AbstractQuantumObject{SuperOperator}, Id_cache = nothing) = O @doc raw""" liouvillian(H::AbstractQuantumObject, c_ops::Union{Nothing,AbstractVector,Tuple}=nothing, Id_cache=I(prod(H.dimensions))) @@ -161,7 +161,7 @@ function liouvillian( H::AbstractQuantumObject{OpType}, c_ops::Union{Nothing,AbstractVector,Tuple} = nothing, Id_cache = I(prod(H.dimensions)), -) where {OpType<:Union{OperatorQuantumObject,SuperOperatorQuantumObject}} +) where {OpType<:Union{Operator,SuperOperator}} L = liouvillian(H, Id_cache) if !(c_ops isa Nothing) L += _sum_lindblad_dissipators(c_ops, Id_cache) @@ -174,10 +174,10 @@ liouvillian(H::Nothing, c_ops::Union{AbstractVector,Tuple}, Id_cache::Diagonal = liouvillian(H::Nothing, c_ops::Nothing) = 0 -liouvillian(H::AbstractQuantumObject{OperatorQuantumObject}, Id_cache::Diagonal = I(prod(H.dimensions))) = - get_typename_wrapper(H)(_liouvillian(H.data, Id_cache), SuperOperator, H.dimensions) +liouvillian(H::AbstractQuantumObject{Operator}, Id_cache::Diagonal = I(prod(H.dimensions))) = + get_typename_wrapper(H)(_liouvillian(H.data, Id_cache), SuperOperator(), H.dimensions) -liouvillian(H::AbstractQuantumObject{SuperOperatorQuantumObject}, Id_cache::Diagonal) = H +liouvillian(H::AbstractQuantumObject{SuperOperator}, Id_cache::Diagonal) = H function _sum_lindblad_dissipators(c_ops, Id_cache::Diagonal) D = 0 diff --git a/src/qobj/synonyms.jl b/src/qobj/synonyms.jl index 6171e4211..370d9d902 100644 --- a/src/qobj/synonyms.jl +++ b/src/qobj/synonyms.jl @@ -63,7 +63,7 @@ Matrix square root of [`Operator`](@ref) type of [`QuantumObject`](@ref) Note that for other types of [`QuantumObject`](@ref) use `sprt(A)` instead. """ -sqrtm(A::QuantumObject{OperatorQuantumObject}) = sqrt(A) +sqrtm(A::QuantumObject{Operator}) = sqrt(A) @doc raw""" logm(A::QuantumObject) @@ -72,7 +72,7 @@ Matrix logarithm of [`QuantumObject`](@ref) Note that this function is same as `log(A)` and only supports for [`Operator`](@ref) and [`SuperOperator`](@ref). """ -logm(A::QuantumObject{ObjType}) where {ObjType<:Union{OperatorQuantumObject,SuperOperatorQuantumObject}} = log(A) +logm(A::QuantumObject{ObjType}) where {ObjType<:Union{Operator,SuperOperator}} = log(A) @doc raw""" expm(A::QuantumObject) @@ -81,7 +81,7 @@ Matrix exponential of [`QuantumObject`](@ref) Note that this function is same as `exp(A)` and only supports for [`Operator`](@ref) and [`SuperOperator`](@ref). """ -expm(A::QuantumObject{ObjType}) where {ObjType<:Union{OperatorQuantumObject,SuperOperatorQuantumObject}} = exp(A) +expm(A::QuantumObject{ObjType}) where {ObjType<:Union{Operator,SuperOperator}} = exp(A) @doc raw""" sinm(A::QuantumObject) @@ -92,7 +92,7 @@ Matrix sine of [`QuantumObject`](@ref), defined as Note that this function is same as `sin(A)` and only supports for [`Operator`](@ref) and [`SuperOperator`](@ref). """ -sinm(A::QuantumObject{ObjType}) where {ObjType<:Union{OperatorQuantumObject,SuperOperatorQuantumObject}} = sin(A) +sinm(A::QuantumObject{ObjType}) where {ObjType<:Union{Operator,SuperOperator}} = sin(A) @doc raw""" cosm(A::QuantumObject) @@ -103,4 +103,4 @@ Matrix cosine of [`QuantumObject`](@ref), defined as Note that this function is same as `cos(A)` and only supports for [`Operator`](@ref) and [`SuperOperator`](@ref). """ -cosm(A::QuantumObject{ObjType}) where {ObjType<:Union{OperatorQuantumObject,SuperOperatorQuantumObject}} = cos(A) +cosm(A::QuantumObject{ObjType}) where {ObjType<:Union{Operator,SuperOperator}} = cos(A) diff --git a/src/spectrum.jl b/src/spectrum.jl index 14bc88893..036d95b3d 100644 --- a/src/spectrum.jl +++ b/src/spectrum.jl @@ -30,8 +30,8 @@ PseudoInverse(; alg::SciMLLinearSolveAlgorithm = KrylovJL_GMRES()) = PseudoInver spectrum(H::QuantumObject, ωlist::AbstractVector, c_ops::Union{Nothing,AbstractVector,Tuple}, - A::QuantumObject{OperatorQuantumObject}, - B::QuantumObject{OperatorQuantumObject}; + A::QuantumObject{Operator}, + B::QuantumObject{Operator}; solver::SpectrumSolver=ExponentialSeries(), kwargs...) @@ -49,11 +49,11 @@ function spectrum( H::QuantumObject{HOpType}, ωlist::AbstractVector, c_ops::Union{Nothing,AbstractVector,Tuple}, - A::QuantumObject{OperatorQuantumObject}, - B::QuantumObject{OperatorQuantumObject}; + A::QuantumObject{Operator}, + B::QuantumObject{Operator}; solver::SpectrumSolver = ExponentialSeries(), kwargs..., -) where {HOpType<:Union{OperatorQuantumObject,SuperOperatorQuantumObject}} +) where {HOpType<:Union{Operator,SuperOperator}} return _spectrum(liouvillian(H, c_ops), ωlist, A, B, solver; kwargs...) end @@ -77,10 +77,10 @@ function _spectrum_get_rates_vecs_ss(L, solver::ExponentialSeries{T,false}) wher end function _spectrum( - L::QuantumObject{SuperOperatorQuantumObject}, + L::QuantumObject{SuperOperator}, ωlist::AbstractVector, - A::QuantumObject{OperatorQuantumObject}, - B::QuantumObject{OperatorQuantumObject}, + A::QuantumObject{Operator}, + B::QuantumObject{Operator}, solver::ExponentialSeries; kwargs..., ) @@ -103,10 +103,10 @@ function _spectrum( end function _spectrum( - L::QuantumObject{SuperOperatorQuantumObject}, + L::QuantumObject{SuperOperator}, ωlist::AbstractVector, - A::QuantumObject{OperatorQuantumObject}, - B::QuantumObject{OperatorQuantumObject}, + A::QuantumObject{Operator}, + B::QuantumObject{Operator}, solver::PseudoInverse; kwargs..., ) diff --git a/src/spin_lattice.jl b/src/spin_lattice.jl index 1abfff235..f3f47b237 100644 --- a/src/spin_lattice.jl +++ b/src/spin_lattice.jl @@ -59,7 +59,7 @@ function multisite_operator(dims::Union{AbstractVector,Tuple}, pairs::Pair{<:Int end data = kron(data, I(prod(_dims[(sites[end]+1):end]))) - return QuantumObject(data; type = Operator, dims = dims) + return QuantumObject(data; type = Operator(), dims = dims) end function multisite_operator(N::Union{Integer,Val}, pairs::Pair{<:Integer,<:QuantumObject}...) dims = ntuple(j -> 2, makeVal(N)) diff --git a/src/steadystate.jl b/src/steadystate.jl index 1d25eda6b..d17ad4783 100644 --- a/src/steadystate.jl +++ b/src/steadystate.jl @@ -115,7 +115,7 @@ function steadystate( c_ops::Union{Nothing,AbstractVector,Tuple} = nothing; solver::SteadyStateSolver = SteadyStateDirectSolver(), kwargs..., -) where {OpType<:Union{OperatorQuantumObject,SuperOperatorQuantumObject}} +) where {OpType<:Union{Operator,SuperOperator}} solver isa SSFloquetEffectiveLiouvillian && throw( ArgumentError( "The solver `SSFloquetEffectiveLiouvillian` is only available for the `steadystate_fourier` function.", @@ -127,7 +127,7 @@ function steadystate( return _steadystate(L, solver; kwargs...) end -function _steadystate(L::QuantumObject{SuperOperatorQuantumObject}, solver::SteadyStateLinearSolver; kwargs...) +function _steadystate(L::QuantumObject{SuperOperator}, solver::SteadyStateLinearSolver; kwargs...) L_tmp = L.data N = prod(L.dimensions) weight = norm(L_tmp, 1) / length(L_tmp) @@ -159,10 +159,10 @@ function _steadystate(L::QuantumObject{SuperOperatorQuantumObject}, solver::Stea ρss = reshape(ρss_vec, N, N) ρss = (ρss + ρss') / 2 # Hermitianize - return QuantumObject(ρss, Operator, L.dimensions) + return QuantumObject(ρss, Operator(), L.dimensions) end -function _steadystate(L::QuantumObject{SuperOperatorQuantumObject}, solver::SteadyStateEigenSolver; kwargs...) +function _steadystate(L::QuantumObject{SuperOperator}, solver::SteadyStateEigenSolver; kwargs...) N = prod(L.dimensions) kwargs = merge((sigma = 1e-8, eigvals = 1), (; kwargs...)) @@ -171,10 +171,10 @@ function _steadystate(L::QuantumObject{SuperOperatorQuantumObject}, solver::Stea ρss = reshape(ρss_vec, N, N) ρss /= tr(ρss) ρss = (ρss + ρss') / 2 # Hermitianize - return QuantumObject(ρss, Operator, L.dimensions) + return QuantumObject(ρss, Operator(), L.dimensions) end -function _steadystate(L::QuantumObject{SuperOperatorQuantumObject}, solver::SteadyStateDirectSolver) +function _steadystate(L::QuantumObject{SuperOperator}, solver::SteadyStateDirectSolver) L_tmp = L.data N = prod(L.dimensions) weight = norm(L_tmp, 1) / length(L_tmp) @@ -196,10 +196,10 @@ function _steadystate(L::QuantumObject{SuperOperatorQuantumObject}, solver::Stea ρ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.dimensions) + return QuantumObject(ρss, Operator(), L.dimensions) end -function _steadystate(L::QuantumObject{SuperOperatorQuantumObject}, solver::SteadyStateODESolver; kwargs...) +function _steadystate(L::QuantumObject{SuperOperator}, solver::SteadyStateODESolver; kwargs...) tmax = solver.tmax ψ0 = isnothing(solver.ψ0) ? rand_ket(L.dimensions) : solver.ψ0 @@ -333,9 +333,9 @@ function steadystate_fourier( solver::FSolver = SteadyStateLinearSolver(), kwargs..., ) where { - OpType1<:Union{OperatorQuantumObject,SuperOperatorQuantumObject}, - OpType2<:Union{OperatorQuantumObject,SuperOperatorQuantumObject}, - OpType3<:Union{OperatorQuantumObject,SuperOperatorQuantumObject}, + OpType1<:Union{Operator,SuperOperator}, + OpType2<:Union{Operator,SuperOperator}, + OpType3<:Union{Operator,SuperOperator}, R<:Real, FSolver<:SteadyStateSolver, } @@ -346,9 +346,9 @@ function steadystate_fourier( end function _steadystate_fourier( - L_0::QuantumObject{SuperOperatorQuantumObject}, - L_p::QuantumObject{SuperOperatorQuantumObject}, - L_m::QuantumObject{SuperOperatorQuantumObject}, + L_0::QuantumObject{SuperOperator}, + L_p::QuantumObject{SuperOperator}, + L_m::QuantumObject{SuperOperator}, ωd::Number, solver::SteadyStateLinearSolver; n_max::Integer = 1, @@ -402,13 +402,13 @@ function _steadystate_fourier( ρ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.dimensions) + ρ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.dimensions) + ρi_m = QuantumObject(ρi_m, type = Operator(), dims = L_0.dimensions) push!(ρ_list, ρi_m) end @@ -416,9 +416,9 @@ function _steadystate_fourier( end function _steadystate_fourier( - L_0::QuantumObject{SuperOperatorQuantumObject}, - L_p::QuantumObject{SuperOperatorQuantumObject}, - L_m::QuantumObject{SuperOperatorQuantumObject}, + L_0::QuantumObject{SuperOperator}, + L_p::QuantumObject{SuperOperator}, + L_m::QuantumObject{SuperOperator}, ωd::Number, solver::SSFloquetEffectiveLiouvillian; n_max::Integer = 1, diff --git a/src/time_evolution/lr_mesolve.jl b/src/time_evolution/lr_mesolve.jl index 4a0f204b3..bc166a540 100644 --- a/src/time_evolution/lr_mesolve.jl +++ b/src/time_evolution/lr_mesolve.jl @@ -366,7 +366,7 @@ get_B(u::AbstractArray{T}, N::Integer, M::Integer) where {T} = reshape(view(u, ( @doc raw""" lr_mesolveProblem( - H::QuantumObject{OperatorQuantumObject}, + H::QuantumObject{Operator}, z::AbstractArray{T,2}, B::AbstractArray{T,2}, tlist::AbstractVector, @@ -391,7 +391,7 @@ Formulates the ODEproblem for the low-rank time evolution of the system. The fun - `kwargs`: Additional keyword arguments. """ function lr_mesolveProblem( - H::QuantumObject{OperatorQuantumObject}, + H::QuantumObject{Operator}, z::AbstractArray{T,2}, B::AbstractArray{T,2}, tlist::AbstractVector, @@ -497,7 +497,7 @@ end @doc raw""" lr_mesolve( - H::QuantumObject{OperatorQuantumObject}, + H::QuantumObject{Operator}, z::AbstractArray{T,2}, B::AbstractArray{T,2}, tlist::AbstractVector, @@ -522,7 +522,7 @@ Time evolution of an open quantum system using the low-rank master equation. For - `kwargs`: Additional keyword arguments. """ function lr_mesolve( - H::QuantumObject{OperatorQuantumObject}, + H::QuantumObject{Operator}, z::AbstractArray{T2,2}, B::AbstractArray{T2,2}, tlist::AbstractVector, @@ -546,7 +546,7 @@ function lr_mesolve(prob::ODEProblem; kwargs...) Bt = map(x -> get_B(x[1], N, x[2]), zip(sol.u, Ml)) zt = map(x -> get_z(x[1], N, x[2]), zip(sol.u, Ml)) - ρt = map(x -> Qobj(x[1] * x[2] * x[1]', type = Operator, dims = prob.p.Hdims), zip(zt, Bt)) + ρt = map(x -> Qobj(x[1] * x[2] * x[1]', type = Operator(), dims = prob.p.Hdims), zip(zt, Bt)) return TimeEvolutionLRSol( sol.t, diff --git a/src/time_evolution/mcsolve.jl b/src/time_evolution/mcsolve.jl index 210cf37a7..7db7048ff 100644 --- a/src/time_evolution/mcsolve.jl +++ b/src/time_evolution/mcsolve.jl @@ -22,7 +22,7 @@ end function _normalize_state!(u, dims, normalize_states) getVal(normalize_states) && normalize!(u) - return QuantumObject(u, type = Ket, dims = dims) + return QuantumObject(u, type = Ket(), dims = dims) end function _mcsolve_make_Heff_QobjEvo(H::QuantumObject, c_ops) @@ -40,8 +40,8 @@ end @doc raw""" mcsolveProblem( - H::Union{AbstractQuantumObject{OperatorQuantumObject},Tuple}, - ψ0::QuantumObject{KetQuantumObject}, + H::Union{AbstractQuantumObject{Operator},Tuple}, + ψ0::QuantumObject{Ket}, tlist::AbstractVector, c_ops::Union{Nothing,AbstractVector,Tuple} = nothing; e_ops::Union{Nothing,AbstractVector,Tuple} = nothing, @@ -109,8 +109,8 @@ If the environmental measurements register a quantum jump, the wave function und - `prob`: The [`TimeEvolutionProblem`](@ref) containing the `ODEProblem` for the Monte Carlo wave function time evolution. """ function mcsolveProblem( - H::Union{AbstractQuantumObject{OperatorQuantumObject},Tuple}, - ψ0::QuantumObject{KetQuantumObject}, + H::Union{AbstractQuantumObject{Operator},Tuple}, + ψ0::QuantumObject{Ket}, tlist::AbstractVector, c_ops::Union{Nothing,AbstractVector,Tuple} = nothing; e_ops::Union{Nothing,AbstractVector,Tuple} = nothing, @@ -141,8 +141,8 @@ end @doc raw""" mcsolveEnsembleProblem( - H::Union{AbstractQuantumObject{OperatorQuantumObject},Tuple}, - ψ0::QuantumObject{KetQuantumObject}, + H::Union{AbstractQuantumObject{Operator},Tuple}, + ψ0::QuantumObject{Ket}, tlist::AbstractVector, c_ops::Union{Nothing,AbstractVector,Tuple} = nothing; e_ops::Union{Nothing,AbstractVector,Tuple} = nothing, @@ -220,8 +220,8 @@ If the environmental measurements register a quantum jump, the wave function und - `prob`: The [`TimeEvolutionProblem`](@ref) containing the Ensemble `ODEProblem` for the Monte Carlo wave function time evolution. """ function mcsolveEnsembleProblem( - H::Union{AbstractQuantumObject{OperatorQuantumObject},Tuple}, - ψ0::QuantumObject{KetQuantumObject}, + H::Union{AbstractQuantumObject{Operator},Tuple}, + ψ0::QuantumObject{Ket}, tlist::AbstractVector, c_ops::Union{Nothing,AbstractVector,Tuple} = nothing; e_ops::Union{Nothing,AbstractVector,Tuple} = nothing, @@ -264,8 +264,8 @@ end @doc raw""" mcsolve( - H::Union{AbstractQuantumObject{OperatorQuantumObject},Tuple}, - ψ0::QuantumObject{KetQuantumObject}, + H::Union{AbstractQuantumObject{Operator},Tuple}, + ψ0::QuantumObject{Ket}, tlist::AbstractVector, c_ops::Union{Nothing,AbstractVector,Tuple} = nothing; alg::OrdinaryDiffEqAlgorithm = Tsit5(), @@ -349,8 +349,8 @@ If the environmental measurements register a quantum jump, the wave function und - `sol::TimeEvolutionMCSol`: The solution of the time evolution. See also [`TimeEvolutionMCSol`](@ref). """ function mcsolve( - H::Union{AbstractQuantumObject{OperatorQuantumObject},Tuple}, - ψ0::QuantumObject{KetQuantumObject}, + H::Union{AbstractQuantumObject{Operator},Tuple}, + ψ0::QuantumObject{Ket}, tlist::AbstractVector, c_ops::Union{Nothing,AbstractVector,Tuple} = nothing; alg::OrdinaryDiffEqAlgorithm = Tsit5(), diff --git a/src/time_evolution/mesolve.jl b/src/time_evolution/mesolve.jl index 968cc8aad..980148889 100644 --- a/src/time_evolution/mesolve.jl +++ b/src/time_evolution/mesolve.jl @@ -1,6 +1,6 @@ export mesolveProblem, mesolve -_mesolve_make_L_QobjEvo(H::Union{QuantumObject,Nothing}, c_ops) = QobjEvo(liouvillian(H, c_ops); type = SuperOperator) +_mesolve_make_L_QobjEvo(H::Union{QuantumObject,Nothing}, c_ops) = QobjEvo(liouvillian(H, c_ops); type = SuperOperator()) _mesolve_make_L_QobjEvo(H::Union{QuantumObjectEvolution,Tuple}, c_ops) = liouvillian(QobjEvo(H), c_ops) _mesolve_make_L_QobjEvo(H::Nothing, c_ops::Nothing) = throw(ArgumentError("Both H and c_ops are Nothing. You are probably running the wrong function.")) @@ -64,10 +64,7 @@ function mesolveProblem( progress_bar::Union{Val,Bool} = Val(true), inplace::Union{Val,Bool} = Val(true), kwargs..., -) where { - HOpType<:Union{OperatorQuantumObject,SuperOperatorQuantumObject}, - StateOpType<:Union{KetQuantumObject,OperatorQuantumObject,OperatorKetQuantumObject}, -} +) where {HOpType<:Union{Operator,SuperOperator},StateOpType<:Union{Ket,Operator,OperatorKet}} (isoper(H) && isket(ψ0) && isnothing(c_ops)) && return sesolveProblem( H, ψ0, @@ -173,10 +170,7 @@ function mesolve( progress_bar::Union{Val,Bool} = Val(true), inplace::Union{Val,Bool} = Val(true), kwargs..., -) where { - HOpType<:Union{OperatorQuantumObject,SuperOperatorQuantumObject}, - StateOpType<:Union{KetQuantumObject,OperatorQuantumObject,OperatorKetQuantumObject}, -} +) where {HOpType<:Union{Operator,SuperOperator},StateOpType<:Union{Ket,Operator,OperatorKet}} (isoper(H) && isket(ψ0) && isnothing(c_ops)) && return sesolve( H, ψ0, @@ -210,9 +204,9 @@ function mesolve(prob::TimeEvolutionProblem, alg::OrdinaryDiffEqAlgorithm = Tsit # No type instabilities since `isoperket` is a Val, and so it is known at compile time if getVal(prob.kwargs.isoperket) - ρt = map(ϕ -> QuantumObject(ϕ, type = OperatorKet, dims = prob.dimensions), sol.u) + ρt = map(ϕ -> QuantumObject(ϕ, type = OperatorKet(), dims = prob.dimensions), sol.u) else - ρt = map(ϕ -> QuantumObject(vec2mat(ϕ), type = Operator, dims = prob.dimensions), sol.u) + ρt = map(ϕ -> QuantumObject(vec2mat(ϕ), type = Operator(), dims = prob.dimensions), sol.u) end return TimeEvolutionSol( diff --git a/src/time_evolution/sesolve.jl b/src/time_evolution/sesolve.jl index 66f1df343..c24970f75 100644 --- a/src/time_evolution/sesolve.jl +++ b/src/time_evolution/sesolve.jl @@ -1,15 +1,17 @@ export sesolveProblem, sesolve _sesolve_make_U_QobjEvo( - H::QuantumObjectEvolution{OperatorQuantumObject,DimsType,<:MatrixOperator}, -) where {DimsType<:AbstractDimensions} = 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) + H::QuantumObjectEvolution{Operator,DimsType,<:MatrixOperator}, +) where {DimsType<:AbstractDimensions} = + 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""" sesolveProblem( - H::Union{AbstractQuantumObject{OperatorQuantumObject},Tuple}, - ψ0::QuantumObject{KetQuantumObject}, + H::Union{AbstractQuantumObject{Operator},Tuple}, + ψ0::QuantumObject{Ket}, tlist::AbstractVector; e_ops::Union{Nothing,AbstractVector,Tuple} = nothing, params = NullParameters(), @@ -47,8 +49,8 @@ Generate the ODEProblem for the Schrödinger time evolution of a quantum system: - `prob`: The [`TimeEvolutionProblem`](@ref) containing the `ODEProblem` for the Schrödinger time evolution of the system. """ function sesolveProblem( - H::Union{AbstractQuantumObject{OperatorQuantumObject},Tuple}, - ψ0::QuantumObject{KetQuantumObject}, + H::Union{AbstractQuantumObject{Operator},Tuple}, + ψ0::QuantumObject{Ket}, tlist::AbstractVector; e_ops::Union{Nothing,AbstractVector,Tuple} = nothing, params = NullParameters(), @@ -86,8 +88,8 @@ end @doc raw""" sesolve( - H::Union{AbstractQuantumObject{OperatorQuantumObject},Tuple}, - ψ0::QuantumObject{KetQuantumObject}, + H::Union{AbstractQuantumObject{Operator},Tuple}, + ψ0::QuantumObject{Ket}, tlist::AbstractVector; alg::OrdinaryDiffEqAlgorithm = Tsit5(), e_ops::Union{Nothing,AbstractVector,Tuple} = nothing, @@ -128,8 +130,8 @@ Time evolution of a closed quantum system using the Schrödinger equation: - `sol::TimeEvolutionSol`: The solution of the time evolution. See also [`TimeEvolutionSol`](@ref) """ function sesolve( - H::Union{AbstractQuantumObject{OperatorQuantumObject},Tuple}, - ψ0::QuantumObject{KetQuantumObject}, + H::Union{AbstractQuantumObject{Operator},Tuple}, + ψ0::QuantumObject{Ket}, tlist::AbstractVector; alg::OrdinaryDiffEqAlgorithm = Tsit5(), e_ops::Union{Nothing,AbstractVector,Tuple} = nothing, @@ -155,7 +157,7 @@ end function sesolve(prob::TimeEvolutionProblem, alg::OrdinaryDiffEqAlgorithm = Tsit5()) sol = solve(prob.prob, alg) - ψt = map(ϕ -> QuantumObject(ϕ, type = Ket, dims = prob.dimensions), sol.u) + ψt = map(ϕ -> QuantumObject(ϕ, type = Ket(), dims = prob.dimensions), sol.u) return TimeEvolutionSol( prob.times, diff --git a/src/time_evolution/smesolve.jl b/src/time_evolution/smesolve.jl index ae28e4716..710029197 100644 --- a/src/time_evolution/smesolve.jl +++ b/src/time_evolution/smesolve.jl @@ -1,7 +1,7 @@ export smesolveProblem, smesolveEnsembleProblem, smesolve -_smesolve_generate_state(u, dims, isoperket::Val{false}) = QuantumObject(vec2mat(u), type = Operator, dims = dims) -_smesolve_generate_state(u, dims, isoperket::Val{true}) = QuantumObject(u, type = OperatorKet, dims = dims) +_smesolve_generate_state(u, dims, isoperket::Val{false}) = QuantumObject(vec2mat(u), type = Operator(), dims = dims) +_smesolve_generate_state(u, dims, isoperket::Val{true}) = QuantumObject(u, type = OperatorKet(), dims = dims) function _smesolve_update_coeff(u, p, t, op_vec) return 2 * real(dot(op_vec, u)) #this is Tr[Sn * ρ + ρ * Sn'] @@ -12,7 +12,7 @@ _smesolve_ScalarOperator(op_vec) = @doc raw""" smesolveProblem( - H::Union{AbstractQuantumObject{OperatorQuantumObject},Tuple}, + H::Union{AbstractQuantumObject{Operator},Tuple}, ψ0::QuantumObject, tlist::AbstractVector, c_ops::Union{Nothing,AbstractVector,Tuple} = nothing, @@ -74,7 +74,7 @@ Above, ``\hat{C}_i`` represent the collapse operators related to pure dissipatio - `prob`: The [`TimeEvolutionProblem`](@ref) containing the `SDEProblem` for the Stochastic Master Equation time evolution. """ function smesolveProblem( - H::Union{AbstractQuantumObject{OperatorQuantumObject},Tuple}, + H::Union{AbstractQuantumObject{Operator},Tuple}, ψ0::QuantumObject{StateOpType}, tlist::AbstractVector, c_ops::Union{Nothing,AbstractVector,Tuple} = nothing, @@ -85,7 +85,7 @@ function smesolveProblem( progress_bar::Union{Val,Bool} = Val(true), store_measurement::Union{Val,Bool} = Val(false), kwargs..., -) where {StateOpType<:Union{KetQuantumObject,OperatorQuantumObject,OperatorKetQuantumObject}} +) where {StateOpType<:Union{Ket,Operator,OperatorKet}} haskey(kwargs, :save_idxs) && throw(ArgumentError("The keyword argument \"save_idxs\" is not supported in QuantumToolbox.")) @@ -153,7 +153,7 @@ end @doc raw""" smesolveEnsembleProblem( - H::Union{AbstractQuantumObject{OperatorQuantumObject},Tuple}, + H::Union{AbstractQuantumObject{Operator},Tuple}, ψ0::QuantumObject, tlist::AbstractVector, c_ops::Union{Nothing,AbstractVector,Tuple} = nothing, @@ -223,7 +223,7 @@ Above, ``\hat{C}_i`` represent the collapse operators related to pure dissipatio - `prob`: The [`TimeEvolutionProblem`](@ref) containing the Ensemble `SDEProblem` for the Stochastic Master Equation time evolution. """ function smesolveEnsembleProblem( - H::Union{AbstractQuantumObject{OperatorQuantumObject},Tuple}, + H::Union{AbstractQuantumObject{Operator},Tuple}, ψ0::QuantumObject{StateOpType}, tlist::AbstractVector, c_ops::Union{Nothing,AbstractVector,Tuple} = nothing, @@ -238,7 +238,7 @@ function smesolveEnsembleProblem( progress_bar::Union{Val,Bool} = Val(true), store_measurement::Union{Val,Bool} = Val(false), kwargs..., -) where {StateOpType<:Union{KetQuantumObject,OperatorQuantumObject,OperatorKetQuantumObject}} +) where {StateOpType<:Union{Ket,Operator,OperatorKet}} _prob_func = isnothing(prob_func) ? _ensemble_dispatch_prob_func( @@ -279,7 +279,7 @@ end @doc raw""" smesolve( - H::Union{AbstractQuantumObject{OperatorQuantumObject},Tuple}, + H::Union{AbstractQuantumObject{Operator},Tuple}, ψ0::QuantumObject, tlist::AbstractVector, c_ops::Union{Nothing,AbstractVector,Tuple} = nothing, @@ -351,7 +351,7 @@ Above, ``\hat{C}_i`` represent the collapse operators related to pure dissipatio - `sol::TimeEvolutionStochasticSol`: The solution of the time evolution. See [`TimeEvolutionStochasticSol`](@ref). """ function smesolve( - H::Union{AbstractQuantumObject{OperatorQuantumObject},Tuple}, + H::Union{AbstractQuantumObject{Operator},Tuple}, ψ0::QuantumObject{StateOpType}, tlist::AbstractVector, c_ops::Union{Nothing,AbstractVector,Tuple} = nothing, @@ -367,7 +367,7 @@ function smesolve( progress_bar::Union{Val,Bool} = Val(true), store_measurement::Union{Val,Bool} = Val(false), kwargs..., -) where {StateOpType<:Union{KetQuantumObject,OperatorQuantumObject,OperatorKetQuantumObject}} +) where {StateOpType<:Union{Ket,Operator,OperatorKet}} ensemble_prob = smesolveEnsembleProblem( H, ψ0, diff --git a/src/time_evolution/ssesolve.jl b/src/time_evolution/ssesolve.jl index b29925481..6945dc81e 100644 --- a/src/time_evolution/ssesolve.jl +++ b/src/time_evolution/ssesolve.jl @@ -14,8 +14,8 @@ _ScalarOperator_e2_2(op, f = +) = @doc raw""" ssesolveProblem( - H::Union{AbstractQuantumObject{OperatorQuantumObject},Tuple}, - ψ0::QuantumObject{KetQuantumObject}, + H::Union{AbstractQuantumObject{Operator},Tuple}, + ψ0::QuantumObject{Ket}, tlist::AbstractVector, sc_ops::Union{Nothing,AbstractVector,Tuple,AbstractQuantumObject} = nothing; e_ops::Union{Nothing,AbstractVector,Tuple} = nothing, @@ -75,8 +75,8 @@ Above, ``\hat{S}_n`` are the stochastic collapse operators and ``dW_n(t)`` is th - `prob`: The `SDEProblem` for the Stochastic Schrödinger time evolution of the system. """ function ssesolveProblem( - H::Union{AbstractQuantumObject{OperatorQuantumObject},Tuple}, - ψ0::QuantumObject{KetQuantumObject}, + H::Union{AbstractQuantumObject{Operator},Tuple}, + ψ0::QuantumObject{Ket}, tlist::AbstractVector, sc_ops::Union{Nothing,AbstractVector,Tuple,AbstractQuantumObject} = nothing; e_ops::Union{Nothing,AbstractVector,Tuple} = nothing, @@ -148,8 +148,8 @@ end @doc raw""" ssesolveEnsembleProblem( - H::Union{AbstractQuantumObject{OperatorQuantumObject},Tuple}, - ψ0::QuantumObject{KetQuantumObject}, + H::Union{AbstractQuantumObject{Operator},Tuple}, + ψ0::QuantumObject{Ket}, tlist::AbstractVector, sc_ops::Union{Nothing,AbstractVector,Tuple,AbstractQuantumObject} = nothing; e_ops::Union{Nothing,AbstractVector,Tuple} = nothing, @@ -218,8 +218,8 @@ Above, ``\hat{S}_n`` are the stochastic collapse operators and ``dW_n(t)`` is t - `prob::EnsembleProblem with SDEProblem`: The Ensemble SDEProblem for the Stochastic Shrödinger time evolution. """ function ssesolveEnsembleProblem( - H::Union{AbstractQuantumObject{OperatorQuantumObject},Tuple}, - ψ0::QuantumObject{KetQuantumObject}, + H::Union{AbstractQuantumObject{Operator},Tuple}, + ψ0::QuantumObject{Ket}, tlist::AbstractVector, sc_ops::Union{Nothing,AbstractVector,Tuple,AbstractQuantumObject} = nothing; e_ops::Union{Nothing,AbstractVector,Tuple} = nothing, @@ -272,8 +272,8 @@ end @doc raw""" ssesolve( - H::Union{AbstractQuantumObject{OperatorQuantumObject},Tuple}, - ψ0::QuantumObject{KetQuantumObject}, + H::Union{AbstractQuantumObject{Operator},Tuple}, + ψ0::QuantumObject{Ket}, tlist::AbstractVector, sc_ops::Union{Nothing,AbstractVector,Tuple,AbstractQuantumObject} = nothing; alg::Union{Nothing,StochasticDiffEqAlgorithm} = nothing, @@ -347,8 +347,8 @@ Above, ``\hat{S}_n`` are the stochastic collapse operators and ``dW_n(t)`` is th - `sol::TimeEvolutionStochasticSol`: The solution of the time evolution. See [`TimeEvolutionStochasticSol`](@ref). """ function ssesolve( - H::Union{AbstractQuantumObject{OperatorQuantumObject},Tuple}, - ψ0::QuantumObject{KetQuantumObject}, + H::Union{AbstractQuantumObject{Operator},Tuple}, + ψ0::QuantumObject{Ket}, tlist::AbstractVector, sc_ops::Union{Nothing,AbstractVector,Tuple,AbstractQuantumObject} = nothing; alg::Union{Nothing,StochasticDiffEqAlgorithm} = nothing, diff --git a/src/time_evolution/time_evolution.jl b/src/time_evolution/time_evolution.jl index 2ab50893a..30730bc9d 100644 --- a/src/time_evolution/time_evolution.jl +++ b/src/time_evolution/time_evolution.jl @@ -423,9 +423,9 @@ end ####################################### function liouvillian_floquet( - L₀::QuantumObject{SuperOperatorQuantumObject}, - Lₚ::QuantumObject{SuperOperatorQuantumObject}, - Lₘ::QuantumObject{SuperOperatorQuantumObject}, + L₀::QuantumObject{SuperOperator}, + Lₚ::QuantumObject{SuperOperator}, + Lₘ::QuantumObject{SuperOperator}, ω::Real; n_max::Int = 3, tol::Real = 1e-15, @@ -443,9 +443,9 @@ function liouvillian_floquet( n_max::Int = 3, tol::Real = 1e-15, ) where { - OpType1<:Union{OperatorQuantumObject,SuperOperatorQuantumObject}, - OpType2<:Union{OperatorQuantumObject,SuperOperatorQuantumObject}, - OpType3<:Union{OperatorQuantumObject,SuperOperatorQuantumObject}, + OpType1<:Union{Operator,SuperOperator}, + OpType2<:Union{Operator,SuperOperator}, + OpType3<:Union{Operator,SuperOperator}, } return liouvillian_floquet(liouvillian(H, c_ops), liouvillian(Hₚ), liouvillian(Hₘ), ω, n_max = n_max, tol = tol) end @@ -459,7 +459,7 @@ Constructs the generalized Liouvillian for a system coupled to a bath of harmoni See, e.g., Settineri, Alessio, et al. "Dissipation and thermal noise in hybrid quantum systems in the ultrastrong-coupling regime." Physical Review A 98.5 (2018): 053834. """ function liouvillian_generalized( - H::QuantumObject{OperatorQuantumObject}, + H::QuantumObject{Operator}, fields::Vector, T_list::Vector{<:Real}; N_trunc::Union{Int,Nothing} = nothing, @@ -474,14 +474,14 @@ function liouvillian_generalized( E = real.(result.values[1:final_size]) U = QuantumObject(result.vectors, result.type, result.dimensions) - H_d = QuantumObject(Diagonal(complex(E)), type = Operator, dims = dims) + H_d = QuantumObject(Diagonal(complex(E)), type = Operator(), dims = dims) Ω = E' .- E Ωp = triu(to_sparse(Ω, tol), 1) # Filter in the Hilbert space σ = isnothing(σ_filter) ? 500 * maximum([norm(field) / length(field) for field in fields]) : σ_filter - F1 = QuantumObject(gaussian.(Ω, 0, σ), type = Operator, dims = dims) + F1 = QuantumObject(gaussian.(Ω, 0, σ), type = Operator(), dims = dims) F1 = to_sparse(F1, tol) # Filter in the Liouville space @@ -491,7 +491,7 @@ function liouvillian_generalized( Ω1 = kron(Ω, M1) Ω2 = kron(M1, Ω) Ωdiff = Ω1 .- Ω2 - F2 = QuantumObject(gaussian.(Ωdiff, 0, σ), SuperOperator, dims) + F2 = QuantumObject(gaussian.(Ωdiff, 0, σ), SuperOperator(), dims) F2 = to_sparse(F2, tol) L = liouvillian(H_d) @@ -505,9 +505,9 @@ function liouvillian_generalized( # Ohmic reservoir N_th = n_thermal.(Ωp, T_list[i]) - Sp₀ = QuantumObject(triu(X_op, 1), type = Operator, dims = dims) - Sp₁ = QuantumObject(droptol!((@. Ωp * N_th * Sp₀.data), tol), type = Operator, dims = dims) - Sp₂ = QuantumObject(droptol!((@. Ωp * (1 + N_th) * Sp₀.data), tol), type = Operator, dims = dims) + Sp₀ = QuantumObject(triu(X_op, 1), type = Operator(), dims = dims) + Sp₁ = QuantumObject(droptol!((@. Ωp * N_th * Sp₀.data), tol), type = Operator(), dims = dims) + Sp₂ = QuantumObject(droptol!((@. Ωp * (1 + N_th) * Sp₀.data), tol), type = Operator(), dims = dims) # S0 = QuantumObject( spdiagm(diag(X_op)), dims=dims ) L += @@ -522,9 +522,9 @@ function liouvillian_generalized( end function _liouvillian_floquet( - L₀::QuantumObject{SuperOperatorQuantumObject}, - Lₚ::QuantumObject{SuperOperatorQuantumObject}, - Lₘ::QuantumObject{SuperOperatorQuantumObject}, + L₀::QuantumObject{SuperOperator}, + Lₚ::QuantumObject{SuperOperator}, + Lₘ::QuantumObject{SuperOperator}, ω::Real, n_max::Int, tol::Real, @@ -543,6 +543,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₀.dimensions) - return QuantumObject(to_sparse(L_0 + L_m * S + L_p * T, tol), SuperOperator, L₀.dimensions) + tol == 0 && return QuantumObject(L_0 + L_m * S + L_p * T, SuperOperator(), L₀.dimensions) + return QuantumObject(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 e46633239..2e07047da 100644 --- a/src/time_evolution/time_evolution_dynamical.jl +++ b/src/time_evolution/time_evolution_dynamical.jl @@ -148,7 +148,7 @@ function dfd_mesolveProblem( params::NamedTuple = NamedTuple(), tol_list::Vector{<:Number} = fill(1e-8, length(maxdims)), kwargs..., -) where {T2<:Integer,StateOpType<:Union{KetQuantumObject,OperatorQuantumObject}} +) where {T2<:Integer,StateOpType<:Union{Ket,Operator}} length(ψ0.dimensions) != length(maxdims) && throw(DimensionMismatch("`dim_list` and `maxdims` do not have the same dimension.")) @@ -219,7 +219,7 @@ function dfd_mesolve( params::NamedTuple = NamedTuple(), tol_list::Vector{<:Number} = fill(1e-8, length(maxdims)), kwargs..., -) where {T2<:Integer,StateOpType<:Union{KetQuantumObject,OperatorQuantumObject}} +) where {T2<:Integer,StateOpType<:Union{Ket,Operator}} dfd_prob = dfd_mesolveProblem( H, ψ0, @@ -239,7 +239,7 @@ function dfd_mesolve( idx = findfirst(>=(sol.t[i]), sol.prob.p.dim_list_evo_times) idx2 = isnothing(idx) ? length(sol.prob.p.dim_list_evo) : (idx == 1 ? 1 : idx - 1) - return QuantumObject(vec2mat(sol.u[i]), type = Operator, dims = sol.prob.p.dim_list_evo[idx2]) + return QuantumObject(vec2mat(sol.u[i]), type = Operator(), dims = sol.prob.p.dim_list_evo[idx2]) end return TimeEvolutionSol( @@ -350,7 +350,7 @@ function dsf_mesolveProblem( δα_list::Vector{<:Real} = fill(0.2, length(op_list)), krylov_dim::Int = max(6, min(10, cld(length(ket2dm(ψ0).data), 4))), kwargs..., -) where {StateOpType<:Union{KetQuantumObject,OperatorQuantumObject}} +) where {StateOpType<:Union{Ket,Operator}} op_list = deepcopy(op_list) H₀ = H(op_list .+ α0_l, dsf_params) c_ops₀ = c_ops(op_list .+ α0_l, dsf_params) @@ -433,7 +433,7 @@ function dsf_mesolve( δα_list::Vector{<:Real} = fill(0.2, length(op_list)), krylov_dim::Int = max(6, min(10, cld(length(ket2dm(ψ0).data), 4))), kwargs..., -) where {StateOpType<:Union{KetQuantumObject,OperatorQuantumObject}} +) where {StateOpType<:Union{Ket,Operator}} dsf_prob = dsf_mesolveProblem( H, ψ0, @@ -465,7 +465,7 @@ function dsf_mesolve( δα_list::Vector{<:Real} = fill(0.2, length(op_list)), krylov_dim::Int = max(6, min(10, cld(length(ket2dm(ψ0).data), 4))), kwargs..., -) where {StateOpType<:Union{KetQuantumObject,OperatorQuantumObject}} +) where {StateOpType<:Union{Ket,Operator}} c_ops = op_list -> () return dsf_mesolve( H, @@ -599,7 +599,7 @@ end function dsf_mcsolveEnsembleProblem( H::Function, - ψ0::QuantumObject{KetQuantumObject}, + ψ0::QuantumObject{Ket}, tlist::AbstractVector, c_ops::Function, op_list::Union{AbstractVector,Tuple}, @@ -694,7 +694,7 @@ Time evolution of a quantum system using the Monte Carlo wave function method an """ function dsf_mcsolve( H::Function, - ψ0::QuantumObject{KetQuantumObject}, + ψ0::QuantumObject{Ket}, tlist::AbstractVector, c_ops::Function, op_list::Union{AbstractVector,Tuple}, diff --git a/src/visualization.jl b/src/visualization.jl index f24485228..e43fb10dd 100644 --- a/src/visualization.jl +++ b/src/visualization.jl @@ -5,7 +5,7 @@ export plot_wigner, plot_fock_distribution state::QuantumObject{OpType}; library::Union{Val,Symbol}=Val(:Makie), kwargs... - ) where {OpType<:Union{BraQuantumObject,KetQuantumObject,OperatorQuantumObject} + ) where {OpType<:Union{Bra,Ket,Operator} Plot the [Wigner quasipropability distribution](https://en.wikipedia.org/wiki/Wigner_quasiprobability_distribution) of `state` using the [`wigner`](@ref) function. @@ -26,14 +26,9 @@ plot_wigner( state::QuantumObject{OpType}; library::Union{Val,Symbol} = Val(:Makie), kwargs..., -) where {OpType<:Union{BraQuantumObject,KetQuantumObject,OperatorQuantumObject}} = - plot_wigner(makeVal(library), state; kwargs...) +) where {OpType<:Union{Bra,Ket,Operator}} = plot_wigner(makeVal(library), state; kwargs...) -plot_wigner( - ::Val{T}, - state::QuantumObject{OpType}; - kwargs..., -) where {T,OpType<:Union{BraQuantumObject,KetQuantumObject,OperatorQuantumObject}} = +plot_wigner(::Val{T}, state::QuantumObject{OpType}; kwargs...) where {T,OpType<:Union{Bra,Ket,Operator}} = throw(ArgumentError("The specified plotting library $T is not available. Try running `using $T` first.")) @doc raw""" @@ -41,7 +36,7 @@ plot_wigner( ρ::QuantumObject{SType}; library::Union{Val, Symbol} = Val(:Makie), kwargs... - ) where {SType<:Union{KetQuantumObject,OperatorQuantumObject}} + ) where {SType<:Union{Ket,Operator}} Plot the [Fock state](https://en.wikipedia.org/wiki/Fock_state) distribution of `ρ`. @@ -62,12 +57,7 @@ plot_fock_distribution( ρ::QuantumObject{SType}; library::Union{Val,Symbol} = Val(:Makie), kwargs..., -) where {SType<:Union{BraQuantumObject,KetQuantumObject,OperatorQuantumObject}} = - plot_fock_distribution(makeVal(library), ρ; kwargs...) +) where {SType<:Union{Bra,Ket,Operator}} = plot_fock_distribution(makeVal(library), ρ; kwargs...) -plot_fock_distribution( - ::Val{T}, - ρ::QuantumObject{SType}; - kwargs..., -) where {T,SType<:Union{BraQuantumObject,KetQuantumObject,OperatorQuantumObject}} = +plot_fock_distribution(::Val{T}, ρ::QuantumObject{SType}; kwargs...) where {T,SType<:Union{Bra,Ket,Operator}} = throw(ArgumentError("The specified plotting library $T is not available. Try running `using $T` first.")) diff --git a/src/wigner.jl b/src/wigner.jl index 813457b6e..b4954a9a4 100644 --- a/src/wigner.jl +++ b/src/wigner.jl @@ -25,7 +25,7 @@ Generates the [Wigner quasipropability distribution](https://en.wikipedia.org/wi The `method` parameter can be either `WignerLaguerre()` or `WignerClenshaw()`. The former uses the Laguerre polynomial expansion of the Wigner function, while the latter uses the Clenshaw algorithm. The Laguerre expansion is faster for sparse matrices, while the Clenshaw algorithm is faster for dense matrices. The `WignerLaguerre` method has an optional `parallel` parameter which defaults to `true` and uses multithreading to speed up the calculation. # Arguments -- `state::QuantumObject`: The quantum state for which the Wigner function is calculated. It can be either a [`KetQuantumObject`](@ref), [`BraQuantumObject`](@ref), or [`OperatorQuantumObject`](@ref). +- `state::QuantumObject`: The quantum state for which the Wigner function is calculated. It can be either a [`Ket`](@ref), [`Bra`](@ref), or [`Operator`](@ref). - `xvec::AbstractVector`: The x-coordinates of the phase space grid. - `yvec::AbstractVector`: The y-coordinates of the phase space grid. - `g::Real`: The scaling factor related to the value of ``\hbar`` in the commutation relation ``[x, y] = i \hbar`` via ``\hbar=2/g^2``. @@ -38,7 +38,7 @@ The `method` parameter can be either `WignerLaguerre()` or `WignerClenshaw()`. T ```jldoctest wigner julia> ψ = fock(10, 0) + fock(10, 1) |> normalize -Quantum Object: type=Ket dims=[10] size=(10,) +Quantum Object: type=Ket() dims=[10] size=(10,) 10-element Vector{ComplexF64}: 0.7071067811865475 + 0.0im 0.7071067811865475 + 0.0im @@ -72,7 +72,7 @@ function wigner( yvec::AbstractVector; g::Real = √2, method::WignerSolver = WignerClenshaw(), -) where {OpType<:Union{BraQuantumObject,KetQuantumObject,OperatorQuantumObject}} +) where {OpType<:Union{Bra,Ket,Operator}} ρ = ket2dm(state).data return _wigner(ρ, xvec, yvec, g, method) diff --git a/test/core-test/eigenvalues_and_operators.jl b/test/core-test/eigenvalues_and_operators.jl index 0b4959beb..a2fe6bcc5 100644 --- a/test/core-test/eigenvalues_and_operators.jl +++ b/test/core-test/eigenvalues_and_operators.jl @@ -8,14 +8,14 @@ λs, ψs, Ts = eigenstates(σx, sparse = true, eigvals = 2) λs1, ψs1, Ts1 = eigenstates(σx, sparse = true, eigvals = 1) - @test all([ψ.type isa KetQuantumObject for ψ in ψd]) + @test all([ψ.type isa Ket for ψ in ψd]) @test typeof(Td) <: AbstractMatrix @test typeof(Ts) <: AbstractMatrix @test typeof(Ts1) <: AbstractMatrix @test all(abs.(eigenenergies(σx, sparse = false)) .≈ abs.(λd)) @test all(abs.(eigenenergies(σx, sparse = true, eigvals = 2)) .≈ abs.(λs)) @test resstring == - "EigsolveResult: type=$(Operator) dims=$(result.dims)\nvalues:\n$(valstring)\nvectors:\n$vecsstring" + "EigsolveResult: type=$(Operator()) dims=$(result.dims)\nvalues:\n$(valstring)\nvectors:\n$vecsstring" N = 30 a = kron(destroy(N), qeye(2)) @@ -76,16 +76,16 @@ valstring = sprint((t, s) -> show(t, "text/plain", s), result.values) vecsstring = sprint((t, s) -> show(t, "text/plain", s), result.vectors) @test resstring == - "EigsolveResult: type=$(SuperOperator) dims=$(result.dims)\nvalues:\n$(valstring)\nvectors:\n$vecsstring" + "EigsolveResult: type=$(SuperOperator()) dims=$(result.dims)\nvalues:\n$(valstring)\nvectors:\n$vecsstring" vals2, vecs2 = eigenstates(L, sparse = false) idxs = sortperm(vals2, by = abs) vals2 = vals2[idxs][1:10] vecs2 = vecs2[idxs][1:10] - @test result.type isa SuperOperatorQuantumObject + @test result.type isa SuperOperator @test result.dims == L.dims - @test all([v.type isa OperatorKetQuantumObject for v in vecs]) + @test all([v.type isa OperatorKet for v in vecs]) @test typeof(result.vectors) <: AbstractMatrix @test isapprox(sum(abs2, vals), sum(abs2, vals2), atol = 1e-7) @test isapprox(abs2(vals2[1]), abs2(vals3[1]), atol = 1e-7) diff --git a/test/core-test/low_rank_dynamics.jl b/test/core-test/low_rank_dynamics.jl index 27066cdb1..07faf788f 100644 --- a/test/core-test/low_rank_dynamics.jl +++ b/test/core-test/low_rank_dynamics.jl @@ -9,7 +9,7 @@ M = latt.N + 1 # Number of states in the LR basis # Define initial state - ϕ = Vector{QuantumObject{KetQuantumObject,Dimensions{M - 1,NTuple{M - 1,Space}},Vector{ComplexF64}}}(undef, M) + ϕ = Vector{QuantumObject{Ket,Dimensions{M - 1,NTuple{M - 1,Space}},Vector{ComplexF64}}}(undef, M) ϕ[1] = kron(fill(basis(2, 1), N_modes)...) i = 1 @@ -68,7 +68,7 @@ mul!(C, z, sqrt(B)) mul!(σ, C', C) - return entropy_vn(Qobj(Hermitian(σ), type = Operator), base = 2) + return entropy_vn(Qobj(Hermitian(σ), type = Operator()), base = 2) end opt = (err_max = 1e-3, p0 = 0.0, atol_inv = 1e-6, adj_condition = "variational", Δt = 0.0, progress = false) diff --git a/test/core-test/quantum_objects.jl b/test/core-test/quantum_objects.jl index 0730b069f..c01eeca33 100644 --- a/test/core-test/quantum_objects.jl +++ b/test/core-test/quantum_objects.jl @@ -2,38 +2,38 @@ # ArgumentError: type is incompatible with vector or matrix @testset "ArgumentError" begin a = rand(ComplexF64, 2) - for t in [Operator, SuperOperator, Bra, OperatorBra] + for t in (Operator(), SuperOperator(), Bra(), OperatorBra()) @test_throws ArgumentError Qobj(a, type = t) end a = rand(ComplexF64, 2, 2) - @test_throws ArgumentError Qobj(a, type = Ket) - @test_throws ArgumentError Qobj(a, type = OperatorKet) + @test_throws ArgumentError Qobj(a, type = Ket()) + @test_throws ArgumentError Qobj(a, type = OperatorKet()) end # DomainError: incompatible between size of array and type @testset "DomainError" begin a = rand(ComplexF64, 3, 2) - for t in [SuperOperator, Bra, OperatorBra] + for t in [SuperOperator(), Bra(), OperatorBra()] @test_throws DomainError Qobj(a, type = t) end a = rand(ComplexF64, 2, 2, 2) - for t in [nothing, Ket, Bra, Operator, SuperOperator, OperatorBra, OperatorKet] + for t in (nothing, Ket(), Bra(), Operator(), SuperOperator(), OperatorBra(), OperatorKet()) @test_throws DomainError Qobj(a, type = t) end a = rand(ComplexF64, 1, 2) - @test_throws DomainError Qobj(a, type = Operator) - @test_throws DomainError Qobj(a, type = SuperOperator) + @test_throws DomainError Qobj(a, type = Operator()) + @test_throws DomainError Qobj(a, type = SuperOperator()) - @test_throws DomainError Qobj(rand(ComplexF64, 2, 1), type = Operator) # should be type = Bra + @test_throws DomainError Qobj(rand(ComplexF64, 2, 1), type = Operator()) # should be type = Bra # check that Ket, Bra, SuperOperator, OperatorKet, and OperatorBra don't support GeneralDimensions - @test_throws DomainError Qobj(rand(ComplexF64, 2), type = Ket, dims = ((2,), (1,))) - @test_throws DomainError Qobj(rand(ComplexF64, 1, 2), type = Bra, dims = ((1,), (2,))) - @test_throws DomainError Qobj(rand(ComplexF64, 4, 4), type = SuperOperator, dims = ((2,), (2,))) - @test_throws DomainError Qobj(rand(ComplexF64, 4), type = OperatorKet, dims = ((2,), (1,))) - @test_throws DomainError Qobj(rand(ComplexF64, 1, 4), type = OperatorBra, dims = ((1,), (2,))) + @test_throws DomainError Qobj(rand(ComplexF64, 2), type = Ket(), dims = ((2,), (1,))) + @test_throws DomainError Qobj(rand(ComplexF64, 1, 2), type = Bra(), dims = ((1,), (2,))) + @test_throws DomainError Qobj(rand(ComplexF64, 4, 4), type = SuperOperator(), dims = ((2,), (2,))) + @test_throws DomainError Qobj(rand(ComplexF64, 4), type = OperatorKet(), dims = ((2,), (1,))) + @test_throws DomainError Qobj(rand(ComplexF64, 1, 4), type = OperatorBra(), dims = ((1,), (2,))) end # unsupported type of dims @@ -84,7 +84,7 @@ a = sprand(ComplexF64, 100, 100, 0.1) a2 = Qobj(a) - a3 = Qobj(a, type = SuperOperator) + a3 = Qobj(a, type = SuperOperator()) a4 = Qobj(sprand(ComplexF64, 100, 10, 0.1)) # GeneralDimensions a5 = QuantumObject(rand(ComplexF64, 2*3*4, 5), dims = ((2, 3, 4), (5,))) @test isket(a2) == false @@ -130,7 +130,7 @@ ρ = Qobj(rand(ComplexF64, 2, 2)) ρ_ket = operator_to_vector(ρ) ρ_bra = ρ_ket' - @test ρ_bra == Qobj(operator_to_vector(ρ.data)', type = OperatorBra) + @test ρ_bra == Qobj(operator_to_vector(ρ.data)', type = OperatorBra()) @test ρ == vector_to_operator(ρ_ket) @test isket(ρ_ket) == false @test isbra(ρ_ket) == false @@ -155,8 +155,8 @@ @test L * ρ_ket ≈ -1im * (+(spre(H) * ρ_ket) - spost(H) * ρ_ket) @test (ρ_bra * L')' == L * ρ_ket @test sum((conj(ρ) .* ρ).data) ≈ dot(ρ_ket, ρ_ket) ≈ ρ_bra * ρ_ket - @test_throws DimensionMismatch Qobj(ρ_ket.data, type = OperatorKet, dims = 4) - @test_throws DimensionMismatch Qobj(ρ_bra.data, type = OperatorBra, dims = 4) + @test_throws DimensionMismatch Qobj(ρ_ket.data, type = OperatorKet(), dims = 4) + @test_throws DimensionMismatch Qobj(ρ_bra.data, type = OperatorBra(), dims = 4) end @testset "Checks on non-QuantumObjects" begin @@ -180,7 +180,7 @@ @testset "arithmetic" begin a = sprand(ComplexF64, 100, 100, 0.1) a2 = Qobj(a) - a3 = Qobj(a, type = SuperOperator) + a3 = Qobj(a, type = SuperOperator()) a4 = to_sparse(a2) @test isequal(a4, a2) == true @test isequal(a4, a3) == false @@ -262,7 +262,7 @@ a_size = size(a) a_isherm = isherm(a) @test opstring == - "\nQuantum Object: type=Operator dims=$a_dims size=$a_size ishermitian=$a_isherm\n$datastring" + "\nQuantum Object: type=Operator() dims=$a_dims size=$a_size ishermitian=$a_isherm\n$datastring" # GeneralDimensions Gop = tensor(a, ψ) @@ -272,7 +272,7 @@ Gop_size = size(Gop) Gop_isherm = isherm(Gop) @test opstring == - "\nQuantum Object: type=Operator dims=$Gop_dims size=$Gop_size ishermitian=$Gop_isherm\n$datastring" + "\nQuantum Object: type=Operator() dims=$Gop_dims size=$Gop_size ishermitian=$Gop_isherm\n$datastring" a = spre(a) opstring = sprint((t, s) -> show(t, "text/plain", s), a) @@ -280,42 +280,42 @@ a_dims = a.dims a_size = size(a) a_isherm = isherm(a) - @test opstring == "\nQuantum Object: type=SuperOperator dims=$a_dims size=$a_size\n$datastring" + @test opstring == "\nQuantum Object: type=SuperOperator() dims=$a_dims size=$a_size\n$datastring" opstring = sprint((t, s) -> show(t, "text/plain", s), ψ) datastring = sprint((t, s) -> show(t, "text/plain", s), ψ.data) ψ_dims = ψ.dims ψ_size = size(ψ) - @test opstring == "\nQuantum Object: type=Ket dims=$ψ_dims size=$ψ_size\n$datastring" + @test opstring == "\nQuantum Object: type=Ket() dims=$ψ_dims size=$ψ_size\n$datastring" ψ = ψ' opstring = sprint((t, s) -> show(t, "text/plain", s), ψ) datastring = sprint((t, s) -> show(t, "text/plain", s), ψ.data) ψ_dims = ψ.dims ψ_size = size(ψ) - @test opstring == "\nQuantum Object: type=Bra dims=$ψ_dims size=$ψ_size\n$datastring" + @test opstring == "\nQuantum Object: type=Bra() dims=$ψ_dims size=$ψ_size\n$datastring" - ψ2 = Qobj(rand(ComplexF64, 4), type = OperatorKet) + ψ2 = Qobj(rand(ComplexF64, 4), type = OperatorKet()) opstring = sprint((t, s) -> show(t, "text/plain", s), ψ2) datastring = sprint((t, s) -> show(t, "text/plain", s), ψ2.data) ψ2_dims = ψ2.dims ψ2_size = size(ψ2) - @test opstring == "\nQuantum Object: type=OperatorKet dims=$ψ2_dims size=$ψ2_size\n$datastring" + @test opstring == "\nQuantum Object: type=OperatorKet() dims=$ψ2_dims size=$ψ2_size\n$datastring" ψ2 = ψ2' opstring = sprint((t, s) -> show(t, "text/plain", s), ψ2) datastring = sprint((t, s) -> show(t, "text/plain", s), ψ2.data) ψ2_dims = ψ2.dims ψ2_size = size(ψ2) - @test opstring == "\nQuantum Object: type=OperatorBra dims=$ψ2_dims size=$ψ2_size\n$datastring" + @test opstring == "\nQuantum Object: type=OperatorBra() dims=$ψ2_dims size=$ψ2_size\n$datastring" end @testset "matrix element" begin H = Qobj([1 2; 3 4]) L = liouvillian(H) - s0 = Qobj(basis(4, 0).data; type = OperatorKet) - s1 = Qobj(basis(4, 1).data; type = OperatorKet) - s_wrong = Qobj(basis(9, 0).data; type = OperatorKet) + s0 = Qobj(basis(4, 0).data; type = OperatorKet()) + s1 = Qobj(basis(4, 1).data; type = OperatorKet()) + s_wrong = Qobj(basis(9, 0).data; type = OperatorKet()) @test matrix_element(basis(2, 0), H, basis(2, 1)) == H[1, 2] @test matrix_element(s0, L, s1) == L[1, 2] @test_throws DimensionMismatch matrix_element(basis(3, 0), H, basis(2, 1)) @@ -351,32 +351,32 @@ end @testset "Type Inference (QuantumObject)" begin - for T in [ComplexF32, ComplexF64] + for T in (ComplexF32, ComplexF64) N = 4 a = rand(T, N) @inferred Qobj(a) - for type in [Ket, OperatorKet] + for type in (Ket(), OperatorKet()) @inferred Qobj(a, type = type) end UnionType = Union{ - QuantumObject{BraQuantumObject,Dimensions{1,Tuple{Space}},Matrix{T}}, - QuantumObject{OperatorQuantumObject,Dimensions{1,Tuple{Space}},Matrix{T}}, + QuantumObject{Bra,Dimensions{1,Tuple{Space}},Matrix{T}}, + QuantumObject{Operator,Dimensions{1,Tuple{Space}},Matrix{T}}, } a = rand(T, 1, N) @inferred UnionType Qobj(a) - for type in [Bra, OperatorBra] + for type in (Bra(), OperatorBra()) @inferred Qobj(a, type = type) end UnionType2 = Union{ - QuantumObject{OperatorQuantumObject,GeneralDimensions{1,1,Tuple{Space},Tuple{Space}},Matrix{T}}, - QuantumObject{OperatorQuantumObject,Dimensions{1,Tuple{Space}},Matrix{T}}, + QuantumObject{Operator,GeneralDimensions{1,1,Tuple{Space},Tuple{Space}},Matrix{T}}, + QuantumObject{Operator,Dimensions{1,Tuple{Space}},Matrix{T}}, } a = rand(T, N, N) @inferred UnionType Qobj(a) - @inferred UnionType2 Qobj(a, type = Operator) - @inferred Qobj(a, type = SuperOperator) + @inferred UnionType2 Qobj(a, type = Operator()) + @inferred Qobj(a, type = SuperOperator()) end @testset "Math Operation" begin diff --git a/test/core-test/quantum_objects_evo.jl b/test/core-test/quantum_objects_evo.jl index 1e923cf31..14e5a3270 100644 --- a/test/core-test/quantum_objects_evo.jl +++ b/test/core-test/quantum_objects_evo.jl @@ -2,21 +2,21 @@ # DomainError: incompatible between size of array and type @testset "Thrown Errors" begin a = MatrixOperator(rand(ComplexF64, 3, 2)) - @test_throws DomainError QobjEvo(a, type = SuperOperator) + @test_throws DomainError QobjEvo(a, type = SuperOperator()) a = MatrixOperator(rand(ComplexF64, 4, 4)) - @test_throws DomainError QobjEvo(a, type = SuperOperator, dims = ((2,), (2,))) + @test_throws DomainError QobjEvo(a, type = SuperOperator(), dims = ((2,), (2,))) a = MatrixOperator(rand(ComplexF64, 3, 2)) - for t in (Ket, Bra, OperatorKet, OperatorBra) + for t in (Ket(), Bra(), OperatorKet(), OperatorBra()) @test_throws ArgumentError QobjEvo(a, type = t) end a = QobjEvo(destroy(20)) - @test_throws ArgumentError QobjEvo(a, type = SuperOperator) + @test_throws ArgumentError QobjEvo(a, type = SuperOperator()) a = MatrixOperator(rand(ComplexF64, 5, 5)) - @test_throws DimensionMismatch QobjEvo(a, type = SuperOperator) + @test_throws DimensionMismatch QobjEvo(a, type = SuperOperator()) ψ = fock(10, 3) @test_throws MethodError QobjEvo(ψ) @@ -38,7 +38,7 @@ @testset "Operator and SuperOperator" begin a = MatrixOperator(sprand(ComplexF64, 100, 100, 0.1)) a2 = QobjEvo(a) - a3 = QobjEvo(a, type = SuperOperator) + a3 = QobjEvo(a, type = SuperOperator()) @test isket(a2) == false @test isbra(a2) == false @@ -67,7 +67,7 @@ @testset "arithmetic" begin a = MatrixOperator(sprand(ComplexF64, 100, 100, 0.1)) a2 = QobjEvo(a) - a3 = QobjEvo(a, type = SuperOperator) + a3 = QobjEvo(a, type = SuperOperator()) @test +a2 == a2 @test -(-a2) == a2 @@ -91,7 +91,7 @@ N = 10 # We use MatrixOperator instead of directly using a Qobj to increase coverage - a = QobjEvo(MatrixOperator(sprand(ComplexF64, N, N, 5 / N)), Operator, N) + a = QobjEvo(MatrixOperator(sprand(ComplexF64, N, N, 5 / N)), Operator(), N) a_d = a' X = a + a_d # Y = 1im * (a - a_d) # Currently doesn't work. Fix in SciMLOperators.jl @@ -115,7 +115,7 @@ H_isherm = isherm(H) H_isconst = isconstant(H) @test opstring == - "\nQuantum Object Evo.: type=Operator dims=$H_dims size=$H_size ishermitian=$H_isherm isconstant=$H_isconst\n$datastring" + "\nQuantum Object Evo.: type=Operator() dims=$H_dims size=$H_size ishermitian=$H_isherm isconstant=$H_isconst\n$datastring" L = QobjEvo(spre(a)) opstring = sprint((t, s) -> show(t, "text/plain", s), L) @@ -125,7 +125,7 @@ L_isherm = isherm(L) L_isconst = isconstant(L) @test opstring == - "\nQuantum Object Evo.: type=SuperOperator dims=$L_dims size=$L_size ishermitian=$L_isherm isconstant=$L_isconst\n$datastring" + "\nQuantum Object Evo.: type=SuperOperator() dims=$L_dims size=$L_size ishermitian=$L_isherm isconstant=$L_isconst\n$datastring" end @testset "Type Inference (QobjEvo)" begin @@ -133,12 +133,12 @@ for T in [ComplexF32, ComplexF64] a = MatrixOperator(rand(T, N, N)) UnionType = Union{ - QuantumObjectEvolution{OperatorQuantumObject,GeneralDimensions{1,Tuple{Space},Tuple{Space}},typeof(a)}, - QuantumObjectEvolution{OperatorQuantumObject,Dimensions{1,Tuple{Space}},typeof(a)}, + QuantumObjectEvolution{Operator,GeneralDimensions{1,Tuple{Space},Tuple{Space}},typeof(a)}, + QuantumObjectEvolution{Operator,Dimensions{1,Tuple{Space}},typeof(a)}, } @inferred UnionType QobjEvo(a) - @inferred UnionType QobjEvo(a, type = Operator) - @inferred QobjEvo(a, type = SuperOperator) + @inferred UnionType QobjEvo(a, type = Operator()) + @inferred QobjEvo(a, type = SuperOperator()) end a = destroy(N) diff --git a/test/core-test/states_and_operators.jl b/test/core-test/states_and_operators.jl index 0cdfbd9e5..1fd77976a 100644 --- a/test/core-test/states_and_operators.jl +++ b/test/core-test/states_and_operators.jl @@ -320,8 +320,8 @@ @testset "identity operator" begin I_op1 = qeye(4) I_op2 = qeye(4, dims = (2, 2)) - I_su1 = qeye(4, type = SuperOperator) - I_su2 = qeye(4, type = SuperOperator, dims = 2) + I_su1 = qeye(4, type = SuperOperator()) + I_su2 = qeye(4, type = SuperOperator(), dims = 2) @test isunitary(I_op1) == true @test isunitary(I_op2) == true @test isunitary(I_su1) == false @@ -334,8 +334,8 @@ @test (I_op2 == I_su2) == false @test (I_su1 == I_su2) == true @test_throws DimensionMismatch qeye(4, dims = 2) - @test_throws DimensionMismatch qeye(2, type = SuperOperator) - @test_throws DimensionMismatch qeye(4, type = SuperOperator, dims = (2, 2)) + @test_throws DimensionMismatch qeye(2, type = SuperOperator()) + @test_throws DimensionMismatch qeye(4, type = SuperOperator(), dims = (2, 2)) end @testset "superoperators" begin