field.py 17.3 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
#
Martin Reinecke's avatar
Martin Reinecke committed
14
# Copyright(C) 2013-2018 Max-Planck-Society
Theo Steininger's avatar
Theo Steininger committed
15
16
17
#
# 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
    """ The discrete representation of a continuous field over multiple spaces.

Martin Reinecke's avatar
Martin Reinecke committed
33
    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.
Theo Steininger's avatar
Theo Steininger committed
35

36
37
    Parameters
    ----------
Martin Reinecke's avatar
Martin Reinecke committed
38
    domain : None, DomainTuple, tuple(Domain), or Domain
Theo Steininger's avatar
Theo Steininger committed
39

Martin Reinecke's avatar
Martin Reinecke committed
40
    val : None, Field, data_object, or scalar
41
        The values the array should contain after init. A scalar input will
Martin Reinecke's avatar
Martin Reinecke committed
42
43
        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
44

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

48
49
    copy: boolean
    """
50

Martin Reinecke's avatar
stage1    
Martin Reinecke committed
51
    def __init__(self, domain=None, val=None, dtype=None, copy=False):
Martin Reinecke's avatar
Martin Reinecke committed
52
        self._domain = self._infer_domain(domain=domain, val=val)
53

Martin Reinecke's avatar
Martin Reinecke committed
54
        dtype = self._infer_dtype(dtype=dtype, val=val)
Martin Reinecke's avatar
cleanup    
Martin Reinecke committed
55
        if isinstance(val, Field):
Martin Reinecke's avatar
Martin Reinecke committed
56
            if self._domain != val._domain:
Martin Reinecke's avatar
cleanup    
Martin Reinecke committed
57
                raise ValueError("Domain mismatch")
58
            self._val = dobj.from_object(val.val, dtype=dtype, copy=copy)
Martin Reinecke's avatar
cleanup    
Martin Reinecke committed
59
        elif (np.isscalar(val)):
Martin Reinecke's avatar
Martin Reinecke committed
60
            self._val = dobj.full(self._domain.shape, dtype=dtype,
61
                                  fill_value=val)
62
        elif isinstance(val, dobj.data_object):
Martin Reinecke's avatar
Martin Reinecke committed
63
            if self._domain.shape == val.shape:
64
                self._val = dobj.from_object(val, dtype=dtype, copy=copy)
Martin Reinecke's avatar
cleanup    
Martin Reinecke committed
65
66
67
            else:
                raise ValueError("Shape mismatch")
        elif val is None:
Martin Reinecke's avatar
Martin Reinecke committed
68
            self._val = dobj.empty(self._domain.shape, dtype=dtype)
Martin Reinecke's avatar
cleanup    
Martin Reinecke committed
69
70
        else:
            raise TypeError("unknown source type")
csongor's avatar
csongor committed
71

72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
    @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")
Martin Reinecke's avatar
Martin Reinecke committed
94
        return Field.full(field._domain, val, dtype)
95
96
97
98
99
100
101

    @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
Martin Reinecke's avatar
Martin Reinecke committed
102
        return Field.zeros(field._domain, dtype)
103
104
105
106
107
108
109

    @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
Martin Reinecke's avatar
Martin Reinecke committed
110
        return Field.ones(field._domain, dtype)
111
112
113
114
115
116
117

    @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
Martin Reinecke's avatar
Martin Reinecke committed
118
        return Field.empty(field._domain, dtype)
119

Martin Reinecke's avatar
Martin Reinecke committed
120
    @staticmethod
Martin Reinecke's avatar
tweaks    
Martin Reinecke committed
121
    def _infer_domain(domain, val=None):
122
        if domain is None:
123
            if isinstance(val, Field):
Martin Reinecke's avatar
Martin Reinecke committed
124
                return val._domain
Martin Reinecke's avatar
tweaks    
Martin Reinecke committed
125
            if np.isscalar(val):
126
                return DomainTuple.make(())  # empty domain tuple
Martin Reinecke's avatar
tweaks    
Martin Reinecke committed
127
            raise TypeError("could not infer domain from value")
Martin Reinecke's avatar
Martin Reinecke committed
128
        return DomainTuple.make(domain)
129

Martin Reinecke's avatar
Martin Reinecke committed
130
131
    @staticmethod
    def _infer_dtype(dtype, val):
Martin Reinecke's avatar
Martin Reinecke committed
132
133
134
135
        if dtype is not None:
            return dtype
        if val is None:
            raise ValueError("could not infer dtype")
Martin Reinecke's avatar
Martin Reinecke committed
136
137
        if isinstance(val, Field):
            return val.dtype
Martin Reinecke's avatar
Martin Reinecke committed
138
        return np.result_type(val)
139

Martin Reinecke's avatar
Martin Reinecke committed
140
141
    @staticmethod
    def from_random(random_type, domain, dtype=np.float64, **kwargs):
142
143
144
145
146
147
148
        """ 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
149

Martin Reinecke's avatar
Martin Reinecke committed
150
        domain : DomainTuple
151
            The domain of the output random field
Theo Steininger's avatar
Theo Steininger committed
152

153
154
        dtype : type
            The datatype of the output random field
Theo Steininger's avatar
Theo Steininger committed
155

156
157
158
159
160
        Returns
        -------
        out : Field
            The output object.
        """
Martin Reinecke's avatar
Martin Reinecke committed
161
        domain = DomainTuple.make(domain)
Martin Reinecke's avatar
Martin Reinecke committed
162
163
164
        return Field(domain=domain,
                     val=dobj.from_random(random_type, dtype=dtype,
                                          shape=domain.shape, **kwargs))
165

Martin Reinecke's avatar
Martin Reinecke committed
166
167
168
    def fill(self, fill_value):
        self._val.fill(fill_value)

Theo Steininger's avatar
Theo Steininger committed
169
170
    @property
    def val(self):
Martin Reinecke's avatar
stage1    
Martin Reinecke committed
171
        """ Returns the data object associated with this Field.
Martin Reinecke's avatar
PEP8    
Martin Reinecke committed
172
        No copy is made.
173
        """
Martin Reinecke's avatar
Martin Reinecke committed
174
        return self._val
csongor's avatar
csongor committed
175

Martin Reinecke's avatar
Martin Reinecke committed
176
177
178
179
    @property
    def dtype(self):
        return self._val.dtype

Martin Reinecke's avatar
Martin Reinecke committed
180
181
182
183
    @property
    def domain(self):
        return self._domain

184
185
    @property
    def shape(self):
Theo Steininger's avatar
Theo Steininger committed
186
        """ Returns the total shape of the Field's data array.
Theo Steininger's avatar
Theo Steininger committed
187

188
189
        Returns
        -------
Martin Reinecke's avatar
Martin Reinecke committed
190
191
        Integer tuple containing the dimensions of the spaces in domain.
        """
Martin Reinecke's avatar
Martin Reinecke committed
192
        return self._domain.shape
csongor's avatar
csongor committed
193

194
    @property
Martin Reinecke's avatar
Martin Reinecke committed
195
    def size(self):
Theo Steininger's avatar
Theo Steininger committed
196
        """ Returns the total number of pixel-dimensions the field has.
Theo Steininger's avatar
Theo Steininger committed
197

Theo Steininger's avatar
Theo Steininger committed
198
        Effectively, all values from shape are multiplied.
Theo Steininger's avatar
Theo Steininger committed
199

200
201
202
203
204
        Returns
        -------
        out : int
            The dimension of the Field.
        """
Martin Reinecke's avatar
Martin Reinecke committed
205
        return self._domain.size
csongor's avatar
csongor committed
206

Theo Steininger's avatar
Theo Steininger committed
207
208
    @property
    def real(self):
Martin Reinecke's avatar
Martin Reinecke committed
209
        """ The real part of the field (data is not copied)."""
210
        if not np.issubdtype(self.dtype, np.complexfloating):
211
            return self
Martin Reinecke's avatar
Martin Reinecke committed
212
        return Field(self._domain, self.val.real)
Theo Steininger's avatar
Theo Steininger committed
213
214
215

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

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

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

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

233
234
    def scalar_weight(self, spaces=None):
        if np.isscalar(spaces):
Martin Reinecke's avatar
Martin Reinecke committed
235
            return self._domain[spaces].scalar_dvol()
236
237

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

Martin Reinecke's avatar
Martin Reinecke committed
247
248
249
250
251
252
253
254
255
256
257
    def total_volume(self, spaces=None):
        if np.isscalar(spaces):
            return self._domain[spaces].total_volume()

        if spaces is None:
            spaces = range(len(self._domain))
        res = 1.
        for i in spaces:
            res *= self._domain[i].total_volume()
        return res

258
    def weight(self, power=1, spaces=None, out=None):
Theo Steininger's avatar
Theo Steininger committed
259
        """ Weights the pixels of `self` with their invidual pixel-volume.
260
261
262
263

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

Theo Steininger's avatar
Theo Steininger committed
266
267
        spaces : tuple of ints
            Determines on which subspace the operation takes place.
Theo Steininger's avatar
Theo Steininger committed
268

269
270
271
272
273
        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"!

274
275
276
        Returns
        -------
        out : Field
Theo Steininger's avatar
Theo Steininger committed
277
            The weighted field.
278
        """
279
280
281
282
283
        if out is None:
            out = self.copy()
        else:
            if out is not self:
                out.copy_content_from(self)
csongor's avatar
csongor committed
284

Martin Reinecke's avatar
Martin Reinecke committed
285
        spaces = utilities.parse_spaces(spaces, len(self._domain))
csongor's avatar
csongor committed
286

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

306
        return out
csongor's avatar
csongor committed
307

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

311
312
313
        Parameters
        ----------
        x : Field
314
            x must live on the same domain as `self`.
Theo Steininger's avatar
Theo Steininger committed
315

316
317
318
        spaces : None, int or tuple of ints (default: None)
            The dot product is only carried out over the sub-domains in this
            tuple. If None, it is carried out over all sub-domains.
Theo Steininger's avatar
Theo Steininger committed
319

320
321
        Returns
        -------
322
323
        out : float, complex, either scalar (for full dot products)
                              or Field (for partial dot products)
324
        """
325
326
327
        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
328

Martin Reinecke's avatar
Martin Reinecke committed
329
        if x._domain != self._domain:
330
            raise ValueError("Domain mismatch")
Theo Steininger's avatar
Theo Steininger committed
331

Martin Reinecke's avatar
Martin Reinecke committed
332
        ndom = len(self._domain)
333
334
335
        spaces = utilities.parse_spaces(spaces, ndom)

        if len(spaces) == ndom:
Martin Reinecke's avatar
Martin Reinecke committed
336
            return dobj.vdot(self.val, x.val)
337
338
        # If we arrive here, we have to do a partial dot product.
        # For the moment, do this the explicit, non-optimized way
Martin Reinecke's avatar
Martin Reinecke committed
339
        return (self.conjugate()*x).sum(spaces=spaces)
Theo Steininger's avatar
Theo Steininger committed
340

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

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

Martin Reinecke's avatar
tweaks    
Martin Reinecke committed
351
    def conjugate(self):
Martin Reinecke's avatar
cleanup    
Martin Reinecke committed
352
        """ Returns the complex conjugate of the field.
Theo Steininger's avatar
Theo Steininger committed
353

354
355
        Returns
        -------
Martin Reinecke's avatar
Martin Reinecke committed
356
        The complex conjugated field.
csongor's avatar
csongor committed
357
        """
Martin Reinecke's avatar
Martin Reinecke committed
358
        return Field(self._domain, self.val.conjugate())
csongor's avatar
csongor committed
359

Theo Steininger's avatar
Theo Steininger committed
360
    # ---General unary/contraction methods---
361

Theo Steininger's avatar
Theo Steininger committed
362
363
    def __pos__(self):
        return self.copy()
364

Theo Steininger's avatar
Theo Steininger committed
365
    def __neg__(self):
366
        return Field(self._domain, -self.val)
csongor's avatar
csongor committed
367

Theo Steininger's avatar
Theo Steininger committed
368
    def __abs__(self):
369
        return Field(self._domain, dobj.abs(self.val))
csongor's avatar
csongor committed
370

371
    def _contraction_helper(self, op, spaces):
Theo Steininger's avatar
Theo Steininger committed
372
        if spaces is None:
373
            return getattr(self.val, op)()
374

Martin Reinecke's avatar
Martin Reinecke committed
375
        spaces = utilities.parse_spaces(spaces, len(self._domain))
csongor's avatar
csongor committed
376

Martin Reinecke's avatar
Martin Reinecke committed
377
        axes_list = tuple(self._domain.axes[sp_index] for sp_index in spaces)
378

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

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

Theo Steininger's avatar
Theo Steininger committed
385
386
387
        # 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
388
        else:
Martin Reinecke's avatar
Martin Reinecke committed
389
            return_domain = tuple(dom
Martin Reinecke's avatar
Martin Reinecke committed
390
                                  for i, dom in enumerate(self._domain)
Theo Steininger's avatar
Theo Steininger committed
391
                                  if i not in spaces)
392

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

395
396
    def sum(self, spaces=None):
        return self._contraction_helper('sum', spaces)
csongor's avatar
csongor committed
397

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

407
408
    def prod(self, spaces=None):
        return self._contraction_helper('prod', spaces)
csongor's avatar
csongor committed
409

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

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

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

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

422
    def mean(self, spaces=None):
423
424
        if self.scalar_weight(spaces) is not None:
            return self._contraction_helper('mean', spaces)
Martin Reinecke's avatar
Martin Reinecke committed
425
426
427
        # MR FIXME: not very efficient
        tmp = self.weight(1)
        return tmp.sum(spaces)*(1./tmp.total_volume(spaces))
csongor's avatar
csongor committed
428

429
    def var(self, spaces=None):
430
431
        if self.scalar_weight(spaces) is not None:
            return self._contraction_helper('var', spaces)
Martin Reinecke's avatar
Martin Reinecke committed
432
433
434
435
436
437
438
439
440
        # MR FIXME: not very efficient or accurate
        m1 = self.mean(spaces)
        if np.issubdtype(self.dtype, np.complexfloating):
            sq = abs(self)**2
            m1 = abs(m1)**2
        else:
            sq = self**2
            m1 **= 2
        return sq.mean(spaces) - m1
csongor's avatar
csongor committed
441

442
    def std(self, spaces=None):
443
444
445
        if self.scalar_weight(spaces) is not None:
            return self._contraction_helper('std', spaces)
        return sqrt(self.var(spaces))
csongor's avatar
csongor committed
446

447
448
449
    def copy_content_from(self, other):
        if not isinstance(other, Field):
            raise TypeError("argument must be a Field")
Martin Reinecke's avatar
Martin Reinecke committed
450
        if other._domain != self._domain:
451
            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

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

462
463
        if np.isscalar(other) or isinstance(other, dobj.data_object):
            tval = getattr(self.val, op)(other)
Martin Reinecke's avatar
Martin Reinecke committed
464
            return self if tval is self.val else Field(self._domain, tval)
465

Martin Reinecke's avatar
Martin Reinecke committed
466
        return NotImplemented
csongor's avatar
csongor committed
467
468

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

    def __str__(self):
Martin Reinecke's avatar
Martin Reinecke committed
523
        return "nifty4.Field instance\n- domain      = " + \
524
               self._domain.__str__() + \
525
               "\n- val         = " + repr(self.val)
Martin Reinecke's avatar
cleanup    
Martin Reinecke committed
526
527
528
529
530
531
532
533


# 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:
Martin Reinecke's avatar
Martin Reinecke committed
534
        if not isinstance(out, Field) or x._domain != out._domain:
Martin Reinecke's avatar
cleanup    
Martin Reinecke committed
535
536
537
538
            raise ValueError("Bad 'out' argument")
        function(x.val, out=out.val)
        return out
    else:
Martin Reinecke's avatar
Martin Reinecke committed
539
        return Field(domain=x._domain, val=function(x.val))
Martin Reinecke's avatar
cleanup    
Martin Reinecke committed
540
541
542
543
544
545
546
547
548
549
550
551
552
553


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
554
555
556
557
def tanh(x, out=None):
    return _math_helper(x, dobj.tanh, out)


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