diff --git a/Changelog.md b/Changelog.md index d0066bbe25..f149948a6c 100644 --- a/Changelog.md +++ b/Changelog.md @@ -5,11 +5,16 @@ All notable Changes to the Julia package `Manopt.jl` will be documented in this The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -## [0.5.2] – unreleased +## [0.5.2] – October 5, 2024 + +### Added + +* three new symbols to easier state to record the `:Gradient`, the `:GradientNorm`, and the `:Stepsize`. ### Changed -* fix a few typos in the docstrings. +* fix a few typos in the documentation +* improved the documentation for the initial guess of [`ArmijoLinesearchStepsize`](https://manoptjl.org/stable/plans/stepsize/#Manopt.ArmijoLinesearch). ## [0.5.1] – September 4, 2024 diff --git a/Project.toml b/Project.toml index 82e35c0d49..5bdec4109e 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Manopt" uuid = "0fc0a36d-df90-57f3-8f93-d78a9fc72bb5" authors = ["Ronny Bergmann "] -version = "0.5.1" +version = "0.5.2" [deps] ColorSchemes = "35d6a980-a343-548e-a6ea-1d62b119f2f4" diff --git a/src/plans/gradient_plan.jl b/src/plans/gradient_plan.jl index f31cc0665d..d065318b0c 100644 --- a/src/plans/gradient_plan.jl +++ b/src/plans/gradient_plan.jl @@ -711,7 +711,7 @@ mutable struct RecordGradient{T} <: RecordAction recorded_values::Array{T,1} RecordGradient{T}() where {T} = new(Array{T,1}()) end -RecordGradient(ξ::T) where {T} = RecordGradient{T}() +RecordGradient(::T) where {T} = RecordGradient{T}() function (r::RecordGradient{T})( ::AbstractManoptProblem, s::AbstractManoptSolverState, k::Int ) where {T} diff --git a/src/plans/record.jl b/src/plans/record.jl index 3dc91435cd..a8ccabdd04 100644 --- a/src/plans/record.jl +++ b/src/plans/record.jl @@ -897,10 +897,14 @@ create a [`RecordAction`](@ref) where * a [`RecordAction`](@ref) is passed through * a [`Symbol`] creates - * `:Change` to record the change of the iterates in `o.x`` + * `:Change` to record the change of the iterates, see [`RecordChange`](@ref) + * `:Gradient` to record the gradient, see [`RecordGradient`](@ref) + * `:GradientNorm to record the norm of the gradient, see [`RecordGradientNorm`](@ref) * `:Iterate` to record the iterate * `:Iteration` to record the current iteration number + * `IterativeTime` to record the time iteratively * `:Cost` to record the current cost function value + * `:Stepsize` to record the current step size * `:Time` to record the total time taken after every iteration * `:IterativeTime` to record the times taken for each iteration. @@ -912,9 +916,12 @@ RecordActionFactory(::AbstractManoptSolverState, sa::Pair{<:RecordAction,Symbol} function RecordActionFactory(s::AbstractManoptSolverState, symbol::Symbol) (symbol == :Change) && return RecordChange() (symbol == :Cost) && return RecordCost() + (symbol == :Gradient) && return RecordGradient(get_gradient(s)) + (symbol == :GradientNorm) && return RecordGradientNorm() (symbol == :Iterate) && return RecordIterate(get_iterate(s)) (symbol == :Iteration) && return RecordIteration() (symbol == :IterativeTime) && return RecordTime(; mode=:iterative) + (symbol == :Stepsize) && return RecordStepsize() (symbol == :Stop) && return RecordStoppingReason() (symbol == :Subsolver) && return RecordSubsolver() (symbol == :Time) && return RecordTime(; mode=:cumulative) diff --git a/src/plans/stepsize.jl b/src/plans/stepsize.jl index 55cddfcfc4..e2759011ca 100644 --- a/src/plans/stepsize.jl +++ b/src/plans/stepsize.jl @@ -34,6 +34,8 @@ default_stepsize(M::AbstractManifold, sT::Type{<:AbstractManoptSolverState}) Get the maximum stepsize (at point `p`) on manifold `M`. It should be used to limit the distance an algorithm is trying to move in a single step. + +By default, this returns [`injectivity_radius`](@extref `ManifoldsBase.injectivity_radius-Tuple{AbstractManifold}`)`(M)`. """ function max_stepsize(M::AbstractManifold, p) return max_stepsize(M) @@ -228,6 +230,27 @@ most prominently the negative gradient. """ abstract type Linesearch <: Stepsize end +""" + armijo_initial_guess(mp::AbstractManoptProblem, s::AbstractManoptSolverState, k, l) + +# Input + +* `mp`: the [`AbstractManoptProblem`](@ref) we are aiminig to minimize +* `s`: the [`AbstractManoptSolverState`](@ref) for the current solver +* `k`: the current iteration +* `l`: the last step size computed in the previous iteration. + +Return an initial guess for the [`ArmijoLinesearchStepsize`](@ref). + +The default provided is based on the [`max_stepsize`](@ref)`(M)`, which we denote by ``m``. +Let further ``X`` be the current descent direction with norm ``n=$(_tex(:norm, "X"; index="p"))`` its length. +Then this (default) initial guess returns + +* ``l`` if ``m`` is not finite +* ``$(_tex(:min))(l, $(_tex(:frac,"m","n")))`` otherwise + +This ensures that the initial guess does not yield to large (initial) steps. +""" function armijo_initial_guess( mp::AbstractManoptProblem, s::AbstractManoptSolverState, ::Int, l::Real ) @@ -247,23 +270,24 @@ based on the search direction `X` # Fields -* `candidate_point`: to store an interim result -* `initial_stepsize`: and initial step size +* `candidate_point`: to store an interim result +* `initial_stepsize`: and initial step size $(_var(:Keyword, :retraction_method)) -* `contraction_factor`: exponent for line search reduction -* `sufficient_decrease`: gain within Armijo's rule -* `last_stepsize`: the last step size to start the search with -* `initial_guess`: based on a [`AbstractManoptProblem`](@ref) `p`, - [`AbstractManoptSolverState`](@ref) `s` and a current iterate `i` and a last step size `l`, - this returns an initial guess. The default uses the last obtained stepsize -* `additional_decrease_condition`: (`(M,p) -> true`) specify a condition a new point has to additionally +* `contraction_factor`: exponent for line search reduction +* `sufficient_decrease`: gain within Armijo's rule +* `last_stepsize`: the last step size to start the search with +* `initial_guess`: a function to provide an initial guess for the step size, + it maps `(m,p,k,l) -> α` based on a [`AbstractManoptProblem`](@ref) `p`, + [`AbstractManoptSolverState`](@ref) `s`, the current iterate `k` and a last step size `l`. + It returns the initial guess `α`. +* `additional_decrease_condition`: specify a condition a new point has to additionally fulfill. The default accepts all points. -* `additional_increase_condition`: (`(M,p) -> true`) specify a condtion that additionally to +* `additional_increase_condition`: specify a condtion that additionally to checking a valid increase has to be fulfilled. The default accepts all points. -* `stop_when_stepsize_less`: smallest stepsize when to stop (the last one before is taken) -* `stop_when_stepsize_exceeds`: largest stepsize when to stop. -* `stop_increasing_at_step`: last step to increase the stepsize (phase 1), -* `stop_decreasing_at_step`: last step size to decrease the stepsize (phase 2), +* `stop_when_stepsize_less`: smallest stepsize when to stop (the last one before is taken) +* `stop_when_stepsize_exceeds`: largest stepsize when to stop. +* `stop_increasing_at_step`: last step to increase the stepsize (phase 1), +* `stop_decreasing_at_step`: last step size to decrease the stepsize (phase 2), Pass `:Messages` to a `debug=` to see `@info`s when these happen. @@ -281,7 +305,7 @@ $(_var(:Keyword, :retraction_method)) * `contraction_factor=0.95` * `sufficient_decrease=0.1` * `last_stepsize=initialstepsize` -* `initial_guess=(p,s,i,l) -> l` +* `initial_guess=[`armijo_initial_guess`](@ref) – (p,s,i,l) -> l` * `stop_when_stepsize_less=0.0` * `stop_when_stepsize_exceeds` * `stop_increasing_at_step=100` @@ -434,7 +458,11 @@ Overall, we look for step size, that provides _enough decrease_, see speciy a point to be used as memory for the candidate points. * `contraction_factor=0.95`: how to update ``s`` in the decrease step * `initial_stepsize=1.0``: specify an initial step size -* `initial_guess=armijo_initial_guess`: instead of the initial step, start with this guess. +* `initial_guess=`[`armijo_initial_guess`](@ref): Compute the initial step size of + a line search based on this function. + The funtion required is `(p,s,k,l) -> α` and computes the initial step size ``α`` + based on a [`AbstractManoptProblem`](@ref) `p`, [`AbstractManoptSolverState`](@ref) `s`, + the current iterate `k` and a last step size `l`. $(_var(:Keyword, :retraction_method)) * `stop_when_stepsize_less=0.0`: a safeguard, stop when the decreasing step is below this (nonnegative) bound. * `stop_when_stepsize_exceeds=max_stepsize(M)`: a safeguard to not choose a too long step size when initially increasing