Skip to content
GitLab
Menu
Projects
Groups
Snippets
Loading...
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
Menu
Open sidebar
ift
NIFTy
Commits
e196bdc5
Commit
e196bdc5
authored
Jan 10, 2019
by
Martin Reinecke
Browse files
Merge remote-tracking branch 'origin/NIFTy_5' into move_dynamic_prior
parents
268962e3
1f18344d
Changes
8
Show whitespace changes
Inline
Side-by-side
docs/source/code.rst
View file @
e196bdc5
...
@@ -184,17 +184,29 @@ a :class:`DomainTuple` or :class:`MultiDomain` object specifying the structure o
...
@@ -184,17 +184,29 @@ a :class:`DomainTuple` or :class:`MultiDomain` object specifying the structure o
describing its output, and finally an overloaded `apply` method, which can
describing its output, and finally an overloaded `apply` method, which can
take
take
- a :class:`Field`/:class:`MultiField`object, in which case it returns the transformed
- a :class:`Field`/:class:`MultiField`
object, in which case it returns the transformed
:class:`Field`/:class:`MultiField`
:class:`Field`/:class:`MultiField`
- a :class:`Linearization` object, in which case it returns the transformed
- a :class:`Linearization` object, in which case it returns the transformed
:class:`Linearization`
:class:`Linearization`
This is the interface that all objects derived from :class:`Operator` must implement.
This is the interface that all objects derived from :class:`Operator` must implement.
In addition, :class:`Operator` objects can be added/subtracted, multiplied, chained
In addition, :class:`Operator` objects can be added/subtracted, multiplied, chained
(via the :class:`__call__` method) and support pointwise application of functions like
(via the :class:`__call__` method and the `@` operator) and support pointwise
application of functions like
:class:`exp()`, :class:`log()`, :class:`sqrt()`, :class:`conjugate()` etc.
:class:`exp()`, :class:`log()`, :class:`sqrt()`, :class:`conjugate()` etc.
Advanced operators
------------------
NIFTy provides a library of more sophisticated operators which are used for more
specific inference problems. Currently these are:
- :class:`AmplitudeOperator`, which returns a smooth power spectrum.
- :class:`InverseGammaOperator`, which models point sources which follow a inverse gamma distribution.
- :class:`CorrelatedField`, which models a diffuse log-normal field. It takes an amplitude operator to specify the correlation structure of the field.
Linear Operators
Linear Operators
================
================
...
@@ -205,8 +217,8 @@ additional functionality which is not available for the more generic :class:`Ope
...
@@ -205,8 +217,8 @@ additional functionality which is not available for the more generic :class:`Ope
class.
class.
Operator basics
Linear
Operator basics
---------------
---------------
-------
There are four basic ways of applying an operator :math:`A` to a field :math:`f`:
There are four basic ways of applying an operator :math:`A` to a field :math:`f`:
...
@@ -218,8 +230,8 @@ There are four basic ways of applying an operator :math:`A` to a field :math:`f`
...
@@ -218,8 +230,8 @@ There are four basic ways of applying an operator :math:`A` to a field :math:`f`
(Because of the linearity, inverse adjoint and adjoint inverse application
(Because of the linearity, inverse adjoint and adjoint inverse application
are equivalent.)
are equivalent.)
These different actions of a
n
operator ``Op`` on a field ``f`` can be
invoked
These different actions of a
linear
operator ``Op`` on a field ``f`` can be
in various ways:
invoked
in various ways:
- direct multiplication: ``Op(f)`` or ``Op.times(f)`` or ``Op.apply(f, Op.TIMES)``
- direct multiplication: ``Op(f)`` or ``Op.times(f)`` or ``Op.apply(f, Op.TIMES)``
- adjoint multiplication: ``Op.adjoint_times(f)`` or ``Op.apply(f, Op.ADJOINT_TIMES)``
- adjoint multiplication: ``Op.adjoint_times(f)`` or ``Op.apply(f, Op.ADJOINT_TIMES)``
...
...
nifty5/linearization.py
View file @
e196bdc5
...
@@ -24,6 +24,22 @@ from .operators.scaling_operator import ScalingOperator
...
@@ -24,6 +24,22 @@ from .operators.scaling_operator import ScalingOperator
class
Linearization
(
object
):
class
Linearization
(
object
):
"""Let `A` be an operator and `x` a field. `Linearization` stores the value
of the operator application (i.e. `A(x)`), the local Jacobian
(i.e. `dA(x)/dx`) and, optionally, the local metric.
Parameters
----------
val : Field/MultiField
the value of the operator application
jac : LinearOperator
the Jacobian
metric : LinearOperator or None (default: None)
the metric
want_metric : bool (default: False)
if True, the metric will be computed for other Linearizations derived
from this one.
"""
def
__init__
(
self
,
val
,
jac
,
metric
=
None
,
want_metric
=
False
):
def
__init__
(
self
,
val
,
jac
,
metric
=
None
,
want_metric
=
False
):
self
.
_val
=
val
self
.
_val
=
val
self
.
_jac
=
jac
self
.
_jac
=
jac
...
@@ -33,36 +49,63 @@ class Linearization(object):
...
@@ -33,36 +49,63 @@ class Linearization(object):
self
.
_metric
=
metric
self
.
_metric
=
metric
def
new
(
self
,
val
,
jac
,
metric
=
None
):
def
new
(
self
,
val
,
jac
,
metric
=
None
):
"""Create a new Linearization, taking the `want_metric` property from
this one.
Parameters
----------
val : Field/MultiField
the value of the operator application
jac : LinearOperator
the Jacobian
metric : LinearOperator or None (default: None)
the metric
"""
return
Linearization
(
val
,
jac
,
metric
,
self
.
_want_metric
)
return
Linearization
(
val
,
jac
,
metric
,
self
.
_want_metric
)
@
property
@
property
def
domain
(
self
):
def
domain
(
self
):
"""DomainTuple/MultiDomain : the Jacobian's domain"""
return
self
.
_jac
.
domain
return
self
.
_jac
.
domain
@
property
@
property
def
target
(
self
):
def
target
(
self
):
"""DomainTuple/MultiDomain : the Jacobian's target (i.e. the value's domain)"""
return
self
.
_jac
.
target
return
self
.
_jac
.
target
@
property
@
property
def
val
(
self
):
def
val
(
self
):
"""Field/MultiField : the value"""
return
self
.
_val
return
self
.
_val
@
property
@
property
def
jac
(
self
):
def
jac
(
self
):
"""LinearOperator : the Jacobian"""
return
self
.
_jac
return
self
.
_jac
@
property
@
property
def
gradient
(
self
):
def
gradient
(
self
):
"""Only available if target is a scalar"""
"""Field/MultiField : the gradient
Notes
-----
Only available if target is a scalar
"""
return
self
.
_jac
.
adjoint_times
(
Field
.
scalar
(
1.
))
return
self
.
_jac
.
adjoint_times
(
Field
.
scalar
(
1.
))
@
property
@
property
def
want_metric
(
self
):
def
want_metric
(
self
):
"""bool : the value of `want_metric`"""
return
self
.
_want_metric
return
self
.
_want_metric
@
property
@
property
def
metric
(
self
):
def
metric
(
self
):
"""Only available if target is a scalar"""
"""LinearOperator : the metric
Notes
-----
Only available if target is a scalar
"""
return
self
.
_metric
return
self
.
_metric
def
__getitem__
(
self
,
name
):
def
__getitem__
(
self
,
name
):
...
...
nifty5/minimization/energy_adapter.py
View file @
e196bdc5
...
@@ -20,6 +20,25 @@ from ..minimization.energy import Energy
...
@@ -20,6 +20,25 @@ from ..minimization.energy import Energy
class
EnergyAdapter
(
Energy
):
class
EnergyAdapter
(
Energy
):
"""Helper class which provides the traditional Nifty Energy interface to
Nifty operators with a scalar target domain.
Parameters
-----------
position: Field or MultiField living on the operator's input domain.
The position where the minimization process is started
op: Operator with a scalar target domain
The expression computing the energy from the input data
constants: list of strings (default: [])
The component names of the operator's input domain which are assumed
to be constant during the minimization process.
If the operator's input domain is not a MultiField, this must be empty.
want_metric: bool (default: False)
if True, the class will provide a `metric` property. This should only
be enabled if it is required, because it will most likely consume
additional resources.
"""
def
__init__
(
self
,
position
,
op
,
constants
=
[],
want_metric
=
False
):
def
__init__
(
self
,
position
,
op
,
constants
=
[],
want_metric
=
False
):
super
(
EnergyAdapter
,
self
).
__init__
(
position
)
super
(
EnergyAdapter
,
self
).
__init__
(
position
)
self
.
_op
=
op
self
.
_op
=
op
...
...
nifty5/minimization/iteration_controllers.py
View file @
e196bdc5
...
@@ -143,7 +143,24 @@ class GradientNormController(IterationController):
...
@@ -143,7 +143,24 @@ class GradientNormController(IterationController):
class
GradInfNormController
(
IterationController
):
class
GradInfNormController
(
IterationController
):
def
__init__
(
self
,
tol
=
None
,
convergence_level
=
1
,
iteration_limit
=
None
,
"""An iteration controller checking (mainly) the L_infinity gradient norm.
Parameters
----------
tol : float
If the L_infinity norm of the energy gradient is below this value, the
convergence counter will be increased in this iteration.
convergence_level : int, default=1
The number which the convergence counter must reach before the
iteration is considered to be converged
iteration_limit : int, optional
The maximum number of iterations that will be carried out.
name : str, optional
if supplied, this string and some diagnostic information will be
printed after every iteration
"""
def
__init__
(
self
,
tol
,
convergence_level
=
1
,
iteration_limit
=
None
,
name
=
None
):
name
=
None
):
self
.
_tol
=
tol
self
.
_tol
=
tol
self
.
_convergence_level
=
convergence_level
self
.
_convergence_level
=
convergence_level
...
@@ -185,6 +202,25 @@ class GradInfNormController(IterationController):
...
@@ -185,6 +202,25 @@ class GradInfNormController(IterationController):
class
DeltaEnergyController
(
IterationController
):
class
DeltaEnergyController
(
IterationController
):
"""An iteration controller checking (mainly) the energy change from one
iteration to the next.
Parameters
----------
tol_rel_deltaE : float
If the difference between the last and current energies divided by
the current energy is below this value, the convergence counter will
be increased in this iteration.
convergence_level : int, default=1
The number which the convergence counter must reach before the
iteration is considered to be converged
iteration_limit : int, optional
The maximum number of iterations that will be carried out.
name : str, optional
if supplied, this string and some diagnostic information will be
printed after every iteration
"""
def
__init__
(
self
,
tol_rel_deltaE
,
convergence_level
=
1
,
def
__init__
(
self
,
tol_rel_deltaE
,
convergence_level
=
1
,
iteration_limit
=
None
,
name
=
None
):
iteration_limit
=
None
,
name
=
None
):
self
.
_tol_rel_deltaE
=
tol_rel_deltaE
self
.
_tol_rel_deltaE
=
tol_rel_deltaE
...
...
nifty5/minimization/scipy_minimizer.py
View file @
e196bdc5
...
@@ -148,6 +148,12 @@ def L_BFGS_B(ftol, gtol, maxiter, maxcor=10, disp=False, bounds=None):
...
@@ -148,6 +148,12 @@ def L_BFGS_B(ftol, gtol, maxiter, maxcor=10, disp=False, bounds=None):
class
ScipyCG
(
Minimizer
):
class
ScipyCG
(
Minimizer
):
"""Returns a ScipyMinimizer object carrying out the conjugate gradient
algorithm as implemented by SciPy.
This class is only intended for double-checking NIFTy's own conjugate
gradient implementation and should not be used otherwise.
"""
def
__init__
(
self
,
tol
,
maxiter
):
def
__init__
(
self
,
tol
,
maxiter
):
if
not
dobj
.
is_numpy
():
if
not
dobj
.
is_numpy
():
raise
NotImplementedError
raise
NotImplementedError
...
...
nifty5/operators/simple_linear_operators.py
View file @
e196bdc5
...
@@ -25,6 +25,13 @@ from .linear_operator import LinearOperator
...
@@ -25,6 +25,13 @@ from .linear_operator import LinearOperator
class
VdotOperator
(
LinearOperator
):
class
VdotOperator
(
LinearOperator
):
"""Operator computing the scalar product of its input with a given Field.
Parameters
----------
field : Field/MultiField
The field used to build the scalar product with the operator input
"""
def
__init__
(
self
,
field
):
def
__init__
(
self
,
field
):
self
.
_field
=
field
self
.
_field
=
field
self
.
_domain
=
field
.
domain
self
.
_domain
=
field
.
domain
...
@@ -39,6 +46,7 @@ class VdotOperator(LinearOperator):
...
@@ -39,6 +46,7 @@ class VdotOperator(LinearOperator):
class
ConjugationOperator
(
EndomorphicOperator
):
class
ConjugationOperator
(
EndomorphicOperator
):
"""Operator computing the complex conjugate of its input."""
def
__init__
(
self
,
domain
):
def
__init__
(
self
,
domain
):
self
.
_domain
=
DomainTuple
.
make
(
domain
)
self
.
_domain
=
DomainTuple
.
make
(
domain
)
self
.
_capability
=
self
.
_all_ops
self
.
_capability
=
self
.
_all_ops
...
@@ -49,6 +57,7 @@ class ConjugationOperator(EndomorphicOperator):
...
@@ -49,6 +57,7 @@ class ConjugationOperator(EndomorphicOperator):
class
Realizer
(
EndomorphicOperator
):
class
Realizer
(
EndomorphicOperator
):
"""Operator returning the real component of its input."""
def
__init__
(
self
,
domain
):
def
__init__
(
self
,
domain
):
self
.
_domain
=
DomainTuple
.
make
(
domain
)
self
.
_domain
=
DomainTuple
.
make
(
domain
)
self
.
_capability
=
self
.
TIMES
|
self
.
ADJOINT_TIMES
self
.
_capability
=
self
.
TIMES
|
self
.
ADJOINT_TIMES
...
@@ -100,6 +109,41 @@ class FieldAdapter(LinearOperator):
...
@@ -100,6 +109,41 @@ class FieldAdapter(LinearOperator):
def
ducktape
(
left
,
right
,
name
):
def
ducktape
(
left
,
right
,
name
):
"""Convenience function for computing an adapter between two operators.
Parameters
----------
left : None, Operator, or Domainoid
Something describing the input domain of the left operator.
If `left` is an `Operator`, its domain is used as `left`.
right : None, Operator, or Domainoid
Something describing the target domain of the right operator.
If `right` is an `Operator`, its target is used as `right`.
name : string
The component of the `MultiDomain` that will be extracted/inserted
Notes
-----
- one of the involved domains must be a `DomainTuple`, the other a
`MultiDomain`.
- `left` and `right` must not be both `None`, but one of them can (and
probably should) be `None`. In this case, the missing information is
inferred.
- the returned operator's domains are
- a `DomainTuple` and
- a `MultiDomain` with exactly one entry called `name` and the same
`DomainTuple`
Which of these is the domain and which is the target depends on the
input.
Returns
-------
FieldAdapter : an adapter operator converting between the two (possibly
partially inferred) domains.
"""
from
..sugar
import
makeDomain
from
..sugar
import
makeDomain
from
.operator
import
Operator
from
.operator
import
Operator
if
left
is
None
:
# need to infer left from right
if
left
is
None
:
# need to infer left from right
...
...
nifty5/probing.py
View file @
e196bdc5
...
@@ -19,10 +19,26 @@ from .field import Field
...
@@ -19,10 +19,26 @@ from .field import Field
class
StatCalculator
(
object
):
class
StatCalculator
(
object
):
"""Helper class to compute mean and variance of a set of inputs.
Notes
-----
- the memory usage of this object is constant, i.e. it does not increase
with the number of samples added
- the code computes the unbiased variance (which contains a `1./(n-1)`
term for `n` samples).
"""
def
__init__
(
self
):
def
__init__
(
self
):
self
.
_count
=
0
self
.
_count
=
0
def
add
(
self
,
value
):
def
add
(
self
,
value
):
"""Adds a sample.
Parameters
----------
value: any type that supports multiplication by a scalar and
element-wise addition/subtraction/multiplication.
"""
self
.
_count
+=
1
self
.
_count
+=
1
if
self
.
_count
==
1
:
if
self
.
_count
==
1
:
self
.
_mean
=
1.
*
value
self
.
_mean
=
1.
*
value
...
@@ -35,12 +51,18 @@ class StatCalculator(object):
...
@@ -35,12 +51,18 @@ class StatCalculator(object):
@
property
@
property
def
mean
(
self
):
def
mean
(
self
):
"""
value type : the mean of all samples added so far.
"""
if
self
.
_count
==
0
:
if
self
.
_count
==
0
:
raise
RuntimeError
raise
RuntimeError
return
1.
*
self
.
_mean
return
1.
*
self
.
_mean
@
property
@
property
def
var
(
self
):
def
var
(
self
):
"""
value type : the unbiased variance of all samples added so far.
"""
if
self
.
_count
<
2
:
if
self
.
_count
<
2
:
raise
RuntimeError
raise
RuntimeError
return
self
.
_M2
*
(
1.
/
(
self
.
_count
-
1
))
return
self
.
_M2
*
(
1.
/
(
self
.
_count
-
1
))
...
...
nifty5/sugar.py
View file @
e196bdc5
...
@@ -41,6 +41,19 @@ __all__ = ['PS_field', 'power_analyze', 'create_power_operator',
...
@@ -41,6 +41,19 @@ __all__ = ['PS_field', 'power_analyze', 'create_power_operator',
def
PS_field
(
pspace
,
func
):
def
PS_field
(
pspace
,
func
):
"""Convenience function sampling a power spectrum
Parameters
----------
pspace : PowerSpace
space at whose `k_lengths` the power spectrum function is evaluated
func : function taking and returning a numpy.ndarray(float)
the power spectrum function
Returns
-------
Field : a field living on (pspace,) containing the computed function values
"""
if
not
isinstance
(
pspace
,
PowerSpace
):
if
not
isinstance
(
pspace
,
PowerSpace
):
raise
TypeError
raise
TypeError
data
=
dobj
.
from_global_data
(
func
(
pspace
.
k_lengths
))
data
=
dobj
.
from_global_data
(
func
(
pspace
.
k_lengths
))
...
@@ -202,30 +215,104 @@ def create_harmonic_smoothing_operator(domain, space, sigma):
...
@@ -202,30 +215,104 @@ def create_harmonic_smoothing_operator(domain, space, sigma):
def
full
(
domain
,
val
):
def
full
(
domain
,
val
):
"""Convenience function creating Fields/MultiFields with uniform values.
Parameters
----------
domain : Domainoid
the intended domain of the output field
val : scalar value
the uniform value to be placed into all entries of the result
Returns
-------
Field / MultiField : the newly created uniform field
"""
if
isinstance
(
domain
,
(
dict
,
MultiDomain
)):
if
isinstance
(
domain
,
(
dict
,
MultiDomain
)):
return
MultiField
.
full
(
domain
,
val
)
return
MultiField
.
full
(
domain
,
val
)
return
Field
.
full
(
domain
,
val
)
return
Field
.
full
(
domain
,
val
)
def
from_random
(
random_type
,
domain
,
dtype
=
np
.
float64
,
**
kwargs
):
def
from_random
(
random_type
,
domain
,
dtype
=
np
.
float64
,
**
kwargs
):
"""Convenience function creating Fields/MultiFields with random values.
Parameters
----------
random_type : 'pm1', 'normal', or 'uniform'
The random distribution to use.
domain : Domainoid
the intended domain of the output field
dtype : type
data type of the output field (e.g. numpy.float64)
**kwargs : additional parameters for the random distribution
('mean' and 'std' for 'normal', 'low' and 'high' for 'uniform')
Returns
-------
Field / MultiField : the newly created random field
"""
if
isinstance
(
domain
,
(
dict
,
MultiDomain
)):
if
isinstance
(
domain
,
(
dict
,
MultiDomain
)):
return
MultiField
.
from_random
(
random_type
,
domain
,
dtype
,
**
kwargs
)
return
MultiField
.
from_random
(
random_type
,
domain
,
dtype
,
**
kwargs
)
return
Field
.
from_random
(
random_type
,
domain
,
dtype
,
**
kwargs
)
return
Field
.
from_random
(
random_type
,
domain
,
dtype
,
**
kwargs
)
def
from_global_data
(
domain
,
arr
,
sum_up
=
False
):
def
from_global_data
(
domain
,
arr
,
sum_up
=
False
):
"""Convenience function creating Fields/MultiFields from Numpy arrays or
dicts of Numpy arrays.
Parameters
----------
domain : Domainoid
the intended domain of the output field
arr : Numpy array if `domain` corresponds to a `DomainTuple`,
dictionary of Numpy arrays if `domain` corresponds to a `MultiDomain`
sum_up : bool
Only meaningful if MPI is enabled
If `True`, the contents of the arrays on all tasks are added together,
otherwise it is assumed that the array on each task holds the correct
field values.
Returns
-------
Field / MultiField : the newly created random field
"""
if
isinstance
(
domain
,
(
dict
,
MultiDomain
)):
if
isinstance
(
domain
,
(
dict
,
MultiDomain
)):
return
MultiField
.
from_global_data
(
domain
,
arr
,
sum_up
)
return
MultiField
.
from_global_data
(
domain
,
arr
,
sum_up
)
return
Field
.
from_global_data
(
domain
,
arr
,
sum_up
)
return
Field
.
from_global_data
(
domain
,
arr
,
sum_up
)
def
from_local_data
(
domain
,
arr
):
def
from_local_data
(
domain
,
arr
):
"""Convenience function creating Fields/MultiFields from Numpy arrays or
dicts of Numpy arrays.
Parameters
----------
domain : Domainoid
the intended domain of the output field
arr : Numpy array if `domain` corresponds to a `DomainTuple`,
dictionary of Numpy arrays if `domain` corresponds to a `MultiDomain`
Returns
-------
Field / MultiField : the newly created field
"""
if
isinstance
(
domain
,
(
dict
,
MultiDomain
)):
if
isinstance
(
domain
,
(
dict
,
MultiDomain
)):
return
MultiField
.
from_local_data
(
domain
,
arr
)
return
MultiField
.
from_local_data
(
domain
,
arr
)
return
Field
.
from_local_data
(
domain
,
arr
)
return
Field
.
from_local_data
(
domain
,
arr
)
def
makeDomain
(
domain
):
def
makeDomain
(
domain
):
"""Convenience function creating DomainTuples/MultiDomains Domainoids.
Parameters
----------
domain : Domainoid (can be DomainTuple, MultiDomain, dict, Domain or list of Domains)
the description of the requested (multi-)domain
Returns
-------
DomainTuple / MultiDomain : the newly created domain object
"""
if
isinstance
(
domain
,
(
MultiDomain
,
dict
)):
if
isinstance
(
domain
,
(
MultiDomain
,
dict
)):
return
MultiDomain
.
make
(
domain
)
return
MultiDomain
.
make
(
domain
)
return
DomainTuple
.
make
(
domain
)
return
DomainTuple
.
make
(
domain
)
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment