energy_and_model_tests.py 4.48 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
# 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.

19
20
from __future__ import absolute_import, division, print_function
from ..compat import *
21
import numpy as np
Martin Reinecke's avatar
Martin Reinecke committed
22
from ..sugar import from_random
Martin Reinecke's avatar
tweak    
Martin Reinecke committed
23
24
from ..minimization.energy import Energy
from ..models.model import Model
25

Martin Reinecke's avatar
Martin Reinecke committed
26
__all__ = ["check_value_gradient_consistency",
Martin Reinecke's avatar
Martin Reinecke committed
27
           "check_value_gradient_metric_consistency"]
28

Philipp Arras's avatar
Philipp Arras committed
29

30
31
32
33
34
def _get_acceptable_model(M):
    val = M.value
    if not np.isfinite(val.sum()):
        raise ValueError('Initial Model value must be finite')
    dir = from_random("normal", M.position.domain)
35
    dirder = M.jacobian(dir)
36
    if dirder.norm() == 0:
37
        dir = dir * val.norm() * 1e-5
38
    else:
39
        dir = dir * val.norm() * (1e-5/dirder.norm())
Philipp Arras's avatar
Philipp Arras committed
40
    # Find a step length that leads to a "reasonable" Model
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
48
        dir = dir*0.5
49
50
51
52
    else:
        raise ValueError("could not find a reasonable initial step")
    return M2

53

Martin Reinecke's avatar
Martin Reinecke committed
54
def _get_acceptable_energy(E):
55
56
    val = E.value
    if not np.isfinite(val):
57
        raise ValueError('Initial Energy must be finite')
58
    dir = from_random("normal", E.position.domain)
59
    dirder = E.gradient.vdot(dir)
60
    dir = dir * (np.abs(val)/np.abs(dirder)*1e-5)
Philipp Arras's avatar
Philipp Arras committed
61
    # Find a step length that leads to a "reasonable" energy
Martin Reinecke's avatar
Martin Reinecke committed
62
63
64
65
66
67
68
    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
69
        dir = dir*0.5
Martin Reinecke's avatar
Martin Reinecke committed
70
71
72
73
74
    else:
        raise ValueError("could not find a reasonable initial step")
    return E2


Martin Reinecke's avatar
Martin Reinecke committed
75
def check_value_gradient_consistency(E, tol=1e-8, ntries=100):
76
    for _ in range(ntries):
77
78
79
80
        if isinstance(E, Energy):
            E2 = _get_acceptable_energy(E)
        else:
            E2 = _get_acceptable_model(E)
81
        val = E.value
Martin Reinecke's avatar
Martin Reinecke committed
82
        dir = E2.position - E.position
Martin Reinecke's avatar
tweak    
Martin Reinecke committed
83
        Enext = E2
Martin Reinecke's avatar
Martin Reinecke committed
84
        dirnorm = dir.norm()
85
        for i in range(50):
Martin Reinecke's avatar
Martin Reinecke committed
86
            Emid = E.at(E.position + 0.5*dir)
87
88
89
            if isinstance(E, Energy):
                dirder = Emid.gradient.vdot(dir)/dirnorm
            else:
90
                dirder = Emid.jacobian(dir)/dirnorm
91
            numgrad = (E2.value-val)/dirnorm
92
            if isinstance(E, Model):
93
                xtol = tol * dirder.norm() / np.sqrt(dirder.size)
94
                if (abs(numgrad-dirder) <= xtol).all():
95
96
97
                    break
            else:
                xtol = tol*Emid.gradient_norm
98
                if abs(numgrad-dirder) <= xtol:
99
                    break
100
            dir = dir*0.5
Martin Reinecke's avatar
Martin Reinecke committed
101
            dirnorm *= 0.5
Martin Reinecke's avatar
Martin Reinecke committed
102
            E2 = Emid
103
        else:
Martin Reinecke's avatar
Martin Reinecke committed
104
            raise ValueError("gradient and value seem inconsistent")
Martin Reinecke's avatar
tweak    
Martin Reinecke committed
105
        E = Enext
Martin Reinecke's avatar
Martin Reinecke committed
106
107


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