test_fisher_metric.py 4 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# 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-2020 Max-Planck-Society
#
# NIFTy is being developed at the Max-Planck-Institut fuer Astrophysik.

import numpy as np
import pytest

import nifty6 as ift

from ..common import list2fixture, setup_function, teardown_function

spaces = [ift.GLSpace(5),
          (ift.RGSpace(3, distances=.789), ift.UnstructuredDomain(2))]
pmp = pytest.mark.parametrize
field = list2fixture([ift.from_random(sp, 'normal') for sp in spaces] +
        [ift.from_random(sp, 'normal', dtype=np.complex128) for sp in spaces])

Reimar Leike's avatar
Reimar Leike committed
31
Nsamp = 2000
Reimar Leike's avatar
Reimar Leike committed
32
np.random.seed(42)
33
34
35
36
37
38
39

def _to_array(d):
    if isinstance(d, np.ndarray):
        return d
    assert isinstance(d, dict)
    return np.concatenate(list(d.values()))

40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
def _complex2real(sp):
    tup = tuple([d for d in sp])
    rsp = ift.DomainTuple.make((ift.UnstructuredDomain(2),) + tup)
    rl = ift.DomainTupleFieldInserter(rsp, 0, (0,))
    im = ift.DomainTupleFieldInserter(rsp, 0, (1,))
    x = ift.ScalingOperator(sp, 1)
    return rl(x.real)+im(x.imag)

def test_complex2real():
    sp = ift.UnstructuredDomain(3)
    op = _complex2real(ift.makeDomain(sp))
    f = ift.from_random(op.domain, 'normal', dtype=np.complex128)
    assert np.all((f == op.adjoint_times(op(f))).val)
    assert op(f).dtype == np.float64
    f = ift.from_random(op.target, 'normal')
    assert np.all((f == op(op.adjoint_times(f))).val)
    
def energy_tester_complex(pos, get_noisy_data, energy_initializer):
    op = _complex2real(pos.domain)
    npos = op(pos)
    nget_noisy_data = lambda mean : get_noisy_data(op.adjoint_times(mean))
    nenergy_initializer = lambda mean : energy_initializer(mean) @ op.adjoint
    energy_tester(npos, nget_noisy_data, nenergy_initializer)

64
def energy_tester(pos, get_noisy_data, energy_initializer):
65
66
67
    if np.issubdtype(pos.dtype, np.complexfloating):
        energy_tester_complex(pos, get_noisy_data, energy_initializer)
        return
68
69
70
71
72
73
74
75
76
    domain = pos.domain
    test_vec = ift.from_random(domain, 'normal')
    results = []
    lin = ift.Linearization.make_var(pos)
    for i in range(Nsamp):
        data = get_noisy_data(pos)
        energy = energy_initializer(data)
        grad = energy(lin).jac.adjoint(ift.full(energy.target, 1.))
        results.append(_to_array((grad*grad.s_vdot(test_vec)).val))
77
78
    print(energy)
    print(grad)
79
80
81
82
83
    res = np.mean(np.array(results), axis=0)
    std = np.std(np.array(results), axis=0)/np.sqrt(Nsamp)
    energy = energy_initializer(data)
    lin = ift.Linearization.make_var(pos, want_metric=True)
    res2 = _to_array(energy(lin).metric(test_vec).val)
Reimar Leike's avatar
Reimar Leike committed
84
    np.testing.assert_allclose(res/std, res2/std, atol=6)
85
86
87

def test_GaussianEnergy(field):
    dtype = field.dtype
88

89
90
    icov = ift.from_random(field.domain, 'normal')**2
    icov = ift.makeOp(icov)
91
    get_noisy_data = lambda mean : mean + icov.draw_sample_with_dtype(
92
93
94
95
96
97
98
99
100
101
102
103
            from_inverse=True, dtype=dtype)
    E_init = lambda mean : ift.GaussianEnergy(mean=mean,
            inverse_covariance=icov)
    energy_tester(field, get_noisy_data, E_init)

def test_PoissonEnergy(field):
    if not isinstance(field, ift.Field):
        return
    if np.iscomplexobj(field.val):
        return
    def get_noisy_data(mean):
        return ift.makeField(mean.domain, np.random.poisson(mean.val))
104
    lam = 10*(field**2).clip(0.1,None) # make rate positive and high enough to avoid bad statistic
105
106
107
    E_init = lambda mean : ift.PoissonianEnergy(mean)
    energy_tester(lam, get_noisy_data, E_init)