diff --git a/src/gradgen_operator.jl b/src/gradgen_operator.jl index 8c4a4be..829660c 100644 --- a/src/gradgen_operator.jl +++ b/src/gradgen_operator.jl @@ -51,8 +51,10 @@ end _exp_prop_convert_operator(::GradgenOperator) = Matrix{ComplexF64} -supports_inplace(::Type{GradgenOperator{N,GT,CGT}}) where {N,GT,CGT} = - (supports_inplace(GT) && supports_inplace(CGT)) +supports_inplace(::Type{GradgenOperator{N,GT,CGT}}) where {N,GT,CGT} = supports_inplace(GT) +# Note: `evaluate!` for `GradgenOperator` reassigns `control_deriv_ops[i]` via +# vector element assignment, so the in-place capability of `CGT` is irrelevant. +# Only the underlying operator `GT` needs to support in-place evaluation. supports_matrix_interface(::Type{<:GradgenOperator{N,GT,CGT}}) where {N,GT,CGT} = supports_matrix_interface(GT) && supports_matrix_interface(CGT) diff --git a/src/linalg.jl b/src/linalg.jl index b5da82a..ab6a1af 100644 --- a/src/linalg.jl +++ b/src/linalg.jl @@ -241,52 +241,32 @@ function Base.fill!(Ψ::GradVector, v) end -# === Matrix interface for GradgenOperator === -# -# The following methods are part of the matrix interface and are only -# meaningful when `supports_matrix_interface` is true for both component types. -# Each method delegates to a private `_name(::Val{supports}, ...)` function: -# the Val{true} method contains the implementation, and the Val{false} method -# throws an error. - -function _size( - ::Val{true}, - O::GradgenOperator{num_controls,GT,CGT} -) where {num_controls,GT,CGT} +function Base.size(O::GradgenOperator{num_controls,GT,CGT}) where {num_controls,GT,CGT} return (num_controls + 1) .* size(O.G) end -function _size(::Val{false}, O::GradgenOperator) - error("$(typeof(O)) does not support the matrix interface") -end - -function Base.size(O::T) where {T<:GradgenOperator} - return _size(Val(supports_matrix_interface(T)), O) -end - - -function _size( - ::Val{true}, +function Base.size( O::GradgenOperator{num_controls,GT,CGT}, dim::Integer ) where {num_controls,GT,CGT} return (num_controls + 1) * size(O.G, dim) end -function _size(::Val{false}, O::GradgenOperator, dim::Integer) - error("$(typeof(O)) does not support the matrix interface") -end -function Base.size(O::T, dim::Integer) where {T<:GradgenOperator} - return _size(Val(supports_matrix_interface(T)), O, dim) -end +# === Matrix interface for GradgenOperator === +# +# The following methods are part of the matrix interface and are only +# meaningful when `supports_matrix_interface` is true for both component types. +# Each method delegates to a private `_name(::Val{supports}, ...)` function: +# the Val{true} method contains the implementation, and the Val{false} method +# throws an error. Note that this does not include `size`, which must be +# defined for _all_ operators, whether or not they define the full matrix +# interface. # As for an `Operator`, we implement `similar` to return a standard `Array` # because `GradgenOperator` does not `setindex!`, so it's arguably not a # "mutable array" even if its components are mutable. -# similar(O) and similar(O, S) call size(O), which will error if -# !supports_matrix_interface. The dims-based variants need no guard. Base.similar(G::GradgenOperator) = Array{eltype(G)}(undef, size(G)) Base.similar(O::GradgenOperator, ::Type{S}) where {S} = Array{S}(undef, size(O)) diff --git a/test/test_interface.jl b/test/test_interface.jl index 24a31fd..ddde93a 100644 --- a/test/test_interface.jl +++ b/test/test_interface.jl @@ -301,8 +301,6 @@ end # Matrix interface methods must throw an error when not supported @test_throws "does not support the matrix interface" op[1, 1] - @test_throws "does not support the matrix interface" size(op) - @test_throws "does not support the matrix interface" size(op, 1) @test_throws "does not support the matrix interface" length(op) @test_throws "does not support the matrix interface" iterate(op)