# SPDX-FileCopyrightText: 2025-2026 AutoLyap contributors
# SPDX-License-Identifier: GPL-3.0-only
import numpy as np
from typing import Tuple
from .algorithm import Algorithm
[docs]
class TripleMomentum(Algorithm):
r"""
Triple-momentum method :cite:`vanscoy2018fastestknownglobally`.
See :doc:`3. Algorithm representation </theory/algorithm_representation>`
for mathematical notation and definitions.
Notation-driven assumptions are declared by the user via
:class:`~autolyap.problemclass.InclusionProblem`: when present, terms written with
:math:`\nabla` use differentiable functions, terms written with
:math:`\prox_{\gamma f}` use proper, lower semicontinuous, convex functions,
and terms written with :math:`J_{\gamma G}` use maximally monotone operators.
Standard form
-------------
Let :math:`q = \mu/L`,
.. math::
\alpha_{\mathrm{tm}} = \frac{2-\sqrt{q}}{L}, \qquad
\beta_{\mathrm{tm}} = \frac{(1-\sqrt{q})^2}{1+\sqrt{q}}, \qquad
\gamma_{\mathrm{tm}} = \frac{(1-\sqrt{q})^2}{(2-\sqrt{q})(1+\sqrt{q})}.
For initial points :math:`x^{-1},x^0 \in \calH`,
.. math::
(\forall k \in \naturals)\quad
\left[
\begin{aligned}
y^k &= x^k + \gamma_{\mathrm{tm}}(x^k - x^{k-1}), \\
x^{k+1} &= x^k + \beta_{\mathrm{tm}}(x^k - x^{k-1}) - \alpha_{\mathrm{tm}} \nabla f(y^k).
\end{aligned}
\right.
State-space representation
--------------------------
The update can be written in the algorithm representation with
.. math::
\bx^k = (x^k, x^{k-1}), \qquad
\bu^k = \nabla f(y^k), \qquad
\by^k = y^k.
Let :math:`q = \mu/L`, and define
.. math::
\alpha_{\mathrm{tm}} = \frac{2-\sqrt{q}}{L}, \qquad
\beta_{\mathrm{tm}} = \frac{(1-\sqrt{q})^2}{1+\sqrt{q}}, \qquad
\gamma_{\mathrm{tm}} = \frac{(1-\sqrt{q})^2}{(2-\sqrt{q})(1+\sqrt{q})}.
With this representation, the system matrices are
.. math::
\begin{aligned}
A_k &=
\begin{bmatrix}
1+\beta_{\mathrm{tm}} & -\beta_{\mathrm{tm}} \\
1 & 0
\end{bmatrix}, &
B_k &=
\begin{bmatrix}
-\alpha_{\mathrm{tm}} \\
0
\end{bmatrix}, \\
C_k &=
\begin{bmatrix}
1+\gamma_{\mathrm{tm}} & -\gamma_{\mathrm{tm}}
\end{bmatrix}, &
D_k &= \begin{bmatrix} 0 \end{bmatrix}.
\end{aligned}
These are the system matrices returned by :meth:`~autolyap.algorithms.Algorithm.get_ABCD`.
Structural parameters
---------------------
.. math::
n = 2,\quad m = 1,\quad (\bar{m}_i)_{i=1}^{m} = (1),\quad \bar{m} = 1.
.. math::
I_{\text{func}} = \{1\},\quad I_{\text{op}} = \varnothing.
"""
def __init__(
self, mu:
float, L:
float)
-> None:
r"""
Initialize the triple-momentum method.
Structural inputs passed to :class:`~autolyap.algorithms.Algorithm` are
.. math::
n = 2,\quad m = 1,\quad (\bar m_i)_{i=1}^{m} = (1),\quad \bar m = 1,\quad
I_{\mathrm{func}} = \{1\},\quad I_{\mathrm{op}} = \varnothing.
"""
super()
.__init__(
2,
1, [
1], [
1], [])
self.set_L(L)
self.set_mu(mu)
[docs]
def set_mu(
self, mu:
float)
-> None:
r"""
Set the strong-convexity parameter :math:`\mu`.
Shared notation follows the class-level reference in
:class:`~autolyap.algorithms.TripleMomentum`.
**Parameters**
- `mu` (:class:`~typing.Union`\[:class:`int`, :class:`float`\]): The value corresponding to :math:`\mu`.
**Raises**
- `ValueError`: If `mu` is not a finite real number or if :math:`\mu \le 0`.
"""
mu
= self._validate_positive_finite_real(mu,
"mu")
self._set_dynamic_parameter(
"mu", mu)
[docs]
def set_L(
self, L:
float)
-> None:
r"""
Set the smoothness parameter :math:`L`.
Shared notation follows the class-level reference in
:class:`~autolyap.algorithms.TripleMomentum`.
**Parameters**
- `L` (:class:`~typing.Union`\[:class:`int`, :class:`float`\]): The value corresponding to :math:`L`.
**Raises**
- `ValueError`: If `L` is not a finite real number or if :math:`L \le 0`.
"""
L
= self._validate_positive_finite_real(L,
"L")
self._set_dynamic_parameter(
"L", L)
[docs]
def get_ABCD(
self, k:
int)
-> Tuple[np
.ndarray, np
.ndarray, np
.ndarray, np
.ndarray]:
q
= self.mu
/ self.L
alpha_tm
= (
2 - np
.sqrt(q))
/ self.L
beta_tm
= (
1 - np
.sqrt(q))
** 2 / (
1 + np
.sqrt(q))
gamma_tm
= (
1 - np
.sqrt(q))
** 2 / ((
2 - np
.sqrt(q))
* (
1 + np
.sqrt(q)))
A
= np
.array([[
1 + beta_tm,
-beta_tm], [
1,
0]])
B
= np
.array([[
-alpha_tm], [
0]])
C
= np
.array([[
1 + gamma_tm,
-gamma_tm]])
D
= np
.array([[
0]])
return (A, B, C, D)