field.py 16.7 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
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
from . import utilities
Martin Reinecke's avatar
Martin Reinecke committed
23
from .domain_tuple import DomainTuple
Martin Reinecke's avatar
Martin Reinecke committed
24
from functools import reduce
25
from . import dobj
26

Martin Reinecke's avatar
cleanup    
Martin Reinecke committed
27
28
__all__ = ["Field", "sqrt", "exp", "log", "conjugate"]

29

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

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

37
38
    Parameters
    ----------
Martin Reinecke's avatar
Martin Reinecke committed
39
    domain : None, DomainTuple, tuple of DomainObjects, or single DomainObject
Theo Steininger's avatar
Theo Steininger committed
40

Martin Reinecke's avatar
Martin Reinecke committed
41
    val : None, Field, data_object, or scalar
42
        The values the array should contain after init. A scalar input will
Martin Reinecke's avatar
Martin Reinecke committed
43
44
        fill the whole array with this scalar. If a data_object is provided,
        its dimensions must match the domain's.
Theo Steininger's avatar
Theo Steininger committed
45

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

49
50
51
52
    copy: boolean

    Attributes
    ----------
Martin Reinecke's avatar
Martin Reinecke committed
53
    val : data_object
Theo Steininger's avatar
Theo Steininger committed
54

Martin Reinecke's avatar
Martin Reinecke committed
55
    domain : DomainTuple
Martin Reinecke's avatar
Martin Reinecke committed
56

57
58
59
    dtype : type
        Contains the datatype stored in the Field.
    """
60

Martin Reinecke's avatar
stage1    
Martin Reinecke committed
61
    def __init__(self, domain=None, val=None, dtype=None, copy=False):
62
        self.domain = self._parse_domain(domain=domain, val=val)
63

Martin Reinecke's avatar
Martin Reinecke committed
64
        dtype = self._infer_dtype(dtype=dtype, val=val)
Martin Reinecke's avatar
cleanup    
Martin Reinecke committed
65
        if isinstance(val, Field):
Martin Reinecke's avatar
PEP8    
Martin Reinecke committed
66
            if self.domain != val.domain:
Martin Reinecke's avatar
cleanup    
Martin Reinecke committed
67
                raise ValueError("Domain mismatch")
68
            self._val = dobj.from_object(val.val, dtype=dtype, copy=copy)
Martin Reinecke's avatar
cleanup    
Martin Reinecke committed
69
        elif (np.isscalar(val)):
70
71
            self._val = dobj.full(self.domain.shape, dtype=dtype,
                                  fill_value=val)
72
        elif isinstance(val, dobj.data_object):
Martin Reinecke's avatar
Martin Reinecke committed
73
            if self.domain.shape == val.shape:
74
                self._val = dobj.from_object(val, dtype=dtype, copy=copy)
Martin Reinecke's avatar
cleanup    
Martin Reinecke committed
75
76
77
            else:
                raise ValueError("Shape mismatch")
        elif val is None:
78
            self._val = dobj.empty(self.domain.shape, dtype=dtype)
Martin Reinecke's avatar
cleanup    
Martin Reinecke committed
79
80
        else:
            raise TypeError("unknown source type")
csongor's avatar
csongor committed
81

82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
    @staticmethod
    def full(domain, val, dtype=None):
        if not np.isscalar(val):
            raise TypeError("val must be a scalar")
        return Field(DomainTuple.make(domain), val, dtype)

    @staticmethod
    def ones(domain, dtype=None):
        return Field(DomainTuple.make(domain), 1., dtype)

    @staticmethod
    def zeros(domain, dtype=None):
        return Field(DomainTuple.make(domain), 0., dtype)

    @staticmethod
    def empty(domain, dtype=None):
        return Field(DomainTuple.make(domain), None, dtype)

    @staticmethod
    def full_like(field, val, dtype=None):
        if not isinstance(field, Field):
            raise TypeError("field must be of Field type")
        return Field.full(field.domain, val, dtype)

    @staticmethod
    def zeros_like(field, dtype=None):
        if not isinstance(field, Field):
            raise TypeError("field must be of Field type")
        if dtype is None:
            dtype = field.dtype
        return Field.zeros(field.domain, dtype)

    @staticmethod
    def ones_like(field, dtype=None):
        if not isinstance(field, Field):
            raise TypeError("field must be of Field type")
        if dtype is None:
            dtype = field.dtype
        return Field.ones(field.domain, dtype)

    @staticmethod
    def empty_like(field, dtype=None):
        if not isinstance(field, Field):
            raise TypeError("field must be of Field type")
        if dtype is None:
            dtype = field.dtype
        return Field.empty(field.domain, dtype)

Martin Reinecke's avatar
Martin Reinecke committed
130
131
    @staticmethod
    def _parse_domain(domain, val=None):
132
        if domain is None:
133
            if isinstance(val, Field):
Martin Reinecke's avatar
tweaks    
Martin Reinecke committed
134
135
                return val.domain
            if np.isscalar(val):
136
                return DomainTuple.make(())  # empty domain tuple
Martin Reinecke's avatar
tweaks    
Martin Reinecke committed
137
            raise TypeError("could not infer domain from value")
Martin Reinecke's avatar
Martin Reinecke committed
138
        return DomainTuple.make(domain)
139

Martin Reinecke's avatar
Martin Reinecke committed
140
    # MR: this needs some rethinking ... do we need to have at least float64?
Martin Reinecke's avatar
Martin Reinecke committed
141
142
    @staticmethod
    def _infer_dtype(dtype, val):
Martin Reinecke's avatar
Martin Reinecke committed
143
144
145
146
147
        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)
148

Martin Reinecke's avatar
Martin Reinecke committed
149
150
    @staticmethod
    def from_random(random_type, domain, dtype=np.float64, **kwargs):
151
152
153
154
155
156
157
        """ 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
158

159
160
        domain : DomainObject
            The domain of the output random field
Theo Steininger's avatar
Theo Steininger committed
161

162
163
        dtype : type
            The datatype of the output random field
Theo Steininger's avatar
Theo Steininger committed
164

165
166
167
168
169
        Returns
        -------
        out : Field
            The output object.
        """
Martin Reinecke's avatar
Martin Reinecke committed
170
        domain = DomainTuple.make(domain)
Martin Reinecke's avatar
Martin Reinecke committed
171
172
173
        return Field(domain=domain,
                     val=dobj.from_random(random_type, dtype=dtype,
                                          shape=domain.shape, **kwargs))
174

Martin Reinecke's avatar
Martin Reinecke committed
175
176
177
    def fill(self, fill_value):
        self._val.fill(fill_value)

theos's avatar
theos committed
178
179
    @property
    def val(self):
Martin Reinecke's avatar
stage1    
Martin Reinecke committed
180
        """ Returns the data object associated with this Field.
Martin Reinecke's avatar
PEP8    
Martin Reinecke committed
181
        No copy is made.
182
        """
Martin Reinecke's avatar
Martin Reinecke committed
183
        return self._val
csongor's avatar
csongor committed
184

Martin Reinecke's avatar
Martin Reinecke committed
185
186
187
188
    @property
    def dtype(self):
        return self._val.dtype

189
190
    @property
    def shape(self):
Theo Steininger's avatar
Theo Steininger committed
191
        """ Returns the total shape of the Field's data array.
Theo Steininger's avatar
Theo Steininger committed
192

193
194
        Returns
        -------
Martin Reinecke's avatar
Martin Reinecke committed
195
196
        Integer tuple containing the dimensions of the spaces in domain.
        """
Martin Reinecke's avatar
Martin Reinecke committed
197
        return self.domain.shape
csongor's avatar
csongor committed
198

199
200
    @property
    def dim(self):
Theo Steininger's avatar
Theo Steininger committed
201
        """ Returns the total number of pixel-dimensions the field has.
Theo Steininger's avatar
Theo Steininger committed
202

Theo Steininger's avatar
Theo Steininger committed
203
        Effectively, all values from shape are multiplied.
Theo Steininger's avatar
Theo Steininger committed
204

205
206
207
208
209
        Returns
        -------
        out : int
            The dimension of the Field.
        """
Martin Reinecke's avatar
Martin Reinecke committed
210
        return self.domain.dim
csongor's avatar
csongor committed
211

Theo Steininger's avatar
Theo Steininger committed
212
213
    @property
    def real(self):
Martin Reinecke's avatar
Martin Reinecke committed
214
        """ The real part of the field (data is not copied)."""
Martin Reinecke's avatar
PEP8    
Martin Reinecke committed
215
        return Field(self.domain, self.val.real)
Theo Steininger's avatar
Theo Steininger committed
216
217
218

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

Martin Reinecke's avatar
Martin Reinecke committed
222
    def copy(self):
223
        """ Returns a full copy of the Field.
Theo Steininger's avatar
Theo Steininger committed
224

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

227
228
229
230
231
        Returns
        -------
        out : Field
            The output object. An identical copy of 'self'.
        """
Martin Reinecke's avatar
Martin Reinecke committed
232
        return Field(val=self, copy=True)
csongor's avatar
csongor committed
233

234
235
    def scalar_weight(self, spaces=None):
        if np.isscalar(spaces):
236
            return self.domain[spaces].scalar_dvol()
237
238
239

        if spaces is None:
            spaces = range(len(self.domain))
Martin Reinecke's avatar
Martin Reinecke committed
240
        res = 1.
241
        for i in spaces:
242
            tmp = self.domain[i].scalar_dvol()
243
244
245
246
247
            if tmp is None:
                return None
            res *= tmp
        return res

248
    def weight(self, power=1, spaces=None, out=None):
Theo Steininger's avatar
Theo Steininger committed
249
        """ Weights the pixels of `self` with their invidual pixel-volume.
250
251
252
253

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

Theo Steininger's avatar
Theo Steininger committed
256
257
        spaces : tuple of ints
            Determines on which subspace the operation takes place.
Theo Steininger's avatar
Theo Steininger committed
258

259
260
261
262
263
        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"!

264
265
266
        Returns
        -------
        out : Field
Theo Steininger's avatar
Theo Steininger committed
267
            The weighted field.
268
        """
269
270
271
272
273
        if out is None:
            out = self.copy()
        else:
            if out is not self:
                out.copy_content_from(self)
csongor's avatar
csongor committed
274

csongor's avatar
csongor committed
275
        if spaces is None:
Martin Reinecke's avatar
Martin Reinecke committed
276
            spaces = range(len(self.domain))
Martin Reinecke's avatar
Martin Reinecke committed
277
278
        else:
            spaces = utilities.cast_iseq_to_tuple(spaces)
csongor's avatar
csongor committed
279

280
281
        fct = 1.
        for ind in spaces:
282
            wgt = self.domain[ind].dvol()
283
284
285
            if np.isscalar(wgt):
                fct *= wgt
            else:
Martin Reinecke's avatar
Martin Reinecke committed
286
                new_shape = np.ones(len(self.shape), dtype=np.int)
Martin Reinecke's avatar
Martin Reinecke committed
287
288
                new_shape[self.domain.axes[ind][0]:
                          self.domain.axes[ind][-1]+1] = wgt.shape
289
                wgt = wgt.reshape(new_shape)
Martin Reinecke's avatar
Martin Reinecke committed
290
291
                if dobj.distaxis(self._val) >= 0 and ind == 0:
                    # we need to distribute the weights along axis 0
Martin Reinecke's avatar
fixes    
Martin Reinecke committed
292
                    wgt = dobj.local_data(dobj.from_global_data(wgt))
293
                out *= wgt**power
294
        fct = fct**power
Martin Reinecke's avatar
Martin Reinecke committed
295
        if fct != 1.:
296
            out *= fct
297

298
        return out
csongor's avatar
csongor committed
299

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

303
304
305
        Parameters
        ----------
        x : Field
Theo Steininger's avatar
Theo Steininger committed
306
            The domain of x must contain `self.domain`
Theo Steininger's avatar
Theo Steininger committed
307

Theo Steininger's avatar
Theo Steininger committed
308
        spaces : tuple of ints
309
310
            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
311

312
313
        Returns
        -------
Martin Reinecke's avatar
Martin Reinecke committed
314
        out : float, complex, either scalar or Field
315
        """
316
317
318
        if not isinstance(x, Field):
            raise ValueError("The dot-partner must be an instance of " +
                             "the NIFTy field class")
theos's avatar
theos committed
319

Martin Reinecke's avatar
Martin Reinecke committed
320
        # Compute the dot respecting the fact of discrete/continuous spaces
Martin Reinecke's avatar
Martin Reinecke committed
321
322
        tmp = self.scalar_weight(spaces)
        if tmp is None:
323
            fct = 1.
Martin Reinecke's avatar
Martin Reinecke committed
324
            y = self.weight(power=1)
325
        else:
Martin Reinecke's avatar
Martin Reinecke committed
326
327
            y = self
            fct = tmp
theos's avatar
theos committed
328

329
        if spaces is None:
Martin Reinecke's avatar
fixes    
Martin Reinecke committed
330
            return fct*dobj.vdot(y.val, x.val)
Martin Reinecke's avatar
Martin Reinecke committed
331
332
333
334
335
336
337
338
339
340
341
342
343

        spaces = utilities.cast_iseq_to_tuple(spaces)
        if spaces == tuple(range(len(self.domain))):  # full contraction
            return fct*dobj.vdot(y.val, x.val)

        raise NotImplementedError("special case for vdot not yet implemented")
        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
theos's avatar
theos committed
344

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

Theo Steininger's avatar
Theo Steininger committed
348
349
        Returns
        -------
Martin Reinecke's avatar
Martin Reinecke committed
350
        norm : float
Martin Reinecke's avatar
tweaks    
Martin Reinecke committed
351
            The L2-norm of the field values.
csongor's avatar
csongor committed
352
        """
353
        return np.sqrt(np.abs(self.vdot(x=self)))
csongor's avatar
csongor committed
354

Martin Reinecke's avatar
tweaks    
Martin Reinecke committed
355
    def conjugate(self):
Martin Reinecke's avatar
cleanup    
Martin Reinecke committed
356
        """ Returns the complex conjugate of the field.
Theo Steininger's avatar
Theo Steininger committed
357

358
359
        Returns
        -------
Martin Reinecke's avatar
Martin Reinecke committed
360
        The complex conjugated field.
csongor's avatar
csongor committed
361
        """
Martin Reinecke's avatar
tweaks    
Martin Reinecke committed
362
        return Field(self.domain, self.val.conjugate(), self.dtype)
csongor's avatar
csongor committed
363

theos's avatar
theos committed
364
    # ---General unary/contraction methods---
365

theos's avatar
theos committed
366
367
    def __pos__(self):
        return self.copy()
368

theos's avatar
theos committed
369
    def __neg__(self):
Martin Reinecke's avatar
PEP8    
Martin Reinecke committed
370
        return Field(self.domain, -self.val, self.dtype)
csongor's avatar
csongor committed
371

theos's avatar
theos committed
372
    def __abs__(self):
373
        return Field(self.domain, dobj.abs(self.val), self.dtype)
csongor's avatar
csongor committed
374

375
    def _contraction_helper(self, op, spaces):
theos's avatar
theos committed
376
        if spaces is None:
377
            return getattr(self.val, op)()
Martin Reinecke's avatar
Martin Reinecke committed
378
379
        else:
            spaces = utilities.cast_iseq_to_tuple(spaces)
csongor's avatar
csongor committed
380

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

Martin Reinecke's avatar
Martin Reinecke committed
383
        if len(axes_list) > 0:
theos's avatar
theos committed
384
            axes_list = reduce(lambda x, y: x+y, axes_list)
csongor's avatar
csongor committed
385

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

theos's avatar
theos committed
389
390
391
        # 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
392
        else:
Martin Reinecke's avatar
Martin Reinecke committed
393
394
            return_domain = tuple(dom
                                  for i, dom in enumerate(self.domain)
theos's avatar
theos committed
395
                                  if i not in spaces)
396

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

399
400
    def sum(self, spaces=None):
        return self._contraction_helper('sum', spaces)
csongor's avatar
csongor committed
401

402
    def integrate(self, spaces=None):
Martin Reinecke's avatar
Martin Reinecke committed
403
404
405
406
407
        swgt = self.scalar_weight(spaces)
        if swgt is not None:
            res = self.sum(spaces)
            res *= swgt
            return res
408
409
410
        tmp = self.weight(1, spaces=spaces)
        return tmp.sum(spaces)

411
412
    def prod(self, spaces=None):
        return self._contraction_helper('prod', spaces)
csongor's avatar
csongor committed
413

414
415
    def all(self, spaces=None):
        return self._contraction_helper('all', spaces)
csongor's avatar
csongor committed
416

417
418
    def any(self, spaces=None):
        return self._contraction_helper('any', spaces)
csongor's avatar
csongor committed
419

420
421
    def min(self, spaces=None):
        return self._contraction_helper('min', spaces)
csongor's avatar
csongor committed
422

423
424
    def max(self, spaces=None):
        return self._contraction_helper('max', spaces)
csongor's avatar
csongor committed
425

426
427
    def mean(self, spaces=None):
        return self._contraction_helper('mean', spaces)
csongor's avatar
csongor committed
428

429
430
    def var(self, spaces=None):
        return self._contraction_helper('var', spaces)
csongor's avatar
csongor committed
431

432
433
    def std(self, spaces=None):
        return self._contraction_helper('std', spaces)
csongor's avatar
csongor committed
434

435
436
437
438
439
    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.")
Martin Reinecke's avatar
tweaks    
Martin Reinecke committed
440
        dobj.local_data(self.val)[()] = dobj.local_data(other.val)[()]
441

442
    def _binary_helper(self, other, op):
csongor's avatar
csongor committed
443
        # if other is a field, make sure that the domains match
444
        if isinstance(other, Field):
445
446
            if other.domain != self.domain:
                raise ValueError("domains are incompatible.")
Martin Reinecke's avatar
Martin Reinecke committed
447
448
            tval = getattr(self.val, op)(other.val)
            return self if tval is self.val else Field(self.domain, tval)
csongor's avatar
csongor committed
449

Martin Reinecke's avatar
Martin Reinecke committed
450
451
        tval = getattr(self.val, op)(other)
        return self if tval is self.val else Field(self.domain, tval)
csongor's avatar
csongor committed
452
453

    def __add__(self, other):
theos's avatar
theos committed
454
        return self._binary_helper(other, op='__add__')
455

456
    def __radd__(self, other):
theos's avatar
theos committed
457
        return self._binary_helper(other, op='__radd__')
csongor's avatar
csongor committed
458
459

    def __iadd__(self, other):
460
        return self._binary_helper(other, op='__iadd__')
csongor's avatar
csongor committed
461
462

    def __sub__(self, other):
theos's avatar
theos committed
463
        return self._binary_helper(other, op='__sub__')
csongor's avatar
csongor committed
464
465

    def __rsub__(self, other):
theos's avatar
theos committed
466
        return self._binary_helper(other, op='__rsub__')
csongor's avatar
csongor committed
467
468

    def __isub__(self, other):
469
        return self._binary_helper(other, op='__isub__')
csongor's avatar
csongor committed
470
471

    def __mul__(self, other):
theos's avatar
theos committed
472
        return self._binary_helper(other, op='__mul__')
473

474
    def __rmul__(self, other):
theos's avatar
theos committed
475
        return self._binary_helper(other, op='__rmul__')
csongor's avatar
csongor committed
476
477

    def __imul__(self, other):
478
        return self._binary_helper(other, op='__imul__')
csongor's avatar
csongor committed
479
480

    def __div__(self, other):
theos's avatar
theos committed
481
        return self._binary_helper(other, op='__div__')
csongor's avatar
csongor committed
482

Martin Reinecke's avatar
Martin Reinecke committed
483
484
485
    def __truediv__(self, other):
        return self._binary_helper(other, op='__truediv__')

csongor's avatar
csongor committed
486
    def __rdiv__(self, other):
theos's avatar
theos committed
487
        return self._binary_helper(other, op='__rdiv__')
csongor's avatar
csongor committed
488

Martin Reinecke's avatar
Martin Reinecke committed
489
490
491
    def __rtruediv__(self, other):
        return self._binary_helper(other, op='__rtruediv__')

csongor's avatar
csongor committed
492
    def __idiv__(self, other):
493
        return self._binary_helper(other, op='__idiv__')
494

csongor's avatar
csongor committed
495
    def __pow__(self, other):
theos's avatar
theos committed
496
        return self._binary_helper(other, op='__pow__')
csongor's avatar
csongor committed
497
498

    def __rpow__(self, other):
theos's avatar
theos committed
499
        return self._binary_helper(other, op='__rpow__')
csongor's avatar
csongor committed
500
501

    def __ipow__(self, other):
502
        return self._binary_helper(other, op='__ipow__')
csongor's avatar
csongor committed
503

theos's avatar
theos committed
504
    def __repr__(self):
Martin Reinecke's avatar
Martin Reinecke committed
505
        return "<nifty2go.Field>"
theos's avatar
theos committed
506
507
508
509

    def __str__(self):
        minmax = [self.min(), self.max()]
        mean = self.mean()
Martin Reinecke's avatar
Martin Reinecke committed
510
        return "nifty2go.Field instance\n- domain      = " + \
theos's avatar
theos committed
511
               repr(self.domain) + \
512
               "\n- val         = " + repr(self.val) + \
theos's avatar
theos committed
513
514
               "\n  - min.,max. = " + str(minmax) + \
               "\n  - mean = " + str(mean)
Martin Reinecke's avatar
cleanup    
Martin Reinecke committed
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544


# Arithmetic functions working on Fields

def _math_helper(x, function, out):
    if not isinstance(x, Field):
        raise TypeError("This function only accepts Field objects.")
    if out is not None:
        if not isinstance(out, Field) or x.domain != out.domain:
            raise ValueError("Bad 'out' argument")
        function(x.val, out=out.val)
        return out
    else:
        return Field(domain=x.domain, val=function(x.val))


def sqrt(x, out=None):
    return _math_helper(x, dobj.sqrt, out)


def exp(x, out=None):
    return _math_helper(x, dobj.exp, out)


def log(x, out=None):
    return _math_helper(x, dobj.log, out)


def conjugate(x, out=None):
    return _math_helper(x, dobj.conjugate, out)