The Riemannian Chambolle-Pock algorithm
The Riemannian Chambolle-Pock algorithm is a generalization of the Chambolle-Pock algorithm Chambolle and Pock [CP11]. 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
\[F(p) + G(Λ(p)),\]
where $F:\mathcal M → \overline{ℝ}$, $G:\mathcal N → \overline{ℝ}$, and $Λ:\mathcal M →\mathcal N$. If the manifolds $\mathcal M$ or $\mathcal N$ are not Hadamard, it has to be considered locally only, that is on geodesically convex sets $\mathcal C \subset \mathcal M$ and $\mathcal D \subset\mathcal N$ such that $Λ(\mathcal C) \subset \mathcal D$.
The algorithm is available in four variants: exact versus linearized (see variant) as well as with primal versus dual relaxation (see relax). For more details, see Bergmann, Herzog, Silva Louzeiro, Tenbrinck and Vidal-Núñez [BHS+21]. In the following, the case of the exact, primal relaxed Riemannian Chambolle-Pock algorithm is described.
Given base points $m∈\mathcal C$, $n=Λ(m)∈\mathcal D$, initial primal and dual values $p^{(0)} ∈\mathcal C$, $ξ_n^{(0)} ∈T_n^*\mathcal N$, and primal and dual step sizes $\sigma_0$, $\tau_0$, relaxation $\theta_0$, as well as acceleration $\gamma$.
As an initialization, perform $\bar p^{(0)} \gets p^{(0)}$.
The algorithm performs the steps $k=0,1,…$ (until a StoppingCriterion is fulfilled)
\[ξ^{(k+1)}_n = \operatorname{prox}_{\tau_k G_n^*}\Bigl(ξ_n^{(k)} + \tau_k \bigl(\log_n Λ (\bar p^{(k)})\bigr)^\flat\Bigr)\]
\[p^{(k+1)} = \operatorname{prox}_{\sigma_k F}\biggl(\exp_{p^{(k)}}\Bigl( \mathcal P_{p^{(k)}\gets m}\bigl(-\sigma_k DΛ(m)^*[ξ_n^{(k+1)}]\bigr)^\sharp\Bigr)\biggr)\]
- If $\gamma > 0$, update (otherwise keep $\theta_k = \theta_0$, $\sigma_{k+1} = \sigma_k$, $\tau_{k+1} = \tau_k$)
- $\theta_k = (1+2\gamma\sigma_k)^{-\frac{1}{2}}$
- $\sigma_{k+1} = \sigma_k\theta_k$
- $\tau_{k+1} = \frac{\tau_k}{\theta_k}$
\[\bar p^{(k+1)} = \exp_{p^{(k+1)}}\bigl(-\theta_k \log_{p^{(k+1)}} p^{(k)}\bigr)\]
Furthermore you can exchange the exponential map, the logarithmic map, and the parallel transport by a retraction, an inverse retraction, and a vector transport.
Finally you can also update the base points $m$ and $n$ during the iterations. This introduces a few additional vector transports. The same holds for the case $Λ(m^{(k)})\neq n^{(k)}$ at some point. All these cases are covered in the algorithm.
Manopt.ChambollePock — Function
ChambollePock(M, N, f, p, X, m, n, prox_F, prox_G_dual, adjoint_linear_operator; kwargs...)
ChambollePock!(M, N, f, p, X, m, n, prox_F, prox_G_dual, adjoint_linear_operator; kwargs...)Perform the Riemannian Chambolle-Pock algorithm.
Given a cost function $\mathcal{E}: \mathcal{M} → ℝ$ of the form
\[\mathcal{E}(p) = F(p) + G( Λ(p) ),\]
where $F:\mathcal{M} → ℝ$, $G:\mathcal{N} → ℝ$, and $Λ:\mathcal{M} → \mathcal{N}$.
This can be done inplace of $p$.
Input parameters
M::AbstractManifold: a Riemannian manifold $\mathcal{M}$N::AbstractManifold: a Riemannian manifold $\mathcal{M}$f: a cost function $f: \mathcal{M}→ ℝ$ implemented as(M, p) -> vp::P: a point on the manifold $\mathcal{M}$X::T: a tangent vector at the point $n$ on the manifold $\mathcal{N}$m::P: a point on the manifold $\mathcal{M}$n::P: a point on the manifold $\mathcal{N}$adjoint_linearized_operator: the adjoint $DΛ^*$ of the linearized operator $DΛ: T_{m}\mathcal{M} → T_{Λ(m)}\mathcal{N}$prox_F, prox_G_dual: the proximal maps of $F$ and $G^\ast_n$
If the forward operator Λ is provided, this performs the exact Riemannian Chambolle Pock algorithm; see the optional keyword linearized_forward_operator= for the linearized variant.
For more details on the algorithm, see [BHS+21].
Keyword Arguments
acceleration=0.05: acceleration parametercallbacks::D = Dict{Symbol,Function}(): provided callback functions given either as a single function(symbol, problem, state, k)called in every hook or as a (vector of) pairs:hook => function, which are processed byprocess_callbacks_arg. As key you can either pass single symbol or an array of symbols to indicate a callback should be added in multiple placesdual_stepsize=1/sqrt(8): proximal parameter of the dual proxevaluation::AbstractEvaluationType=AllocatingEvaluation(): specify whether the functions that return an array, for example a point or a tangent vector, work by allocating their result (AllocatingEvaluation) or whether they modify their input argument to return the result therein (InplaceEvaluation). Since usually the first argument is the manifold, the modified argument is the second.inverse_retraction_method::AbstractInverseRetractionMethod=default_inverse_retraction_method(M, typeof(p)): an inverse retraction $\operatorname{retr}^{-1}$ to use, see the section on retractions and their inversesinverse_retraction_method_dual::AbstractInverseRetractionMethod=default_inverse_retraction_method(N, typeof(n)): an inverse retraction $\operatorname{retr}^{-1}$ to use, see the section on retractions and their inversesΛ=missing: the (forward) operator $Λ(⋅)$ (required for the:exactvariant)linearized_forward_operator=missing: its linearization $DΛ(⋅)[⋅]$ (required for the:linearizedvariant)primal_stepsize=1/sqrt(8): proximal parameter of the primal proxrelaxation=1.: the relaxation parameter $θ$relax=:primal: whether to relax the primal or dualvariant=:linearizedifΛis missing, otherwise:exact: variant to use. Note that this changes the arguments theforward_operatoris called with.stopping_criterion::StoppingCriterion=StopAfterIteration(200): a functor indicating that the stopping criterion is fulfilledupdate_primal_base=missing: function to updatem(identity by default/missing)update_dual_base=missing: function to updaten(identity by default/missing)retraction_method::AbstractRetractionMethod=default_retraction_method(M, typeof(p)): a retraction $\operatorname{retr}$ to use, see the section on retractionsvector_transport_method::AbstractVectorTransportMethod=default_vector_transport_method(M, typeof(p)): a vector transport $\mathcal T_{⋅←⋅}$ to use, see the section on vector transportsvector_transport_method_dual::AbstractVectorTransportMethod=default_vector_transport_method(N, typeof(n)): a vector transport $\mathcal T_{⋅←⋅}$ to use, see the section on vector transports
Output
The obtained approximate minimizer $p^*$. To obtain the whole final state of the solver, see get_solver_return for details, especially the return_state= keyword.
Manopt.ChambollePock! — Function
ChambollePock(M, N, f, p, X, m, n, prox_F, prox_G_dual, adjoint_linear_operator; kwargs...)
ChambollePock!(M, N, f, p, X, m, n, prox_F, prox_G_dual, adjoint_linear_operator; kwargs...)Perform the Riemannian Chambolle-Pock algorithm.
Given a cost function $\mathcal{E}: \mathcal{M} → ℝ$ of the form
\[\mathcal{E}(p) = F(p) + G( Λ(p) ),\]
where $F:\mathcal{M} → ℝ$, $G:\mathcal{N} → ℝ$, and $Λ:\mathcal{M} → \mathcal{N}$.
This can be done inplace of $p$.
Input parameters
M::AbstractManifold: a Riemannian manifold $\mathcal{M}$N::AbstractManifold: a Riemannian manifold $\mathcal{M}$f: a cost function $f: \mathcal{M}→ ℝ$ implemented as(M, p) -> vp::P: a point on the manifold $\mathcal{M}$X::T: a tangent vector at the point $n$ on the manifold $\mathcal{N}$m::P: a point on the manifold $\mathcal{M}$n::P: a point on the manifold $\mathcal{N}$adjoint_linearized_operator: the adjoint $DΛ^*$ of the linearized operator $DΛ: T_{m}\mathcal{M} → T_{Λ(m)}\mathcal{N}$prox_F, prox_G_dual: the proximal maps of $F$ and $G^\ast_n$
If the forward operator Λ is provided, this performs the exact Riemannian Chambolle Pock algorithm; see the optional keyword linearized_forward_operator= for the linearized variant.
For more details on the algorithm, see [BHS+21].
Keyword Arguments
acceleration=0.05: acceleration parametercallbacks::D = Dict{Symbol,Function}(): provided callback functions given either as a single function(symbol, problem, state, k)called in every hook or as a (vector of) pairs:hook => function, which are processed byprocess_callbacks_arg. As key you can either pass single symbol or an array of symbols to indicate a callback should be added in multiple placesdual_stepsize=1/sqrt(8): proximal parameter of the dual proxevaluation::AbstractEvaluationType=AllocatingEvaluation(): specify whether the functions that return an array, for example a point or a tangent vector, work by allocating their result (AllocatingEvaluation) or whether they modify their input argument to return the result therein (InplaceEvaluation). Since usually the first argument is the manifold, the modified argument is the second.inverse_retraction_method::AbstractInverseRetractionMethod=default_inverse_retraction_method(M, typeof(p)): an inverse retraction $\operatorname{retr}^{-1}$ to use, see the section on retractions and their inversesinverse_retraction_method_dual::AbstractInverseRetractionMethod=default_inverse_retraction_method(N, typeof(n)): an inverse retraction $\operatorname{retr}^{-1}$ to use, see the section on retractions and their inversesΛ=missing: the (forward) operator $Λ(⋅)$ (required for the:exactvariant)linearized_forward_operator=missing: its linearization $DΛ(⋅)[⋅]$ (required for the:linearizedvariant)primal_stepsize=1/sqrt(8): proximal parameter of the primal proxrelaxation=1.: the relaxation parameter $θ$relax=:primal: whether to relax the primal or dualvariant=:linearizedifΛis missing, otherwise:exact: variant to use. Note that this changes the arguments theforward_operatoris called with.stopping_criterion::StoppingCriterion=StopAfterIteration(200): a functor indicating that the stopping criterion is fulfilledupdate_primal_base=missing: function to updatem(identity by default/missing)update_dual_base=missing: function to updaten(identity by default/missing)retraction_method::AbstractRetractionMethod=default_retraction_method(M, typeof(p)): a retraction $\operatorname{retr}$ to use, see the section on retractionsvector_transport_method::AbstractVectorTransportMethod=default_vector_transport_method(M, typeof(p)): a vector transport $\mathcal T_{⋅←⋅}$ to use, see the section on vector transportsvector_transport_method_dual::AbstractVectorTransportMethod=default_vector_transport_method(N, typeof(n)): a vector transport $\mathcal T_{⋅←⋅}$ to use, see the section on vector transports
Output
The obtained approximate minimizer $p^*$. To obtain the whole final state of the solver, see get_solver_return for details, especially the return_state= keyword.
Objective
Manopt.PrimalDualManifoldObjective — Type
PrimalDualManifoldObjective <: AbstractPrimalDualManifoldObjectiveDescribes an Objective linearized or exact Chambolle-Pock algorithm, cf. [BHS+21], [CP11]
Fields
cost: $F + G(Λ(⋅))$ to evaluate interim cost function valueslinearized_forward_operator!: linearized operator for the forward operation in the algorithm $DΛ$adjoint_linearized_operator!: the adjoint differential $(DΛ)^* : T\mathcal{N} → T\mathcal{M}$prox_f!: the proximal map belonging to $f$prox_g_dual!: the proximal map belonging to $g_n^*$Λ!: the forward operator (if given) $Λ: \mathcal{M} → \mathcal{N}$
Either the linearized operator $DΛ$ or $Λ$ are required usually.
Constructor
PrimalDualManifoldObjective(cost, prox_f, prox_g_dual, adjoint_linearized_operator; linearized_forward_operator::Union{Function,Missing}=missing, Λ::Union{Function,Missing}=missing, evaluation::AbstractEvaluationType = AllocatingEvaluation(),)Using the evaluation= keyword can be used to specify that all functions work in-place instead of the default allocating one.
State
Manopt.ChambollePockState — Type
ChambollePockState <: AbstractPrimalDualSolverStatestores all options and variables within a linearized or exact Chambolle Pock.
Fields
callbacks::D: provided callback functions given as a dictionary with symbols as keysacceleration::R: acceleration factordual_stepsize::R: proximal parameter of the dual proxinverse_retraction_method::AbstractInverseRetractionMethod: an inverse retraction $\operatorname{retr}^{-1}$ to use, see the section on retractions and their inversesinverse_retraction_method_dual::AbstractInverseRetractionMethod: an inverse retraction $\operatorname{retr}^{-1}$ to use, see the section on retractions and their inversesm::P: base point on $\mathcal{M}$n::Q: base point on $\mathcal{N}$p::P: an initial point $p^{(0)} ∈ \mathcal{M}$pbar::P: the relaxed iterate used in the next dual update step (when using:primalrelaxation)primal_stepsize::R: proximal parameter of the primal proxX::T: an initial tangent vector $X^{(0)} ∈ T_{n}\mathcal{N}$Xbar::T: the relaxed iterate used in the next primal update step (when using:dualrelaxation)relaxation::R: relaxation in the primal relaxation step (used to computepbar)relax::Symbol: which variable to relax,:primalor:dualretraction_method::AbstractRetractionMethod: a retraction $\operatorname{retr}$ to use, see the section on retractionsstop::StoppingCriterion: a functor indicating that the stopping criterion is fulfilledvariant: whether to perform an:exactor:linearizedChambolle-Pockupdate_primal_base: function(pr, st, k) -> mto update the primal baseupdate_dual_base: function(pr, st, k) -> nto update the dual basevector_transport_method::AbstractVectorTransportMethod: a vector transport $\mathcal T_{⋅←⋅}$ to use, see the section on vector transportsvector_transport_method_dual::AbstractVectorTransportMethod: a vector transport $\mathcal T_{⋅←⋅}$ to use, see the section on vector transports
Here, P is a point type on $\mathcal{M}$, T its tangent vector type, Q a point type on $\mathcal{N}$, and R<:Real is a real number type
The functions update_primal_base and update_dual_base are called with an AbstractManoptProblemamp, an AbstractManoptSolverStateams and the current iteration k as arguments. If you activate these to be different from the default identity, you have to provide the forward operator Λ of the objective for the algorithm to work (which might be missing in the linearized case).
Constructor
ChambollePockState(M::AbstractManifold, N::AbstractManifold; kwargs...) where {P, Q, T, R <: Real}Keyword arguments
callbacks::D = Dict{Symbol,Function}(): provided callback functions given as a dictionary with symbols as keysn=rand(N)p=rand(M)m=rand(M)X=zero_vector(N, n)acceleration=0.0dual_stepsize=1/sqrt(8)primal_stepsize=1/sqrt(8)inverse_retraction_method::AbstractInverseRetractionMethod=default_inverse_retraction_method(M, typeof(p)): an inverse retraction $\operatorname{retr}^{-1}$ to use, see the section on retractions and their inversesinverse_retraction_method_dual::AbstractInverseRetractionMethod=default_inverse_retraction_method(N, typeof(n)): an inverse retraction $\operatorname{retr}^{-1}$ to use, see the section on retractions and their inversesrelaxation=1.0relax=:primal: relax the primal variable by defaultretraction_method::AbstractRetractionMethod=default_retraction_method(M, typeof(p)): a retraction $\operatorname{retr}$ to use, see the section on retractionsstopping_criterion::StoppingCriterion=StopAfterIteration(300): a functor indicating that the stopping criterion is fulfilledvariant=:exact: run the exact Chambolle Pock by defaultupdate_primal_base=missingupdate_dual_base=missingvector_transport_method::AbstractVectorTransportMethod=default_vector_transport_method(M, typeof(p)): a vector transport $\mathcal T_{⋅←⋅}$ to use, see the section on vector transportsvector_transport_method_dual::AbstractVectorTransportMethod=default_vector_transport_method(N, typeof(n)): a vector transport $\mathcal T_{⋅←⋅}$ to use, see the section on vector transports
if Manifolds.jl is loaded, N is also a keyword argument and set to TangentBundle(M) by default.
Constructing the default n on a TangentBundle requires RecursiveArrayTools.jl to be loaded as well.
Useful terms
Manopt.primal_residual — Function
primal_residual(tmp::TwoManifoldProblem, apds::AbstractPrimalDualSolverState, p_old, X_old, n_old)Compute the primal residual at iteration $k$ given the necessary values $p_{k-1}, X_{k-1}$, and $n_{k-1}$ from the previous iteration.
\[\lVert \frac{1}{σ}\operatorname{retr}^{-1}_{p_{k}}p_{k-1} - V_{p_k←m_k} \bigl( DΛ^*(m_k)\bigl[V_{n_k← n_{k-1}}X_{k-1} - X_k \bigr]\bigr) \rVert\]
where $V_{⋅←⋅}$ is the vector transport used in the ChambollePockState.
Manopt.dual_residual — Function
dual_residual(tmp::TwoManifoldProblem, apds::AbstractPrimalDualSolverState, p_old, X_old, n_old)Compute the dual residual at iteration $k$ given the necessary values $p_{k-1}, X_{k-1}$, and $n_{k-1}$ from the previous iteration. The formula is slightly different depending on the apds.variant used:
For the :linearized it reads
\[\lVert \frac{1}{τ}\bigl( V_{n_{k}← n_{k-1}}(X_{k-1}) - X_k \bigr ) - DΛ(m_k)\bigl[ V_{m_k← p_k}\operatorname{retr}^{-1}_{p_{k}}(p_{k-1})\bigr] \rVert\]
and for the :exact variant
\[\lVert \frac{1}{τ}\bigl( V_{n_{k}← n_{k-1}}(X_{k-1}) - X_k \bigr ) - \operatorname{retr}^{-1}_{n_{k}}\bigl( Λ(\operatorname{retr}_{m_{k}}(V_{m_k← p_k}\operatorname{retr}^{-1}_{p_{k}}p_{k-1}))\bigr) \rVert\]
where in both cases $V_{⋅←⋅}$ is the vector transport used in the ChambollePockState.
Solver specific debug output
DebugDualBaseIterate, DebugDualBaseChange, DebugPrimalBaseIterate, DebugPrimalBaseChange, DebugDualChange, DebugDualIterate, DebugDualResidual, DebugPrimalChange, DebugPrimalIterate, DebugPrimalResidual, DebugPrimalDualResidual
Solver specific recording actions
Manopt.RecordDualBaseIterate — Function
RecordDualBaseIterate(n)Create a RecordAction that records the dual base point, a RecordEntry of the field n of the state.
Manopt.RecordDualBaseChange — Function
RecordDualBaseChange()Create a RecordAction that records the dual base point change, a RecordEntryChange of the field n with distance to the last value to store a value.
Manopt.RecordDualChange — Function
RecordDualChange()Create a RecordAction that records the change of the dual iterate, a RecordEntryChange of the field X with distance to the last value to store a value.
Manopt.RecordDualIterate — Function
RecordDualIterate(X)Create a RecordAction that records the dual iterate, a RecordEntry of the field X of the state.
Manopt.RecordPrimalBaseIterate — Function
RecordPrimalBaseIterate(m)Create a RecordAction that records the primal base point, a RecordEntry of the field m of the state.
Manopt.RecordPrimalBaseChange — Function
RecordPrimalBaseChange()Create a RecordAction that records the primal base point change, a RecordEntryChange of the field m with distance to the last value to store a value.
Manopt.RecordPrimalChange — Function
RecordPrimalChange()Create a RecordAction that records the primal value change, a RecordChange, to record the change of the iterate p.
Manopt.RecordPrimalIterate — Function
RecordPrimalIterate(p)Create a RecordAction that records the primal point, a RecordIterate of the iterate p.
Internals
Manopt.update_prox_parameters! — Function
update_prox_parameters!(pds)update the prox parameters as described in Algorithm 2 of [CP11],
- $θ_k = \frac{1}{\sqrt{1+2γσ_k}}$
- $σ_{k+1} = θ_kσ_k$
- $τ_{k+1} = \frac{τ_k}{θ_k}$
Technical details
The ChambollePock solver requires the following functions of a manifold to be available for both the manifolds $\mathcal M$ and $\mathcal N$
- A
retract!(M, q, p, X); it is recommended to set thedefault_retraction_methodto a favorite retraction. If this default is set, aretraction_method=does not have to be specified. - An
inverse_retract!(M, X, p, q); it is recommended to set thedefault_inverse_retraction_methodto a favorite inverse retraction. If this default is set, aninverse_retraction_method=orinverse_retraction_method_dual=(for $\mathcal N$) does not have to be specified. - A
vector_transport_to!(M, Y, p, X, q); it is recommended to set thedefault_vector_transport_methodto a favorite vector transport. If this default is set, avector_transport_method=orvector_transport_method_dual=(for $\mathcal N$) does not have to be specified. - A
copyto!(M, q, p)andcopy(M,p)for points.
Literature
- [BHS+21]
- R. Bergmann, R. Herzog, M. Silva Louzeiro, D. Tenbrinck and J. Vidal-Núñez. Fenchel duality theory and a primal-dual algorithm on Riemannian manifolds. Foundations of Computational Mathematics 21, 1465–1504 (2021), arXiv:1908.02022.
- [CP11]
- A. Chambolle and T. Pock. A first-order primal-dual algorithm for convex problems with applications to imaging. Journal of Mathematical Imaging and Vision 40, 120–145 (2011).