Skip to content

Commit 99ef858

Browse files
committed
Calculate derivatives using Jacobian-vector products
1 parent 11d039f commit 99ef858

File tree

9 files changed

+40
-49
lines changed

9 files changed

+40
-49
lines changed

.github/workflows/ci.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -12,15 +12,15 @@ jobs:
1212
runs-on: ${{ matrix.os }}
1313
strategy:
1414
matrix:
15-
julia-version: ['min', 'lts', '1']
15+
# julia-version: ['min', 'lts', '1']
16+
julia-version: ['1']
1617
os: [ubuntu-latest, windows-latest, macOS-latest]
1718
steps:
1819
- uses: actions/checkout@v2
1920
- uses: julia-actions/setup-julia@v2
2021
with:
2122
version: ${{ matrix.julia-version }}
2223
- uses: julia-actions/cache@v2
23-
- uses: julia-actions/julia-buildpkg@v1
2424
- uses: julia-actions/julia-runtest@v1
2525
with:
2626
annotate: true

Project.toml

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
name = "LineSearches"
22
uuid = "d3d80556-e9d4-5f37-9878-2ab0fcc64255"
3-
version = "7.5.1"
3+
version = "7.6.0"
44

55
[deps]
66
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
@@ -14,9 +14,8 @@ DoubleFloats = "1"
1414
ExplicitImports = "1.14"
1515
JET = "0.9, 0.10"
1616
LinearAlgebra = "<0.0.1, 1"
17-
NLSolversBase = "7"
17+
NLSolversBase = "8"
1818
NaNMath = "1"
19-
Optim = "1"
2019
OptimTestProblems = "2"
2120
Printf = "<0.0.1, 1"
2221
Test = "<0.0.1, 1"
@@ -27,9 +26,11 @@ Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595"
2726
DoubleFloats = "497a8b3b-efae-58df-a0af-a86822472b78"
2827
ExplicitImports = "7d51a73a-1435-4ff3-83d9-f097790105c7"
2928
JET = "c3a54625-cd67-489e-a8e7-0a5a0ff4e31b"
30-
Optim = "429524aa-4258-5aef-a3af-852621145aeb"
3129
OptimTestProblems = "cec144fc-5a64-5bc6-99fb-dde8f63e154c"
3230
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"
3331

3432
[targets]
35-
test = ["Aqua", "ExplicitImports", "JET", "Test", "OptimTestProblems", "Optim", "DoubleFloats"]
33+
test = ["Aqua", "ExplicitImports", "JET", "Test", "OptimTestProblems", "DoubleFloats"]
34+
35+
[sources]
36+
NLSolversBase = { url = "https://github.com/devmotion/NLSolversBase.jl.git", rev = "dmw/jvp" }

src/LineSearches.jl

Lines changed: 9 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
module LineSearches
22

33
using Printf: @sprintf
4-
using LinearAlgebra: dot, norm
4+
using LinearAlgebra: norm
55
using NaNMath: NaNMath
66
using NLSolversBase: NLSolversBase, AbstractObjective
77

@@ -16,36 +16,32 @@ export InitialHagerZhang, InitialStatic, InitialPrevious,
1616
function make_ϕ(df, x_new, x, s)
1717
function ϕ(α)
1818
# Move a distance of alpha in the direction of s
19-
x_new .= x .+ α.*s
19+
x_new .= muladd.(α, s, x)
2020

2121
# Evaluate f(x+α*s)
22-
NLSolversBase.value!(df, x_new)
22+
return NLSolversBase.value!(df, x_new)
2323
end
2424
ϕ
2525
end
2626
function make_ϕdϕ(df, x_new, x, s)
2727
function ϕdϕ(α)
2828
# Move a distance of alpha in the direction of s
29-
x_new .= x .+ α.*s
30-
31-
# Evaluate ∇f(x+α*s)
32-
f_x_new, g_x_new = NLSolversBase.value_gradient!(df, x_new)
29+
x_new .= muladd.(α, s, x)
3330

3431
# Calculate ϕ(a_i), ϕ'(a_i)
35-
return f_x_new, real(dot(g_x_new, s))
32+
ϕ, dϕ = NLSolversBase.value_jvp!(df, x_new, s)
33+
34+
return ϕ, real(dϕ)
3635
end
3736
ϕdϕ
3837
end
3938
function make_ϕ_dϕ(df, x_new, x, s)
4039
function (α)
4140
# Move a distance of alpha in the direction of s
42-
x_new .= x .+ α.*s
43-
44-
# Evaluate ∇f(x+α*s)
45-
g_x_new = NLSolversBase.gradient!(df, x_new)
41+
x_new .= muladd.(α, s, x)
4642

4743
# Calculate ϕ'(a_i)
48-
return real(dot(g_x_new, s))
44+
return real(NLSolversBase.jvp!(df, x_new, s))
4945
end
5046
make_ϕ(df, x_new, x, s), dϕ
5147
end

src/initialguess.jl

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ is scaled with the `l_2` norm of the step direction.
1313
end
1414

1515
function (is::InitialStatic{T})(ls, state, phi_0, dphi_0, df) where T
16+
# phi_0 is (or should be) equal to NLSolversBase.value(df, state.x) and `state.f_x`
17+
@assert phi_0 == state.f_x
1618
PT = promote_type(T, real(eltype(state.s)))
1719
if is.scaled == true && (ns = real(norm(state.s))) > convert(PT, 0)
1820
# TODO: Type instability if there's a type mismatch between is.alpha and ns?
@@ -70,11 +72,13 @@ If αmax ≠ 1.0, then you should consider to ensure that snap2one[2] < αmax.
7072
end
7173

7274
function (is::InitialQuadratic{T})(ls, state, phi_0, dphi_0, df) where T
75+
# phi_0 is (or should be) equal to NLSolversBase.value(df, state.x) and `state.f_x`
76+
@assert phi_0 == state.f_x
7377
if !isfinite(state.f_x_previous) || isapprox(dphi_0, convert(T, 0), atol=eps(T)) # Need to add a tolerance
7478
# If we're at the first iteration
7579
αguess = is.α0
7680
else
77-
αguess = 2 * (phi_0 - state.f_x_previous) / dphi_0
81+
αguess = 2 * (state.f_x - state.f_x_previous) / dphi_0
7882
αguess = NaNMath.max(is.αmin, state.alpha*is.ρ, αguess)
7983
αguess = NaNMath.min(is.αmax, αguess)
8084
# if αguess ≈ 1, then make it 1 (Newton-type behaviour)
@@ -135,6 +139,8 @@ function InitialConstantChange(; αmin = 1e-12,
135139
end
136140

137141
function (is::InitialConstantChange{T})(ls, state, phi_0, dphi_0, df) where T
142+
# phi_0 is (or should be) equal to NLSolversBase.value(df, state.x) and `state.f_x`
143+
@assert phi_0 == state.f_x
138144
if !isfinite(is.dϕ_0_previous[]) || !isfinite(state.alpha) ||
139145
isapprox(dphi_0, convert(T, 0), atol=eps(T))
140146
# If we're at the first iteration
@@ -175,15 +181,19 @@ otherwise, we select according to procedure I1-2, with starting value α0.
175181
end
176182

177183
function (is::InitialHagerZhang)(ls::Tls, state, phi_0, dphi_0, df) where Tls
184+
# phi_0 is (or should be) equal to NLSolversBase.value(df, state.x) and `state.f_x`
185+
@assert phi_0 == state.f_x
178186
if isnan(state.f_x_previous) && isnan(is.α0)
179187
# If we're at the first iteration (f_x_previous is NaN)
180188
# and the user has not provided an initial step size (is.α0 is NaN),
181189
# then we
182190
# pick the initial step size according to HZ #I0
183-
# phi_0 is (or should be) equal to NLSolversBase.value(df, state.x)
184-
# TODO: Make the gradient available as part of the state?
185-
g_x = NLSolversBase.gradient!(df, state.x)
186-
state.alpha = _hzI0(state.x, g_x, phi_0,
191+
g_x = if hasproperty(state, :g_x)
192+
state.g_x
193+
else
194+
NLSolversBase.gradient!(df, state.x)
195+
end
196+
state.alpha = _hzI0(state.x, g_x, state.f_x,
187197
is.αmax,
188198
convert(eltype(state.x), is.ψ0)) # Hack to deal with type instability between is{T} and state.x
189199
if Tls <: HagerZhang

src/types.jl

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
mutable struct LineSearchException{T<:Real} <: Exception
1+
struct LineSearchException{T<:Real} <: Exception
22
message::AbstractString
33
alpha::T
44
end

test/arbitrary_precision.jl

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ doublefloatstypes = [Double64, Double32, Double16]
2222
@test dphi_0 isa T
2323

2424
function getstate()
25-
state = StateDummy(convert(T, 1), x, similar(x), convert(T, NaN), p)
25+
state = StateDummy(convert(T, 1), x, phi_0, grtmp, similar(x), convert(T, NaN), p)
2626
end
2727
# Test HagerZhang I0
2828
ls = HagerZhang{T}()
@@ -98,6 +98,7 @@ doublefloatstypes = [Double64, Double32, Double16]
9898
ls = HagerZhang{T}()
9999
state = getstate()
100100
state.f_x_previous = 2*phi_0
101+
state.x_ls = zeros(T, 2)
101102
is = InitialQuadratic{T}(snap2one=(convert(T, 0.9),convert(T, Inf)))
102103
is(ls, state, phi_0, dphi_0, df)
103104
@test !isnan(state.alpha)
@@ -107,6 +108,7 @@ doublefloatstypes = [Double64, Double32, Double16]
107108
ls = HagerZhang{T}()
108109
state = getstate()
109110
state.f_x_previous = 2*phi_0
111+
state.x_ls = zeros(T, 2)
110112
is = InitialQuadratic{T}(snap2one=(convert(T, 0.75),convert(T, Inf)))
111113
is(ls, state, phi_0, dphi_0, df)
112114
@test !isnan(state.alpha)

test/examples.jl

Lines changed: 0 additions & 20 deletions
This file was deleted.

test/initial.jl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,7 @@
1212
dphi_0 = dot(p, grtmp)
1313

1414
function getstate()
15-
state = StateDummy(1.0, x, similar(x), NaN, p)
15+
state = StateDummy(1.0, x, phi_0, grtmp, similar(x), NaN, p)
1616
end
1717
# Test HagerZhang I0
1818
ls = HagerZhang()
@@ -88,6 +88,7 @@
8888
ls = HagerZhang()
8989
state = getstate()
9090
state.f_x_previous = 2*phi_0
91+
state.x_ls = zeros(2)
9192
is = InitialQuadratic(snap2one=(0.9,Inf))
9293
is(ls, state, phi_0, dphi_0, df)
9394
@test state.alpha == 0.5

test/runtests.jl

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@ my_tests = [
1414
"initial.jl",
1515
"alphacalc.jl",
1616
"arbitrary_precision.jl",
17-
"examples.jl",
1817
"captured.jl",
1918
"issues.jl",
2019
"qa.jl",
@@ -23,6 +22,8 @@ my_tests = [
2322
mutable struct StateDummy
2423
alpha
2524
x
25+
f_x
26+
g_x
2627
x_ls
2728
f_x_previous
2829
s

0 commit comments

Comments
 (0)