Skip to content
Snippets Groups Projects
Commit 654b09e0 authored by Martin Reinecke's avatar Martin Reinecke
Browse files

add documentation; tweak corresponding tests

parent 78cac76f
No related branches found
No related tags found
1 merge request!84Document FFTOperator and rename its "inverse_times" to "adjoint_times"
Pipeline #
...@@ -34,7 +34,74 @@ from transformations import RGRGTransformation,\ ...@@ -34,7 +34,74 @@ from transformations import RGRGTransformation,\
class FFTOperator(LinearOperator): class FFTOperator(LinearOperator):
""" Transforms between a pair of position and harmonic domains.
Possible domain pairs are
- a harmonic and a non-harmonic RGSpace (with matching distances)
- a HPSpace and a LMSpace
- a GLSpace and a LMSpace
Within a domain pair, both orderings are possible.
The operator provides a "times" and an "adjoint_times" operation.
For a pair of RGSpaces, the "adjoint_times" operation is equivalent to
"inverse_times"; for the sphere-related domains this is not the case, since
the operator matrix is not square.
Parameters
----------
domain: Space or single-element tuple of Spaces
The domain of the data that is input by "times" and output by
"adjoint_times".
target: Space or single-element tuple of Spaces (optional)
The domain of the data that is output by "times" and input by
"adjoint_times".
If omitted, a co-domain will be chosen automatically.
Whenever "domain" is an RGSpace, the codomain (and its parameters) are
uniquely determined. For GLSpace, HPSpace, and LMSpace, a sensible
(but not unique) co-domain is chosen that should work satisfactorily in
most situations, but for full control, the user should explicitly
specify a codomain.
module: String (optional)
Software module employed for carrying out the transform operations.
For RGSpace pairs this can be "numpy" or "fftw", where "numpy" is always
available, but "fftw" offers higher performance and parallelization.
For sphere-related domains, only "pyHealpix" is available.
If omitted, "fftw" is selected for RGSpaces if available, else "numpy";
on the sphere the default is (unsurprisingly) "pyHealpix".
domain_dtype: data type (optional)
Data type of the fields that go into "times" and come out of
"adjoint_times". Default is "numpy.float".
target_dtype: data type (optional)
Data type of the fields that go into "adjoint_times" and come out of
"times". Default is "numpy.complex".
(MR: I feel this is not really a good idea, since it makes no sense for
SHTs. Also, wouldn't it make sense to specify data types
only to "times" and "adjoint_times"? Does the operator itself really
need to know this, or only the individual call?)
Attributes
----------
domain: Tuple of Spaces (with one entry)
The domain of the data that is input by "times" and output by
"adjoint_times".
target: Tuple of Spaces (with one entry)
The domain of the data that is output by "times" and input by
"adjoint_times".
unitary: bool
Returns False.
This is strictly speaking a lie, because FFTOperators on RGSpaces are
in fact unitary ... but if we return True in this case, then
LinearOperator will call _inverse_times instead of _adjoint_times, which
does not exist. This needs some more work.
Raises
------
ValueError:
if "domain" or "target" are not of the proper type.
"""
# ---Class attributes--- # ---Class attributes---
default_codomain_dictionary = {RGSpace: RGSpace, default_codomain_dictionary = {RGSpace: RGSpace,
...@@ -170,6 +237,32 @@ class FFTOperator(LinearOperator): ...@@ -170,6 +237,32 @@ class FFTOperator(LinearOperator):
@classmethod @classmethod
def get_default_codomain(cls, domain): def get_default_codomain(cls, domain):
""" Returns a codomain to the given domain.
Parameters
----------
domain: Space
An instance of RGSpace, HPSpace, GLSpace or LMSpace.
Returns
-------
target: Space
A (more or less perfect) counterpart to "domain" with respect
to a FFT operation.
Whenever "domain" is an RGSpace, the codomain (and its parameters)
are uniquely determined. For GLSpace, HPSpace, and LMSpace, a
sensible (but not unique) co-domain is chosen that should work
satisfactorily in most situations. For full control however, the
user should not rely on this method.
Raises
------
ValueError:
if no default codomain is defined for "domain".
"""
domain_class = domain.__class__ domain_class = domain.__class__
try: try:
codomain_class = cls.default_codomain_dictionary[domain_class] codomain_class = cls.default_codomain_dictionary[domain_class]
......
...@@ -26,9 +26,8 @@ from nifty.config import dependency_injector as di ...@@ -26,9 +26,8 @@ from nifty.config import dependency_injector as di
from nifty import Field,\ from nifty import Field,\
RGSpace,\ RGSpace,\
LMSpace,\ LMSpace,\
RGRGTransformation, \ HPSpace,\
LMGLTransformation, \ GLSpace,\
LMHPTransformation, \
FFTOperator FFTOperator
from itertools import product from itertools import product
...@@ -75,7 +74,7 @@ class Misc_Tests(unittest.TestCase): ...@@ -75,7 +74,7 @@ class Misc_Tests(unittest.TestCase):
raise SkipTest raise SkipTest
tol = _get_rtol(itp) tol = _get_rtol(itp)
a = RGSpace(dim1, zerocenter=zc1, distances=d) a = RGSpace(dim1, zerocenter=zc1, distances=d)
b = RGRGTransformation.get_codomain(a, zerocenter=zc2) b = RGSpace(dim1, zerocenter=zc2,distances=1./(dim1*d),harmonic=True)
fft = FFTOperator(domain=a, target=b, domain_dtype=itp, fft = FFTOperator(domain=a, target=b, domain_dtype=itp,
target_dtype=_harmonic_type(itp), module=module) target_dtype=_harmonic_type(itp), module=module)
inp = Field.from_random(domain=a, random_type='normal', std=7, mean=3, inp = Field.from_random(domain=a, random_type='normal', std=7, mean=3,
...@@ -85,13 +84,15 @@ class Misc_Tests(unittest.TestCase): ...@@ -85,13 +84,15 @@ class Misc_Tests(unittest.TestCase):
@expand(product(["numpy", "fftw"], [10, 11], [9, 12], [False, True], @expand(product(["numpy", "fftw"], [10, 11], [9, 12], [False, True],
[False, True], [False, True], [False, True], [0.1, 1, 3.7], [False, True], [False, True], [False, True], [0.1, 1, 3.7],
[0.4, 1, 2.7],
[np.float64, np.complex128, np.float32, np.complex64])) [np.float64, np.complex128, np.float32, np.complex64]))
def test_fft2D(self, module, dim1, dim2, zc1, zc2, zc3, zc4, d, itp): def test_fft2D(self, module, dim1, dim2, zc1, zc2, zc3, zc4, d1, d2, itp):
if module == "fftw" and "pyfftw" not in di: if module == "fftw" and "pyfftw" not in di:
raise SkipTest raise SkipTest
tol = _get_rtol(itp) tol = _get_rtol(itp)
a = RGSpace([dim1, dim2], zerocenter=[zc1, zc2], distances=d) a = RGSpace([dim1, dim2], zerocenter=[zc1, zc2], distances=[d1,d2])
b = RGRGTransformation.get_codomain(a, zerocenter=[zc3, zc4]) b = RGSpace([dim1, dim2], zerocenter=[zc3, zc4],
distances=[1./(dim1*d1),1./(dim2*d2)],harmonic=True)
fft = FFTOperator(domain=a, target=b, domain_dtype=itp, fft = FFTOperator(domain=a, target=b, domain_dtype=itp,
target_dtype=_harmonic_type(itp), module=module) target_dtype=_harmonic_type(itp), module=module)
inp = Field.from_random(domain=a, random_type='normal', std=7, mean=3, inp = Field.from_random(domain=a, random_type='normal', std=7, mean=3,
...@@ -106,7 +107,7 @@ class Misc_Tests(unittest.TestCase): ...@@ -106,7 +107,7 @@ class Misc_Tests(unittest.TestCase):
raise SkipTest raise SkipTest
tol = _get_rtol(tp) tol = _get_rtol(tp)
a = LMSpace(lmax=lm) a = LMSpace(lmax=lm)
b = LMGLTransformation.get_codomain(a) b = GLSpace(nlat=lm+1)
fft = FFTOperator(domain=a, target=b, domain_dtype=tp, target_dtype=tp) fft = FFTOperator(domain=a, target=b, domain_dtype=tp, target_dtype=tp)
inp = Field.from_random(domain=a, random_type='normal', std=7, mean=3, inp = Field.from_random(domain=a, random_type='normal', std=7, mean=3,
dtype=tp) dtype=tp)
...@@ -119,7 +120,7 @@ class Misc_Tests(unittest.TestCase): ...@@ -119,7 +120,7 @@ class Misc_Tests(unittest.TestCase):
if 'pyHealpix' not in di: if 'pyHealpix' not in di:
raise SkipTest raise SkipTest
a = LMSpace(lmax=lm) a = LMSpace(lmax=lm)
b = LMHPTransformation.get_codomain(a) b = HPSpace(nside=lm//2)
fft = FFTOperator(domain=a, target=b, domain_dtype=tp, target_dtype=tp) fft = FFTOperator(domain=a, target=b, domain_dtype=tp, target_dtype=tp)
inp = Field.from_random(domain=a, random_type='normal', std=1, mean=0, inp = Field.from_random(domain=a, random_type='normal', std=1, mean=0,
dtype=tp) dtype=tp)
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment