Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 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
10 changes: 10 additions & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,16 @@ The file was started with Version `0.4`.
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.6.6] August 24, 2026

### Fixed

* `augmented_Lagrangian_method` set the penalty parameter `ρ` of the sub problem's cost to the
constant `1/3` instead of the current `alms.ρ`, while its gradient did receive `alms.ρ`. (#637)
* `set_parameter!` for `:μ` and `:λ` tied the dual variable's type to the type parameter of
`AbstractConstrainedFunction`. It hence never applied to `AugmentedLagrangianCost` and, falling
back to a no-op, silently left its `μ` and `λ` at their initial values. (#637)

## [0.6.5] August 22, 2026

### Added
Expand Down
2 changes: 1 addition & 1 deletion Project.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name = "Manopt"
uuid = "0fc0a36d-df90-57f3-8f93-d78a9fc72bb5"
version = "0.6.5"
version = "0.6.6"

[workspace]
projects = ["test", "docs", "tutorials"]
Expand Down
4 changes: 2 additions & 2 deletions src/base/function/constrained.jl
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,12 @@ constraints of type `T`.
"""
abstract type AbstractConstrainedFunction{T} <: AbstractManifoldFunction end

function set_parameter!(acf::AbstractConstrainedFunction{T}, ::Val{:μ}, μ::T) where {T}
function set_parameter!(acf::AbstractConstrainedFunction, ::Val{:μ}, μ)
acf.μ = μ
return acf
end
get_parameter(acf::AbstractConstrainedFunction, ::Val{:μ}) = acf.μ
function set_parameter!(acf::AbstractConstrainedFunction{T}, ::Val{:λ}, λ::T) where {T}
function set_parameter!(acf::AbstractConstrainedFunction, ::Val{:λ}, λ)
acf.λ = λ
return acf
end
Expand Down
2 changes: 1 addition & 1 deletion src/solvers/augmented_Lagrangian_method.jl
Original file line number Diff line number Diff line change
Expand Up @@ -480,7 +480,7 @@ end
function step_solver!(mp::AbstractManoptProblem, alms::AugmentedLagrangianMethodState, iter)
M = get_manifold(mp)
# use subsolver to minimize the augmented Lagrangian
set_parameter!(alms.sub_problem, Val(:Objective), Val(:Cost), Val(:ρ), 1 / 3)
set_parameter!(alms.sub_problem, Val(:Objective), Val(:Cost), Val(:ρ), alms.ρ)
set_parameter!(alms.sub_problem, Val(:Objective), Val(:Cost), Val(:μ), alms.μ)
set_parameter!(alms.sub_problem, Val(:Objective), Val(:Cost), Val(:λ), alms.λ)
set_parameter!(alms.sub_problem, Val(:Objective), Val(:Gradient), Val(:ρ), alms.ρ)
Expand Down
24 changes: 24 additions & 0 deletions test/solvers/test_augmented_lagrangian.jl
Original file line number Diff line number Diff line change
Expand Up @@ -79,4 +79,28 @@ using LinearAlgebra: I, tr
@test q isa Real
@test f(M, q) < f(M, 4)
end
@testset "Sub problem penalty parameters" begin
# The cost and the gradient of the sub problem have to be set up with the same penalty
# parameter ρ. If they disagree, the sub solver minimises a different function than the
# one it computes gradients for, and since ρ grows geometrically the two drift apart.
M = Euclidean(2)
f(M, p) = p[1]
grad_f(M, p) = [1.0, 0.0]
h(M, p) = [p[1]^2 + p[2]^2 - 1.0]
grad_h(M, p) = [[2 * p[1], 2 * p[2]]]
s = augmented_Lagrangian_method(
M, f, grad_f, [0.5, 0.5];
h = h, grad_h = grad_h, equality_constraints = 1,
stopping_criterion = StopAfterIteration(20), return_state = true,
)
sub_objective = Manopt.get_objective(s.sub_problem)
sub_cost = Manopt.get_cost_function(sub_objective)
# the gradient is wrapped for the in-place dispatch, so it needs one more unwrap
sub_gradient = sub_objective.functions.gradient.f
@test sub_cost.ρ == sub_gradient.ρ
@test sub_cost.μ == sub_gradient.μ
@test sub_cost.λ == sub_gradient.λ
# with matching parameters the solver reaches the minimiser (-1, 0)
@test distance(M, get_solver_result(s), [-1.0, 0.0]) < 1.0e-3
end
end
Loading