Skip to content

Commit 2cada32

Browse files
Use FillArrays.jl for handling superoperators
1 parent 402c27a commit 2cada32

File tree

7 files changed

+58
-79
lines changed

7 files changed

+58
-79
lines changed

Project.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ DiffEqCallbacks = "459566f4-90b8-5000-8ac3-15dfb0a30def"
1010
DiffEqNoiseProcess = "77a26b50-5914-5dd7-bc55-306e6241c503"
1111
Distributed = "8ba89e20-285c-5b6f-9357-94700520ee1b"
1212
FFTW = "7a1cc6ca-52ef-59f5-83cd-3a7055c09341"
13+
FillArrays = "1a297f60-69ca-5386-bcde-b61e274b549b"
1314
Graphs = "86223c79-3864-5bf0-83f7-82e725a168b6"
1415
IncompleteLU = "40713840-3770-5561-ab4c-a76e7d0d7895"
1516
LaTeXStrings = "b964fa9f-0449-5b57-a5c2-d3ea65f4040f"
@@ -50,6 +51,7 @@ DiffEqCallbacks = "4.2.1 - 4"
5051
DiffEqNoiseProcess = "5"
5152
Distributed = "1"
5253
FFTW = "1.5"
54+
FillArrays = "1"
5355
GPUArrays = "10, 11"
5456
Graphs = "1.7"
5557
IncompleteLU = "0.2"

src/QuantumToolbox.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,7 @@ import DiffEqNoiseProcess: RealWienerProcess!, RealWienerProcess
5959
import ArrayInterface: allowed_getindex, allowed_setindex!
6060
import Distributed: RemoteChannel
6161
import FFTW: fft, ifft, fftfreq, fftshift
62+
import FillArrays: Eye
6263
import Graphs: connected_components, DiGraph
6364
import IncompleteLU: ilu
6465
import LaTeXStrings: @L_str

src/qobj/superoperators.jl

Lines changed: 49 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -6,76 +6,75 @@ export spre, spost, sprepost, liouvillian, lindblad_dissipator
66

77
# intrinsic functions for super-operators
88
## keep these because they take AbstractMatrix as input and ensure the output is sparse matrix
9-
_spre(A::AbstractMatrix, Id::AbstractMatrix) = kron(Id, sparse(A))
10-
_spre(A::AbstractSparseMatrix, Id::AbstractMatrix) = kron(Id, A)
11-
_spost(B::AbstractMatrix, Id::AbstractMatrix) = kron(transpose(sparse(B)), Id)
12-
_spost(B::AbstractSparseMatrix, Id::AbstractMatrix) = kron(transpose(B), Id)
9+
_spre(A::AbstractMatrix) = kron(Eye(size(A, 1)), sparse(A))
10+
_spre(A::AbstractSparseMatrix) = kron(Eye(size(A, 1)), A)
11+
_spost(B::AbstractMatrix) = kron(transpose(sparse(B)), Eye(size(B, 1)))
12+
_spost(B::AbstractSparseMatrix) = kron(transpose(B), Eye(size(B, 1)))
1313
_sprepost(A::AbstractMatrix, B::AbstractMatrix) = kron(transpose(sparse(B)), sparse(A))
1414
_sprepost(A::AbstractMatrix, B::AbstractSparseMatrix) = kron(transpose(B), sparse(A))
1515
_sprepost(A::AbstractSparseMatrix, B::AbstractMatrix) = kron(transpose(sparse(B)), A)
1616
_sprepost(A::AbstractSparseMatrix, B::AbstractSparseMatrix) = kron(transpose(B), A)
1717
function _sprepost(A, B) # for any other input types
18-
Id_cache = I(size(A, 1))
19-
return _spre(A, Id_cache) * _spost(B, Id_cache)
18+
return _spre(A) * _spost(B)
2019
end
2120

2221
## if input is AbstractSciMLOperator
2322
## some of them are optimized to speed things up
2423
## the rest of the SciMLOperators will just use lazy tensor (and prompt a warning)
25-
_spre(A::MatrixOperator, Id::AbstractMatrix) = MatrixOperator(_spre(A.A, Id))
26-
_spre(A::ScaledOperator, Id::AbstractMatrix) = ScaledOperator(A.λ, _spre(A.L, Id))
27-
_spre(A::AddedOperator, Id::AbstractMatrix) = AddedOperator(map(op -> _spre(op, Id), A.ops))
28-
function _spre(A::AbstractSciMLOperator, Id::AbstractMatrix)
24+
_spre(A::MatrixOperator) = MatrixOperator(_spre(A.A))
25+
_spre(A::ScaledOperator) = ScaledOperator(A.λ, _spre(A.L))
26+
_spre(A::AddedOperator) = AddedOperator(map(op -> _spre(op), A.ops))
27+
function _spre(A::AbstractSciMLOperator)
28+
Id = Eye(size(A, 1))
2929
_lazy_tensor_warning(Id, A)
3030
return kron(Id, A)
3131
end
3232

33-
_spost(B::MatrixOperator, Id::AbstractMatrix) = MatrixOperator(_spost(B.A, Id))
34-
_spost(B::ScaledOperator, Id::AbstractMatrix) = ScaledOperator(B.λ, _spost(B.L, Id))
35-
_spost(B::AddedOperator, Id::AbstractMatrix) = AddedOperator(map(op -> _spost(op, Id), B.ops))
36-
function _spost(B::AbstractSciMLOperator, Id::AbstractMatrix)
33+
_spost(B::MatrixOperator) = MatrixOperator(_spost(B.A))
34+
_spost(B::ScaledOperator) = ScaledOperator(B.λ, _spost(B.L))
35+
_spost(B::AddedOperator) = AddedOperator(map(op -> _spost(op), B.ops))
36+
function _spost(B::AbstractSciMLOperator)
3737
B_T = transpose(B)
38+
Id = Eye(size(B, 1))
3839
_lazy_tensor_warning(B_T, Id)
3940
return kron(B_T, Id)
4041
end
4142

4243
## intrinsic liouvillian
4344
function _liouvillian(
4445
H::MT,
45-
Id::AbstractMatrix,
4646
::Val{assume_hermitian},
4747
) where {MT<:Union{AbstractMatrix,AbstractSciMLOperator},assume_hermitian}
48-
H_spre = _spre(H, Id)
49-
H_spost = assume_hermitian ? _spost(H, Id) : _spost(H', Id)
48+
H_spre = _spre(H)
49+
H_spost = assume_hermitian ? _spost(H) : _spost(H')
5050
return -1im * (H_spre - H_spost)
5151
end
52-
function _liouvillian(H::MatrixOperator, Id::AbstractMatrix, assume_hermitian::Val)
52+
function _liouvillian(H::MatrixOperator, assume_hermitian::Val)
5353
isconstant(H) ||
5454
throw(ArgumentError("The given Hamiltonian for constructing Liouvillian must be constant MatrixOperator."))
55-
return MatrixOperator(_liouvillian(H.A, Id, assume_hermitian))
55+
return MatrixOperator(_liouvillian(H.A, assume_hermitian))
5656
end
57-
_liouvillian(H::ScaledOperator, Id::AbstractMatrix, assume_hermitian::Val{true}) =
58-
ScaledOperator(H.λ, _liouvillian(H.L, Id, assume_hermitian))
59-
_liouvillian(H::AddedOperator, Id::AbstractMatrix, assume_hermitian::Val) =
60-
AddedOperator(map(op -> _liouvillian(op, Id, assume_hermitian), H.ops))
57+
_liouvillian(H::ScaledOperator, assume_hermitian::Val{true}) = ScaledOperator(H.λ, _liouvillian(H.L, assume_hermitian))
58+
_liouvillian(H::AddedOperator, assume_hermitian::Val) =
59+
AddedOperator(map(op -> _liouvillian(op, assume_hermitian), H.ops))
6160

6261
# intrinsic lindblad_dissipator
63-
function _lindblad_dissipator(O::MT, Id::AbstractMatrix) where {MT<:Union{AbstractMatrix,AbstractSciMLOperator}}
62+
function _lindblad_dissipator(O::MT) where {MT<:Union{AbstractMatrix,AbstractSciMLOperator}}
6463
Od_O = O' * O
65-
return _sprepost(O, O') - (_spre(Od_O, Id) + _spost(Od_O, Id)) / 2
64+
return _sprepost(O, O') - (_spre(Od_O) + _spost(Od_O)) / 2
6665
end
67-
function _lindblad_dissipator(O::MatrixOperator, Id::AbstractMatrix)
66+
function _lindblad_dissipator(O::MatrixOperator)
6867
_O = O.A
6968
Od_O = _O' * _O
70-
return MatrixOperator(_sprepost(_O, _O') - (_spre(Od_O, Id) + _spost(Od_O, Id)) / 2)
69+
return MatrixOperator(_sprepost(_O, _O') - (_spre(Od_O) + _spost(Od_O)) / 2)
7170
end
72-
function _lindblad_dissipator(O::ScaledOperator, Id::AbstractMatrix)
71+
function _lindblad_dissipator(O::ScaledOperator)
7372
λc_λ = conj(O.λ) * O.λ
74-
return ScaledOperator(λc_λ, _lindblad_dissipator(O.L, Id))
73+
return ScaledOperator(λc_λ, _lindblad_dissipator(O.L))
7574
end
7675

7776
@doc raw"""
78-
spre(A::AbstractQuantumObject, Id_cache=I(size(A,1)))
77+
spre(A::AbstractQuantumObject)
7978
8079
Returns the [`SuperOperator`](@ref) form of `A` acting on the left of the density matrix operator: ``\mathcal{O} \left(\hat{A}\right) \left[ \hat{\rho} \right] = \hat{A} \hat{\rho}``.
8180
@@ -86,15 +85,12 @@ Since the density matrix is vectorized in [`OperatorKet`](@ref) form: ``|\hat{\r
8685
```
8786
(see the section in documentation: [Superoperators and Vectorized Operators](@ref doc:Superoperators-and-Vectorized-Operators) for more details)
8887
89-
The optional argument `Id_cache` can be used to pass a precomputed identity matrix. This can be useful when the same function is applied multiple times with a known Hilbert space dimension.
90-
9188
See also [`spost`](@ref) and [`sprepost`](@ref).
9289
"""
93-
spre(A::AbstractQuantumObject{Operator}, Id_cache = I(size(A, 1))) =
94-
get_typename_wrapper(A)(_spre(A.data, Id_cache), SuperOperator(), A.dimensions)
90+
spre(A::AbstractQuantumObject{Operator}) = get_typename_wrapper(A)(_spre(A.data), SuperOperator(), A.dimensions)
9591

9692
@doc raw"""
97-
spost(B::AbstractQuantumObject, Id_cache=I(size(B,1)))
93+
spost(B::AbstractQuantumObject)
9894
9995
Returns the [`SuperOperator`](@ref) form of `B` acting on the right of the density matrix operator: ``\mathcal{O} \left(\hat{B}\right) \left[ \hat{\rho} \right] = \hat{\rho} \hat{B}``.
10096
@@ -105,12 +101,9 @@ Since the density matrix is vectorized in [`OperatorKet`](@ref) form: ``|\hat{\r
105101
```
106102
(see the section in documentation: [Superoperators and Vectorized Operators](@ref doc:Superoperators-and-Vectorized-Operators) for more details)
107103
108-
The optional argument `Id_cache` can be used to pass a precomputed identity matrix. This can be useful when the same function is applied multiple times with a known Hilbert space dimension.
109-
110104
See also [`spre`](@ref) and [`sprepost`](@ref).
111105
"""
112-
spost(B::AbstractQuantumObject{Operator}, Id_cache = I(size(B, 1))) =
113-
get_typename_wrapper(B)(_spost(B.data, Id_cache), SuperOperator(), B.dimensions)
106+
spost(B::AbstractQuantumObject{Operator}) = get_typename_wrapper(B)(_spost(B.data), SuperOperator(), B.dimensions)
114107

115108
@doc raw"""
116109
sprepost(A::AbstractQuantumObject, B::AbstractQuantumObject)
@@ -132,7 +125,7 @@ function sprepost(A::AbstractQuantumObject{Operator}, B::AbstractQuantumObject{O
132125
end
133126

134127
@doc raw"""
135-
lindblad_dissipator(O::AbstractQuantumObject, Id_cache=I(size(O,1))
128+
lindblad_dissipator(O::AbstractQuantumObject)
136129
137130
Returns the Lindblad [`SuperOperator`](@ref) defined as
138131
@@ -141,21 +134,18 @@ Returns the Lindblad [`SuperOperator`](@ref) defined as
141134
\hat{O}^\dagger \hat{O} \hat{\rho} - \hat{\rho} \hat{O}^\dagger \hat{O} \right)
142135
```
143136
144-
The optional argument `Id_cache` can be used to pass a precomputed identity matrix. This can be useful when the same function is applied multiple times with a known Hilbert space dimension.
145-
146137
See also [`spre`](@ref), [`spost`](@ref), and [`sprepost`](@ref).
147138
"""
148-
lindblad_dissipator(O::AbstractQuantumObject{Operator}, Id_cache = I(size(O, 1))) =
149-
get_typename_wrapper(O)(_lindblad_dissipator(O.data, Id_cache), SuperOperator(), O.dimensions)
139+
lindblad_dissipator(O::AbstractQuantumObject{Operator}) =
140+
get_typename_wrapper(O)(_lindblad_dissipator(O.data), SuperOperator(), O.dimensions)
150141

151142
# It is already a SuperOperator
152-
lindblad_dissipator(O::AbstractQuantumObject{SuperOperator}, Id_cache = nothing) = O
143+
lindblad_dissipator(O::AbstractQuantumObject{SuperOperator}) = O
153144

154145
@doc raw"""
155146
liouvillian(
156147
H::AbstractQuantumObject,
157-
c_ops::Union{Nothing,AbstractVector,Tuple}=nothing,
158-
Id_cache=I(prod(H.dimensions));
148+
c_ops::Union{Nothing,AbstractVector,Tuple}=nothing;
159149
assume_hermitian::Union{Bool,Val} = Val(true),
160150
)
161151
@@ -179,54 +169,44 @@ where
179169
\mathcal{D}(\hat{C}_n) [\cdot] = \hat{C}_n [\cdot] \hat{C}_n^\dagger - \frac{1}{2} \hat{C}_n^\dagger \hat{C}_n [\cdot] - \frac{1}{2} [\cdot] \hat{C}_n^\dagger \hat{C}_n
180170
```
181171
182-
The optional argument `Id_cache` can be used to pass a precomputed identity matrix. This can be useful when the same function is applied multiple times with a known Hilbert space dimension.
183-
184172
See also [`spre`](@ref), [`spost`](@ref), and [`lindblad_dissipator`](@ref).
185173
186174
!!! warning "Beware of type-stability!"
187175
If you want to keep type stability, it is recommended to use `assume_hermitian = Val(true)` instead of `assume_hermitian = true`. See [this link](https://docs.julialang.org/en/v1/manual/performance-tips/#man-performance-value-type) and the [related Section](@ref doc:Type-Stability) about type stability for more details.
188176
"""
189177
function liouvillian(
190178
H::AbstractQuantumObject{OpType},
191-
c_ops::Union{Nothing,AbstractVector,Tuple} = nothing,
192-
Id_cache::Diagonal = I(prod(H.dimensions));
179+
c_ops::Union{Nothing,AbstractVector,Tuple} = nothing;
193180
assume_hermitian::Union{Bool,Val} = Val(true),
194181
) where {OpType<:Union{Operator,SuperOperator}}
195-
L = liouvillian(H, Id_cache; assume_hermitian = assume_hermitian)
182+
L = liouvillian(H; assume_hermitian = assume_hermitian)
196183
if !isnothing(c_ops)
197-
return L + _sum_lindblad_dissipators(c_ops, Id_cache)
184+
return L + _sum_lindblad_dissipators(c_ops)
198185
end
199186
return L
200187
end
201188

202-
liouvillian(H::Nothing, c_ops::Union{AbstractVector,Tuple}, Id_cache::Diagonal = I(prod(c_ops[1].dims)); kwargs...) =
203-
_sum_lindblad_dissipators(c_ops, Id_cache)
189+
liouvillian(H::Nothing, c_ops::Union{AbstractVector,Tuple}; kwargs...) = _sum_lindblad_dissipators(c_ops)
204190

205191
liouvillian(H::Nothing, c_ops::Nothing; kwargs...) = 0
206192

207-
liouvillian(
208-
H::AbstractQuantumObject{Operator},
209-
Id_cache::Diagonal = I(prod(H.dimensions));
210-
assume_hermitian::Union{Bool,Val} = Val(true),
211-
) = get_typename_wrapper(H)(_liouvillian(H.data, Id_cache, makeVal(assume_hermitian)), SuperOperator(), H.dimensions)
212-
213-
liouvillian(H::AbstractQuantumObject{SuperOperator}, Id_cache::Diagonal; kwargs...) = H
193+
liouvillian(H::AbstractQuantumObject{Operator}; assume_hermitian::Union{Bool,Val} = Val(true)) =
194+
get_typename_wrapper(H)(_liouvillian(H.data, makeVal(assume_hermitian)), SuperOperator(), H.dimensions)
214195

215-
_sum_lindblad_dissipators(c_ops::Nothing, Id_cache::Diagonal) = 0
196+
liouvillian(H::AbstractQuantumObject{SuperOperator}; kwargs...) = H
197+
_sum_lindblad_dissipators(c_ops::Nothing) = 0
216198

217-
function _sum_lindblad_dissipators(c_ops::AbstractVector, Id_cache::Diagonal)
218-
return sum(op -> lindblad_dissipator(op, Id_cache), c_ops; init = 0)
219-
end
199+
_sum_lindblad_dissipators(c_ops::AbstractVector) = sum(op -> lindblad_dissipator(op), c_ops; init = 0)
220200

221201
# Help the compiler to unroll the sum at compile time
222-
@generated function _sum_lindblad_dissipators(c_ops::Tuple, Id_cache::Diagonal)
202+
@generated function _sum_lindblad_dissipators(c_ops::Tuple)
223203
N = length(c_ops.parameters)
224204
if N == 0
225205
return :(0)
226206
end
227-
ex = :(lindblad_dissipator(c_ops[1], Id_cache))
207+
ex = :(lindblad_dissipator(c_ops[1]))
228208
for i in 2:N
229-
ex = :($ex + lindblad_dissipator(c_ops[$i], Id_cache))
209+
ex = :($ex + lindblad_dissipator(c_ops[$i]))
230210
end
231211
return ex
232212
end

src/spin_lattice.jl

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -53,11 +53,11 @@ function multisite_operator(dims::Union{AbstractVector,Tuple}, pairs::Pair{<:Int
5353

5454
_dims[sites] == [get_dimensions_to(op)[1].size for op in ops] || throw(ArgumentError("The dimensions of the operators do not match the dimensions of the lattice."))
5555

56-
data = kron(I(prod(_dims[1:(sites[1]-1)])), ops[1].data)
56+
data = kron(Eye(prod(_dims[1:(sites[1]-1)])), ops[1].data)
5757
for i in 2:length(sites)
58-
data = kron(data, I(prod(_dims[(sites[i-1]+1):(sites[i]-1)])), ops[i].data)
58+
data = kron(data, Eye(prod(_dims[(sites[i-1]+1):(sites[i]-1)])), ops[i].data)
5959
end
60-
data = kron(data, I(prod(_dims[(sites[end]+1):end])))
60+
data = kron(data, Eye(prod(_dims[(sites[end]+1):end])))
6161

6262
return QuantumObject(data; type = Operator(), dims = dims)
6363
end

src/time_evolution/brmesolve.jl

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -126,7 +126,6 @@ function _brterm(
126126
_check_br_spectra(spectra)
127127

128128
U = rst.vectors
129-
Id = I(prod(rst.dimensions))
130129

131130
skew = @. rst.values - rst.values' |> real
132131
spectrum = spectra.(skew)
@@ -166,7 +165,7 @@ function _brterm(
166165
tidyup!(bd_term)
167166
end
168167

169-
out = (_sprepost(A_mat_spec_t, A_mat) + _sprepost(A_mat, A_mat_spec) - _spost(ac_term, Id) - _spre(bd_term, Id)) / 2
168+
out = (_sprepost(A_mat_spec_t, A_mat) + _sprepost(A_mat, A_mat_spec) - _spost(ac_term) - _spre(bd_term)) / 2
170169

171170
(sec_cutoff != -1) && (out .*= M_cut)
172171

src/time_evolution/smesolve.jl

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -111,13 +111,12 @@ function smesolveProblem(
111111

112112
K = get_data(L_evo)
113113

114-
Id = I(prod(dims))
115114
Id_op = IdentityOperator(prod(dims)^2)
116115
D_l = map(sc_ops_evo_data) do op
117116
# TODO: # Currently, we are assuming a time-independent MatrixOperator
118117
# Also, the u state may become non-hermitian, so Tr[Sn * ρ + ρ * Sn'] != real(Tr[Sn * ρ]) / 2
119118
op_vec = mat2vec(adjoint(op.A))
120-
return AddedOperator(_spre(op, Id), _spost(op', Id), _smesolve_ScalarOperator(op_vec) * Id_op)
119+
return AddedOperator(_spre(op), _spost(op'), _smesolve_ScalarOperator(op_vec) * Id_op)
121120
end
122121
D = DiffusionOperator(D_l)
123122

src/time_evolution/time_evolution_dynamical.jl

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -285,7 +285,6 @@ function _DSF_mesolve_Affect!(integrator)
285285
dsf_cache = internal_params.dsf_cache
286286
dsf_params = internal_params.dsf_params
287287
expv_cache = internal_params.expv_cache
288-
dsf_identity = internal_params.dsf_identity
289288
dsf_displace_cache_full = internal_params.dsf_displace_cache_full
290289

291290
op_l_length = length(op_list)
@@ -334,7 +333,7 @@ function _DSF_mesolve_Affect!(integrator)
334333
e_ops2 = e_ops(op_l2, dsf_params)
335334
_mesolve_callbacks_new_e_ops!(integrator, [_generate_mesolve_e_op(op) for op in e_ops2])
336335
# By doing this, we are assuming that the system is time-independent and f is a MatrixOperator
337-
copyto!(integrator.f.f.A, liouvillian(H(op_l2, dsf_params), c_ops(op_l2, dsf_params), dsf_identity).data)
336+
copyto!(integrator.f.f.A, liouvillian(H(op_l2, dsf_params), c_ops(op_l2, dsf_params)).data)
338337
return u_modified!(integrator, true)
339338
end
340339

@@ -385,7 +384,6 @@ function dsf_mesolveProblem(
385384
δα_list = δα_list,
386385
dsf_cache = similar(ψ0.data, length(ψ0.data)^2),
387386
expv_cache = expv_cache,
388-
dsf_identity = dsf_identity,
389387
dsf_params = dsf_params,
390388
dsf_displace_cache_full = dsf_displace_cache_full,
391389
),

0 commit comments

Comments
 (0)