Skip to content

Commit 97368c9

Browse files
committed
Merge #101 from branch operator-matrix-interface
2 parents 9d1331a + b1c80cc commit 97368c9

3 files changed

Lines changed: 110 additions & 6 deletions

File tree

src/generators.jl

Lines changed: 49 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,33 @@ Base.size(O::Operator) = size(O.ops[1])
180180
Base.size(O::Operator, dim::Integer) = size(O.ops[1], dim)
181181
Base.eltype(::Type{Operator{OT,CT}}) where {OT,CT} = promote_type(eltype(OT), CT)
182182

183+
function Base.getindex(O::Operator, i::Int, j::Int)
184+
drift_offset = length(O.ops) - length(O.coeffs)
185+
result = (drift_offset == 0 ? O.coeffs[1] : one(eltype(O))) * O.ops[1][i, j]
186+
for k = 2:length(O.ops)
187+
c = k <= drift_offset ? one(eltype(O)) : O.coeffs[k-drift_offset]
188+
result += c * O.ops[k][i, j]
189+
end
190+
return result
191+
end
192+
193+
Base.length(O::Operator) = prod(size(O))
194+
195+
function Base.iterate(O::Operator, k = 1)
196+
n = length(O)
197+
k > n && return nothing
198+
n_rows = size(O, 1)
199+
i = (k - 1) % n_rows + 1
200+
j = (k - 1) ÷ n_rows + 1
201+
return (O[i, j], k + 1)
202+
end
203+
204+
Base.similar(O::Operator) = Array{eltype(O)}(undef, size(O))
205+
Base.similar(O::Operator, ::Type{S}) where {S} = Array{S}(undef, size(O))
206+
Base.similar(O::Operator, dims::Tuple{Vararg{Int}}) = Array{eltype(O)}(undef, dims)
207+
Base.similar(O::Operator, ::Type{S}, dims::Tuple{Vararg{Int}}) where {S} =
208+
Array{S}(undef, dims)
209+
183210

184211
function LinearAlgebra.ishermitian(O::Operator{OT,CT}) where {OT,CT}
185212
return all(ishermitian(op) for op in O.ops) && all(isreal(c) for c in O.coeffs)
@@ -254,8 +281,28 @@ Base.Array(O::ScaledOperator) = Array{eltype(O)}(O)
254281

255282
Base.size(O::ScaledOperator) = size(O.operator)
256283
Base.size(O::ScaledOperator, dim::Integer) = size(O.operator, dim)
257-
Base.eltype(::Type{ScaledOperator{CT,Operator{OOT,OCT}}}) where {CT,OOT,OCT} =
258-
promote_type(CT, eltype(OOT), OCT)
284+
Base.eltype(::Type{ScaledOperator{CT,OT}}) where {CT,OT} = promote_type(CT, eltype(OT))
285+
286+
function Base.getindex(O::ScaledOperator, i::Int, j::Int)
287+
return O.coeff * O.operator[i, j]
288+
end
289+
290+
Base.length(O::ScaledOperator) = prod(size(O))
291+
292+
function Base.iterate(O::ScaledOperator, k = 1)
293+
n = length(O)
294+
k > n && return nothing
295+
n_rows = size(O, 1)
296+
i = (k - 1) % n_rows + 1
297+
j = (k - 1) ÷ n_rows + 1
298+
return (O[i, j], k + 1)
299+
end
300+
301+
Base.similar(O::ScaledOperator) = Array{eltype(O)}(undef, size(O))
302+
Base.similar(O::ScaledOperator, ::Type{S}) where {S} = Array{S}(undef, size(O))
303+
Base.similar(O::ScaledOperator, dims::Tuple{Vararg{Int}}) = Array{eltype(O)}(undef, dims)
304+
Base.similar(O::ScaledOperator, ::Type{S}, dims::Tuple{Vararg{Int}}) where {S} =
305+
Array{S}(undef, dims)
259306

260307
LinearAlgebra.ishermitian(O::ScaledOperator) = (isreal(O.coeff) && ishermitian(O.operator))
261308

src/interfaces/supports_matrix_interface.jl

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,6 @@ of `AbstractMatrix`. Calling `supports_matrix_interface` on an instance
1313
`x` also works via a convenience fallback that forwards to
1414
`supports_matrix_interface(typeof(x))`.
1515
16-
Depending the value of [`supports_inplace`](@ref), `T` may implement a
17-
read-write matrix (`setindex!` etc.) or a read-only matrix.
18-
1916
The matrix interface is encouraged for [operators](@ref Operators), and the
2017
specific conditions of the required interface in that context are checked via
2118
[`QuantumPropagators.Interfaces.check_operator`](@ref).
@@ -25,3 +22,10 @@ supports_matrix_interface(::Type{<:AbstractMatrix}) = true
2522
supports_matrix_interface(::Type{T}) where {T} = false
2623

2724
supports_matrix_interface(x) = supports_matrix_interface(typeof(x))
25+
26+
import ..Operator
27+
import ..ScaledOperator
28+
29+
supports_matrix_interface(::Type{<:Operator{OT}}) where {OT} = supports_matrix_interface(OT)
30+
supports_matrix_interface(::Type{<:ScaledOperator{<:Any,OT}}) where {OT} =
31+
supports_matrix_interface(OT)

test/test_operator_linalg.jl

Lines changed: 54 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,29 @@
11
using Test
22
using LinearAlgebra
33
using QuantumControlTestUtils.RandomObjects: random_matrix, random_state_vector
4-
using QuantumPropagators.Interfaces: check_operator
4+
using QuantumPropagators.Interfaces: check_operator, supports_matrix_interface
5+
import QuantumPropagators.Interfaces: supports_inplace
6+
import QuantumPropagators.Controls: get_controls, evaluate
57
using StaticArrays: SMatrix, SVector
68

79
using QuantumPropagators: Generator, Operator, ScaledOperator
810

11+
# A minimal operator type that does not subtype AbstractMatrix, to test that
12+
# supports_matrix_interface propagates correctly through Operator/ScaledOperator.
13+
struct MatrixFreeOp
14+
mat::Matrix{ComplexF64}
15+
end
16+
17+
Base.size(op::MatrixFreeOp) = size(op.mat)
18+
Base.size(op::MatrixFreeOp, dim::Integer) = size(op.mat, dim)
19+
Base.:*::Number, op::MatrixFreeOp) = MatrixFreeOp* op.mat)
20+
Base.:*(op::MatrixFreeOp, Ψ::AbstractVector) = op.mat * Ψ
21+
LinearAlgebra.mul!(ϕ, op::MatrixFreeOp, Ψ, α::Number, β::Number) = mul!(ϕ, op.mat, Ψ, α, β)
22+
LinearAlgebra.dot(ϕ, op::MatrixFreeOp, Ψ) = dot(ϕ, op.mat, Ψ)
23+
supports_inplace(::Type{MatrixFreeOp}) = true
24+
get_controls(::MatrixFreeOp) = ()
25+
evaluate(op::MatrixFreeOp, args...; kwargs...) = op
26+
927

1028
@testset "Operator mul!" begin
1129

@@ -71,6 +89,7 @@ end
7189
H₂ = random_matrix(5; hermitian = true)
7290
Op = Operator([H₀, H₁, H₂], [2.1, 1.1])
7391
Ψ = random_state_vector(5)
92+
@test supports_matrix_interface(typeof(Op))
7493
@test check_operator(Op; state = Ψ)
7594
end
7695

@@ -277,5 +296,39 @@ end
277296
Op = Operator([H₀, H₁, H₂], [2.1, 1.1]) * 0.5
278297
@test Op isa ScaledOperator
279298
Ψ = random_state_vector(5)
299+
@test supports_matrix_interface(typeof(Op))
280300
@test check_operator(Op; state = Ψ)
281301
end
302+
303+
304+
@testset "supports_matrix_interface" begin
305+
306+
H₀ = random_matrix(5; hermitian = true)
307+
H₁ = random_matrix(5; hermitian = true)
308+
H₂ = random_matrix(5; hermitian = true)
309+
Ψ = random_state_vector(5)
310+
311+
# Operator wrapping standard Matrix → true
312+
Op = Operator([H₀, H₁, H₂], [2.1, 1.1])
313+
@test supports_matrix_interface(typeof(Op))
314+
315+
# ScaledOperator wrapping Operator{Matrix} → true
316+
SOp = Op * 0.5
317+
@test SOp isa ScaledOperator
318+
@test supports_matrix_interface(typeof(SOp))
319+
320+
# Operator wrapping MatrixFreeOp (not AbstractMatrix) → false, but valid operator
321+
h₀ = MatrixFreeOp(H₀)
322+
h₁ = MatrixFreeOp(H₁)
323+
h₂ = MatrixFreeOp(H₂)
324+
FreeOp = Operator([h₀, h₁, h₂], [2.1, 1.1])
325+
@test !supports_matrix_interface(typeof(FreeOp))
326+
@test check_operator(FreeOp; state = Ψ)
327+
328+
# ScaledOperator wrapping Operator{MatrixFreeOp} → false, but valid operator
329+
SFreeOp = FreeOp * 0.5
330+
@test SFreeOp isa ScaledOperator
331+
@test !supports_matrix_interface(typeof(SFreeOp))
332+
@test check_operator(SFreeOp; state = Ψ)
333+
334+
end

0 commit comments

Comments
 (0)