Source code for gemseo.algos.sequence_transformer.sequence_transformer
# Copyright 2021 IRT Saint Exupéry, https://www.irt-saintexupery.com
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU Lesser General Public
# License version 3 as published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
# Lesser General Public License for more details.
#
# You should have received a copy of the GNU Lesser General Public License
# along with this program; if not, write to the Free Software Foundation,
# Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
# Contributors:
# INITIAL AUTHORS - initial API and implementation and/or initial
# documentation
# :author: Charlie Vanaret, Benoit Pauwels, Francois Gallard
# OTHER AUTHORS - MACROSCOPIC CHANGES
"""Sequence transformer methods."""
from __future__ import annotations
from abc import abstractmethod
from collections import deque
from typing import TYPE_CHECKING
from gemseo.utils.metaclasses import ABCGoogleDocstringInheritanceMeta
if TYPE_CHECKING:
from typing import ClassVar
from numpy.typing import NDArray
[docs]
class SequenceTransformer(metaclass=ABCGoogleDocstringInheritanceMeta):
r"""A vector sequence transformer for fixed-point iteration method.
For any function :math:`G : \mathbb{R}^n \rightarrow \mathbb{R}^n`, the fixed point
iteration method computes the sequence :math:`x_{n+1} = G(x_n)`, whih is exepcted to
converge towards a fixed point of :math:`G`.
A sequence transformer is a function :math:`f : \mathbb{R}^n \rightarrow
\mathbb{R}^n` so that the new iterate is instead computed as
.. math::
x_{n+1}' = f(G, x_n, \dots, x_{n-k}, G(x_n)),
for a given :math:`k \geq 0`.
The transformed sequence is expected to exhibit faster convergence and/or better
numerical stability.
"""
_MINIMUM_NUMBER_OF_ITERATES: ClassVar[int]
"""The minimum number of :math:`G(x_i)` required to compute the transformation."""
_MINIMUM_NUMBER_OF_RESIDUALS: ClassVar[int]
"""The minimum number of residuals :math:`G(x_i) - x_i` required to compute the
transformation."""
_iterates: deque
"""The previously computed iterates :math:`G(x_i)`."""
_residuals: deque
"""The previously computed residuals :math:`G(x_i) - x_i`."""
def __init__(self) -> None: # noqa:D107
# Instantiate double-ended queues to store the relevant quantities
self._iterates = deque(maxlen=self._MINIMUM_NUMBER_OF_ITERATES)
self._residuals = deque(maxlen=self._MINIMUM_NUMBER_OF_RESIDUALS)
[docs]
def clear(self) -> None:
"""Clear the iterates."""
self._iterates.clear()
self._residuals.clear()
[docs]
def compute_transformed_iterate(
self,
iterate: NDArray,
residual: NDArray,
) -> NDArray:
"""Compute the next transformed iterate.
Args:
iterate: The iterate :math:`G(x_n)`.
residual: The associated residual :math:`G(x_n) - x_n`.
Returns:
The next transformed iterate :math:`x_{n+1}`.
"""
# Store iterates and residuals
self._iterates.append(iterate.copy())
self._residuals.append(residual.copy())
# Compute the transformed iterate only if sufficient material at hand
if (
len(self._iterates) >= self._MINIMUM_NUMBER_OF_ITERATES
and len(self._residuals) >= self._MINIMUM_NUMBER_OF_RESIDUALS
):
return self._compute_transformed_iterate()
return iterate
@abstractmethod
def _compute_transformed_iterate(self) -> NDArray:
"""Compute the next transformed iterate."""