Skip to content

Commit 1e51035

Browse files
author
RAYNAUD Paul (raynaudp)
committed
Reduce allocations in push! + modify tests
1 parent cab923d commit 1e51035

File tree

2 files changed

+45
-28
lines changed

2 files changed

+45
-28
lines changed

src/compressed_lbfgs.jl

+39-24
Original file line numberDiff line numberDiff line change
@@ -36,7 +36,6 @@ In addition to this structures which are circurlarly update when `k` reaches `m`
3636
- `inverse_intermediate_2`: a R²ᵏˣ²ᵏ matrix;
3737
- `intermediary_vector`: a vector ∈ Rᵏ to store intermediate solutions;
3838
- `sol`: a vector ∈ Rᵏ to store intermediate solutions;
39-
- `intermediate_structure_updated`: inform if the intermediate structures are up-to-date or not.
4039
This implementation is designed to work either on CPU or GPU.
4140
"""
4241
mutable struct CompressedLBFGS{T, M<:AbstractMatrix{T}, V<:AbstractVector{T}}
@@ -50,26 +49,26 @@ mutable struct CompressedLBFGS{T, M<:AbstractMatrix{T}, V<:AbstractVector{T}}
5049
Lₖ::LowerTriangular{T,M} # m * m
5150

5251
chol_matrix::M # 2m * 2m
52+
intermediate_diagonal::Diagonal{T,V} # m * m
5353
intermediate_1::UpperTriangular{T,M} # 2m * 2m
5454
intermediate_2::LowerTriangular{T,M} # 2m * 2m
5555
inverse_intermediate_1::UpperTriangular{T,M} # 2m * 2m
5656
inverse_intermediate_2::LowerTriangular{T,M} # 2m * 2m
5757
intermediary_vector::V # 2m
5858
sol::V # m
59-
intermediate_structure_updated::Bool
6059
end
6160

6261
default_gpu() = CUDA.functional() ? true : false
63-
default_matrix_type(gpu::Bool, T::DataType) = gpu ? CuMatrix{T} : Matrix{T}
64-
default_vector_type(gpu::Bool, T::DataType) = gpu ? CuVector{T} : Vector{T}
62+
default_matrix_type(gpu::Bool; T::DataType=Float64) = gpu ? CuMatrix{T} : Matrix{T}
63+
default_vector_type(gpu::Bool; T::DataType=Float64) = gpu ? CuVector{T} : Vector{T}
6564

6665
"""
6766
CompressedLBFGS(n::Int; [T=Float64, m=5], gpu:Bool)
6867
6968
A implementation of a LBFGS operator (forward), representing a `nxn` linear application.
7069
It considers at most `k` BFGS iterates, and fit the architecture depending if it is launched on a CPU or a GPU.
7170
"""
72-
function CompressedLBFGS(n::Int; m::Int=5, T=Float64, gpu=default_gpu(), M=default_matrix_type(gpu, T), V=default_vector_type(gpu, T))
71+
function CompressedLBFGS(n::Int; m::Int=5, T=Float64, gpu=default_gpu(), M=default_matrix_type(gpu; T), V=default_vector_type(gpu; T))
7372
α = (T)(1)
7473
k = 0
7574
Sₖ = M(undef, n, m)
@@ -78,14 +77,14 @@ function CompressedLBFGS(n::Int; m::Int=5, T=Float64, gpu=default_gpu(), M=defau
7877
Lₖ = LowerTriangular(M(undef, m, m))
7978

8079
chol_matrix = M(undef, m, m)
80+
intermediate_diagonal = Diagonal(V(undef, m))
8181
intermediate_1 = UpperTriangular(M(undef, 2*m, 2*m))
8282
intermediate_2 = LowerTriangular(M(undef, 2*m, 2*m))
8383
inverse_intermediate_1 = UpperTriangular(M(undef, 2*m, 2*m))
8484
inverse_intermediate_2 = LowerTriangular(M(undef, 2*m, 2*m))
8585
intermediary_vector = V(undef, 2*m)
8686
sol = V(undef, 2*m)
87-
intermediate_structure_updated = false
88-
return CompressedLBFGS{T,M,V}(m, n, k, α, Sₖ, Yₖ, Dₖ, Lₖ, chol_matrix, intermediate_1, intermediate_2, inverse_intermediate_1, inverse_intermediate_2, intermediary_vector, sol, intermediate_structure_updated)
87+
return CompressedLBFGS{T,M,V}(m, n, k, α, Sₖ, Yₖ, Dₖ, Lₖ, chol_matrix, intermediate_diagonal, intermediate_1, intermediate_2, inverse_intermediate_1, inverse_intermediate_2, intermediary_vector, sol)
8988
end
9089

9190
function Base.push!(op::CompressedLBFGS{T,M,V}, s::V, y::V) where {T,M,V<:AbstractVector{T}}
@@ -109,13 +108,16 @@ function Base.push!(op::CompressedLBFGS{T,M,V}, s::V, y::V) where {T,M,V<:Abstra
109108
# for the time being, reinstantiate completely the Lₖ matrix
110109
for j in 1:op.k
111110
for i in 1:j-1
112-
op.Lₖ.data[j, i] = dot(op.Sₖ[:, j], op.Yₖ[:, i])
111+
op.Lₖ.data[j, i] = dot(view(op.Sₖ,:, j), view(op.Yₖ, :, i))
113112
end
114113
end
115114
end
115+
116+
# step 4 and 6
117+
precompile_iterated_structure!(op)
118+
116119
# secant equation fails if uncommented
117120
# op.α = dot(y,s)/dot(s,s)
118-
op.intermediate_structure_updated = false
119121
return op
120122
end
121123

@@ -141,8 +143,18 @@ end
141143

142144
# Algorithm 3.2 (p15)
143145
# step 4, Jₖ is computed only if needed
144-
function inverse_cholesky(op::CompressedLBFGS)
145-
view(op.chol_matrix, 1:op.k, 1:op.k) .= op.α .* (transpose(view(op.Sₖ, :, 1:op.k)) * view(op.Sₖ, :, 1:op.k)) .+ view(op.Lₖ, 1:op.k, 1:op.k) * inv(Diagonal(op.Dₖ[1:op.k, 1:op.k])) * transpose(view(op.Lₖ, 1:op.k, 1:op.k))
146+
function inverse_cholesky(op::CompressedLBFGS{T,M,V}) where {T,M,V}
147+
view(op.intermediate_diagonal.diag, 1:op.k) .= inv.(view(op.Dₖ.diag, 1:op.k))
148+
149+
# view(op.Lₖ, 1:op.k, 1:op.k) * inv(Diagonal(op.Dₖ[1:op.k, 1:op.k])) * transpose(view(op.Lₖ, 1:op.k, 1:op.k))
150+
mul!(view(op.inverse_intermediate_1, 1:op.k, 1:op.k), view(op.intermediate_diagonal, 1:op.k, 1:op.k), transpose(view(op.Lₖ, 1:op.k, 1:op.k)))
151+
mul!(view(op.chol_matrix, 1:op.k, 1:op.k), view(op.Lₖ, 1:op.k, 1:op.k), view(op.inverse_intermediate_1, 1:op.k, 1:op.k))
152+
153+
# view(op.chol_matrix, 1:op.k, 1:op.k) .= op.α .* (transpose(view(op.Sₖ, :, 1:op.k)) * view(op.Sₖ, :, 1:op.k))
154+
mul!(view(op.chol_matrix, 1:op.k, 1:op.k), transpose(view(op.Sₖ, :, 1:op.k)), view(op.Sₖ, :, 1:op.k), op.α, (T)(1))
155+
156+
# view(op.chol_matrix, 1:op.k, 1:op.k) .= op.α .* (transpose(view(op.Sₖ, :, 1:op.k)) * view(op.Sₖ, :, 1:op.k)) .+ view(op.Lₖ, 1:op.k, 1:op.k) * inv(Diagonal(op.Dₖ[1:op.k, 1:op.k])) * transpose(view(op.Lₖ, 1:op.k, 1:op.k))
157+
146158
cholesky!(Symmetric(view(op.chol_matrix, 1:op.k, 1:op.k)))
147159
Jₖ = transpose(UpperTriangular(view(op.chol_matrix, 1:op.k, 1:op.k)))
148160
return Jₖ
@@ -152,29 +164,32 @@ end
152164
function precompile_iterated_structure!(op::CompressedLBFGS)
153165
Jₖ = inverse_cholesky(op)
154166

155-
view(op.intermediate_1, 1:op.k,1:op.k) .= .- view(op.Dₖ, 1:op.k, 1:op.k)^(1/2)
156-
view(op.intermediate_1, 1:op.k,op.k+1:2*op.k) .= view(op.Dₖ, 1:op.k, 1:op.k)^(-1/2) * transpose(view(op.Lₖ, 1:op.k, 1:op.k))
167+
# constant update
157168
view(op.intermediate_1, op.k+1:2*op.k, 1:op.k) .= 0
158-
view(op.intermediate_1, op.k+1:2*op.k, op.k+1:2*op.k) .= transpose(Jₖ)
159-
160-
view(op.intermediate_2, 1:op.k, 1:op.k) .= view(op.Dₖ, 1:op.k, 1:op.k)^(1/2)
161169
view(op.intermediate_2, 1:op.k, op.k+1:2*op.k) .= 0
162-
view(op.intermediate_2, op.k+1:2*op.k, 1:op.k) .= .- view(op.Lₖ, 1:op.k, 1:op.k) * view(op.Dₖ, 1:op.k, 1:op.k)^(-1/2)
170+
view(op.intermediate_1, op.k+1:2*op.k, op.k+1:2*op.k) .= transpose(Jₖ)
163171
view(op.intermediate_2, op.k+1:2*op.k, op.k+1:2*op.k) .= Jₖ
164172

173+
# updates related to D^(1/2)
174+
view(op.intermediate_diagonal.diag, 1:op.k) .= sqrt.(view(op.Dₖ.diag, 1:op.k))
175+
view(op.intermediate_1, 1:op.k,1:op.k) .= .- view(op.intermediate_diagonal, 1:op.k, 1:op.k)
176+
view(op.intermediate_2, 1:op.k, 1:op.k) .= view(op.intermediate_diagonal, 1:op.k, 1:op.k)
177+
178+
# updates related to D^(-1/2)
179+
view(op.intermediate_diagonal.diag, 1:op.k) .= (x -> 1/sqrt(x)).(view(op.Dₖ.diag, 1:op.k))
180+
mul!(view(op.intermediate_1, 1:op.k,op.k+1:2*op.k), view(op.intermediate_diagonal, 1:op.k, 1:op.k), transpose(view(op.Lₖ, 1:op.k, 1:op.k)))
181+
# view(op.intermediate_1, 1:op.k,op.k+1:2*op.k) .= view(op.Dₖ, 1:op.k, 1:op.k)^(-1/2) * transpose(view(op.Lₖ, 1:op.k, 1:op.k))
182+
mul!(view(op.intermediate_2, op.k+1:2*op.k, 1:op.k), view(op.Lₖ, 1:op.k, 1:op.k), view(op.intermediate_diagonal, 1:op.k, 1:op.k))
183+
view(op.intermediate_2, op.k+1:2*op.k, 1:op.k) .= view(op.intermediate_2, op.k+1:2*op.k, 1:op.k) .* -1
184+
# view(op.intermediate_2, op.k+1:2*op.k, 1:op.k) .= .- view(op.Lₖ, 1:op.k, 1:op.k) * view(op.Dₖ, 1:op.k, 1:op.k)^(-1/2)
185+
165186
view(op.inverse_intermediate_1, 1:2*op.k, 1:2*op.k) .= inv(op.intermediate_1[1:2*op.k, 1:2*op.k])
166187
view(op.inverse_intermediate_2, 1:2*op.k, 1:2*op.k) .= inv(op.intermediate_2[1:2*op.k, 1:2*op.k])
167-
168-
op.intermediate_structure_updated = true
169188
end
170189

171190
# Algorithm 3.2 (p15)
172191
function LinearAlgebra.mul!(Bv::V, op::CompressedLBFGS{T,M,V}, v::V) where {T,M,V<:AbstractVector{T}}
173-
# step 1-3 mainly done by Base.push!
174-
175-
# steps 4 and 6, in case the intermediary structures required are not up to date
176-
(!op.intermediate_structure_updated) && (precompile_iterated_structure!(op))
177-
192+
# step 1-4 and 6 mainly done by Base.push!
178193
# step 5
179194
mul!(view(op.sol, 1:op.k), transpose(view(op.Yₖ, :, 1:op.k)), v)
180195
mul!(view(op.sol, op.k+1:2*op.k), transpose(view(op.Sₖ, :, 1:op.k)), v)

test/test_clbfgs.jl

+6-4
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,15 @@
33
n=100
44
n=5
55
lbfgs = CompressedLBFGS(n) # m=5
6-
Bv = rand(n)
6+
V = LinearOperators.default_vector_type(LinearOperators.default_gpu())
7+
Bv = V(rand(n))
8+
s = V(rand(n))
9+
mul!(Bv, lbfgs, s) # warm-up
710
for i in 1:iter
8-
s = rand(n)
9-
y = rand(n)
11+
s = V(rand(n))
12+
y = V(rand(n))
1013
push!(lbfgs, s, y)
1114
# warmp-up computing the mandatory intermediate structures
12-
mul!(Bv, lbfgs, s)
1315
allocs = @allocated mul!(Bv, lbfgs, s)
1416
@test allocs == 0
1517
@test Bv y

0 commit comments

Comments
 (0)