def quadrature_scheme_softmaxnormal_gauss_hermite(
    normal_loc, normal_scale, quadrature_size,
    validate_args=False, name=None):
  """Use Gauss-Hermite quadrature to form quadrature on `K - 1` simplex.

  A `SoftmaxNormal` random variable `Y` may be generated via

  ```
  Y = SoftmaxCentered(X),
  X = Normal(normal_loc, normal_scale)
  ```

  Note: for a given `quadrature_size`, this method is generally less accurate
  than `quadrature_scheme_softmaxnormal_quantiles`.

  Args:
    normal_loc: `float`-like `Tensor` with shape `[b1, ..., bB, K-1]`, B>=0.
      The location parameter of the Normal used to construct the SoftmaxNormal.
    normal_scale: `float`-like `Tensor`. Broadcastable with `normal_loc`.
      The scale parameter of the Normal used to construct the SoftmaxNormal.
    quadrature_size: Python `int` scalar representing the number of quadrature
      points.
    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.
    name: Python `str` name prefixed to Ops created by this class.

  Returns:
    grid: Shape `[b1, ..., bB, K, quadrature_size]` `Tensor` representing the
      convex combination of affine parameters for `K` components.
      `grid[..., :, n]` is the `n`-th grid point, living in the `K - 1` simplex.
    probs:  Shape `[b1, ..., bB, K, quadrature_size]` `Tensor` representing the
      associated with each grid point.
  """
  with ops.name_scope(name, "quadrature_scheme_softmaxnormal_gauss_hermite",
                      [normal_loc, normal_scale]):
    normal_loc = ops.convert_to_tensor(normal_loc, name="normal_loc")
    dt = normal_loc.dtype.base_dtype
    normal_scale = ops.convert_to_tensor(
        normal_scale, dtype=dt, name="normal_scale")

    normal_scale = maybe_check_quadrature_param(
        normal_scale, "normal_scale", validate_args)

    grid, probs = np.polynomial.hermite.hermgauss(deg=quadrature_size)
    grid = grid.astype(dt.dtype.as_numpy_dtype)
    probs = probs.astype(dt.dtype.as_numpy_dtype)
    probs /= np.linalg.norm(probs, ord=1, keepdims=True)
    probs = ops.convert_to_tensor(probs, name="probs", dtype=dt)

    grid = softmax(
        -distribution_util.pad(
            (normal_loc[..., array_ops.newaxis] +
             np.sqrt(2.) * normal_scale[..., array_ops.newaxis] * grid),
            axis=-2,
            front=True),
        axis=-2)  # shape: [B, components, deg]

    return grid, probs
Exemple #2
0
def quadrature_scheme_softmaxnormal_gauss_hermite(
    normal_loc, normal_scale, quadrature_size,
    validate_args=False, name=None):
  """Use Gauss-Hermite quadrature to form quadrature on `K - 1` simplex.

  A `SoftmaxNormal` random variable `Y` may be generated via

  ```
  Y = SoftmaxCentered(X),
  X = Normal(normal_loc, normal_scale)
  ```

  Note: for a given `quadrature_size`, this method is generally less accurate
  than `quadrature_scheme_softmaxnormal_quantiles`.

  Args:
    normal_loc: `float`-like `Tensor` with shape `[b1, ..., bB, K-1]`, B>=0.
      The location parameter of the Normal used to construct the SoftmaxNormal.
    normal_scale: `float`-like `Tensor`. Broadcastable with `normal_loc`.
      The scale parameter of the Normal used to construct the SoftmaxNormal.
    quadrature_size: Python `int` scalar representing the number of quadrature
      points.
    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.
    name: Python `str` name prefixed to Ops created by this class.

  Returns:
    grid: Shape `[b1, ..., bB, K, quadrature_size]` `Tensor` representing the
      convex combination of affine parameters for `K` components.
      `grid[..., :, n]` is the `n`-th grid point, living in the `K - 1` simplex.
    probs:  Shape `[b1, ..., bB, K, quadrature_size]` `Tensor` representing the
      associated with each grid point.
  """
  with ops.name_scope(name, "quadrature_scheme_softmaxnormal_gauss_hermite",
                      [normal_loc, normal_scale]):
    normal_loc = ops.convert_to_tensor(normal_loc, name="normal_loc")
    dt = normal_loc.dtype.base_dtype
    normal_scale = ops.convert_to_tensor(
        normal_scale, dtype=dt, name="normal_scale")

    normal_scale = maybe_check_quadrature_param(
        normal_scale, "normal_scale", validate_args)

    grid, probs = np.polynomial.hermite.hermgauss(deg=quadrature_size)
    grid = grid.astype(dt.dtype.as_numpy_dtype)
    probs = probs.astype(dt.dtype.as_numpy_dtype)
    probs /= np.linalg.norm(probs, ord=1, keepdims=True)
    probs = ops.convert_to_tensor(probs, name="probs", dtype=dt)

    grid = softmax(
        -distribution_util.pad(
            (normal_loc[..., array_ops.newaxis] +
             np.sqrt(2.) * normal_scale[..., array_ops.newaxis] * grid),
            axis=-2,
            front=True),
        axis=-2)  # shape: [B, components, deg]

    return grid, probs
Exemple #3
0
    def testPosAxisCorrectness(self):
        x_ = np.float32([[1., 2, 3], [4, 5, 6]])
        value_ = np.float32(0.25)
        count_ = np.int32(2)
        with self.test_session() as sess:
            x = array_ops.placeholder_with_default(
                x_, shape=x_.shape if self.is_static_shape else None)
            value = (constant_op.constant(value_) if self.is_static_shape else
                     array_ops.placeholder_with_default(value_, shape=None))
            count = (constant_op.constant(count_) if self.is_static_shape else
                     array_ops.placeholder_with_default(count_, shape=None))

            x1_front = distribution_util.pad(x,
                                             axis=1,
                                             value=value,
                                             count=count,
                                             front=True)
            x1_back = distribution_util.pad(x, axis=1, count=count, back=True)
            x1_both = distribution_util.pad(x,
                                            axis=1,
                                            value=value,
                                            front=True,
                                            back=True)

            if self.is_static_shape:
                self.assertAllEqual([2, 5], x1_front.shape)
                self.assertAllEqual([2, 5], x1_back.shape)
                self.assertAllEqual([2, 5], x1_both.shape)

            [x1_front_, x1_back_,
             x1_both_] = sess.run([x1_front, x1_back, x1_both])

            self.assertAllClose(np.float32([[value_] * 2 + [1, 2, 3],
                                            [value_] * 2 + [4, 5, 6]]),
                                x1_front_,
                                atol=0.,
                                rtol=1e-6)
            self.assertAllClose(np.float32([[1, 2, 3] + [0.] * 2,
                                            [4, 5, 6] + [0.] * 2]),
                                x1_back_,
                                atol=0.,
                                rtol=1e-6)
            self.assertAllClose(np.float32([[value_, 1, 2, 3, value_],
                                            [value_, 4, 5, 6, value_]]),
                                x1_both_,
                                atol=0.,
                                rtol=1e-6)
  def testNegAxisCorrectness(self):
    x_ = np.float32([[1., 2, 3],
                     [4, 5, 6]])
    value_ = np.float32(0.25)
    count_ = np.int32(2)
    with self.test_session() as sess:
      x = array_ops.placeholder_with_default(
          x_, shape=x_.shape if self.is_static_shape else None)
      value = (constant_op.constant(value_) if self.is_static_shape
               else array_ops.placeholder_with_default(value_, shape=None))
      count = (constant_op.constant(count_) if self.is_static_shape
               else array_ops.placeholder_with_default(count_, shape=None))

      x0_front = distribution_util.pad(
          x, axis=-2, value=value, count=count, front=True)
      x0_back = distribution_util.pad(
          x, axis=-2, count=count, back=True)
      x0_both = distribution_util.pad(
          x, axis=-2, value=value, front=True, back=True)

      if self.is_static_shape:
        self.assertAllEqual([4, 3], x0_front.shape)
        self.assertAllEqual([4, 3], x0_back.shape)
        self.assertAllEqual([4, 3], x0_both.shape)

      [x0_front_, x0_back_, x0_both_] = sess.run([
          x0_front, x0_back, x0_both])

      self.assertAllClose(
          np.float32([[value_]*3,
                      [value_]*3,
                      [1, 2, 3],
                      [4, 5, 6]]),
          x0_front_, atol=0., rtol=1e-6)
      self.assertAllClose(
          np.float32([[1, 2, 3],
                      [4, 5, 6],
                      [0.]*3,
                      [0.]*3]),
          x0_back_, atol=0., rtol=1e-6)
      self.assertAllClose(
          np.float32([[value_]*3,
                      [1, 2, 3],
                      [4, 5, 6],
                      [value_]*3]),
          x0_both_, atol=0., rtol=1e-6)
Exemple #5
0
    def _forward(self, x):
        # Pad the last dim with a zeros vector. We need this because it lets us
        # infer the scale in the inverse function.
        y = distribution_util.pad(x, axis=-1, back=True)

        # Set shape hints.
        if x.shape.ndims is not None:
            shape = x.shape[:-1].concatenate(x.shape[-1] + 1)
            y.shape.assert_is_compatible_with(shape)
            y.set_shape(shape)

        return nn_ops.softmax(y)
  def _forward(self, x):
    # Pad the last dim with a zeros vector. We need this because it lets us
    # infer the scale in the inverse function.
    y = distribution_util.pad(x, axis=-1, back=True)

    # Set shape hints.
    if x.shape.ndims is not None:
      shape = x.shape[:-1].concatenate(x.shape[-1] + 1)
      y.shape.assert_is_compatible_with(shape)
      y.set_shape(shape)

    return nn_ops.softmax(y)
Exemple #7
0
    def _forward(self, x):
        # Pad the last dim with a zeros vector. We need this because it lets us
        # infer the scale in the inverse function.
        y = distribution_util.pad(x, axis=-1, back=True)

        # Set shape hints.
        if x.shape.ndims is not None:
            shape = x.shape[:-1].concatenate(x.shape[-1] + 1)
            y.shape.assert_is_compatible_with(shape)
            y.set_shape(shape)

        # Since we only support event_ndims in [0, 1] and we do padding, we always
        # reduce over the last dimension, i.e., dim=-1 (which is the default).
        return nn_ops.softmax(y)
  def _forward(self, x):
    # Pad the last dim with a zeros vector. We need this because it lets us
    # infer the scale in the inverse function.
    y = distribution_util.pad(x, axis=-1, back=True)

    # Set shape hints.
    if x.shape.ndims is not None:
      shape = x.shape[:-1].concatenate(x.shape[-1] + 1)
      y.shape.assert_is_compatible_with(shape)
      y.set_shape(shape)

    # Since we only support event_ndims in [0, 1] and we do padding, we always
    # reduce over the last dimension, i.e., dim=-1 (which is the default).
    return nn_ops.softmax(y)
  def _forward(self, x):
    # Pad the last dim with a zeros vector. We need this because it lets us
    # infer the scale in the inverse function.
    y = array_ops.expand_dims(x, dim=-1) if self._static_event_ndims == 0 else x
    y = distribution_util.pad(y, axis=-1, back=True)

    # Set shape hints.
    if x.shape.ndims is not None:
      shape = x.shape.as_list()
      if self._static_event_ndims == 0:
        shape += [2]
      elif shape[-1] is not None:
        shape[-1] += 1
      shape = tensor_shape.TensorShape(shape)
      y.shape.assert_is_compatible_with(shape)
      y.set_shape(shape)

    # Since we only support event_ndims in [0, 1] and we do padding, we always
    # reduce over the last dimension, i.e., dim=-1 (which is the default).
    return nn_ops.softmax(y)