Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/tensorflow/python/ops/linalg/linear_operator_adjoint.py: 46%
76 statements
« prev ^ index » next coverage.py v7.4.0, created at 2024-01-03 07:57 +0000
« prev ^ index » next coverage.py v7.4.0, created at 2024-01-03 07:57 +0000
1# Copyright 2018 The TensorFlow Authors. All Rights Reserved.
2#
3# Licensed under the Apache License, Version 2.0 (the "License");
4# you may not use this file except in compliance with the License.
5# You may obtain a copy of the License at
6#
7# http://www.apache.org/licenses/LICENSE-2.0
8#
9# Unless required by applicable law or agreed to in writing, software
10# distributed under the License is distributed on an "AS IS" BASIS,
11# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12# See the License for the specific language governing permissions and
13# limitations under the License.
14# ==============================================================================
15"""Takes the adjoint of a `LinearOperator`."""
17from tensorflow.python.framework import ops
18from tensorflow.python.ops import array_ops
19from tensorflow.python.ops import math_ops
20from tensorflow.python.ops.linalg import linalg_impl as linalg
21from tensorflow.python.ops.linalg import linear_operator
22from tensorflow.python.ops.linalg import linear_operator_util
23from tensorflow.python.util.tf_export import tf_export
25__all__ = []
28@tf_export("linalg.LinearOperatorAdjoint")
29@linear_operator.make_composite_tensor
30class LinearOperatorAdjoint(linear_operator.LinearOperator):
31 """`LinearOperator` representing the adjoint of another operator.
33 This operator represents the adjoint of another operator.
35 ```python
36 # Create a 2 x 2 linear operator.
37 operator = LinearOperatorFullMatrix([[1 - i., 3.], [0., 1. + i]])
38 operator_adjoint = LinearOperatorAdjoint(operator)
40 operator_adjoint.to_dense()
41 ==> [[1. + i, 0.]
42 [3., 1 - i]]
44 operator_adjoint.shape
45 ==> [2, 2]
47 operator_adjoint.log_abs_determinant()
48 ==> - log(2)
50 x = ... Shape [2, 4] Tensor
51 operator_adjoint.matmul(x)
52 ==> Shape [2, 4] Tensor, equal to operator.matmul(x, adjoint=True)
53 ```
55 #### Performance
57 The performance of `LinearOperatorAdjoint` depends on the underlying
58 operators performance.
60 #### Matrix property hints
62 This `LinearOperator` is initialized with boolean flags of the form `is_X`,
63 for `X = non_singular, self_adjoint, positive_definite, square`.
64 These have the following meaning:
66 * If `is_X == True`, callers should expect the operator to have the
67 property `X`. This is a promise that should be fulfilled, but is *not* a
68 runtime assert. For example, finite floating point precision may result
69 in these promises being violated.
70 * If `is_X == False`, callers should expect the operator to not have `X`.
71 * If `is_X == None` (the default), callers should have no expectation either
72 way.
73 """
75 def __init__(self,
76 operator,
77 is_non_singular=None,
78 is_self_adjoint=None,
79 is_positive_definite=None,
80 is_square=None,
81 name=None):
82 r"""Initialize a `LinearOperatorAdjoint`.
84 `LinearOperatorAdjoint` is initialized with an operator `A`. The `solve`
85 and `matmul` methods effectively flip the `adjoint` argument. E.g.
87 ```
88 A = MyLinearOperator(...)
89 B = LinearOperatorAdjoint(A)
90 x = [....] # a vector
92 assert A.matvec(x, adjoint=True) == B.matvec(x, adjoint=False)
93 ```
95 Args:
96 operator: `LinearOperator` object.
97 is_non_singular: Expect that this operator is non-singular.
98 is_self_adjoint: Expect that this operator is equal to its hermitian
99 transpose.
100 is_positive_definite: Expect that this operator is positive definite,
101 meaning the quadratic form `x^H A x` has positive real part for all
102 nonzero `x`. Note that we do not require the operator to be
103 self-adjoint to be positive-definite. See:
104 https://en.wikipedia.org/wiki/Positive-definite_matrix#Extension_for_non-symmetric_matrices
105 is_square: Expect that this operator acts like square [batch] matrices.
106 name: A name for this `LinearOperator`. Default is `operator.name +
107 "_adjoint"`.
109 Raises:
110 ValueError: If `operator.is_non_singular` is False.
111 """
112 parameters = dict(
113 operator=operator,
114 is_non_singular=is_non_singular,
115 is_self_adjoint=is_self_adjoint,
116 is_positive_definite=is_positive_definite,
117 is_square=is_square,
118 name=name,
119 )
121 self._operator = operator
123 # The congruency of is_non_singular and is_self_adjoint was checked in the
124 # base operator.
125 combine_hint = (
126 linear_operator_util.use_operator_or_provided_hint_unless_contradicting)
128 is_square = combine_hint(
129 operator, "is_square", is_square,
130 "An operator is square if and only if its adjoint is square.")
132 is_non_singular = combine_hint(
133 operator, "is_non_singular", is_non_singular,
134 "An operator is non-singular if and only if its adjoint is "
135 "non-singular.")
137 is_self_adjoint = combine_hint(
138 operator, "is_self_adjoint", is_self_adjoint,
139 "An operator is self-adjoint if and only if its adjoint is "
140 "self-adjoint.")
142 is_positive_definite = combine_hint(
143 operator, "is_positive_definite", is_positive_definite,
144 "An operator is positive-definite if and only if its adjoint is "
145 "positive-definite.")
147 # Initialization.
148 if name is None:
149 name = operator.name + "_adjoint"
150 with ops.name_scope(name):
151 super(LinearOperatorAdjoint, self).__init__(
152 dtype=operator.dtype,
153 is_non_singular=is_non_singular,
154 is_self_adjoint=is_self_adjoint,
155 is_positive_definite=is_positive_definite,
156 is_square=is_square,
157 parameters=parameters,
158 name=name)
160 @property
161 def operator(self):
162 """The operator before taking the adjoint."""
163 return self._operator
165 def _assert_non_singular(self):
166 return self.operator.assert_non_singular()
168 def _assert_positive_definite(self):
169 return self.operator.assert_positive_definite()
171 def _assert_self_adjoint(self):
172 return self.operator.assert_self_adjoint()
174 def _shape(self):
175 # Rotate last dimension
176 shape = self.operator.shape
177 return shape[:-2].concatenate([shape[-1], shape[-2]])
179 def _shape_tensor(self):
180 # Rotate last dimension
181 shape = self.operator.shape_tensor()
182 return array_ops.concat([
183 shape[:-2], [shape[-1], shape[-2]]], axis=-1)
185 def _matmul(self, x, adjoint=False, adjoint_arg=False):
186 return self.operator.matmul(
187 x, adjoint=(not adjoint), adjoint_arg=adjoint_arg)
189 def _matvec(self, x, adjoint=False):
190 return self.operator.matvec(x, adjoint=(not adjoint))
192 def _determinant(self):
193 if self.is_self_adjoint:
194 return self.operator.determinant()
195 return math_ops.conj(self.operator.determinant())
197 def _log_abs_determinant(self):
198 return self.operator.log_abs_determinant()
200 def _trace(self):
201 if self.is_self_adjoint:
202 return self.operator.trace()
203 return math_ops.conj(self.operator.trace())
205 def _solve(self, rhs, adjoint=False, adjoint_arg=False):
206 return self.operator.solve(
207 rhs, adjoint=(not adjoint), adjoint_arg=adjoint_arg)
209 def _solvevec(self, rhs, adjoint=False):
210 return self.operator.solvevec(rhs, adjoint=(not adjoint))
212 def _to_dense(self):
213 if self.is_self_adjoint:
214 return self.operator.to_dense()
215 return linalg.adjoint(self.operator.to_dense())
217 def _add_to_tensor(self, x):
218 return self.to_dense() + x
220 def _eigvals(self):
221 eigvals = self.operator.eigvals()
222 if not self.operator.is_self_adjoint:
223 eigvals = math_ops.conj(eigvals)
224 return eigvals
226 def _cond(self):
227 return self.operator.cond()
229 @property
230 def _composite_tensor_fields(self):
231 return ("operator",)
233 @property
234 def _experimental_parameter_ndims_to_matrix_ndims(self):
235 return {"operator": 0}