Skip to content

Commit 5af33c9

Browse files
Allow only the struct to avoid type instabilities
1 parent eafeb59 commit 5af33c9

26 files changed

+153
-155
lines changed

docs/src/getting_started/type_stability.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -183,7 +183,7 @@ The `dims` field contains the dimensions of the subsystems (in this case, three
183183

184184
```@example type-stability
185185
function reshape_operator_data(dims)
186-
op = Qobj(randn(prod(dims), prod(dims)), type=Operator, dims=dims)
186+
op = Qobj(randn(prod(dims), prod(dims)), type=Operator(), dims=dims)
187187
op_dims = op.dims
188188
op_data = op.data
189189
return reshape(op_data, vcat(op_dims, op_dims)...)
@@ -234,7 +234,7 @@ function my_fock(N::Int, j::Int = 0; sparse::Bool = false)
234234
array = zeros(ComplexF64, N)
235235
array[j+1] = 1
236236
end
237-
return QuantumObject(array; type = Ket)
237+
return QuantumObject(array; type = Ket())
238238
end
239239
@show my_fock(2, 1)
240240
@show my_fock(2, 1; sparse = true)
@@ -262,7 +262,7 @@ function my_fock_good(N::Int, j::Int = 0; sparse::Val = Val(false))
262262
else
263263
array = sparsevec([j + 1], [1.0 + 0im], N)
264264
end
265-
return QuantumObject(array; type = Ket)
265+
return QuantumObject(array; type = Ket())
266266
end
267267
@show my_fock_good(2, 1)
268268
@show my_fock_good(2, 1; sparse = Val(true))

docs/src/users_guide/QuantumObject/QuantumObject.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ Qobj(M, dims = SVector(2, 2)) # dims as StaticArrays.SVector (recommended)
5454
Please note that here we put the `dims` as a tuple `(2, 2)`. Although it supports also `Vector` type (`dims = [2, 2]`), it is recommended to use `Tuple` or `SVector` from [`StaticArrays.jl`](https://github.com/JuliaArrays/StaticArrays.jl) to improve performance. For a brief explanation on the impact of the type of `dims`, see the Section [The Importance of Type-Stability](@ref doc:Type-Stability).
5555

5656
```@example Qobj
57-
Qobj(rand(4, 4), type = SuperOperator)
57+
Qobj(rand(4, 4), type = SuperOperator())
5858
```
5959

6060
!!! note "Difference between `dims` and `size`"

docs/src/users_guide/states_and_operators.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -389,7 +389,7 @@ B = Qobj(rand(ComplexF64, N, N))
389389
mat2vec(A * ρ * B) ≈ spre(A) * spost(B) * mat2vec(ρ) ≈ sprepost(A, B) * mat2vec(ρ)
390390
```
391391

392-
In addition, dynamical generators on this extended space, often called Liouvillian superoperators, can be created using the [`liouvillian`](@ref) function. Each of these takes a Hamiltonian along with a list of collapse operators, and returns a [`type=SuperOperator`](@ref SuperOperator) object that can be exponentiated to find the superoperator for that evolution.
392+
In addition, dynamical generators on this extended space, often called Liouvillian superoperators, can be created using the [`liouvillian`](@ref) function. Each of these takes a Hamiltonian along with a list of collapse operators, and returns a [`QuantumObject`](@ref) of type [`SuperOperator`](@ref) that can be exponentiated to find the superoperator for that evolution.
393393

394394
```@example states_and_operators
395395
H = 10 * sigmaz()

src/negativity.jl

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -97,7 +97,7 @@ function _partial_transpose(ρ::QuantumObject{Operator}, mask::Vector{Bool})
9797
]
9898
return QuantumObject(
9999
reshape(permutedims(reshape.data, (dims..., dims...)), pt_idx), size(ρ)),
100-
Operator,
100+
Operator(),
101101
Dimensions.dimensions.to),
102102
)
103103
end
@@ -144,5 +144,5 @@ function _partial_transpose(
144144
end
145145
end
146146

147-
return QuantumObject(sparse(I_pt, J_pt, V_pt, M, N), Operator, ρ.dimensions)
147+
return QuantumObject(sparse(I_pt, J_pt, V_pt, M, N), Operator(), ρ.dimensions)
148148
end

src/qobj/arithmetic_and_attributes.jl

Lines changed: 14 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ for ADimType in (:Dimensions, :GeneralDimensions)
6969
)
7070
check_dimensions(A, B)
7171
QType = promote_op_type(A, B)
72-
return QType(A.data * B.data, Operator, A.dimensions)
72+
return QType(A.data * B.data, Operator(), A.dimensions)
7373
end
7474
end
7575
else
@@ -82,7 +82,7 @@ for ADimType in (:Dimensions, :GeneralDimensions)
8282
QType = promote_op_type(A, B)
8383
return QType(
8484
A.data * B.data,
85-
Operator,
85+
Operator(),
8686
GeneralDimensions(get_dimensions_to(A), get_dimensions_from(B)),
8787
)
8888
end
@@ -93,35 +93,35 @@ end
9393

9494
function Base.:(*)(A::AbstractQuantumObject{Operator}, B::QuantumObject{Ket,<:Dimensions})
9595
check_mul_dimensions(get_dimensions_from(A), get_dimensions_to(B))
96-
return QuantumObject(A.data * B.data, Ket, Dimensions(get_dimensions_to(A)))
96+
return QuantumObject(A.data * B.data, Ket(), Dimensions(get_dimensions_to(A)))
9797
end
9898
function Base.:(*)(A::QuantumObject{Bra,<:Dimensions}, B::AbstractQuantumObject{Operator})
9999
check_mul_dimensions(get_dimensions_from(A), get_dimensions_to(B))
100-
return QuantumObject(A.data * B.data, Bra, Dimensions(get_dimensions_from(B)))
100+
return QuantumObject(A.data * B.data, Bra(), Dimensions(get_dimensions_from(B)))
101101
end
102102
function Base.:(*)(A::QuantumObject{Ket}, B::QuantumObject{Bra})
103103
check_dimensions(A, B)
104-
return QuantumObject(A.data * B.data, Operator, A.dimensions) # to align with QuTiP, don't use kron(A, B) to do it.
104+
return QuantumObject(A.data * B.data, Operator(), A.dimensions) # to align with QuTiP, don't use kron(A, B) to do it.
105105
end
106106
function Base.:(*)(A::QuantumObject{Bra}, B::QuantumObject{Ket})
107107
check_dimensions(A, B)
108108
return A.data * B.data
109109
end
110110
function Base.:(*)(A::AbstractQuantumObject{SuperOperator}, B::QuantumObject{Operator})
111111
check_dimensions(A, B)
112-
return QuantumObject(vec2mat(A.data * mat2vec(B.data)), Operator, A.dimensions)
112+
return QuantumObject(vec2mat(A.data * mat2vec(B.data)), Operator(), A.dimensions)
113113
end
114114
function Base.:(*)(A::QuantumObject{OperatorBra}, B::QuantumObject{OperatorKet})
115115
check_dimensions(A, B)
116116
return A.data * B.data
117117
end
118118
function Base.:(*)(A::AbstractQuantumObject{SuperOperator}, B::QuantumObject{OperatorKet})
119119
check_dimensions(A, B)
120-
return QuantumObject(A.data * B.data, OperatorKet, A.dimensions)
120+
return QuantumObject(A.data * B.data, OperatorKet(), A.dimensions)
121121
end
122122
function Base.:(*)(A::QuantumObject{OperatorBra}, B::AbstractQuantumObject{SuperOperator})
123123
check_dimensions(A, B)
124-
return QuantumObject(A.data * B.data, OperatorBra, A.dimensions)
124+
return QuantumObject(A.data * B.data, OperatorBra(), A.dimensions)
125125
end
126126

127127
Base.:(^)(A::QuantumObject, n::T) where {T<:Number} = QuantumObject(^(A.data, n), A.type, A.dimensions)
@@ -213,10 +213,10 @@ Lazy adjoint (conjugate transposition) of the [`AbstractQuantumObject`](@ref)
213213
"""
214214
Base.adjoint(A::AbstractQuantumObject{OpType}) where {OpType<:Union{Operator,SuperOperator}} =
215215
get_typename_wrapper(A)(adjoint(A.data), A.type, adjoint(A.dimensions))
216-
Base.adjoint(A::QuantumObject{Ket}) = QuantumObject(adjoint(A.data), Bra, adjoint(A.dimensions))
217-
Base.adjoint(A::QuantumObject{Bra}) = QuantumObject(adjoint(A.data), Ket, adjoint(A.dimensions))
218-
Base.adjoint(A::QuantumObject{OperatorKet}) = QuantumObject(adjoint(A.data), OperatorBra, adjoint(A.dimensions))
219-
Base.adjoint(A::QuantumObject{OperatorBra}) = QuantumObject(adjoint(A.data), OperatorKet, adjoint(A.dimensions))
216+
Base.adjoint(A::QuantumObject{Ket}) = QuantumObject(adjoint(A.data), Bra(), adjoint(A.dimensions))
217+
Base.adjoint(A::QuantumObject{Bra}) = QuantumObject(adjoint(A.data), Ket(), adjoint(A.dimensions))
218+
Base.adjoint(A::QuantumObject{OperatorKet}) = QuantumObject(adjoint(A.data), OperatorBra(), adjoint(A.dimensions))
219+
Base.adjoint(A::QuantumObject{OperatorBra}) = QuantumObject(adjoint(A.data), OperatorKet(), adjoint(A.dimensions))
220220

221221
@doc raw"""
222222
inv(A::AbstractQuantumObject)
@@ -521,7 +521,7 @@ function ptrace(QO::QuantumObject{Ket}, sel::Union{AbstractVector{Int},Tuple})
521521

522522
_sort_sel = sort(SVector{length(sel),Int}(sel))
523523
ρtr, dkeep = _ptrace_ket(QO.data, QO.dims, _sort_sel)
524-
return QuantumObject(ρtr, type = Operator, dims = Dimensions(dkeep))
524+
return QuantumObject(ρtr, type = Operator(), dims = Dimensions(dkeep))
525525
end
526526

527527
ptrace(QO::QuantumObject{Bra}, sel::Union{AbstractVector{Int},Tuple}) = ptrace(QO', sel)
@@ -549,7 +549,7 @@ function ptrace(QO::QuantumObject{Operator}, sel::Union{AbstractVector{Int},Tupl
549549
dims = dimensions_to_dims(get_dimensions_to(QO))
550550
_sort_sel = sort(SVector{length(sel),Int}(sel))
551551
ρtr, dkeep = _ptrace_oper(QO.data, dims, _sort_sel)
552-
return QuantumObject(ρtr, type = Operator, dims = Dimensions(dkeep))
552+
return QuantumObject(ρtr, type = Operator(), dims = Dimensions(dkeep))
553553
end
554554
ptrace(QO::QuantumObject, sel::Int) = ptrace(QO, SVector(sel))
555555

src/qobj/eigsolve.jl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -91,9 +91,9 @@ Base.iterate(res::EigsolveResult) = (res.values, Val(:vector_list))
9191
Base.iterate(res::EigsolveResult{T1,T2,Nothing}, ::Val{:vector_list}) where {T1,T2} =
9292
([res.vectors[:, k] for k in 1:length(res.values)], Val(:vectors))
9393
Base.iterate(res::EigsolveResult{T1,T2,Operator}, ::Val{:vector_list}) where {T1,T2} =
94-
([QuantumObject(res.vectors[:, k], Ket, res.dimensions) for k in 1:length(res.values)], Val(:vectors))
94+
([QuantumObject(res.vectors[:, k], Ket(), res.dimensions) for k in 1:length(res.values)], Val(:vectors))
9595
Base.iterate(res::EigsolveResult{T1,T2,SuperOperator}, ::Val{:vector_list}) where {T1,T2} =
96-
([QuantumObject(res.vectors[:, k], OperatorKet, res.dimensions) for k in 1:length(res.values)], Val(:vectors))
96+
([QuantumObject(res.vectors[:, k], OperatorKet(), res.dimensions) for k in 1:length(res.values)], Val(:vectors))
9797
Base.iterate(res::EigsolveResult, ::Val{:vectors}) = (res.vectors, Val(:done))
9898
Base.iterate(res::EigsolveResult, ::Val{:done}) = nothing
9999

@@ -408,7 +408,7 @@ function eigsolve_al(
408408
L_evo = _mesolve_make_L_QobjEvo(H, c_ops)
409409
prob = mesolveProblem(
410410
L_evo,
411-
QuantumObject(ρ0, type = Operator, dims = H.dimensions),
411+
QuantumObject(ρ0, type = Operator(), dims = H.dimensions),
412412
[zero(T), T];
413413
params = params,
414414
progress_bar = Val(false),

src/qobj/functions.jl

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -208,7 +208,7 @@ for ADimType in (:Dimensions, :GeneralDimensions)
208208
_lazy_tensor_warning(A.data, B.data)
209209
return QType(
210210
kron(A.data, B.data),
211-
Operator,
211+
Operator(),
212212
GeneralDimensions(
213213
(get_dimensions_to(A)..., get_dimensions_to(B)...),
214214
(get_dimensions_from(A)..., get_dimensions_from(B)...),
@@ -230,7 +230,7 @@ for AOpType in (:Ket, :Bra, :Operator)
230230
_lazy_tensor_warning(A.data, B.data)
231231
return QType(
232232
kron(A.data, B.data),
233-
Operator,
233+
Operator(),
234234
GeneralDimensions(
235235
(get_dimensions_to(A)..., get_dimensions_to(B)...),
236236
(get_dimensions_from(A)..., get_dimensions_from(B)...),
@@ -267,7 +267,7 @@ Convert a quantum object from vector ([`OperatorKet`](@ref)-type) to matrix ([`O
267267
!!! note
268268
`vector_to_operator` is a synonym of `vec2mat`.
269269
"""
270-
vec2mat(A::QuantumObject{OperatorKet}) = QuantumObject(vec2mat(A.data), Operator, A.dimensions)
270+
vec2mat(A::QuantumObject{OperatorKet}) = QuantumObject(vec2mat(A.data), Operator(), A.dimensions)
271271

272272
@doc raw"""
273273
mat2vec(A::QuantumObject)
@@ -278,7 +278,7 @@ Convert a quantum object from matrix ([`Operator`](@ref)-type) to vector ([`Oper
278278
!!! note
279279
`operator_to_vector` is a synonym of `mat2vec`.
280280
"""
281-
mat2vec(A::QuantumObject{Operator}) = QuantumObject(mat2vec(A.data), OperatorKet, A.dimensions)
281+
mat2vec(A::QuantumObject{Operator}) = QuantumObject(mat2vec(A.data), OperatorKet(), A.dimensions)
282282

283283
@doc raw"""
284284
mat2vec(A::AbstractMatrix)

src/qobj/operators.jl

Lines changed: 18 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,7 @@ function rand_unitary(dimensions::Union{Dimensions,AbstractVector{Int},Tuple}, :
4848
# Because inv(Λ) ⋅ R has real and strictly positive elements, Q · Λ is therefore Haar distributed.
4949
Λ = diag(R) # take the diagonal elements of R
5050
Λ ./= abs.(Λ) # rescaling the elements
51-
return QuantumObject(to_dense(Q * Diagonal(Λ)); type = Operator, dims = dimensions)
51+
return QuantumObject(to_dense(Q * Diagonal(Λ)); type = Operator(), dims = dimensions)
5252
end
5353
function rand_unitary(dimensions::Union{Dimensions,AbstractVector{Int},Tuple}, ::Val{:exp})
5454
N = prod(dimensions)
@@ -57,7 +57,7 @@ function rand_unitary(dimensions::Union{Dimensions,AbstractVector{Int},Tuple}, :
5757
Z = randn(ComplexF64, N, N)
5858

5959
# generate Hermitian matrix
60-
H = QuantumObject((Z + Z') / 2; type = Operator, dims = dimensions)
60+
H = QuantumObject((Z + Z') / 2; type = Operator(), dims = dimensions)
6161

6262
return to_dense(exp(-1.0im * H))
6363
end
@@ -99,7 +99,7 @@ julia> fock(20, 3)' * a * fock(20, 4)
9999
2.0 + 0.0im
100100
```
101101
"""
102-
destroy(N::Int) = QuantumObject(spdiagm(1 => Array{ComplexF64}(sqrt.(1:(N-1)))), Operator, N)
102+
destroy(N::Int) = QuantumObject(spdiagm(1 => Array{ComplexF64}(sqrt.(1:(N-1)))), Operator(), N)
103103

104104
@doc raw"""
105105
create(N::Int)
@@ -125,7 +125,7 @@ julia> fock(20, 4)' * a_d * fock(20, 3)
125125
2.0 + 0.0im
126126
```
127127
"""
128-
create(N::Int) = QuantumObject(spdiagm(-1 => Array{ComplexF64}(sqrt.(1:(N-1)))), Operator, N)
128+
create(N::Int) = QuantumObject(spdiagm(-1 => Array{ComplexF64}(sqrt.(1:(N-1)))), Operator(), N)
129129

130130
@doc raw"""
131131
displace(N::Int, α::Number)
@@ -166,7 +166,7 @@ Bosonic number operator with Hilbert space cutoff `N`.
166166
167167
This operator is defined as ``\hat{N}=\hat{a}^\dagger \hat{a}``, where ``\hat{a}`` is the bosonic annihilation operator.
168168
"""
169-
num(N::Int) = QuantumObject(spdiagm(0 => Array{ComplexF64}(0:(N-1))), Operator, N)
169+
num(N::Int) = QuantumObject(spdiagm(0 => Array{ComplexF64}(0:(N-1))), Operator(), N)
170170

171171
@doc raw"""
172172
position(N::Int)
@@ -222,7 +222,7 @@ function phase(N::Int, ϕ0::Real = 0)
222222
N_list = collect(0:(N-1))
223223
ϕ = ϕ0 .+ (2 * π / N) .* N_list
224224
states = [exp.((1.0im * ϕ[m]) .* N_list) ./ sqrt(N) for m in 1:N]
225-
return QuantumObject(sum([ϕ[m] * states[m] * states[m]' for m in 1:N]); type = Operator, dims = N)
225+
return QuantumObject(sum([ϕ[m] * states[m] * states[m]' for m in 1:N]); type = Operator(), dims = N)
226226
end
227227

228228
@doc raw"""
@@ -276,36 +276,36 @@ function jmat(j::Real, ::Val{:x})
276276
throw(ArgumentError("The spin quantum number (j) must be a non-negative integer or half-integer."))
277277

278278
σ = _jm(j)
279-
return QuantumObject((σ' + σ) / 2, Operator, Int(J))
279+
return QuantumObject((σ' + σ) / 2, Operator(), Int(J))
280280
end
281281
function jmat(j::Real, ::Val{:y})
282282
J = 2 * j + 1
283283
((floor(J) != J) || (j < 0)) &&
284284
throw(ArgumentError("The spin quantum number (j) must be a non-negative integer or half-integer."))
285285

286286
σ = _jm(j)
287-
return QuantumObject((σ' - σ) / 2im, Operator, Int(J))
287+
return QuantumObject((σ' - σ) / 2im, Operator(), Int(J))
288288
end
289289
function jmat(j::Real, ::Val{:z})
290290
J = 2 * j + 1
291291
((floor(J) != J) || (j < 0)) &&
292292
throw(ArgumentError("The spin quantum number (j) must be a non-negative integer or half-integer."))
293293

294-
return QuantumObject(_jz(j), Operator, Int(J))
294+
return QuantumObject(_jz(j), Operator(), Int(J))
295295
end
296296
function jmat(j::Real, ::Val{:+})
297297
J = 2 * j + 1
298298
((floor(J) != J) || (j < 0)) &&
299299
throw(ArgumentError("The spin quantum number (j) must be a non-negative integer or half-integer."))
300300

301-
return QuantumObject(adjoint(_jm(j)), Operator, Int(J))
301+
return QuantumObject(adjoint(_jm(j)), Operator(), Int(J))
302302
end
303303
function jmat(j::Real, ::Val{:-})
304304
J = 2 * j + 1
305305
((floor(J) != J) || (j < 0)) &&
306306
throw(ArgumentError("The spin quantum number (j) must be a non-negative integer or half-integer."))
307307

308-
return QuantumObject(_jm(j), Operator, Int(J))
308+
return QuantumObject(_jm(j), Operator(), Int(J))
309309
end
310310
jmat(j::Real, ::Val{T}) where {T} = throw(ArgumentError("Invalid spin operator: $(T)"))
311311

@@ -428,8 +428,7 @@ Note that `type` can only be either [`Operator`](@ref) or [`SuperOperator`](@ref
428428
!!! note
429429
`qeye` is a synonym of `eye`.
430430
"""
431-
function eye(N::Int; type = Operator, dims = nothing)
432-
type = _get_type(type)
431+
function eye(N::Int; type = Operator(), dims = nothing)
433432
if dims isa Nothing
434433
dims = isa(type, Operator) ? N : isqrt(N)
435434
end
@@ -486,7 +485,7 @@ function _Jordan_Wigner(::Val{N}, j::Int, op::QuantumObject{Operator}) where {N}
486485
S = 2^(N - j)
487486
I_tensor = sparse((1.0 + 0.0im) * LinearAlgebra.I, S, S)
488487

489-
return QuantumObject(kron(Z_tensor, op.data, I_tensor); type = Operator, dims = ntuple(i -> 2, Val(N)))
488+
return QuantumObject(kron(Z_tensor, op.data, I_tensor); type = Operator(), dims = ntuple(i -> 2, Val(N)))
490489
end
491490

492491
@doc raw"""
@@ -495,7 +494,7 @@ end
495494
Generates the projection operator ``\hat{O} = |i \rangle\langle j|`` with Hilbert space dimension `N`.
496495
"""
497496
projection(N::Int, i::Int, j::Int) =
498-
QuantumObject(sparse([i + 1], [j + 1], [1.0 + 0.0im], N, N), type = Operator, dims = N)
497+
QuantumObject(sparse([i + 1], [j + 1], [1.0 + 0.0im], N, N), type = Operator(), dims = N)
499498

500499
@doc raw"""
501500
tunneling(N::Int, m::Int=1; sparse::Union{Bool,Val{<:Bool}}=Val(false))
@@ -518,9 +517,9 @@ function tunneling(N::Int, m::Int = 1; sparse::Union{Bool,Val} = Val(false))
518517

519518
data = ones(ComplexF64, N - m)
520519
if getVal(sparse)
521-
return QuantumObject(spdiagm(m => data, -m => data); type = Operator, dims = N)
520+
return QuantumObject(spdiagm(m => data, -m => data); type = Operator(), dims = N)
522521
else
523-
return QuantumObject(diagm(m => data, -m => data); type = Operator, dims = N)
522+
return QuantumObject(diagm(m => data, -m => data); type = Operator(), dims = N)
524523
end
525524
end
526525

@@ -551,9 +550,9 @@ where ``\omega = \exp(\frac{2 \pi i}{N})``.
551550
!!! warning "Beware of type-stability!"
552551
It is highly recommended to use `qft(dimensions)` with `dimensions` as `Tuple` or `SVector` from [StaticArrays.jl](https://github.com/JuliaArrays/StaticArrays.jl) to keep type stability. See the [related Section](@ref doc:Type-Stability) about type stability for more details.
553552
"""
554-
qft(dimensions::Int) = QuantumObject(_qft_op(dimensions), Operator, dimensions)
553+
qft(dimensions::Int) = QuantumObject(_qft_op(dimensions), Operator(), dimensions)
555554
qft(dimensions::Union{Dimensions,AbstractVector{Int},Tuple}) =
556-
QuantumObject(_qft_op(prod(dimensions)), Operator, dimensions)
555+
QuantumObject(_qft_op(prod(dimensions)), Operator(), dimensions)
557556
function _qft_op(N::Int)
558557
ω = exp(2.0im * π / N)
559558
arr = 0:(N-1)

0 commit comments

Comments
 (0)