Commit e53a1ac6 authored by Martin Reinecke's avatar Martin Reinecke

more doc work

parent ff879f9c
Pipeline #25106 passed with stages
in 6 minutes and 36 seconds
......@@ -128,6 +128,10 @@ class) can be interpreted as an (implicitly defined) matrix.
It can be applied to :class:`Field` instances, resulting in other :class:`Field`
instances that potentially live on other domains.
Operator basics
---------------
There are four basic ways of applying an operator :math:`A` to a field :math:`f`:
- direct multiplication: :math:`A\cdot f`
......@@ -179,6 +183,10 @@ Further operator classes provided by NIFTy are
unstructured ones. This is typically needed when building instrument response
operators.
Syntatic sugar
--------------
Nifty4 allows simple and intuitive construction of altered and combined
operators.
As an example, if ``A``, ``B`` and ``C`` are of type :class:`LinearOperator`
......@@ -201,16 +209,68 @@ were the original operator's adjoint or inverse, respectively.
Minimization
============
Most problems in IFT are solved by (possibly nested) minimizations of high-dimensional functions, which are often nonlinear.
Most problems in IFT are solved by (possibly nested) minimizations of
high-dimensional functions, which are often nonlinear.
Energy functionals
------------------
In NIFTy4 such functions are represented by objects of type :class:`Energy`.
These hold the prescription how to calculate the function's value, gradient and (optionally) curvature at any given position.
Function values are floating-point scalars, gradients have the form of fields living on the energy's position domain, and curvatures are represented by linear operator objects.
These hold the prescription how to calculate the function's
:attr:`~Energy.value`, :attr:`~Energy.gradient` and
(optionally) :attr:`~Energy.curvature` at any given position.
Function values are floating-point scalars, gradients have the form of fields
living on the energy's position domain, and curvatures are represented by
linear operator objects.
Some examples of concrete energy classes delivered with NIFTy4 are :class:`QuadraticEnergy` (with position-independent curvature, mainly used with conjugate gradient minimization) and :class:`WienerFilterEnergy`.
Energies are classes that typically have to be provided by the user when tackling new IFT problems.
The minimization procedure can be carried out by one of several algorithms; NIFTy4 currently ships solvers based on
Convergence control
-------------------
Iterative minimization of an energy reqires some means of
controlling the quality of the current solution estimate and stopping once
it is sufficiently accurate. In case of numerical problems, the iteration needs
to be terminated as well, returning a suitable error description.
In NIFTy4, this functionality is encapsulated in the abstract
:class:`IterationController` class, which is provided with the initial energy
object before starting the minimization, and is updated with the improved
energy after every iteration. Based on this information, it can either continue
the minimization or return the current estimate indicating convergence or
failure.
Sensible stopping criteria can vary significantly with the problem being
solved; NIFTy provides one concrete sub-class of :class:`IterationController`
called :class:`GradientNormController`, which should be appropriate in many
circumstances, but users have complete freedom to implement custom sub-classes
for their specific applications.
Minimization algorithms
-----------------------
It is important to realize that the machinery presented here cannot only be
used for minimizing IFT Hamiltonians, but also for the numerical inversion of
linear operators, if the desired application mode is not directly available.
A classical example is the information propagator
:math:`D = \left(R^\dagger N^{-1} R + S^{-1}\right)^{-1}`,
which must be applied when calculating a Wiener filter. Only its inverse
application is straightforward; to use it in forward direction, we make use
of NIFTy's :class:`InversionEnabler` class, which internally performs a
minimization of a :class:`QuadraticEnergy` by means of a
:class:`ConjugateGradient` algorithm.
Some examples of concrete energy classes delivered with NIFTy4 are
:class:`QuadraticEnergy` (with position-independent curvature, mainly used with
conjugate gradient minimization) and :class:`WienerFilterEnergy`.
Energies are classes that typically have to be provided by the user when
tackling new IFT problems.
The minimization procedure can be carried out by one of several algorithms;
NIFTy4 currently ships solvers based on
- the conjugate gradient method (for quadratic energies)
- the steepest descent method
......
......@@ -53,12 +53,12 @@ class Energy(NiftyMetaBase()):
self._position = position.lock()
def at(self, position):
""" Initializes and returns a new Energy object at the new position.
""" Returns a new Energy object, initialized at `position`.
Parameters
----------
position : Field
Parameter for the new Energy object.
Location in parameter space for the new Energy object.
Returns
-------
......@@ -70,15 +70,16 @@ class Energy(NiftyMetaBase()):
@property
def position(self):
"""
The Field location in parameter space where value, gradient and
curvature are evaluated.
Field : selected location in parameter space
The Field location in parameter space where value, gradient and
curvature are evaluated.
"""
return self._position
@property
def value(self):
"""
float
float : value of the functional
The value of the energy functional at given `position`.
"""
raise NotImplementedError
......@@ -86,8 +87,7 @@ class Energy(NiftyMetaBase()):
@property
def gradient(self):
"""
Field
The gradient at given `position`.
Field : The gradient at given `position`.
"""
raise NotImplementedError
......@@ -95,15 +95,14 @@ class Energy(NiftyMetaBase()):
@memo
def gradient_norm(self):
"""
float
The length of the gradient at given `position`.
float : L2-norm of the gradient at given `position`.
"""
return self.gradient.norm()
@property
def curvature(self):
"""
LinearOperator
LinearOperator : implicitly defined curvature
A positive semi-definite operator or function describing the
curvature of the potential at the given `position`.
"""
......
......@@ -21,6 +21,27 @@ from .. import dobj
class GradientNormController(IterationController):
"""An iteration controller checking (mainly) the L2 gradient norm.
Parameters
----------
tol_abs_gradnorm : float, optional
If the L2 norm of the energy gradient is below this value, the
convergence counter will be increased in this iteration.
tol_rel_gradnorm : float, optional
If the L2 norm of the energy gradient divided by its initial L2 norm
is below this value, the convergence counter will be increased in this
iteration.
convergence_level : int, default=1
The number which the convergence counter must reach before the
iteration is considered to be converged
iteration_limit : int, optional
The maximum number of iterations that will be carried out.
name : str, optional
if supplied, this string and some diagnostic information will be
printed after every iteration
"""
def __init__(self, tol_abs_gradnorm=None, tol_rel_gradnorm=None,
convergence_level=1, iteration_limit=None, name=None):
super(GradientNormController, self).__init__()
......@@ -31,6 +52,20 @@ class GradientNormController(IterationController):
self._name = name
def start(self, energy):
""" Start a new iteration.
The iteration and convergence counters are set to 0.
Parameters
----------
energy : Energy
The energy functional to be minimized.
Returns
-------
int : iteration status
can be CONVERGED or CONTINUE
"""
self._itcount = -1
self._ccount = 0
if self._tol_rel_gradnorm is not None:
......@@ -39,6 +74,27 @@ class GradientNormController(IterationController):
return self.check(energy)
def check(self, energy):
""" Check for convergence.
- Increase the iteration counter by 1.
- If any of the convergence criteria are fulfilled, increase the
convergence counter by 1; else decrease it by 1 (but not below 0).
- If the convergence counter exceeds the convergence level, return
CONVERGED.
- If the iteration counter exceeds the iteration limit, return
CONVERGED.
- Otherwise return CONTINUE.
Parameters
----------
energy : Energy
The current solution estimate
Returns
-------
int : iteration status
can be CONVERGED or CONTINUE
"""
self._itcount += 1
inclvl = False
......
......@@ -77,24 +77,21 @@ class LineEnergy(object):
@property
def energy(self):
"""
Energy
The underlying Energy object
Energy : The underlying Energy object
"""
return self._energy
@property
def value(self):
"""
float
The value of the energy functional at given `position`.
float : The value of the energy functional at given `position`.
"""
return self._energy.value
@property
def directional_derivative(self):
"""
float
The directional derivative at the given `position`.
float : The directional derivative at the given `position`.
"""
res = self._energy.gradient.vdot(self._line_direction)
if abs(res.imag) / max(abs(res.real), 1.) > 1e-12:
......
......@@ -42,6 +42,24 @@ class QuadraticEnergy(Energy):
return QuadraticEnergy(position=position, A=self._A, b=self._b)
def at_with_grad(self, position, grad):
""" Specialized version of `at`, taking also a gradient.
This custom method is meant for use within :class:ConjugateGradient`
minimizers, which already have the gradient available. It saves time
by not recomputing it.
Parameters
----------
position : Field
Location in parameter space for the new Energy object.
grad : Field
Energy gradient at the new position.
Returns
-------
Energy
Energy object at new position.
"""
return QuadraticEnergy(position=position, A=self._A, b=self._b,
_grad=grad)
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment