Commit 08279a38 authored by Martin Reinecke's avatar Martin Reinecke

Merge branch 'NIFTy_5' into privatization

parents bfdb0c7f e1e58be3
...@@ -74,7 +74,7 @@ if __name__ == '__main__': ...@@ -74,7 +74,7 @@ if __name__ == '__main__':
ic_sampling = ift.GradientNormController(iteration_limit=100) ic_sampling = ift.GradientNormController(iteration_limit=100)
# Minimize the Hamiltonian # Minimize the Hamiltonian
H = ift.Hamiltonian(likelihood, ic_sampling) H = ift.StandardHamiltonian(likelihood, ic_sampling)
H = ift.EnergyAdapter(position, H, want_metric=True) H = ift.EnergyAdapter(position, H, want_metric=True)
# minimizer = ift.L_BFGS(ic_newton) # minimizer = ift.L_BFGS(ic_newton)
H, convergence = minimizer(H) H, convergence = minimizer(H)
......
...@@ -99,7 +99,7 @@ if __name__ == '__main__': ...@@ -99,7 +99,7 @@ if __name__ == '__main__':
minimizer = ift.NewtonCG(ic_newton) minimizer = ift.NewtonCG(ic_newton)
# Compute MAP solution by minimizing the information Hamiltonian # Compute MAP solution by minimizing the information Hamiltonian
H = ift.Hamiltonian(likelihood) H = ift.StandardHamiltonian(likelihood)
initial_position = ift.from_random('normal', domain) initial_position = ift.from_random('normal', domain)
H = ift.EnergyAdapter(initial_position, H, want_metric=True) H = ift.EnergyAdapter(initial_position, H, want_metric=True)
H, convergence = minimizer(H) H, convergence = minimizer(H)
......
...@@ -100,10 +100,10 @@ if __name__ == '__main__': ...@@ -100,10 +100,10 @@ if __name__ == '__main__':
# Set up likelihood and information Hamiltonian # Set up likelihood and information Hamiltonian
likelihood = ift.GaussianEnergy(mean=data, covariance=N)(signal_response) likelihood = ift.GaussianEnergy(mean=data, covariance=N)(signal_response)
H = ift.Hamiltonian(likelihood, ic_sampling) H = ift.StandardHamiltonian(likelihood, ic_sampling)
initial_position = ift.MultiField.full(H.domain, 0.) initial_mean = ift.MultiField.full(H.domain, 0.)
position = initial_position mean = initial_mean
plot = ift.Plot() plot = ift.Plot()
plot.add(signal(mock_position), title='Ground Truth') plot.add(signal(mock_position), title='Ground Truth')
...@@ -117,9 +117,9 @@ if __name__ == '__main__': ...@@ -117,9 +117,9 @@ if __name__ == '__main__':
# Draw new samples to approximate the KL five times # Draw new samples to approximate the KL five times
for i in range(5): for i in range(5):
# Draw new samples and minimize KL # Draw new samples and minimize KL
KL = ift.KL_Energy(position, H, N_samples) KL = ift.MetricGaussianKL(mean, H, N_samples)
KL, convergence = minimizer(KL) KL, convergence = minimizer(KL)
position = KL.position mean = KL.position
# Plot current reconstruction # Plot current reconstruction
plot = ift.Plot() plot = ift.Plot()
...@@ -128,7 +128,7 @@ if __name__ == '__main__': ...@@ -128,7 +128,7 @@ if __name__ == '__main__':
plot.output(ny=1, ysize=6, xsize=16, name="loop-{:02}.png".format(i)) plot.output(ny=1, ysize=6, xsize=16, name="loop-{:02}.png".format(i))
# Draw posterior samples # Draw posterior samples
KL = ift.KL_Energy(position, H, N_samples) KL = ift.MetricGaussianKL(mean, H, N_samples)
sc = ift.StatCalculator() sc = ift.StatCalculator()
for sample in KL.samples: for sample in KL.samples:
sc.add(signal(sample + KL.position)) sc.add(signal(sample + KL.position))
......
...@@ -103,7 +103,7 @@ N = ift.DiagonalOperator(ift.from_global_data(d_space, var)) ...@@ -103,7 +103,7 @@ N = ift.DiagonalOperator(ift.from_global_data(d_space, var))
IC = ift.DeltaEnergyController(tol_rel_deltaE=1e-12, iteration_limit=200) IC = ift.DeltaEnergyController(tol_rel_deltaE=1e-12, iteration_limit=200)
likelihood = ift.GaussianEnergy(d, N)(R) likelihood = ift.GaussianEnergy(d, N)(R)
Ham = ift.Hamiltonian(likelihood, IC) Ham = ift.StandardHamiltonian(likelihood, IC)
H = ift.EnergyAdapter(params, Ham, want_metric=True) H = ift.EnergyAdapter(params, Ham, want_metric=True)
# Minimize # Minimize
......
# rm -rf docs/build docs/source/mod
sphinx-apidoc -e -o docs/source/mod nifty5 sphinx-apidoc -e -o docs/source/mod nifty5
sphinx-build -b html docs/source/ docs/build/ sphinx-build -b html docs/source/ docs/build/
This diff is collapsed.
...@@ -19,6 +19,7 @@ from .field import Field ...@@ -19,6 +19,7 @@ from .field import Field
from .multi_field import MultiField from .multi_field import MultiField
from .operators.operator import Operator from .operators.operator import Operator
from .operators.adder import Adder
from .operators.diagonal_operator import DiagonalOperator from .operators.diagonal_operator import DiagonalOperator
from .operators.distributors import DOFDistributor, PowerDistributor from .operators.distributors import DOFDistributor, PowerDistributor
from .operators.domain_tuple_field_inserter import DomainTupleFieldInserter from .operators.domain_tuple_field_inserter import DomainTupleFieldInserter
...@@ -33,7 +34,6 @@ from .operators.field_zero_padder import FieldZeroPadder ...@@ -33,7 +34,6 @@ from .operators.field_zero_padder import FieldZeroPadder
from .operators.inversion_enabler import InversionEnabler from .operators.inversion_enabler import InversionEnabler
from .operators.linear_operator import LinearOperator from .operators.linear_operator import LinearOperator
from .operators.mask_operator import MaskOperator from .operators.mask_operator import MaskOperator
from .operators.offset_operator import OffsetOperator
from .operators.qht_operator import QHTOperator from .operators.qht_operator import QHTOperator
from .operators.regridding_operator import RegriddingOperator from .operators.regridding_operator import RegriddingOperator
from .operators.sampling_enabler import SamplingEnabler from .operators.sampling_enabler import SamplingEnabler
...@@ -49,7 +49,7 @@ from .operators.simple_linear_operators import ( ...@@ -49,7 +49,7 @@ from .operators.simple_linear_operators import (
from .operators.value_inserter import ValueInserter from .operators.value_inserter import ValueInserter
from .operators.energy_operators import ( from .operators.energy_operators import (
EnergyOperator, GaussianEnergy, PoissonianEnergy, InverseGammaLikelihood, EnergyOperator, GaussianEnergy, PoissonianEnergy, InverseGammaLikelihood,
BernoulliEnergy, Hamiltonian, AveragedEnergy) BernoulliEnergy, StandardHamiltonian, AveragedEnergy)
from .probing import probe_with_posterior_samples, probe_diagonal, \ from .probing import probe_with_posterior_samples, probe_diagonal, \
StatCalculator StatCalculator
...@@ -68,7 +68,7 @@ from .minimization.scipy_minimizer import L_BFGS_B ...@@ -68,7 +68,7 @@ from .minimization.scipy_minimizer import L_BFGS_B
from .minimization.energy import Energy from .minimization.energy import Energy
from .minimization.quadratic_energy import QuadraticEnergy from .minimization.quadratic_energy import QuadraticEnergy
from .minimization.energy_adapter import EnergyAdapter from .minimization.energy_adapter import EnergyAdapter
from .minimization.kl_energy import KL_Energy from .minimization.metric_gaussian_kl import MetricGaussianKL
from .sugar import * from .sugar import *
from .plot import Plot from .plot import Plot
......
...@@ -16,10 +16,9 @@ ...@@ -16,10 +16,9 @@
# NIFTy is being developed at the Max-Planck-Institut fuer Astrophysik. # NIFTy is being developed at the Max-Planck-Institut fuer Astrophysik.
from ..minimization.energy_adapter import EnergyAdapter from ..minimization.energy_adapter import EnergyAdapter
from ..multi_domain import MultiDomain
from ..multi_field import MultiField from ..multi_field import MultiField
from ..operators.distributors import PowerDistributor from ..operators.distributors import PowerDistributor
from ..operators.energy_operators import Hamiltonian, InverseGammaLikelihood from ..operators.energy_operators import StandardHamiltonian, InverseGammaLikelihood
from ..operators.scaling_operator import ScalingOperator from ..operators.scaling_operator import ScalingOperator
from ..operators.simple_linear_operators import ducktape from ..operators.simple_linear_operators import ducktape
...@@ -35,25 +34,27 @@ def make_adjust_variances(a, ...@@ -35,25 +34,27 @@ def make_adjust_variances(a,
Constructs a Hamiltonian to solve constant likelihood optimizations of the Constructs a Hamiltonian to solve constant likelihood optimizations of the
form phi = a * xi under the constraint that phi remains constant. form phi = a * xi under the constraint that phi remains constant.
FIXME xi is white.
Parameters Parameters
---------- ----------
a : Operator a : Operator
Operator which gives the amplitude when evaluated at a position Gives the amplitude when evaluated at a position.
xi : Operator xi : Operator
Operator which gives the excitation when evaluated at a position Gives the excitation when evaluated at a position.
position : Field, MultiField position : Field, MultiField
Position of the whole problem Position of the entire problem.
samples : Field, MultiField samples : Field, MultiField
Residual samples of the whole problem Residual samples of the whole problem.
scaling : Float scaling : Float
Optional rescaling of the Likelihood Optional rescaling of the Likelihood.
ic_samp : Controller ic_samp : Controller
Iteration Controller for Hamiltonian Iteration Controller for Hamiltonian.
Returns Returns
------- -------
Hamiltonian StandardHamiltonian
A Hamiltonian that can be used for further minimization A Hamiltonian that can be used for further minimization.
""" """
d = a*xi d = a*xi
...@@ -71,7 +72,7 @@ def make_adjust_variances(a, ...@@ -71,7 +72,7 @@ def make_adjust_variances(a,
if scaling is not None: if scaling is not None:
x = ScalingOperator(scaling, x.target)(x) x = ScalingOperator(scaling, x.target)(x)
return Hamiltonian(InverseGammaLikelihood(d_eval)(x), ic_samp=ic_samp) return StandardHamiltonian(InverseGammaLikelihood(d_eval)(x), ic_samp=ic_samp)
def do_adjust_variances(position, def do_adjust_variances(position,
...@@ -79,6 +80,9 @@ def do_adjust_variances(position, ...@@ -79,6 +80,9 @@ def do_adjust_variances(position,
minimizer, minimizer,
xi_key='xi', xi_key='xi',
samples=[]): samples=[]):
'''
FIXME
'''
h_space = position[xi_key].domain[0] h_space = position[xi_key].domain[0]
pd = PowerDistributor(h_space, amplitude_operator.target[0]) pd = PowerDistributor(h_space, amplitude_operator.target[0])
......
...@@ -24,7 +24,7 @@ from ..operators.harmonic_operators import HarmonicTransformOperator ...@@ -24,7 +24,7 @@ from ..operators.harmonic_operators import HarmonicTransformOperator
from ..operators.simple_linear_operators import ducktape from ..operators.simple_linear_operators import ducktape
def CorrelatedField(target, amplitude_operator, name='xi'): def CorrelatedField(target, amplitude_operator, name='xi', codomain=None):
"""Constructs an operator which turns a white Gaussian excitation field """Constructs an operator which turns a white Gaussian excitation field
into a correlated field. into a correlated field.
...@@ -42,16 +42,21 @@ def CorrelatedField(target, amplitude_operator, name='xi'): ...@@ -42,16 +42,21 @@ def CorrelatedField(target, amplitude_operator, name='xi'):
amplitude_operator: Operator amplitude_operator: Operator
name : string name : string
:class:`MultiField` key for the xi-field. :class:`MultiField` key for the xi-field.
codomain : Domain
The codomain for target[0]. If not supplied, it is inferred.
Returns Returns
------- -------
Correlated field : Operator Operator
Correlated field
""" """
tgt = DomainTuple.make(target) tgt = DomainTuple.make(target)
if len(tgt) > 1: if len(tgt) > 1:
raise ValueError raise ValueError
h_space = tgt[0].get_default_codomain() if codomain is None:
ht = HarmonicTransformOperator(h_space, tgt[0]) codomain = tgt[0].get_default_codomain()
h_space = codomain
ht = HarmonicTransformOperator(h_space, target=tgt[0])
p_space = amplitude_operator.target[0] p_space = amplitude_operator.target[0]
power_distributor = PowerDistributor(h_space, p_space) power_distributor = PowerDistributor(h_space, p_space)
A = power_distributor(amplitude_operator) A = power_distributor(amplitude_operator)
...@@ -70,7 +75,7 @@ def MfCorrelatedField(target, amplitudes, name='xi'): ...@@ -70,7 +75,7 @@ def MfCorrelatedField(target, amplitudes, name='xi'):
Parameters Parameters
---------- ----------
target : Domain, DomainTuple or tuple of Domain target : Domain, DomainTuple or tuple of Domain
Target of the operator. Must contain exactly one space. Target of the operator. Must contain exactly two spaces.
amplitudes: iterable of Operator amplitudes: iterable of Operator
List of two amplitude operators. List of two amplitude operators.
name : string name : string
...@@ -78,7 +83,8 @@ def MfCorrelatedField(target, amplitudes, name='xi'): ...@@ -78,7 +83,8 @@ def MfCorrelatedField(target, amplitudes, name='xi'):
Returns Returns
------- -------
Correlated field : Operator Operator
Correlated field
""" """
tgt = DomainTuple.make(target) tgt = DomainTuple.make(target)
if len(tgt) != 2: if len(tgt) != 2:
...@@ -88,7 +94,7 @@ def MfCorrelatedField(target, amplitudes, name='xi'): ...@@ -88,7 +94,7 @@ def MfCorrelatedField(target, amplitudes, name='xi'):
hsp = DomainTuple.make([tt.get_default_codomain() for tt in tgt]) hsp = DomainTuple.make([tt.get_default_codomain() for tt in tgt])
ht1 = HarmonicTransformOperator(hsp, target=tgt[0], space=0) ht1 = HarmonicTransformOperator(hsp, target=tgt[0], space=0)
ht2 = HarmonicTransformOperator(ht1.target, space=1) ht2 = HarmonicTransformOperator(ht1.target, target=tgt[1], space=1)
ht = ht2 @ ht1 ht = ht2 @ ht1
psp = [aa.target[0] for aa in amplitudes] psp = [aa.target[0] for aa in amplitudes]
......
...@@ -43,7 +43,8 @@ def _make_dynamic_operator(target, ...@@ -43,7 +43,8 @@ def _make_dynamic_operator(target,
causal, causal,
minimum_phase, minimum_phase,
sigc=None, sigc=None,
quant=None): quant=None,
codomain=None):
if not isinstance(target, RGSpace): if not isinstance(target, RGSpace):
raise TypeError("RGSpace required") raise TypeError("RGSpace required")
if not target.harmonic: if not target.harmonic:
...@@ -64,7 +65,9 @@ def _make_dynamic_operator(target, ...@@ -64,7 +65,9 @@ def _make_dynamic_operator(target,
if cone and (sigc is None or quant is None): if cone and (sigc is None or quant is None):
raise RuntimeError raise RuntimeError
dom = DomainTuple.make(target.get_default_codomain()) if codomain is None:
codomain = target.get_default_codomain()
dom = DomainTuple.make(codomain)
ops = {} ops = {}
FFT = FFTOperator(dom) FFT = FFTOperator(dom)
Real = Realizer(dom) Real = Realizer(dom)
...@@ -146,7 +149,7 @@ def dynamic_operator(*, ...@@ -146,7 +149,7 @@ def dynamic_operator(*,
minimum_phase=False): minimum_phase=False):
"""Constructs an operator encoding the Green's function of a linear """Constructs an operator encoding the Green's function of a linear
homogeneous dynamic system. homogeneous dynamic system.
When evaluated, this operator returns the Green's function representation When evaluated, this operator returns the Green's function representation
in harmonic space. This result can be used as a convolution kernel to in harmonic space. This result can be used as a convolution kernel to
construct solutions of the homogeneous stochastic differential equation construct solutions of the homogeneous stochastic differential equation
...@@ -216,7 +219,7 @@ def dynamic_lightcone_operator(*, ...@@ -216,7 +219,7 @@ def dynamic_lightcone_operator(*,
minimum_phase=False): minimum_phase=False):
'''Extends the functionality of :function: dynamic_operator to a Green's '''Extends the functionality of :function: dynamic_operator to a Green's
function which is constrained to be within a light cone. function which is constrained to be within a light cone.
The resulting Green's function is constrained to be within a light cone. The resulting Green's function is constrained to be within a light cone.
This is achieved via convolution of the function with a light cone in This is achieved via convolution of the function with a light cone in
space-time. Thereby the first axis of the space is set to be the teporal space-time. Thereby the first axis of the space is set to be the teporal
......
...@@ -20,8 +20,8 @@ import numpy as np ...@@ -20,8 +20,8 @@ import numpy as np
from ..domain_tuple import DomainTuple from ..domain_tuple import DomainTuple
from ..domains.power_space import PowerSpace from ..domains.power_space import PowerSpace
from ..field import Field from ..field import Field
from ..operators.adder import Adder
from ..operators.exp_transform import ExpTransform from ..operators.exp_transform import ExpTransform
from ..operators.offset_operator import OffsetOperator
from ..operators.qht_operator import QHTOperator from ..operators.qht_operator import QHTOperator
from ..operators.slope_operator import SlopeOperator from ..operators.slope_operator import SlopeOperator
from ..operators.symmetrizing_operator import SymmetrizingOperator from ..operators.symmetrizing_operator import SymmetrizingOperator
...@@ -29,7 +29,7 @@ from ..sugar import makeOp ...@@ -29,7 +29,7 @@ from ..sugar import makeOp
def _ceps_kernel(k, a, k0): def _ceps_kernel(k, a, k0):
return (a/(1+np.sum((k.T/k0)**2, axis=-1).T))**2 return (a/(1 + np.sum((k.T/k0)**2, axis=-1).T))**2
def CepstrumOperator(target, a, k0): def CepstrumOperator(target, a, k0):
...@@ -189,7 +189,7 @@ def SLAmplitude(*, target, n_pix, a, k0, sm, sv, im, iv, keys=['tau', 'phi']): ...@@ -189,7 +189,7 @@ def SLAmplitude(*, target, n_pix, a, k0, sm, sv, im, iv, keys=['tau', 'phi']):
sig = np.array([sv, iv]) sig = np.array([sv, iv])
mean = Field.from_global_data(sl.domain, mean) mean = Field.from_global_data(sl.domain, mean)
sig = Field.from_global_data(sl.domain, sig) sig = Field.from_global_data(sl.domain, sig)
linear = (sl @ OffsetOperator(mean) @ makeOp(sig)).ducktape(keys[1]) linear = sl @ Adder(mean) @ makeOp(sig).ducktape(keys[1])
# Combine linear and smooth component # Combine linear and smooth component
loglog_ampl = 0.5*(smooth + linear) loglog_ampl = 0.5*(smooth + linear)
......
...@@ -20,31 +20,70 @@ from ..linearization import Linearization ...@@ -20,31 +20,70 @@ from ..linearization import Linearization
from .. import utilities from .. import utilities
class KL_Energy(Energy): class MetricGaussianKL(Energy):
def __init__(self, position, h, nsamp, constants=[], """Provides the sampled Kullback-Leibler divergence between a distribution
constants_samples=None, gen_mirrored_samples=False, and a Metric Gaussian.
A Metric Gaussian is used to approximate some other distribution.
It is a Gaussian distribution that uses the Fisher Information Metric
of the other distribution at the location of its mean to approximate the
variance. In order to infer the mean, the a stochastic estimate of the
Kullback-Leibler divergence is minimized. This estimate is obtained by
drawing samples from the Metric Gaussian at the current mean.
During minimization these samples are kept constant, updating only the
mean. Due to the typically nonlinear structure of the true distribution
these samples have to be updated by re-initializing this class at some
point. Here standard parametrization of the true distribution is assumed.
Parameters
----------
mean : Field
The current mean of the Gaussian.
hamiltonian : StandardHamiltonian
The StandardHamiltonian of the approximated probability distribution.
n_samples : integer
The number of samples used to stochastically estimate the KL.
constants : list
A list of parameter keys that are kept constant during optimization.
point_estimates : list
A list of parameter keys for which no samples are drawn, but that are
optimized for, corresponding to point estimates of these.
mirror_samples : boolean
Whether the negative of the drawn samples are also used,
as they are equaly legitimate samples. If true, the number of used
samples doubles. Mirroring samples stabilizes the KL estimate as
extreme sample variation is counterbalanced. (default : False)
Notes
-----
For further details see: Metric Gaussian Variational Inference
(in preparation)
"""
def __init__(self, mean, hamiltonian, n_sampels, constants=[],
point_estimates=None, mirror_samples=False,
_samples=None): _samples=None):
super(KL_Energy, self).__init__(position) super(MetricGaussianKL, self).__init__(mean)
if h.domain is not position.domain: if hamiltonian.domain is not mean.domain:
raise TypeError raise TypeError
self._h = h self._hamiltonian = hamiltonian
self._constants = constants self._constants = constants
if constants_samples is None: if point_estimates is None:
constants_samples = constants point_estimates = constants
self._constants_samples = constants_samples self._constants_samples = point_estimates
if _samples is None: if _samples is None:
met = h(Linearization.make_partial_var( met = hamiltonian(Linearization.make_partial_var(
position, constants_samples, True)).metric mean, point_estimates, True)).metric
_samples = tuple(met.draw_sample(from_inverse=True) _samples = tuple(met.draw_sample(from_inverse=True)
for _ in range(nsamp)) for _ in range(n_sampels))
if gen_mirrored_samples: if mirror_samples:
_samples += tuple(-s for s in _samples) _samples += tuple(-s for s in _samples)
self._samples = _samples self._samples = _samples
self._lin = Linearization.make_partial_var(position, constants) self._lin = Linearization.make_partial_var(mean, constants)
v, g = None, None v, g = None, None
for s in self._samples: for s in self._samples:
tmp = self._h(self._lin+s) tmp = self._hamiltonian(self._lin+s)
if v is None: if v is None:
v = tmp.val.local_data[()] v = tmp.val.local_data[()]
g = tmp.gradient g = tmp.gradient
...@@ -56,9 +95,9 @@ class KL_Energy(Energy): ...@@ -56,9 +95,9 @@ class KL_Energy(Energy):
self._metric = None self._metric = None
def at(self, position): def at(self, position):
return KL_Energy(position, self._h, 0, return MetricGaussianKL(position, self._hamiltonian, 0,
self._constants, self._constants_samples, self._constants, self._constants_samples,
_samples=self._samples) _samples=self._samples)
@property @property
def value(self): def value(self):
...@@ -71,7 +110,8 @@ class KL_Energy(Energy): ...@@ -71,7 +110,8 @@ class KL_Energy(Energy):
def _get_metric(self): def _get_metric(self):
if self._metric is None: if self._metric is None:
lin = self._lin.with_want_metric() lin = self._lin.with_want_metric()
mymap = map(lambda v: self._h(lin+v).metric, self._samples) mymap = map(lambda v: self._hamiltonian(lin+v).metric,
self._samples)
self._metric = utilities.my_sum(mymap) self._metric = utilities.my_sum(mymap)
self._metric = self._metric.scale(1./len(self._samples)) self._metric = self._metric.scale(1./len(self._samples))
......
...@@ -15,18 +15,22 @@ ...@@ -15,18 +15,22 @@
# #
# NIFTy is being developed at the Max-Planck-Institut fuer Astrophysik. # NIFTy is being developed at the Max-Planck-Institut fuer Astrophysik.
from ..field import Field
from ..multi_field import MultiField
from .operator import Operator from .operator import Operator
class OffsetOperator(Operator): class Adder(Operator):
"""Shifts the input by a fixed field. """Adds a fixed field.
Parameters Parameters
---------- ----------
field : Field field : Field or MultiField
The field by which the input is shifted. The field by which the input is shifted.
""" """
def __init__(self, field): def __init__(self, field):
if not isinstance(field, (Field, MultiField)):
raise TypeError
self._field = field self._field = field
self._domain = self._target = field.domain self._domain = self._target = field.domain
......