def testTriL(self): with self.test_session(): shift = np.array([-1, 0, 1], dtype=np.float32) tril = np.array([[[1, 0, 0], [2, -1, 0], [3, 2, 1]], [[2, 0, 0], [3, -2, 0], [4, 3, 2]]], dtype=np.float32) scale = linalg.LinearOperatorTriL(tril, is_non_singular=True) affine = affine_linear_operator_lib.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))), axis=-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).eval()) self.assertAllClose(-affine.inverse_log_det_jacobian(y).eval(), affine.forward_log_det_jacobian(x).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 = locals() with ops.name_scope(name, values=[scale]): 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.LinearOperatorTriL( 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 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.LinearOperatorTriL( 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)
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 the last dimension of `loc` or the last dimension of the matrix implied by `scale`. 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: if validate_args: scale_tril = control_flow_ops.with_dependencies( [ # TODO(b/35157376): Use `assert_none_equal` once it exists. check_ops.assert_greater( math_ops.abs( array_ops.matrix_diag_part( scale_tril)), array_ops.zeros([], scale_tril.dtype), message= "`scale_tril` must have non-zero diagonal" ), ], scale_tril) scale = linalg.LinearOperatorTriL( 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