Skip to content
This repository was archived by the owner on Jul 19, 2023. It is now read-only.

Commit 3197ca5

Browse files
Merge pull request #55 from MSeeker1340/linear-combination
Linear Combinations of Operators
2 parents 396f7ea + 590acb6 commit 3197ca5

File tree

7 files changed

+114
-10
lines changed

7 files changed

+114
-10
lines changed

src/DiffEqOperators.jl

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ __precompile__()
33
module DiffEqOperators
44

55
import LinearMaps: LinearMap, AbstractLinearMap
6+
using LinearMaps: LinearCombination, IdentityMap
67
import Base: *, getindex
78
using DiffEqBase, StaticArrays
89
import DiffEqBase: update_coefficients, update_coefficients!
@@ -21,6 +22,10 @@ include("derivative_operators/derivative_operator.jl")
2122
include("derivative_operators/abstract_operator_functions.jl")
2223
include("derivative_operators/boundary_operators.jl")
2324

25+
### Linear Combination of Operators
26+
include("operator_combination.jl")
27+
2428
export DiffEqScalar, DiffEqArrayOperator
2529
export AbstractDerivativeOperator, DerivativeOperator, UpwindOperator, FiniteDifference
30+
export normbound
2631
end # module

src/array_operator.jl

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -52,18 +52,11 @@ Base.issymmetric(L::DiffEqArrayOperator) = L._issymmetric
5252
Base.ishermitian(L::DiffEqArrayOperator) = L._ishermitian
5353
Base.isposdef(L::DiffEqArrayOperator) = L._isposdef
5454
DiffEqBase.is_constant(L::DiffEqArrayOperator) = L.update_func == DEFAULT_UPDATE_FUNC
55-
function Base.expm(L::DiffEqArrayOperator)
56-
tmp = full(L.A) # If not lazy then this is a no-op
57-
tmp .*= L.α.coeff
58-
out = expm(tmp)
59-
if tmp === L.A
60-
L.A ./= L.α.coeff # Undo change if not lazy
61-
end
62-
out
63-
end
55+
Base.full(L::DiffEqArrayOperator) = full(L.A) .* L.α.coeff
56+
Base.expm(L::DiffEqArrayOperator) = expm(full(L))
6457
DiffEqBase.has_expm(L::DiffEqArrayOperator) = true
6558
Base.size(L::DiffEqArrayOperator) = size(L.A)
66-
Base.norm(L::DiffEqArrayOperator, p::Real=2) = norm(L.A, p)
59+
Base.norm(L::DiffEqArrayOperator, p::Real=2) = norm(L.A, p) * abs(L.α.coeff)
6760
DiffEqBase.update_coefficients!(L::DiffEqArrayOperator,t,u) = (L.update_func(L.A,t,u); L.α = L.α(t); nothing)
6861
DiffEqBase.update_coefficients(L::DiffEqArrayOperator,t,u) = (L.update_func(L.A,t,u); L.α = L.α(t); L)
6962

src/derivative_operators/abstract_operator_functions.jl

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -209,3 +209,12 @@ function Base.sparse(A::AbstractDerivativeOperator{T}) where T
209209
end
210210
return mat
211211
end
212+
213+
#=
214+
Fallback methods that use the full representation of the operator
215+
=#
216+
Base.expm(A::AbstractDerivativeOperator{T}) where T = expm(full(A))
217+
Base.:\(A::AbstractVecOrMat, B::AbstractDerivativeOperator) = A \ full(B)
218+
Base.:\(A::AbstractDerivativeOperator, B::AbstractVecOrMat) = full(A) \ B
219+
Base.:/(A::AbstractVecOrMat, B::AbstractDerivativeOperator) = A / full(B)
220+
Base.:/(A::AbstractDerivativeOperator, B::AbstractVecOrMat) = full(A) / B

src/operator_combination.jl

Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
#=
2+
Most of the functionality for linear combination of operators is already
3+
covered in LinearMaps.jl.
4+
=#
5+
6+
(L::LinearCombination)(u,p,t) = L*u
7+
(L::LinearCombination)(du,u,p,t) = A_mul_B!(du,L,u)
8+
9+
#=
10+
The fallback implementation in LinearMaps.jl effectively computes A*eye(N),
11+
which is very inefficient.
12+
13+
Instead, build up the full matrix for each operator iteratively.
14+
=#
15+
# TODO: Type dispatch for this is incorrect at the moment
16+
# function Base.full(A::LinearCombination{T,Tuple{Vararg{O}},Ts}) where {T,O<:Union{AbstractDiffEqLinearOperator,IdentityMap},Ts}
17+
# out = zeros(T,size(A))
18+
# for i = 1:length(A.maps)
19+
# c = A.coeffs[i]
20+
# op = A.maps[i]
21+
# if isa(op, IdentityMap)
22+
# @. out += c * eye(size(A,1))
23+
# else
24+
# @. out += c * full(op)
25+
# end
26+
# end
27+
# return out
28+
# end
29+
30+
#=
31+
Fallback methods that use the full representation
32+
=#
33+
Base.expm(A::LinearCombination) = expm(full(A))
34+
Base.:\(A::AbstractVecOrMat, B::LinearCombination) = A \ full(B)
35+
Base.:\(A::LinearCombination, B::AbstractVecOrMat) = full(A) \ B
36+
Base.:/(A::AbstractVecOrMat, B::LinearCombination) = A / full(B)
37+
Base.:/(A::LinearCombination, B::AbstractVecOrMat) = full(A) / B
38+
39+
Base.norm(A::IdentityMap{T}, p::Real=2) where T = real(one(T))
40+
Base.norm(A::LinearCombination, p::Real=2) = norm(full(A), p)
41+
#=
42+
The norm of A+B is difficult to calculate, but in many applications we only
43+
need an estimate of the norm (e.g. for error analysis) so it makes sense to
44+
compute the upper bound given by the triangle inequality
45+
46+
|A + B| <= |A| + |B|
47+
48+
For derivative operators A and B, their Inf norm can be calculated easily
49+
and thus so is the Inf norm bound of A + B.
50+
=#
51+
normbound(a::Number, p::Real=2) = abs(a)
52+
normbound(A::AbstractArray, p::Real=2) = norm(A, p)
53+
normbound(A::Union{AbstractDiffEqLinearOperator,IdentityMap}, p::Real=2) = norm(A, p)
54+
normbound(A::LinearCombination, p::Real=2) = sum(abs.(A.coeffs) .* normbound.(A.maps, p))

test/array_operators_interface.jl

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
using DiffEqOperators
2+
using Base.Test
3+
4+
N = 5
5+
srand(0); A = rand(N,N); u = rand(N)
6+
L = DiffEqArrayOperator(A)
7+
a = 3.5
8+
La = L * a
9+
10+
@test La * u (a*A) * u
11+
@test lufact(La) \ u (a*A) \ u
12+
@test norm(La) norm(a*A)
13+
@test expm(La) expm(a*A)
14+
@test La[2,3] A[2,3] # should this be La[2,3] == a*A[2,3]?
15+
16+
update_func = (_A,t,u) -> _A .= t * A
17+
t = 3.0
18+
Atmp = zeros(N,N)
19+
Lt = DiffEqArrayOperator(Atmp, a, update_func)
20+
@test Lt(u,nothing,t) (a*t*A) * u

test/derivative_operators_interface.jl

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,3 +87,25 @@ end
8787
@test G*B 8*ones(N,M) atol=1e-2
8888
@test A*G*B zeros(N,M) atol=1e-2
8989
end
90+
91+
@testset "Linear combinations of operators" begin
92+
# Only tests the additional functionality defined in "operator_combination.jl"
93+
N = 10
94+
srand(0); LA = DiffEqArrayOperator(rand(N,N))
95+
LD = DerivativeOperator{Float64}(2,2,1.0,N,:Dirichlet0,:Dirichlet0)
96+
L = 1.1*LA - 2.2*LD + 3.3*I
97+
# Builds full(L) the brute-force way
98+
fullL = zeros(N,N)
99+
v = zeros(N)
100+
for i = 1:N
101+
v[i] = 1.0
102+
fullL[:,i] = L*v
103+
v[i] = 0.0
104+
end
105+
@test full(L) fullL
106+
@test expm(L) expm(fullL)
107+
for p in [1,2,Inf]
108+
@test norm(L,p) norm(fullL,p)
109+
@test normbound(L,p) 1.1*norm(LA,p) + 2.2*norm(LD,p) + 3.3
110+
end
111+
end

test/runtests.jl

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ using Base.Test
33
using SpecialMatrices, SpecialFunctions
44

55
tic()
6+
@time @testset "Array Operators Interface" begin include("array_operators_interface.jl") end
67
@time @testset "Derivative Operators Interface" begin include("derivative_operators_interface.jl") end
78
@time @testset "Dirichlet BCs" begin include("dirichlet.jl") end
89
@time @testset "Periodic BCs" begin include("periodic.jl") end

0 commit comments

Comments
 (0)