Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Resolve type instabilities while generating Qobj (2nd try) #179

Merged
merged 3 commits into from
Jun 27, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
159 changes: 90 additions & 69 deletions src/qobj/quantum_object.jl
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)
Expand Down
61 changes: 45 additions & 16 deletions test/quantum_objects.jl
Original file line number Diff line number Diff line change
@@ -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])
Expand All @@ -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
Expand All @@ -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)
Expand All @@ -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()
Expand Down