Example #1
0
  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)
Example #3
0
    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
Example #6
0
  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)
Example #7
0
  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