field.py 17 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
141
    @staticmethod
    def _infer_dtype(dtype, val):
Martin Reinecke's avatar
Martin Reinecke committed
142
143
144
145
        if dtype is not None:
            return dtype
        if val is None:
            raise ValueError("could not infer dtype")
Martin Reinecke's avatar
Martin Reinecke committed
146
147
        if isinstance(val, Field):
            return val.dtype
Martin Reinecke's avatar
Martin Reinecke committed
148
        return np.result_type(val)
149

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

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

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

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

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

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

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

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

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

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

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

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

Theo Steininger's avatar
Theo Steininger committed
213
214
    @property
    def real(self):
Martin Reinecke's avatar
Martin Reinecke committed
215
        """ The real part of the field (data is not copied)."""
216
217
        if not np.issubdtype(self.dtype, np.complexfloating):
            raise ValueError(".real called on a non-complex Field")
Martin Reinecke's avatar
PEP8    
Martin Reinecke committed
218
        return Field(self.domain, self.val.real)
Theo Steininger's avatar
Theo Steininger committed
219
220
221

    @property
    def imag(self):
Martin Reinecke's avatar
Martin Reinecke committed
222
        """ The imaginary part of the field (data is not copied)."""
223
224
        if not np.issubdtype(self.dtype, np.complexfloating):
            raise ValueError(".imag called on a non-complex Field")
Martin Reinecke's avatar
PEP8    
Martin Reinecke committed
225
        return Field(self.domain, self.val.imag)
Theo Steininger's avatar
Theo Steininger committed
226

Martin Reinecke's avatar
Martin Reinecke committed
227
    def copy(self):
228
        """ Returns a full copy of the Field.
Theo Steininger's avatar
Theo Steininger committed
229

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

232
233
234
235
236
        Returns
        -------
        out : Field
            The output object. An identical copy of 'self'.
        """
Martin Reinecke's avatar
Martin Reinecke committed
237
        return Field(val=self, copy=True)
csongor's avatar
csongor committed
238

239
240
    def scalar_weight(self, spaces=None):
        if np.isscalar(spaces):
241
            return self.domain[spaces].scalar_dvol()
242
243
244

        if spaces is None:
            spaces = range(len(self.domain))
Martin Reinecke's avatar
Martin Reinecke committed
245
        res = 1.
246
        for i in spaces:
247
            tmp = self.domain[i].scalar_dvol()
248
249
250
251
252
            if tmp is None:
                return None
            res *= tmp
        return res

253
    def weight(self, power=1, spaces=None, out=None):
Theo Steininger's avatar
Theo Steininger committed
254
        """ Weights the pixels of `self` with their invidual pixel-volume.
255
256
257
258

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

Theo Steininger's avatar
Theo Steininger committed
261
262
        spaces : tuple of ints
            Determines on which subspace the operation takes place.
Theo Steininger's avatar
Theo Steininger committed
263

264
265
266
267
268
        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"!

269
270
271
        Returns
        -------
        out : Field
Theo Steininger's avatar
Theo Steininger committed
272
            The weighted field.
273
        """
274
275
276
277
278
        if out is None:
            out = self.copy()
        else:
            if out is not self:
                out.copy_content_from(self)
csongor's avatar
csongor committed
279

csongor's avatar
csongor committed
280
        if spaces is None:
Martin Reinecke's avatar
Martin Reinecke committed
281
            spaces = range(len(self.domain))
Martin Reinecke's avatar
Martin Reinecke committed
282
283
        else:
            spaces = utilities.cast_iseq_to_tuple(spaces)
csongor's avatar
csongor committed
284

285
286
        fct = 1.
        for ind in spaces:
287
            wgt = self.domain[ind].dvol()
288
289
290
            if np.isscalar(wgt):
                fct *= wgt
            else:
Martin Reinecke's avatar
Martin Reinecke committed
291
                new_shape = np.ones(len(self.shape), dtype=np.int)
Martin Reinecke's avatar
Martin Reinecke committed
292
293
                new_shape[self.domain.axes[ind][0]:
                          self.domain.axes[ind][-1]+1] = wgt.shape
294
                wgt = wgt.reshape(new_shape)
Martin Reinecke's avatar
Martin Reinecke committed
295
296
                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
297
                    wgt = dobj.local_data(dobj.from_global_data(wgt))
298
                out *= wgt**power
299
        fct = fct**power
Martin Reinecke's avatar
Martin Reinecke committed
300
        if fct != 1.:
301
            out *= fct
302

303
        return out
csongor's avatar
csongor committed
304

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

308
309
310
        Parameters
        ----------
        x : Field
Theo Steininger's avatar
Theo Steininger committed
311
            The domain of x must contain `self.domain`
Theo Steininger's avatar
Theo Steininger committed
312

Theo Steininger's avatar
Theo Steininger committed
313
        spaces : tuple of ints
314
315
            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
316

317
318
        Returns
        -------
Martin Reinecke's avatar
Martin Reinecke committed
319
        out : float, complex, either scalar or Field
320
        """
321
322
323
        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
324

Martin Reinecke's avatar
Martin Reinecke committed
325
        # Compute the dot respecting the fact of discrete/continuous spaces
Martin Reinecke's avatar
Martin Reinecke committed
326
327
        tmp = self.scalar_weight(spaces)
        if tmp is None:
328
            fct = 1.
Martin Reinecke's avatar
Martin Reinecke committed
329
            y = self.weight(power=1)
330
        else:
Martin Reinecke's avatar
Martin Reinecke committed
331
332
            y = self
            fct = tmp
Theo Steininger's avatar
Theo Steininger committed
333

334
        if spaces is None:
Martin Reinecke's avatar
fixes    
Martin Reinecke committed
335
            return fct*dobj.vdot(y.val, x.val)
Martin Reinecke's avatar
Martin Reinecke committed
336
337
338
339
340
341
342
343
344
345
346
347
348

        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
Theo Steininger's avatar
Theo Steininger committed
349

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

Theo Steininger's avatar
Theo Steininger committed
353
354
        Returns
        -------
Martin Reinecke's avatar
Martin Reinecke committed
355
        norm : float
Martin Reinecke's avatar
tweaks    
Martin Reinecke committed
356
            The L2-norm of the field values.
csongor's avatar
csongor committed
357
        """
358
        return np.sqrt(np.abs(self.vdot(x=self)))
csongor's avatar
csongor committed
359

Martin Reinecke's avatar
tweaks    
Martin Reinecke committed
360
    def conjugate(self):
Martin Reinecke's avatar
cleanup    
Martin Reinecke committed
361
        """ Returns the complex conjugate of the field.
Theo Steininger's avatar
Theo Steininger committed
362

363
364
        Returns
        -------
Martin Reinecke's avatar
Martin Reinecke committed
365
        The complex conjugated field.
csongor's avatar
csongor committed
366
        """
Martin Reinecke's avatar
tweaks    
Martin Reinecke committed
367
        return Field(self.domain, self.val.conjugate(), self.dtype)
csongor's avatar
csongor committed
368

Theo Steininger's avatar
Theo Steininger committed
369
    # ---General unary/contraction methods---
370

Theo Steininger's avatar
Theo Steininger committed
371
372
    def __pos__(self):
        return self.copy()
373

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

Theo Steininger's avatar
Theo Steininger committed
377
    def __abs__(self):
378
        return Field(self.domain, dobj.abs(self.val), self.dtype)
csongor's avatar
csongor committed
379

380
    def _contraction_helper(self, op, spaces):
Theo Steininger's avatar
Theo Steininger committed
381
        if spaces is None:
382
            return getattr(self.val, op)()
Martin Reinecke's avatar
Martin Reinecke committed
383
384
        else:
            spaces = utilities.cast_iseq_to_tuple(spaces)
csongor's avatar
csongor committed
385

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

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

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

Theo Steininger's avatar
Theo Steininger committed
394
395
396
        # 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
397
        else:
Martin Reinecke's avatar
Martin Reinecke committed
398
399
            return_domain = tuple(dom
                                  for i, dom in enumerate(self.domain)
Theo Steininger's avatar
Theo Steininger committed
400
                                  if i not in spaces)
401

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

404
405
    def sum(self, spaces=None):
        return self._contraction_helper('sum', spaces)
csongor's avatar
csongor committed
406

407
    def integrate(self, spaces=None):
Martin Reinecke's avatar
Martin Reinecke committed
408
409
410
411
412
        swgt = self.scalar_weight(spaces)
        if swgt is not None:
            res = self.sum(spaces)
            res *= swgt
            return res
413
414
415
        tmp = self.weight(1, spaces=spaces)
        return tmp.sum(spaces)

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

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

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

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

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

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

434
435
    def var(self, spaces=None):
        return self._contraction_helper('var', spaces)
csongor's avatar
csongor committed
436

437
438
    def std(self, spaces=None):
        return self._contraction_helper('std', spaces)
csongor's avatar
csongor committed
439

440
441
442
443
444
    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
445
        dobj.local_data(self.val)[()] = dobj.local_data(other.val)[()]
446

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

Martin Reinecke's avatar
Martin Reinecke committed
455
456
        tval = getattr(self.val, op)(other)
        return self if tval is self.val else Field(self.domain, tval)
csongor's avatar
csongor committed
457
458

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

461
    def __radd__(self, other):
Theo Steininger's avatar
Theo Steininger committed
462
        return self._binary_helper(other, op='__radd__')
csongor's avatar
csongor committed
463
464

    def __iadd__(self, other):
465
        return self._binary_helper(other, op='__iadd__')
csongor's avatar
csongor committed
466
467

    def __sub__(self, other):
Theo Steininger's avatar
Theo Steininger committed
468
        return self._binary_helper(other, op='__sub__')
csongor's avatar
csongor committed
469
470

    def __rsub__(self, other):
Theo Steininger's avatar
Theo Steininger committed
471
        return self._binary_helper(other, op='__rsub__')
csongor's avatar
csongor committed
472
473

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

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

479
    def __rmul__(self, other):
Theo Steininger's avatar
Theo Steininger committed
480
        return self._binary_helper(other, op='__rmul__')
csongor's avatar
csongor committed
481
482

    def __imul__(self, other):
483
        return self._binary_helper(other, op='__imul__')
csongor's avatar
csongor committed
484
485

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

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

csongor's avatar
csongor committed
491
    def __rdiv__(self, other):
Theo Steininger's avatar
Theo Steininger committed
492
        return self._binary_helper(other, op='__rdiv__')
csongor's avatar
csongor committed
493

Martin Reinecke's avatar
Martin Reinecke committed
494
495
496
    def __rtruediv__(self, other):
        return self._binary_helper(other, op='__rtruediv__')

csongor's avatar
csongor committed
497
    def __idiv__(self, other):
498
        return self._binary_helper(other, op='__idiv__')
499

csongor's avatar
csongor committed
500
    def __pow__(self, other):
Theo Steininger's avatar
Theo Steininger committed
501
        return self._binary_helper(other, op='__pow__')
csongor's avatar
csongor committed
502
503

    def __rpow__(self, other):
Theo Steininger's avatar
Theo Steininger committed
504
        return self._binary_helper(other, op='__rpow__')
csongor's avatar
csongor committed
505
506

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

Theo Steininger's avatar
Theo Steininger committed
509
    def __repr__(self):
Martin Reinecke's avatar
Martin Reinecke committed
510
        return "<nifty2go.Field>"
Theo Steininger's avatar
Theo Steininger committed
511
512
513
514

    def __str__(self):
        minmax = [self.min(), self.max()]
        mean = self.mean()
Martin Reinecke's avatar
Martin Reinecke committed
515
        return "nifty2go.Field instance\n- domain      = " + \
Theo Steininger's avatar
Theo Steininger committed
516
               repr(self.domain) + \
517
               "\n- val         = " + repr(self.val) + \
Theo Steininger's avatar
Theo Steininger committed
518
519
               "\n  - min.,max. = " + str(minmax) + \
               "\n  - mean = " + str(mean)
Martin Reinecke's avatar
cleanup    
Martin Reinecke committed
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
545
546
547


# 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)


Martin Reinecke's avatar
Martin Reinecke committed
548
549
550
551
def tanh(x, out=None):
    return _math_helper(x, dobj.tanh, out)


Martin Reinecke's avatar
cleanup    
Martin Reinecke committed
552
553
def conjugate(x, out=None):
    return _math_helper(x, dobj.conjugate, out)