Example #1
0
    def test_is_x_flags(self):
        # The is_x flags are by default all True.
        operator = linalg_lib.LinearOperatorIdentity(num_rows=2)
        self.assertTrue(operator.is_positive_definite)
        self.assertTrue(operator.is_non_singular)
        self.assertTrue(operator.is_self_adjoint)

        # Any of them False raises because the identity is always self-adjoint etc..
        with self.assertRaisesRegexp(ValueError, "is always non-singular"):
            operator = linalg_lib.LinearOperatorIdentity(
                num_rows=2,
                is_non_singular=None,
            )
Example #2
0
 def test_non_scalar_num_rows_raises_dynamic(self):
     with self.test_session():
         num_rows = array_ops.placeholder(dtypes.int32)
         operator = linalg_lib.LinearOperatorIdentity(
             num_rows, assert_proper_shapes=True)
         with self.assertRaisesOpError("must be a 0-D Tensor"):
             operator.to_dense().eval(feed_dict={num_rows: [2]})
Example #3
0
 def test_negative_batch_shape_raises_dynamic(self):
     with self.test_session():
         batch_shape = array_ops.placeholder(dtypes.int32)
         operator = linalg_lib.LinearOperatorIdentity(
             num_rows=2, batch_shape=batch_shape, assert_proper_shapes=True)
         with self.assertRaisesOpError("must be non-negative"):
             operator.to_dense().eval(feed_dict={batch_shape: [-2]})
Example #4
0
    def test_broadcast_apply_dynamic_shapes(self):
        # These cannot be done in the automated (base test class) tests since they
        # test shapes that tf.batch_matmul cannot handle.
        # In particular, tf.batch_matmul does not broadcast.
        with self.test_session() as sess:
            # Given this x and LinearOperatorIdentity shape of (2, 1, 3, 3), the
            # broadcast shape of operator and 'x' is (2, 2, 3, 4)
            x = array_ops.placeholder(dtypes.float32)
            num_rows = array_ops.placeholder(dtypes.int32)
            batch_shape = array_ops.placeholder(dtypes.int32)

            operator = linalg_lib.LinearOperatorIdentity(
                num_rows, batch_shape=batch_shape)
            feed_dict = {
                x: rng.rand(1, 2, 3, 4),
                num_rows: 3,
                batch_shape: (2, 1)
            }

            # Batch matrix of zeros with the broadcast shape of x and operator.
            zeros = array_ops.zeros(shape=(2, 2, 3, 4), dtype=x.dtype)

            # Expected result of apply and solve.
            expected = x + zeros

            operator_apply = operator.apply(x)
            self.assertAllClose(
                *sess.run([operator_apply, expected], feed_dict=feed_dict))
Example #5
0
 def test_float16_apply(self):
     # float16 cannot be tested by base test class because tf.matrix_solve does
     # not work with float16.
     with self.test_session():
         operator = linalg_lib.LinearOperatorIdentity(num_rows=2,
                                                      dtype=dtypes.float16)
         x = rng.randn(2, 3).astype(np.float16)
         y = operator.apply(x)
         self.assertAllClose(x, y.eval())
Example #6
0
    def test_wrong_matrix_dimensions_raises_dynamic(self):
        num_rows = array_ops.placeholder(dtypes.int32)
        x = array_ops.placeholder(dtypes.float32)

        with self.test_session():
            operator = linalg_lib.LinearOperatorIdentity(
                num_rows, assert_proper_shapes=True)
            y = operator.apply(x)
            with self.assertRaisesOpError("Incompatible.*dimensions"):
                y.eval(feed_dict={num_rows: 2, x: rng.rand(3, 3)})
Example #7
0
    def test_default_batch_shape_broadcasts_with_everything_static(self):
        # These cannot be done in the automated (base test class) tests since they
        # test shapes that tf.batch_matmul cannot handle.
        # In particular, tf.batch_matmul does not broadcast.
        with self.test_session() as sess:
            x = random_ops.random_normal(shape=(1, 2, 3, 4))
            operator = linalg_lib.LinearOperatorIdentity(num_rows=3,
                                                         dtype=x.dtype)

            operator_apply = operator.apply(x)
            expected = x

            self.assertAllEqual(operator_apply.get_shape(),
                                expected.get_shape())
            self.assertAllClose(*sess.run([operator_apply, expected]))
Example #8
0
    def test_default_batch_shape_broadcasts_with_everything_dynamic(self):
        # These cannot be done in the automated (base test class) tests since they
        # test shapes that tf.batch_matmul cannot handle.
        # In particular, tf.batch_matmul does not broadcast.
        with self.test_session() as sess:
            x = array_ops.placeholder(dtypes.float32)
            operator = linalg_lib.LinearOperatorIdentity(num_rows=3,
                                                         dtype=x.dtype)

            operator_apply = operator.apply(x)
            expected = x

            feed_dict = {x: rng.randn(1, 2, 3, 4)}

            self.assertAllClose(
                *sess.run([operator_apply, expected], feed_dict=feed_dict))
Example #9
0
    def _operator_and_mat_and_feed_dict(self, shape, dtype, use_placeholder):
        shape = list(shape)
        assert shape[-1] == shape[-2]

        batch_shape = shape[:-2]
        num_rows = shape[-1]

        operator = linalg_lib.LinearOperatorIdentity(num_rows,
                                                     batch_shape=batch_shape,
                                                     dtype=dtype)
        mat = linalg_ops.eye(num_rows, batch_shape=batch_shape, dtype=dtype)

        # Nothing to feed since LinearOperatorIdentity takes no Tensor args.
        if use_placeholder:
            feed_dict = {}
        else:
            feed_dict = None

        return operator, mat, feed_dict
Example #10
0
    def test_broadcast_apply_static_shapes(self):
        # These cannot be done in the automated (base test class) tests since they
        # test shapes that tf.batch_matmul cannot handle.
        # In particular, tf.batch_matmul does not broadcast.
        with self.test_session() as sess:
            # Given this x and LinearOperatorIdentity shape of (2, 1, 3, 3), the
            # broadcast shape of operator and 'x' is (2, 2, 3, 4)
            x = random_ops.random_normal(shape=(1, 2, 3, 4))
            operator = linalg_lib.LinearOperatorIdentity(num_rows=3,
                                                         batch_shape=(2, 1),
                                                         dtype=x.dtype)

            # Batch matrix of zeros with the broadcast shape of x and operator.
            zeros = array_ops.zeros(shape=(2, 2, 3, 4), dtype=x.dtype)

            # Expected result of apply and solve.
            expected = x + zeros

            operator_apply = operator.apply(x)
            self.assertAllEqual(operator_apply.get_shape(),
                                expected.get_shape())
            self.assertAllClose(*sess.run([operator_apply, expected]))
def make_diag_scale(loc,
                    scale_diag,
                    scale_identity_multiplier,
                    validate_args,
                    assert_positive,
                    name=None):
    """Creates a LinOp from `scale_diag`, `scale_identity_multiplier` kwargs."""
    def _convert_to_tensor(x, name):
        return None if x is None else ops.convert_to_tensor(x, name=name)

    def _maybe_attach_assertion(x):
        if not validate_args:
            return x
        if assert_positive:
            return control_flow_ops.with_dependencies([
                check_ops.assert_positive(
                    x, message="diagonal part must be positive"),
            ], x)
        # TODO(b/35157376): Use `assert_none_equal` once it exists.
        return control_flow_ops.with_dependencies([
            check_ops.assert_greater(math_ops.abs(x),
                                     array_ops.zeros([], x.dtype),
                                     message="diagonal part must be non-zero"),
        ], x)

    with ops.name_scope(name,
                        "make_diag_scale",
                        values=[loc, scale_diag, scale_identity_multiplier]):
        loc = _convert_to_tensor(loc, name="loc")
        scale_diag = _convert_to_tensor(scale_diag, name="scale_diag")
        scale_identity_multiplier = _convert_to_tensor(
            scale_identity_multiplier, name="scale_identity_multiplier")

        if scale_diag is not None:
            if scale_identity_multiplier is not None:
                scale_diag += scale_identity_multiplier[..., array_ops.newaxis]
            return linalg.LinearOperatorDiag(
                diag=_maybe_attach_assertion(scale_diag),
                is_non_singular=True,
                is_self_adjoint=True,
                is_positive_definite=assert_positive)

        # TODO(b/35290280): Consider inferring shape from scale_perturb_factor.
        if loc is None:
            raise ValueError(
                "Cannot infer `event_shape` unless `loc` is specified.")

        num_rows = dimension_size(loc, -1)

        if scale_identity_multiplier is None:
            return linalg.LinearOperatorIdentity(
                num_rows=num_rows,
                dtype=loc.dtype.base_dtype,
                is_self_adjoint=True,
                is_positive_definite=True,
                assert_proper_shapes=validate_args)

        return linalg.LinearOperatorScaledIdentity(
            num_rows=num_rows,
            multiplier=_maybe_attach_assertion(scale_identity_multiplier),
            is_non_singular=True,
            is_self_adjoint=True,
            is_positive_definite=assert_positive,
            assert_proper_shapes=validate_args)
Example #12
0
 def test_negative_num_rows_raises_static(self):
     with self.assertRaisesRegexp(ValueError, "must be non-negative"):
         linalg_lib.LinearOperatorIdentity(num_rows=-2)
Example #13
0
 def test_non_integer_num_rows_raises_static(self):
     with self.assertRaisesRegexp(TypeError, "must be integer"):
         linalg_lib.LinearOperatorIdentity(num_rows=2.)
Example #14
0
 def test_non_scalar_num_rows_raises_static(self):
     with self.assertRaisesRegexp(ValueError, "must be a 0-D Tensor"):
         linalg_lib.LinearOperatorIdentity(num_rows=[2])
Example #15
0
 def test_wrong_matrix_dimensions_raises_static(self):
     operator = linalg_lib.LinearOperatorIdentity(num_rows=2)
     x = rng.randn(3, 3).astype(np.float32)
     with self.assertRaisesRegexp(ValueError, "Dimensions.*not compatible"):
         operator.apply(x)
Example #16
0
 def test_assert_self_adjoint(self):
     with self.test_session():
         operator = linalg_lib.LinearOperatorIdentity(num_rows=2)
         operator.assert_self_adjoint().run()  # Should not fail
Example #17
0
 def test_assert_positive_definite(self):
     with self.test_session():
         operator = linalg_lib.LinearOperatorIdentity(num_rows=2)
         operator.assert_positive_definite().run()  # Should not fail
def make_diag_scale(loc=None,
                    scale_diag=None,
                    scale_identity_multiplier=None,
                    shape_hint=None,
                    validate_args=False,
                    assert_positive=False,
                    name=None):
    """Creates a LinOp representing a diagonal matrix.

  Args:
    loc: Floating-point `Tensor`. This is used for inferring shape in the case
      where only `scale_identity_multiplier` is set.
    scale_diag: Floating-point `Tensor` representing the diagonal matrix.
      `scale_diag` has shape [N1, N2, ...  k], which represents a k x k
      diagonal matrix.
      When `None` no diagonal term is added to the LinOp.
    scale_identity_multiplier: floating point rank 0 `Tensor` representing a
      scaling done to the identity matrix.
      When `scale_identity_multiplier = scale_diag = scale_tril = None` then
      `scale += IdentityMatrix`. Otherwise no scaled-identity-matrix is added
      to `scale`.
    shape_hint: scalar integer `Tensor` representing a hint at the dimension of
      the identity matrix when only `scale_identity_multiplier` is set.
    validate_args: Python `bool` indicating whether arguments should be
      checked for correctness.
    assert_positive: Python `bool` indicating whether LinOp should be checked
      for being positive definite.
    name: Python `str` name given to ops managed by this object.

  Returns:
    `LinearOperator` representing a lower triangular matrix.

  Raises:
    ValueError:  If only `scale_identity_multiplier` is set and `loc` and
      `shape_hint` are both None.
  """
    def _maybe_attach_assertion(x):
        if not validate_args:
            return x
        if assert_positive:
            return control_flow_ops.with_dependencies([
                check_ops.assert_positive(
                    x, message="diagonal part must be positive"),
            ], x)
        return control_flow_ops.with_dependencies([
            check_ops.assert_none_equal(
                x,
                array_ops.zeros([], x.dtype),
                message="diagonal part must be non-zero")
        ], x)

    with ops.name_scope(name,
                        "make_diag_scale",
                        values=[loc, scale_diag, scale_identity_multiplier]):
        loc = _convert_to_tensor(loc, name="loc")
        scale_diag = _convert_to_tensor(scale_diag, name="scale_diag")
        scale_identity_multiplier = _convert_to_tensor(
            scale_identity_multiplier, name="scale_identity_multiplier")

        if scale_diag is not None:
            if scale_identity_multiplier is not None:
                scale_diag += scale_identity_multiplier[..., array_ops.newaxis]
            return linalg.LinearOperatorDiag(
                diag=_maybe_attach_assertion(scale_diag),
                is_non_singular=True,
                is_self_adjoint=True,
                is_positive_definite=assert_positive)

        if loc is None and shape_hint is None:
            raise ValueError("Cannot infer `event_shape` unless `loc` or "
                             "`shape_hint` is specified.")

        if shape_hint is None:
            shape_hint = loc.shape[-1]

        if scale_identity_multiplier is None:
            return linalg.LinearOperatorIdentity(
                num_rows=shape_hint,
                dtype=loc.dtype.base_dtype,
                is_self_adjoint=True,
                is_positive_definite=True,
                assert_proper_shapes=validate_args)

        return linalg.LinearOperatorScaledIdentity(
            num_rows=shape_hint,
            multiplier=_maybe_attach_assertion(scale_identity_multiplier),
            is_non_singular=True,
            is_self_adjoint=True,
            is_positive_definite=assert_positive,
            assert_proper_shapes=validate_args)
Example #19
0
    def __init__(self,
                 loc=None,
                 scale_tril=None,
                 validate_args=False,
                 allow_nan_stats=True,
                 name="MultivariateNormalTriL"):
        """Construct Multivariate Normal distribution on `R^k`.

    The `batch_shape` is the broadcast shape between `loc` and `scale`
    arguments.

    The `event_shape` is given by last dimension of the matrix implied by
    `scale`. The last dimension of `loc` (if provided) must broadcast with this.

    Recall that `covariance = scale @ scale.T`. A (non-batch) `scale` matrix is:

    ```none
    scale = scale_tril
    ```

    where `scale_tril` is lower-triangular `k x k` matrix with non-zero
    diagonal, i.e., `tf.diag_part(scale_tril) != 0`.

    Additional leading dimensions (if any) will index batches.

    Args:
      loc: Floating-point `Tensor`. If this is set to `None`, `loc` is
        implicitly `0`. When specified, may have shape `[B1, ..., Bb, k]` where
        `b >= 0` and `k` is the event size.
      scale_tril: Floating-point, lower-triangular `Tensor` with non-zero
        diagonal elements. `scale_tril` has shape `[B1, ..., Bb, k, k]` where
        `b >= 0` and `k` is the event size.
      validate_args: Python `bool`, default `False`. When `True` distribution
        parameters are checked for validity despite possibly degrading runtime
        performance. When `False` invalid inputs may silently render incorrect
        outputs.
      allow_nan_stats: Python `bool`, default `True`. When `True`,
        statistics (e.g., mean, mode, variance) use the value "`NaN`" to
        indicate the result is undefined. When `False`, an exception is raised
        if one or more of the statistic's batch members are undefined.
      name: Python `str` name prefixed to Ops created by this class.

    Raises:
      ValueError: if neither `loc` nor `scale_tril` are specified.
    """
        parameters = locals()

        def _convert_to_tensor(x, name):
            return None if x is None else ops.convert_to_tensor(x, name=name)

        if loc is None and scale_tril is None:
            raise ValueError(
                "Must specify one or both of `loc`, `scale_tril`.")
        with ops.name_scope(name):
            with ops.name_scope("init", values=[loc, scale_tril]):
                loc = _convert_to_tensor(loc, name="loc")
                scale_tril = _convert_to_tensor(scale_tril, name="scale_tril")
                if scale_tril is None:
                    scale = linalg.LinearOperatorIdentity(
                        num_rows=distribution_util.dimension_size(loc, -1),
                        dtype=loc.dtype,
                        is_self_adjoint=True,
                        is_positive_definite=True,
                        assert_proper_shapes=validate_args)
                else:
                    # No need to validate that scale_tril is non-singular.
                    # LinearOperatorLowerTriangular has an assert_non_singular
                    # method that is called by the Bijector.
                    scale = linalg.LinearOperatorLowerTriangular(
                        scale_tril,
                        is_non_singular=True,
                        is_self_adjoint=False,
                        is_positive_definite=False)
        super(MultivariateNormalTriL,
              self).__init__(loc=loc,
                             scale=scale,
                             validate_args=validate_args,
                             allow_nan_stats=allow_nan_stats,
                             name=name)
        self._parameters = parameters
Example #20
0
 def test_non_1d_batch_shape_raises_static(self):
     with self.assertRaisesRegexp(ValueError, "must be a 1-D"):
         linalg_lib.LinearOperatorIdentity(num_rows=2, batch_shape=2)