Skip to content

Commit acb4737

Browse files
committed
Support alpha scaling for LBroyden
1 parent cf04e3f commit acb4737

File tree

2 files changed

+32
-24
lines changed

2 files changed

+32
-24
lines changed

Project.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,7 +54,7 @@ NonlinearSolveSymbolicsExt = "Symbolics"
5454
NonlinearSolveZygoteExt = "Zygote"
5555

5656
[compat]
57-
ADTypes = "0.2.5"
57+
ADTypes = "0.2.6"
5858
Aqua = "0.8"
5959
ArrayInterface = "7.7"
6060
BandedMatrices = "1.4"

src/algorithms/lbroyden.jl

Lines changed: 31 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
"""
22
LimitedMemoryBroyden(; max_resets::Int = 3, linesearch = NoLineSearch(),
3-
threshold::Val = Val(10), reset_tolerance = nothing)
3+
threshold::Val = Val(10), reset_tolerance = nothing, alpha = nothing)
44
55
An implementation of `LimitedMemoryBroyden` [ziani2008autoadaptative](@cite) with resetting
66
and line search.
@@ -12,54 +12,61 @@ and line search.
1212
`sqrt(eps(real(eltype(u))))`.
1313
- `threshold`: the number of vectors to store in the low rank approximation. Defaults
1414
to `Val(10)`.
15+
- `alpha`: The initial Jacobian inverse is set to be `(αI)⁻¹`. Defaults to `nothing`
16+
which implies `α = max(norm(u), 1) / (2 * norm(fu))`.
1517
"""
1618
function LimitedMemoryBroyden(; max_resets::Int = 3, linesearch = NoLineSearch(),
17-
threshold::Union{Val, Int} = Val(10), reset_tolerance = nothing)
19+
threshold::Union{Val, Int} = Val(10), reset_tolerance = nothing, alpha = nothing)
1820
threshold isa Int && (threshold = Val(threshold))
1921
return ApproximateJacobianSolveAlgorithm{false, :LimitedMemoryBroyden}(; linesearch,
2022
descent = NewtonDescent(), update_rule = GoodBroydenUpdateRule(), max_resets,
21-
initialization = BroydenLowRankInitialization{_unwrap_val(threshold)}(threshold),
22-
reinit_rule = NoChangeInStateReset(; reset_tolerance))
23+
initialization = BroydenLowRankInitialization{_unwrap_val(threshold)}(alpha,
24+
threshold), reinit_rule = NoChangeInStateReset(; reset_tolerance))
2325
end
2426

2527
"""
26-
BroydenLowRankInitialization{T}(threshold::Val{T})
28+
BroydenLowRankInitialization{T}(alpha, threshold::Val{T})
2729
2830
An initialization for `LimitedMemoryBroyden` that uses a low rank approximation of the
2931
Jacobian. The low rank updates to the Jacobian matrix corresponds to what SciPy calls
3032
["simple"](https://docs.scipy.org/doc/scipy/reference/generated/scipy.optimize.broyden2.html#scipy-optimize-broyden2).
3133
"""
32-
struct BroydenLowRankInitialization{T} <: AbstractJacobianInitialization
34+
@concrete struct BroydenLowRankInitialization{T} <: AbstractJacobianInitialization
35+
alpha
3336
threshold::Val{T}
3437
end
3538

3639
jacobian_initialized_preinverted(::BroydenLowRankInitialization) = true
3740

3841
function SciMLBase.init(prob::AbstractNonlinearProblem,
39-
alg::BroydenLowRankInitialization{T},
40-
solver, f::F, fu, u, p; maxiters = 1000, kwargs...) where {T, F}
42+
alg::BroydenLowRankInitialization{T}, solver, f::F, fu, u, p; maxiters = 1000,
43+
internalnorm::IN = DEFAULT_NORM, kwargs...) where {T, F, IN}
4144
if u isa Number # Use the standard broyden
4245
return init(prob, IdentityInitialization(true, FullStructure()), solver, f, fu, u,
4346
p; maxiters, kwargs...)
4447
end
4548
# Pay to cost of slightly more allocations to prevent type-instability for StaticArrays
49+
α = inv(__initial_alpha(alg.alpha, u, fu, internalnorm))
4650
if u isa StaticArray
47-
J = BroydenLowRankJacobian(fu, u; alg.threshold)
51+
J = BroydenLowRankJacobian(fu, u; alg.threshold, alpha = α)
4852
else
4953
threshold = min(_unwrap_val(alg.threshold), maxiters)
50-
J = BroydenLowRankJacobian(fu, u; threshold)
54+
J = BroydenLowRankJacobian(fu, u; threshold, alpha = α)
5155
end
52-
return InitializedApproximateJacobianCache(J, FullStructure(), alg, nothing, true, 0.0)
56+
return InitializedApproximateJacobianCache(J, FullStructure(), alg, nothing, true,
57+
internalnorm)
5358
end
5459

5560
function (cache::InitializedApproximateJacobianCache)(alg::BroydenLowRankInitialization, fu,
5661
u)
62+
α = __initial_alpha(alg.alpha, u, fu, cache.internalnorm)
5763
cache.J.idx = 0
64+
cache.J.alpha = inv(α)
5865
return
5966
end
6067

6168
"""
62-
BroydenLowRankJacobian{T}(U, Vᵀ, idx, cache)
69+
BroydenLowRankJacobian{T}(U, Vᵀ, idx, cache, alpha)
6370
6471
Low Rank Approximation of the Jacobian Matrix. Currently only used for
6572
[`LimitedMemoryBroyden`](@ref). This computes the Jacobian as ``U \\times V^T``.
@@ -69,6 +76,7 @@ Low Rank Approximation of the Jacobian Matrix. Currently only used for
6976
Vᵀ
7077
idx::Int
7178
cache
79+
alpha
7280
end
7381

7482
__safe_inv!!(workspace, op::BroydenLowRankJacobian) = op # Already Inverted form
@@ -88,61 +96,61 @@ for op in (:adjoint, :transpose)
8896
# FIXME: adjoint might be a problem here. Fix if a complex number issue shows up
8997
@eval function Base.$(op)(operator::BroydenLowRankJacobian{T}) where {T}
9098
return BroydenLowRankJacobian{T}(operator.Vᵀ, operator.U,
91-
operator.idx, operator.cache)
99+
operator.idx, operator.cache, operator.alpha)
92100
end
93101
end
94102

95103
# Storing the transpose to ensure contiguous memory on splicing
96104
function BroydenLowRankJacobian(fu::StaticArray{S2, T2}, u::StaticArray{S1, T1};
97-
threshold::Val{Th} = Val(10)) where {S1, S2, T1, T2, Th}
105+
alpha = true, threshold::Val{Th} = Val(10)) where {S1, S2, T1, T2, Th}
98106
T = promote_type(T1, T2)
99107
fuSize, uSize = Size(fu), Size(u)
100108
U = MArray{Tuple{prod(fuSize), Th}, T}(undef)
101109
Vᵀ = MArray{Tuple{prod(uSize), Th}, T}(undef)
102-
return BroydenLowRankJacobian{T}(U, Vᵀ, 0, nothing)
110+
return BroydenLowRankJacobian{T}(U, Vᵀ, 0, nothing, T(alpha))
103111
end
104112

105-
function BroydenLowRankJacobian(fu, u; threshold::Int = 10)
113+
function BroydenLowRankJacobian(fu, u; threshold::Int = 10, alpha = true)
106114
T = promote_type(eltype(u), eltype(fu))
107115
U = similar(fu, T, length(fu), threshold)
108116
Vᵀ = similar(u, T, length(u), threshold)
109117
cache = similar(u, T, threshold)
110-
return BroydenLowRankJacobian{T}(U, Vᵀ, 0, cache)
118+
return BroydenLowRankJacobian{T}(U, Vᵀ, 0, cache, T(alpha))
111119
end
112120

113121
function Base.:*(J::BroydenLowRankJacobian, x::AbstractVector)
114122
J.idx == 0 && return -x
115123
cache, U, Vᵀ = __get_components(J)
116-
return U * (Vᵀ * x) .- x
124+
return U * (Vᵀ * x) .- J.alpha .* x
117125
end
118126

119127
function LinearAlgebra.mul!(y::AbstractVector, J::BroydenLowRankJacobian, x::AbstractVector)
120128
if J.idx == 0
121-
@. y = -x
129+
@. y = -J.alpha * x
122130
return y
123131
end
124132
cache, U, Vᵀ = __get_components(J)
125133
@bb cache = Vᵀ × x
126134
mul!(y, U, cache)
127-
@bb @. y -= x
135+
@bb @. y -= J.alpha * x
128136
return y
129137
end
130138

131139
function Base.:*(x::AbstractVector, J::BroydenLowRankJacobian)
132140
J.idx == 0 && return -x
133141
cache, U, Vᵀ = __get_components(J)
134-
return Vᵀ' * (U' * x) .- x
142+
return Vᵀ' * (U' * x) .- J.alpha .* x
135143
end
136144

137145
function LinearAlgebra.mul!(y::AbstractVector, x::AbstractVector, J::BroydenLowRankJacobian)
138146
if J.idx == 0
139-
@. y = -x
147+
@. y = -J.alpha * x
140148
return y
141149
end
142150
cache, U, Vᵀ = __get_components(J)
143151
@bb cache = transpose(U) × x
144152
mul!(y, transpose(Vᵀ), cache)
145-
@bb @. y -= x
153+
@bb @. y -= J.alpha * x
146154
return y
147155
end
148156

0 commit comments

Comments
 (0)