linear_operator.py 6.08 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 builtins import str
20
import abc
Martin Reinecke's avatar
Martin Reinecke committed
21
22
23
from ..nifty_meta import NiftyMeta
from ..field import Field
from .. import nifty_utilities as utilities
Martin Reinecke's avatar
Martin Reinecke committed
24
from future.utils import with_metaclass
25
26


27
class LinearOperator(with_metaclass(
Martin Reinecke's avatar
Martin Reinecke committed
28
        NiftyMeta, type('NewBase', (object,), {}))):
29
    """NIFTY base class for linear operators.
Theo Steininger's avatar
Theo Steininger committed
30

31
    The base NIFTY operator class is an abstract class from which
32
    other specific operator subclasses are derived.
33
34
35
36


    Attributes
    ----------
Theo Steininger's avatar
Theo Steininger committed
37
38
39
40
    domain : tuple of DomainObjects, i.e. Spaces and FieldTypes
        The domain on which the Operator's input Field lives.
    target : tuple of DomainObjects, i.e. Spaces and FieldTypes
        The domain in which the Operators result lives.
41
    unitary : boolean
Theo Steininger's avatar
Theo Steininger committed
42
        Indicates whether the Operator is unitary or not.
43
44
    """

45
46
    def __init__(self):
        pass
47

48
    @abc.abstractproperty
49
    def domain(self):
50
        """
Martin Reinecke's avatar
Martin Reinecke committed
51
        domain : DomainTuple
Theo Steininger's avatar
Theo Steininger committed
52
53
            The domain on which the Operator's input Field lives.
            Every Operator which inherits from the abstract LinearOperator
54
55
            base class must have this attribute.
        """
56
        raise NotImplementedError
57
58
59

    @abc.abstractproperty
    def target(self):
60
        """
Martin Reinecke's avatar
Martin Reinecke committed
61
        target : DomainTuple
Theo Steininger's avatar
Theo Steininger committed
62
63
            The domain on which the Operator's output Field lives.
            Every Operator which inherits from the abstract LinearOperator
64
65
            base class must have this attribute.
        """
66
67
        raise NotImplementedError

68
69
    @abc.abstractproperty
    def unitary(self):
70
71
72
73
74
75
        """
        unitary : boolean
            States whether the Operator is unitary or not.
            Every Operator which inherits from the abstract LinearOperator
            base class must have this attribute.
        """
76
77
        raise NotImplementedError

78
79
    def __call__(self, x):
        return self.times(x)
80

81
    def times(self, x):
82
83
84
85
        """ Applies the Operator to a given Field.

        Parameters
        ----------
Theo Steininger's avatar
Theo Steininger committed
86
        x : Field
Martin Reinecke's avatar
Martin Reinecke committed
87
            The input Field, living on the Operator's domain.
88
89
90

        Returns
        -------
Theo Steininger's avatar
Theo Steininger committed
91
        out : Field
Martin Reinecke's avatar
Martin Reinecke committed
92
            The processed Field living on the Operator's target domain.
93
        """
94
95
        self._check_input_compatibility(x)
        return self._times(x)
96

97
    def inverse_times(self, x):
Martin Reinecke's avatar
Martin Reinecke committed
98
        """Applies the inverse Operator to a given Field.
99
100
101

        Parameters
        ----------
Theo Steininger's avatar
Theo Steininger committed
102
        x : Field
Martin Reinecke's avatar
Martin Reinecke committed
103
            The input Field, living on the Operator's target domain
104
105
106

        Returns
        -------
Theo Steininger's avatar
Theo Steininger committed
107
        out : Field
Martin Reinecke's avatar
Martin Reinecke committed
108
            The processed Field living on the Operator's domain.
109
        """
110
        self._check_input_compatibility(x, inverse=True)
111
        try:
112
            y = self._inverse_times(x)
113
114
        except(NotImplementedError):
            if (self.unitary):
115
                y = self._adjoint_times(x)
116
117
            else:
                raise
118
119
        return y

120
    def adjoint_times(self, x):
Martin Reinecke's avatar
Martin Reinecke committed
121
        """Applies the adjoint-Operator to a given Field.
122
123
124

        Parameters
        ----------
Theo Steininger's avatar
Theo Steininger committed
125
        x : Field
Martin Reinecke's avatar
Martin Reinecke committed
126
            The input Field, living on the Operator's target domain
127
128
129

        Returns
        -------
Theo Steininger's avatar
Theo Steininger committed
130
        out : Field
Martin Reinecke's avatar
Martin Reinecke committed
131
            The processed Field living on the Operator's domain.
132
        """
133
        if self.unitary:
134
            return self.inverse_times(x)
135

136
        self._check_input_compatibility(x, inverse=True)
137
        try:
138
            y = self._adjoint_times(x)
139
140
        except(NotImplementedError):
            if (self.unitary):
141
                y = self._inverse_times(x)
142
143
            else:
                raise
144
145
        return y

146
    def adjoint_inverse_times(self, x):
147
148
149
150
        """ Applies the adjoint-inverse Operator to a given Field.

        Parameters
        ----------
Theo Steininger's avatar
Theo Steininger committed
151
        x : Field
Martin Reinecke's avatar
Martin Reinecke committed
152
            The input Field, living on the Operator's domain.
153
154
155

        Returns
        -------
Theo Steininger's avatar
Theo Steininger committed
156
        out : Field
Martin Reinecke's avatar
Martin Reinecke committed
157
            The processed Field living on the Operator's target domain.
158

Theo Steininger's avatar
Theo Steininger committed
159
160
161
162
        Notes
        -----
        If the operator has an `inverse` then the inverse adjoint is identical
        to the adjoint inverse. We provide both names for convenience.
163
        """
164
        self._check_input_compatibility(x)
165
        try:
166
            y = self._adjoint_inverse_times(x)
167
168
        except(NotImplementedError):
            if self.unitary:
169
                y = self._times(x)
170
171
            else:
                raise
172
173
        return y

174
175
    def inverse_adjoint_times(self, x):
        return self.adjoint_inverse_times(x)
176

177
    def _times(self, x):
178
179
        raise NotImplementedError(
            "no generic instance method 'times'.")
180

181
    def _adjoint_times(self, x):
182
183
        raise NotImplementedError(
            "no generic instance method 'adjoint_times'.")
184

185
    def _inverse_times(self, x):
186
187
        raise NotImplementedError(
            "no generic instance method 'inverse_times'.")
188

189
    def _adjoint_inverse_times(self, x):
190
191
        raise NotImplementedError(
            "no generic instance method 'adjoint_inverse_times'.")
192

193
    def _check_input_compatibility(self, x, inverse=False):
194
        if not isinstance(x, Field):
Martin Reinecke's avatar
updates    
Martin Reinecke committed
195
            raise ValueError("supplied object is not a `Field`.")
196

197
198
199
        if x.domain != (self.target if inverse else self.domain):
            raise ValueError("The operator's and and field's domains "
                             "don't match.")
200
201
202

    def __repr__(self):
        return str(self.__class__)