Skip to content

Draft a ConcretizedOperator #213

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 11 commits into
base: master
Choose a base branch
from
3 changes: 2 additions & 1 deletion src/SciMLOperators.jl
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,8 @@ export
AffineOperator,
AddVector,
FunctionOperator,
TensorProductOperator
TensorProductOperator,
ConcretizedOperator

export update_coefficients!,
update_coefficients,
Expand Down
115 changes: 115 additions & 0 deletions src/basic.jl
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,14 @@ has_mul!(::IdentityOperator) = true
has_ldiv(::IdentityOperator) = true
has_ldiv!(::IdentityOperator) = true

function concretize!(A, L::IdentityOperator, α, β)
@assert size(A) == (L.len, L.len)
A .*= β
for i in 1:L.len
A[i, i] += α
end
end

# opeator application
for op in (
:*, :\,
Expand Down Expand Up @@ -259,6 +267,10 @@ has_mul!(L::ScaledOperator) = has_mul!(L.L)
has_ldiv(L::ScaledOperator) = has_ldiv(L.L) & !iszero(L.λ)
has_ldiv!(L::ScaledOperator) = has_ldiv!(L.L) & !iszero(L.λ)

function concretize!(A, L::ScaledOperator{T}, α, β) where {T}
concretize!(A, L.L, convert(Number, L.λ) * α, β)
end

function cache_internals(L::ScaledOperator, u::AbstractVecOrMat)
@set! L.L = cache_operator(L.L, u)
@set! L.λ = cache_operator(L.λ, u)
Expand Down Expand Up @@ -420,6 +432,13 @@ function update_coefficients(L::AddedOperator, u, p, t)
@set! L.ops = ops
end

function concretize!(A, L::AddedOperator{T}, α, β) where {T}
A .*= β
for op in L.ops
concretize!(A, op, α, one(T))
end
end

getops(L::AddedOperator) = L.ops
islinear(L::AddedOperator) = all(islinear, getops(L))
Base.iszero(L::AddedOperator) = all(iszero, getops(L))
Expand Down Expand Up @@ -819,4 +838,100 @@ function LinearAlgebra.ldiv!(L::InvertedOperator, u::AbstractVecOrMat)
copy!(L.cache, u)
mul!(u, L.L, L.cache)
end

struct ConcretizedOperator{T, LType, AType} <: AbstractSciMLOperator{T}
L::LType
A::AType
function ConcretizedOperator(L::AbstractSciMLOperator{T}, A::AbstractMatrix) where {T}
new{T,typeof(L),typeof(A)}(L, A)
end
end


"""
ConcretizedOperator(L::AbstractSciMLOperator)

Concretization of a SciMLOperator `L`, with a concrete backing `A = concretize(L)` that is used
for all linear algebra operations. Unlike `A` itself, a concretized operator correctly supports
updates to the operator state, by first updating `L` and then updating `A` accordingly.
"""
function ConcretizedOperator(L::AbstractSciMLOperator{T}) where {T}
ConcretizedOperator(L, convert(AbstractMatrix, L))
end

Base.convert(::Type{AbstractMatrix}, L::ConcretizedOperator) = L.A

function Base.show(io::IO, L::ConcretizedOperator)
print(io, "ConcretizedOperator(")
show(io, L.L)
print(io, ")")
end
Base.size(L::ConcretizedOperator) = size(L.A)
function Base.resize!(L::ConcretizedOperator, n::Integer)
resize!(L.L, n)
resize!(L.cache, n)
# TODO: these next two lines seem dangerous... in which cases do they make sense?
resize!(L.A, n)
concretize!(L.A, L.L)
end

function update_coefficients(L::ConcretizedOperator, u, p, t; kwargs...)
@set! L.L = update_coefficients(L.L, u, p, t; kwargs...)
@set! L.A = concretize(L.L)
end

function update_coefficients!(L::ConcretizedOperator, u, p, t; kwargs...)
for op in getops(L)
update_coefficients!(op, u, p, t; kwargs...)
end
concretize!(L.A, L.L) # TODO: this needs to be supported. Also, problematic if L.A is scalar, should we only support matrix?
end

getops(L::ConcretizedOperator) = (L.L,)
islinear(L::ConcretizedOperator) = islinear(L.L)
isconvertible(::ConcretizedOperator) = true

for op in (
:adjoint,
:transpose,
:conj,
)
@eval Base.$op(L::ConcretizedOperator) = ConcretizedOperator($op(L.L), $op(L.A))
end

@forward ConcretizedOperator.L (
# LinearAlgebra
LinearAlgebra.issymmetric,
LinearAlgebra.ishermitian,
LinearAlgebra.isposdef,
LinearAlgebra.opnorm,

# SciML
isconstant,
has_adjoint,
has_mul,
has_mul!,
has_ldiv,
has_ldiv!
)

Base.:*(L::ConcretizedOperator, u::AbstractVecOrMat) = L.A * u
Base.:\(L::ConcretizedOperator, u::AbstractVecOrMat) = L.A \ u

function LinearAlgebra.mul!(v::AbstractVecOrMat, L::ConcretizedOperator, u::AbstractVecOrMat)
mul!(v, L.A, u)
end

function LinearAlgebra.mul!(v::AbstractVecOrMat, L::ConcretizedOperator, u::AbstractVecOrMat, α, β)
mul!(v, L.A, u, α, β)
end

function LinearAlgebra.ldiv!(v::AbstractVecOrMat, L::ConcretizedOperator, u::AbstractVecOrMat)
ldiv!(v, L.A, u)
end

function LinearAlgebra.ldiv!(L::ConcretizedOperator, u::AbstractVecOrMat)
ldiv!(L.A, u)
end

#
15 changes: 15 additions & 0 deletions src/interface.jl
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,21 @@ concretize(L::Union{
}
) = convert(Number, L)

function concretize!(A, L)
T = eltype(L)
concretize!(A, L, one(T), zero(T))
end

function concretize!(A, L::AbstractMatrix, α, β)
A .= α .* L .+ β .* A
end

function concretize!(A, L::Union{Factorization, AbstractSciMLOperator}, α, β)
@warn """using concretize-based fallback for concretize! for $(typeof(L))"""
# TODO: could also use a mul! based fallback on the unit vectors
concretize!(A, concretize(L), α, β)
end

"""
$SIGNATURES

Expand Down
4 changes: 4 additions & 0 deletions src/matrix.jl
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,10 @@ function update_coefficients!(L::MatrixOperator, u, p, t; kwargs...)
L.update_func!(L.A, u, p, t; kwargs...)
end

function concretize!(A, L::MatrixOperator, α, β)
return concretize!(A, L.A, α, β)
end

SparseArrays.sparse(L::MatrixOperator) = sparse(L.A)
SparseArrays.issparse(L::MatrixOperator) = issparse(L.A)

Expand Down
12 changes: 12 additions & 0 deletions test/basic.jl
Original file line number Diff line number Diff line change
Expand Up @@ -326,4 +326,16 @@ end
v=rand(N); @test ldiv!(v, Di, u) ≈ u .* s
v=copy(u); @test ldiv!(Di, u) ≈ v .* s
end

@testset "ConcretizedOperator" begin
A = rand(2, 2); B = rand(2, 2);
L = MatrixOperator(A; update_func=(u,p,t)->t * A) + 2 * MatrixOperator(B; update_func=(u,p,t)->t * B) + I
C = ConcretizedOperator(L)
v = rand(2)
C * v ≈ L * v
@test C.A ≈ convert(AbstractMatrix, L)
update_coefficients!(C, nothing, nothing, 2.0)
C * v ≈ L * v
@test C.A ≈ convert(AbstractMatrix, L)
end
#