Skip to content

Commit dafc6f7

Browse files
Refactor dimensions handling and update QuantumObject structure for improved clarity and consistency
1 parent f8af7d4 commit dafc6f7

File tree

9 files changed

+82
-84
lines changed

9 files changed

+82
-84
lines changed

src/qobj/arithmetic_and_attributes.jl

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -65,11 +65,11 @@ end
6565

6666
function Base.:(*)(A::AbstractQuantumObject{Operator}, B::QuantumObject{Ket})
6767
check_mul_dimensions(get_dimensions_from(A), get_dimensions_to(B))
68-
return QuantumObject(A.data * B.data, Ket(), Dimensions(get_dimensions_to(A)))
68+
return QuantumObject(A.data * B.data, Ket(), ProductDimensions(get_dimensions_to(A)))
6969
end
7070
function Base.:(*)(A::QuantumObject{Bra}, B::AbstractQuantumObject{Operator})
7171
check_mul_dimensions(get_dimensions_from(A), get_dimensions_to(B))
72-
return QuantumObject(A.data * B.data, Bra(), Dimensions(get_dimensions_from(B)))
72+
return QuantumObject(A.data * B.data, Bra(), ProductDimensions(get_dimensions_from(B)))
7373
end
7474
function Base.:(*)(A::QuantumObject{Ket}, B::QuantumObject{Bra})
7575
check_dimensions(A, B)
@@ -653,15 +653,15 @@ Get the coherence value ``\alpha`` by measuring the expectation value of the des
653653
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|``.
654654
"""
655655
function get_coherence::QuantumObject{Ket})
656-
a = destroy(prod.dimensions))
656+
a = destroy(hilbert_dimensions_to_size.dimensions)[1])
657657
α = expect(a, ψ)
658658
D = exp* a' - conj(α) * a)
659659

660660
return α, D' * ψ
661661
end
662662

663663
function get_coherence::QuantumObject{Operator})
664-
a = destroy(prod.dimensions))
664+
a = destroy(hilbert_dimensions_to_size.dimensions)[1])
665665
α = expect(a, ρ)
666666
D = exp* a' - conj(α) * a)
667667

src/qobj/dimensions.jl

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,19 @@ abstract type AbstractDimensions{M,N} end
1313
end
1414
1515
A structure that describes the left-hand side (`to`) and right-hand side (`from`) dimensions of a quantum object.
16+
17+
The `from` field can be different from `nothing` only for non-square [`Operator`](@ref) and [`SuperOperator`](@ref) quantum objects.
1618
"""
1719
struct ProductDimensions{M,N,T1<:Tuple,T2<:Union{<:Tuple,Nothing}} <: AbstractDimensions{M,N}
1820
to::T1 # space acting on the left
1921
from::T2 # space acting on the right
2022

21-
function ProductDimensions(to::NTuple{M}, from::Union{NTuple,Nothing}) where {M}
23+
function ProductDimensions(to::Union{AbstractVector,NTuple}, from::Union{NTuple,Nothing})
24+
M = length(to)
2225
N = isnothing(from) ? M : length(from)
2326

24-
_non_static_array_warning("to", to)
25-
isnothing(from) || _non_static_array_warning("from", from)
27+
_non_static_array_warning("dims", to)
28+
isnothing(from) || _non_static_array_warning("dims", from)
2629

2730
to_space = _dims_tuple_of_space(to)
2831
from_space = _dims_tuple_of_space(from)
@@ -37,18 +40,25 @@ function ProductDimensions(dims::Union{AbstractVector,Tuple})
3740
end
3841

3942
ProductDimensions(dims::Union{Int,AbstractSpace}) = ProductDimensions((dims,), nothing)
40-
ProductDimensions(dims::Union{AbstractVector{T},NTuple{N,T}}) where {T<:Integer,N} = ProductDimensions(dims, nothing)
43+
ProductDimensions(dims::Union{AbstractVector{<:Integer},NTuple{N,Integer}}) where {N} = ProductDimensions(dims, nothing)
44+
ProductDimensions(dims::Union{AbstractVector{<:AbstractSpace},NTuple{N,AbstractSpace}}) where {N} = ProductDimensions(dims, nothing)
4145
ProductDimensions(dims::ProductDimensions) = dims
4246

4347
# obtain dims in the type of SVector with integers
44-
dimensions_to_dims(dimensions::NTuple{N,AbstractSpace}) where {N} = SVector{N}(map(dimensions_to_dims, dimensions))
45-
dimensions_to_dims(dimensions::ProductDimensions) =
46-
SVector{2}(dimensions_to_dims(dimensions.to), dimensions_to_dims(dimensions.from))
48+
dimensions_to_dims(dimensions::NTuple{N,AbstractSpace}) where {N} = vcat(map(dimensions_to_dims, dimensions)...)
49+
function dimensions_to_dims(dimensions::ProductDimensions)
50+
dims_to = dimensions_to_dims(dimensions.to)
51+
isnothing(dimensions.from) && return dims_to
52+
dims_from = dimensions_to_dims(dimensions.from)
53+
return SVector{2}(dims_to, dims_from)
54+
end
4755
dimensions_to_dims(::Nothing) = nothing # for EigsolveResult.dimensions = nothing
4856

4957
hilbert_dimensions_to_size(dimensions::ProductDimensions) =
5058
(hilbert_dimensions_to_size(dimensions.to), hilbert_dimensions_to_size(dimensions.from))
5159
hilbert_dimensions_to_size(dimensions::NTuple{N,AbstractSpace}) where {N} = prod(hilbert_dimensions_to_size, dimensions)
60+
hilbert_dimensions_to_size(dim::Int) = dim
61+
hilbert_dimensions_to_size(dimensions::Union{AbstractVector, NTuple{N,Integer}}) where {N} = prod(dimensions)
5262
hilbert_dimensions_to_size(::Nothing) = nothing
5363

5464
liouvillian_dimensions_to_size(dimensions::ProductDimensions) =
@@ -66,9 +76,10 @@ Base.adjoint(dimensions::AbstractDimensions) = transpose(dimensions)
6676
Base.:(==)(dim1::ProductDimensions, dim2::ProductDimensions) = (dim1.to == dim2.to) && (dim1.from == dim2.from)
6777

6878
_dims_tuple_of_space(dims::NTuple{N,AbstractSpace}) where {N} = dims
69-
function _dims_tuple_of_space(dims::NTuple{N, Integer}) where {N}
79+
function _dims_tuple_of_space(dims::Union{AbstractVector{<:Integer},SVector{M, <:Integer}, NTuple{M, Integer}}) where {M}
7080
_non_static_array_warning("dims", dims)
7181

82+
N = length(dims)
7283
N > 0 || throw(DomainError(N, "The length of `dims` must be larger or equal to 1."))
7384

7485
return ntuple(dim -> HilbertSpace(dims[dim]), Val(N))
@@ -81,7 +92,10 @@ _gen_dimensions(dims) = ProductDimensions(dims)
8192
# this is used to show `dims` for Qobj and QobjEvo
8293
function _get_dims_string(dimensions::ProductDimensions)
8394
dims = dimensions_to_dims(dimensions)
84-
isnothing(dims[2]) && return string(dims[1])
95+
isnothing(dimensions.from) && return string(dims)
8596
return "[$(string(dims[1])), $(string(dims[2]))]"
8697
end
8798
_get_dims_string(::Nothing) = "nothing" # for EigsolveResult.dimensions = nothing
99+
100+
space_one_list(dimensions::NTuple{N,AbstractSpace}) where {N} =
101+
ntuple(i -> one(dimensions[i]), Val(sum(length, dimensions)))

src/qobj/functions.jl

Lines changed: 20 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -186,41 +186,35 @@ julia> a.dims, O.dims
186186
([20], [20, 20])
187187
```
188188
"""
189-
Base.kron(A::AbstractQuantumObject, B::AbstractQuantumObject)
190-
191-
for type in (:Operator, :SuperOperator)
192-
@eval function Base.kron(
193-
A::AbstractQuantumObject{$type,ProductDimensions},
194-
B::AbstractQuantumObject{$type,ProductDimensions},
195-
)
196-
QType = promote_op_type(A, B)
197-
_lazy_tensor_warning(A.data, B.data)
198-
199-
A_dims_from = get_dimensions_from(A)
200-
B_dims_from = get_dimensions_from(B)
201-
AB_dims_to = (get_dimensions_to(A)..., get_dimensions_to(B)...)
202-
AB_dims_from = (isnothing(A_dims_from) && isnothing(B_dims_from)) ? nothing : (A_dims_from..., B_dims_from...)
203-
204-
return QType(kron(A.data, B.data), $type(), ProductDimensions(AB_dims_to, AB_dims_from))
205-
end
189+
function Base.kron(
190+
A::AbstractQuantumObject{ObjType,<:ProductDimensions},
191+
B::AbstractQuantumObject{ObjType,<:ProductDimensions},
192+
) where {ObjType<:Union{Ket, Bra, Operator}}
193+
QType = promote_op_type(A, B)
194+
_lazy_tensor_warning(A.data, B.data)
195+
196+
A_dims_from = get_dimensions_from(A)
197+
B_dims_from = get_dimensions_from(B)
198+
AB_dims_to = (get_dimensions_to(A)..., get_dimensions_to(B)...)
199+
AB_dims_from = (A.dimensions.from === B.dimensions.from === nothing) ? nothing : (A_dims_from..., B_dims_from...)
200+
201+
return QType(kron(A.data, B.data), A.type, ProductDimensions(AB_dims_to, AB_dims_from))
206202
end
207203

208-
# if A and B are different type (must return Operator with GeneralDimensions)
209204
for AOpType in (:Ket, :Bra, :Operator)
210205
for BOpType in (:Ket, :Bra, :Operator)
211206
if (AOpType != BOpType)
212207
@eval begin
213208
function Base.kron(A::AbstractQuantumObject{$AOpType}, B::AbstractQuantumObject{$BOpType})
214209
QType = promote_op_type(A, B)
215210
_lazy_tensor_warning(A.data, B.data)
216-
return QType(
217-
kron(A.data, B.data),
218-
Operator(),
219-
GeneralDimensions(
220-
(get_dimensions_to(A)..., get_dimensions_to(B)...),
221-
(get_dimensions_from(A)..., get_dimensions_from(B)...),
222-
),
223-
)
211+
212+
A_dims_from = get_dimensions_from(A)
213+
B_dims_from = get_dimensions_from(B)
214+
AB_dims_to = (get_dimensions_to(A)..., get_dimensions_to(B)...)
215+
AB_dims_from = (A.dimensions.from === B.dimensions.from === nothing) ? nothing : (A_dims_from..., B_dims_from...)
216+
217+
return QType(kron(A.data, B.data), Operator(), ProductDimensions(AB_dims_to, AB_dims_from))
224218
end
225219
end
226220
end

src/qobj/quantum_object.jl

Lines changed: 15 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,7 @@ struct QuantumObject{ObjType<:QuantumObjectType,DimType<:AbstractDimensions,Data
5555

5656
ObjType = _check_type(type)
5757

58-
_size = _get_size(data)
59-
_check_QuantumObject(type, dimensions, _size[1], _size[2])
58+
_check_QuantumObject(type, dimensions, size(data, 1), size(data, 2))
6059

6160
return new{ObjType,typeof(dimensions),DT}(data, type, dimensions)
6261
end
@@ -72,12 +71,10 @@ Generate [`QuantumObject`](@ref) with a given `A::AbstractArray` and specified `
7271
`Qobj` is a synonym of `QuantumObject`.
7372
"""
7473
function QuantumObject(A::AbstractMatrix{T}; type = nothing, dims = nothing) where {T}
75-
_size = _get_size(A)
76-
7774
_check_type(type)
7875

79-
if type isa Nothing
80-
type = (_size[1] == 1 && _size[2] > 1) ? Bra() : Operator() # default type
76+
if isnothing(type)
77+
type = (size(A, 1) == 1 && size(A, 2) > 1) ? Bra() : Operator() # default type
8178
elseif !(type isa Operator) && !(type isa SuperOperator) && !(type isa Bra) && !(type isa OperatorBra)
8279
throw(
8380
ArgumentError(
@@ -86,15 +83,15 @@ function QuantumObject(A::AbstractMatrix{T}; type = nothing, dims = nothing) whe
8683
)
8784
end
8885

89-
if dims isa Nothing
86+
if isnothing(dims)
9087
if type isa Bra
91-
dims = Dimensions(_size[2])
88+
dims = ProductDimensions(size(A, 2))
9289
elseif type isa Operator
93-
dims =
94-
(_size[1] == _size[2]) ? Dimensions(_size[1]) :
95-
GeneralDimensions(SVector{2}(SVector{1}(_size[1]), SVector{1}(_size[2])))
90+
dims_to = (size(A, 1), )
91+
dims_from = (size(A, 1) == size(A, 2)) ? nothing : (size(A, 2), )
92+
dims = ProductDimensions(dims_to, dims_from)
9693
elseif type isa SuperOperator || type isa OperatorBra
97-
dims = Dimensions(isqrt(_size[2]))
94+
dims = ProductDimensions(isqrt(size(A, 2)))
9895
end
9996
end
10097

@@ -103,18 +100,18 @@ end
103100

104101
function QuantumObject(A::AbstractVector{T}; type = nothing, dims = nothing) where {T}
105102
_check_type(type)
106-
if type isa Nothing
103+
104+
if isnothing(type)
107105
type = Ket() # default type
108106
elseif !(type isa Ket) && !(type isa OperatorKet)
109107
throw(ArgumentError("The argument type must be Ket() or OperatorKet() if the input array is a vector."))
110108
end
111109

112-
if dims isa Nothing
113-
_size = _get_size(A)
110+
if isnothing(dims)
114111
if type isa Ket
115-
dims = Dimensions(_size[1])
112+
dims = ProductDimensions(size(A, 1))
116113
elseif type isa OperatorKet
117-
dims = Dimensions(isqrt(_size[1]))
114+
dims = ProductDimensions(isqrt(size(A, 1)))
118115
end
119116
end
120117

@@ -126,10 +123,9 @@ function QuantumObject(A::AbstractArray{T,N}; type = nothing, dims = nothing) wh
126123
end
127124

128125
function QuantumObject(A::QuantumObject; type = A.type, dims = A.dimensions)
129-
_size = _get_size(A.data)
130126
dimensions = _gen_dimensions(dims)
131127
_check_type(type)
132-
_check_QuantumObject(type, dimensions, _size[1], _size[2])
128+
_check_QuantumObject(type, dimensions, size(A, 1), size(A, 2))
133129
return QuantumObject(copy(A.data), type, dimensions)
134130
end
135131

src/qobj/quantum_object_base.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -210,8 +210,8 @@ end
210210

211211
get_dimensions_to(A::AbstractQuantumObject) = A.dimensions.to
212212
get_dimensions_from(A::AbstractQuantumObject) = isnothing(A.dimensions.from) ? A.dimensions.to : A.dimensions.from
213-
get_dimensions_from(::AbstractQuantumObject{Ket}) = nothing
214-
get_dimensions_to(::AbstractQuantumObject{Bra}) = nothing
213+
get_dimensions_from(A::AbstractQuantumObject{Ket}) = space_one_list(A.dimensions.to)
214+
get_dimensions_to(A::AbstractQuantumObject{Bra}) = space_one_list(A.dimensions.to)
215215

216216
# functions for getting Float or Complex element type
217217
_float_type(A::AbstractQuantumObject) = _float_type(eltype(A))

src/qobj/quantum_object_evo.jl

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -124,8 +124,7 @@ struct QuantumObjectEvolution{
124124

125125
dimensions = _gen_dimensions(dims)
126126

127-
_size = _get_size(data)
128-
_check_QuantumObject(type, dimensions, _size[1], _size[2])
127+
_check_QuantumObject(type, dimensions, size(data, 1), size(data, 2))
129128

130129
return new{ObjType,typeof(dimensions),DT}(data, type, dimensions)
131130
end
@@ -158,16 +157,15 @@ Generate a [`QuantumObjectEvolution`](@ref) object from a [`SciMLOperator`](http
158157
Note that `QobjEvo` is a synonym of `QuantumObjectEvolution`
159158
"""
160159
function QuantumObjectEvolution(data::AbstractSciMLOperator; type = Operator(), dims = nothing)
161-
_size = _get_size(data)
162160
_check_type(type)
163161

164-
if dims isa Nothing
162+
if isnothing(dims)
165163
if type isa Operator
166-
dims =
167-
(_size[1] == _size[2]) ? Dimensions(_size[1]) :
168-
GeneralDimensions(SVector{2}(SVector{1}(_size[1]), SVector{1}(_size[2])))
164+
dims_to = (size(data, 1), )
165+
dims_from = (size(data, 1) == size(data, 2)) ? nothing : (size(data, 2), )
166+
dims = ProductDimensions(dims_to, dims_from)
169167
elseif type isa SuperOperator
170-
dims = Dimensions(isqrt(_size[2]))
168+
dims = ProductDimensions(isqrt(size(data, 2)))
171169
end
172170
end
173171

src/qobj/space.jl

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,9 @@ struct HilbertSpace <: AbstractSpace
2222
end
2323
end
2424

25+
Base.one(::HilbertSpace) = HilbertSpace(1)
26+
Base.length(::HilbertSpace) = 1
27+
2528
dimensions_to_dims(s::HilbertSpace) = SVector{1,Int}(s.size)
2629
hilbert_dimensions_to_size(s::HilbertSpace) = s.size
2730
liouvillian_dimensions_to_size(s::HilbertSpace) = s.size^2

src/utilities.jl

Lines changed: 0 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -143,10 +143,6 @@ makeVal(x) = Val(x)
143143
getVal(x::Val{T}) where {T} = T
144144
getVal(x) = x # getVal for any other type
145145

146-
_get_size(A::AbstractMatrix) = size(A)
147-
_get_size(A::AbstractVector) = (length(A), 1)
148-
_get_size(A::AbstractSciMLOperator) = size(A)
149-
150146
_non_static_array_warning(argname, arg::Tuple{}) =
151147
throw(ArgumentError("The argument $argname must be a Tuple or a StaticVector of non-zero length."))
152148
_non_static_array_warning(argname, arg::Union{SVector{N,T},MVector{N,T},NTuple{N,T}}) where {N,T} = nothing

test/core-test/quantum_objects.jl

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@
1717
# DomainError: incompatible between size of array and type
1818
@testset "DomainError" begin
1919
a = rand(ComplexF64, 3, 2)
20-
for t in [SuperOperator(), Bra(), OperatorBra()]
20+
for t in (Bra(), OperatorBra())
2121
@test_throws DomainError Qobj(a, type = t)
2222
end
2323

@@ -33,17 +33,14 @@
3333
@test_throws DomainError Qobj(rand(ComplexF64, 2, 1), type = Operator()) # should be type = Bra
3434

3535
# check that Ket, Bra, SuperOperator, OperatorKet, and OperatorBra don't support GeneralDimensions
36-
@test_throws DomainError Qobj(rand(ComplexF64, 2), type = Ket(), dims = ((2,), (1,)))
37-
@test_throws DomainError Qobj(rand(ComplexF64, 1, 2), type = Bra(), dims = ((1,), (2,)))
38-
@test_throws DomainError Qobj(rand(ComplexF64, 4, 4), type = SuperOperator(), dims = ((2,), (2,)))
39-
@test_throws DomainError Qobj(rand(ComplexF64, 4), type = OperatorKet(), dims = ((2,), (1,)))
40-
@test_throws DomainError Qobj(rand(ComplexF64, 1, 4), type = OperatorBra(), dims = ((1,), (2,)))
36+
@test_throws DimensionMismatch Qobj(rand(ComplexF64, 2), type = Ket(), dims = ((2,), (1,)))
37+
@test_throws DimensionMismatch Qobj(rand(ComplexF64, 1, 2), type = Bra(), dims = ((1,), (2,)))
4138
end
4239

4340
# unsupported type of dims
4441
@testset "unsupported dims" begin
45-
@test_throws ArgumentError Qobj(rand(2, 2), dims = 2.0)
46-
@test_throws ArgumentError Qobj(rand(2, 2), dims = 2.0 + 0.0im)
42+
@test_throws MethodError Qobj(rand(2, 2), dims = 2.0)
43+
@test_throws MethodError Qobj(rand(2, 2), dims = 2.0 + 0.0im)
4744
@test_throws DomainError Qobj(rand(2, 2), dims = 0)
4845
@test_throws DomainError Qobj(rand(2, 2), dims = (2, -2))
4946
@test_logs (
@@ -367,8 +364,8 @@
367364
end
368365

369366
UnionType = Union{
370-
QuantumObject{Bra,Dimensions{1,Tuple{Space}},Matrix{T}},
371-
QuantumObject{Operator,Dimensions{1,Tuple{Space}},Matrix{T}},
367+
QuantumObject{Bra,ProductDimensions{1,1,Tuple{HilbertSpace},Nothing},Matrix{T}},
368+
QuantumObject{Operator,ProductDimensions{1,1,Tuple{HilbertSpace},Nothing},Matrix{T}},
372369
}
373370
a = rand(T, 1, N)
374371
@inferred UnionType Qobj(a)
@@ -377,8 +374,8 @@
377374
end
378375

379376
UnionType2 = Union{
380-
QuantumObject{Operator,GeneralDimensions{1,1,Tuple{Space},Tuple{Space}},Matrix{T}},
381-
QuantumObject{Operator,Dimensions{1,Tuple{Space}},Matrix{T}},
377+
QuantumObject{Operator,ProductDimensions{1,1,Tuple{HilbertSpace},Tuple{HilbertSpace}},Matrix{T}},
378+
QuantumObject{Operator,ProductDimensions{1,1,Tuple{HilbertSpace},Nothing},Matrix{T}},
382379
}
383380
a = rand(T, N, N)
384381
@inferred UnionType Qobj(a)

0 commit comments

Comments
 (0)