Commit 7e28bd27 authored by ru87him's avatar ru87him Committed by Philipp Arras
Browse files

Implemented Uniformoperator and generalised InterpolationOperator to supply the correct jacobian

parent 35cf799b
...@@ -72,7 +72,7 @@ from .sugar import * ...@@ -72,7 +72,7 @@ from .sugar import *
from .plot import Plot from .plot import Plot
from .library.special_distributions import InverseGammaOperator from .library.special_distributions import InverseGammaOperator, UniformOperator
from .library.los_response import LOSResponse from .library.los_response import LOSResponse
from .library.dynamic_operator import (dynamic_operator, from .library.dynamic_operator import (dynamic_operator,
dynamic_lightcone_operator) dynamic_lightcone_operator)
......
...@@ -23,20 +23,59 @@ from ..field import Field ...@@ -23,20 +23,59 @@ from ..field import Field
from ..linearization import Linearization from ..linearization import Linearization
from ..operators.operator import Operator from ..operators.operator import Operator
from ..sugar import makeOp from ..sugar import makeOp
from .. import Adder
class _InterpolationOperator(Operator): class _InterpolationOperator(Operator):
def __init__(self, domain, func, xmin, xmax, delta, table_func=None, inverse_table_func=None): """
Calculates a function pointwise on a field by interpolation.
Can be supplied with a table_func to increase the interpolation accuracy,
Best results are achieved when table_func(func) is roughly linear.
Parameters
----------
domain : Domain, tuple of Domain or DomainTuple
The domain on which the field shall be defined. This is at the same
time the domain and the target of the operator.
func : function
The function which is applied on the field.
xmin : float
The smallest value for which func will be evaluated.
xmax : float
The largest value for which func will be evaluated.
delta : float
Distance between sampling points for linear interpolation.
table_func : {'None', 'exp', 'log', 'power'}
exponent : float
This is only used by the 'power' table_func.
"""
def __init__(self, domain, func, xmin, xmax, delta, table_func=None, exponent=None):
self._domain = self._target = DomainTuple.make(domain) self._domain = self._target = DomainTuple.make(domain)
self._xmin, self._xmax = float(xmin), float(xmax) self._xmin, self._xmax = float(xmin), float(xmax)
self._d = float(delta) self._d = float(delta)
self._xs = np.arange(xmin, xmax+2*self._d, self._d) self._xs = np.arange(xmin, xmax+2*self._d, self._d)
if table_func is not None: self._table = func(self._xs)
foo = func(np.random.randn(10)) self._transform = table_func is not None
np.testing.assert_allclose(foo, inverse_table_func(table_func(foo))) self._args = []
self._table = table_func(func(self._xs)) if exponent is not None and table_func is not 'power':
self._deriv = (self._table[1:]-self._table[:-1]) / self._d raise Exception("exponent is only used when table_func is 'power'.")
self._inv_table_func = inverse_table_func if table_func is None:
pass
elif table_func == 'exp':
self._table = np.exp(self._table)
self._inv_table_func = 'log'
elif table_func == 'log':
self._table = np.log(self._table)
self._inv_table_func = 'exp'
elif table_func == 'power':
if not np.isscalar(exponent):
return NotImplemented
self._table = np.power(self._table, exponent)
self._inv_table_func = '__pow__'
self._args = [np.power(float(exponent), -1)]
else:
return NotImplemented
self._deriv = (self._table[1:] - self._table[:-1]) / self._d
def apply(self, x): def apply(self, x):
self._check_input(x) self._check_input(x)
...@@ -45,16 +84,21 @@ class _InterpolationOperator(Operator): ...@@ -45,16 +84,21 @@ class _InterpolationOperator(Operator):
val = (np.clip(xval, self._xmin, self._xmax) - self._xmin) / self._d val = (np.clip(xval, self._xmin, self._xmax) - self._xmin) / self._d
fi = np.floor(val).astype(int) fi = np.floor(val).astype(int)
w = val - fi w = val - fi
res = self._inv_table_func((1-w)*self._table[fi] + w*self._table[fi+1]) res = (1-w)*self._table[fi] + w*self._table[fi+1]
resfld = Field(self._domain, res) resfld = Field(self._domain, res)
if not lin: if not lin:
if self._transform:
resfld = getattr(resfld, self._inv_table_func)(*self._args)
return resfld return resfld
jac = makeOp(Field(self._domain, self._deriv[fi]*res)) lin = Linearization.make_var(resfld)
return x.new(resfld, jac) if self._transform:
lin = getattr(lin, self._inv_table_func)(*self._args)
jac = makeOp(Field(self._domain, self._deriv[fi]))
return x.new(lin.val, lin.jac@jac)
def InverseGammaOperator(domain, alpha, q, delta=0.001): def InverseGammaOperator(domain, alpha, q, delta=0.001):
"""Transforms a Gaussian into an inverse gamma distribution. """Transforms a Gaussian with unit covariance and zero mean into an inverse gamma distribution.
The pdf of the inverse gamma distribution is defined as follows: The pdf of the inverse gamma distribution is defined as follows:
...@@ -67,7 +111,7 @@ def InverseGammaOperator(domain, alpha, q, delta=0.001): ...@@ -67,7 +111,7 @@ def InverseGammaOperator(domain, alpha, q, delta=0.001):
The mode is :math:`q / (\\alpha + 1)`. The mode is :math:`q / (\\alpha + 1)`.
This transformation is implemented as a linear interpolation which maps a This transformation is implemented as a linear interpolation which maps a
Gaussian onto a inverse gamma distribution. Gaussian onto an inverse gamma distribution.
Parameters Parameters
---------- ----------
...@@ -76,14 +120,39 @@ def InverseGammaOperator(domain, alpha, q, delta=0.001): ...@@ -76,14 +120,39 @@ def InverseGammaOperator(domain, alpha, q, delta=0.001):
time the domain and the target of the operator. time the domain and the target of the operator.
alpha : float alpha : float
The alpha-parameter of the inverse-gamma distribution. The alpha-parameter of the inverse-gamma distribution.
q : float q : float or Field
The q-parameter of the inverse-gamma distribution. The q-parameter of the inverse-gamma distribution.
delta : float delta : float
distance between sampling points for linear interpolation. Distance between sampling points for linear interpolation.
""" """
op = _InterpolationOperator(domain, op = _InterpolationOperator(domain,
lambda x: invgamma.ppf(norm.cdf(x), float(alpha)), lambda x: invgamma.ppf(norm.cdf(x), float(alpha)),
-8.2, 8.2, delta, np.log, np.exp) -8.2, 8.2, delta, 'log')
if np.isscalar(q): if np.isscalar(q):
return op.scale(q) return op.scale(q)
return makeOp(q) @ op return makeOp(q) @ op
def UniformOperator(domain, loc=0, scale=1, delta=1e-3):
"""
Transforms a Gaussian with unit covariance and zero mean into a uniform distribution.
The uniform distribution's support is ``[loc, loc + scale]``.
Parameters
----------
domain : Domain, tuple of Domain or DomainTuple
The domain on which the field shall be defined. This is at the same
time the domain and the target of the operator.
loc: float or Field
scale: float or Field
delta : float
Distance between sampling points for linear interpolation.
"""
op = _InterpolationOperator(domain,
lambda x: norm.cdf(x),
-8.2, 8.2, delta)
loc = Adder(loc, domain=domain)
if np.isscalar(scale):
return loc(op.scale(scale))
return loc(makeOp(scale) @ op)
Supports Markdown
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