def test_convert_variables_to_tensors(self):
     tril = variables_module.Variable([[1., 0.], [0., 1.]])
     operator = linalg_lib.LinearOperatorLowerTriangular(
         tril, is_non_singular=True)
     with self.cached_session() as sess:
         sess.run([tril.initializer])
         self.check_convert_variables_to_tensors(operator)
Пример #2
0
    def testTriL(self):
        with self.cached_session():
            shift = np.array([-1, 0, 1], dtype=np.float32)
            tril = np.array([[[3, 0, 0], [2, -1, 0], [3, 2, 1]],
                             [[2, 0, 0], [3, -2, 0], [4, 3, 2]]],
                            dtype=np.float32)
            scale = linalg.LinearOperatorLowerTriangular(tril,
                                                         is_non_singular=True)
            affine = AffineLinearOperator(shift=shift,
                                          scale=scale,
                                          validate_args=True)

            x = np.array([[[1, 0, -1], [2, 3, 4]], [[4, 1, -7], [6, 9, 8]]],
                         dtype=np.float32)
            # If we made the bijector do x*A+b then this would be simplified to:
            # y = np.matmul(x, tril) + shift.
            y = np.squeeze(np.matmul(tril, np.expand_dims(x, -1)), -1) + shift
            ildj = -np.sum(
                np.log(np.abs(np.diagonal(tril, axis1=-2, axis2=-1))))

            self.assertEqual(affine.name, "affine_linear_operator")
            self.assertAllClose(y, affine.forward(x).eval())
            self.assertAllClose(x, affine.inverse(y).eval())
            self.assertAllClose(
                ildj,
                affine.inverse_log_det_jacobian(y, event_ndims=2).eval())
            self.assertAllClose(
                -affine.inverse_log_det_jacobian(y, event_ndims=2).eval(),
                affine.forward_log_det_jacobian(x, event_ndims=2).eval())
  def __init__(self,
               df,
               scale,
               cholesky_input_output_matrices=False,
               validate_args=False,
               allow_nan_stats=True,
               name="WishartCholesky"):
    """Construct Wishart distributions.

    Args:
      df: `float` or `double` `Tensor`. Degrees of freedom, must be greater than
        or equal to dimension of the scale matrix.
      scale: `float` or `double` `Tensor`. The Cholesky factorization of
        the symmetric positive definite scale matrix of the distribution.
      cholesky_input_output_matrices: Python `bool`. Any function which whose
        input or output is a matrix assumes the input is Cholesky and returns a
        Cholesky factored matrix. Example `log_prob` input takes a Cholesky and
        `sample_n` returns a Cholesky when
        `cholesky_input_output_matrices=True`.
      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.
    """
    parameters = dict(locals())
    with ops.name_scope(name, values=[scale]) as name:
      with ops.name_scope("init", values=[scale]):
        scale = ops.convert_to_tensor(scale)
        if validate_args:
          scale = control_flow_ops.with_dependencies([
              check_ops.assert_positive(
                  array_ops.matrix_diag_part(scale),
                  message="scale must be positive definite"),
              check_ops.assert_equal(
                  array_ops.shape(scale)[-1],
                  array_ops.shape(scale)[-2],
                  message="scale must be square")
          ] if validate_args else [], scale)

      super(WishartCholesky, self).__init__(
          df=df,
          scale_operator=linalg.LinearOperatorLowerTriangular(
              tril=scale,
              is_non_singular=True,
              is_positive_definite=True,
              is_square=True),
          cholesky_input_output_matrices=cholesky_input_output_matrices,
          validate_args=validate_args,
          allow_nan_stats=allow_nan_stats,
          name=name)
    self._parameters = parameters
    def test_triangular_diag_matmul(self):
        operator1 = linalg_lib.LinearOperatorLowerTriangular([[1., 0., 0.],
                                                              [2., 1., 0.],
                                                              [2., 3., 3.]])
        operator2 = linalg_lib.LinearOperatorDiag([2., 2., 3.])
        operator_matmul = operator1.matmul(operator2)
        self.assertTrue(
            isinstance(operator_matmul,
                       linalg_lib.LinearOperatorLowerTriangular))
        self.assertAllClose(
            math_ops.matmul(operator1.to_dense(), operator2.to_dense()),
            self.evaluate(operator_matmul.to_dense()))

        operator_matmul = operator2.matmul(operator1)
        self.assertTrue(
            isinstance(operator_matmul,
                       linalg_lib.LinearOperatorLowerTriangular))
        self.assertAllClose(
            math_ops.matmul(operator2.to_dense(), operator1.to_dense()),
            self.evaluate(operator_matmul.to_dense()))
 def test_tape_safe(self):
   tril = variables_module.Variable([[1., 0.], [0., 1.]])
   operator = linalg_lib.LinearOperatorLowerTriangular(
       tril, is_non_singular=True)
   self.check_tape_safe(operator)
def make_tril_scale(loc=None,
                    scale_tril=None,
                    scale_diag=None,
                    scale_identity_multiplier=None,
                    shape_hint=None,
                    validate_args=False,
                    assert_positive=False,
                    name=None):
    """Creates a LinOp representing a lower triangular matrix.

  Args:
    loc: Floating-point `Tensor`. This is used for inferring shape in the case
      where only `scale_identity_multiplier` is set.
    scale_tril: Floating-point `Tensor` representing the diagonal matrix.
      `scale_diag` has shape [N1, N2, ...  k, k], which represents a k x k
      lower triangular matrix.
      When `None` no `scale_tril` term is added to the LinOp.
      The upper triangular elements above the diagonal are ignored.
    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(
                    array_ops.matrix_diag_part(x),
                    message="diagonal part must be positive"),
            ], x)
        return control_flow_ops.with_dependencies([
            check_ops.assert_none_equal(
                array_ops.matrix_diag_part(x),
                array_ops.zeros([], x.dtype),
                message="diagonal part must be non-zero"),
        ], x)

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

        loc = _convert_to_tensor(loc, name="loc")
        scale_tril = _convert_to_tensor(scale_tril, name="scale_tril")
        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_tril is not None:
        scale_tril = array_ops.matrix_band_part(scale_tril, -1,
                                                0)  # Zero out TriU.
        tril_diag = array_ops.matrix_diag_part(scale_tril)
        if scale_diag is not None:
            tril_diag += scale_diag
        if scale_identity_multiplier is not None:
            tril_diag += scale_identity_multiplier[..., array_ops.newaxis]

        scale_tril = array_ops.matrix_set_diag(scale_tril, tril_diag)

        return linalg.LinearOperatorLowerTriangular(
            tril=_maybe_attach_assertion(scale_tril),
            is_non_singular=True,
            is_self_adjoint=False,
            is_positive_definite=assert_positive)

    return make_diag_scale(loc=loc,
                           scale_diag=scale_diag,
                           scale_identity_multiplier=scale_identity_multiplier,
                           shape_hint=shape_hint,
                           validate_args=validate_args,
                           assert_positive=assert_positive,
                           name=name)
Пример #7
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.linalg.tensor_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 = dict(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) as 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