Coverage for /pythoncovmergedfiles/medio/medio/usr/local/lib/python3.8/site-packages/tensorflow/python/ops/linalg/linear_operator_inversion.py: 48%
56 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"""Inverts a non-singular `LinearOperator`."""
17from tensorflow.python.framework import ops
18from tensorflow.python.ops.linalg import linear_operator
19from tensorflow.python.ops.linalg import linear_operator_util
20from tensorflow.python.util.tf_export import tf_export
22__all__ = []
25@tf_export("linalg.LinearOperatorInversion")
26@linear_operator.make_composite_tensor
27class LinearOperatorInversion(linear_operator.LinearOperator):
28 """`LinearOperator` representing the inverse of another operator.
30 This operator represents the inverse of another operator.
32 ```python
33 # Create a 2 x 2 linear operator.
34 operator = LinearOperatorFullMatrix([[1., 0.], [0., 2.]])
35 operator_inv = LinearOperatorInversion(operator)
37 operator_inv.to_dense()
38 ==> [[1., 0.]
39 [0., 0.5]]
41 operator_inv.shape
42 ==> [2, 2]
44 operator_inv.log_abs_determinant()
45 ==> - log(2)
47 x = ... Shape [2, 4] Tensor
48 operator_inv.matmul(x)
49 ==> Shape [2, 4] Tensor, equal to operator.solve(x)
50 ```
52 #### Performance
54 The performance of `LinearOperatorInversion` depends on the underlying
55 operators performance: `solve` and `matmul` are swapped, and determinant is
56 inverted.
58 #### Matrix property hints
60 This `LinearOperator` is initialized with boolean flags of the form `is_X`,
61 for `X = non_singular, self_adjoint, positive_definite, square`.
62 These have the following meaning:
64 * If `is_X == True`, callers should expect the operator to have the
65 property `X`. This is a promise that should be fulfilled, but is *not* a
66 runtime assert. For example, finite floating point precision may result
67 in these promises being violated.
68 * If `is_X == False`, callers should expect the operator to not have `X`.
69 * If `is_X == None` (the default), callers should have no expectation either
70 way.
71 """
73 def __init__(self,
74 operator,
75 is_non_singular=None,
76 is_self_adjoint=None,
77 is_positive_definite=None,
78 is_square=None,
79 name=None):
80 r"""Initialize a `LinearOperatorInversion`.
82 `LinearOperatorInversion` is initialized with an operator `A`. The `solve`
83 and `matmul` methods are effectively swapped. E.g.
85 ```
86 A = MyLinearOperator(...)
87 B = LinearOperatorInversion(A)
88 x = [....] # a vector
90 assert A.matvec(x) == B.solvevec(x)
91 ```
93 Args:
94 operator: `LinearOperator` object. If `operator.is_non_singular == False`,
95 an exception is raised. We do allow `operator.is_non_singular == None`,
96 in which case this operator will have `is_non_singular == None`.
97 Similarly for `is_self_adjoint` and `is_positive_definite`.
98 is_non_singular: Expect that this operator is non-singular.
99 is_self_adjoint: Expect that this operator is equal to its hermitian
100 transpose.
101 is_positive_definite: Expect that this operator is positive definite,
102 meaning the quadratic form `x^H A x` has positive real part for all
103 nonzero `x`. Note that we do not require the operator to be
104 self-adjoint to be positive-definite. See:
105 https://en.wikipedia.org/wiki/Positive-definite_matrix#Extension_for_non-symmetric_matrices
106 is_square: Expect that this operator acts like square [batch] matrices.
107 name: A name for this `LinearOperator`. Default is `operator.name +
108 "_inv"`.
110 Raises:
111 ValueError: If `operator.is_non_singular` is False.
112 """
113 parameters = dict(
114 operator=operator,
115 is_non_singular=is_non_singular,
116 is_self_adjoint=is_self_adjoint,
117 is_positive_definite=is_positive_definite,
118 is_square=is_square,
119 name=name
120 )
122 self._operator = operator
124 # Auto-set and check hints.
125 if operator.is_non_singular is False or is_non_singular is False:
126 raise ValueError(
127 f"Argument `is_non_singular` or argument `operator` must have "
128 f"supplied hint `is_non_singular` equal to `True` or `None`. "
129 f"Found `operator.is_non_singular`: {operator.is_non_singular}, "
130 f"`is_non_singular`: {is_non_singular}.")
131 if operator.is_square is False or is_square is False:
132 raise ValueError(
133 f"Argument `is_square` or argument `operator` must have supplied "
134 f"hint `is_square` equal to `True` or `None`. Found "
135 f"`operator.is_square`: {operator.is_square}, "
136 f"`is_square`: {is_square}.")
138 # The congruency of is_non_singular and is_self_adjoint was checked in the
139 # base operator. Other hints are, in this special case of inversion, ones
140 # that must be the same for base/derived operator.
141 combine_hint = (
142 linear_operator_util.use_operator_or_provided_hint_unless_contradicting)
144 is_square = combine_hint(
145 operator, "is_square", is_square,
146 "An operator is square if and only if its inverse is square.")
148 is_non_singular = combine_hint(
149 operator, "is_non_singular", is_non_singular,
150 "An operator is non-singular if and only if its inverse is "
151 "non-singular.")
153 is_self_adjoint = combine_hint(
154 operator, "is_self_adjoint", is_self_adjoint,
155 "An operator is self-adjoint if and only if its inverse is "
156 "self-adjoint.")
158 is_positive_definite = combine_hint(
159 operator, "is_positive_definite", is_positive_definite,
160 "An operator is positive-definite if and only if its inverse is "
161 "positive-definite.")
163 # Initialization.
164 if name is None:
165 name = operator.name + "_inv"
166 with ops.name_scope(name):
167 super(LinearOperatorInversion, self).__init__(
168 dtype=operator.dtype,
169 is_non_singular=is_non_singular,
170 is_self_adjoint=is_self_adjoint,
171 is_positive_definite=is_positive_definite,
172 is_square=is_square,
173 parameters=parameters,
174 name=name)
176 @property
177 def operator(self):
178 """The operator before inversion."""
179 return self._operator
181 def _assert_non_singular(self):
182 return self.operator.assert_non_singular()
184 def _assert_positive_definite(self):
185 return self.operator.assert_positive_definite()
187 def _assert_self_adjoint(self):
188 return self.operator.assert_self_adjoint()
190 def _shape(self):
191 return self.operator.shape
193 def _shape_tensor(self):
194 return self.operator.shape_tensor()
196 def _matmul(self, x, adjoint=False, adjoint_arg=False):
197 return self.operator.solve(x, adjoint=adjoint, adjoint_arg=adjoint_arg)
199 def _determinant(self):
200 return 1. / self.operator.determinant()
202 def _log_abs_determinant(self):
203 return -1. * self.operator.log_abs_determinant()
205 def _solve(self, rhs, adjoint=False, adjoint_arg=False):
206 return self.operator.matmul(rhs, adjoint=adjoint, adjoint_arg=adjoint_arg)
208 def _eigvals(self):
209 return 1. / self.operator.eigvals()
211 def _cond(self):
212 return self.operator.cond()
214 @property
215 def _composite_tensor_fields(self):
216 return ("operator",)
218 @property
219 def _experimental_parameter_ndims_to_matrix_ndims(self):
220 return {"operator": 0}