Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion .codecov.yml
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
ignore:
- "src/helpers/exports/.*" # exclude exports, since there are no CI tests for Asy-exports
- "src/documentation_glossary.jl" # exclude this since it is just use to create the docs and code cov goes bogus on this.
- "src/documentation_glossary.jl" # exclude this since it is just use to create the docs and code cov goes bogus on this.
- "src/helpers/test.jl" # exclude tests since they are not part of the package code but merely helpers
1 change: 1 addition & 0 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ updates:
- "/"
- "/docs"
- "/tutorials"
- "/test"
schedule:
interval: "daily"
groups:
Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/CompatHelper.yml
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ jobs:
- name: "Run CompatHelper"
run: |
using CompatHelper
CompatHelper.main(; subdirs = ["", "docs", "tutorials"])
CompatHelper.main(; subdirs = ["", "docs", "tutorials", "test"])
shell: julia --color=yes {0}
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
Expand Down
14 changes: 10 additions & 4 deletions Changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,22 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Added

* a keyword argument `atol` to the `ConstrainedManifoldObjective` to set a tolerance for constraint satisfaction.
* a keyword argument `atol` to the `ConstrainedManifoldObjective` to set a tolerance for constraint satisfaction. (#545)

### Fixed

* Fixed a typo in `DebugFeasibility`, where an undefined variable was used.
* Fixed a typo in `DebugFeasibility`, where an undefined variable was used. (#544)

### Changed

* Removed `atol` from `DebugFeasibility` and instead use the one newly added `atol` from the `ConstrainedManifoldObjective`.
* Move from CompatHelper to dependabot to keep track of dependency updates in Julia packages.
* Removed `atol` from `DebugFeasibility` and instead use the one newly added `atol` from the `ConstrainedManifoldObjective`. (#546)
* Move from CompatHelper to dependabot to keep track of dependency updates in Julia packages. (#547)
* moved the `ManoptTestSuite` module to a sub module `Manopt.Test` within `Manopt.jl`,
so it can be easier resused by others as well (#550)
* moved to using a `Project.toml` for tests and an overall `[Workspace]`.
This also allows finally to run single test files without installing all packages manually, but instead just switching to and instantiating the test environment. (#550)
* for compatibility, state also `[source]` entries consistently in the sub `Project.toml` files. (#550)


## [0.5.28] November 17, 2025

Expand Down
29 changes: 6 additions & 23 deletions Project.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
name = "Manopt"
uuid = "0fc0a36d-df90-57f3-8f93-d78a9fc72bb5"
authors = ["Ronny Bergmann <manopt@ronnybergmann.net>"]
version = "0.5.28"
version = "0.5.29"

[deps]
ColorSchemes = "35d6a980-a343-548e-a6ea-1d62b119f2f4"
Expand Down Expand Up @@ -38,20 +38,18 @@ ManoptRecursiveArrayToolsExt = "RecursiveArrayTools"
ManoptRipQPQuadraticModelsExt = ["RipQP", "QuadraticModels"]

[compat]
Aqua = "0.8"
ColorSchemes = "3.5.0"
ColorTypes = "0.9.1, 0.10, 0.11, 0.12"
Colors = "0.11.2, 0.12, 0.13"
DataStructures = "0.17, 0.18, 0.19"
Dates = "1.10"
ForwardDiff = "0.10, 1"
JuMP = "1.15"
LRUCache = "1.4"
LineSearches = "7.2.0"
LinearAlgebra = "1.10"
LineSearches = "7.2.0"
LRUCache = "1.4"
ManifoldDiff = "0.3.8, 0.4"
Manifolds = "0.11.2"
ManifoldsBase = "2.2.0"
Manifolds = "0.11.2"
Markdown = "1.10"
Plots = "1.30"
Preferences = "1.4"
Expand All @@ -62,22 +60,7 @@ RecursiveArrayTools = "2, 3"
RipQP = "0.6.4, 0.7"
SparseArrays = "1.10"
Statistics = "1.10"
Test = "1.10"
julia = "1.10"

[extras]
Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595"
ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210"
JuMP = "4076af6c-e467-56ae-b986-b466b2749572"
LRUCache = "8ac3fa9e-de4c-5943-b1dc-09c6b5f20637"
LineSearches = "d3d80556-e9d4-5f37-9878-2ab0fcc64255"
ManifoldDiff = "af67fdf4-a580-4b9f-bbec-742ef357defd"
Manifolds = "1cead3c2-87b3-11e9-0ccd-23c62b72b94e"
Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"
QuadraticModels = "f468eda6-eac5-11e8-05a5-ff9e497bcd19"
RecursiveArrayTools = "731186ca-8d62-57ce-b412-fbd966d074cd"
RipQP = "1e40b3f8-35eb-4cd8-8edd-3e515bb9de08"
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"

[targets]
test = ["Test", "Aqua", "ForwardDiff", "JuMP", "Manifolds", "ManifoldDiff", "Plots", "LineSearches", "LRUCache", "RecursiveArrayTools", "RipQP", "QuadraticModels"]
[workspace]
projects = ["test", "docs", "tutorials"]
11 changes: 8 additions & 3 deletions docs/make.jl
Original file line number Diff line number Diff line change
Expand Up @@ -156,8 +156,9 @@ makedocs(;
format = Documenter.HTML(;
prettyurls = run_on_CI || ("--prettyurls" ∈ ARGS),
assets = ["assets/favicon.ico", "assets/citations.css", "assets/link-icons.css"],
size_threshold = 350 * 2^10, # raise slightly 200 to to 300 KiB
size_threshold_warn = 11 * 2^12, # raise from 500 KiB to 1.1 MB (for search index)
size_threshold = 1100 * 2^10, # raise slightly 200 to to 300 KiB
size_threshold_warn = 900 * 2^10, # raise from 500 KiB to 1.1 MB (for search index)
search_size_threshold_warn = 2000 * 2^10,
),
modules = [
Manopt,
Expand Down Expand Up @@ -215,7 +216,11 @@ makedocs(;
"Debug Output" => "plans/debug.md",
"Recording values" => "plans/record.md",
],
"Helpers" => ["Checks" => "helpers/checks.md", "Exports" => "helpers/exports.md"],
"Helpers" => [
"Checks" => "helpers/checks.md",
"Exports" => "helpers/exports.md",
"Test" => "helpers/test.md",
],
"Contributing to Manopt.jl" => "contributing.md",
"Extensions" => "extensions.md",
"Notation" => "notation.md",
Expand Down
13 changes: 13 additions & 0 deletions docs/src/helpers/test.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
# Manopt.Test

```@docs
Manopt.Test
```

For now those tests mainly consider a few parts from `ManoptExamples.jl`
and a few dummy types.

```@docs
Manopt.Test.mean_task
Manopt.Test.Circle_mean_task
```
1 change: 1 addition & 0 deletions ext/ManoptManifoldsExt/ManoptManifoldsExt.jl
Original file line number Diff line number Diff line change
Expand Up @@ -26,4 +26,5 @@ Rn(::Val{:Manifolds}, args...; kwargs...) = Euclidean(args...; kwargs...)
const NONMUTATINGMANIFOLDS = Union{Circle, PositiveNumbers, Euclidean{Tuple{}}}
include("manifold_functions.jl")
include("ChambollePockManifolds.jl")
include("test_examples.jl")
end
11 changes: 11 additions & 0 deletions ext/ManoptManifoldsExt/test_examples.jl
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
#
#
# Fill examples in the Tests submodule

function Manopt.Test.Circle_mean_task()
M = Circle()
data = [-π / 2, π / 4, 0.0, π / 4]
p_star = 0.0
f, grad_f = Manopt.Test.mean_task(M, data)
return M, f, grad_f, data[1], p_star
end
2 changes: 2 additions & 0 deletions ext/ManoptRecursiveArrayToolsExt.jl
Original file line number Diff line number Diff line change
Expand Up @@ -128,4 +128,6 @@ function alternating_gradient_descent!(
Manopt.solve!(dmp, agds)
return Manopt.get_solver_return(get_objective(dmp), agds)
end

## Prox TV on
end
3 changes: 3 additions & 0 deletions src/Manopt.jl
Original file line number Diff line number Diff line change
Expand Up @@ -218,9 +218,12 @@ include("solvers/subgradient.jl")
include("solvers/vectorbundle_newton.jl")
include("solvers/debug_solver.jl")
include("solvers/record_solver.jl")

include("helpers/checks.jl")
include("helpers/exports/Asymptote.jl")
include("helpers/LineSearchesTypes.jl")
include("helpers//test.jl")

include("deprecated.jl")

function JuMP_Optimizer end
Expand Down
96 changes: 81 additions & 15 deletions test/ManoptTestSuite.jl/ManoptTestSuite.jl → src/helpers/test.jl
Original file line number Diff line number Diff line change
@@ -1,10 +1,24 @@
"""
ManoptTestSuite.jl
Manopt.Test

A small module to provide common dummy types and defaults for testing.
The module `Manopt.Test` provides dummy types and small test problems and examples
that can be used throughout testing.

Some of these are simplified variants from problems from `ManoptExamples.jl`,
that are added here to not introduce a circular dependency.

Some of the functionality is only populated when certain packages are loaded,
that is
* `Test.jl`
* `Manifolds.jl`
"""
module ManoptTestSuite
using Manifolds, ManifoldDiff, ManifoldsBase, Manopt, Test
module Test
using ..Manopt
using ..Manopt: AbstractManifoldObjective, AbstractManoptProblem, AbstractEvaluationType
using ..Manopt: AbstractManoptSolverState
using ..Manopt: StoppingCriterionSet, StoppingCriterion
using ManifoldsBase
using ManifoldDiff

#
#
Expand All @@ -31,22 +45,32 @@ end
DummyState() = DummyState([])
Manopt.get_iterate(::DummyState) = NaN
Manopt.set_parameter!(s::DummyState, ::Val, v) = s

#
#
# Grouped Tasks
Manopt.set_parameter!(s::DummyState, ::Val{:StoppingCriterion}, v) = s
"""
M, f, grad_f, p0, p_star = Circle_mean_task()

Create a small mean problem on the circle to test Number-based algorithms
Requires `Manifolds.jl` to be loaded, use [`Manopt.Test.mean_task`](@ref)`(M, data)`
for the general case
"""
function Circle_mean_task()
M = Circle()
data = [-π / 2, π / 4, 0.0, π / 4]
p_star = 0.0
f(M, p) = 1 / 10 * sum(distance.(Ref(M), data, Ref(p)) .^ 2)
grad_f(M, p) = 1 / 5 * sum(-log.(Ref(M), Ref(p), data))
return M, f, grad_f, data[1], p_star
function Circle_mean_task end

@doc raw"""
f, grad_f = Manopt.Test.mean_task(M, data)

Returns cost and gradient for computing the mean of `data` ``d_i`` on manifold `M`

```math
\begin{align*}
f(p) = \frac{1}{2n} \sum_{i=1}^n d_M(p, d_i)^2
\operatorname{grad} f(p) = -\frac{1}{n} \sum_{i=1}^n \log_p(d_i)
\end{align*}
"""
function mean_task(M::AbstractManifold, data::AbstractVector)
n = length(data)
f(M, p) = 1 / (2n) * sum(distance.(Ref(M), Ref(p), data) .^ 2)
grad_f(M, p) = -1 / n * sum(log.(Ref(M), Ref(p), data))
return f, grad_f
end

#
Expand Down Expand Up @@ -339,6 +363,27 @@ function prox_Total_Variation(
ManifoldsBase.exp_fused(M, x[2], log(M, x[2], x[1]), t),
)
end
function prox_Total_Variation(
M::PowerManifold, λ::Number, x::Tuple{T, T}, p::Int = 1
) where {T}
d = distance(M, x[1], x[2])
if p == 1
t = min(0.5, λ / d)
elseif p == 2
t = λ / (1 + 2 * λ)
else
throw(
ErrorException(
"Proximal Map of TV(M,x1,x2,p) not implemented for p=$(p) (requires p=1 or 2)",
),
)
end
return (
ManifoldsBase.exp_fused(M, x[1], log(M, x[1], x[2]), t),
ManifoldsBase.exp_fused(M, x[2], log(M, x[2], x[1]), t),
)
end

function prox_Total_Variation!(
M::AbstractManifold, y, λ::Number, x::Tuple{T, T}, p::Int = 1
) where {T}
Expand All @@ -360,6 +405,27 @@ function prox_Total_Variation!(
ManifoldsBase.exp_fused!(M, y[2], x[2], X2, t)
return y
end
function prox_Total_Variation!(
M::PowerManifold, y, λ::Number, x::Tuple{T, T}, p::Int = 1
) where {T}
d = distance(M, x[1], x[2])
if p == 1
t = min(0.5, λ / d)
elseif p == 2
t = λ / (1 + 2 * λ)
else
throw(
ErrorException(
"Proximal Map of TV(M,x1,x2,p) not implemented for p=$(p) (requires p=1 or 2)",
),
)
end
X1 = log(M, x[1], x[2])
X2 = log(M, x[2], x[1])
ManifoldsBase.exp_fused!(M, y[1], x[1], X1, t)
ManifoldsBase.exp_fused!(M, y[2], x[2], X2, t)
return y
end
function prox_Total_Variation(M::PowerManifold, λ, x, p::Int = 1)
y = deepcopy(x)
power_size = power_dimensions(M)
Expand Down
39 changes: 39 additions & 0 deletions test/Project.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
[deps]
Aqua = "4c88cf16-eb10-579e-8560-4a9242c79595"
Dates = "ade2ca70-3891-5945-98fb-dc099432e06a"
ForwardDiff = "f6369f11-7733-5829-9624-2563aa707210"
JuMP = "4076af6c-e467-56ae-b986-b466b2749572"
LRUCache = "8ac3fa9e-de4c-5943-b1dc-09c6b5f20637"
LineSearches = "d3d80556-e9d4-5f37-9878-2ab0fcc64255"
LinearAlgebra = "37e2e46d-f89d-539d-b4ee-838fcccc9c8e"
ManifoldDiff = "af67fdf4-a580-4b9f-bbec-742ef357defd"
Manifolds = "1cead3c2-87b3-11e9-0ccd-23c62b72b94e"
ManifoldsBase = "3362f125-f0bb-47a3-aa74-596ffd7ef2fb"
Manopt = "0fc0a36d-df90-57f3-8f93-d78a9fc72bb5"
Plots = "91a5bcdd-55d7-5caf-9e0b-520d859cae80"
QuadraticModels = "f468eda6-eac5-11e8-05a5-ff9e497bcd19"
Random = "9a3f8284-a2c9-5f02-9a11-845980a1fd5c"
RecursiveArrayTools = "731186ca-8d62-57ce-b412-fbd966d074cd"
RipQP = "1e40b3f8-35eb-4cd8-8edd-3e515bb9de08"
Test = "8dfed614-e22c-5e08-85e1-65c5234f0b40"

[sources]
Manopt = {path = ".."}

[compat]
Aqua = "0.8"
Dates = "1.10"
ForwardDiff = "0.10, 1"
JuMP = "1.15"
LRUCache = "1.4"
LinearAlgebra = "1.10"
LineSearches = "7.2.0"
ManifoldDiff = "0.3.8, 0.4"
Manifolds = "0.11.2"
Plots = "1.30"
QuadraticModels = "0.9"
Random = "1.10"
RecursiveArrayTools = "2, 3"
RipQP = "0.6.4, 0.7"
Test = "1.10"
julia = "1.10"
15 changes: 6 additions & 9 deletions test/plans/test_cache.jl
Original file line number Diff line number Diff line change
@@ -1,7 +1,4 @@
s = joinpath(@__DIR__, "..", "ManoptTestSuite.jl")
!(s in LOAD_PATH) && (push!(LOAD_PATH, s))

using LinearAlgebra, LRUCache, Manifolds, Manopt, ManoptTestSuite, Test, Random
using LinearAlgebra, LRUCache, Manifolds, Manopt, Test, Random

# Three dummy functors that are just meant to count their calls
mutable struct TestCostCount
Expand Down Expand Up @@ -77,8 +74,8 @@ end
A `SimpleManifoldCachedObjective`""",
)
@test startswith(
repr((sco1, ManoptTestSuite.DummyState())),
"""ManoptTestSuite.DummyState(Float64[])
repr((sco1, Manopt.Test.DummyState())),
"""Manopt.Test.DummyState(Float64[])

## Cache
A `SimpleManifoldCachedObjective`""",
Expand Down Expand Up @@ -229,10 +226,10 @@ end
lco = objective_cache_factory(M, co, (:LRU, [:Cost, :Gradient, :Differential]))
@test startswith(repr(lco), "## Cache\n * ")
@test startswith(
repr((lco, ManoptTestSuite.DummyState())),
"ManoptTestSuite.DummyState(Float64[])\n\n## Cache\n * ",
repr((lco, Manopt.Test.DummyState())),
"Manopt.Test.DummyState(Float64[])\n\n## Cache\n * ",
)
ro = ManoptTestSuite.DummyDecoratedObjective(o)
ro = Manopt.Test.DummyDecoratedObjective(o)
#undecorated works as well
lco2 = objective_cache_factory(M, o, (:LRU, [:Cost, :Gradient]))
@test Manopt.get_cost_function(lco2) != Manopt.get_cost_function(o)
Expand Down
Loading
Loading