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 39 40
    Parameters
    ----------
    domain : DomainObject
        One of the space types NIFTY supports. RGSpace, GLSpace, HPSpace,
Theo Steininger's avatar
Theo Steininger committed
41
        LMSpace or PowerSpace. It might also be a FieldArray, which is
42
        an unstructured domain.
Theo Steininger's avatar
Theo Steininger committed
43

Martin Reinecke's avatar
stage1  
Martin Reinecke committed
44
    val : scalar, numpy.ndarray, Field
45 46 47
        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
48

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

52 53 54 55
    copy: boolean

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

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

63 64 65 66 67 68 69 70
    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
    """
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)
85
        elif isinstance(val, dobj.data_object):
Martin Reinecke's avatar
Martin Reinecke committed
86
            if self.domain.shape == val.shape:
87
                self._val = dobj.from_object(val, dtype=dtype, copy=copy)
Martin Reinecke's avatar
cleanup  
Martin Reinecke committed
88 89 90
            else:
                raise ValueError("Shape mismatch")
        elif val is None:
91
            self._val = dobj.empty(self.domain.shape, dtype=dtype)
Martin Reinecke's avatar
cleanup  
Martin Reinecke committed
92 93
        else:
            raise TypeError("unknown source type")
csongor's avatar
csongor committed
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 130 131 132 133 134 135 136 137 138 139 140 141 142
    @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
143 144
    @staticmethod
    def _parse_domain(domain, val=None):
145
        if domain is None:
146
            if isinstance(val, Field):
Martin Reinecke's avatar
tweaks  
Martin Reinecke committed
147 148
                return val.domain
            if np.isscalar(val):
149
                return DomainTuple.make(())  # empty domain tuple
Martin Reinecke's avatar
tweaks  
Martin Reinecke committed
150
            raise TypeError("could not infer domain from value")
Martin Reinecke's avatar
Martin Reinecke committed
151
        return DomainTuple.make(domain)
152

Martin Reinecke's avatar
Martin Reinecke committed
153
    # MR: this needs some rethinking ... do we need to have at least float64?
Martin Reinecke's avatar
Martin Reinecke committed
154 155
    @staticmethod
    def _infer_dtype(dtype, val):
Martin Reinecke's avatar
Martin Reinecke committed
156 157 158 159 160
        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)
161

Martin Reinecke's avatar
Martin Reinecke committed
162 163
    @staticmethod
    def from_random(random_type, domain, dtype=np.float64, **kwargs):
164 165 166 167 168 169 170
        """ 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
171

172 173
        domain : DomainObject
            The domain of the output random field
Theo Steininger's avatar
Theo Steininger committed
174

175 176
        dtype : type
            The datatype of the output random field
Theo Steininger's avatar
Theo Steininger committed
177

178 179 180 181 182
        Returns
        -------
        out : Field
            The output object.
        """
Martin Reinecke's avatar
Martin Reinecke committed
183
        domain = DomainTuple.make(domain)
Martin Reinecke's avatar
Martin Reinecke committed
184 185 186
        return Field(domain=domain,
                     val=dobj.from_random(random_type, dtype=dtype,
                                          shape=domain.shape, **kwargs))
187

Martin Reinecke's avatar
Martin Reinecke committed
188 189 190
    def fill(self, fill_value):
        self._val.fill(fill_value)

Theo Steininger's avatar
Theo Steininger committed
191
    # ---Properties---
192

Theo Steininger's avatar
Theo Steininger committed
193 194
    @property
    def val(self):
Martin Reinecke's avatar
stage1  
Martin Reinecke committed
195
        """ Returns the data object associated with this Field.
Martin Reinecke's avatar
PEP8  
Martin Reinecke committed
196
        No copy is made.
197
        """
Martin Reinecke's avatar
Martin Reinecke committed
198
        return self._val
csongor's avatar
csongor committed
199

Martin Reinecke's avatar
Martin Reinecke committed
200 201 202 203
    @property
    def dtype(self):
        return self._val.dtype

204 205
    @property
    def shape(self):
Theo Steininger's avatar
Theo Steininger committed
206
        """ Returns the total shape of the Field's data array.
Theo Steininger's avatar
Theo Steininger committed
207

208 209
        Returns
        -------
Martin Reinecke's avatar
Martin Reinecke committed
210 211
        Integer tuple containing the dimensions of the spaces in domain.
        """
Martin Reinecke's avatar
Martin Reinecke committed
212
        return self.domain.shape
csongor's avatar
csongor committed
213

214 215
    @property
    def dim(self):
Theo Steininger's avatar
Theo Steininger committed
216
        """ Returns the total number of pixel-dimensions the field has.
Theo Steininger's avatar
Theo Steininger committed
217

Theo Steininger's avatar
Theo Steininger committed
218
        Effectively, all values from shape are multiplied.
Theo Steininger's avatar
Theo Steininger committed
219

220 221 222 223 224
        Returns
        -------
        out : int
            The dimension of the Field.
        """
Martin Reinecke's avatar
Martin Reinecke committed
225
        return self.domain.dim
csongor's avatar
csongor committed
226

Theo Steininger's avatar
Theo Steininger committed
227 228
    @property
    def real(self):
Martin Reinecke's avatar
Martin Reinecke committed
229
        """ The real part of the field (data is not copied)."""
Martin Reinecke's avatar
PEP8  
Martin Reinecke committed
230
        return Field(self.domain, self.val.real)
Theo Steininger's avatar
Theo Steininger committed
231 232 233

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

Theo Steininger's avatar
Theo Steininger committed
237
    # ---Special unary/binary operations---
238

Martin Reinecke's avatar
Martin Reinecke committed
239
    def copy(self):
240
        """ Returns a full copy of the Field.
Theo Steininger's avatar
Theo Steininger committed
241

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

244 245 246 247 248
        Returns
        -------
        out : Field
            The output object. An identical copy of 'self'.
        """
Martin Reinecke's avatar
Martin Reinecke committed
249
        return Field(val=self, copy=True)
csongor's avatar
csongor committed
250

251 252
    def scalar_weight(self, spaces=None):
        if np.isscalar(spaces):
253
            return self.domain[spaces].scalar_dvol()
254 255 256

        if spaces is None:
            spaces = range(len(self.domain))
Martin Reinecke's avatar
Martin Reinecke committed
257
        res = 1.
258
        for i in spaces:
259
            tmp = self.domain[i].scalar_dvol()
260 261 262 263 264
            if tmp is None:
                return None
            res *= tmp
        return res

265
    def weight(self, power=1, spaces=None, out=None):
Theo Steininger's avatar
Theo Steininger committed
266
        """ Weights the pixels of `self` with their invidual pixel-volume.
267 268 269 270

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

Theo Steininger's avatar
Theo Steininger committed
273 274
        spaces : tuple of ints
            Determines on which subspace the operation takes place.
Theo Steininger's avatar
Theo Steininger committed
275

276 277 278 279 280
        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"!

281 282 283
        Returns
        -------
        out : Field
Theo Steininger's avatar
Theo Steininger committed
284
            The weighted field.
285
        """
286 287 288 289 290
        if out is None:
            out = self.copy()
        else:
            if out is not self:
                out.copy_content_from(self)
csongor's avatar
csongor committed
291

csongor's avatar
csongor committed
292
        if spaces is None:
Martin Reinecke's avatar
Martin Reinecke committed
293
            spaces = range(len(self.domain))
Martin Reinecke's avatar
Martin Reinecke committed
294 295
        else:
            spaces = utilities.cast_iseq_to_tuple(spaces)
csongor's avatar
csongor committed
296

297 298
        fct = 1.
        for ind in spaces:
299
            wgt = self.domain[ind].dvol()
300 301 302
            if np.isscalar(wgt):
                fct *= wgt
            else:
Martin Reinecke's avatar
Martin Reinecke committed
303
                new_shape = np.ones(len(self.shape), dtype=np.int)
Martin Reinecke's avatar
Martin Reinecke committed
304 305
                new_shape[self.domain.axes[ind][0]:
                          self.domain.axes[ind][-1]+1] = wgt.shape
306
                wgt = wgt.reshape(new_shape)
Martin Reinecke's avatar
Martin Reinecke committed
307 308
                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
309
                    wgt = dobj.local_data(dobj.from_global_data(wgt))
310
                out *= wgt**power
311
        fct = fct**power
Martin Reinecke's avatar
Martin Reinecke committed
312
        if fct != 1.:
313
            out *= fct
314

315
        return out
csongor's avatar
csongor committed
316

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

320 321 322
        Parameters
        ----------
        x : Field
Theo Steininger's avatar
Theo Steininger committed
323
            The domain of x must contain `self.domain`
Theo Steininger's avatar
Theo Steininger committed
324

Theo Steininger's avatar
Theo Steininger committed
325
        spaces : tuple of ints
326 327
            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
328

329 330
        Returns
        -------
Martin Reinecke's avatar
Martin Reinecke committed
331
        out : float, complex, either scalar or Field
332
        """
333 334 335
        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
336

Martin Reinecke's avatar
Martin Reinecke committed
337
        # Compute the dot respecting the fact of discrete/continuous spaces
Martin Reinecke's avatar
Martin Reinecke committed
338 339
        tmp = self.scalar_weight(spaces)
        if tmp is None:
340
            fct = 1.
Martin Reinecke's avatar
Martin Reinecke committed
341
            y = self.weight(power=1)
342
        else:
Martin Reinecke's avatar
Martin Reinecke committed
343 344
            y = self
            fct = tmp
Theo Steininger's avatar
Theo Steininger committed
345

346
        if spaces is None:
Martin Reinecke's avatar
fixes  
Martin Reinecke committed
347
            return fct*dobj.vdot(y.val, x.val)
Martin Reinecke's avatar
Martin Reinecke committed
348 349 350 351 352 353 354 355 356 357 358 359 360

        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
361

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

Theo Steininger's avatar
Theo Steininger committed
365 366
        Returns
        -------
Martin Reinecke's avatar
Martin Reinecke committed
367
        norm : float
Martin Reinecke's avatar
tweaks  
Martin Reinecke committed
368
            The L2-norm of the field values.
csongor's avatar
csongor committed
369
        """
370
        return np.sqrt(np.abs(self.vdot(x=self)))
csongor's avatar
csongor committed
371

Martin Reinecke's avatar
tweaks  
Martin Reinecke committed
372
    def conjugate(self):
Martin Reinecke's avatar
cleanup  
Martin Reinecke committed
373
        """ Returns the complex conjugate of the field.
Theo Steininger's avatar
Theo Steininger committed
374

375 376
        Returns
        -------
Martin Reinecke's avatar
Martin Reinecke committed
377
        The complex conjugated field.
csongor's avatar
csongor committed
378
        """
Martin Reinecke's avatar
tweaks  
Martin Reinecke committed
379
        return Field(self.domain, self.val.conjugate(), self.dtype)
csongor's avatar
csongor committed
380

Theo Steininger's avatar
Theo Steininger committed
381
    # ---General unary/contraction methods---
382

Theo Steininger's avatar
Theo Steininger committed
383 384
    def __pos__(self):
        return self.copy()
385

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

Theo Steininger's avatar
Theo Steininger committed
389
    def __abs__(self):
390
        return Field(self.domain, dobj.abs(self.val), self.dtype)
csongor's avatar
csongor committed
391

392
    def _contraction_helper(self, op, spaces):
Theo Steininger's avatar
Theo Steininger committed
393
        if spaces is None:
394
            return getattr(self.val, op)()
Martin Reinecke's avatar
Martin Reinecke committed
395 396
        else:
            spaces = utilities.cast_iseq_to_tuple(spaces)
csongor's avatar
csongor committed
397

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

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

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

Theo Steininger's avatar
Theo Steininger committed
406 407 408
        # 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
409
        else:
Martin Reinecke's avatar
Martin Reinecke committed
410 411
            return_domain = tuple(dom
                                  for i, dom in enumerate(self.domain)
Theo Steininger's avatar
Theo Steininger committed
412
                                  if i not in spaces)
413

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

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

419 420 421 422
    def integrate(self, spaces=None):
        tmp = self.weight(1, spaces=spaces)
        return tmp.sum(spaces)

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

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

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

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

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

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

441 442
    def var(self, spaces=None):
        return self._contraction_helper('var', spaces)
csongor's avatar
csongor committed
443

444 445
    def std(self, spaces=None):
        return self._contraction_helper('std', spaces)
csongor's avatar
csongor committed
446

447 448 449 450 451
    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
452
        dobj.local_data(self.val)[()] = dobj.local_data(other.val)[()]
453

Theo Steininger's avatar
Theo Steininger committed
454
    # ---General binary methods---
csongor's avatar
csongor committed
455

456
    def _binary_helper(self, other, op):
csongor's avatar
csongor committed
457
        # if other is a field, make sure that the domains match
458
        if isinstance(other, Field):
459 460
            if other.domain != self.domain:
                raise ValueError("domains are incompatible.")
Martin Reinecke's avatar
Martin Reinecke committed
461 462
            tval = getattr(self.val, op)(other.val)
            return self if tval is self.val else Field(self.domain, tval)
csongor's avatar
csongor committed
463

Martin Reinecke's avatar
Martin Reinecke committed
464 465
        tval = getattr(self.val, op)(other)
        return self if tval is self.val else Field(self.domain, tval)
csongor's avatar
csongor committed
466 467

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

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

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

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

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

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

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

488
    def __rmul__(self, other):
Theo Steininger's avatar
Theo Steininger committed
489
        return self._binary_helper(other, op='__rmul__')
csongor's avatar
csongor committed
490 491

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

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

Martin Reinecke's avatar
Martin Reinecke committed
497 498 499
    def __truediv__(self, other):
        return self._binary_helper(other, op='__truediv__')

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

Martin Reinecke's avatar
Martin Reinecke committed
503 504 505
    def __rtruediv__(self, other):
        return self._binary_helper(other, op='__rtruediv__')

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

csongor's avatar
csongor committed
509
    def __pow__(self, other):
Theo Steininger's avatar
Theo Steininger committed
510
        return self._binary_helper(other, op='__pow__')
csongor's avatar
csongor committed
511 512

    def __rpow__(self, other):
Theo Steininger's avatar
Theo Steininger committed
513
        return self._binary_helper(other, op='__rpow__')
csongor's avatar
csongor committed
514 515

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

Theo Steininger's avatar
Theo Steininger committed
518
    def __repr__(self):
Martin Reinecke's avatar
Martin Reinecke committed
519
        return "<nifty2go.Field>"
Theo Steininger's avatar
Theo Steininger committed
520 521 522 523

    def __str__(self):
        minmax = [self.min(), self.max()]
        mean = self.mean()
Martin Reinecke's avatar
Martin Reinecke committed
524
        return "nifty2go.Field instance\n- domain      = " + \
Theo Steininger's avatar
Theo Steininger committed
525
               repr(self.domain) + \
526
               "\n- val         = " + repr(self.val) + \
Theo Steininger's avatar
Theo Steininger committed
527 528
               "\n  - min.,max. = " + str(minmax) + \
               "\n  - mean = " + str(mean)
Martin Reinecke's avatar
cleanup  
Martin Reinecke committed
529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558


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