From 81b73283fde430051dc269c27609dd0214a3acac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Beno=C3=AEt=20Legat?= Date: Mon, 16 Oct 2023 11:28:05 +0200 Subject: [PATCH 1/3] Add jacobian_tangent_basis to docstring (#304) * Add jacobian_tangent_basis to docstring * Update src/solvers/LevenbergMarquardt.jl --------- Co-authored-by: Mateusz Baran --- src/solvers/LevenbergMarquardt.jl | 1 + 1 file changed, 1 insertion(+) diff --git a/src/solvers/LevenbergMarquardt.jl b/src/solvers/LevenbergMarquardt.jl index 6457daa61d..56674b5b2f 100644 --- a/src/solvers/LevenbergMarquardt.jl +++ b/src/solvers/LevenbergMarquardt.jl @@ -40,6 +40,7 @@ then the keyword `jacobian_tangent_basis` below is ignored * `β` – parameter by which the damping term is multiplied when the current new point is rejected * `initial_residual_values` – the initial residual vector of the cost function `f`. * `initial_jacobian_f` – the initial Jacobian of the cost function `f`. +* `jacobian_tangent_basis` - [`AbstractBasis`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/bases/#ManifoldsBase.AbstractBasis) specify the basis of the tangent space for `jacobian_f`. All other keyword arguments are passed to [`decorate_state!`](@ref) for decorators or [`decorate_objective!`](@ref), respectively. From d6acf643e41a399f4a4e678eeceda2a8a54156cd Mon Sep 17 00:00:00 2001 From: Johannes Terblanche <6612981+Affie@users.noreply.github.com> Date: Tue, 17 Oct 2023 15:04:15 +0200 Subject: [PATCH 2/3] Only compute Jacobian in RLM if last step success (#303) Co-authored-by: Johannes Terblanche --- src/plans/nonlinear_least_squares_plan.jl | 2 ++ src/solvers/LevenbergMarquardt.jl | 7 ++++++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/src/plans/nonlinear_least_squares_plan.jl b/src/plans/nonlinear_least_squares_plan.jl index d517ffa058..9d8a3bcbf5 100644 --- a/src/plans/nonlinear_least_squares_plan.jl +++ b/src/plans/nonlinear_least_squares_plan.jl @@ -192,6 +192,7 @@ mutable struct LevenbergMarquardtState{ damping_term_min::Tparams β::Tparams expect_zero_residual::Bool + last_step_successful::Bool function LevenbergMarquardtState( M::AbstractManifold, p::P, @@ -244,6 +245,7 @@ mutable struct LevenbergMarquardtState{ damping_term_min, β, expect_zero_residual, + true, ) end end diff --git a/src/solvers/LevenbergMarquardt.jl b/src/solvers/LevenbergMarquardt.jl index 56674b5b2f..e89fd37d35 100644 --- a/src/solvers/LevenbergMarquardt.jl +++ b/src/solvers/LevenbergMarquardt.jl @@ -264,7 +264,10 @@ function step_solver!( M = get_manifold(dmp) nlso = get_objective(dmp) basis_ox = _maybe_get_basis(M, lms.p, nlso.jacobian_tangent_basis) - get_jacobian!(dmp, lms.jacF, lms.p, basis_ox) + # a new Jacobian is only needed if the last step was successful + if lms.last_step_successful + get_jacobian!(dmp, lms.jacF, lms.p, basis_ox) + end λk = lms.damping_term * norm(lms.residual_values)^2 JJ = transpose(lms.jacF) * lms.jacF + λk * I @@ -292,8 +295,10 @@ function step_solver!( if lms.expect_zero_residual lms.damping_term = max(lms.damping_term_min, lms.damping_term / lms.β) end + lms.last_step_successful = true else lms.damping_term *= lms.β + lms.last_step_successful = false end return lms end From 7563b32d8c44689a82d9b2ac38663859259c0de1 Mon Sep 17 00:00:00 2001 From: Ronny Bergmann Date: Wed, 25 Oct 2023 10:19:03 +0200 Subject: [PATCH 3/3] Bump dependencies. (#309) * Bump dependencies. * Move ARC CG subsolver to the main module * Update Changelog.md * Add a workflow that makes sure we do not forget to add an entry to Changelog.md on PRs. * Apply suggestions from code review * Add Changelog to docs. --------- Co-authored-by: Mateusz Baran --- .github/workflows/changelog.yml | 12 ++ .gitignore | 1 + Changelog.md | 64 +++++++++- Project.toml | 6 +- docs/Project.toml | 6 +- docs/make.jl | 109 +++++++++++------- docs/src/notation.md | 4 +- docs/src/solvers/ChambollePock.md | 2 +- ext/ManoptManifoldsExt/ARC_CG.jl | 46 -------- ext/ManoptManifoldsExt/ManoptManifoldsExt.jl | 1 - src/Manopt.jl | 3 + src/data/artificialDataFunctions.jl | 26 ++--- .../adaptive_regularization_with_cubics.jl | 46 ++++++++ ...est_adaptive_regularization_with_cubics.jl | 2 +- tutorials/HowToRecord.qmd | 2 +- tutorials/Project.toml | 4 +- 16 files changed, 217 insertions(+), 117 deletions(-) create mode 100644 .github/workflows/changelog.yml delete mode 100644 ext/ManoptManifoldsExt/ARC_CG.jl diff --git a/.github/workflows/changelog.yml b/.github/workflows/changelog.yml new file mode 100644 index 0000000000..6c0643cd10 --- /dev/null +++ b/.github/workflows/changelog.yml @@ -0,0 +1,12 @@ +name: Check Changelog +on: + pull_request: + +jobs: + Check-Changelog: + name: Check Changelog Action + runs-on: ubuntu-latest + steps: + - uses: tarides/changelog-check-action@v2 + with: + changelog: Changelog.md \ No newline at end of file diff --git a/.gitignore b/.gitignore index 8b385cdeb9..af0a1746a2 100644 --- a/.gitignore +++ b/.gitignore @@ -20,3 +20,4 @@ docs/src/tutorials/*.md docs/.CondaPkg docs/src/tutorials/Optimize!_files docs/src/tutorials/*.html +docs/src/changelog.md diff --git a/Changelog.md b/Changelog.md index 4a6efb6a97..b38d775d0d 100644 --- a/Changelog.md +++ b/Changelog.md @@ -5,6 +5,68 @@ 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.4.40] – 24/10/2023 + +### Added + +* add a `--help` argument to `docs/make.jl` to document all availabel command line arguments +* add a `--exclude-tutorials` argument to `docs/make.jl`. This way, when quarto is not available + on a computer, the docs can still be build with the tutorials not being added to the menu + such that documenter does not expect them to exist. + +### Changes + +* Bump dependencies to `ManifoldsBase.jl` 0.15 and `Manifolds.jl` 0.9 +* move the ARC CG subsolver to the main package, since `TangentSpace` is now already + available from `ManifoldsBase`. + +## [0.4.39] – 09/10/2023 + +### Changes + +* also use the pair of a retraction and the inverse retraction (see last update) + to perform the relaxation within the Douglas-Rachford algorithm. + +## [0.4.38] – 08/10/2023 + +### Changes + +* avoid allocations when calling `get_jacobian!` within the Levenberg-Marquard Algorithm. + +### Fixed + +* Fix a lot of typos in the documentation + +## [0.4.37] – 28/09/2023 + +### Changes + +* add more of the Riemannian Levenberg-Marquard algorithms parameters as keywords, so they + can be changed on call +* generalize the internal reflection of Douglas-Rachford, such that is also works with an + arbitrary pair of a reflection and an inverse reflection. + +## [0.4.36] – 20/09/2023 + +### Fixed + +* Fixed a bug that caused non-matrix points and vectors to fail when working with approcimate + +## [0.4.35] – 14/09/2023 + +### Added + +* The access to functions of the objective is now unified and encapsulated in proper `get_` + functions. + +## [0.4.34] – 02/09/2023 + +### Added + +* an `ManifoldEuclideanGradientObjetive` to allow the cost, gradient, and Hessian and other + first or second derivative based elements to be Euclidean and converted when needed. +* a keyword `objective_type=:Euclidean` for all solvers, that specifies that an Objective shall be created of the above type + ## [0.4.33] - 24/08/2023 ### Added @@ -282,7 +344,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 * a `max_stepsize` per manifold to avoid leaving the injectivity radius, which it also defaults to -## {0.4.0] - 10/01/2023 +## [0.4.0] - 10/01/2023 ### Added diff --git a/Project.toml b/Project.toml index 23c18bd6c2..bcf76e6578 100644 --- a/Project.toml +++ b/Project.toml @@ -1,7 +1,7 @@ name = "Manopt" uuid = "0fc0a36d-df90-57f3-8f93-d78a9fc72bb5" authors = ["Ronny Bergmann "] -version = "0.4.39" +version = "0.4.40" [deps] ColorSchemes = "35d6a980-a343-548e-a6ea-1d62b119f2f4" @@ -40,8 +40,8 @@ Colors = "0.11.2, 0.12" DataStructures = "0.17, 0.18" LRUCache = "1.4" ManifoldDiff = "0.2, 0.3.3" -Manifolds = "0.8.75" -ManifoldsBase = "0.14.10" +Manifolds = "0.9" +ManifoldsBase = "0.15" PolynomialRoots = "1" Requires = "0.5, 1" julia = "1.6" diff --git a/docs/Project.toml b/docs/Project.toml index 4c1e7b5828..0152f6625a 100644 --- a/docs/Project.toml +++ b/docs/Project.toml @@ -22,6 +22,6 @@ Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c" [compat] BenchmarkTools = "1.3" CondaPkg = "0.2" -Documenter = "0.27" -Manifolds = "0.8.75" -ManifoldsBase = "0.13, 0.14" +Documenter = "1" +Manifolds = "0.8.81, 0.9" +ManifoldsBase = "0.14.12, 0.15" diff --git a/docs/make.jl b/docs/make.jl index f36f320db0..430d51e03c 100755 --- a/docs/make.jl +++ b/docs/make.jl @@ -2,6 +2,28 @@ # # +if "--help" ∈ ARGS + println( + """ +docs/make.jl + +Render the `Manopt.jl` documenation with optinal arguments + +Arguments +* `--exclude-docs` - exclude the tutorials from the menu of Documenter, + this can be used if you do not have Quarto installed to still be able to render the docs + locally on this machine. This option should not be set on CI. +* `--help` - print this help and exit without rendering the documentation +* `--quarto` – run the Quarto notebooks from the `tutorials/` folder before generating the documentation + this has to be run locally at least once for the `tutorials/*.md` files to exist that are included in + the documentation (see `--exclude-tutorials`) for the alternative. + If they are generated ones they are cached accordingly. + Then you can spare time in the rendering by not passing this argument. +""", + ) + exit(0) +end + # # (a) if docs is not the current active environment, switch to it # (from https://github.com/JuliaIO/HDF5.jl/pull/1020/)  @@ -29,6 +51,17 @@ if "--quarto" ∈ ARGS end end +tutorials_in_menu = true +if "--exclude-tutorials" ∈ ARGS + @warn """ + You are excluding the tutorials from the Menu, + which might be done if you can not render them locally. + + Remember that this should never be done on CI for the full documentation. + """ + tutorials_in_menu = false +end + # (c) load necessary packages for the docs using Documenter using DocumenterCitations @@ -38,26 +71,42 @@ using LineSearches, LRUCache, Manopt, Manifolds, Plots generated_path = joinpath(@__DIR__, "src") base_url = "https://github.com/JuliaManifolds/Manopt.jl/blob/master/" isdir(generated_path) || mkdir(generated_path) -open(joinpath(generated_path, "contributing.md"), "w") do io - # Point to source license file - println( - io, - """ - ```@meta - EditURL = "$(base_url)CONTRIBUTING.md" - ``` - """, - ) - # Write the contents out below the meta block - for line in eachline(joinpath(dirname(@__DIR__), "CONTRIBUTING.md")) - println(io, line) +for (md_file, doc_file) in + [("CONTRIBUTING.md", "contributing.md"), ("Changelog.md", "changelog.md")] + open(joinpath(generated_path, doc_file), "w") do io + # Point to source license file + println( + io, + """ + ```@meta + EditURL = "$(base_url)$(md_file)" + ``` + """, + ) + # Write the contents out below the meta block + for line in eachline(joinpath(dirname(@__DIR__), md_file)) + println(io, line) + end end end +## Build titorials menu +tutorials_menu = + "How to..." => [ + "Get started: Optimize!" => "tutorials/Optimize!.md", + "Speedup using Inplace computations" => "tutorials/InplaceGradient.md", + "Use Automatic Differentiation" => "tutorials/AutomaticDifferentiation.md", + "Define Objectives in the Embedding" => "tutorials/EmbeddingObjectives.md", + "Count and use a Cache" => "tutorials/CountAndCache.md", + "Print Debug Output" => "tutorials/HowToDebug.md", + "Record values" => "tutorials/HowToRecord.md", + "Implement a Solver" => "tutorials/ImplementASolver.md", + "Do Constrained Optimization" => "tutorials/ConstrainedOptimization.md", + "Do Geodesic Regression" => "tutorials/GeodesicRegression.md", + ] # (e) ...finally! make docs bib = CitationBibliography(joinpath(@__DIR__, "src", "references.bib"); style=:alpha) -makedocs( - bib; +makedocs(; format=Documenter.HTML(; mathengine=MathJax3(), prettyurls=get(ENV, "CI", nothing) == "true" ), @@ -86,36 +135,10 @@ makedocs( ], authors="Ronny Bergmann and contributors.", sitename="Manopt.jl", - strict=[ - :doctest, - :linkcheck, - :parse_error, - :example_block, - :autodocs_block, - :cross_references, - :docs_block, - :eval_block, - :example_block, - :footnote, - :meta_block, - :missing_docs, - :setup_block, - ], pages=[ "Home" => "index.md", "About" => "about.md", - "How to..." => [ - "Get started: Optimize!" => "tutorials/Optimize!.md", - "Speedup using Inplace computations" => "tutorials/InplaceGradient.md", - "Use Automatic Differentiation" => "tutorials/AutomaticDifferentiation.md", - "Define Objectives in the Embedding" => "tutorials/EmbeddingObjectives.md", - "Count and use a Cache" => "tutorials/CountAndCache.md", - "Print Debug Output" => "tutorials/HowToDebug.md", - "Record values" => "tutorials/HowToRecord.md", - "Implement a Solver" => "tutorials/ImplementASolver.md", - "Do Constrained Optimization" => "tutorials/ConstrainedOptimization.md", - "Do Geodesic Regression" => "tutorials/GeodesicRegression.md", - ], + (tutorials_in_menu ? [tutorials_menu] : [])..., "Solvers" => [ "Introduction" => "solvers/index.md", "Adaptive Regularization with Cubics" => "solvers/adaptive-regularization-with-cubics.md", @@ -168,8 +191,10 @@ makedocs( "Contributing to Manopt.jl" => "contributing.md", "Extensions" => "extensions.md", "Notation" => "notation.md", + "Changelog" => "changelog.md", "References" => "references.md", ], + plugins=[bib], ) deploydocs(; repo="github.com/JuliaManifolds/Manopt.jl", push_preview=true) #back to main env diff --git a/docs/src/notation.md b/docs/src/notation.md index 1cb545bddd..9d74aa127e 100644 --- a/docs/src/notation.md +++ b/docs/src/notation.md @@ -2,10 +2,8 @@ In this package, we follow the notation introduced in [Manifolds.jl – Notation](https://juliamanifolds.github.io/Manifolds.jl/latest/misc/notation.html) -with the following additional or slightly changed notation +with the following additional notation | Symbol | Description | Also used | Comment | |:--:|:--------------- |:--:|:-- | | ``∇`` | The [Levi-Cevita connection](https://en.wikipedia.org/wiki/Levi-Civita_connection) | | | -| ``\operatorname{grad}f`` | The Riemannian gradient | ``∇f``| due to possible confusion with the connection, we try to avoid ``∇f`` | -| ``\operatorname{Hess}f``| The Riemannian Hessian | | diff --git a/docs/src/solvers/ChambollePock.md b/docs/src/solvers/ChambollePock.md index f5cc9540fd..8d8503f76a 100644 --- a/docs/src/solvers/ChambollePock.md +++ b/docs/src/solvers/ChambollePock.md @@ -3,7 +3,7 @@ The Riemannian Chambolle–Pock is a generalization of the Chambolle–Pock algorithm [ChambollePock:2011](@citet*) It is also known as primal-dual hybrid gradient (PDHG) or primal-dual proximal splitting (PDPS) algorithm. -In order to minimize over $p∈\mathcal M§ the cost function consisting of +In order to minimize over $p∈\mathcal M$ the cost function consisting of ```math F(p) + G(Λ(p)), diff --git a/ext/ManoptManifoldsExt/ARC_CG.jl b/ext/ManoptManifoldsExt/ARC_CG.jl deleted file mode 100644 index d3f42dd395..0000000000 --- a/ext/ManoptManifoldsExt/ARC_CG.jl +++ /dev/null @@ -1,46 +0,0 @@ -function set_manopt_parameter!(M::TangentSpaceAtPoint, ::Val{:p}, v) - M.point .= v - return M -end -function (f::Manopt.AdaptiveRegularizationCubicCost)(M::TangentSpaceAtPoint, X) - ## (33) in Agarwal et al. - return get_cost(base_manifold(M), f.mho, M.point) + - inner(base_manifold(M), M.point, X, f.X) + - 1 / 2 * inner( - base_manifold(M), - M.point, - X, - get_hessian(base_manifold(M), f.mho, M.point, X), - ) + - f.σ / 3 * norm(base_manifold(M), M.point, X)^3 -end -function (grad_f::Manopt.AdaptiveRegularizationCubicGrad)(M::TangentSpaceAtPoint, X) - # (37) in Agarwal et - return grad_f.X + - get_hessian(base_manifold(M), grad_f.mho, M.point, X) + - grad_f.σ * norm(base_manifold(M), M.point, X) * X -end -function (grad_f::Manopt.AdaptiveRegularizationCubicGrad)(M::TangentSpaceAtPoint, Y, X) - get_hessian!(base_manifold(M), Y, grad_f.mho, M.point, X) - Y .= Y + grad_f.X + grad_f.σ * norm(base_manifold(M), M.point, X) * X - return Y -end -function (c::StopWhenFirstOrderProgress)( - dmp::AbstractManoptProblem{<:TangentSpaceAtPoint}, - ams::AbstractManoptSolverState, - i::Int, -) - if (i == 0) - c.reason = "" - return false - end - #Update Gradient - TpM = get_manifold(dmp) - nG = norm(base_manifold(TpM), TpM.point, get_gradient(dmp, ams.p)) - nX = norm(base_manifold(TpM), TpM.point, ams.p) - if (i > 0) && (nG <= c.θ * nX^2) - c.reason = "The algorithm has reduced the model grad norm by $(c.θ).\n" - return true - end - return false -end diff --git a/ext/ManoptManifoldsExt/ManoptManifoldsExt.jl b/ext/ManoptManifoldsExt/ManoptManifoldsExt.jl index fcbcfe3a4d..9582aac4fe 100644 --- a/ext/ManoptManifoldsExt/ManoptManifoldsExt.jl +++ b/ext/ManoptManifoldsExt/ManoptManifoldsExt.jl @@ -30,5 +30,4 @@ include("nonmutating_manifolds_functions.jl") include("artificialDataFunctionsManifolds.jl") include("ChambollePockManifolds.jl") include("alternating_gradient.jl") -include("ARC_CG.jl") end diff --git a/src/Manopt.jl b/src/Manopt.jl index 33f26b2274..6b6095170d 100644 --- a/src/Manopt.jl +++ b/src/Manopt.jl @@ -69,8 +69,10 @@ using ManifoldsBase: NestedPowerRepresentation, ParallelTransport, PowerManifold, + ProductManifold, ProjectionTransport, QRRetraction, + TangentSpace, ^, _read, _write, @@ -123,6 +125,7 @@ using ManifoldsBase: set_component!, shortest_geodesic, shortest_geodesic!, + submanifold_components, vector_transport_to, vector_transport_to!, zero_vector, diff --git a/src/data/artificialDataFunctions.jl b/src/data/artificialDataFunctions.jl index f1adb6171e..6534cf865c 100644 --- a/src/data/artificialDataFunctions.jl +++ b/src/data/artificialDataFunctions.jl @@ -98,7 +98,7 @@ end generate a real-valued signal having piecewise constant, linear and quadratic intervals with jumps in between. If the resulting manifold the data lives on, is the [`Circle`](hhttps://juliamanifolds.github.io/Manifolds.jl/latest/manifolds/circle.html) -the data is also wrapped to $[-\pi,\pi)$. This is data for an example from [Bergmann et. al., SIAM J Imag Sci, 2014](@cite BergmannLausSteidlWeinmann:2014:1). +the data is also wrapped to ``[-\pi,\pi)``. This is data for an example from [Bergmann et. al., SIAM J Imag Sci, 2014](@cite BergmannLausSteidlWeinmann:2014:1). # Optional * `pts` – (`500`) number of points to sample the function @@ -110,7 +110,7 @@ function artificial_S1_signal(pts::Integer=500) end @doc raw""" artificial_S1_signal(x) -evaluate the example signal $f(x), x ∈ [0,1]$, +evaluate the example signal ``f(x), x ∈ [0,1]``, of phase-valued data introduces in Sec. 5.1 of [Bergmann et. al., SIAM J Imag Sci, 2014](@cite BergmannLausSteidlWeinmann:2014:1) for values outside that interval, this Signal is `missing`. """ @@ -147,7 +147,7 @@ function artificial_S2_whirl_image end Generate an artificial image of data on the 2 sphere, # Arguments -* `pts` – (`64`) size of the image in `pts`$\times$`pts` pixel. +* `pts` – (`64`) size of the image in `pts`×`pts` pixel. This example dataset was used in the numerical example in Section 5.5 of [Laus et al., SIAM J Imag Sci., 2017](@cite LausNikolovaPerschSteidl:2017) @@ -172,7 +172,7 @@ artificial_S2_rotation_image() @doc raw""" artificial_S2_whirl_patch([pts=5]) -create a whirl within the `pts`$\times$`pts` patch of +create a whirl within the `pts`×`pts` patch of [Sphere](https://juliamanifolds.github.io/Manifolds.jl/stable/manifolds/sphere.html)(@ref)`(2)`-valued image data. These patches are used within [`artificial_S2_whirl_image`](@ref). @@ -213,11 +213,11 @@ p_2 = \begin{bmatrix}-1&0&0\end{bmatrix}^{\mathrm{T}}, p_3 = \begin{bmatrix}0&0&-1\end{bmatrix}^{\mathrm{T}}, ```` -where each segment is a cubic Bézier curve, i.e. each point, except $p_3$ has a first point -within the following segment $b_i^+$, $i=0,1,2$ and a last point within the previous -segment, except for $p_0$, which are denoted by $b_i^-$, $i=1,2,3$. -This curve is differentiable by the conditions $b_i^- = \gamma_{b_i^+,p_i}(2)$, $i=1,2$, -where $\gamma_{a,b}$ is the [`shortest_geodesic`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/functions/#ManifoldsBase.shortest_geodesic-Tuple{AbstractManifold,%20Any,%20Any}) connecting $a$ and $b$. +where each segment is a cubic Bézier curve, i.e. each point, except ``p_3`` has a first point +within the following segment ``b_i^+``, ``i=0,1,2`` and a last point within the previous +segment, except for ``p_0``, which are denoted by ``b_i^-``, ``i=1,2,3``. +This curve is differentiable by the conditions ``b_i^- = \gamma_{b_i^+,p_i}(2)``, ``i=1,2``, +where ``\gamma_{a,b}`` is the [`shortest_geodesic`](https://juliamanifolds.github.io/ManifoldsBase.jl/stable/functions/#ManifoldsBase.shortest_geodesic-Tuple{AbstractManifold,%20Any,%20Any}) connecting ``a`` and ``b``. The remaining points are defined as ````math @@ -237,7 +237,7 @@ artificial_S2_composite_bezier_curve() artificial_SPD_image([pts=64, stepsize=1.5]) create an artificial image of symmetric positive definite matrices of size -`pts`$\times$`pts` pixel with a jump of size `stepsize`. +`pts`×`pts` pixel with a jump of size `stepsize`. This dataset was used in the numerical example of Section 5.2 of [Bačák et al., SIAM J Sci Comput, 2016](@cite BacakBergmannSteidlWeinmann:2016). """ @@ -273,7 +273,7 @@ function artificial_SPD_image2 end artificial_SPD_image2([pts=64, fraction=.66]) create an artificial image of symmetric positive definite matrices of size -`pts`$\times$`pts` pixel with right hand side `fraction` is moved upwards. +`pts`×`pts` pixel with right hand side `fraction` is moved upwards. This data set was introduced in the numerical examples of Section of [Bergmann, Presch, Steidl, SIAM J Imag Sci, 2016](@cite BergmannPerschSteidl:2016) """ @@ -282,7 +282,7 @@ artificial_SPD_image2(pts, fraction) @doc raw""" artificial_S2_lemniscate(p, t::Float64; a::Float64=π/2) -Generate a point from the signal on the [`Sphere`](https://juliamanifolds.github.io/Manifolds.jl/stable/manifolds/sphere.html) $\mathbb S^2$ by +Generate a point from the signal on the [`Sphere`](https://juliamanifolds.github.io/Manifolds.jl/stable/manifolds/sphere.html) ``\mathbb S^2`` by creating the [Lemniscate of Bernoulli](https://en.wikipedia.org/wiki/Lemniscate_of_Bernoulli) in the tangent space of `p` sampled at `t` and use exp` to obtain a point on the [`Sphere`](https://juliamanifolds.github.io/Manifolds.jl/stable/manifolds/sphere.html). @@ -302,7 +302,7 @@ artificial_S2_lemniscate(p, t::Float64, a::Float64=π / 2.0) @doc raw""" artificial_S2_lemniscate(p [,pts=128,a=π/2,interval=[0,2π]) -Generate a Signal on the [`Sphere`](https://juliamanifolds.github.io/Manifolds.jl/stable/manifolds/sphere.html) $\mathbb S^2$ by creating the +Generate a Signal on the [`Sphere`](https://juliamanifolds.github.io/Manifolds.jl/stable/manifolds/sphere.html) ``\mathbb S^2`` by creating the [Lemniscate of Bernoulli](https://en.wikipedia.org/wiki/Lemniscate_of_Bernoulli) in the tangent space of `p` sampled at `pts` points and use `exp` to get a signal on the [`Sphere`](https://juliamanifolds.github.io/Manifolds.jl/stable/manifolds/sphere.html). diff --git a/src/solvers/adaptive_regularization_with_cubics.jl b/src/solvers/adaptive_regularization_with_cubics.jl index ff85b47993..adce490d9e 100644 --- a/src/solvers/adaptive_regularization_with_cubics.jl +++ b/src/solvers/adaptive_regularization_with_cubics.jl @@ -839,3 +839,49 @@ function show(io::IO, c::StopWhenAllLanczosVectorsUsed) "StopWhenAllLanczosVectorsUsed($(repr(c.maxLanczosVectors)))\n $(status_summary(c))", ) end +# +# +function set_manopt_parameter!(M::TangentSpace, ::Val{:p}, v) + M.point .= v + return M +end +function (f::Manopt.AdaptiveRegularizationCubicCost)(M::TangentSpace, X) + ## (33) in Agarwal et al. + return get_cost(base_manifold(M), f.mho, M.point) + + inner(base_manifold(M), M.point, X, f.X) + + 1 / 2 * inner( + base_manifold(M), + M.point, + X, + get_hessian(base_manifold(M), f.mho, M.point, X), + ) + + f.σ / 3 * norm(base_manifold(M), M.point, X)^3 +end +function (grad_f::Manopt.AdaptiveRegularizationCubicGrad)(M::TangentSpace, X) + # (37) in Agarwal et + return grad_f.X + + get_hessian(base_manifold(M), grad_f.mho, M.point, X) + + grad_f.σ * norm(base_manifold(M), M.point, X) * X +end +function (grad_f::Manopt.AdaptiveRegularizationCubicGrad)(M::TangentSpace, Y, X) + get_hessian!(base_manifold(M), Y, grad_f.mho, M.point, X) + Y .= Y + grad_f.X + grad_f.σ * norm(base_manifold(M), M.point, X) * X + return Y +end +function (c::StopWhenFirstOrderProgress)( + dmp::AbstractManoptProblem{<:TangentSpace}, ams::AbstractManoptSolverState, i::Int +) + if (i == 0) + c.reason = "" + return false + end + #Update Gradient + TpM = get_manifold(dmp) + nG = norm(base_manifold(TpM), TpM.point, get_gradient(dmp, ams.p)) + nX = norm(base_manifold(TpM), TpM.point, ams.p) + if (i > 0) && (nG <= c.θ * nX^2) + c.reason = "The algorithm has reduced the model grad norm by $(c.θ).\n" + return true + end + return false +end diff --git a/test/solvers/test_adaptive_regularization_with_cubics.jl b/test/solvers/test_adaptive_regularization_with_cubics.jl index 047d12e814..2cd1802424 100644 --- a/test/solvers/test_adaptive_regularization_with_cubics.jl +++ b/test/solvers/test_adaptive_regularization_with_cubics.jl @@ -16,7 +16,7 @@ include("../utils/example_tasks.jl") Hess_f(M, p, X) = -A * X + p * p' * A * X + X * p' * A * p p0 = Matrix{Float64}(I, n, n)[:, 1:k] - M2 = TangentSpaceAtPoint(M, p0) + M2 = TangentSpace(M, p0) mho = ManifoldHessianObjective(f, grad_f, Hess_f) g = AdaptiveRegularizationCubicCost(M2, mho) diff --git a/tutorials/HowToRecord.qmd b/tutorials/HowToRecord.qmd index 853511b461..b940739dd7 100644 --- a/tutorials/HowToRecord.qmd +++ b/tutorials/HowToRecord.qmd @@ -198,7 +198,7 @@ mutable struct RecordCount <: RecordAction end function (r::RecordCount)(p::AbstractManoptProblem, ::AbstractManoptSolverState, i) if i > 0 - push!(r.recorded_values, get_cost_function(get_objective(p)).count) + push!(r.recorded_values, Manopt.get_cost_function(get_objective(p)).count) elseif i < 0 # reset if negative r.recorded_values = Vector{Int}() end diff --git a/tutorials/Project.toml b/tutorials/Project.toml index d0b552b10d..2a4ae0b4f6 100644 --- a/tutorials/Project.toml +++ b/tutorials/Project.toml @@ -18,7 +18,7 @@ FiniteDifferences = "0.12" IJulia = "1" LRUCache = "1.4" ManifoldDiff = "0.3" -Manifolds = "0.8.75" -ManifoldsBase = "0.14.5" +Manifolds = "0.8.81, 0.9" +ManifoldsBase = "0.14.12, 0.15" Manopt = "0.4.22" Plots = "1.38"