Skip to content

Commit d9b5ae1

Browse files
authored
Moving all abstract functionality to QuantumInterface.jl (#100)
* avoid piracy of SparseArrays.permutedims(::AbstractSparseMatrix, ...) use the new _permutedims in operators_sparse * new abstract ParticleBasis to avoid piracy of QuantumInterface.Basis * move all operations on abstract types to QuantumInterface.jl * document the purposeful piracy of identityoperator * test with QuantumInterface v0.2.0 * bump version number
1 parent c5543d8 commit d9b5ae1

13 files changed

+26
-553
lines changed

Project.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name = "QuantumOpticsBase"
22
uuid = "4f57444f-1401-5e15-980d-4471b28d5678"
3-
version = "0.4.1"
3+
version = "0.4.2"
44

55
[deps]
66
Adapt = "79e6a3ab-5dfb-504d-930d-738a2a938a0e"
@@ -19,7 +19,7 @@ Adapt = "1, 2, 3.3"
1919
FFTW = "1.2"
2020
FillArrays = "0.13, 1"
2121
LRUCache = "1"
22-
QuantumInterface = "0.1.0"
22+
QuantumInterface = "0.2.0"
2323
Strided = "1, 2"
2424
UnsafeArrays = "1"
2525
julia = "1.3"

src/QuantumOpticsBase.jl

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,11 @@ module QuantumOpticsBase
33
using SparseArrays, LinearAlgebra, LRUCache, Strided, UnsafeArrays, FillArrays
44
import LinearAlgebra: mul!, rmul!
55

6-
import QuantumInterface: dagger, directsum, , dm, embed, expect, permutesystems,
7-
projector, ptrace, reduced, tensor,
6+
import QuantumInterface: dagger, directsum, , dm, embed, expect, identityoperator,
7+
permutesystems, projector, ptrace, reduced, tensor, , variance
8+
9+
# index helpers
10+
import QuantumInterface: complement, remove, shiftremove, reducedindices!, check_indices, check_sortedindices, check_embed_indices
811

912
export Basis, GenericBasis, CompositeBasis, basis,
1013
tensor, , permutesystems, @samebases,
@@ -55,7 +58,6 @@ export Basis, GenericBasis, CompositeBasis, basis,
5558
SumBasis, directsum, , LazyDirectSum, getblock, setblock!,
5659
qfunc, wigner, coherentspinstate, qfuncsu2, wignersu2
5760

58-
include("sortedindices.jl")
5961
include("polynomials.jl")
6062
include("bases.jl")
6163
include("states.jl")

src/operators.jl

Lines changed: 1 addition & 231 deletions
Original file line numberDiff line numberDiff line change
@@ -13,94 +13,7 @@ abstract type DataOperator{BL,BR} <: AbstractOperator{BL,BR} end
1313

1414

1515
# Common error messages
16-
arithmetic_unary_error(funcname, x::AbstractOperator) = throw(ArgumentError("$funcname is not defined for this type of operator: $(typeof(x)).\nTry to convert to another operator type first with e.g. dense() or sparse()."))
17-
arithmetic_binary_error(funcname, a::AbstractOperator, b::AbstractOperator) = throw(ArgumentError("$funcname is not defined for this combination of types of operators: $(typeof(a)), $(typeof(b)).\nTry to convert to a common operator type first with e.g. dense() or sparse()."))
18-
addnumbererror() = throw(ArgumentError("Can't add or subtract a number and an operator. You probably want 'op + identityoperator(op)*x'."))
19-
20-
length(a::AbstractOperator) = length(a.basis_l)::Int*length(a.basis_r)::Int
21-
basis(a::AbstractOperator) = (check_samebases(a); a.basis_l)
22-
23-
# Ensure scalar broadcasting
24-
Base.broadcastable(x::AbstractOperator) = Ref(x)
25-
26-
# Arithmetic operations
27-
+(a::AbstractOperator, b::AbstractOperator) = arithmetic_binary_error("Addition", a, b)
28-
+(a::Number, b::AbstractOperator) = addnumbererror()
29-
+(a::AbstractOperator, b::Number) = addnumbererror()
30-
+(a::AbstractOperator) = a
31-
32-
-(a::AbstractOperator) = arithmetic_unary_error("Negation", a)
33-
-(a::AbstractOperator, b::AbstractOperator) = arithmetic_binary_error("Subtraction", a, b)
34-
-(a::Number, b::AbstractOperator) = addnumbererror()
35-
-(a::AbstractOperator, b::Number) = addnumbererror()
36-
37-
*(a::AbstractOperator, b::AbstractOperator) = arithmetic_binary_error("Multiplication", a, b)
38-
^(a::AbstractOperator, b::Integer) = Base.power_by_squaring(a, b)
39-
40-
41-
dagger(a::AbstractOperator) = arithmetic_unary_error("Hermitian conjugate", a)
42-
Base.adjoint(a::AbstractOperator) = dagger(a)
43-
44-
conj(a::AbstractOperator) = arithmetic_unary_error("Complex conjugate", a)
45-
conj!(a::AbstractOperator) = conj(a::AbstractOperator)
46-
47-
# dense(a::AbstractOperator) = arithmetic_unary_error("Conversion to dense", a)
48-
49-
transpose(a::AbstractOperator) = arithmetic_unary_error("Transpose", a)
50-
51-
"""
52-
ishermitian(op::AbstractOperator)
53-
54-
Check if an operator is Hermitian.
55-
"""
56-
ishermitian(op::AbstractOperator) = arithmetic_unary_error(ishermitian, op)
57-
58-
59-
"""
60-
tensor(x::AbstractOperator, y::AbstractOperator, z::AbstractOperator...)
61-
62-
Tensor product ``\\hat{x}⊗\\hat{y}⊗\\hat{z}⊗…`` of the given operators.
63-
"""
64-
tensor(a::AbstractOperator, b::AbstractOperator) = arithmetic_binary_error("Tensor product", a, b)
65-
tensor(op::AbstractOperator) = op
66-
tensor(operators::AbstractOperator...) = reduce(tensor, operators)
67-
68-
69-
"""
70-
embed(basis1[, basis2], indices::Vector, operators::Vector)
71-
72-
Tensor product of operators where missing indices are filled up with identity operators.
73-
"""
74-
function embed(basis_l::CompositeBasis, basis_r::CompositeBasis,
75-
indices, operators::Vector{T}) where T<:AbstractOperator
76-
77-
@assert check_embed_indices(indices)
78-
79-
N = length(basis_l.bases)
80-
@assert length(basis_r.bases) == N
81-
@assert length(indices) == length(operators)
82-
83-
# Embed all single-subspace operators.
84-
idxop_sb = [x for x in zip(indices, operators) if x[1] isa Integer]
85-
indices_sb = [x[1] for x in idxop_sb]
86-
ops_sb = [x[2] for x in idxop_sb]
87-
88-
for (idxsb, opsb) in zip(indices_sb, ops_sb)
89-
(opsb.basis_l == basis_l.bases[idxsb]) || throw(IncompatibleBases())
90-
(opsb.basis_r == basis_r.bases[idxsb]) || throw(IncompatibleBases())
91-
end
92-
93-
S = length(operators) > 0 ? mapreduce(eltype, promote_type, operators) : Any
94-
embed_op = tensor([i indices_sb ? ops_sb[indexin(i, indices_sb)[1]] : identityoperator(T, S, basis_l.bases[i], basis_r.bases[i]) for i=1:N]...)
95-
96-
# Embed all joint-subspace operators.
97-
idxop_comp = [x for x in zip(indices, operators) if x[1] isa Array]
98-
for (idxs, op) in idxop_comp
99-
embed_op *= embed(basis_l, basis_r, idxs, op)
100-
end
101-
102-
return embed_op
103-
end
16+
using QuantumInterface: arithmetic_binary_error, arithmetic_unary_error, addnumbererror
10417

10518

10619
"""
@@ -156,12 +69,6 @@ function embed(basis_l::CompositeBasis, basis_r::CompositeBasis,
15669

15770
return unpermuted_op
15871
end
159-
# The dictionary implementation works for non-DataOperators
160-
embed(basis_l::CompositeBasis, basis_r::CompositeBasis, indices, op::T) where T<:AbstractOperator = embed(basis_l, basis_r, Dict(indices=>op))
161-
162-
embed(basis_l::CompositeBasis, basis_r::CompositeBasis, index::Integer, op::AbstractOperator) = embed(basis_l, basis_r, index, [op])
163-
embed(basis::CompositeBasis, indices, operators::Vector{T}) where {T<:AbstractOperator} = embed(basis, basis, indices, operators)
164-
embed(basis::CompositeBasis, indices, op::AbstractOperator) = embed(basis, basis, indices, op)
16572

16673
function embed(basis_l::CompositeBasis, basis_r::CompositeBasis,
16774
index::Integer, op::T) where T<:DataOperator
@@ -195,70 +102,6 @@ function embed(basis_l::CompositeBasis, basis_r::CompositeBasis,
195102
return Operator(basis_l, basis_r, data)
196103
end
197104

198-
"""
199-
embed(basis1[, basis2], operators::Dict)
200-
201-
`operators` is a dictionary `Dict{Vector{Int}, AbstractOperator}`. The integer vector
202-
specifies in which subsystems the corresponding operator is defined.
203-
"""
204-
function embed(basis_l::CompositeBasis, basis_r::CompositeBasis,
205-
operators::Dict{<:Vector{<:Integer}, T}) where T<:AbstractOperator
206-
@assert length(basis_l.bases) == length(basis_r.bases)
207-
N = length(basis_l.bases)::Int # type assertion to help type inference
208-
if length(operators) == 0
209-
return identityoperator(T, basis_l, basis_r)
210-
end
211-
indices, operator_list = zip(operators...)
212-
operator_list = [operator_list...;]
213-
S = mapreduce(eltype, promote_type, operator_list)
214-
indices_flat = [indices...;]::Vector{Int} # type assertion to help type inference
215-
start_indices_flat = [i[1] for i in indices]
216-
complement_indices_flat = Int[i for i=1:N if i indices_flat]
217-
operators_flat = AbstractOperator[]
218-
if all(([minimum(I):maximum(I);]==I)::Bool for I in indices) # type assertion to help type inference
219-
for i in 1:N
220-
if i in complement_indices_flat
221-
push!(operators_flat, identityoperator(T, S, basis_l.bases[i], basis_r.bases[i]))
222-
elseif i in start_indices_flat
223-
push!(operators_flat, operator_list[indexin(i, start_indices_flat)[1]])
224-
end
225-
end
226-
return tensor(operators_flat...)
227-
else
228-
complement_operators = [identityoperator(T, S, basis_l.bases[i], basis_r.bases[i]) for i in complement_indices_flat]
229-
op = tensor([operator_list; complement_operators]...)
230-
perm = sortperm([indices_flat; complement_indices_flat])
231-
return permutesystems(op, perm)
232-
end
233-
end
234-
embed(basis_l::CompositeBasis, basis_r::CompositeBasis, operators::Dict{<:Integer, T}; kwargs...) where {T<:AbstractOperator} = embed(basis_l, basis_r, Dict([i]=>op_i for (i, op_i) in operators); kwargs...)
235-
embed(basis::CompositeBasis, operators::Dict{<:Integer, T}; kwargs...) where {T<:AbstractOperator} = embed(basis, basis, operators; kwargs...)
236-
embed(basis::CompositeBasis, operators::Dict{<:Vector{<:Integer}, T}; kwargs...) where {T<:AbstractOperator} = embed(basis, basis, operators; kwargs...)
237-
238-
239-
"""
240-
tr(x::AbstractOperator)
241-
242-
Trace of the given operator.
243-
"""
244-
tr(x::AbstractOperator) = arithmetic_unary_error("Trace", x)
245-
246-
ptrace(a::AbstractOperator, index) = arithmetic_unary_error("Partial trace", a)
247-
248-
"""
249-
normalize(op)
250-
251-
Return the normalized operator so that its `tr(op)` is one.
252-
"""
253-
normalize(op::AbstractOperator) = op/tr(op)
254-
255-
"""
256-
normalize!(op)
257-
258-
In-place normalization of the given operator so that its `tr(x)` is one.
259-
"""
260-
normalize!(op::AbstractOperator) = throw(ArgumentError("normalize! is not defined for this type of operator: $(typeof(op)).\n You may have to fall back to the non-inplace version 'normalize()'."))
261-
262105
"""
263106
expect(op, state)
264107
@@ -267,30 +110,15 @@ Expectation value of the given operator `op` for the specified `state`.
267110
`state` can either be a (density) operator or a ket.
268111
"""
269112
expect(op::AbstractOperator{B,B}, state::Ket{B}) where B = dot(state.data, (op * state).data)
270-
expect(op::AbstractOperator{B1,B2}, state::AbstractOperator{B2,B2}) where {B1,B2} = tr(op*state)
271113

272-
"""
273-
expect(index, op, state)
274-
275-
If an `index` is given, it assumes that `op` is defined in the subsystem specified by this number.
276-
"""
277-
function expect(indices, op::AbstractOperator{B1,B2}, state::AbstractOperator{B3,B3}) where {B1,B2,B3<:CompositeBasis}
278-
N = length(state.basis_l.shape)
279-
indices_ = complement(N, indices)
280-
expect(op, ptrace(state, indices_))
281-
end
282114
function expect(indices, op::AbstractOperator{B,B}, state::Ket{B2}) where {B,B2<:CompositeBasis}
283115
N = length(state.basis.shape)
284116
indices_ = complement(N, indices)
285117
expect(op, ptrace(state, indices_))
286118
end
287119

288-
expect(index::Integer, op::AbstractOperator{B1,B2}, state::AbstractOperator{B3,B3}) where {B1,B2,B3<:CompositeBasis} = expect([index], op, state)
289120
expect(index::Integer, op::AbstractOperator{B,B}, state::Ket{B2}) where {B,B2<:CompositeBasis} = expect([index], op, state)
290121

291-
expect(op::AbstractOperator, states::Vector) = [expect(op, state) for state=states]
292-
expect(indices, op::AbstractOperator, states::Vector) = [expect(indices, op, state) for state=states]
293-
294122
"""
295123
variance(op, state)
296124
@@ -302,60 +130,14 @@ function variance(op::AbstractOperator{B,B}, state::Ket{B}) where B
302130
x = op*state
303131
state.data'*(op*x).data - (state.data'*x.data)^2
304132
end
305-
function variance(op::AbstractOperator{B,B}, state::AbstractOperator{B,B}) where B
306-
expect(op*op, state) - expect(op, state)^2
307-
end
308-
309-
"""
310-
variance(index, op, state)
311133

312-
If an `index` is given, it assumes that `op` is defined in the subsystem specified by this number
313-
"""
314-
function variance(indices, op::AbstractOperator{B,B}, state::AbstractOperator{BC,BC}) where {B,BC<:CompositeBasis}
315-
N = length(state.basis_l.shape)
316-
indices_ = complement(N, indices)
317-
variance(op, ptrace(state, indices_))
318-
end
319134
function variance(indices, op::AbstractOperator{B,B}, state::Ket{BC}) where {B,BC<:CompositeBasis}
320135
N = length(state.basis.shape)
321136
indices_ = complement(N, indices)
322137
variance(op, ptrace(state, indices_))
323138
end
324139

325-
variance(index::Integer, op::AbstractOperator{B,B}, state::AbstractOperator{BC,BC}) where {B,BC<:CompositeBasis} = variance([index], op, state)
326140
variance(index::Integer, op::AbstractOperator{B,B}, state::Ket{BC}) where {B,BC<:CompositeBasis} = variance([index], op, state)
327-
variance(op::AbstractOperator, states::Vector) = [variance(op, state) for state=states]
328-
variance(indices, op::AbstractOperator, states::Vector) = [variance(indices, op, state) for state=states]
329-
330-
331-
"""
332-
exp(op::AbstractOperator)
333-
334-
Operator exponential.
335-
"""
336-
exp(op::AbstractOperator) = throw(ArgumentError("exp() is not defined for this type of operator: $(typeof(op)).\nTry to convert to dense operator first with dense()."))
337-
338-
permutesystems(a::AbstractOperator, perm) = arithmetic_unary_error("Permutations of subsystems", a)
339-
340-
"""
341-
identityoperator(a::Basis[, b::Basis])
342-
identityoperator(::Type{<:AbstractOperator}, a::Basis[, b::Basis])
343-
identityoperator(::Type{<:Number}, a::Basis[, b::Basis])
344-
identityoperator(::Type{<:AbstractOperator}, ::Type{<:Number}, a::Basis[, b::Basis])
345-
346-
Return an identityoperator in the given bases. One can optionally specify the container
347-
type which has to a subtype of [`AbstractOperator`](@ref) as well as the number type
348-
to be used in the identity matrix.
349-
"""
350-
identityoperator(::Type{T}, ::Type{S}, b1::Basis, b2::Basis) where {T<:AbstractOperator,S} = throw(ArgumentError("Identity operator not defined for operator type $T."))
351-
identityoperator(::Type{T}, ::Type{S}, b::Basis) where {T<:AbstractOperator,S} = identityoperator(T,S,b,b)
352-
identityoperator(::Type{T}, bases::Basis...) where T<:AbstractOperator = identityoperator(T,eltype(T),bases...)
353-
identityoperator(op::T) where {T<:AbstractOperator} = identityoperator(T, op.basis_l, op.basis_r)
354-
355-
# Catch case where eltype cannot be inferred from type; this is a bit hacky
356-
identityoperator(::Type{T}, ::Type{Any}, b1::Basis, b2::Basis) where T<:AbstractOperator = identityoperator(T, ComplexF64, b1, b2)
357-
358-
one(x::Union{<:Basis,<:AbstractOperator}) = identityoperator(x)
359141

360142
# Helper functions to check validity of arguments
361143
function check_ptrace_arguments(a::AbstractOperator, indices)
@@ -383,17 +165,5 @@ function check_ptrace_arguments(a::StateVector, indices)
383165
check_indices(length(basis(a).shape), indices)
384166
end
385167

386-
samebases(a::AbstractOperator) = samebases(a.basis_l, a.basis_r)::Bool
387-
samebases(a::AbstractOperator, b::AbstractOperator) = samebases(a.basis_l, b.basis_l)::Bool && samebases(a.basis_r, b.basis_r)::Bool
388-
check_samebases(a::AbstractOperator) = check_samebases(a.basis_l, a.basis_r)
389-
390168
multiplicable(a::AbstractOperator, b::Ket) = multiplicable(a.basis_r, b.basis)
391169
multiplicable(a::Bra, b::AbstractOperator) = multiplicable(a.basis, b.basis_l)
392-
multiplicable(a::AbstractOperator, b::AbstractOperator) = multiplicable(a.basis_r, b.basis_l)
393-
394-
Base.size(op::AbstractOperator) = (length(op.basis_l),length(op.basis_r))
395-
function Base.size(op::AbstractOperator, i::Int)
396-
i < 1 && throw(ErrorException("dimension index is < 1"))
397-
i > 2 && return 1
398-
i==1 ? length(op.basis_l) : length(op.basis_r)
399-
end

src/operators_dense.jl

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -204,9 +204,6 @@ function ptrace(psi::Bra, indices)
204204
return Operator(b_, b_, result)
205205
end
206206

207-
_index_complement(b::CompositeBasis, indices) = complement(length(b.bases), indices)
208-
reduced(a, indices) = ptrace(a, _index_complement(basis(a), indices))
209-
210207
normalize!(op::Operator) = (rmul!(op.data, 1.0/tr(op)); op)
211208

212209
function expect(op::DataOperator{B,B}, state::Ket{B}) where B

src/operators_sparse.jl

Lines changed: 2 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,6 @@ SparseOperator(::Type{T},b::Basis) where T = SparseOperator(b,b,spzeros(T,length
2424
SparseOperator(b1::Basis, b2::Basis) = SparseOperator(ComplexF64, b1, b2)
2525
SparseOperator(b::Basis) = SparseOperator(ComplexF64, b, b)
2626

27-
"""
28-
sparse(op::AbstractOperator)
29-
30-
Convert an arbitrary operator into a [`SparseOperator`](@ref).
31-
"""
32-
sparse(a::AbstractOperator) = throw(ArgumentError("Direct conversion from $(typeof(a)) not implemented. Use sparse(full(op)) instead."))
3327
sparse(a::DataOperator) = Operator(a.basis_l, a.basis_r, sparse(a.data))
3428

3529
function ptrace(op::SparseOpPureType, indices)
@@ -56,7 +50,7 @@ function permutesystems(rho::SparseOpPureType{B1,B2}, perm) where {B1<:Composite
5650
@assert length(rho.basis_l.bases) == length(rho.basis_r.bases) == length(perm)
5751
@assert isperm(perm)
5852
shape = [rho.basis_l.shape; rho.basis_r.shape]
59-
data = permutedims(rho.data, shape, [perm; perm .+ length(perm)])
53+
data = _permutedims(rho.data, shape, [perm; perm .+ length(perm)])
6054
SparseOperator(permutesystems(rho.basis_l, perm), permutesystems(rho.basis_r, perm), data)
6155
end
6256

@@ -74,10 +68,7 @@ identityoperator(::Type{T}, ::Type{S}, b1::Basis, b2::Basis) where {T<:DataOpera
7468
identityoperator(::Type{T}, ::Type{S}, b::Basis) where {T<:DataOperator,S<:Number} =
7569
Operator(b, b, Eye{S}(length(b)))
7670

77-
identityoperator(::Type{T}, b1::Basis, b2::Basis) where T<:Number = identityoperator(DataOperator, T, b1, b2)
78-
identityoperator(::Type{T}, b::Basis) where T<:Number = identityoperator(DataOperator, T, b)
79-
identityoperator(b1::Basis, b2::Basis) = identityoperator(ComplexF64, b1, b2)
80-
identityoperator(b::Basis) = identityoperator(ComplexF64, b)
71+
identityoperator(::Type{T}, b1::Basis, b2::Basis) where T<:Number = identityoperator(DataOperator, T, b1, b2) # XXX This is purposeful type piracy over QuantumInterface, that hardcodes the use of QuantumOpticsBase.DataOperator in identityoperator. Also necessary for backward compatibility.
8172

8273
"""
8374
diagonaloperator(b::Basis)

0 commit comments

Comments
 (0)