def _gen_positive_diag(self, dtype, diag_shape): if dtype.is_complex: diag = linear_operator_test_util.random_uniform( diag_shape, minval=1e-4, maxval=1., dtype=dtypes.float32) return math_ops.cast(diag, dtype=dtype) return linear_operator_test_util.random_uniform( diag_shape, minval=1e-4, maxval=1., dtype=dtype)
def _gen_positive_diag(self, dtype, diag_shape): if dtype.is_complex: diag = linear_operator_test_util.random_uniform( diag_shape, minval=1e-4, maxval=1., dtype=dtypes.float32) return math_ops.cast(diag, dtype=dtype) return linear_operator_test_util.random_uniform( diag_shape, minval=1e-4, maxval=1., dtype=dtype)
def _operator_and_matrix(self, build_info, dtype, use_placeholder): shape = build_info.shape # For this test class, we are creating Hermitian spectrums. # We also want the spectrum to have eigenvalues bounded away from zero. # # pre_spectrum is bounded away from zero. pre_spectrum = linear_operator_test_util.random_uniform( shape=self._shape_to_spectrum_shape(shape), minval=1., maxval=2.) pre_spectrum_c = _to_complex(pre_spectrum) # Real{IFFT[pre_spectrum]} # = IFFT[EvenPartOf[pre_spectrum]] # is the IFFT of something that is also bounded away from zero. # Therefore, FFT[pre_h] would be a well-conditioned spectrum. pre_h = math_ops.ifft2d(pre_spectrum_c) # A spectrum is Hermitian iff it is the DFT of a real convolution kernel. # So we will make spectrum = FFT[h], for real valued h. h = math_ops.real(pre_h) h_c = _to_complex(h) spectrum = math_ops.fft2d(h_c) lin_op_spectrum = spectrum if use_placeholder: lin_op_spectrum = array_ops.placeholder_with_default(spectrum, shape=None) operator = linalg.LinearOperatorCirculant2D(lin_op_spectrum, input_output_dtype=dtype) mat = self._spectrum_to_circulant_2d(spectrum, shape, dtype=dtype) return operator, mat
def test_defining_spd_operator_by_taking_real_part(self): with self.cached_session() as sess: # S is real and positive. s = linear_operator_test_util.random_uniform( shape=(10, 2, 3, 4), dtype=dtypes.float32, minval=1., maxval=2.) # Let S = S1 + S2, the Hermitian and anti-hermitian parts. # S1 = 0.5 * (S + S^H), S2 = 0.5 * (S - S^H), # where ^H is the Hermitian transpose of the function: # f(n0, n1, n2)^H := ComplexConjugate[f(N0-n0, N1-n1, N2-n2)]. # We want to isolate S1, since # S1 is Hermitian by construction # S1 is real since S is # S1 is positive since it is the sum of two positive kernels # IDFT[S] = IDFT[S1] + IDFT[S2] # = H1 + H2 # where H1 is real since it is Hermitian, # and H2 is imaginary since it is anti-Hermitian. ifft_s = fft_ops.ifft3d(math_ops.cast(s, dtypes.complex64)) # Throw away H2, keep H1. real_ifft_s = math_ops.real(ifft_s) # This is the perfect spectrum! # spectrum = DFT[H1] # = S1, fft_real_ifft_s = fft_ops.fft3d( math_ops.cast(real_ifft_s, dtypes.complex64)) # S1 is Hermitian ==> operator is real. # S1 is real ==> operator is self-adjoint. # S1 is positive ==> operator is positive-definite. operator = linalg.LinearOperatorCirculant3D(fft_real_ifft_s) # Allow for complex output so we can check operator has zero imag part. self.assertEqual(operator.dtype, dtypes.complex64) matrix, matrix_t = sess.run([ operator.to_dense(), array_ops.matrix_transpose(operator.to_dense()) ]) operator.assert_positive_definite().run() # Should not fail. np.testing.assert_allclose(0, np.imag(matrix), atol=1e-6) self.assertAllClose(matrix, matrix_t) # Just to test the theory, get S2 as well. # This should create an imaginary operator. # S2 is anti-Hermitian ==> operator is imaginary. # S2 is real ==> operator is self-adjoint. imag_ifft_s = math_ops.imag(ifft_s) fft_imag_ifft_s = fft_ops.fft3d( 1j * math_ops.cast(imag_ifft_s, dtypes.complex64)) operator_imag = linalg.LinearOperatorCirculant3D(fft_imag_ifft_s) matrix, matrix_h = sess.run([ operator_imag.to_dense(), array_ops.matrix_transpose(math_ops.conj(operator_imag.to_dense())) ]) self.assertAllClose(matrix, matrix_h) np.testing.assert_allclose(0, np.real(matrix), atol=1e-7)
def _operator_and_matrix(self, build_info, dtype, use_placeholder): shape = build_info.shape # For this test class, we are creating Hermitian spectrums. # We also want the spectrum to have eigenvalues bounded away from zero. # # pre_spectrum is bounded away from zero. pre_spectrum = linear_operator_test_util.random_uniform( shape=self._shape_to_spectrum_shape(shape), minval=1., maxval=2.) pre_spectrum_c = _to_complex(pre_spectrum) # Real{IFFT[pre_spectrum]} # = IFFT[EvenPartOf[pre_spectrum]] # is the IFFT of something that is also bounded away from zero. # Therefore, FFT[pre_h] would be a well-conditioned spectrum. pre_h = math_ops.ifft2d(pre_spectrum_c) # A spectrum is Hermitian iff it is the DFT of a real convolution kernel. # So we will make spectrum = FFT[h], for real valued h. h = math_ops.real(pre_h) h_c = _to_complex(h) spectrum = math_ops.fft2d(h_c) lin_op_spectrum = spectrum if use_placeholder: lin_op_spectrum = array_ops.placeholder_with_default(spectrum, shape=None) operator = linalg.LinearOperatorCirculant2D( lin_op_spectrum, input_output_dtype=dtype) mat = self._spectrum_to_circulant_2d(spectrum, shape, dtype=dtype) return operator, mat
def test_defining_spd_operator_by_taking_real_part(self): with self.cached_session() as sess: # S is real and positive. s = linear_operator_test_util.random_uniform( shape=(10, 2, 3, 4), dtype=dtypes.float32, minval=1., maxval=2.) # Let S = S1 + S2, the Hermitian and anti-hermitian parts. # S1 = 0.5 * (S + S^H), S2 = 0.5 * (S - S^H), # where ^H is the Hermitian transpose of the function: # f(n0, n1, n2)^H := ComplexConjugate[f(N0-n0, N1-n1, N2-n2)]. # We want to isolate S1, since # S1 is Hermitian by construction # S1 is real since S is # S1 is positive since it is the sum of two positive kernels # IDFT[S] = IDFT[S1] + IDFT[S2] # = H1 + H2 # where H1 is real since it is Hermitian, # and H2 is imaginary since it is anti-Hermitian. ifft_s = fft_ops.ifft3d(math_ops.cast(s, dtypes.complex64)) # Throw away H2, keep H1. real_ifft_s = math_ops.real(ifft_s) # This is the perfect spectrum! # spectrum = DFT[H1] # = S1, fft_real_ifft_s = fft_ops.fft3d( math_ops.cast(real_ifft_s, dtypes.complex64)) # S1 is Hermitian ==> operator is real. # S1 is real ==> operator is self-adjoint. # S1 is positive ==> operator is positive-definite. operator = linalg.LinearOperatorCirculant3D(fft_real_ifft_s) # Allow for complex output so we can check operator has zero imag part. self.assertEqual(operator.dtype, dtypes.complex64) matrix, matrix_t = sess.run([ operator.to_dense(), array_ops.matrix_transpose(operator.to_dense()) ]) operator.assert_positive_definite().run() # Should not fail. np.testing.assert_allclose(0, np.imag(matrix), atol=1e-6) self.assertAllClose(matrix, matrix_t) # Just to test the theory, get S2 as well. # This should create an imaginary operator. # S2 is anti-Hermitian ==> operator is imaginary. # S2 is real ==> operator is self-adjoint. imag_ifft_s = math_ops.imag(ifft_s) fft_imag_ifft_s = fft_ops.fft3d( 1j * math_ops.cast(imag_ifft_s, dtypes.complex64)) operator_imag = linalg.LinearOperatorCirculant3D(fft_imag_ifft_s) matrix, matrix_h = sess.run([ operator_imag.to_dense(), array_ops.matrix_transpose(math_ops.conj(operator_imag.to_dense())) ]) self.assertAllClose(matrix, matrix_h) np.testing.assert_allclose(0, np.real(matrix), atol=1e-7)
def operator_and_matrix(self, shape_info, dtype, use_placeholder, ensure_self_adjoint_and_pd=False): shape = shape_info.shape # For this test class, we are creating Hermitian spectrums. # We also want the spectrum to have eigenvalues bounded away from zero. # # pre_spectrum is bounded away from zero. pre_spectrum = linear_operator_test_util.random_uniform( shape=self._shape_to_spectrum_shape(shape), dtype=dtype, minval=1., maxval=2.) pre_spectrum_c = _to_complex(pre_spectrum) # Real{IFFT[pre_spectrum]} # = IFFT[EvenPartOf[pre_spectrum]] # is the IFFT of something that is also bounded away from zero. # Therefore, FFT[pre_h] would be a well-conditioned spectrum. pre_h = fft_ops.ifft2d(pre_spectrum_c) # A spectrum is Hermitian iff it is the DFT of a real convolution kernel. # So we will make spectrum = FFT[h], for real valued h. h = math_ops.real(pre_h) h_c = _to_complex(h) spectrum = fft_ops.fft2d(h_c) lin_op_spectrum = spectrum if use_placeholder: lin_op_spectrum = array_ops.placeholder_with_default(spectrum, shape=None) operator = linalg.LinearOperatorCirculant2D( lin_op_spectrum, is_positive_definite=True if ensure_self_adjoint_and_pd else None, is_self_adjoint=True if ensure_self_adjoint_and_pd else None, input_output_dtype=dtype) self.assertEqual( operator.parameters, { "input_output_dtype": dtype, "is_non_singular": None, "is_positive_definite": ( True if ensure_self_adjoint_and_pd else None), "is_self_adjoint": ( True if ensure_self_adjoint_and_pd else None), "is_square": True, "name": "LinearOperatorCirculant2D", "spectrum": lin_op_spectrum, }) mat = self._spectrum_to_circulant_2d(spectrum, shape, dtype=dtype) return operator, mat
def _operator_and_mat_and_feed_dict(self, build_info, dtype, use_placeholder): shape = list(build_info.shape) expected_blocks = (build_info.__dict__["blocks"] if "blocks" in build_info.__dict__ else [shape]) diag_matrices = [ linear_operator_test_util.random_uniform(shape=block_shape[:-1], minval=1., maxval=20., dtype=dtype) for block_shape in expected_blocks ] if use_placeholder: diag_matrices_ph = [ array_ops.placeholder(dtype=dtype) for _ in expected_blocks ] diag_matrices = self.evaluate(diag_matrices) # Evaluate here because (i) you cannot feed a tensor, and (ii) # values are random and we want the same value used for both mat and # feed_dict. operator = block_diag.LinearOperatorBlockDiag( [linalg.LinearOperatorDiag(m_ph) for m_ph in diag_matrices_ph]) feed_dict = { m_ph: m for (m_ph, m) in zip(diag_matrices_ph, diag_matrices) } else: operator = block_diag.LinearOperatorBlockDiag( [linalg.LinearOperatorDiag(m) for m in diag_matrices]) feed_dict = None # Should be auto-set. self.assertTrue(operator.is_square) # Broadcast the shapes. expected_shape = list(build_info.shape) matrices = linear_operator_util.broadcast_matrix_batch_dims([ array_ops.matrix_diag(diag_block) for diag_block in diag_matrices ]) block_diag_dense = _block_diag_dense(expected_shape, matrices) if not use_placeholder: block_diag_dense.set_shape( expected_shape[:-2] + [expected_shape[-1], expected_shape[-1]]) return operator, block_diag_dense, feed_dict
def _operator_and_mat_and_feed_dict(self, build_info, dtype, use_placeholder): shape = list(build_info.shape) expected_blocks = ( build_info.__dict__["blocks"] if "blocks" in build_info.__dict__ else [shape]) diag_matrices = [ linear_operator_test_util.random_uniform( shape=block_shape[:-1], minval=1., maxval=20., dtype=dtype) for block_shape in expected_blocks ] if use_placeholder: diag_matrices_ph = [ array_ops.placeholder(dtype=dtype) for _ in expected_blocks ] diag_matrices = self.evaluate(diag_matrices) # Evaluate here because (i) you cannot feed a tensor, and (ii) # values are random and we want the same value used for both mat and # feed_dict. operator = block_diag.LinearOperatorBlockDiag( [linalg.LinearOperatorDiag(m_ph) for m_ph in diag_matrices_ph]) feed_dict = {m_ph: m for (m_ph, m) in zip( diag_matrices_ph, diag_matrices)} else: operator = block_diag.LinearOperatorBlockDiag( [linalg.LinearOperatorDiag(m) for m in diag_matrices]) feed_dict = None # Should be auto-set. self.assertTrue(operator.is_square) # Broadcast the shapes. expected_shape = list(build_info.shape) matrices = linear_operator_util.broadcast_matrix_batch_dims( [array_ops.matrix_diag(diag_block) for diag_block in diag_matrices]) block_diag_dense = _block_diag_dense(expected_shape, matrices) if not use_placeholder: block_diag_dense.set_shape( expected_shape[:-2] + [expected_shape[-1], expected_shape[-1]]) return operator, block_diag_dense, feed_dict
def _operator_and_mat_and_feed_dict(self, build_info, dtype, use_placeholder): shape = build_info.shape # For this test class, we are creating Hermitian spectrums. # We also want the spectrum to have eigenvalues bounded away from zero. # # pre_spectrum is bounded away from zero. pre_spectrum = linear_operator_test_util.random_uniform( shape=self._shape_to_spectrum_shape(shape), minval=1., maxval=2.) pre_spectrum_c = _to_complex(pre_spectrum) # Real{IFFT[pre_spectrum]} # = IFFT[EvenPartOf[pre_spectrum]] # is the IFFT of something that is also bounded away from zero. # Therefore, FFT[pre_h] would be a well-conditioned spectrum. pre_h = math_ops.ifft2d(pre_spectrum_c) # A spectrum is Hermitian iff it is the DFT of a real convolution kernel. # So we will make spectrum = FFT[h], for real valued h. h = math_ops.real(pre_h) h_c = _to_complex(h) spectrum = math_ops.fft2d(h_c) if use_placeholder: spectrum_ph = array_ops.placeholder(dtypes.complex64) # Evaluate here because (i) you cannot feed a tensor, and (ii) # it is random and we want the same value used for both mat and feed_dict. spectrum = spectrum.eval() operator = linalg.LinearOperatorCirculant2D( spectrum_ph, input_output_dtype=dtype) feed_dict = {spectrum_ph: spectrum} else: operator = linalg.LinearOperatorCirculant2D( spectrum, input_output_dtype=dtype) feed_dict = None mat = self._spectrum_to_circulant_2d(spectrum, shape, dtype=dtype) return operator, mat, feed_dict
def _operator_and_mat_and_feed_dict(self, build_info, dtype, use_placeholder): shape = build_info.shape # For this test class, we are creating Hermitian spectrums. # We also want the spectrum to have eigenvalues bounded away from zero. # # pre_spectrum is bounded away from zero. pre_spectrum = linear_operator_test_util.random_uniform( shape=self._shape_to_spectrum_shape(shape), minval=1., maxval=2.) pre_spectrum_c = _to_complex(pre_spectrum) # Real{IFFT[pre_spectrum]} # = IFFT[EvenPartOf[pre_spectrum]] # is the IFFT of something that is also bounded away from zero. # Therefore, FFT[pre_h] would be a well-conditioned spectrum. pre_h = math_ops.ifft2d(pre_spectrum_c) # A spectrum is Hermitian iff it is the DFT of a real convolution kernel. # So we will make spectrum = FFT[h], for real valued h. h = math_ops.real(pre_h) h_c = _to_complex(h) spectrum = math_ops.fft2d(h_c) if use_placeholder: spectrum_ph = array_ops.placeholder(dtypes.complex64) # Evaluate here because (i) you cannot feed a tensor, and (ii) # it is random and we want the same value used for both mat and feed_dict. spectrum = spectrum.eval() operator = linalg.LinearOperatorCirculant2D( spectrum_ph, input_output_dtype=dtype) feed_dict = {spectrum_ph: spectrum} else: operator = linalg.LinearOperatorCirculant2D( spectrum, input_output_dtype=dtype) feed_dict = None mat = self._spectrum_to_circulant_2d(spectrum, shape, dtype=dtype) return operator, mat, feed_dict
def _operator_and_mat_and_feed_dict(self, build_info, dtype, use_placeholder): # Recall A = L + UDV^H shape = list(build_info.shape) diag_shape = shape[:-1] k = shape[-2] // 2 + 1 u_perturbation_shape = shape[:-1] + [k] diag_update_shape = shape[:-2] + [k] # base_operator L will be a symmetric positive definite diagonal linear # operator, with condition number as high as 1e4. base_diag = linear_operator_test_util.random_uniform( diag_shape, minval=1e-4, maxval=1., dtype=dtype) base_diag_ph = array_ops.placeholder(dtype=dtype) # U u = linear_operator_test_util.random_normal_correlated_columns( u_perturbation_shape, dtype=dtype) u_ph = array_ops.placeholder(dtype=dtype) # V v = linear_operator_test_util.random_normal_correlated_columns( u_perturbation_shape, dtype=dtype) v_ph = array_ops.placeholder(dtype=dtype) # D if self._is_diag_update_positive: diag_update = linear_operator_test_util.random_uniform( diag_update_shape, minval=1e-4, maxval=1., dtype=dtype) else: diag_update = linear_operator_test_util.random_normal( diag_update_shape, stddev=1e-4, dtype=dtype) diag_update_ph = array_ops.placeholder(dtype=dtype) if use_placeholder: # Evaluate here because (i) you cannot feed a tensor, and (ii) # values are random and we want the same value used for both mat and # feed_dict. base_diag = base_diag.eval() u = u.eval() v = v.eval() diag_update = diag_update.eval() # In all cases, set base_operator to be positive definite. base_operator = linalg.LinearOperatorDiag( base_diag_ph, is_positive_definite=True) operator = linalg.LinearOperatorLowRankUpdate( base_operator, u=u_ph, v=v_ph if self._use_v else None, diag_update=diag_update_ph if self._use_diag_update else None, is_diag_update_positive=self._is_diag_update_positive) feed_dict = { base_diag_ph: base_diag, u_ph: u, v_ph: v, diag_update_ph: diag_update} else: base_operator = linalg.LinearOperatorDiag( base_diag, is_positive_definite=True) operator = linalg.LinearOperatorLowRankUpdate( base_operator, u, v=v if self._use_v else None, diag_update=diag_update if self._use_diag_update else None, is_diag_update_positive=self._is_diag_update_positive) feed_dict = None # The matrix representing L base_diag_mat = array_ops.matrix_diag(base_diag) # The matrix representing D diag_update_mat = array_ops.matrix_diag(diag_update) # Set up mat as some variant of A = L + UDV^H if self._use_v and self._use_diag_update: # In this case, we have L + UDV^H and it isn't symmetric. expect_use_cholesky = False mat = base_diag_mat + math_ops.matmul( u, math_ops.matmul(diag_update_mat, v, adjoint_b=True)) elif self._use_v: # In this case, we have L + UDV^H and it isn't symmetric. expect_use_cholesky = False mat = base_diag_mat + math_ops.matmul(u, v, adjoint_b=True) elif self._use_diag_update: # In this case, we have L + UDU^H, which is PD if D > 0, since L > 0. expect_use_cholesky = self._is_diag_update_positive mat = base_diag_mat + math_ops.matmul( u, math_ops.matmul(diag_update_mat, u, adjoint_b=True)) else: # In this case, we have L + UU^H, which is PD since L > 0. expect_use_cholesky = True mat = base_diag_mat + math_ops.matmul(u, u, adjoint_b=True) if expect_use_cholesky: self.assertTrue(operator._use_cholesky) else: self.assertFalse(operator._use_cholesky) return operator, mat, feed_dict
def _operator_and_mat_and_feed_dict(self, shape, dtype, use_placeholder): # Recall A = L + UDV^H shape = list(shape) diag_shape = shape[:-1] k = shape[-2] // 2 + 1 u_perturbation_shape = shape[:-1] + [k] diag_update_shape = shape[:-2] + [k] # base_operator L will be a symmetric positive definite diagonal linear # operator, with condition number as high as 1e4. base_diag = linear_operator_test_util.random_uniform( diag_shape, minval=1e-4, maxval=1., dtype=dtype) base_diag_ph = array_ops.placeholder(dtype=dtype) # U u = linear_operator_test_util.random_normal_correlated_columns( u_perturbation_shape, dtype=dtype) u_ph = array_ops.placeholder(dtype=dtype) # V v = linear_operator_test_util.random_normal_correlated_columns( u_perturbation_shape, dtype=dtype) v_ph = array_ops.placeholder(dtype=dtype) # D if self._is_diag_update_positive: diag_update = linear_operator_test_util.random_uniform( diag_update_shape, minval=1e-4, maxval=1., dtype=dtype) else: diag_update = linear_operator_test_util.random_normal( diag_update_shape, stddev=1e-4, dtype=dtype) diag_update_ph = array_ops.placeholder(dtype=dtype) if use_placeholder: # Evaluate here because (i) you cannot feed a tensor, and (ii) # values are random and we want the same value used for both mat and # feed_dict. base_diag = base_diag.eval() u = u.eval() v = v.eval() diag_update = diag_update.eval() # In all cases, set base_operator to be positive definite. base_operator = linalg.LinearOperatorDiag( base_diag_ph, is_positive_definite=True) operator = linalg.LinearOperatorLowRankUpdate( base_operator, u=u_ph, v=v_ph if self._use_v else None, diag_update=diag_update_ph if self._use_diag_update else None, is_diag_update_positive=self._is_diag_update_positive) feed_dict = { base_diag_ph: base_diag, u_ph: u, v_ph: v, diag_update_ph: diag_update} else: base_operator = linalg.LinearOperatorDiag( base_diag, is_positive_definite=True) operator = linalg.LinearOperatorLowRankUpdate( base_operator, u, v=v if self._use_v else None, diag_update=diag_update if self._use_diag_update else None, is_diag_update_positive=self._is_diag_update_positive) feed_dict = None # The matrix representing L base_diag_mat = array_ops.matrix_diag(base_diag) # The matrix representing D diag_update_mat = array_ops.matrix_diag(diag_update) # Set up mat as some variant of A = L + UDV^H if self._use_v and self._use_diag_update: # In this case, we have L + UDV^H and it isn't symmetric. expect_use_cholesky = False mat = base_diag_mat + math_ops.matmul( u, math_ops.matmul(diag_update_mat, v, adjoint_b=True)) elif self._use_v: # In this case, we have L + UDV^H and it isn't symmetric. expect_use_cholesky = False mat = base_diag_mat + math_ops.matmul(u, v, adjoint_b=True) elif self._use_diag_update: # In this case, we have L + UDU^H, which is PD if D > 0, since L > 0. expect_use_cholesky = self._is_diag_update_positive mat = base_diag_mat + math_ops.matmul( u, math_ops.matmul(diag_update_mat, u, adjoint_b=True)) else: # In this case, we have L + UU^H, which is PD since L > 0. expect_use_cholesky = True mat = base_diag_mat + math_ops.matmul(u, u, adjoint_b=True) if expect_use_cholesky: self.assertTrue(operator._use_cholesky) else: self.assertFalse(operator._use_cholesky) return operator, mat, feed_dict
def _operator_and_matrix(self, build_info, dtype, use_placeholder): # Recall A = L + UDV^H shape = list(build_info.shape) diag_shape = shape[:-1] k = shape[-2] // 2 + 1 u_perturbation_shape = shape[:-1] + [k] diag_update_shape = shape[:-2] + [k] # base_operator L will be a symmetric positive definite diagonal linear # operator, with condition number as high as 1e4. base_diag = linear_operator_test_util.random_uniform( diag_shape, minval=1e-4, maxval=1., dtype=dtype) lin_op_base_diag = base_diag # U u = linear_operator_test_util.random_normal_correlated_columns( u_perturbation_shape, dtype=dtype) lin_op_u = u # V v = linear_operator_test_util.random_normal_correlated_columns( u_perturbation_shape, dtype=dtype) lin_op_v = v # D if self._is_diag_update_positive: diag_update = linear_operator_test_util.random_uniform( diag_update_shape, minval=1e-4, maxval=1., dtype=dtype) else: diag_update = linear_operator_test_util.random_normal( diag_update_shape, stddev=1e-4, dtype=dtype) lin_op_diag_update = diag_update if use_placeholder: lin_op_base_diag = array_ops.placeholder_with_default( base_diag, shape=None) lin_op_u = array_ops.placeholder_with_default(u, shape=None) lin_op_v = array_ops.placeholder_with_default(v, shape=None) lin_op_diag_update = array_ops.placeholder_with_default( diag_update, shape=None) base_operator = linalg.LinearOperatorDiag( lin_op_base_diag, is_positive_definite=True) operator = linalg.LinearOperatorLowRankUpdate( base_operator, lin_op_u, v=lin_op_v if self._use_v else None, diag_update=lin_op_diag_update if self._use_diag_update else None, is_diag_update_positive=self._is_diag_update_positive) # The matrix representing L base_diag_mat = array_ops.matrix_diag(base_diag) # The matrix representing D diag_update_mat = array_ops.matrix_diag(diag_update) # Set up mat as some variant of A = L + UDV^H if self._use_v and self._use_diag_update: # In this case, we have L + UDV^H and it isn't symmetric. expect_use_cholesky = False matrix = base_diag_mat + math_ops.matmul( u, math_ops.matmul(diag_update_mat, v, adjoint_b=True)) elif self._use_v: # In this case, we have L + UDV^H and it isn't symmetric. expect_use_cholesky = False matrix = base_diag_mat + math_ops.matmul(u, v, adjoint_b=True) elif self._use_diag_update: # In this case, we have L + UDU^H, which is PD if D > 0, since L > 0. expect_use_cholesky = self._is_diag_update_positive matrix = base_diag_mat + math_ops.matmul( u, math_ops.matmul(diag_update_mat, u, adjoint_b=True)) else: # In this case, we have L + UU^H, which is PD since L > 0. expect_use_cholesky = True matrix = base_diag_mat + math_ops.matmul(u, u, adjoint_b=True) if expect_use_cholesky: self.assertTrue(operator._use_cholesky) else: self.assertFalse(operator._use_cholesky) return operator, matrix
def _operator_and_matrix(self, build_info, dtype, use_placeholder): # Recall A = L + UDV^H shape = list(build_info.shape) diag_shape = shape[:-1] k = shape[-2] // 2 + 1 u_perturbation_shape = shape[:-1] + [k] diag_update_shape = shape[:-2] + [k] # base_operator L will be a symmetric positive definite diagonal linear # operator, with condition number as high as 1e4. base_diag = linear_operator_test_util.random_uniform(diag_shape, minval=1e-4, maxval=1., dtype=dtype) lin_op_base_diag = base_diag # U u = linear_operator_test_util.random_normal_correlated_columns( u_perturbation_shape, dtype=dtype) lin_op_u = u # V v = linear_operator_test_util.random_normal_correlated_columns( u_perturbation_shape, dtype=dtype) lin_op_v = v # D if self._is_diag_update_positive: diag_update = linear_operator_test_util.random_uniform( diag_update_shape, minval=1e-4, maxval=1., dtype=dtype) else: diag_update = linear_operator_test_util.random_normal( diag_update_shape, stddev=1e-4, dtype=dtype) lin_op_diag_update = diag_update if use_placeholder: lin_op_base_diag = array_ops.placeholder_with_default(base_diag, shape=None) lin_op_u = array_ops.placeholder_with_default(u, shape=None) lin_op_v = array_ops.placeholder_with_default(v, shape=None) lin_op_diag_update = array_ops.placeholder_with_default( diag_update, shape=None) base_operator = linalg.LinearOperatorDiag(lin_op_base_diag, is_positive_definite=True) operator = linalg.LinearOperatorLowRankUpdate( base_operator, lin_op_u, v=lin_op_v if self._use_v else None, diag_update=lin_op_diag_update if self._use_diag_update else None, is_diag_update_positive=self._is_diag_update_positive) # The matrix representing L base_diag_mat = array_ops.matrix_diag(base_diag) # The matrix representing D diag_update_mat = array_ops.matrix_diag(diag_update) # Set up mat as some variant of A = L + UDV^H if self._use_v and self._use_diag_update: # In this case, we have L + UDV^H and it isn't symmetric. expect_use_cholesky = False matrix = base_diag_mat + math_ops.matmul( u, math_ops.matmul(diag_update_mat, v, adjoint_b=True)) elif self._use_v: # In this case, we have L + UDV^H and it isn't symmetric. expect_use_cholesky = False matrix = base_diag_mat + math_ops.matmul(u, v, adjoint_b=True) elif self._use_diag_update: # In this case, we have L + UDU^H, which is PD if D > 0, since L > 0. expect_use_cholesky = self._is_diag_update_positive matrix = base_diag_mat + math_ops.matmul( u, math_ops.matmul(diag_update_mat, u, adjoint_b=True)) else: # In this case, we have L + UU^H, which is PD since L > 0. expect_use_cholesky = True matrix = base_diag_mat + math_ops.matmul(u, u, adjoint_b=True) if expect_use_cholesky: self.assertTrue(operator._use_cholesky) else: self.assertFalse(operator._use_cholesky) return operator, matrix