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

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`.""" 

16 

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 

21 

22__all__ = [] 

23 

24 

25@tf_export("linalg.LinearOperatorInversion") 

26@linear_operator.make_composite_tensor 

27class LinearOperatorInversion(linear_operator.LinearOperator): 

28 """`LinearOperator` representing the inverse of another operator. 

29 

30 This operator represents the inverse of another operator. 

31 

32 ```python 

33 # Create a 2 x 2 linear operator. 

34 operator = LinearOperatorFullMatrix([[1., 0.], [0., 2.]]) 

35 operator_inv = LinearOperatorInversion(operator) 

36 

37 operator_inv.to_dense() 

38 ==> [[1., 0.] 

39 [0., 0.5]] 

40 

41 operator_inv.shape 

42 ==> [2, 2] 

43 

44 operator_inv.log_abs_determinant() 

45 ==> - log(2) 

46 

47 x = ... Shape [2, 4] Tensor 

48 operator_inv.matmul(x) 

49 ==> Shape [2, 4] Tensor, equal to operator.solve(x) 

50 ``` 

51 

52 #### Performance 

53 

54 The performance of `LinearOperatorInversion` depends on the underlying 

55 operators performance: `solve` and `matmul` are swapped, and determinant is 

56 inverted. 

57 

58 #### Matrix property hints 

59 

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: 

63 

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 """ 

72 

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`. 

81 

82 `LinearOperatorInversion` is initialized with an operator `A`. The `solve` 

83 and `matmul` methods are effectively swapped. E.g. 

84 

85 ``` 

86 A = MyLinearOperator(...) 

87 B = LinearOperatorInversion(A) 

88 x = [....] # a vector 

89 

90 assert A.matvec(x) == B.solvevec(x) 

91 ``` 

92 

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"`. 

109 

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 ) 

121 

122 self._operator = operator 

123 

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}.") 

137 

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) 

143 

144 is_square = combine_hint( 

145 operator, "is_square", is_square, 

146 "An operator is square if and only if its inverse is square.") 

147 

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.") 

152 

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.") 

157 

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.") 

162 

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) 

175 

176 @property 

177 def operator(self): 

178 """The operator before inversion.""" 

179 return self._operator 

180 

181 def _assert_non_singular(self): 

182 return self.operator.assert_non_singular() 

183 

184 def _assert_positive_definite(self): 

185 return self.operator.assert_positive_definite() 

186 

187 def _assert_self_adjoint(self): 

188 return self.operator.assert_self_adjoint() 

189 

190 def _shape(self): 

191 return self.operator.shape 

192 

193 def _shape_tensor(self): 

194 return self.operator.shape_tensor() 

195 

196 def _matmul(self, x, adjoint=False, adjoint_arg=False): 

197 return self.operator.solve(x, adjoint=adjoint, adjoint_arg=adjoint_arg) 

198 

199 def _determinant(self): 

200 return 1. / self.operator.determinant() 

201 

202 def _log_abs_determinant(self): 

203 return -1. * self.operator.log_abs_determinant() 

204 

205 def _solve(self, rhs, adjoint=False, adjoint_arg=False): 

206 return self.operator.matmul(rhs, adjoint=adjoint, adjoint_arg=adjoint_arg) 

207 

208 def _eigvals(self): 

209 return 1. / self.operator.eigvals() 

210 

211 def _cond(self): 

212 return self.operator.cond() 

213 

214 @property 

215 def _composite_tensor_fields(self): 

216 return ("operator",) 

217 

218 @property 

219 def _experimental_parameter_ndims_to_matrix_ndims(self): 

220 return {"operator": 0}