Skip to content

Commit 4d852a1

Browse files
Change the type of dims for QuantumObject to SVector for type stabilities (qutip#217)
* Improve c_ops handling * Format code * Change dims to SVector * Fix JET errors * Fix dfd_mesolve issues * Fix entanglement issues * Fix low rank dynamics * Fix all tests * Format code * Change to StaticArraysCore.jl and minor changes * Make a few comments on the documentation * Update some docstring * Minor changes * FIx error on Documentation
1 parent 6579bc1 commit 4d852a1

19 files changed

+329
-220
lines changed

Project.toml

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
name = "QuantumToolbox"
22
uuid = "6c2fb7c5-b903-41d2-bc5e-5a7c320b9fab"
33
authors = ["Alberto Mercurio", "Luca Gravina", "Yi-Te Huang"]
4-
version = "0.12.1"
4+
version = "0.13.0"
55

66
[deps]
77
ArrayInterface = "4fba245c-0d91-5ea0-9b3e-6abc04ee57a9"
@@ -18,6 +18,7 @@ Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
1818
Reexport = "189a3867-3050-52da-a836-e630ba90ab69"
1919
SparseArrays = "2f01184e-e22b-5df5-ae63-d93ebab69eaf"
2020
SpecialFunctions = "276daf66-3868-5448-9aa4-cd146d93841b"
21+
StaticArraysCore = "1e83bf80-4336-4d27-bf5d-d5a4f845583c"
2122

2223
[weakdeps]
2324
CUDA = "052768ef-5323-5732-b1bb-66c8b64840ba"
@@ -41,6 +42,7 @@ Random = "<0.0.1, 1"
4142
Reexport = "1"
4243
SparseArrays = "<0.0.1, 1"
4344
SpecialFunctions = "2"
45+
StaticArraysCore = "1"
4446
Test = "<0.0.1, 1"
4547
julia = "1.7"
4648

docs/make.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,7 @@ const PAGES = [
2828
"users_guide/QuantumObject/QuantumObject.md",
2929
"users_guide/QuantumObject/QuantumObject_functions.md",
3030
],
31+
"The Importance of Type-Stability" => "users_guide/type_stability.md",
3132
"Manipulating States and Operators" => "users_guide/states_and_operators.md",
3233
"Tensor Products and Partial Traces" => "users_guide/tensor.md",
3334
"Time Evolution and Dynamics" => [

docs/src/users_guide/QuantumObject/QuantumObject.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,9 +39,15 @@ Qobj(rand(4, 4))
3939
```
4040

4141
```@example Qobj
42-
Qobj(rand(4, 4), dims = [2, 2])
42+
M = rand(ComplexF64, 4, 4)
43+
Qobj(M, dims = [2, 2]) # dims as Vector
44+
Qobj(M, dims = (2, 2)) # dims as Tuple (recommended)
45+
Qobj(M, dims = SVector(2, 2)) # dims as StaticArrays.SVector (recommended)
4346
```
4447

48+
> [!IMPORTANT]
49+
> 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).
50+
4551
```@example Qobj
4652
Qobj(rand(4, 4), type = SuperOperator)
4753
```
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
# [The Importance of Type-Stability](@id doc:Type-Stability)
2+
3+
This page is still under construction.

src/QuantumToolbox.jl

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,8 @@ import OrdinaryDiffEq: OrdinaryDiffEqAlgorithm
3232
import Pkg
3333
import Random
3434
import SpecialFunctions: loggamma
35+
@reexport import StaticArraysCore: SVector
36+
import StaticArraysCore: MVector
3537

3638
# Setting the number of threads to 1 allows
3739
# to achieve better performances for more massive parallelizations

src/metrics.jl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -62,21 +62,21 @@ function entropy_vn(
6262
end
6363

6464
"""
65-
entanglement(QO::QuantumObject, sel::Vector)
65+
entanglement(QO::QuantumObject, sel::Union{Int,AbstractVector{Int},Tuple})
6666
6767
Calculates the entanglement by doing the partial trace of `QO`, selecting only the dimensions
6868
with the indices contained in the `sel` vector, and then using the Von Neumann entropy [`entropy_vn`](@ref).
6969
"""
7070
function entanglement(
7171
QO::QuantumObject{<:AbstractArray{T},OpType},
72-
sel::Vector{Int},
72+
sel::Union{AbstractVector{Int},Tuple},
7373
) where {T,OpType<:Union{BraQuantumObject,KetQuantumObject,OperatorQuantumObject}}
7474
ψ = normalize(QO)
7575
ρ_tr = ptrace(ψ, sel)
7676
entropy = entropy_vn(ρ_tr)
7777
return (entropy > 0) * entropy
7878
end
79-
entanglement(QO::QuantumObject, sel::Int) = entanglement(QO, [sel])
79+
entanglement(QO::QuantumObject, sel::Int) = entanglement(QO, (sel,))
8080

8181
@doc raw"""
8282
tracedist(ρ::QuantumObject, σ::QuantumObject)

src/qobj/arithmetic_and_attributes.jl

Lines changed: 68 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -471,7 +471,7 @@ proj(ψ::QuantumObject{<:AbstractArray{T},KetQuantumObject}) where {T} = ψ * ψ
471471
proj::QuantumObject{<:AbstractArray{T},BraQuantumObject}) where {T} = ψ' * ψ
472472

473473
@doc raw"""
474-
ptrace(QO::QuantumObject, sel::Vector{Int})
474+
ptrace(QO::QuantumObject, sel)
475475
476476
[Partial trace](https://en.wikipedia.org/wiki/Partial_trace) of a quantum state `QO` leaving only the dimensions
477477
with the indices present in the `sel` vector.
@@ -487,7 +487,7 @@ Quantum Object: type=Ket dims=[2, 2] size=(4,)
487487
0.0 + 0.0im
488488
0.0 + 0.0im
489489
490-
julia> ptrace(ψ, [2])
490+
julia> ptrace(ψ, 2)
491491
Quantum Object: type=Operator dims=[2] size=(2, 2) ishermitian=true
492492
2×2 Matrix{ComplexF64}:
493493
0.0+0.0im 0.0+0.0im
@@ -504,63 +504,79 @@ Quantum Object: type=Ket dims=[2, 2] size=(4,)
504504
0.0 + 0.0im
505505
0.7071067811865475 + 0.0im
506506
507-
julia> ptrace(ψ, [1])
507+
julia> ptrace(ψ, 1)
508508
Quantum Object: type=Operator dims=[2] size=(2, 2) ishermitian=true
509509
2×2 Matrix{ComplexF64}:
510510
0.5+0.0im 0.0+0.0im
511511
0.0+0.0im 0.5+0.0im
512512
```
513513
"""
514-
function ptrace(QO::QuantumObject{<:AbstractArray{T1},KetQuantumObject}, sel::Vector{T2}) where {T1,T2<:Integer}
514+
function ptrace(QO::QuantumObject{<:AbstractArray,KetQuantumObject}, sel::Union{AbstractVector{Int},Tuple})
515515
length(QO.dims) == 1 && return QO
516516

517-
ρtr, dkeep = _ptrace_ket(QO.data, QO.dims, sel)
517+
ρtr, dkeep = _ptrace_ket(QO.data, QO.dims, SVector(sel))
518518
return QuantumObject(ρtr, dims = dkeep)
519519
end
520520

521-
ptrace(QO::QuantumObject{<:AbstractArray{T1},BraQuantumObject}, sel::Vector{T2}) where {T1,T2<:Integer} =
522-
ptrace(QO', sel)
521+
ptrace(QO::QuantumObject{<:AbstractArray,BraQuantumObject}, sel::Union{AbstractVector{Int},Tuple}) = ptrace(QO', sel)
523522

524-
function ptrace(QO::QuantumObject{<:AbstractArray{T1},OperatorQuantumObject}, sel::Vector{T2}) where {T1,T2<:Integer}
523+
function ptrace(QO::QuantumObject{<:AbstractArray,OperatorQuantumObject}, sel::Union{AbstractVector{Int},Tuple})
525524
length(QO.dims) == 1 && return QO
526525

527-
ρtr, dkeep = _ptrace_oper(QO.data, QO.dims, sel)
526+
ρtr, dkeep = _ptrace_oper(QO.data, QO.dims, SVector(sel))
528527
return QuantumObject(ρtr, dims = dkeep)
529528
end
530-
ptrace(QO::QuantumObject, sel::Int) = ptrace(QO, [sel])
531-
532-
function _ptrace_ket(QO::AbstractArray{T1}, dims::Vector{<:Integer}, sel::Vector{T2}) where {T1,T2<:Integer}
533-
rd = dims
534-
nd = length(rd)
535-
536-
nd == 1 && return QO, rd
537-
538-
dkeep = rd[sel]
539-
qtrace = filter!(e -> e sel, Vector(1:nd))
540-
dtrace = @view(rd[qtrace])
529+
ptrace(QO::QuantumObject, sel::Int) = ptrace(QO, SVector(sel))
530+
531+
function _ptrace_ket(QO::AbstractArray, dims::Union{SVector,MVector}, sel)
532+
nd = length(dims)
533+
534+
nd == 1 && return QO, dims
535+
536+
qtrace = filter(i -> i sel, 1:nd)
537+
dkeep = dims[sel]
538+
dtrace = dims[qtrace]
539+
# Concatenate sel and qtrace without loosing the length information
540+
sel_qtrace = ntuple(Val(nd)) do i
541+
if i <= length(sel)
542+
@inbounds sel[i]
543+
else
544+
@inbounds qtrace[i-length(sel)]
545+
end
546+
end
541547

542-
vmat = reshape(QO, reverse(rd)...)
543-
topermute = nd + 1 .- vcat(sel, qtrace)
548+
vmat = reshape(QO, reverse(dims)...)
549+
topermute = nd + 1 .- sel_qtrace
544550
vmat = PermutedDimsArray(vmat, topermute)
545551
vmat = reshape(vmat, prod(dkeep), prod(dtrace))
546552

547553
return vmat * vmat', dkeep
548554
end
549555

550-
function _ptrace_oper(QO::AbstractArray{T1}, dims::Vector{<:Integer}, sel::Vector{T2}) where {T1,T2<:Integer}
551-
rd = dims
552-
nd = length(rd)
553-
554-
nd == 1 && return QO, rd
555-
556-
dkeep = rd[sel]
557-
qtrace = filter!(e -> e sel, Vector(1:nd))
558-
dtrace = @view(rd[qtrace])
556+
function _ptrace_oper(QO::AbstractArray, dims::Union{SVector,MVector}, sel)
557+
nd = length(dims)
558+
559+
nd == 1 && return QO, dims
560+
561+
qtrace = filter(i -> i sel, 1:nd)
562+
dkeep = dims[sel]
563+
dtrace = dims[qtrace]
564+
# Concatenate sel and qtrace without loosing the length information
565+
qtrace_sel = ntuple(Val(2 * nd)) do i
566+
if i <= length(qtrace)
567+
@inbounds qtrace[i]
568+
elseif i <= 2 * length(qtrace)
569+
@inbounds qtrace[i-length(qtrace)] + nd
570+
elseif i <= 2 * length(qtrace) + length(sel)
571+
@inbounds sel[i-length(qtrace)-length(sel)]
572+
else
573+
@inbounds sel[i-length(qtrace)-2*length(sel)] + nd
574+
end
575+
end
559576

560-
ρmat = reshape(QO, reverse!(repeat(rd, 2))...)
561-
topermute = 2 * nd + 1 .- vcat(qtrace, qtrace .+ nd, sel, sel .+ nd)
562-
reverse!(topermute)
563-
ρmat = PermutedDimsArray(ρmat, topermute)
577+
ρmat = reshape(QO, reverse(vcat(dims, dims))...)
578+
topermute = 2 * nd + 1 .- qtrace_sel
579+
ρmat = PermutedDimsArray(ρmat, reverse(topermute))
564580

565581
## TODO: Check if it works always
566582

@@ -639,7 +655,7 @@ function get_coherence(ρ::QuantumObject{<:AbstractArray,OperatorQuantumObject})
639655
end
640656

641657
@doc raw"""
642-
permute(A::QuantumObject, order::Vector{Int})
658+
permute(A::QuantumObject, order::Union{AbstractVector{Int},Tuple})
643659
644660
Permute the tensor structure of a [`QuantumObject`](@ref) `A` according to the specified `order` list
645661
@@ -660,28 +676,36 @@ true
660676
"""
661677
function permute(
662678
A::QuantumObject{<:AbstractArray{T},ObjType},
663-
order::AbstractVector{Int},
679+
order::Union{AbstractVector{Int},Tuple},
664680
) where {T,ObjType<:Union{KetQuantumObject,BraQuantumObject,OperatorQuantumObject}}
665681
(length(order) != length(A.dims)) &&
666682
throw(ArgumentError("The order list must have the same length as the number of subsystems (A.dims)"))
667683

668684
!isperm(order) && throw(ArgumentError("$(order) is not a valid permutation of the subsystems (A.dims)"))
669685

686+
_non_static_array_warning("order", order)
687+
688+
order_svector = SVector{length(order),Int}(order) # convert it to SVector for performance
689+
670690
# obtain the arguments: dims for reshape; perm for PermutedDimsArray
671-
dims, perm = _dims_and_perm(A.type, A.dims, order, length(order))
691+
dims, perm = _dims_and_perm(A.type, A.dims, order_svector, length(order_svector))
672692

673-
return QuantumObject(reshape(PermutedDimsArray(reshape(A.data, dims...), perm), size(A)), A.type, A.dims[order])
693+
return QuantumObject(
694+
reshape(PermutedDimsArray(reshape(A.data, dims...), Tuple(perm)), size(A)),
695+
A.type,
696+
A.dims[order_svector],
697+
)
674698
end
675699

676700
function _dims_and_perm(
677701
::ObjType,
678-
dims::AbstractVector{Int},
702+
dims::SVector{N,Int},
679703
order::AbstractVector{Int},
680704
L::Int,
681-
) where {ObjType<:Union{KetQuantumObject,BraQuantumObject}}
682-
return reverse(dims), reverse!((L + 1) .- order)
705+
) where {ObjType<:Union{KetQuantumObject,BraQuantumObject},N}
706+
return reverse(dims), reverse((L + 1) .- order)
683707
end
684708

685-
function _dims_and_perm(::OperatorQuantumObject, dims::AbstractVector{Int}, order::AbstractVector{Int}, L::Int)
686-
return reverse!([dims; dims]), reverse!((2 * L + 1) .- [order; order .+ L])
709+
function _dims_and_perm(::OperatorQuantumObject, dims::SVector{N,Int}, order::AbstractVector{Int}, L::Int) where {N}
710+
return reverse(vcat(dims, dims)), reverse((2 * L + 1) .- vcat(order, order .+ L))
687711
end

src/qobj/eigsolve.jl

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

88
@doc raw"""
9-
struct EigsolveResult{T1<:Vector{<:Number}, T2<:AbstractMatrix{<:Number}, ObjType<:Union{Nothing,OperatorQuantumObject,SuperOperatorQuantumObject}}
9+
struct EigsolveResult{T1<:Vector{<:Number}, T2<:AbstractMatrix{<:Number}, ObjType<:Union{Nothing,OperatorQuantumObject,SuperOperatorQuantumObject},N}
1010
values::T1
1111
vectors::T2
1212
type::ObjType
13-
dims::Vector{Int}
13+
dims::SVector{N,Int}
1414
iter::Int
1515
numops::Int
1616
converged::Bool
@@ -22,7 +22,7 @@ A struct containing the eigenvalues, the eigenvectors, and some information from
2222
- `values::AbstractVector`: the eigenvalues
2323
- `vectors::AbstractMatrix`: the transformation matrix (eigenvectors)
2424
- `type::Union{Nothing,QuantumObjectType}`: the type of [`QuantumObject`](@ref), or `nothing` means solving eigen equation for general matrix
25-
- `dims::Vector{Int}`: the `dims` of [`QuantumObject`](@ref)
25+
- `dims::SVector`: the `dims` of [`QuantumObject`](@ref)
2626
- `iter::Int`: the number of iteration during the solving process
2727
- `numops::Int` : number of times the linear map was applied in krylov methods
2828
- `converged::Bool`: Whether the result is converged
@@ -54,11 +54,12 @@ struct EigsolveResult{
5454
T1<:Vector{<:Number},
5555
T2<:AbstractMatrix{<:Number},
5656
ObjType<:Union{Nothing,OperatorQuantumObject,SuperOperatorQuantumObject},
57+
N,
5758
}
5859
values::T1
5960
vectors::T2
6061
type::ObjType
61-
dims::Vector{Int}
62+
dims::SVector{N,Int}
6263
iter::Int
6364
numops::Int
6465
converged::Bool
@@ -271,7 +272,7 @@ function _eigsolve(
271272
A,
272273
b::AbstractVector{T},
273274
type::ObjType,
274-
dims::Vector{Int},
275+
dims::SVector,
275276
k::Int = 1,
276277
m::Int = max(20, 2 * k + 1);
277278
tol::Real = 1e-8,
@@ -398,7 +399,7 @@ function eigsolve(
398399
A;
399400
v0::Union{Nothing,AbstractVector} = nothing,
400401
type::Union{Nothing,OperatorQuantumObject,SuperOperatorQuantumObject} = nothing,
401-
dims::Vector{Int} = Int[],
402+
dims = SVector{0,Int}(),
402403
sigma::Union{Nothing,Real} = nothing,
403404
k::Int = 1,
404405
krylovdim::Int = max(20, 2 * k + 1),
@@ -411,6 +412,8 @@ function eigsolve(
411412
isH = ishermitian(A)
412413
v0 === nothing && (v0 = normalize!(rand(T, size(A, 1))))
413414

415+
dims = SVector(dims)
416+
414417
if sigma === nothing
415418
res = _eigsolve(A, v0, type, dims, k, krylovdim, tol = tol, maxiter = maxiter)
416419
vals = res.values

0 commit comments

Comments
 (0)