diff --git a/src/qobj/quantum_object.jl b/src/qobj/quantum_object.jl index 9e20d19d..72abdef7 100644 --- a/src/qobj/quantum_object.jl +++ b/src/qobj/quantum_object.jl @@ -141,124 +141,111 @@ struct QuantumObject{MT<:AbstractArray,ObjType<:QuantumObjectType} <: AbstractQu dims::Vector{Int} end -function Base.show( - io::IO, - QO::QuantumObject{<:AbstractArray{T},OpType}, -) where { - T, - OpType<:Union{ - BraQuantumObject, - KetQuantumObject, - OperatorBraQuantumObject, - OperatorKetQuantumObject, - SuperOperatorQuantumObject, - }, -} - op_data = QO.data - println(io, "Quantum Object: type=", QO.type, " dims=", QO.dims, " size=", size(op_data)) - return show(io, MIME("text/plain"), op_data) -end - -function Base.show(io::IO, QO::QuantumObject{<:AbstractArray{T},OpType}) where {T,OpType<:OperatorQuantumObject} - op_data = QO.data - println( - io, - "Quantum Object: type=", - QO.type, - " dims=", - QO.dims, - " size=", - size(op_data), - " ishermitian=", - ishermitian(op_data), - ) - return show(io, MIME("text/plain"), op_data) -end - function QuantumObject( - A::AbstractArray{T,N}; + A::AbstractMatrix{T}; type::ObjType = nothing, dims = nothing, -) where {T,N,ObjType<:Union{Nothing,QuantumObjectType}} +) where {T,ObjType<:Union{Nothing,QuantumObjectType}} + _size = size(A) + + if type isa Nothing + type = (_size[1] == 1 && _size[2] > 1) ? Bra : Operator # default type + elseif type != Operator && type != SuperOperator && type != Bra && type != OperatorBra + throw( + ArgumentError( + "The argument type must be Operator, SuperOperator, Bra or OperatorBra if the input array is a matrix.", + ), + ) + end - # only accept 1D- and 2D-array - if N == 1 - Size = (length(A), 1) + if dims isa Nothing + if type isa OperatorQuantumObject || type isa BraQuantumObject + dims = [_size[2]] + elseif type isa SuperOperatorQuantumObject || type isa OperatorBraQuantumObject + dims = [isqrt(_size[2])] + end else - Size = size(A) - (N > 2) && throw(DomainError(Size, "The dimension of the array is not compatible with Quantum Object")) + _check_dims(dims) end - # decide QuantumObjectType from the size of A + _check_QuantumObject(type, dims, _size[1], _size[2]) + return QuantumObject(A, type, dims) +end + +function QuantumObject( + A::AbstractVector{T}; + type::ObjType = nothing, + dims = nothing, +) where {T,ObjType<:Union{Nothing,QuantumObjectType}} if type isa Nothing - if Size[1] == Size[2] - type = Operator - elseif Size[2] == 1 - type = Ket - elseif Size[1] == 1 - type = Bra - else - throw(DomainError(Size, "The dimension of the array is not compatible with Quantum Object")) - end + 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.")) end - # decide dims from the size of A and the given type + _size = (length(A), 1) + if dims isa Nothing - if (type isa KetQuantumObject) || (type isa OperatorQuantumObject) - dims = [Size[1]] - elseif (type isa SuperOperatorQuantumObject) || (type isa OperatorKetQuantumObject) - dims = [isqrt(Size[1])] - elseif type isa BraQuantumObject - dims = [Size[2]] - elseif type isa OperatorBraQuantumObject - dims = [isqrt(Size[2])] + if type isa KetQuantumObject + dims = [_size[1]] + elseif type isa OperatorKetQuantumObject + dims = [isqrt(_size[1])] end else _check_dims(dims) end - _check_QuantumObject(type, dims, Size[1], Size[2]) + + _check_QuantumObject(type, dims, _size[1], _size[2]) return QuantumObject(A, type, dims) end +function QuantumObject( + A::AbstractArray{T,N}; + type::ObjType = nothing, + dims = nothing, +) where {T,N,ObjType<:Union{Nothing,QuantumObjectType}} + throw(DomainError(size(A), "The size of the array is not compatible with vector or matrix.")) +end + _check_dims(dims::Vector{Int}) = all(>(0), dims) || throw(DomainError(dims, "The argument dims must be a vector with positive integers.")) _check_dims(dims::Any) = throw(ArgumentError("The argument dims must be a vector with positive integers.")) function _check_QuantumObject(type::KetQuantumObject, dims::Vector{Int}, m::Int, n::Int) - (n != 1) && throw(DomainError((m, n), "The dimension of the array is not compatible with Ket")) + (n != 1) && throw(DomainError((m, n), "The size of the array is not compatible with Ket")) (prod(dims) != m) && throw(DimensionMismatch("Ket with dims = $(dims) does not fit the array size = $((m, n)).")) return nothing end function _check_QuantumObject(type::BraQuantumObject, dims::Vector{Int}, m::Int, n::Int) - (m != 1) && throw(DomainError((m, n), "The dimension of the array is not compatible with Bra")) + (m != 1) && throw(DomainError((m, n), "The size of the array is not compatible with Bra")) (prod(dims) != n) && throw(DimensionMismatch("Bra with dims = $(dims) does not fit the array size = $((m, n)).")) return nothing end function _check_QuantumObject(type::OperatorQuantumObject, dims::Vector{Int}, m::Int, n::Int) - (m != n) && throw(DomainError((m, n), "The dimension of the array is not compatible with Operator")) + (m != n) && throw(DomainError((m, n), "The size of the array is not compatible with Operator")) (prod(dims) != m) && throw(DimensionMismatch("Operator with dims = $(dims) does not fit the array size = $((m, n)).")) return nothing end function _check_QuantumObject(type::SuperOperatorQuantumObject, dims::Vector{Int}, m::Int, n::Int) - (m != n) && throw(DomainError((m, n), "The dimension of the array is not compatible with SuperOperator")) + (m != n) && throw(DomainError((m, n), "The size of the array is not compatible with SuperOperator")) (prod(dims) != sqrt(m)) && throw(DimensionMismatch("SuperOperator with dims = $(dims) does not fit the array size = $((m, n)).")) return nothing end function _check_QuantumObject(type::OperatorKetQuantumObject, dims::Vector{Int}, m::Int, n::Int) - (n != 1) && throw(DomainError((m, n), "The dimension of the array is not compatible with OperatorKet")) + (n != 1) && throw(DomainError((m, n), "The size of the array is not compatible with OperatorKet")) (prod(dims) != sqrt(m)) && throw(DimensionMismatch("OperatorKet with dims = $(dims) does not fit the array size = $((m, n)).")) return nothing end function _check_QuantumObject(type::OperatorBraQuantumObject, dims::Vector{Int}, m::Int, n::Int) - (m != 1) && throw(DomainError((m, n), "The dimension of the array is not compatible with OperatorBra")) + (m != 1) && throw(DomainError((m, n), "The size of the array is not compatible with OperatorBra")) (prod(dims) != sqrt(n)) && throw(DimensionMismatch("OperatorBra with dims = $(dims) does not fit the array size = $((m, n)).")) return nothing @@ -269,11 +256,45 @@ function QuantumObject( type::ObjType = A.type, dims = A.dims, ) where {T,N,ObjType<:QuantumObjectType} - Size = N == 1 ? (length(A), 1) : size(A) - _check_QuantumObject(type, dims, Size[1], Size[2]) + _size = N == 1 ? (length(A), 1) : size(A) + _check_QuantumObject(type, dims, _size[1], _size[2]) return QuantumObject(copy(A.data), type, dims) end +function Base.show( + io::IO, + QO::QuantumObject{<:AbstractArray{T},OpType}, +) where { + T, + OpType<:Union{ + BraQuantumObject, + KetQuantumObject, + OperatorBraQuantumObject, + OperatorKetQuantumObject, + SuperOperatorQuantumObject, + }, +} + op_data = QO.data + println(io, "Quantum Object: type=", QO.type, " dims=", QO.dims, " size=", size(op_data)) + return show(io, MIME("text/plain"), op_data) +end + +function Base.show(io::IO, QO::QuantumObject{<:AbstractArray{T},OpType}) where {T,OpType<:OperatorQuantumObject} + op_data = QO.data + println( + io, + "Quantum Object: type=", + QO.type, + " dims=", + QO.dims, + " size=", + size(op_data), + " ishermitian=", + ishermitian(op_data), + ) + return show(io, MIME("text/plain"), op_data) +end + @doc raw""" size(A::QuantumObject) size(A::QuantumObject, idx::Int) diff --git a/test/quantum_objects.jl b/test/quantum_objects.jl index fca5e418..b267248f 100644 --- a/test/quantum_objects.jl +++ b/test/quantum_objects.jl @@ -1,12 +1,51 @@ @testset "Quantum Objects" begin - # unsupported size of array - for a in [rand(ComplexF64, 3, 2), rand(ComplexF64, 2, 2, 2)] - for t in [nothing, Ket, Bra, Operator, SuperOperator, OperatorBra, OperatorKet] - @test_throws DomainError Qobj(a, type = t) + @testset "Type Inference" begin + for T in [ComplexF32, ComplexF64] + N = 4 + a = rand(T, N) + @inferred QuantumObject{typeof(a),KetQuantumObject} Qobj(a) + for type in [Ket, OperatorKet] + @inferred Qobj(a, type = type) + end + + UnionType = Union{QuantumObject{Matrix{T},BraQuantumObject},QuantumObject{Matrix{T},OperatorQuantumObject}} + a = rand(T, 1, N) + @inferred UnionType Qobj(a) + for type in [Bra, OperatorBra] + @inferred Qobj(a, type = type) + end + + a = rand(T, N, N) + @inferred UnionType Qobj(a) + for type in [Operator, SuperOperator] + @inferred Qobj(a, type = type) + end end end - # unsupported dims + # ArgumentError: type is incompatible with vector or matrix + a = rand(ComplexF64, 2) + 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) + + # DomainError: incompatible between size of array and type + a = rand(ComplexF64, 3, 2) + for t in [nothing, Operator, 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] + @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) + + # unsupported type of dims @test_throws ArgumentError Qobj(rand(2, 2), dims = 2) @test_throws ArgumentError Qobj(rand(2, 2), dims = [2.0]) @test_throws ArgumentError Qobj(rand(2, 2), dims = [2.0 + 0.0im]) @@ -16,19 +55,11 @@ N = 10 a = rand(ComplexF64, 10) # @test_logs (:warn, "The norm of the input data is not one.") QuantumObject(a) - @test_throws DomainError Qobj(a, type = Bra) - @test_throws DomainError Qobj(a, type = Operator) - @test_throws DomainError Qobj(a, type = SuperOperator) - @test_throws DomainError Qobj(a, type = OperatorBra) - @test_throws DomainError Qobj(a', type = Ket) - @test_throws DomainError Qobj(a', type = Operator) - @test_throws DomainError Qobj(a', type = SuperOperator) - @test_throws DomainError Qobj(a', type = OperatorKet) @test_throws DimensionMismatch Qobj(a, dims = [2]) @test_throws DimensionMismatch Qobj(a', dims = [2]) a2 = Qobj(a') a3 = Qobj(a) - @test dag(a3) == a2 + @test dag(a3) == a2 # Here we are also testing the dag function @test isket(a2) == false @test isbra(a2) == true @test isoper(a2) == false @@ -43,7 +74,6 @@ @test isoperbra(a3) == false @test Qobj(a3) == a3 @test !(Qobj(a3) === a3) - @test isket(Qobj(Matrix([2 3])')) == true a = sprand(ComplexF64, 100, 100, 0.1) a2 = Qobj(a) @@ -62,7 +92,6 @@ @test isoperket(a3) == false @test isoperbra(a3) == false @test_throws DimensionMismatch Qobj(a, dims = [2]) - @test_throws DimensionMismatch Qobj(a, dims = [2]) # Operator-Ket, Operator-Bra tests H = 0.3 * sigmax() + 0.7 * sigmaz()