energy_and_model_tests.py 4.28 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19
# 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/>.
#
# Copyright(C) 2013-2018 Max-Planck-Society
#
# NIFTy is being developed at the Max-Planck-Institut fuer Astrophysik
# and financially supported by the Studienstiftung des deutschen Volkes.

import numpy as np
Martin Reinecke's avatar
Martin Reinecke committed
20
from ..sugar import from_random
21
from .. import Energy, Model
22

Martin Reinecke's avatar
Martin Reinecke committed
23 24
__all__ = ["check_value_gradient_consistency",
           "check_value_gradient_curvature_consistency"]
25

Philipp Arras's avatar
Philipp Arras committed
26

27 28 29 30 31 32 33 34
def _get_acceptable_model(M):
    # TODO: do for model
    val = M.value
    if not np.isfinite(val.sum()):
        raise ValueError('Initial Model value must be finite')
    dir = from_random("normal", M.position.domain)
    dirder = M.gradient(dir)
    dir *= val/(dirder).norm()*1e-5
Philipp Arras's avatar
Philipp Arras committed
35
    # Find a step length that leads to a "reasonable" Model
36 37 38 39 40 41 42 43 44 45 46 47
    for i in range(50):
        try:
            M2 = M.at(M.position+dir)
            if np.isfinite(M2.value.sum()) and abs(M2.value.sum()) < 1e20:
                break
        except FloatingPointError:
            pass
        dir *= 0.5
    else:
        raise ValueError("could not find a reasonable initial step")
    return M2

48

Martin Reinecke's avatar
Martin Reinecke committed
49
def _get_acceptable_energy(E):
50 51
    val = E.value
    if not np.isfinite(val):
52
        raise ValueError('Initial Energy must be finite')
53
    dir = from_random("normal", E.position.domain)
54 55
    dirder = E.gradient.vdot(dir)
    dir *= np.abs(val)/np.abs(dirder)*1e-5
Philipp Arras's avatar
Philipp Arras committed
56
    # Find a step length that leads to a "reasonable" energy
Martin Reinecke's avatar
Martin Reinecke committed
57 58 59 60 61 62 63 64 65 66 67 68 69
    for i in range(50):
        try:
            E2 = E.at(E.position+dir)
            if np.isfinite(E2.value) and abs(E2.value) < 1e20:
                break
        except FloatingPointError:
            pass
        dir *= 0.5
    else:
        raise ValueError("could not find a reasonable initial step")
    return E2


Martin Reinecke's avatar
Martin Reinecke committed
70
def check_value_gradient_consistency(E, tol=1e-8, ntries=100):
71
    for _ in range(ntries):
72 73 74 75
        if isinstance(E, Energy):
            E2 = _get_acceptable_energy(E)
        else:
            E2 = _get_acceptable_model(E)
76
        val = E.value
Martin Reinecke's avatar
Martin Reinecke committed
77
        dir = E2.position - E.position
Philipp Arras's avatar
Philipp Arras committed
78
        # Enext = E2
Martin Reinecke's avatar
Martin Reinecke committed
79
        dirnorm = dir.norm()
80
        for i in range(50):
Martin Reinecke's avatar
Martin Reinecke committed
81
            Emid = E.at(E.position + 0.5*dir)
82 83 84 85 86
            if isinstance(E, Energy):
                dirder = Emid.gradient.vdot(dir)/dirnorm
            else:
                dirder = Emid.gradient(dir)/dirnorm
            if isinstance(E, Model):
Philipp Arras's avatar
Philipp Arras committed
87
                # TODO: use relative error here instead
88 89 90 91 92 93
                if ((E2.value-val)/dirnorm - dirder).norm()/dirder.norm()< tol:
                    break
            else:
                xtol = tol*Emid.gradient_norm
                if abs((E2.value-val)/dirnorm - dirder) < xtol:
                    break
94
            dir *= 0.5
Martin Reinecke's avatar
Martin Reinecke committed
95
            dirnorm *= 0.5
Martin Reinecke's avatar
Martin Reinecke committed
96
            E2 = Emid
97
        else:
Martin Reinecke's avatar
Martin Reinecke committed
98 99 100 101
            raise ValueError("gradient and value seem inconsistent")
        # E = Enext


Martin Reinecke's avatar
Martin Reinecke committed
102
def check_value_gradient_curvature_consistency(E, tol=1e-8, ntries=100):
103 104
    if isinstance(E, Model):
        raise ValueError('Models have no curvature, thus it cannot be tested.')
Martin Reinecke's avatar
Martin Reinecke committed
105 106
    for _ in range(ntries):
        E2 = _get_acceptable_energy(E)
107
        val = E.value
Martin Reinecke's avatar
Martin Reinecke committed
108
        dir = E2.position - E.position
Philipp Arras's avatar
Philipp Arras committed
109
        # Enext = E2
Martin Reinecke's avatar
Martin Reinecke committed
110
        dirnorm = dir.norm()
111
        for i in range(50):
112 113 114
            Emid = E.at(E.position + 0.5*dir)
            dirder = Emid.gradient.vdot(dir)/dirnorm
            dgrad = Emid.curvature(dir)/dirnorm
Martin Reinecke's avatar
Martin Reinecke committed
115 116 117
            xtol = tol*Emid.gradient_norm
            if abs((E2.value-val)/dirnorm - dirder) < xtol and \
               (abs((E2.gradient-E.gradient)/dirnorm-dgrad) < xtol).all():
118 119
                break
            dir *= 0.5
Martin Reinecke's avatar
Martin Reinecke committed
120
            dirnorm *= 0.5
121
            E2 = Emid
122
        else:
Martin Reinecke's avatar
Martin Reinecke committed
123 124
            raise ValueError("gradient, value and curvature seem inconsistent")
        # E = Enext