Skip to content
Merged
Show file tree
Hide file tree
Changes from 13 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
6 changes: 6 additions & 0 deletions Changelog.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,12 @@ 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).

## [Unreleased]

### Added

* a keyword argument `atol` to the `ConstrainedManifoldObjective` to set a tolerance for constraint satisfaction.

Comment thread
kellertuer marked this conversation as resolved.
## [0.5.28] November 17, 2025

### Changed
Expand Down
27 changes: 16 additions & 11 deletions src/plans/constrained_plan.jl
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ $(_problem(:Constrained))
evaluation=AllocatingEvaluation(),
M = nothing,
p = isnothing(M) ? nothing : rand(M),
atol = 0,
)

Generate the constrained objective based on all involved single functions `f`, `grad_f`, `g`,
Expand All @@ -101,6 +102,8 @@ Both variants require that at least one of the constraints (and its gradient) is
If any of the three parts provides a Hessian, the corresponding object, that is a
[`ManifoldHessianObjective`](@ref) for `f` or a [`VectorHessianFunction`](@ref) for `g` or `h`,
respectively, is created.

Feasibility of points with respect to the constraints is determined up to the tolerance `atol`.
"""
struct ConstrainedManifoldObjective{
E <: AbstractEvaluationType,
Expand All @@ -111,6 +114,7 @@ struct ConstrainedManifoldObjective{
objective::MO
equality_constraints::EMO
inequality_constraints::IMO
atol::Float64
end
function _vector_function_type_hint(f)
(!isnothing(f) && isa(f, AbstractVector)) && return ComponentVectorialType()
Expand Down Expand Up @@ -176,7 +180,7 @@ function ConstrainedManifoldObjective(
inequality_constraints::Union{Integer, Nothing} = nothing,
M::Union{AbstractManifold, Nothing} = nothing,
p = isnothing(M) ? nothing : rand(M),
kwargs...,
atol = 0,
)
if isnothing(hess_f)
objective = ManifoldGradientObjective(f, grad_f; evaluation = evaluation)
Expand Down Expand Up @@ -266,13 +270,14 @@ function ConstrainedManifoldObjective(
end
end
return ConstrainedManifoldObjective(
objective; equality_constraints = eq, inequality_constraints = ineq
objective; equality_constraints = eq, inequality_constraints = ineq, atol = atol
)
end
function ConstrainedManifoldObjective(
objective::MO;
equality_constraints::EMO = nothing,
inequality_constraints::IMO = nothing,
atol = 0,
kwargs...,
) where {E <: AbstractEvaluationType, MO <: AbstractManifoldObjective{E}, IMO, EMO}
if isnothing(equality_constraints) && isnothing(inequality_constraints)
Expand All @@ -290,7 +295,7 @@ function ConstrainedManifoldObjective(
)
end
return ConstrainedManifoldObjective{E, MO, EMO, IMO}(
objective, equality_constraints, inequality_constraints
objective, equality_constraints, inequality_constraints, atol
)
end
function ConstrainedManifoldObjective(
Expand Down Expand Up @@ -980,9 +985,10 @@ end

@doc """
is_feasible(M::AbstractManifold, cmo::ConstrainedManifoldObjective, p, kwargs...)
is_feasible(M::AbstractManifold, o::AbstractDecoratedManifoldObjective, p, kwargs...)

Evaluate whether a boint `p` on `M` is feasible with respect to the [`ConstrainedManifoldObjective`](@ref) `cmo`.
That is for the provided inequality constaints ``g: $(_math(:M)) → ℝ^m`` and equality constaints ``h: $(_math(:M)) \to ℝ^m``
Evaluate whether a point `p` on `M` is feasible with respect to the [`ConstrainedManifoldObjective`](@ref) `cmo`.
That is for the provided inequality constraints ``g: $(_math(:M)) → ℝ^m`` and equality constraints ``h: $(_math(:M)) \to ℝ^m``
from within `cmo`, the point ``p ∈ $(_math(:M))`` is feasible if
```math
g_i(p) ≤ 0, \text{ for all } i=1,…,m$(_tex(:quad))\text{ and }$(_tex(:quad)) h_j(p) = 0, \text{ for all } j=1,…,n.
Expand All @@ -997,15 +1003,14 @@ g_i(p) ≤ 0, \text{ for all } i=1,…,m$(_tex(:quad))\text{ and }$(_tex(:quad))
* `:warn`: displays the error message as a @warning.

The keyword `error=` and all other `kwargs...` are passed on to [`is_point`](@extref ManifoldsBase :jl:method:`ManifoldsBase.is_point-Tuple{AbstractManifold, Any, Bool}`)
if the point is verfied (see `check_point`).

All other keywords are passed on to `is_poi`
if the point is verified (see `check_point`).
"""
function is_feasible(M, cmo, p; check_point::Bool = true, error::Symbol = :none, kwargs...)
v = !check_point || is_point(M, p; error = error)
function is_feasible(M, o, p; check_point::Bool = true, error::Symbol = :none, kwargs...)
cmo = get_objective(o)
v = !check_point || is_point(M, p; error = error, kwargs...)
g = get_inequality_constraint(M, cmo, p, :)
h = get_equality_constraint(M, cmo, p, :)
feasible = v && all(g .<= 0) && all(h .== 0)
feasible = v && all(g .<= cmo.atol) && isapprox.(h, 0; atol = cmo.atol) |> all
# if we are feasible or no error shall be generated
((error === :none) || feasible) && return feasible
# collect information about infeasibily
Expand Down
15 changes: 7 additions & 8 deletions src/plans/debug.jl
Original file line number Diff line number Diff line change
Expand Up @@ -427,7 +427,6 @@ end
Display information about the feasibility of the current iterate

# Fields
* `atol`: absolute tolerance for when either equality or inequality constraints are counted as violated
* `format`: a vector of symbols and string formatting the output
* `io`: default stream to print the debug to.

Expand All @@ -450,27 +449,27 @@ format to print the output.
DebugFeasibility(
format=["feasible: ", :Feasible];
io::IO=stdout,
atol=1e-13
)

"""
mutable struct DebugFeasibility <: DebugAction
atol::Float64
format::Vector{Union{String, Symbol}}
io::IO
function DebugFeasibility(format = ["feasible: ", :Feasible]; io::IO = stdout, atol = 1.0e-13)
return new(atol, format, io)
function DebugFeasibility(format = ["feasible: ", :Feasible]; io::IO = stdout, atol = NaN)
isnan(atol) && (@warn "Providing atol= directly to DebugFeasibility is deprecated. Use the keyword for the ConstrainedObjective instead. The value provided here ($(atol)) is ignored")
return new(format, io)
end
end
function (d::DebugFeasibility)(
mp::AbstractManoptProblem, st::AbstractManoptSolverState, k::Int
)
s = ""
cmo = get_objective(mp)
p = get_iterate(st)
eqc = get_equality_constraint(mp, p, :)
eqc_nz = eqc[abs.(eqc) .> d.atol]
eqc_nz = eqc[abs.(eqc) .> cmo.atol]
ineqc = get_inequality_constraint(mp, p, :)
ineqc_pos = ineqc[ineqc .> d.atol]
ineqc_pos = ineqc[ineqc .> cmo.atol]
feasible = (length(eqc_nz) == 0) && (length(ineqc_pos) == 0)
n_eq = length(eqc_nz)
n_ineq = length(ineqc_pos)
Expand All @@ -491,7 +490,7 @@ function (d::DebugFeasibility)(
end
function show(io::IO, d::DebugFeasibility)
sf = "[" * (join([e isa String ? "\"$e\"" : ":$e" for e in d.format], ", ")) * "]"
return print(io, "DebugFeasibility($sf; atol=$(d.atol))")
return print(io, "DebugFeasibility($sf)")
end
function status_summary(d::DebugFeasibility)
sf = "[" * (join([e isa String ? "\"$e\"" : ":$e" for e in d.format], ", ")) * "]"
Expand Down
8 changes: 4 additions & 4 deletions src/solvers/interior_point_Newton.jl
Original file line number Diff line number Diff line change
Expand Up @@ -120,16 +120,16 @@ function interior_point_Newton(
g = nothing, h = nothing,
grad_g = nothing, grad_h = nothing,
Hess_g = nothing, Hess_h = nothing,
inequality_constrains::Union{Integer, Nothing} = nothing,
equality_constrains::Union{Nothing, Integer} = nothing,
inequality_constraints::Union{Integer, Nothing} = nothing,
equality_constraints::Union{Nothing, Integer} = nothing,
kwargs...,
)
cmo = ConstrainedManifoldObjective(
f, grad_f, g, grad_g, h, grad_h;
hess_f = Hess_f, hess_g = Hess_g, hess_h = Hess_h,
evaluation = evaluation,
inequality_constrains = inequality_constrains,
equality_constrains = equality_constrains,
inequality_constraints = inequality_constraints,
equality_constraints = equality_constraints,
M = M, p = p,
)
return interior_point_Newton(M, cmo, p; evaluation = evaluation, kwargs...)
Expand Down
2 changes: 1 addition & 1 deletion test/plans/test_constrained_plan.jl
Original file line number Diff line number Diff line change
Expand Up @@ -379,7 +379,7 @@ using LRUCache, Manifolds, ManifoldsBase, Manopt, ManoptTestSuite, Test, Recursi
mp = DefaultManoptProblem(M, coh)
io = IOBuffer()
df = DebugFeasibility(; io = io)
@test repr(df) === "DebugFeasibility([\"feasible: \", :Feasible]; atol=1.0e-13)"
@test repr(df) === "DebugFeasibility([\"feasible: \", :Feasible])"
# short form:
@test Manopt.status_summary(df) === "(:Feasibility, [\"feasible: \", :Feasible])"
df(mp, st, 1)
Expand Down
Loading