Skip to content

Commit d3977c6

Browse files
authored
first try of Space and Dimensions (#359)
2 parents 270d3f6 + 2930089 commit d3977c6

20 files changed

+427
-153
lines changed

src/QuantumToolbox.jl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -79,6 +79,8 @@ include("progress_bar.jl")
7979
include("linear_maps.jl")
8080

8181
# Quantum Object
82+
include("qobj/space.jl")
83+
include("qobj/dimensions.jl")
8284
include("qobj/quantum_object_base.jl")
8385
include("qobj/quantum_object.jl")
8486
include("qobj/quantum_object_evo.jl")

src/negativity.jl

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -77,28 +77,37 @@ end
7777

7878
# for dense matrices
7979
function _partial_transpose::QuantumObject{<:AbstractArray,OperatorQuantumObject}, mask::Vector{Bool})
80+
isa.dims, CompoundDimensions) &&
81+
.to != ρ.from) &&
82+
throw(ArgumentError("Invalid partial transpose for dims = $(ρ.dims)"))
83+
8084
mask2 = [1 + Int(i) for i in mask]
8185
# mask2 has elements with values equal to 1 or 2
8286
# 1 - the subsystem don't need to be transposed
8387
# 2 - the subsystem need be transposed
8488

8589
nsys = length(mask2)
90+
dimslist = dims_to_list.to)
8691
pt_dims = reshape(Vector(1:(2*nsys)), (nsys, 2))
8792
pt_idx = [
8893
[pt_dims[n, mask2[n]] for n in 1:nsys] # origin value in mask2
8994
[pt_dims[n, 3-mask2[n]] for n in 1:nsys] # opposite value in mask2 (1 -> 2, and 2 -> 1)
9095
]
9196
return QuantumObject(
92-
reshape(permutedims(reshape.data, (ρ.dims..., ρ.dims...)), pt_idx), size(ρ)),
97+
reshape(permutedims(reshape.data, (dimslist..., dimslist...)), pt_idx), size(ρ)),
9398
Operator,
94-
ρ.dims,
99+
Dimensions(ρ.dims.to),
95100
)
96101
end
97102

98103
# for sparse matrices
99104
function _partial_transpose::QuantumObject{<:AbstractSparseArray,OperatorQuantumObject}, mask::Vector{Bool})
105+
isa.dims, CompoundDimensions) &&
106+
.to != ρ.from) &&
107+
throw(ArgumentError("Invalid partial transpose for dims = $(ρ.dims)"))
108+
100109
M, N = size(ρ)
101-
dimsTuple = Tuple(ρ.dims)
110+
dimsTuple = Tuple(dims_to_list.to))
102111
colptr = ρ.data.colptr
103112
rowval = ρ.data.rowval
104113
nzval = ρ.data.nzval

src/qobj/arithmetic_and_attributes.jl

Lines changed: 77 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -50,26 +50,63 @@ 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+
QType = promote_op_type(A, B)
72+
return QType(A.data * B.data, Operator, A.dims)
73+
end
74+
end
75+
else
76+
@eval begin
77+
function LinearAlgebra.:(*)(
78+
A::AbstractQuantumObject{DT1,OperatorQuantumObject,$ADimType{NA}},
79+
B::AbstractQuantumObject{DT2,OperatorQuantumObject,$BDimType{NB}},
80+
) where {DT1,DT2,NA,NB}
81+
check_mul_dims(A.from, B.to)
82+
QType = promote_op_type(A, B)
83+
return QType(A.data * B.data, Operator, CompoundDimensions(A.to, B.from))
84+
end
85+
end
86+
end
87+
end
88+
end
89+
5390
function LinearAlgebra.:(*)(
5491
A::AbstractQuantumObject{DT1,OperatorQuantumObject},
5592
B::QuantumObject{DT2,KetQuantumObject},
5693
) where {DT1,DT2}
57-
check_dims(A, B)
58-
return QuantumObject(A.data * B.data, Ket, A.dims)
94+
check_mul_dims(A.from, B.to)
95+
return QuantumObject(A.data * B.data, Ket, Dimensions(A.to))
5996
end
6097
function LinearAlgebra.:(*)(
6198
A::QuantumObject{DT1,BraQuantumObject},
6299
B::AbstractQuantumObject{DT2,OperatorQuantumObject},
63100
) where {DT1,DT2}
64-
check_dims(A, B)
65-
return QuantumObject(A.data * B.data, Bra, A.dims)
101+
check_mul_dims(A.from, B.to)
102+
return QuantumObject(A.data * B.data, Bra, Dimensions(B.from))
66103
end
67104
function LinearAlgebra.:(*)(
68105
A::QuantumObject{DT1,KetQuantumObject},
69106
B::QuantumObject{DT2,BraQuantumObject},
70107
) where {DT1,DT2}
71108
check_dims(A, B)
72-
return QuantumObject(A.data * B.data, Operator, A.dims)
109+
return QuantumObject(A.data * B.data, Operator, A.dims) # to align with QuTiP, don't use kron(A, B) to do it.
73110
end
74111
function LinearAlgebra.:(*)(
75112
A::QuantumObject{DT1,BraQuantumObject},
@@ -196,7 +233,7 @@ Lazy matrix transpose of the [`AbstractQuantumObject`](@ref).
196233
LinearAlgebra.transpose(
197234
A::AbstractQuantumObject{DT,OpType},
198235
) where {DT,OpType<:Union{OperatorQuantumObject,SuperOperatorQuantumObject}} =
199-
get_typename_wrapper(A)(transpose(A.data), A.type, A.dims)
236+
get_typename_wrapper(A)(transpose(A.data), A.type, transpose(A.dims))
200237

201238
@doc raw"""
202239
A'
@@ -211,13 +248,15 @@ Lazy adjoint (conjugate transposition) of the [`AbstractQuantumObject`](@ref)
211248
LinearAlgebra.adjoint(
212249
A::AbstractQuantumObject{DT,OpType},
213250
) where {DT,OpType<:Union{OperatorQuantumObject,SuperOperatorQuantumObject}} =
214-
get_typename_wrapper(A)(adjoint(A.data), A.type, A.dims)
215-
LinearAlgebra.adjoint(A::QuantumObject{DT,KetQuantumObject}) where {DT} = QuantumObject(adjoint(A.data), Bra, A.dims)
216-
LinearAlgebra.adjoint(A::QuantumObject{DT,BraQuantumObject}) where {DT} = QuantumObject(adjoint(A.data), Ket, A.dims)
251+
get_typename_wrapper(A)(adjoint(A.data), A.type, transpose(A.dims))
252+
LinearAlgebra.adjoint(A::QuantumObject{DT,KetQuantumObject}) where {DT} =
253+
QuantumObject(adjoint(A.data), Bra, transpose(A.dims))
254+
LinearAlgebra.adjoint(A::QuantumObject{DT,BraQuantumObject}) where {DT} =
255+
QuantumObject(adjoint(A.data), Ket, transpose(A.dims))
217256
LinearAlgebra.adjoint(A::QuantumObject{DT,OperatorKetQuantumObject}) where {DT} =
218-
QuantumObject(adjoint(A.data), OperatorBra, A.dims)
257+
QuantumObject(adjoint(A.data), OperatorBra, transpose(A.dims))
219258
LinearAlgebra.adjoint(A::QuantumObject{DT,OperatorBraQuantumObject}) where {DT} =
220-
QuantumObject(adjoint(A.data), OperatorKet, A.dims)
259+
QuantumObject(adjoint(A.data), OperatorKet, transpose(A.dims))
221260

222261
@doc raw"""
223262
inv(A::AbstractQuantumObject)
@@ -557,14 +596,19 @@ function ptrace(QO::QuantumObject{<:AbstractArray,KetQuantumObject}, sel::Union{
557596
(n_d == 1) && return ket2dm(QO) # ptrace should always return Operator
558597
end
559598

599+
dimslist = dims_to_list(QO.dims)
560600
_sort_sel = sort(SVector{length(sel),Int}(sel))
561-
ρtr, dkeep = _ptrace_ket(QO.data, QO.dims, _sort_sel)
562-
return QuantumObject(ρtr, type = Operator, dims = dkeep)
601+
ρtr, dkeep = _ptrace_ket(QO.data, dimslist, _sort_sel)
602+
return QuantumObject(ρtr, type = Operator, dims = Dimensions(dkeep))
563603
end
564604

565605
ptrace(QO::QuantumObject{<:AbstractArray,BraQuantumObject}, sel::Union{AbstractVector{Int},Tuple}) = ptrace(QO', sel)
566606

567607
function ptrace(QO::QuantumObject{<:AbstractArray,OperatorQuantumObject}, sel::Union{AbstractVector{Int},Tuple})
608+
isa(QO.dims, CompoundDimensions) &&
609+
(QO.to != QO.from) &&
610+
throw(ArgumentError("Invalid partial trace for dims = $(QO.dims)"))
611+
568612
_non_static_array_warning("sel", sel)
569613

570614
n_s = length(sel)
@@ -580,9 +624,10 @@ function ptrace(QO::QuantumObject{<:AbstractArray,OperatorQuantumObject}, sel::U
580624
(n_d == 1) && return QO
581625
end
582626

627+
dimslist = dims_to_list(QO.to)
583628
_sort_sel = sort(SVector{length(sel),Int}(sel))
584-
ρtr, dkeep = _ptrace_oper(QO.data, QO.dims, _sort_sel)
585-
return QuantumObject(ρtr, type = Operator, dims = dkeep)
629+
ρtr, dkeep = _ptrace_oper(QO.data, dimslist, _sort_sel)
630+
return QuantumObject(ρtr, type = Operator, dims = Dimensions(dkeep))
586631
end
587632
ptrace(QO::QuantumObject, sel::Int) = ptrace(QO, SVector(sel))
588633

@@ -758,24 +803,31 @@ function permute(
758803
order_svector = SVector{length(order),Int}(order) # convert it to SVector for performance
759804

760805
# obtain the arguments: dims for reshape; perm for PermutedDimsArray
761-
dims, perm = _dims_and_perm(A.type, A.dims, order_svector, length(order_svector))
806+
dimslist = dims_to_list(A.dims)
807+
dims, perm = _dims_and_perm(A.type, dimslist, order_svector, length(order_svector))
762808

763809
return QuantumObject(
764810
reshape(permutedims(reshape(A.data, dims...), Tuple(perm)), size(A)),
765811
A.type,
766-
A.dims[order_svector],
812+
Dimensions(A.dims.to[order_svector]),
767813
)
768814
end
769815

770-
function _dims_and_perm(
816+
_dims_and_perm(
771817
::ObjType,
772-
dims::SVector{N,Int},
818+
dimslist::SVector{N,Int},
773819
order::AbstractVector{Int},
774820
L::Int,
775-
) where {ObjType<:Union{KetQuantumObject,BraQuantumObject},N}
776-
return reverse(dims), reverse((L + 1) .- order)
777-
end
821+
) where {ObjType<:Union{KetQuantumObject,BraQuantumObject},N} = reverse(dimslist), reverse((L + 1) .- order)
778822

779-
function _dims_and_perm(::OperatorQuantumObject, dims::SVector{N,Int}, order::AbstractVector{Int}, L::Int) where {N}
780-
return reverse(vcat(dims, dims)), reverse((2 * L + 1) .- vcat(order, order .+ L))
781-
end
823+
# if dims originates from Dimensions
824+
_dims_and_perm(::OperatorQuantumObject, dimslist::SVector{N,Int}, order::AbstractVector{Int}, L::Int) where {N} =
825+
reverse(vcat(dimslist, dimslist)), reverse((2 * L + 1) .- vcat(order, order .+ L))
826+
827+
# if dims originates from CompoundDimensions
828+
_dims_and_perm(
829+
::OperatorQuantumObject,
830+
dimslist::SVector{2,SVector{N,Int}},
831+
order::AbstractVector{Int},
832+
L::Int,
833+
) where {N} = reverse(vcat(dimslist[1], dimslist[2])), reverse((2 * L + 1) .- vcat(order, order .+ L))

src/qobj/dimensions.jl

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
export AbstractDimensions, Dimensions, CompoundDimensions
2+
3+
abstract type AbstractDimensions{N} end
4+
5+
# this show function is for printing AbstractDimensions
6+
function Base.show(io::IO, svec::SVector{N,AbstractSpace}) where {N}
7+
print(io, "[")
8+
join(io, string.(svec), ", ")
9+
return print(io, "]")
10+
end
11+
12+
struct Dimensions{N} <: AbstractDimensions{N}
13+
to::SVector{N,AbstractSpace}
14+
end
15+
function Dimensions(dims::Union{AbstractVector{T},NTuple{N,T}}) where {T<:Integer,N}
16+
_non_static_array_warning("dims", dims)
17+
L = length(dims)
18+
(L > 0) || throw(DomainError(dims, "The argument dims must be of non-zero length"))
19+
20+
return Dimensions{L}(SVector{L,AbstractSpace}(Space.(dims)))
21+
end
22+
Dimensions(dims::Int) = Dimensions(SVector{1,Int}(dims))
23+
Dimensions(dims::Any) = throw(
24+
ArgumentError(
25+
"The argument dims must be a Tuple or a StaticVector of non-zero length and contain only positive integers.",
26+
),
27+
)
28+
29+
Base.show(io::IO, D::Dimensions) = print(io, D.to)
30+
31+
struct CompoundDimensions{N} <: AbstractDimensions{N}
32+
# note that the number `N` should be the same for both `to` and `from`
33+
to::SVector{N,AbstractSpace} # space acting on the left
34+
from::SVector{N,AbstractSpace} # space acting on the right
35+
end
36+
function CompoundDimensions(dims::Union{AbstractVector{T},NTuple{N,T}}) where {T<:Union{AbstractVector,NTuple},N}
37+
(length(dims) != 2) && throw(ArgumentError("Invalid dims = $dims"))
38+
39+
_non_static_array_warning("dims[1]", dims[1])
40+
_non_static_array_warning("dims[2]", dims[2])
41+
42+
L1 = length(dims[1])
43+
L2 = length(dims[2])
44+
((L1 > 0) && (L1 == L2)) || throw(
45+
DomainError(
46+
(L1, L2),
47+
"The length of the arguments `dims[1]` and `dims[2]` must be in the same length and have at least one element.",
48+
),
49+
)
50+
51+
return CompoundDimensions{L1}(
52+
SVector{L1,AbstractSpace}(Space.(dims[1])),
53+
SVector{L1,AbstractSpace}(Space.(dims[2])),
54+
)
55+
end
56+
57+
Base.show(io::IO, D::CompoundDimensions) = print(io, "[", D.to, ", ", D.from, "]")
58+
59+
_gen_dims(dims::AbstractDimensions) = dims
60+
_gen_dims(dims::Union{AbstractVector{T},NTuple{N,T}}) where {T<:Integer,N} = Dimensions(dims)
61+
_gen_dims(dims::Union{AbstractVector{T},NTuple{N,T}}) where {T<:Union{AbstractVector,NTuple},N} =
62+
CompoundDimensions(dims)
63+
_gen_dims(dims::Any) = Dimensions(dims)
64+
65+
# obtain dims in the type of SVector with integers
66+
dims_to_list(dimsvec::SVector{N,AbstractSpace}) where {N} = SVector{N,Int}(ntuple(i -> dimsvec[i].size, Val(N)))
67+
dims_to_list(dims::Dimensions) = dims_to_list(dims.to)
68+
dims_to_list(dims::CompoundDimensions) = SVector{2}(dims_to_list(dims.to), dims_to_list(dims.from))
69+
70+
Base.:(==)(vect::AbstractVector{T}, dims::AbstractDimensions) where {T} = vect == dims_to_list(dims)
71+
Base.:(==)(dims::AbstractDimensions, vect::AbstractVector{T}) where {T} = vect == dims
72+
73+
Base.length(::AbstractDimensions{N}) where {N} = N
74+
75+
Base.prod(dims::Dimensions) = prod(dims.to)
76+
77+
LinearAlgebra.transpose(dims::Dimensions) = dims
78+
LinearAlgebra.transpose(dims::CompoundDimensions) = CompoundDimensions(dims.from, dims.to) # switch `to` and `from`

src/qobj/eigsolve.jl

Lines changed: 7 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,11 @@ export eigenenergies, eigenstates, eigsolve
77
export eigsolve_al
88

99
@doc raw"""
10-
struct EigsolveResult{T1<:Vector{<:Number}, T2<:AbstractMatrix{<:Number}, ObjType<:Union{Nothing,OperatorQuantumObject,SuperOperatorQuantumObject},N}
10+
struct EigsolveResult{T1<:Vector{<:Number}, T2<:AbstractMatrix{<:Number}, ObjType<:Union{Nothing,OperatorQuantumObject,SuperOperatorQuantumObject},DimType<:AbstractDimensions}
1111
values::T1
1212
vectors::T2
1313
type::ObjType
14-
dims::SVector{N,Int}
14+
dims::DimType
1515
iter::Int
1616
numops::Int
1717
converged::Bool
@@ -23,7 +23,7 @@ A struct containing the eigenvalues, the eigenvectors, and some information from
2323
- `values::AbstractVector`: the eigenvalues
2424
- `vectors::AbstractMatrix`: the transformation matrix (eigenvectors)
2525
- `type::Union{Nothing,QuantumObjectType}`: the type of [`QuantumObject`](@ref), or `nothing` means solving eigen equation for general matrix
26-
- `dims::SVector`: the `dims` of [`QuantumObject`](@ref)
26+
- `dims::AbstractDimensions`: the `dims` of [`QuantumObject`](@ref)
2727
- `iter::Int`: the number of iteration during the solving process
2828
- `numops::Int` : number of times the linear map was applied in krylov methods
2929
- `converged::Bool`: Whether the result is converged
@@ -70,12 +70,12 @@ struct EigsolveResult{
7070
T1<:Vector{<:Number},
7171
T2<:AbstractMatrix{<:Number},
7272
ObjType<:Union{Nothing,OperatorQuantumObject,SuperOperatorQuantumObject},
73-
N,
73+
DimType<:AbstractDimensions,
7474
}
7575
values::T1
7676
vectors::T2
7777
type::ObjType
78-
dims::SVector{N,Int}
78+
dims::DimType
7979
iter::Int
8080
numops::Int
8181
converged::Bool
@@ -159,7 +159,7 @@ function _eigsolve(
159159
A,
160160
b::AbstractVector{T},
161161
type::ObjType,
162-
dims::SVector,
162+
dims::AbstractDimensions,
163163
k::Int = 1,
164164
m::Int = max(20, 2 * k + 1);
165165
tol::Real = 1e-8,
@@ -296,7 +296,7 @@ function eigsolve(
296296
A;
297297
v0::Union{Nothing,AbstractVector} = nothing,
298298
type::Union{Nothing,OperatorQuantumObject,SuperOperatorQuantumObject} = nothing,
299-
dims = SVector{0,Int}(),
299+
dims = Dimensions{0}(SVector{0,AbstractSpace}()),
300300
sigma::Union{Nothing,Real} = nothing,
301301
k::Int = 1,
302302
krylovdim::Int = max(20, 2 * k + 1),
@@ -309,8 +309,6 @@ function eigsolve(
309309
isH = ishermitian(A)
310310
v0 === nothing && (v0 = normalize!(rand(T, size(A, 1))))
311311

312-
dims = SVector(dims)
313-
314312
if sigma === nothing
315313
res = _eigsolve(A, v0, type, dims, k, krylovdim, tol = tol, maxiter = maxiter)
316314
vals = res.values

0 commit comments

Comments
 (0)