Skip to content

Commit 12e22ab

Browse files
committed
support * for Compoundimensions
1 parent 235b4f0 commit 12e22ab

File tree

7 files changed

+124
-59
lines changed

7 files changed

+124
-59
lines changed

src/negativity.jl

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ end
7878
# for dense matrices
7979
function _partial_transpose::QuantumObject{<:AbstractArray,OperatorQuantumObject}, mask::Vector{Bool})
8080
isa.dims, CompoundDimensions) &&
81-
.dims.to != ρ.dims.from) &&
81+
.to != ρ.from) &&
8282
throw(ArgumentError("Invalid partial transpose for dims = $(ρ.dims)"))
8383

8484
mask2 = [1 + Int(i) for i in mask]
@@ -87,7 +87,7 @@ function _partial_transpose(ρ::QuantumObject{<:AbstractArray,OperatorQuantumObj
8787
# 2 - the subsystem need be transposed
8888

8989
nsys = length(mask2)
90-
dimslist = dims_to_list.dims)
90+
dimslist = dims_to_list.dims.to) # need `dims.to` here if QO is CompoundDimensions
9191
pt_dims = reshape(Vector(1:(2*nsys)), (nsys, 2))
9292
pt_idx = [
9393
[pt_dims[n, mask2[n]] for n in 1:nsys] # origin value in mask2
@@ -103,11 +103,11 @@ end
103103
# for sparse matrices
104104
function _partial_transpose::QuantumObject{<:AbstractSparseArray,OperatorQuantumObject}, mask::Vector{Bool})
105105
isa.dims, CompoundDimensions) &&
106-
.dims.to != ρ.dims.from) &&
106+
.to != ρ.from) &&
107107
throw(ArgumentError("Invalid partial transpose for dims = $(ρ.dims)"))
108108

109109
M, N = size(ρ)
110-
dimsTuple = Tuple(dims_to_list.dims))
110+
dimsTuple = Tuple(dims_to_list.dims.to)) # need `dims.to` here if QO is CompoundDimensions
111111
colptr = ρ.data.colptr
112112
rowval = ρ.data.rowval
113113
nzval = ρ.data.nzval

src/qobj/arithmetic_and_attributes.jl

Lines changed: 43 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -50,19 +50,54 @@ for op in (:(+), :(-), :(*))
5050
end
5151
end
5252

53+
function check_mul_dims(from::SVector{NA,AbstractSpace}, to::SVector{NB,AbstractSpace}) where {NA,NB}
54+
(from != to) && throw(
55+
DimensionMismatch(
56+
"The quantum object with dims = $from can not multiply a quantum object with dims = $to on the right-hand side.",
57+
),
58+
)
59+
return nothing
60+
end
61+
62+
for ADimType in (:Dimensions, :CompoundDimensions)
63+
for BDimType in (:Dimensions, :CompoundDimensions)
64+
if ADimType == BDimType == :Dimensions
65+
@eval begin
66+
function LinearAlgebra.:(*)(
67+
A::AbstractQuantumObject{DT1,OperatorQuantumObject,$ADimType{NA}},
68+
B::AbstractQuantumObject{DT2,OperatorQuantumObject,$BDimType{NB}},
69+
) where {DT1,DT2,NA,NB}
70+
check_dims(A, B)
71+
return QuantumObject(A.data * B.data, Operator, A.dims)
72+
end
73+
end
74+
else
75+
@eval begin
76+
function LinearAlgebra.:(*)(
77+
A::AbstractQuantumObject{DT1,OperatorQuantumObject,$ADimType{NA}},
78+
B::AbstractQuantumObject{DT2,OperatorQuantumObject,$BDimType{NB}},
79+
) where {DT1,DT2,NA,NB}
80+
check_mul_dims(A.from, B.to)
81+
return QuantumObject(A.data * B.data, Operator, CompoundDimensions(A.to, B.from))
82+
end
83+
end
84+
end
85+
end
86+
end
87+
5388
function LinearAlgebra.:(*)(
5489
A::AbstractQuantumObject{DT1,OperatorQuantumObject},
5590
B::QuantumObject{DT2,KetQuantumObject},
5691
) where {DT1,DT2}
57-
check_dims(A, B)
58-
return QuantumObject(A.data * B.data, Ket, A.dims)
92+
check_mul_dims(A.from, B.to)
93+
return QuantumObject(A.data * B.data, Ket, Dimensions(A.to))
5994
end
6095
function LinearAlgebra.:(*)(
6196
A::QuantumObject{DT1,BraQuantumObject},
6297
B::AbstractQuantumObject{DT2,OperatorQuantumObject},
6398
) where {DT1,DT2}
64-
check_dims(A, B)
65-
return QuantumObject(A.data * B.data, Bra, A.dims)
99+
check_mul_dims(A.from, B.to)
100+
return QuantumObject(A.data * B.data, Bra, Dimensions(B.from))
66101
end
67102
function LinearAlgebra.:(*)(
68103
A::QuantumObject{DT1,KetQuantumObject},
@@ -569,7 +604,7 @@ ptrace(QO::QuantumObject{<:AbstractArray,BraQuantumObject}, sel::Union{AbstractV
569604

570605
function ptrace(QO::QuantumObject{<:AbstractArray,OperatorQuantumObject}, sel::Union{AbstractVector{Int},Tuple})
571606
isa(QO.dims, CompoundDimensions) &&
572-
(QO.dims.to != QO.dims.from) &&
607+
(QO.to != QO.from) &&
573608
throw(ArgumentError("Invalid partial trace for dims = $(QO.dims)"))
574609

575610
_non_static_array_warning("sel", sel)
@@ -587,7 +622,7 @@ function ptrace(QO::QuantumObject{<:AbstractArray,OperatorQuantumObject}, sel::U
587622
(n_d == 1) && return QO
588623
end
589624

590-
dimslist = dims_to_list(QO.dims)
625+
dimslist = dims_to_list(QO.dims.to) # need `dims.to` here if QO is CompoundDimensions
591626
_sort_sel = sort(SVector{length(sel),Int}(sel))
592627
ρtr, dkeep = _ptrace_oper(QO.data, dimslist, _sort_sel)
593628
return QuantumObject(ρtr, type = Operator, dims = Dimensions(dkeep))
@@ -757,7 +792,7 @@ function permute(
757792
order::Union{AbstractVector{Int},Tuple},
758793
) where {T,ObjType<:Union{KetQuantumObject,BraQuantumObject,OperatorQuantumObject}}
759794
isa(A.dims, CompoundDimensions) &&
760-
(A.dims.to != A.dims.from) &&
795+
(A.to != A.from) &&
761796
throw(ArgumentError("Invalid permutation for dims = $(A.dims)"))
762797

763798
(length(order) != length(A.dims)) &&
@@ -770,7 +805,7 @@ function permute(
770805
order_svector = SVector{length(order),Int}(order) # convert it to SVector for performance
771806

772807
# obtain the arguments: dims for reshape; perm for PermutedDimsArray
773-
dimslist = dims_to_list(A.dims)
808+
dimslist = dims_to_list(A.dims.to) # need `dims.to` here if QO is CompoundDimensions
774809
dims, perm = _dims_and_perm(A.type, dimslist, order_svector, length(order_svector))
775810

776811
return QuantumObject(

src/qobj/functions.jl

Lines changed: 33 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -178,27 +178,36 @@ julia> a.dims, O.dims
178178
([20], [20, 20])
179179
```
180180
"""
181-
function LinearAlgebra.kron( # for same OpType. Note that `<:DimType` means same DimType but can have different length N : DimType{N}
182-
A::AbstractQuantumObject{DT1,OpType,<:DimType},
183-
B::AbstractQuantumObject{DT2,OpType,<:DimType},
184-
) where {DT1,DT2,OpType<:Union{KetQuantumObject,BraQuantumObject,OperatorQuantumObject},DimType<:AbstractDimensions}
181+
function LinearAlgebra.kron(
182+
A::AbstractQuantumObject{DT1,OpType,Dimensions{NA}},
183+
B::AbstractQuantumObject{DT2,OpType,Dimensions{NB}},
184+
) where {DT1,DT2,OpType<:Union{KetQuantumObject,BraQuantumObject,OperatorQuantumObject},NA,NB}
185185
QType = promote_op_type(A, B)
186-
return QType(kron(A.data, B.data), A.type, kron(A.dims, B.dims))
186+
return QType(kron(A.data, B.data), A.type, Dimensions{NA + NB}(vcat(A.dims.to, B.dims.to)))
187187
end
188-
function LinearAlgebra.kron( # if A and B are both Operator but different Dimensions type
189-
A::AbstractQuantumObject{DT1,OperatorQuantumObject,<:Dimensions},
190-
B::AbstractQuantumObject{DT2,OperatorQuantumObject,<:CompoundDimensions},
191-
) where {DT1,DT2}
192-
QType = promote_op_type(A, B)
193-
return QType(kron(A.data, B.data), Operator, kron(CompoundDimensions(A.type, A.dims), B.dims))
194-
end
195-
function LinearAlgebra.kron( # if A and B are both Operator but different Dimensions type
196-
A::AbstractQuantumObject{DT1,OperatorQuantumObject,<:CompoundDimensions},
197-
B::AbstractQuantumObject{DT2,OperatorQuantumObject,<:Dimensions},
198-
) where {DT1,DT2}
199-
QType = promote_op_type(A, B)
200-
return QType(kron(A.data, B.data), Operator, kron(A.dims, CompoundDimensions(B.type, B.dims)))
188+
189+
# if A and B are both Operator but either one of them has CompoundDimensions
190+
for ADimType in (:Dimensions, :CompoundDimensions)
191+
for BDimType in (:Dimensions, :CompoundDimensions)
192+
if !(ADimType == BDimType == :Dimensions) # not for this case because it's already implemented
193+
@eval begin
194+
function LinearAlgebra.kron(
195+
A::AbstractQuantumObject{DT1,OperatorQuantumObject,$ADimType{NA}},
196+
B::AbstractQuantumObject{DT2,OperatorQuantumObject,$BDimType{NB}},
197+
) where {DT1,DT2,NA,NB}
198+
QType = promote_op_type(A, B)
199+
return QType(
200+
kron(A.data, B.data),
201+
Operator,
202+
CompoundDimensions{NA + NB}(vcat(A.to, B.to), vcat(A.from, B.from)),
203+
)
204+
end
205+
end
206+
end
207+
end
201208
end
209+
210+
# if A and B are differet type (must return Operator with CompoundDimensions)
202211
for AOpType in (:KetQuantumObject, :BraQuantumObject, :OperatorQuantumObject)
203212
for BOpType in (:KetQuantumObject, :BraQuantumObject, :OperatorQuantumObject)
204213
if (AOpType != BOpType)
@@ -208,17 +217,17 @@ for AOpType in (:KetQuantumObject, :BraQuantumObject, :OperatorQuantumObject)
208217
B::AbstractQuantumObject{DT2,$BOpType},
209218
) where {DT1,DT2}
210219
QType = promote_op_type(A, B)
211-
212-
# transfer to CompoundDimensions
213-
_Adims = CompoundDimensions(A.type, A.dims)
214-
_Bdims = CompoundDimensions(B.type, B.dims)
215-
216-
return QType(kron(A.data, B.data), Operator, kron(_Adims, _Bdims))
220+
return QType(
221+
kron(A.data, B.data),
222+
Operator,
223+
CompoundDimensions(vcat(A.to, B.to), vcat(A.from, B.from)),
224+
)
217225
end
218226
end
219227
end
220228
end
221229
end
230+
222231
LinearAlgebra.kron(A::AbstractQuantumObject) = A
223232
function LinearAlgebra.kron(A::Vector{<:AbstractQuantumObject})
224233
@warn "`tensor(A)` or `kron(A)` with `A` is a `Vector` can hurt performance. Try to use `tensor(A...)` or `kron(A...)` instead."

src/qobj/operators.jl

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -435,7 +435,7 @@ function eye(
435435
if dims isa Nothing
436436
dims = isa(type, OperatorQuantumObject) ? N : isqrt(N)
437437
end
438-
QuantumObject(Diagonal(ones(ComplexF64, N)); type = type, dims = dims)
438+
return QuantumObject(Diagonal(ones(ComplexF64, N)); type = type, dims = dims)
439439
end
440440

441441
@doc raw"""
@@ -497,7 +497,8 @@ end
497497
498498
Generates the projection operator ``\hat{O} = |i \rangle\langle j|`` with Hilbert space dimension `N`.
499499
"""
500-
projection(N::Int, i::Int, j::Int) = QuantumObject(sparse([i + 1], [j + 1], [1.0 + 0.0im], N, N), type = Operator, dims = N)
500+
projection(N::Int, i::Int, j::Int) =
501+
QuantumObject(sparse([i + 1], [j + 1], [1.0 + 0.0im], N, N), type = Operator, dims = N)
501502

502503
@doc raw"""
503504
tunneling(N::Int, m::Int=1; sparse::Union{Bool,Val{<:Bool}}=Val(false))

src/qobj/quantum_object_base.jl

Lines changed: 20 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -209,23 +209,26 @@ function _check_QuantumObject(type::OperatorBraQuantumObject, dims::Dimensions,
209209
return nothing
210210
end
211211

212-
CompoundDimensions(::KetQuantumObject, dims::Dimensions{N}) where {N} =
213-
CompoundDimensions{N}(dims.to, Field_list(N))
214-
CompoundDimensions(::BraQuantumObject, dims::Dimensions{N}) where {N} =
215-
CompoundDimensions{N}(Field_list(N), dims.to)
216-
CompoundDimensions(::OperatorQuantumObject, dims::Dimensions{N}) where {N} = CompoundDimensions{N}(dims.to, dims.to)
217-
CompoundDimensions(::OperatorQuantumObject, dims::CompoundDimensions) = dims
218-
219-
# support Qobj.to and Qobj.from (but maybe this is not a good idea)
220-
# Base.getproperty(A::AbstractQuantumObject, key::Symbol) = getproperty(A, Val{key}())
221-
# Base.getproperty(A::AbstractQuantumObject{DT,KetQuantumObject,<:Dimensions}, ::Val{:to}) where {DT} = A.dims.to
222-
# Base.getproperty(A::AbstractQuantumObject{DT,KetQuantumObject,<:Dimensions}, ::Val{:from}) where {DT} = Dimensions(Field_list(length(A.dims))
223-
# Base.getproperty(A::AbstractQuantumObject{DT,BraQuantumObject,<:Dimensions}, ::Val{:to}) where {DT} = Dimensions(Field_list(length(A.dims))
224-
# Base.getproperty(A::AbstractQuantumObject{DT,BraQuantumObject,<:Dimensions}, ::Val{:from}) where {DT} = A.dims.to
225-
# Base.getproperty(A::AbstractQuantumObject{DT,OperatorQuantumObject}, ::Val{:to}) where {DT} = A.dims.to
226-
# Base.getproperty(A::AbstractQuantumObject{DT,OperatorQuantumObject,<:Dimensions}, ::Val{:from}) where {DT} = A.dims.to
227-
# Base.getproperty(A::AbstractQuantumObject{DT,OperatorQuantumObject,<:CompoundDimensions}, ::Val{:from}) where {DT} = A.dims.from
228-
# Base.getproperty(A::AbstractQuantumObject{DT,ObjType}, ::Val{T}) where {DT,ObjType<:QuantumObjectType,T} = throw(ArgumentError("Invalid property `$(T)` for $(A.type)"))
212+
Base.getproperty(A::AbstractQuantumObject, key::Symbol) = getproperty(A, Val{key}())
213+
Base.getproperty(A::AbstractQuantumObject, ::Val{K}) where {K} = getfield(A, K)
214+
215+
# support `AbstractQuantumObject.to` and `AbstractQuantumObject.from`
216+
Base.getproperty(A::AbstractQuantumObject{DT,KetQuantumObject,<:Dimensions}, ::Val{:to}) where {DT} = A.dims.to
217+
Base.getproperty(A::AbstractQuantumObject{DT,KetQuantumObject,Dimensions{N}}, ::Val{:from}) where {DT,N} = Field_list(N)
218+
Base.getproperty(A::AbstractQuantumObject{DT,BraQuantumObject,Dimensions{N}}, ::Val{:to}) where {DT,N} = Field_list(N)
219+
Base.getproperty(A::AbstractQuantumObject{DT,BraQuantumObject,<:Dimensions}, ::Val{:from}) where {DT} = A.dims.to
220+
Base.getproperty(A::AbstractQuantumObject{DT,OperatorQuantumObject}, ::Val{:to}) where {DT} = A.dims.to
221+
Base.getproperty(A::AbstractQuantumObject{DT,OperatorQuantumObject,<:Dimensions}, ::Val{:from}) where {DT} = A.dims.to
222+
Base.getproperty(A::AbstractQuantumObject{DT,OperatorQuantumObject,<:CompoundDimensions}, ::Val{:from}) where {DT} =
223+
A.dims.from
224+
Base.getproperty(
225+
A::AbstractQuantumObject{DT,ObjType,<:Dimensions},
226+
::KeyType,
227+
) where {
228+
DT,
229+
ObjType<:Union{SuperOperatorQuantumObject,OperatorBraQuantumObject,OperatorKetQuantumObject},
230+
KeyType<:Union{Val{:to},Val{:from}},
231+
} = A.dims.to
229232

230233
# functions for getting Float or Complex element type
231234
_FType(A::AbstractQuantumObject) = _FType(eltype(A))

src/qobj/space.jl

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,15 +14,14 @@ Field_list(N::Int) = SVector{N,AbstractSpace}(ntuple(i -> Field(), Val(N)))
1414
struct Space <: AbstractSpace
1515
size::Int
1616

17-
function Space(size::Int)
17+
Space(size::Int) =
1818
if size == 1
1919
return Field()
2020
elseif size > 1
2121
return new(size)
2222
else
2323
throw(DomainError(size, "The size of Space must be positive integer (≥ 1)."))
2424
end
25-
end
2625
end
2726
Base.string(s::Space) = string(s.size) # this is only used when printing AbstractDimensions
2827

test/core-test/quantum_objects.jl

Lines changed: 20 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -652,8 +652,26 @@
652652
ρ = kron(ρ1, ρ2)
653653
ρ1_ptr = ptrace(ρ, 1)
654654
ρ2_ptr = ptrace(ρ, 2)
655-
@test ρ1.data ρ1_ptr.data atol = 1e-10
656-
@test ρ2.data ρ2_ptr.data atol = 1e-10
655+
656+
# use CompoundDimensions to do partial trace
657+
ρ1_compound = Qobj(zeros(ComplexF64, 2, 2), dims = CompoundDimensions((2, 1), (2, 1)))
658+
basis2 = [tensor(eye(2), basis(2, i)) for i in 0:1]
659+
for b in basis2
660+
ρ1_compound += b' * ρ * b
661+
end
662+
ρ2_compound = Qobj(zeros(ComplexF64, 2, 2), dims = CompoundDimensions((1, 2), (1, 2)))
663+
basis1 = [tensor(basis(2, i), eye(2)) for i in 0:1]
664+
for b in basis1
665+
ρ2_compound += b' * ρ * b
666+
end
667+
@test ρ1.data ρ1_ptr.data ρ1_compound.data
668+
@test ρ2.data ρ2_ptr.data ρ2_compound.data
669+
@test ρ1.dims != ρ1_compound.dims
670+
@test ρ2.dims != ρ2_compound.dims
671+
ρ1_compound = ptrace(ρ1_compound, 1)
672+
ρ2_compound = ptrace(ρ2_compound, 2)
673+
@test ρ1.dims == ρ1_compound.dims
674+
@test ρ2.dims == ρ2_compound.dims
657675

658676
ψlist = [rand_ket(2), rand_ket(3), rand_ket(4), rand_ket(5)]
659677
ρlist = [rand_dm(2), rand_dm(3), rand_dm(4), rand_dm(5)]

0 commit comments

Comments
 (0)