Commit 80d3ebe0 authored by Martin Reinecke's avatar Martin Reinecke
Browse files

move more operators

parent 1533d285
Pipeline #28432 passed with stages
in 2 minutes and 37 seconds
from .multi_domain import MultiDomain
from .multi_field import MultiField
from .multi_linear_operator import MultiLinearOperator
from .multi_endomorphic_operator import MultiEndomorphicOperator
from .multi_chain_operator import MultiChainOperator
from .multi_sum_operator import MultiSumOperator
from .multi_scaling_operator import MultiScalingOperator
__all__ = ["MultiDomain", "MultiField", "MultiLinearOperator",
"MultiChainOperator", "MultiSumOperator"]
"MultiEndomorphicOperator", "MultiChainOperator",
"MultiSumOperator", "MultiScalingOperator"]
......@@ -64,7 +64,7 @@ class MultiChainOperator(MultiLinearOperator):
return self._capability
def apply(self, x, mode):
self._check_input(x, mode)
t_ops = self._ops if mode & self._backwards else reversed(self._ops)
for op in t_ops:
x = op.apply(x, mode)
import numpy as np
from .multi_linear_operator import MultiLinearOperator
class MultiEndomorphicOperator(MultiLinearOperator):
Class for multi endomorphic operators.
By definition, domain and target are the same in
def target(self):
MultiDomain : returns :attr:`domain`
Returns `self.domain`, because this is also the target domain
for endomorphic operators.
return self.domain
def draw_sample(self, from_inverse=False, dtype=np.float64):
"""Generate a zero-mean sample
Generates a sample from a Gaussian distribution with zero mean and
covariance given by the operator. If from_inverse is True, the sample
is drawn from the inverse of the operator.
A sample from the Gaussian of given covariance.
raise NotImplementedError
from ..operators.linear_operator import LinearOperator
from .multi_field import MultiField
class MultiLinearOperator(LinearOperator):
......@@ -38,3 +39,11 @@ class MultiLinearOperator(LinearOperator):
from .multi_sum_operator import MultiSumOperator
other = self._toOperator(other, self.domain)
return MultiSumOperator.make([other, self], [False, True])
def _check_input(self, x, mode):
if not isinstance(x, MultiField):
raise ValueError("supplied object is not a `MultiField`.")
if x.domain != self._dom(mode):
raise ValueError("The operator's and field's domains don't match.")
# 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
# 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 <>.
# Copyright(C) 2013-2018 Max-Planck-Society
# NIFTy is being developed at the Max-Planck-Institut fuer Astrophysik
# and financially supported by the Studienstiftung des deutschen Volkes.
from __future__ import division
import numpy as np
from ..field import Field
from ..domain_tuple import DomainTuple
from .multi_endomorphic_operator import MultiEndomorphicOperator
class MultiScalingOperator(MultiEndomorphicOperator):
"""Operator which multiplies a Multifield with a scalar.
The NIFTy MultiScalingOperator class is a subclass derived from the
EndomorphicOperator. It multiplies an input field with a given factor.
factor : scalar
The multiplication factor
domain : MultiDomain
The domain on which the Operator's input Field lives.
Formally, this operator always supports all operation modes (times,
adjoint_times, inverse_times and inverse_adjoint_times), even if `factor`
is 0 or infinity. It is the user's responsibility to apply the operator
only in appropriate ways (e.g. call inverse_times only if `factor` is
This shortcoming will hopefully be fixed in the future.
def __init__(self, factor, domain):
super(MultiScalingOperator, self).__init__()
if not np.isscalar(factor):
raise TypeError("Scalar required")
self._factor = factor
self._domain = domain
def apply(self, x, mode):
self._check_input(x, mode)
if self._factor == 1.:
return x.copy()
if mode == self.TIMES:
return x*self._factor
elif mode == self.ADJOINT_TIMES:
return x*np.conj(self._factor)
elif mode == self.INVERSE_TIMES:
return x*(1./self._factor)
return x*(1./np.conj(self._factor))
def _flip_modes(self, trafo):
if trafo == 0:
return self
if trafo == ADJ and np.issubdtype(type(self._factor), np.floating):
return self
if trafo == ADJ:
return ScalingOperator(np.conj(self._factor), self._domain)
elif trafo == INV:
return ScalingOperator(1./self._factor, self._domain)
elif trafo == ADJ | INV:
return ScalingOperator(1./np.conj(self._factor), self._domain)
raise ValueError("invalid operator transformation")
def domain(self):
return self._domain
def capability(self):
return self._all_ops
def draw_sample(self, from_inverse=False, dtype=np.float64):
fct = self._factor
if fct.imag != 0. or fct.real <= 0.:
raise ValueError("operator not positive definite")
fct = 1./np.sqrt(fct) if from_inverse else np.sqrt(fct)
return Field.from_random(
random_type="normal", domain=self._domain, std=fct, dtype=dtype)
......@@ -61,7 +61,7 @@ class MultiSumOperator(MultiLinearOperator):
return self._capability
def apply(self, x, mode):
self._check_input(x, mode)
for i, op in enumerate(self._ops):
if i == 0:
res = -op.apply(x, mode) if self._neg[i] else op.apply(x, mode)
......@@ -67,7 +67,7 @@ class InversionEnabler(EndomorphicOperator):
if self._op.capability & mode:
return self._op.apply(x, mode)
x0 = x.zeros(self._tgt(mode), dtype=x.dtype)
x0 = x*0.
invmode = self._modeTable[self.INVERSE_BIT][self._ilog[mode]]
invop = self._op._flip_modes(self._ilog[invmode])
prec = self._approximation
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