field.py 21.8 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
# 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
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# 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 <http://www.gnu.org/licenses/>.
Theo Steininger's avatar
Theo Steininger committed
13
14
15
16
17
#
# Copyright(C) 2013-2017 Max-Planck-Society
#
# NIFTy is being developed at the Max-Planck-Institut fuer Astrophysik
# and financially supported by the Studienstiftung des deutschen Volkes.
18

Martin Reinecke's avatar
Martin Reinecke committed
19
from __future__ import division, print_function
Martin Reinecke's avatar
Martin Reinecke committed
20
from builtins import range
csongor's avatar
csongor committed
21
import numpy as np
Martin Reinecke's avatar
Martin Reinecke committed
22
23
24
from .spaces.power_space import PowerSpace
from . import nifty_utilities as utilities
from .random import Random
Martin Reinecke's avatar
Martin Reinecke committed
25
from .domain_tuple import DomainTuple
Martin Reinecke's avatar
Martin Reinecke committed
26
from functools import reduce
27
from . import dobj
28

Martin Reinecke's avatar
Martin Reinecke committed
29
class Field(object):
Theo Steininger's avatar
Theo Steininger committed
30
31
32
    """ The discrete representation of a continuous field over multiple spaces.

    In NIFTY, Fields are used to store data arrays and carry all the needed
33
    metainformation (i.e. the domain) for operators to be able to work on them.
Martin Reinecke's avatar
updates    
Martin Reinecke committed
34
    In addition, Field has methods to work with power spectra.
Theo Steininger's avatar
Theo Steininger committed
35

36
37
38
39
    Parameters
    ----------
    domain : DomainObject
        One of the space types NIFTY supports. RGSpace, GLSpace, HPSpace,
Theo Steininger's avatar
Theo Steininger committed
40
        LMSpace or PowerSpace. It might also be a FieldArray, which is
41
        an unstructured domain.
Theo Steininger's avatar
Theo Steininger committed
42

Martin Reinecke's avatar
stage1    
Martin Reinecke committed
43
    val : scalar, numpy.ndarray, Field
44
45
46
        The values the array should contain after init. A scalar input will
        fill the whole array with this scalar. If an array is provided the
        array's dimensions must match the domain's.
Theo Steininger's avatar
Theo Steininger committed
47

48
    dtype : type
Martin Reinecke's avatar
updates    
Martin Reinecke committed
49
        A numpy.type. Most common are float and complex.
Theo Steininger's avatar
Theo Steininger committed
50

51
52
53
54
    copy: boolean

    Attributes
    ----------
Martin Reinecke's avatar
stage1    
Martin Reinecke committed
55
    val : numpy.ndarray
Theo Steininger's avatar
Theo Steininger committed
56

Martin Reinecke's avatar
Martin Reinecke committed
57
    domain : DomainTuple
58
59
60
        See Parameters.
    dtype : type
        Contains the datatype stored in the Field.
Theo Steininger's avatar
Theo Steininger committed
61

62
63
64
65
66
67
68
    Raise
    -----
    TypeError
        Raised if
            *the given domain contains something that is not a DomainObject
             instance
            *val is an array that has a different dimension than the domain
Theo Steininger's avatar
Theo Steininger committed
69

70
    """
71

Theo Steininger's avatar
Theo Steininger committed
72
    # ---Initialization methods---
73

Martin Reinecke's avatar
stage1    
Martin Reinecke committed
74
    def __init__(self, domain=None, val=None, dtype=None, copy=False):
75
        self.domain = self._parse_domain(domain=domain, val=val)
76

Martin Reinecke's avatar
Martin Reinecke committed
77
        dtype = self._infer_dtype(dtype=dtype, val=val)
Martin Reinecke's avatar
cleanup    
Martin Reinecke committed
78
        if isinstance(val, Field):
Martin Reinecke's avatar
PEP8    
Martin Reinecke committed
79
            if self.domain != val.domain:
Martin Reinecke's avatar
cleanup    
Martin Reinecke committed
80
                raise ValueError("Domain mismatch")
81
            self._val = dobj.from_object(val.val, dtype=dtype, copy=copy)
Martin Reinecke's avatar
cleanup    
Martin Reinecke committed
82
        elif (np.isscalar(val)):
83
84
            self._val = dobj.full(self.domain.shape, dtype=dtype, fill_value=val)
        elif isinstance(val, dobj.data_object):
Martin Reinecke's avatar
Martin Reinecke committed
85
            if self.domain.shape == val.shape:
86
                self._val = dobj.from_object(val, dtype=dtype, copy=copy)
Martin Reinecke's avatar
cleanup    
Martin Reinecke committed
87
88
89
            else:
                raise ValueError("Shape mismatch")
        elif val is None:
90
            self._val = dobj.empty(self.domain.shape, dtype=dtype)
Martin Reinecke's avatar
cleanup    
Martin Reinecke committed
91
92
        else:
            raise TypeError("unknown source type")
csongor's avatar
csongor committed
93

Martin Reinecke's avatar
Martin Reinecke committed
94
95
    @staticmethod
    def _parse_domain(domain, val=None):
96
        if domain is None:
97
            if isinstance(val, Field):
Martin Reinecke's avatar
tweaks    
Martin Reinecke committed
98
99
                return val.domain
            if np.isscalar(val):
100
                return DomainTuple.make(())  # empty domain tuple
Martin Reinecke's avatar
tweaks    
Martin Reinecke committed
101
            raise TypeError("could not infer domain from value")
Martin Reinecke's avatar
Martin Reinecke committed
102
        return DomainTuple.make(domain)
103

Martin Reinecke's avatar
Martin Reinecke committed
104
    # MR: this needs some rethinking ... do we need to have at least float64?
Martin Reinecke's avatar
Martin Reinecke committed
105
106
    @staticmethod
    def _infer_dtype(dtype, val):
Martin Reinecke's avatar
Martin Reinecke committed
107
108
109
110
111
        if val is None or dtype is not None:
            return np.result_type(dtype, np.float64)
        if isinstance(val, Field):
            return val.dtype
        return np.result_type(val, np.float64)
112

113
    # ---Factory methods---
114

Martin Reinecke's avatar
Martin Reinecke committed
115
116
    @staticmethod
    def from_random(random_type, domain, dtype=np.float64, **kwargs):
117
118
119
120
121
122
123
        """ Draws a random field with the given parameters.

        Parameters
        ----------
        random_type : String
            'pm1', 'normal', 'uniform' are the supported arguments for this
            method.
Theo Steininger's avatar
Theo Steininger committed
124

125
126
        domain : DomainObject
            The domain of the output random field
Theo Steininger's avatar
Theo Steininger committed
127

128
129
        dtype : type
            The datatype of the output random field
Theo Steininger's avatar
Theo Steininger committed
130

131
132
133
134
135
136
137
        Returns
        -------
        out : Field
            The output object.

        See Also
        --------
138
        power_synthesize
139
        """
Theo Steininger's avatar
Theo Steininger committed
140

Martin Reinecke's avatar
Martin Reinecke committed
141
        domain = DomainTuple.make(domain)
142
        generator_function = getattr(Random, random_type)
143
        return Field(domain=domain, val=generator_function(dtype=dtype,
Martin Reinecke's avatar
Martin Reinecke committed
144
                     shape=domain.shape, **kwargs))
145

146
147
    # ---Powerspectral methods---

Martin Reinecke's avatar
Martin Reinecke committed
148
149
    def power_analyze(self, spaces=None, binbounds=None,
                      keep_phase_information=False):
Theo Steininger's avatar
Theo Steininger committed
150
        """ Computes the square root power spectrum for a subspace of `self`.
Theo Steininger's avatar
Theo Steininger committed
151

Theo Steininger's avatar
Theo Steininger committed
152
153
154
        Creates a PowerSpace for the space addressed by `spaces` with the given
        binning and computes the power spectrum as a Field over this
        PowerSpace. This can only be done if the subspace to  be analyzed is a
155
        harmonic space. The resulting field has the same units as the initial
Theo Steininger's avatar
Theo Steininger committed
156
        field, corresponding to the square root of the power spectrum.
157
158
159

        Parameters
        ----------
Theo Steininger's avatar
Theo Steininger committed
160
        spaces : int *optional*
Martin Reinecke's avatar
Martin Reinecke committed
161
            The subspace for which the powerspectrum shall be computed.
Theo Steininger's avatar
Theo Steininger committed
162
163
164
            (default : None).
        binbounds : array-like *optional*
            Inner bounds of the bins (default : None).
Martin Reinecke's avatar
Martin Reinecke committed
165
            if binbounds==None : bins are inferred.
166
167
168
169
170
171
172
173
174
175
        keep_phase_information : boolean, *optional*
            If False, return a real-valued result containing the power spectrum
            of the input Field.
            If True, return a complex-valued result whose real component
            contains the power spectrum computed from the real part of the
            input Field, and whose imaginary component contains the power
            spectrum computed from the imaginary part of the input Field.
            The absolute value of this result should be identical to the output
            of power_analyze with keep_phase_information=False.
            (default : False).
Theo Steininger's avatar
Theo Steininger committed
176

177
178
        Raise
        -----
Martin Reinecke's avatar
Martin Reinecke committed
179
180
        TypeError
            Raised if any of the input field's domains is not harmonic
Theo Steininger's avatar
Theo Steininger committed
181

182
183
        Returns
        -------
Theo Steininger's avatar
Theo Steininger committed
184
        out : Field
Martin Reinecke's avatar
Martin Reinecke committed
185
            The output object. Its domain is a PowerSpace and it contains
186
187
188
189
190
            the power spectrum of 'self's field.

        See Also
        --------
        power_synthesize, PowerSpace
Theo Steininger's avatar
Theo Steininger committed
191

192
        """
Theo Steininger's avatar
Theo Steininger committed
193

Theo Steininger's avatar
Theo Steininger committed
194
        # check if all spaces in `self.domain` are either harmonic or
195
196
197
        # power_space instances
        for sp in self.domain:
            if not sp.harmonic and not isinstance(sp, PowerSpace):
Martin Reinecke's avatar
Martin Reinecke committed
198
199
                print("WARNING: Field has a space in `domain` which is "
                      "neither harmonic nor a PowerSpace.")
200
201

        # check if the `spaces` input is valid
202
        if spaces is None:
Martin Reinecke's avatar
Martin Reinecke committed
203
            spaces = range(len(self.domain))
Martin Reinecke's avatar
Martin Reinecke committed
204
205
        else:
            spaces = utilities.cast_iseq_to_tuple(spaces)
206
207

        if len(spaces) == 0:
Martin Reinecke's avatar
cleanup    
Martin Reinecke committed
208
            raise ValueError("No space for analysis specified.")
209

210
        if keep_phase_information:
211
            parts = [self.real*self.real, self.imag*self.imag]
212
        else:
213
            parts = [self.real*self.real + self.imag*self.imag]
214

Martin Reinecke's avatar
Martin Reinecke committed
215
        parts = [ part.weight(1,spaces) for part in parts ]
216
        for space_index in spaces:
Martin Reinecke's avatar
Martin Reinecke committed
217
218
            parts = [self._single_power_analyze(field=part,
                                                idx=space_index,
219
                                                binbounds=binbounds)
220
                     for part in parts]
221

222
        return parts[0] + 1j*parts[1] if keep_phase_information else parts[0]
223

Martin Reinecke's avatar
Martin Reinecke committed
224
    @staticmethod
Martin Reinecke's avatar
Martin Reinecke committed
225
    def _single_power_analyze(field, idx, binbounds):
226
        from .operators.power_projection_operator import PowerProjectionOperator
Martin Reinecke's avatar
Martin Reinecke committed
227
        power_domain = PowerSpace(field.domain[idx], binbounds)
228
229
        ppo = PowerProjectionOperator(field.domain,idx,power_domain)
        return ppo(field)
230

Martin Reinecke's avatar
Martin Reinecke committed
231
    def _compute_spec(self, spaces):
232
233
        from .operators.power_projection_operator import PowerProjectionOperator
        from .basic_arithmetics import sqrt
234
235
        if spaces is None:
            spaces = range(len(self.domain))
Martin Reinecke's avatar
Martin Reinecke committed
236
237
        else:
            spaces = utilities.cast_iseq_to_tuple(spaces)
238
239
240

        # create the result domain
        result_domain = list(self.domain)
241
242

        spec = sqrt(self)
243
244
        for i in spaces:
            result_domain[i] = self.domain[i].harmonic_partner
245
246
            ppo = PowerProjectionOperator(result_domain,i,self.domain[i])
            spec = ppo.adjoint_times(spec)
247

248
        return spec
249
250

    def power_synthesize(self, spaces=None, real_power=True, real_signal=True):
Theo Steininger's avatar
Theo Steininger committed
251
        """ Yields a sampled field with `self`**2 as its power spectrum.
Theo Steininger's avatar
Theo Steininger committed
252

Theo Steininger's avatar
Theo Steininger committed
253
        This method draws a Gaussian random field in the harmonic partner
Martin Reinecke's avatar
typos    
Martin Reinecke committed
254
        domain of this field's domains, using this field as power spectrum.
Theo Steininger's avatar
Theo Steininger committed
255

256
257
258
        Parameters
        ----------
        spaces : {tuple, int, None} *optional*
Theo Steininger's avatar
Theo Steininger committed
259
260
261
            Specifies the subspace containing all the PowerSpaces which
            should be converted (default : None).
            if spaces==None : Tries to convert the whole domain.
262
        real_power : boolean *optional*
Theo Steininger's avatar
Theo Steininger committed
263
264
            Determines whether the power spectrum is treated as intrinsically
            real or complex (default : True).
265
        real_signal : boolean *optional*
Theo Steininger's avatar
Theo Steininger committed
266
267
            True will result in a purely real signal-space field
            (default : True).
Theo Steininger's avatar
Theo Steininger committed
268

269
270
271
272
        Returns
        -------
        out : Field
            The output object. A random field created with the power spectrum
Theo Steininger's avatar
Theo Steininger committed
273
            stored in the `spaces` in `self`.
274

Theo Steininger's avatar
Theo Steininger committed
275
276
277
278
279
280
        Notes
        -----
        For this the spaces specified by `spaces` must be a PowerSpace.
        This expects this field to be the square root of a power spectrum, i.e.
        to have the unit of the field to be sampled.

281
282
283
        See Also
        --------
        power_analyze
Theo Steininger's avatar
Theo Steininger committed
284
285
286
287
288

        Raises
        ------
        ValueError : If domain specified by `spaces` is not a PowerSpace.

289
        """
Theo Steininger's avatar
Theo Steininger committed
290

Martin Reinecke's avatar
Martin Reinecke committed
291
        spec = self._compute_spec(spaces)
292
293
294

        # create random samples: one or two, depending on whether the
        # power spectrum is real or complex
295
        result = [self.from_random('normal', mean=0., std=1.,
Martin Reinecke's avatar
Martin Reinecke committed
296
                                   domain=spec.domain,
297
298
299
300
301
302
303
                                   dtype=np.float if real_signal
                                   else np.complex)
                  for x in range(1 if real_power else 2)]

        # MR: dummy call - will be removed soon
        if real_signal:
            self.from_random('normal', mean=0., std=1.,
Martin Reinecke's avatar
Martin Reinecke committed
304
                             domain=spec.domain, dtype=np.float)
305
306

        # apply the rescaler to the random fields
307
        result[0] *= spec.real
308
        if not real_power:
309
            result[1] *= spec.imag
310

311
        return result[0] if real_power else result[0] + 1j*result[1]
312

313
    def power_synthesize_special(self, spaces=None):
Martin Reinecke's avatar
Martin Reinecke committed
314
        spec = self._compute_spec(spaces)
315
316
317

        # MR: dummy call - will be removed soon
        self.from_random('normal', mean=0., std=1.,
Martin Reinecke's avatar
Martin Reinecke committed
318
                         domain=spec.domain, dtype=np.complex)
319
320

        return spec.real
321

Theo Steininger's avatar
Theo Steininger committed
322
    # ---Properties---
323

Theo Steininger's avatar
Theo Steininger committed
324
325
    @property
    def val(self):
Martin Reinecke's avatar
stage1    
Martin Reinecke committed
326
        """ Returns the data object associated with this Field.
Martin Reinecke's avatar
PEP8    
Martin Reinecke committed
327
        No copy is made.
Theo Steininger's avatar
Theo Steininger committed
328

329
330
        Returns
        -------
Martin Reinecke's avatar
stage1    
Martin Reinecke committed
331
        out : numpy.ndarray
332
        """
Martin Reinecke's avatar
Martin Reinecke committed
333
        return self._val
csongor's avatar
csongor committed
334

Martin Reinecke's avatar
Martin Reinecke committed
335
336
337
338
    @property
    def dtype(self):
        return self._val.dtype

339
340
    @property
    def shape(self):
Theo Steininger's avatar
Theo Steininger committed
341
        """ Returns the total shape of the Field's data array.
Theo Steininger's avatar
Theo Steininger committed
342

343
344
345
        Returns
        -------
        out : tuple
Martin Reinecke's avatar
Martin Reinecke committed
346
            The output object. The tuple contains the dimensions of the spaces
347
            in domain.
Martin Reinecke's avatar
PEP8    
Martin Reinecke committed
348
       """
Martin Reinecke's avatar
Martin Reinecke committed
349
        return self.domain.shape
csongor's avatar
csongor committed
350

351
352
    @property
    def dim(self):
Theo Steininger's avatar
Theo Steininger committed
353
        """ Returns the total number of pixel-dimensions the field has.
Theo Steininger's avatar
Theo Steininger committed
354

Theo Steininger's avatar
Theo Steininger committed
355
        Effectively, all values from shape are multiplied.
Theo Steininger's avatar
Theo Steininger committed
356

357
358
359
360
361
        Returns
        -------
        out : int
            The dimension of the Field.
        """
Martin Reinecke's avatar
Martin Reinecke committed
362
        return self.domain.dim
csongor's avatar
csongor committed
363

Theo Steininger's avatar
Theo Steininger committed
364
365
366
367
    @property
    def real(self):
        """ The real part of the field (data is not copied).
        """
Martin Reinecke's avatar
PEP8    
Martin Reinecke committed
368
        return Field(self.domain, self.val.real)
Theo Steininger's avatar
Theo Steininger committed
369
370
371
372
373

    @property
    def imag(self):
        """ The imaginary part of the field (data is not copied).
        """
Martin Reinecke's avatar
PEP8    
Martin Reinecke committed
374
        return Field(self.domain, self.val.imag)
Theo Steininger's avatar
Theo Steininger committed
375

Theo Steininger's avatar
Theo Steininger committed
376
    # ---Special unary/binary operations---
377

Martin Reinecke's avatar
Martin Reinecke committed
378
    def copy(self):
379
        """ Returns a full copy of the Field.
Theo Steininger's avatar
Theo Steininger committed
380

Martin Reinecke's avatar
Martin Reinecke committed
381
        The returned object will be an identical copy of the original Field.
Theo Steininger's avatar
Theo Steininger committed
382

383
384
385
386
387
        Returns
        -------
        out : Field
            The output object. An identical copy of 'self'.
        """
Martin Reinecke's avatar
Martin Reinecke committed
388
        return Field(val=self, copy=True)
csongor's avatar
csongor committed
389

390
391
    def scalar_weight(self, spaces=None):
        if np.isscalar(spaces):
392
            return self.domain[spaces].scalar_dvol()
393
394
395

        if spaces is None:
            spaces = range(len(self.domain))
Martin Reinecke's avatar
Martin Reinecke committed
396
        res = 1.
397
        for i in spaces:
398
            tmp = self.domain[i].scalar_dvol()
399
400
401
402
403
            if tmp is None:
                return None
            res *= tmp
        return res

404
    def weight(self, power=1, spaces=None, out=None):
Theo Steininger's avatar
Theo Steininger committed
405
        """ Weights the pixels of `self` with their invidual pixel-volume.
406
407
408
409

        Parameters
        ----------
        power : number
Theo Steininger's avatar
Theo Steininger committed
410
            The pixels get weighted with the volume-factor**power.
Theo Steininger's avatar
Theo Steininger committed
411

Theo Steininger's avatar
Theo Steininger committed
412
413
        spaces : tuple of ints
            Determines on which subspace the operation takes place.
Theo Steininger's avatar
Theo Steininger committed
414

415
416
417
418
419
        out : Field or None
            if not None, the result is returned in a new Field
            otherwise the contents of "out" are overwritten with the result.
            "out" may be identical to "self"!

420
421
422
        Returns
        -------
        out : Field
Theo Steininger's avatar
Theo Steininger committed
423
            The weighted field.
424
425

        """
426
427
428
429
430
        if out is None:
            out = self.copy()
        else:
            if out is not self:
                out.copy_content_from(self)
csongor's avatar
csongor committed
431

csongor's avatar
csongor committed
432
        if spaces is None:
Martin Reinecke's avatar
Martin Reinecke committed
433
            spaces = range(len(self.domain))
Martin Reinecke's avatar
Martin Reinecke committed
434
435
        else:
            spaces = utilities.cast_iseq_to_tuple(spaces)
csongor's avatar
csongor committed
436

437
438
        fct = 1.
        for ind in spaces:
439
            wgt = self.domain[ind].dvol()
440
441
442
            if np.isscalar(wgt):
                fct *= wgt
            else:
443
                new_shape = dobj.ones(len(self.shape), dtype=np.int)
Martin Reinecke's avatar
Martin Reinecke committed
444
445
                new_shape[self.domain.axes[ind][0]:
                          self.domain.axes[ind][-1]+1] = wgt.shape
446
                wgt = wgt.reshape(new_shape)
447
                out *= wgt**power
448
        fct = fct**power
Martin Reinecke's avatar
Martin Reinecke committed
449
        if fct != 1.:
450
            out *= fct
451

452
        return out
csongor's avatar
csongor committed
453

Martin Reinecke's avatar
Martin Reinecke committed
454
    def vdot(self, x=None, spaces=None):
Theo Steininger's avatar
Theo Steininger committed
455
        """ Computes the volume-factor-aware dot product of 'self' with x.
Theo Steininger's avatar
Theo Steininger committed
456

457
458
459
        Parameters
        ----------
        x : Field
Theo Steininger's avatar
Theo Steininger committed
460
            The domain of x must contain `self.domain`
Theo Steininger's avatar
Theo Steininger committed
461

Theo Steininger's avatar
Theo Steininger committed
462
        spaces : tuple of ints
463
464
            If the domain of `self` and `x` are not the same, `spaces` defines
            which domains of `x` are mapped to those of `self`.
Theo Steininger's avatar
Theo Steininger committed
465

466
467
468
        Returns
        -------
        out : float, complex
Theo Steininger's avatar
Theo Steininger committed
469

470
        """
471
472
473
        if not isinstance(x, Field):
            raise ValueError("The dot-partner must be an instance of " +
                             "the NIFTy field class")
Theo Steininger's avatar
Theo Steininger committed
474

Martin Reinecke's avatar
Martin Reinecke committed
475
        # Compute the dot respecting the fact of discrete/continuous spaces
Martin Reinecke's avatar
Martin Reinecke committed
476
477
        tmp = self.scalar_weight(spaces)
        if tmp is None:
478
            fct = 1.
Martin Reinecke's avatar
Martin Reinecke committed
479
            y = self.weight(power=1)
480
        else:
Martin Reinecke's avatar
Martin Reinecke committed
481
482
            y = self
            fct = tmp
Theo Steininger's avatar
Theo Steininger committed
483

484
        if spaces is None:
485
            return fct*dobj.vdot(y.val.ravel(), x.val.ravel())
486
        else:
487
488
489
490
491
492
493
494
            spaces = utilities.cast_iseq_to_tuple(spaces)
            active_axes = []
            for i in spaces:
                active_axes += self.domain.axes[i]
            res = 0.
            for sl in utilities.get_slice_list(self.shape, active_axes):
                res += dobj.vdot(y.val, x.val[sl])
            return res*fct
Theo Steininger's avatar
Theo Steininger committed
495

Theo Steininger's avatar
Theo Steininger committed
496
    def norm(self):
Martin Reinecke's avatar
tweaks    
Martin Reinecke committed
497
        """ Computes the L2-norm of the field values.
csongor's avatar
csongor committed
498

Theo Steininger's avatar
Theo Steininger committed
499
500
        Returns
        -------
Martin Reinecke's avatar
Martin Reinecke committed
501
        norm : float
Martin Reinecke's avatar
tweaks    
Martin Reinecke committed
502
            The L2-norm of the field values.
csongor's avatar
csongor committed
503
504

        """
505
        return np.sqrt(np.abs(self.vdot(x=self)))
csongor's avatar
csongor committed
506

Martin Reinecke's avatar
tweaks    
Martin Reinecke committed
507
    def conjugate(self):
Martin Reinecke's avatar
cleanup    
Martin Reinecke committed
508
        """ Returns the complex conjugate of the field.
Theo Steininger's avatar
Theo Steininger committed
509

510
511
512
513
        Returns
        -------
        cc : field
            The complex conjugated field.
csongor's avatar
csongor committed
514
515

        """
Martin Reinecke's avatar
tweaks    
Martin Reinecke committed
516
        return Field(self.domain, self.val.conjugate(), self.dtype)
csongor's avatar
csongor committed
517

Theo Steininger's avatar
Theo Steininger committed
518
    # ---General unary/contraction methods---
519

Theo Steininger's avatar
Theo Steininger committed
520
521
    def __pos__(self):
        return self.copy()
522

Theo Steininger's avatar
Theo Steininger committed
523
    def __neg__(self):
Martin Reinecke's avatar
PEP8    
Martin Reinecke committed
524
        return Field(self.domain, -self.val, self.dtype)
csongor's avatar
csongor committed
525

Theo Steininger's avatar
Theo Steininger committed
526
    def __abs__(self):
527
        return Field(self.domain, dobj.abs(self.val), self.dtype)
csongor's avatar
csongor committed
528

529
    def _contraction_helper(self, op, spaces):
Theo Steininger's avatar
Theo Steininger committed
530
        if spaces is None:
531
            return getattr(self.val, op)()
Martin Reinecke's avatar
Martin Reinecke committed
532
533
        else:
            spaces = utilities.cast_iseq_to_tuple(spaces)
csongor's avatar
csongor committed
534

Martin Reinecke's avatar
Martin Reinecke committed
535
        axes_list = tuple(self.domain.axes[sp_index] for sp_index in spaces)
536

Martin Reinecke's avatar
Martin Reinecke committed
537
        if len(axes_list) > 0:
Theo Steininger's avatar
Theo Steininger committed
538
            axes_list = reduce(lambda x, y: x+y, axes_list)
csongor's avatar
csongor committed
539

Martin Reinecke's avatar
stage1    
Martin Reinecke committed
540
        # perform the contraction on the data
541
        data = getattr(self.val, op)(axis=axes_list)
csongor's avatar
csongor committed
542

Theo Steininger's avatar
Theo Steininger committed
543
544
545
        # check if the result is scalar or if a result_field must be constr.
        if np.isscalar(data):
            return data
csongor's avatar
csongor committed
546
        else:
Theo Steininger's avatar
Theo Steininger committed
547
            return_domain = tuple(self.domain[i]
Martin Reinecke's avatar
Martin Reinecke committed
548
                                  for i in range(len(self.domain))
Theo Steininger's avatar
Theo Steininger committed
549
                                  if i not in spaces)
550

Martin Reinecke's avatar
updates    
Martin Reinecke committed
551
            return Field(domain=return_domain, val=data, copy=False)
csongor's avatar
csongor committed
552

553
554
    def sum(self, spaces=None):
        return self._contraction_helper('sum', spaces)
csongor's avatar
csongor committed
555

556
557
558
559
    def integrate(self, spaces=None):
        tmp = self.weight(1, spaces=spaces)
        return tmp.sum(spaces)

560
561
    def prod(self, spaces=None):
        return self._contraction_helper('prod', spaces)
csongor's avatar
csongor committed
562

563
564
    def all(self, spaces=None):
        return self._contraction_helper('all', spaces)
csongor's avatar
csongor committed
565

566
567
    def any(self, spaces=None):
        return self._contraction_helper('any', spaces)
csongor's avatar
csongor committed
568

569
570
    def min(self, spaces=None):
        return self._contraction_helper('min', spaces)
csongor's avatar
csongor committed
571

572
573
    def max(self, spaces=None):
        return self._contraction_helper('max', spaces)
csongor's avatar
csongor committed
574

575
576
    def mean(self, spaces=None):
        return self._contraction_helper('mean', spaces)
csongor's avatar
csongor committed
577

578
579
    def var(self, spaces=None):
        return self._contraction_helper('var', spaces)
csongor's avatar
csongor committed
580

581
582
    def std(self, spaces=None):
        return self._contraction_helper('std', spaces)
csongor's avatar
csongor committed
583

584
585
586
587
588
589
590
    def copy_content_from(self, other):
        if not isinstance(other, Field):
            raise TypeError("argument must be a Field")
        if other.domain != self.domain:
            raise ValueError("domains are incompatible.")
        self.val[()] = other.val

Theo Steininger's avatar
Theo Steininger committed
591
    # ---General binary methods---
csongor's avatar
csongor committed
592

593
    def _binary_helper(self, other, op):
csongor's avatar
csongor committed
594
        # if other is a field, make sure that the domains match
595
        if isinstance(other, Field):
596
597
            if other.domain != self.domain:
                raise ValueError("domains are incompatible.")
Martin Reinecke's avatar
Martin Reinecke committed
598
599
            tval = getattr(self.val, op)(other.val)
            return self if tval is self.val else Field(self.domain, tval)
csongor's avatar
csongor committed
600

Martin Reinecke's avatar
Martin Reinecke committed
601
602
        tval = getattr(self.val, op)(other)
        return self if tval is self.val else Field(self.domain, tval)
csongor's avatar
csongor committed
603
604

    def __add__(self, other):
Theo Steininger's avatar
Theo Steininger committed
605
        return self._binary_helper(other, op='__add__')
606

607
    def __radd__(self, other):
Theo Steininger's avatar
Theo Steininger committed
608
        return self._binary_helper(other, op='__radd__')
csongor's avatar
csongor committed
609
610

    def __iadd__(self, other):
611
        return self._binary_helper(other, op='__iadd__')
csongor's avatar
csongor committed
612
613

    def __sub__(self, other):
Theo Steininger's avatar
Theo Steininger committed
614
        return self._binary_helper(other, op='__sub__')
csongor's avatar
csongor committed
615
616

    def __rsub__(self, other):
Theo Steininger's avatar
Theo Steininger committed
617
        return self._binary_helper(other, op='__rsub__')
csongor's avatar
csongor committed
618
619

    def __isub__(self, other):
620
        return self._binary_helper(other, op='__isub__')
csongor's avatar
csongor committed
621
622

    def __mul__(self, other):
Theo Steininger's avatar
Theo Steininger committed
623
        return self._binary_helper(other, op='__mul__')
624

625
    def __rmul__(self, other):
Theo Steininger's avatar
Theo Steininger committed
626
        return self._binary_helper(other, op='__rmul__')
csongor's avatar
csongor committed
627
628

    def __imul__(self, other):
629
        return self._binary_helper(other, op='__imul__')
csongor's avatar
csongor committed
630
631

    def __div__(self, other):
Theo Steininger's avatar
Theo Steininger committed
632
        return self._binary_helper(other, op='__div__')
csongor's avatar
csongor committed
633

Martin Reinecke's avatar
Martin Reinecke committed
634
635
636
    def __truediv__(self, other):
        return self._binary_helper(other, op='__truediv__')

csongor's avatar
csongor committed
637
    def __rdiv__(self, other):
Theo Steininger's avatar
Theo Steininger committed
638
        return self._binary_helper(other, op='__rdiv__')
csongor's avatar
csongor committed
639

Martin Reinecke's avatar
Martin Reinecke committed
640
641
642
    def __rtruediv__(self, other):
        return self._binary_helper(other, op='__rtruediv__')

csongor's avatar
csongor committed
643
    def __idiv__(self, other):
644
        return self._binary_helper(other, op='__idiv__')
645

csongor's avatar
csongor committed
646
    def __pow__(self, other):
Theo Steininger's avatar
Theo Steininger committed
647
        return self._binary_helper(other, op='__pow__')
csongor's avatar
csongor committed
648
649

    def __rpow__(self, other):
Theo Steininger's avatar
Theo Steininger committed
650
        return self._binary_helper(other, op='__rpow__')
csongor's avatar
csongor committed
651
652

    def __ipow__(self, other):
653
        return self._binary_helper(other, op='__ipow__')
csongor's avatar
csongor committed
654
655

    def __lt__(self, other):
Theo Steininger's avatar
Theo Steininger committed
656
        return self._binary_helper(other, op='__lt__')
csongor's avatar
csongor committed
657
658

    def __le__(self, other):
Theo Steininger's avatar
Theo Steininger committed
659
        return self._binary_helper(other, op='__le__')
csongor's avatar
csongor committed
660
661
662
663
664

    def __ne__(self, other):
        if other is None:
            return True
        else:
Theo Steininger's avatar
Theo Steininger committed
665
            return self._binary_helper(other, op='__ne__')
csongor's avatar
csongor committed
666
667
668
669
670

    def __eq__(self, other):
        if other is None:
            return False
        else:
Theo Steininger's avatar
Theo Steininger committed
671
            return self._binary_helper(other, op='__eq__')
csongor's avatar
csongor committed
672
673

    def __ge__(self, other):
Theo Steininger's avatar
Theo Steininger committed
674
        return self._binary_helper(other, op='__ge__')
csongor's avatar
csongor committed
675
676

    def __gt__(self, other):
Theo Steininger's avatar
Theo Steininger committed
677
678
679
        return self._binary_helper(other, op='__gt__')

    def __repr__(self):
Martin Reinecke's avatar
Martin Reinecke committed
680
        return "<nifty2go.Field>"
Theo Steininger's avatar
Theo Steininger committed
681
682
683
684

    def __str__(self):
        minmax = [self.min(), self.max()]
        mean = self.mean()
Martin Reinecke's avatar
Martin Reinecke committed
685
        return "nifty2go.Field instance\n- domain      = " + \
Theo Steininger's avatar
Theo Steininger committed
686
               repr(self.domain) + \
687
               "\n- val         = " + repr(self.val) + \
Theo Steininger's avatar
Theo Steininger committed
688
689
               "\n  - min.,max. = " + str(minmax) + \
               "\n  - mean = " + str(mean)