Commit 943811e7 authored by Martin Reinecke's avatar Martin Reinecke

Merge remote-tracking branch 'origin/NIFTy_5' into spectral2

parents c34e168b 04f044dc
......@@ -51,18 +51,14 @@ test_mpi:
- mpiexec -n 2 --bind-to none pytest-3 -q test
pages:
stage: release
before_script:
- ls
# FIXME Build only for main branch and set stage to release
stage: test
script:
- python3 setup.py install --user -f
- sh docs/generate.sh
- mv docs/build/ public/
artifacts:
paths:
- public
only:
- NIFTy_4
before_script:
- python3 setup.py install --user -f
......
......@@ -6,7 +6,7 @@ RUN apt-get update && apt-get install -y \
# Packages needed for NIFTy
python3-scipy \
# Documentation build dependencies
python3-sphinx-rtd-theme \
python3-sphinx-rtd-theme dvipng texlive-latex-base texlive-latex-extra \
# Testing dependencies
python3-pytest-cov jupyter \
# Optional NIFTy dependencies
......
......@@ -29,4 +29,6 @@ add_module_names = False
html_theme = "sphinx_rtd_theme"
html_logo = 'nifty_logo_black.png'
exclude_patterns = ['mod/modules.rst', 'mod/*version.rst']
exclude_patterns = [
'mod/modules.rst', 'mod/nifty5.git_version.rst', 'mod/nifty5.logger.rst'
]
......@@ -14,12 +14,12 @@ Plotting support is added via::
pip3 install --user matplotlib
FFTW support is added via:
FFTW support is added via::
sudo apt-get install libfftw3-dev
pip3 install --user pyfftw
To actually use FFTW in your Nifty calculations, you need to call
To actually use FFTW in your Nifty calculations, you need to call::
nifty5.fft.enable_fftw()
......
......@@ -22,8 +22,7 @@ from .linearization import Linearization
from .operators.linear_operator import LinearOperator
from .sugar import from_random
__all__ = ["consistency_check", "check_value_gradient_consistency",
"check_value_gradient_metric_consistency"]
__all__ = ["consistency_check", "check_jacobian_consistency"]
def _assert_allclose(f1, f2, atol, rtol):
......@@ -74,25 +73,31 @@ def _check_linearity(op, domain_dtype, atol, rtol):
def consistency_check(op, domain_dtype=np.float64, target_dtype=np.float64,
atol=0, rtol=1e-7):
"""Checks whether times(), adjoint_times(), inverse_times() and
"""
Checks an operator for algebraic consistency of its capabilities.
Checks whether times(), adjoint_times(), inverse_times() and
adjoint_inverse_times() (if in capability list) is implemented
consistently. Additionally, it checks whether the operator is linear
actually.
consistently. Additionally, it checks whether the operator is linear.
Parameters
----------
op : LinearOperator
Operator which shall be checked.
domain_dtype : FIXME
domain_dtype : dtype
The data type of the random vectors in the operator's domain. Default
is `np.float64`.
target_dtype : FIXME
target_dtype : dtype
The data type of the random vectors in the operator's target. Default
is `np.float64`.
atol : float
FIXME. Default is 0.
Absolute tolerance for the check. If rtol is specified,
then satisfying any tolerance will let the check pass.
Default: 0.
rtol : float
FIXME. Default is 0.
Relative tolerance for the check. If atol is specified,
then satisfying any tolerance will let the check pass.
Default: 0.
"""
if not isinstance(op, LinearOperator):
raise TypeError('This test tests only linear operators.')
......@@ -128,25 +133,37 @@ def _get_acceptable_location(op, loc, lin):
return loc2, lin2
def _check_consistency(op, loc, tol, ntries, do_metric):
def check_jacobian_consistency(op, loc, tol=1e-8, ntries=100):
"""
Checks the Jacobian of an operator against its finite difference
approximation.
Computes the Jacobian with finite differences and compares it to the
implemented Jacobian.
Parameters
----------
op : Operator
Operator which shall be checked.
loc : Field or MultiField
An Field or MultiField instance which has the same domain
as op. The location at which the gradient is checked
tol : float
Tolerance for the check.
"""
for _ in range(ntries):
lin = op(Linearization.make_var(loc, do_metric))
lin = op(Linearization.make_var(loc))
loc2, lin2 = _get_acceptable_location(op, loc, lin)
dir = loc2-loc
locnext = loc2
dirnorm = dir.norm()
for i in range(50):
locmid = loc + 0.5*dir
linmid = op(Linearization.make_var(locmid, do_metric))
linmid = op(Linearization.make_var(locmid))
dirder = linmid.jac(dir)
numgrad = (lin2.val-lin.val)
xtol = tol * dirder.norm() / np.sqrt(dirder.size)
cond = (abs(numgrad-dirder) <= xtol).all()
if do_metric:
dgrad = linmid.metric(dir)
dgrad2 = (lin2.gradient-lin.gradient)
cond = cond and (abs(dgrad-dgrad2) <= xtol).all()
if cond:
if (abs(numgrad-dirder) <= xtol).all():
break
dir = dir*0.5
dirnorm *= 0.5
......@@ -155,12 +172,3 @@ def _check_consistency(op, loc, tol, ntries, do_metric):
raise ValueError("gradient and value seem inconsistent")
loc = locnext
def check_value_gradient_consistency(op, loc, tol=1e-8, ntries=100):
"""FIXME"""
_check_consistency(op, loc, tol, ntries, False)
def check_value_gradient_metric_consistency(op, loc, tol=1e-8, ntries=100):
"""FIXME"""
_check_consistency(op, loc, tol, ntries, True)
......@@ -36,8 +36,8 @@ class Field(object):
This object's global shape must match the domain shape
After construction, the object will no longer be writeable!
Note
----
Notes
-----
If possible, do not invoke the constructor directly, but use one of the
many convenience functions for instantiation!
"""
......@@ -190,8 +190,8 @@ class Field(object):
def val(self):
"""dobj.data_object : the data object storing the field's entries.
Note
----
Notes
-----
This property is intended for low-level, internal use only. Do not use
from outside of NIFTy's core; there should be better alternatives.
"""
......
......@@ -50,6 +50,13 @@ def CorrelatedField(target, amplitude_operator, name='xi', codomain=None):
-------
Operator
Correlated field
Notes
-----
In NIFTy, non-harmonic RGSpaces are by definition periodic. Therefore
the operator constructed by this method will output a correlated field
with *periodic* boundary conditions. If a non-periodic field is needed,
one needs to combine this operator with a :class:`FieldZeroPadder`.
"""
tgt = DomainTuple.make(target)
if len(tgt) > 1:
......@@ -90,6 +97,14 @@ def MfCorrelatedField(target, amplitudes, name='xi'):
-------
Operator
Correlated field
Notes
-----
In NIFTy, non-harmonic RGSpaces are by definition periodic. Therefore
the operator constructed by this method will output a correlated field
with *periodic* boundary conditions. If a non-periodic field is needed,
one needs to combine this operator with a :class:`FieldZeroPadder` or even
two (one for the energy and one for the spatial subdomain)
"""
tgt = DomainTuple.make(target)
if len(tgt) != 2:
......
......@@ -217,7 +217,7 @@ def dynamic_lightcone_operator(*,
quant,
causal=True,
minimum_phase=False):
'''Extends the functionality of :function: dynamic_operator to a Green's
'''Extends the functionality of :func:`dynamic_operator` to a Green's
function which is constrained to be within a light cone.
The resulting Green's function is constrained to be within a light cone.
......
......@@ -96,12 +96,39 @@ def _cone_arrays(c, domain, sigx, want_gradient):
class LightConeOperator(Operator):
'''
FIXME
'''Constructs a Light cone from a set of lightspeed parameters.
The resulting cone is defined as follows
.. math::
\\exp \\left(- \\frac{1}{2} \\Re \\left( \\Delta \\right)^2 \\right)
with
.. math::
\\Delta = \\sqrt{- \\left(t^2 - \\frac{x^\\dagger C^{-1} x}
{\\sigma_x^2} \\right)}
where t and x are the coordinates of the target space. Note that axis zero
of the space is interpreted as the time axis. C denotes the input
paramters of the operator and parametrizes the shape of the cone.
sigx is the width of the asymptotic Gaussian in x necessary for
discretization.
Parameters
----------
domain : Domain, tuple of Domain or DomainTuple
The domain of the input parameters of the light cone, the values of the
lightspeed tensor.
target : Domain, tuple of Domain or DomainTuple
Output space on which the lightcone should be defined. The zeroth axis
of this space is interpreted as the time axis.
sigx : float
Width of the Gaussian for the discretized representation of the cone.
'''
def __init__(self, domain, target, sigx):
self._domain = domain
self._target = target
self._domain = DomainTuple.make(domain)
self._target = DomainTuple.make(target)
self._sigx = sigx
def apply(self, x):
......
......@@ -57,10 +57,10 @@ class MetricGaussianKL(Energy):
Notes
-----
For further details see: Metric Gaussian Variational Inference
(in preparation)
(FIXME in preparation)
"""
def __init__(self, mean, hamiltonian, n_sampels, constants=[],
def __init__(self, mean, hamiltonian, n_samples, constants=[],
point_estimates=None, mirror_samples=False,
_samples=None):
super(MetricGaussianKL, self).__init__(mean)
......@@ -75,7 +75,7 @@ class MetricGaussianKL(Energy):
met = hamiltonian(Linearization.make_partial_var(
mean, point_estimates, True)).metric
_samples = tuple(met.draw_sample(from_inverse=True)
for _ in range(n_sampels))
for _ in range(n_samples))
if mirror_samples:
_samples += tuple(-s for s in _samples)
self._samples = _samples
......
......@@ -44,7 +44,7 @@ def field(request):
def test_gaussian(field):
energy = ift.GaussianEnergy(domain=field.domain)
ift.extra.check_value_gradient_consistency(energy, field)
ift.extra.check_jacobian_consistency(energy, field)
def test_inverse_gamma(field):
......@@ -53,7 +53,7 @@ def test_inverse_gamma(field):
d = np.random.normal(10, size=space.shape)**2
d = ift.Field.from_global_data(space, d)
energy = ift.InverseGammaLikelihood(d)
ift.extra.check_value_gradient_consistency(energy, field, tol=1e-7)
ift.extra.check_jacobian_consistency(energy, field, tol=1e-7)
def testPoissonian(field):
......@@ -62,7 +62,7 @@ def testPoissonian(field):
d = np.random.poisson(120, size=space.shape)
d = ift.Field.from_global_data(space, d)
energy = ift.PoissonianEnergy(d)
ift.extra.check_value_gradient_consistency(energy, field, tol=1e-7)
ift.extra.check_jacobian_consistency(energy, field, tol=1e-7)
def test_hamiltonian_and_KL(field):
......@@ -70,11 +70,11 @@ def test_hamiltonian_and_KL(field):
space = field.domain
lh = ift.GaussianEnergy(domain=space)
hamiltonian = ift.StandardHamiltonian(lh)
ift.extra.check_value_gradient_consistency(hamiltonian, field)
ift.extra.check_jacobian_consistency(hamiltonian, field)
S = ift.ScalingOperator(1., space)
samps = [S.draw_sample() for i in range(3)]
kl = ift.AveragedEnergy(hamiltonian, samps)
ift.extra.check_value_gradient_consistency(kl, field)
ift.extra.check_jacobian_consistency(kl, field)
def test_bernoulli(field):
......@@ -83,4 +83,4 @@ def test_bernoulli(field):
d = np.random.binomial(1, 0.1, size=space.shape)
d = ift.Field.from_global_data(space, d)
energy = ift.BernoulliEnergy(d)
ift.extra.check_value_gradient_consistency(energy, field, tol=1e-6)
ift.extra.check_jacobian_consistency(energy, field, tol=1e-6)
......@@ -45,8 +45,6 @@ def test_gaussian_energy(space, nonlinearity, noise, seed):
pspace = ift.PowerSpace(hspace, binbounds=binbounds)
Dist = ift.PowerDistributor(target=hspace, power_space=pspace)
xi0 = ift.Field.from_random(domain=hspace, random_type='normal')
# FIXME Needed?
xi0_var = ift.Linearization.make_var(xi0)
def pspec(k):
return 1/(1 + k**2)**dim
......@@ -55,8 +53,6 @@ def test_gaussian_energy(space, nonlinearity, noise, seed):
A = Dist(ift.sqrt(pspec))
N = ift.ScalingOperator(noise, space)
n = N.draw_sample()
# FIXME Needed?
s = ht(ift.makeOp(A)(xi0_var))
R = ift.ScalingOperator(10., space)
def d_model():
......@@ -73,9 +69,5 @@ def test_gaussian_energy(space, nonlinearity, noise, seed):
N = None
energy = ift.GaussianEnergy(d, N)(d_model())
if nonlinearity == "":
ift.extra.check_value_gradient_metric_consistency(
energy, xi0, ntries=10)
else:
ift.extra.check_value_gradient_consistency(
energy, xi0, ntries=10, tol=5e-8)
ift.extra.check_jacobian_consistency(
energy, xi0, ntries=10, tol=5e-8)
......@@ -47,7 +47,7 @@ def _make_linearization(type, space, seed):
def testBasics(space, seed):
var = _make_linearization("Variable", space, seed)
model = ift.ScalingOperator(6., var.target)
ift.extra.check_value_gradient_consistency(model, var.val)
ift.extra.check_jacobian_consistency(model, var.val)
@pmp('type1', ['Variable', 'Constant'])
......@@ -65,32 +65,32 @@ def testBinary(type1, type2, space, seed):
select_s2 = ift.ducktape(None, dom, "s2")
model = select_s1*select_s2
pos = ift.from_random("normal", dom)
ift.extra.check_value_gradient_consistency(model, pos, ntries=20)
ift.extra.check_jacobian_consistency(model, pos, ntries=20)
model = select_s1 + select_s2
pos = ift.from_random("normal", dom)
ift.extra.check_value_gradient_consistency(model, pos, ntries=20)
ift.extra.check_jacobian_consistency(model, pos, ntries=20)
model = select_s1.scale(3.)
pos = ift.from_random("normal", dom1)
ift.extra.check_value_gradient_consistency(model, pos, ntries=20)
ift.extra.check_jacobian_consistency(model, pos, ntries=20)
model = ift.ScalingOperator(2.456, space)(select_s1*select_s2)
pos = ift.from_random("normal", dom)
ift.extra.check_value_gradient_consistency(model, pos, ntries=20)
ift.extra.check_jacobian_consistency(model, pos, ntries=20)
model = ift.sigmoid(2.456*(select_s1*select_s2))
pos = ift.from_random("normal", dom)
ift.extra.check_value_gradient_consistency(model, pos, ntries=20)
ift.extra.check_jacobian_consistency(model, pos, ntries=20)
pos = ift.from_random("normal", dom)
model = ift.OuterProduct(pos['s1'], ift.makeDomain(space))
ift.extra.check_value_gradient_consistency(model, pos['s2'], ntries=20)
ift.extra.check_jacobian_consistency(model, pos['s2'], ntries=20)
model = select_s1**2
pos = ift.from_random("normal", dom1)
ift.extra.check_value_gradient_consistency(model, pos, ntries=20)
ift.extra.check_jacobian_consistency(model, pos, ntries=20)
model = select_s1.clip(-1, 1)
pos = ift.from_random("normal", dom1)
ift.extra.check_value_gradient_consistency(model, pos, ntries=20)
ift.extra.check_jacobian_consistency(model, pos, ntries=20)
if isinstance(space, ift.RGSpace):
model = ift.FFTOperator(space)(select_s1*select_s2)
pos = ift.from_random("normal", dom)
ift.extra.check_value_gradient_consistency(model, pos, ntries=20)
ift.extra.check_jacobian_consistency(model, pos, ntries=20)
def testModelLibrary(space, seed):
......@@ -102,18 +102,18 @@ def testModelLibrary(space, seed):
assert_(isinstance(model, ift.Operator))
S = ift.ScalingOperator(1., model.domain)
pos = S.draw_sample()
ift.extra.check_value_gradient_consistency(model, pos, ntries=20)
ift.extra.check_jacobian_consistency(model, pos, ntries=20)
model2 = ift.CorrelatedField(space, model)
S = ift.ScalingOperator(1., model2.domain)
pos = S.draw_sample()
ift.extra.check_value_gradient_consistency(model2, pos, ntries=20)
ift.extra.check_jacobian_consistency(model2, pos, ntries=20)
domtup = ift.DomainTuple.make((space, space))
model3 = ift.MfCorrelatedField(domtup, [model, model])
S = ift.ScalingOperator(1., model3.domain)
pos = S.draw_sample()
ift.extra.check_value_gradient_consistency(model3, pos, ntries=20)
ift.extra.check_jacobian_consistency(model3, pos, ntries=20)
def testPointModel(space, seed):
......@@ -123,7 +123,7 @@ def testPointModel(space, seed):
q = 0.73
model = ift.InverseGammaOperator(space, alpha, q)
# FIXME All those cdfs and ppfs are not very accurate
ift.extra.check_value_gradient_consistency(model, pos, tol=1e-2, ntries=20)
ift.extra.check_jacobian_consistency(model, pos, tol=1e-2, ntries=20)
@pmp('target', [
......@@ -148,7 +148,7 @@ def testDynamicModel(target, causal, minimum_phase, seed):
S = ift.ScalingOperator(1., model.domain)
pos = S.draw_sample()
# FIXME I dont know why smaller tol fails for 3D example
ift.extra.check_value_gradient_consistency(model, pos, tol=1e-5, ntries=20)
ift.extra.check_jacobian_consistency(model, pos, tol=1e-5, ntries=20)
if len(target.shape) > 1:
dct = {
'target': target,
......@@ -169,5 +169,5 @@ def testDynamicModel(target, causal, minimum_phase, seed):
S = ift.ScalingOperator(1., model.domain)
pos = S.draw_sample()
# FIXME I dont know why smaller tol fails for 3D example
ift.extra.check_value_gradient_consistency(
ift.extra.check_jacobian_consistency(
model, pos, tol=1e-5, ntries=20)
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