Skip to content

Commit 921ca83

Browse files
committed
Introduce two safeguards for WolfePowell
1 parent 0f15e0c commit 921ca83

2 files changed

Lines changed: 57 additions & 33 deletions

File tree

Changelog.md

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,12 @@ The file was started with Version `0.4`.
66
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
77
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
88

9+
## [Unreleased]
10+
11+
### Added
12+
13+
* In `WolfePowellLinesearchStepsize`, two new keyword arguments `stop_increasing_at_step=` and `stop_decreasing_at_step=` were added to limit the number of increase/decrease steps in the initial bracketing phase for s_plus and s_minus, respectively. (resolves (#495))
14+
915
## [0.5.26] November 5, 2025
1016

1117
### Added
@@ -235,7 +241,7 @@ present; they were changed to `retact_fused!`.
235241
* add a `PreconditionedDirection` variant to the `direction` gradient processor
236242
keyword argument and its corresponding `PreconditionedDirectionRule`
237243
* make the preconditioner available in quasi Newton.
238-
* in `gradient_descent` and `conjugate_gradient_descent` the rule can be added anyways.
244+
* in `gradient_descent` and `conjugate_gradient_descent` the rule can be added anyway.
239245

240246
### Fixed
241247

@@ -370,7 +376,7 @@ In general this introduces a few factories, that avoid having to pass the manifo
370376
* index for equality constraints is unified to `j` running from `1,...,n`
371377
* iterations are using now `k`
372378
* `get_manopt_parameter` has been renamed to `get_parameter` since it is internal,
373-
so internally that is clear; accessing it from outside hence reads anyways `Manopt.get_parameter`
379+
so internally that is clear; accessing it from outside hence reads anyway `Manopt.get_parameter`
374380
* `set_manopt_parameter!` has been renamed to `set_parameter!` since it is internal,
375381
so internally that is clear; accessing it from outside hence reads `Manopt.set_parameter!`
376382
* changed the `stabilize::Bool=` keyword in `quasi_Newton` to the more flexible `project!=`

src/plans/stepsize.jl

Lines changed: 49 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -902,7 +902,7 @@ stepsize ``t`` from ``p`` in direction ``η``.
902902
* `cbls:::CubicBracketingLinesearchStepsize`: containing `retraction_method`, `vector_transport` and the temporary `candidate_point` and `candidate_direction`
903903
* `p`: point in the manifold of `mp`
904904
* `η`: search direction at `p`
905-
* `t::Real`: step size
905+
* `t::Real`: step size
906906
"""
907907
function get_univariate_triple!(mp::AbstractManoptProblem, cbls::CubicBracketingLinesearchStepsize, p, η, t)
908908
M = get_manifold(mp)
@@ -1849,21 +1849,25 @@ $(_var(:Keyword, :X; add = "as type of memory allocated for the candidates direc
18491849
* `max_stepsize=`[`max_stepsize`](@ref)`(M, p)`: largest stepsize allowed here.
18501850
$(_var(:Keyword, :retraction_method))
18511851
* `stop_when_stepsize_less=0.0`: smallest stepsize when to stop (the last one before is taken)
1852+
* `stop_increasing_at_step=100`: for the initial increase test (s_plus), stop after these many steps
1853+
* `stop_decreasing_at_step=1000`: for the initial decrease test (s_minus), stop after these many steps
18521854
$(_var(:Keyword, :vector_transport_method))
18531855
"""
18541856
mutable struct WolfePowellLinesearchStepsize{
1855-
R <: Real, TRM <: AbstractRetractionMethod, VTM <: AbstractVectorTransportMethod, P, T,
1857+
R, TRM <: AbstractRetractionMethod, VTM <: AbstractVectorTransportMethod, P, T, I,
18561858
} <: Linesearch
18571859
sufficient_decrease::R
18581860
sufficient_curvature::R
18591861
candidate_direction::T
18601862
candidate_point::P
18611863
last_stepsize::R
18621864
max_stepsize::R
1865+
message::String
18631866
retraction_method::TRM
18641867
stop_when_stepsize_less::R
18651868
vector_transport_method::VTM
1866-
1869+
stop_increasing_at_step::Int
1870+
stop_decreasing_at_step::Int
18671871
function WolfePowellLinesearchStepsize(
18681872
M::AbstractManifold;
18691873
p::P = allocate_result(M, rand),
@@ -1874,28 +1878,35 @@ mutable struct WolfePowellLinesearchStepsize{
18741878
sufficient_curvature::R = 0.999,
18751879
vector_transport_method::VTM = default_vector_transport_method(M),
18761880
stop_when_stepsize_less::R = 0.0,
1877-
) where {TRM, VTM, P, T, R}
1878-
return new{R, TRM, VTM, P, T}(
1881+
stop_increasing_at_step::I = 100,
1882+
stop_decreasing_at_step::I = 1000,
1883+
) where {TRM, VTM, P, T, R, I}
1884+
return new{R, TRM, VTM, P, T, I}(
18791885
sufficient_decrease,
18801886
sufficient_curvature,
18811887
X,
18821888
p,
18831889
0.0,
18841890
max_stepsize,
1891+
"", # init empty message
18851892
retraction_method,
18861893
stop_when_stepsize_less,
18871894
vector_transport_method,
1895+
stop_increasing_at_step,
1896+
stop_decreasing_at_step,
18881897
)
18891898
end
18901899
end
18911900
function (a::WolfePowellLinesearchStepsize)(
18921901
mp::AbstractManoptProblem,
18931902
ams::AbstractManoptSolverState,
1894-
::Int,
1903+
k::Int,
18951904
η = (-get_gradient(mp, get_iterate(ams)));
18961905
kwargs...,
18971906
)
18981907
# For readability extract a few variables
1908+
# maybe collect and warn
1909+
a.message = ""
18991910
M = get_manifold(mp)
19001911
p = get_iterate(ams)
19011912
l = get_differential(mp, p, η)
@@ -1916,39 +1927,42 @@ function (a::WolfePowellLinesearchStepsize)(
19161927
# Temp tangent vector
19171928
Y = zero_vector(M, a.candidate_point)
19181929
if fNew > f0 + a.sufficient_decrease * step * l
1930+
i = 0
19191931
while (fNew > f0 + a.sufficient_decrease * step * l) && (s_minus > 10^(-9)) # decrease
19201932
s_minus = s_minus * 0.5
19211933
step = s_minus
1922-
ManifoldsBase.retract_fused!(
1923-
M, a.candidate_point, p, η, step, a.retraction_method
1924-
)
1934+
ManifoldsBase.retract_fused!(M, a.candidate_point, p, η, step, a.retraction_method)
19251935
fNew = get_cost(mp, a.candidate_point)
1936+
i += 1
1937+
if i == a.stop_decreasing_at_step
1938+
a.message = (length(a.message) > 0) ? a.message * "\n " : "Wolfe-Powell line search:\n"
1939+
a.message = "$(a.message) Max decrease steps for s_minus ($(a.stop_decreasing_at_step)) reached in iteration $k."
1940+
break
1941+
end
19261942
end
19271943
s_plus = 2.0 * s_minus
19281944
else
1929-
vector_transport_to!(
1930-
M, a.candidate_direction, p, η, a.candidate_point, a.vector_transport_method
1931-
)
1932-
if get_differential(mp, a.candidate_point, a.candidate_direction; Y = Y) <
1933-
a.sufficient_curvature * l
1934-
while fNew <= f0 + a.sufficient_decrease * step * l &&
1935-
(s_plus < max_step_increase) # increase
1945+
vector_transport_to!(M, a.candidate_direction, p, η, a.candidate_point, a.vector_transport_method)
1946+
if get_differential(mp, a.candidate_point, a.candidate_direction; Y = Y) < a.sufficient_curvature * l
1947+
i = 0
1948+
while fNew <= f0 + a.sufficient_decrease * step * l && (s_plus < max_step_increase)
1949+
# increase
19361950
s_plus = s_plus * 2.0
19371951
step = s_plus
1938-
ManifoldsBase.retract_fused!(
1939-
M, a.candidate_point, p, η, step, a.retraction_method
1940-
)
1952+
ManifoldsBase.retract_fused!(M, a.candidate_point, p, η, step, a.retraction_method)
19411953
fNew = get_cost(mp, a.candidate_point)
1954+
if i == a.stop_increasing_at_step
1955+
a.message = (length(a.message) > 0) ? a.message * "\n " : "Wolfe-Powell line search:\n"
1956+
a.message = "$(a.message) Max increase steps for s_plus ($(a.stop_increasing_at_step)) reached in iteration $k."
1957+
break
1958+
end
19421959
end
19431960
s_minus = s_plus / 2.0
19441961
end
19451962
end
19461963
ManifoldsBase.retract_fused!(M, a.candidate_point, p, η, s_minus, a.retraction_method)
1947-
vector_transport_to!(
1948-
M, a.candidate_direction, p, η, a.candidate_point, a.vector_transport_method
1949-
)
1950-
while get_differential(mp, a.candidate_point, a.candidate_direction; Y = Y) <
1951-
a.sufficient_curvature * l
1964+
vector_transport_to!(M, a.candidate_direction, p, η, a.candidate_point, a.vector_transport_method)
1965+
while get_differential(mp, a.candidate_point, a.candidate_direction; Y = Y) < a.sufficient_curvature * l
19521966
step = (s_minus + s_plus) / 2
19531967
ManifoldsBase.retract_fused!(M, a.candidate_point, p, η, step, a.retraction_method)
19541968
fNew = get_cost(mp, a.candidate_point)
@@ -1957,13 +1971,13 @@ function (a::WolfePowellLinesearchStepsize)(
19571971
else
19581972
s_plus = step
19591973
end
1960-
abs(s_plus - s_minus) <= a.stop_when_stepsize_less && break
1961-
ManifoldsBase.retract_fused!(
1962-
M, a.candidate_point, p, η, s_minus, a.retraction_method
1963-
)
1964-
vector_transport_to!(
1965-
M, a.candidate_direction, p, η, a.candidate_point, a.vector_transport_method
1966-
)
1974+
if abs(s_plus - s_minus) <= a.stop_when_stepsize_less
1975+
a.message = (length(a.message) > 0) ? a.message * "\n " : "Wolfe-Powell line search:\n"
1976+
a.message = "$(a.message) Minimum step size ($(a.stop_when_stepsize_less)) exceeded in iteration $k."
1977+
break
1978+
end
1979+
ManifoldsBase.retract_fused!(M, a.candidate_point, p, η, s_minus, a.retraction_method)
1980+
vector_transport_to!(M, a.candidate_direction, p, η, a.candidate_point, a.vector_transport_method)
19671981
end
19681982
step = s_minus
19691983
a.last_stepsize = step
@@ -1979,6 +1993,8 @@ function show(io::IO, a::WolfePowellLinesearchStepsize)
19791993
retraction_method = $(a.retraction_method),
19801994
vector_transport_method = $(a.vector_transport_method),
19811995
stop_when_stepsize_less = $(a.stop_when_stepsize_less),
1996+
stop_increasing_at_step = $(a.stop_increasing_at_step),
1997+
stop_decreasing_at_step = $(a.stop_decreasing_at_step),
19821998
)""",
19831999
)
19842000
end
@@ -2016,6 +2032,8 @@ $(_var(:Keyword, :X; add = "as type of memory allocated for the candidates direc
20162032
* `max_stepsize=`[`max_stepsize`](@ref)`(M, p)`: largest stepsize allowed here.
20172033
$(_var(:Keyword, :retraction_method))
20182034
* `stop_when_stepsize_less=0.0`: smallest stepsize when to stop (the last one before is taken)
2035+
* `stop_increasing_at_step=100`: for the initial increase test (s_plus), stop after these many steps
2036+
* `stop_decreasing_at_step=1000`: for the initial decrease test (s_minus), stop after these many steps
20192037
$(_var(:Keyword, :vector_transport_method))
20202038
"""
20212039
function WolfePowellLinesearch(args...; kwargs...)

0 commit comments

Comments
 (0)