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

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

16 

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 

24 

25__all__ = [] 

26 

27 

28@tf_export("linalg.LinearOperatorAdjoint") 

29@linear_operator.make_composite_tensor 

30class LinearOperatorAdjoint(linear_operator.LinearOperator): 

31 """`LinearOperator` representing the adjoint of another operator. 

32 

33 This operator represents the adjoint of another operator. 

34 

35 ```python 

36 # Create a 2 x 2 linear operator. 

37 operator = LinearOperatorFullMatrix([[1 - i., 3.], [0., 1. + i]]) 

38 operator_adjoint = LinearOperatorAdjoint(operator) 

39 

40 operator_adjoint.to_dense() 

41 ==> [[1. + i, 0.] 

42 [3., 1 - i]] 

43 

44 operator_adjoint.shape 

45 ==> [2, 2] 

46 

47 operator_adjoint.log_abs_determinant() 

48 ==> - log(2) 

49 

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

51 operator_adjoint.matmul(x) 

52 ==> Shape [2, 4] Tensor, equal to operator.matmul(x, adjoint=True) 

53 ``` 

54 

55 #### Performance 

56 

57 The performance of `LinearOperatorAdjoint` depends on the underlying 

58 operators performance. 

59 

60 #### Matrix property hints 

61 

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: 

65 

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

74 

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

83 

84 `LinearOperatorAdjoint` is initialized with an operator `A`. The `solve` 

85 and `matmul` methods effectively flip the `adjoint` argument. E.g. 

86 

87 ``` 

88 A = MyLinearOperator(...) 

89 B = LinearOperatorAdjoint(A) 

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

91 

92 assert A.matvec(x, adjoint=True) == B.matvec(x, adjoint=False) 

93 ``` 

94 

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

108 

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 ) 

120 

121 self._operator = operator 

122 

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) 

127 

128 is_square = combine_hint( 

129 operator, "is_square", is_square, 

130 "An operator is square if and only if its adjoint is square.") 

131 

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

136 

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

141 

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

146 

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) 

159 

160 @property 

161 def operator(self): 

162 """The operator before taking the adjoint.""" 

163 return self._operator 

164 

165 def _assert_non_singular(self): 

166 return self.operator.assert_non_singular() 

167 

168 def _assert_positive_definite(self): 

169 return self.operator.assert_positive_definite() 

170 

171 def _assert_self_adjoint(self): 

172 return self.operator.assert_self_adjoint() 

173 

174 def _shape(self): 

175 # Rotate last dimension 

176 shape = self.operator.shape 

177 return shape[:-2].concatenate([shape[-1], shape[-2]]) 

178 

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) 

184 

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

186 return self.operator.matmul( 

187 x, adjoint=(not adjoint), adjoint_arg=adjoint_arg) 

188 

189 def _matvec(self, x, adjoint=False): 

190 return self.operator.matvec(x, adjoint=(not adjoint)) 

191 

192 def _determinant(self): 

193 if self.is_self_adjoint: 

194 return self.operator.determinant() 

195 return math_ops.conj(self.operator.determinant()) 

196 

197 def _log_abs_determinant(self): 

198 return self.operator.log_abs_determinant() 

199 

200 def _trace(self): 

201 if self.is_self_adjoint: 

202 return self.operator.trace() 

203 return math_ops.conj(self.operator.trace()) 

204 

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

206 return self.operator.solve( 

207 rhs, adjoint=(not adjoint), adjoint_arg=adjoint_arg) 

208 

209 def _solvevec(self, rhs, adjoint=False): 

210 return self.operator.solvevec(rhs, adjoint=(not adjoint)) 

211 

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()) 

216 

217 def _add_to_tensor(self, x): 

218 return self.to_dense() + x 

219 

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 

225 

226 def _cond(self): 

227 return self.operator.cond() 

228 

229 @property 

230 def _composite_tensor_fields(self): 

231 return ("operator",) 

232 

233 @property 

234 def _experimental_parameter_ndims_to_matrix_ndims(self): 

235 return {"operator": 0}