energy.py 6.14 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12
# This program is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
# 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 General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.
Theo Steininger's avatar
Theo Steininger committed
13
#
Martin Reinecke's avatar
Martin Reinecke committed
14
# Copyright(C) 2013-2018 Max-Planck-Society
Theo Steininger's avatar
Theo Steininger committed
15 16 17
#
# NIFTy is being developed at the Max-Planck-Institut fuer Astrophysik
# and financially supported by the Studienstiftung des deutschen Volkes.
18

Martin Reinecke's avatar
Martin Reinecke committed
19
from __future__ import absolute_import, division, print_function
20

Martin Reinecke's avatar
Martin Reinecke committed
21
from ..compat import *
Philipp Arras's avatar
Philipp Arras committed
22
from ..utilities import NiftyMetaBase, memo
23

24

Martin Reinecke's avatar
Martin Reinecke committed
25
class Energy(NiftyMetaBase()):
26
    """ Provides the functional used by minimization schemes.
27

28
   The Energy object is an implementation of a scalar function including its
Martin Reinecke's avatar
Martin Reinecke committed
29
   gradient and metric at some position.
30 31 32

    Parameters
    ----------
33 34
    position : Field
        The input parameter of the scalar function.
35 36 37

    Notes
    -----
38
    An instance of the Energy class is defined at a certain location. If one
Martin Reinecke's avatar
Martin Reinecke committed
39
    is interested in the value, gradient or metric of the abstract energy
40 41 42
    functional one has to 'jump' to the new position using the `at` method.
    This method returns a new energy instance residing at the new position. By
    this approach, intermediate results from computing e.g. the gradient can
Martin Reinecke's avatar
Martin Reinecke committed
43
    safely be reused for e.g. the value or the metric.
44

45 46 47
    Memorizing the evaluations of some quantities (using the memo decorator)
    minimizes the computational effort for multiple calls.

Martin Reinecke's avatar
Martin Reinecke committed
48
    See Also
49 50
    --------
    memo
51 52

    """
53

54
    def __init__(self, position):
55
        super(Energy, self).__init__()
56
        self._position = position
57 58

    def at(self, position):
Martin Reinecke's avatar
Martin Reinecke committed
59
        """ Returns a new Energy object, initialized at `position`.
60 61 62 63

        Parameters
        ----------
        position : Field
Martin Reinecke's avatar
Martin Reinecke committed
64
            Location in parameter space for the new Energy object.
65 66 67

        Returns
        -------
Martin Reinecke's avatar
Martin Reinecke committed
68
        Energy
69 70
            Energy object at new position.
        """
71 72
        return self.__class__(position)

73 74
    @property
    def position(self):
75
        """
Martin Reinecke's avatar
Martin Reinecke committed
76 77 78
        Field : selected location in parameter space.

        The Field location in parameter space where value, gradient and
Martin Reinecke's avatar
Martin Reinecke committed
79
        metric are evaluated.
80
        """
81 82
        return self._position

83 84
    @property
    def value(self):
85
        """
Martin Reinecke's avatar
Martin Reinecke committed
86 87
        float : value of the functional.

Martin Reinecke's avatar
Martin Reinecke committed
88
            The value of the energy functional at given `position`.
89
        """
90 91 92 93
        raise NotImplementedError

    @property
    def gradient(self):
94
        """
Martin Reinecke's avatar
Martin Reinecke committed
95
        Field : The gradient at given `position`.
96
        """
97 98
        raise NotImplementedError

Martin Reinecke's avatar
Martin Reinecke committed
99 100 101 102
    @property
    @memo
    def gradient_norm(self):
        """
Martin Reinecke's avatar
Martin Reinecke committed
103
        float : L2-norm of the gradient at given `position`.
Martin Reinecke's avatar
Martin Reinecke committed
104 105 106
        """
        return self.gradient.norm()

107
    @property
Martin Reinecke's avatar
Martin Reinecke committed
108
    def metric(self):
109
        """
Martin Reinecke's avatar
Martin Reinecke committed
110
        LinearOperator : implicitly defined metric.
Martin Reinecke's avatar
Martin Reinecke committed
111
            A positive semi-definite operator or function describing the
Martin Reinecke's avatar
Martin Reinecke committed
112
            metric of the potential at the given `position`.
113
        """
114
        raise NotImplementedError
Martin Reinecke's avatar
stage 1  
Martin Reinecke committed
115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130

    def longest_step(self, dir):
        """Returns the longest allowed step size along `dir`

        Parameters
        ----------
        dir : Field
            the search direction

        Returns
        -------
        float or None
            the longest allowed step when starting from `self.position` along
            `dir`. If None, the step size is not limited.
        """
        return None
Philipp Arras's avatar
Philipp Arras committed
131

132
    def makeInvertible(self, controller, preconditioner=None):
Martin Reinecke's avatar
fixes  
Martin Reinecke committed
133
        from .iteration_controller import IterationController
134 135
        if not isinstance(controller, IterationController):
            raise TypeError
Martin Reinecke's avatar
Martin Reinecke committed
136
        return MetricInversionEnabler(self, controller, preconditioner)
137

138 139
    def __mul__(self, factor):
        from .energy_sum import EnergySum
Philipp Arras's avatar
Philipp Arras committed
140 141 142
        if isinstance(factor, (float, int)):
            return EnergySum.make([self], [factor])
        return NotImplemented
Philipp Arras's avatar
Philipp Arras committed
143

144 145 146 147 148
    def __rmul__(self, factor):
        return self.__mul__(factor)

    def __add__(self, other):
        from .energy_sum import EnergySum
Philipp Arras's avatar
Philipp Arras committed
149 150 151
        if isinstance(other, Energy):
            return EnergySum.make([self, other])
        return NotImplemented
152 153 154

    def __sub__(self, other):
        from .energy_sum import EnergySum
Philipp Arras's avatar
Philipp Arras committed
155 156 157
        if isinstance(other, Energy):
            return EnergySum.make([self, other], [1., -1.])
        return NotImplemented
158 159 160 161

    def __neg__(self):
        from .energy_sum import EnergySum
        return EnergySum.make([self], [-1.])
162 163


Martin Reinecke's avatar
Martin Reinecke committed
164
class MetricInversionEnabler(Energy):
165
    def __init__(self, ene, controller, preconditioner):
Martin Reinecke's avatar
Martin Reinecke committed
166
        super(MetricInversionEnabler, self).__init__(ene.position)
167 168 169 170 171 172 173
        self._energy = ene
        self._controller = controller
        self._preconditioner = preconditioner

    def at(self, position):
        if self._position.isSubsetOf(position):
            return self
Martin Reinecke's avatar
Martin Reinecke committed
174
        return MetricInversionEnabler(
175 176 177 178 179 180 181 182 183 184 185 186 187 188 189
            self._energy.at(position), self._controller, self._preconditioner)

    @property
    def position(self):
        return self._energy.position

    @property
    def value(self):
        return self._energy.value

    @property
    def gradient(self):
        return self._energy.gradient

    @property
Martin Reinecke's avatar
Martin Reinecke committed
190
    def metric(self):
191 192
        from ..operators.linear_operator import LinearOperator
        from ..operators.inversion_enabler import InversionEnabler
Martin Reinecke's avatar
Martin Reinecke committed
193
        curv = self._energy.metric
194 195 196 197 198
        if self._preconditioner is None:
            precond = None
        elif isinstance(self._preconditioner, LinearOperator):
            precond = self._preconditioner
        elif isinstance(self._preconditioner, Energy):
Martin Reinecke's avatar
Martin Reinecke committed
199
            precond = self._preconditioner.at(self.position).metric
200 201 202 203
        return InversionEnabler(curv, self._controller, precond)

    def longest_step(self, dir):
        return self._energy.longest_step(dir)