if sample_shape_static_val is None:
            if ndims is None or not sample_shape.get_shape().is_fully_defined(
            ):
                ndims = array_ops.rank(sample_shape)
            expanded_shape = distribution_util.pick_vector(
                math_ops.equal(ndims, 0),
                np.array((1, ), dtype=dtypes.int32.as_numpy_dtype()),
                array_ops.shape(sample_shape))
            sample_shape = array_ops.reshape(sample_shape, expanded_shape)
            total = math_ops.reduce_prod(sample_shape)  # reduce_prod([]) == 1
        else:
            if ndims is None:
                raise ValueError(
                    "Shouldn't be here; ndims cannot be none when we have a "
                    "tf.constant shape.")
            if ndims == 0:
                sample_shape_static_val = np.reshape(sample_shape_static_val,
                                                     [1])
                sample_shape = ops.convert_to_tensor(sample_shape_static_val,
                                                     dtype=dtypes.int32,
                                                     name="sample_shape")
            total = np.prod(sample_shape_static_val,
                            dtype=dtypes.int32.as_numpy_dtype())
        return sample_shape, total


distribution_util.append_class_fun_doc(BaseDistribution.sample_n,
                                       doc_str=Distribution.sample_n.__doc__)
distribution_util.append_class_fun_doc(BaseDistribution.log_prob,
                                       doc_str=Distribution.log_prob.__doc__)
Exemple #2
0
    ndims = sample_shape.get_shape().ndims
    if sample_shape_static_val is None:
      if ndims is None or not sample_shape.get_shape().is_fully_defined():
        ndims = array_ops.rank(sample_shape)
      expanded_shape = distribution_util.pick_vector(
          math_ops.equal(ndims, 0),
          np.array((1,), dtype=dtypes.int32.as_numpy_dtype()),
          array_ops.shape(sample_shape))
      sample_shape = array_ops.reshape(sample_shape, expanded_shape)
      total = math_ops.reduce_prod(sample_shape)  # reduce_prod([]) == 1
    else:
      if ndims is None:
        raise ValueError(
            "Shouldn't be here; ndims cannot be none when we have a "
            "tf.constant shape.")
      if ndims == 0:
        sample_shape_static_val = np.reshape(sample_shape_static_val, [1])
        sample_shape = ops.convert_to_tensor(
            sample_shape_static_val,
            dtype=dtypes.int32,
            name="sample_shape")
      total = np.prod(sample_shape_static_val,
                      dtype=dtypes.int32.as_numpy_dtype())
    return sample_shape, total


distribution_util.append_class_fun_doc(BaseDistribution.sample_n,
                                       doc_str=Distribution.sample_n.__doc__)
distribution_util.append_class_fun_doc(BaseDistribution.log_prob,
                                       doc_str=Distribution.log_prob.__doc__)
Exemple #3
0
              message="mode not defined for components of alpha <= 1")
      ], mode)

  def _assert_valid_sample(self, x):
    if not self.validate_args: return x
    return control_flow_ops.with_dependencies([
        check_ops.assert_positive(x),
        distribution_util.assert_close(
            array_ops.ones((), dtype=self.dtype),
            math_ops.reduce_sum(x, reduction_indices=[-1])),
    ], x)


_prob_note = """

  Note that the input must be a non-negative tensor with dtype `dtype` and whose
  shape can be broadcast with `self.alpha`.  For fixed leading dimensions, the
  last dimension represents counts for the corresponding Dirichlet distribution
  in `self.alpha`. `x` is only legal if it sums up to one.
"""
distribution_util.append_class_fun_doc(Dirichlet.log_prob, doc_str=_prob_note)
distribution_util.append_class_fun_doc(Dirichlet.prob, doc_str=_prob_note)

distribution_util.append_class_fun_doc(Dirichlet.mode, doc_str="""

  Note that the mode for the Dirichlet distribution is only defined
  when `alpha > 1`. This returns the mode when `alpha > 1`,
  and NaN otherwise. If `self.allow_nan_stats` is `False`, an exception
  will be raised rather than returning `NaN`.
""")
Exemple #4
0
    return control_flow_ops.with_dependencies([
        check_ops.assert_non_negative(
            counts, message="counts has negative components."),
        check_ops.assert_less_equal(
            counts, self._n, message="counts are not less than or equal to n."),
        distribution_util.assert_integer_form(
            counts, message="counts have non-integer components.")], counts)


_prob_note = """

    For each batch member of counts `k`, `P[counts]` is the probability that
    after sampling `n` draws from this Binomial distribution, the number of
    successes is `k`.  Note that different sequences of draws can result in the
    same counts, thus the probability includes a combinatorial coefficient.

    counts:  Non-negative tensor with dtype `dtype` and whose shape can be
      broadcast with `self.p` and `self.n`. `counts` is only legal if it is
      less than or equal to `n` and its components are equal to integer
      values.
"""
distribution_util.append_class_fun_doc(Binomial.log_prob, doc_str=_prob_note)
distribution_util.append_class_fun_doc(Binomial.prob, doc_str=_prob_note)

distribution_util.append_class_fun_doc(Binomial.mode, doc_str="""

    Note that when `(n + 1) * p` is an integer, there are actually two modes.
    Namely, `(n + 1) * p` and `(n + 1) * p - 1` are both modes. Here we return
    only the larger of the two modes.
""")
accurate for large values of `y`, and in this case the `survival_function` must
also be defined on `y - 1`.
"""
)

_log_prob_note = (
    _prob_base_note
    + """
The base distribution's `log_cdf` method must be defined on `y - 1`.  If the
base distribution has a `log_survival_function` method results will be more
accurate for large values of `y`, and in this case the `log_survival_function`
must also be defined on `y - 1`.
"""
)

distribution_util.append_class_fun_doc(QuantizedDistribution.prob, doc_str=_prob_note)

distribution_util.append_class_fun_doc(QuantizedDistribution.log_prob, doc_str=_log_prob_note)

_cdf_base_note = """

For whole numbers `y`,

```
cdf(y) := P[Y <= y]
        = 1, if y >= upper_cutoff,
        = 0, if y < lower_cutoff,
        = P[X <= y], otherwise.
```

Since `Y` only has mass at whole numbers, `P[Y <= y] = P[Y <= floor(y)]`.
Exemple #6
0
      nan = np.array(np.nan, dtype=self.dtype.as_numpy_dtype())
      return math_ops.select(
          self.alpha >= 1.,
          mode,
          array_ops.fill(self.batch_shape(), nan, name="nan"))
    else:
      return control_flow_ops.with_dependencies([
          check_ops.assert_less(
              array_ops.ones((), self.dtype),
              self.alpha,
              message="mode not defined for components of alpha <= 1"),
          ], mode)


distribution_util.append_class_fun_doc(Gamma.sample_n, doc_str="""

    See the documentation for tf.random_gamma for more details.
""")

distribution_util.append_class_fun_doc(Gamma.entropy, doc_str="""

    This is defined to be

    ```
    entropy = alpha - log(beta) + log(Gamma(alpha))
                 + (1-alpha)digamma(alpha)
    ```

    where digamma(alpha) is the digamma function.
""")

distribution_util.append_class_fun_doc(Gamma.mode, doc_str="""
Exemple #7
0
            check_ops.assert_positive(
                x,
                message="Negative events lie outside Beta distribution support."
            ),
            check_ops.assert_less(
                x,
                array_ops.ones((), self.dtype),
                message="Event>=1 lies outside Beta distribution support."),
        ], x)


_prob_note = """

    Note that the argument `x` must be a non-negative floating point tensor
    whose shape can be broadcast with `self.a` and `self.b`.  For fixed leading
    dimensions, the last dimension represents counts for the corresponding Beta
    distribution in `self.a` and `self.b`. `x` is only legal if `0 < x < 1`.
"""

distribution_util.append_class_fun_doc(Beta.log_prob, doc_str=_prob_note)
distribution_util.append_class_fun_doc(Beta.prob, doc_str=_prob_note)

distribution_util.append_class_fun_doc(Beta.mode,
                                       doc_str="""

    Note that the mode for the Beta distribution is only defined
    when `a > 1`, `b > 1`. This returns the mode when `a > 1` and `b > 1`,
    and `NaN` otherwise. If `self.allow_nan_stats` is `False`, an exception
    will be raised rather than returning `NaN`.
""")
Exemple #8
0
    def _mean(self):
        return array_ops.identity(self.p)

    def _variance(self):
        return self.q * self.p

    def _std(self):
        return math_ops.sqrt(self._variance())

    def _mode(self):
        return math_ops.cast(self.p > self.q, self.dtype)


distribution_util.append_class_fun_doc(Bernoulli.mode,
                                       doc_str="""

  Specific notes:
    1 if p > 1-p. 0 otherwise.
""")


@kullback_leibler.RegisterKL(Bernoulli, Bernoulli)
def _kl_bernoulli_bernoulli(a, b, name=None):
    """Calculate the batched KL divergence KL(a || b) with a and b Bernoulli.

  Args:
    a: instance of a Bernoulli distribution object.
    b: instance of a Bernoulli distribution object.
    name: (optional) Name to use for created operations.
      default is "kl_bernoulli_bernoulli".

  Returns:
Exemple #9
0
                counts,
                self._n,
                message="counts are not less than or equal to n."),
            distribution_util.assert_integer_form(
                counts, message="counts have non-integer components.")
        ], counts)


_prob_note = """

    For each batch member of counts `k`, `P[counts]` is the probability that
    after sampling `n` draws from this Binomial distribution, the number of
    successes is `k`.  Note that different sequences of draws can result in the
    same counts, thus the probability includes a combinatorial coefficient.

    counts:  Non-negative tensor with dtype `dtype` and whose shape can be
      broadcast with `self.p` and `self.n`. `counts` is only legal if it is
      less than or equal to `n` and its components are equal to integer
      values.
"""
distribution_util.append_class_fun_doc(Binomial.log_prob, doc_str=_prob_note)
distribution_util.append_class_fun_doc(Binomial.prob, doc_str=_prob_note)

distribution_util.append_class_fun_doc(Binomial.mode,
                                       doc_str="""

    Note that when `(n + 1) * p` is an integer, there are actually two modes.
    Namely, `(n + 1) * p` and `(n + 1) * p - 1` are both modes. Here we return
    only the larger of the two modes.
""")
Exemple #10
0
              message="variance not defined for components of df <= 1"),
      ], result_where_defined)

  def _std(self):
    return math_ops.sqrt(self.variance())

  def _mode(self):
    return array_ops.identity(self.mu)

  def _ones(self):
    return array_ops.ones(self.batch_shape(), dtype=self.dtype)


distribution_util.append_class_fun_doc(StudentT.mean, doc_str="""

    The mean of Student's T equals `mu` if `df > 1`, otherwise it is `NaN`.  If
    `self.allow_nan_stats=False`, then an exception will be raised rather than
    returning `NaN`.
""")

distribution_util.append_class_fun_doc(StudentT.variance, doc_str="""

    Variance for Student's T equals

    ```
    df / (df - 2), when df > 2
    infinity, when 1 < df <= 2
    NaN, when df <= 1
    ```

""")
Exemple #11
0
    return math_ops.sqrt(self.variance())

  def _mode(self):
    return math_ops.floor(self.lam)

  def _assert_valid_sample(self, x, check_integer=True):
    if not self.validate_args: return x
    with ops.name_scope('check_x', values=[x]):
      dependencies = [check_ops.assert_non_negative(x)]
      if check_integer:
        dependencies += [distribution_util.assert_integer_form(
            x, message="x has non-integer components.")]
      return control_flow_ops.with_dependencies(dependencies, x)


_prob_note = """

    Note thet the input value must be a non-negative floating point tensor with
    dtype `dtype` and whose shape can be broadcast with `self.lam`. `x` is only
    legal if it is non-negative and its components are equal to integer values.
"""
distribution_util.append_class_fun_doc(Poisson.log_prob, doc_str=_prob_note)
distribution_util.append_class_fun_doc(Poisson.prob, doc_str=_prob_note)

distribution_util.append_class_fun_doc(Poisson.mode, doc_str="""

    Note that when `lam` is an integer, there are actually two modes.
    Namely, `lam` and `lam - 1` are both modes. Here we return
    only the larger of the two modes.
""")
_prob_note = """

  For each batch of counts `[n_1,...,n_k]`, `P[counts]` is the probability
  that after sampling `n` draws from this Dirichlet Multinomial
  distribution, the number of draws falling in class `j` is `n_j`.  Note that
  different sequences of draws can result in the same counts, thus the
  probability includes a combinatorial coefficient.

  Note that input, "counts", must be a non-negative tensor with dtype `dtype`
  and whose shape can be broadcast with `self.alpha`.  For fixed leading
  dimensions, the last dimension represents counts for the corresponding
  Dirichlet Multinomial distribution in `self.alpha`. `counts` is only legal if
  it sums up to `n` and its components are equal to integer values.
"""
distribution_util.append_class_fun_doc(DirichletMultinomial.log_prob,
                                       doc_str=_prob_note)
distribution_util.append_class_fun_doc(DirichletMultinomial.prob,
                                       doc_str=_prob_note)

distribution_util.append_class_fun_doc(DirichletMultinomial.variance,
                                       doc_str="""

  The variance for each batch member is defined as the following:

  ```
  Var(X_j) = n * alpha_j / alpha_0 * (1 - alpha_j / alpha_0) *
    (n + alpha_0) / (1 + alpha_0)
  ```

  where `alpha_0 = sum_j alpha_j`.
Exemple #13
0
            ], result_where_defined)

    def _std(self):
        return math_ops.sqrt(self.variance())

    def _mode(self):
        return array_ops.identity(self.mu)

    def _ones(self):
        return array_ops.ones(self.batch_shape(), dtype=self.dtype)


distribution_util.append_class_fun_doc(StudentT.mean,
                                       doc_str="""

    The mean of Student's T equals `mu` if `df > 1`, otherwise it is `NaN`.  If
    `self.allow_nan_stats=False`, then an exception will be raised rather than
    returning `NaN`.
""")

distribution_util.append_class_fun_doc(StudentT.variance,
                                       doc_str="""

    Variance for Student's T equals

    ```
    df / (df - 2), when df > 2
    infinity, when 1 < df <= 2
    NaN, when df <= 1
    ```
Exemple #14
0
                check_ops.assert_positive(x, message="Negative events lie outside Beta distribution support."),
                check_ops.assert_less(
                    x, array_ops.ones((), self.dtype), message="Event>=1 lies outside Beta distribution support."
                ),
            ],
            x,
        )


_prob_note = """

    Note that the argument `x` must be a non-negative floating point tensor
    whose shape can be broadcast with `self.a` and `self.b`.  For fixed leading
    dimensions, the last dimension represents counts for the corresponding Beta
    distribution in `self.a` and `self.b`. `x` is only legal if `0 < x < 1`.
"""

distribution_util.append_class_fun_doc(Beta.log_prob, doc_str=_prob_note)
distribution_util.append_class_fun_doc(Beta.prob, doc_str=_prob_note)

distribution_util.append_class_fun_doc(
    Beta.mode,
    doc_str="""

    Note that the mode for the Beta distribution is only defined
    when `a > 1`, `b > 1`. This returns the mode when `a > 1` and `b > 1`,
    and `NaN` otherwise. If `self.allow_nan_stats` is `False`, an exception
    will be raised rather than returning `NaN`.
""",
)
        y = ops.convert_to_tensor(y, name="y")
        with ops.name_scope("inverse"):
            if y in self._inverse_cache:
                x = self._inverse_cache[y]
            elif self.inverse:
                x = self.inverse(y)
            else:
                raise ValueError("No inverse function exists and input `y` was not " "returned from `sample`.")
        return x


distribution_util.append_class_fun_doc(
    TransformedDistribution.batch_shape,
    doc_str="""

    The product of the dimensions of the `batch_shape` is the number of
    independent distributions of this kind the instance represents.

""",
)

distribution_util.append_class_fun_doc(
    TransformedDistribution.sample_n,
    doc_str="""

    Samples from the base distribution and then passes through the transform.
""",
)

distribution_util.append_class_fun_doc(
    TransformedDistribution.log_prob,
Exemple #16
0
        return math_ops.floor(self.lam)

    def _assert_valid_sample(self, x, check_integer=True):
        if not self.validate_args: return x
        with ops.name_scope("check_x", values=[x]):
            dependencies = [check_ops.assert_non_negative(x)]
            if check_integer:
                dependencies += [
                    distribution_util.assert_integer_form(
                        x, message="x has non-integer components.")
                ]
            return control_flow_ops.with_dependencies(dependencies, x)


_prob_note = """

    Note thet the input value must be a non-negative floating point tensor with
    dtype `dtype` and whose shape can be broadcast with `self.lam`. `x` is only
    legal if it is non-negative and its components are equal to integer values.
"""
distribution_util.append_class_fun_doc(Poisson.log_prob, doc_str=_prob_note)
distribution_util.append_class_fun_doc(Poisson.prob, doc_str=_prob_note)

distribution_util.append_class_fun_doc(Poisson.mode,
                                       doc_str="""

    Note that when `lam` is an integer, there are actually two modes.
    Namely, `lam` and `lam - 1` are both modes. Here we return
    only the larger of the two modes.
""")
Exemple #17
0
  def _mode(self):
    return array_ops.identity(self._mu)


_prob_note = """
    `x` is a batch vector with compatible shape if `x` is a `Tensor` whose
    shape can be broadcast up to either:

    ````
    self.batch_shape + self.event_shape
    OR
    [M1,...,Mm] + self.batch_shape + self.event_shape
    ```

"""
distribution_util.append_class_fun_doc(_MultivariateNormalOperatorPD.log_prob,
                                       doc_str=_prob_note)
distribution_util.append_class_fun_doc(_MultivariateNormalOperatorPD.prob,
                                       doc_str=_prob_note)


class MultivariateNormalDiag(_MultivariateNormalOperatorPD):
  """The multivariate normal distribution on `R^k`.

  This distribution is defined by a 1-D mean `mu` and a 1-D diagonal
  `diag_stdev`, representing the standard deviations.  This distribution
  assumes the random variables, `(X_1,...,X_k)` are independent, thus no
  non-diagonal terms of the covariance matrix are needed.

  This allows for `O(k)` pdf evaluation, sampling, and storage.

  #### Mathematical details
Exemple #18
0
  def _mean(self):
    return array_ops.identity(self.p)

  def _variance(self):
    return self.q * self.p

  def _std(self):
    return math_ops.sqrt(self._variance())

  def _mode(self):
    return math_ops.cast(self.p > self.q, self.dtype)


distribution_util.append_class_fun_doc(Bernoulli.mode, doc_str="""

  Specific notes:
    1 if p > 1-p. 0 otherwise.
""")


class BernoulliWithSigmoidP(Bernoulli):
  """Bernoulli with `p = sigmoid(p)`."""

  def __init__(self,
               p=None,
               dtype=dtypes.int32,
               validate_args=False,
               allow_nan_stats=True,
               name="BernoulliWithSigmoidP"):
    with ops.name_scope(name) as ns:
      super(BernoulliWithSigmoidP, self).__init__(
Exemple #19
0
  def _assert_valid_sample(self, counts):
    """Check counts for proper shape, values, then return tensor version."""
    if not self.validate_args: return counts
    return control_flow_ops.with_dependencies([
        check_ops.assert_non_negative(
            counts, message="counts has negative components."),
        check_ops.assert_equal(
            self.n, math_ops.reduce_sum(counts, reduction_indices=[-1]),
            message="counts do not sum to n."),
        distribution_util.assert_integer_form(
            counts, message="counts have non-integer components.")
    ], counts)

_prob_note = """

    For each batch of counts `[n_1,...,n_k]`, `P[counts]` is the probability
    that after sampling `n` draws from this Multinomial distribution, the
    number of draws falling in class `j` is `n_j`.  Note that different
    sequences of draws can result in the same counts, thus the probability
    includes a combinatorial coefficient.

    Note that input "counts" must be a non-negative tensor with dtype `dtype`
    and whose shape can be broadcast with `self.p` and `self.n`.  For fixed
    leading dimensions, the last dimension represents counts for the
    corresponding Multinomial distribution in `self.p`. `counts` is only legal
    if it sums up to `n` and its components are equal to integer values.
"""
distribution_util.append_class_fun_doc(Multinomial.log_prob, doc_str=_prob_note)
distribution_util.append_class_fun_doc(Multinomial.prob, doc_str=_prob_note)
Exemple #20
0
            else:
                raise ValueError(
                    "No inverse function exists and input `y` was not "
                    "returned from `sample`.")
        with ops.name_scope("log_det_jacobian"):
            log_det_jacobian = self.log_det_jacobian(x)
        return self.base_distribution.log_prob(x) - log_det_jacobian

    def _prob(self, y):
        return math_ops.exp(self._log_prob(y))


distribution_util.append_class_fun_doc(TransformedDistribution.batch_shape,
                                       doc_str="""

    The product of the dimensions of the `batch_shape` is the number of
    independent distributions of this kind the instance represents.

""")

distribution_util.append_class_fun_doc(TransformedDistribution.sample_n,
                                       doc_str="""

    Samples from the base distribution and then passes through the transform.
""")

distribution_util.append_class_fun_doc(TransformedDistribution.log_prob,
                                       doc_str="""

  `(log o p o g)(y) - (log o det o J o g)(y)`,
  where `g` is the inverse of `transform`.
Exemple #21
0
            nan = np.array(np.nan, dtype=self.dtype.as_numpy_dtype())
            return math_ops.select(
                self.alpha >= 1., mode,
                array_ops.fill(self.batch_shape(), nan, name="nan"))
        else:
            return control_flow_ops.with_dependencies([
                check_ops.assert_less(
                    array_ops.ones((), self.dtype),
                    self.alpha,
                    message="mode not defined for components of alpha <= 1"),
            ], mode)


distribution_util.append_class_fun_doc(Gamma.sample_n,
                                       doc_str="""

    See the documentation for tf.random_gamma for more details.
""")

distribution_util.append_class_fun_doc(Gamma.entropy,
                                       doc_str="""

    This is defined to be

    ```
    entropy = alpha - log(beta) + log(Gamma(alpha))
                 + (1-alpha)digamma(alpha)
    ```

    where digamma(alpha) is the digamma function.
""")
_prob_note = """

  For each batch of counts `[n_1,...,n_k]`, `P[counts]` is the probability
  that after sampling `n` draws from this Dirichlet Multinomial
  distribution, the number of draws falling in class `j` is `n_j`.  Note that
  different sequences of draws can result in the same counts, thus the
  probability includes a combinatorial coefficient.

  Note that input, "counts", must be a non-negative tensor with dtype `dtype`
  and whose shape can be broadcast with `self.alpha`.  For fixed leading
  dimensions, the last dimension represents counts for the corresponding
  Dirichlet Multinomial distribution in `self.alpha`. `counts` is only legal if
  it sums up to `n` and its components are equal to integer values.
"""
distribution_util.append_class_fun_doc(DirichletMultinomial.log_prob,
                                       doc_str=_prob_note)
distribution_util.append_class_fun_doc(DirichletMultinomial.prob,
                                       doc_str=_prob_note)

distribution_util.append_class_fun_doc(DirichletMultinomial.variance,
                                       doc_str="""

  The variance for each batch member is defined as the following:

  ```
  Var(X_j) = n * alpha_j / alpha_0 * (1 - alpha_j / alpha_0) *
    (n + alpha_0) / (1 + alpha_0)
  ```

  where `alpha_0 = sum_j alpha_j`.
Exemple #23
0
    def _mode(self):
        return array_ops.identity(self._mu)


_prob_note = """
    `x` is a batch vector with compatible shape if `x` is a `Tensor` whose
    shape can be broadcast up to either:

    ````
    self.batch_shape + self.event_shape
    OR
    [M1,...,Mm] + self.batch_shape + self.event_shape
    ```

"""
distribution_util.append_class_fun_doc(_MultivariateNormalOperatorPD.log_prob,
                                       doc_str=_prob_note)
distribution_util.append_class_fun_doc(_MultivariateNormalOperatorPD.prob,
                                       doc_str=_prob_note)


class MultivariateNormalDiag(_MultivariateNormalOperatorPD):
    """The multivariate normal distribution on `R^k`.

  This distribution is defined by a 1-D mean `mu` and a 1-D diagonal
  `diag_stdev`, representing the standard deviations.  This distribution
  assumes the random variables, `(X_1,...,X_k)` are independent, thus no
  non-diagonal terms of the covariance matrix are needed.

  This allows for `O(k)` pdf evaluation, sampling, and storage.

  #### Mathematical details
Exemple #24
0
_prob_note = _prob_base_note + """
The base distribution's `cdf` method must be defined on `y - 1`.  If the
base distribution has a `survival_function` method, results will be more
accurate for large values of `y`, and in this case the `survival_function` must
also be defined on `y - 1`.
"""

_log_prob_note = _prob_base_note + """
The base distribution's `log_cdf` method must be defined on `y - 1`.  If the
base distribution has a `log_survival_function` method results will be more
accurate for large values of `y`, and in this case the `log_survival_function`
must also be defined on `y - 1`.
"""

distribution_util.append_class_fun_doc(QuantizedDistribution.prob,
                                       doc_str=_prob_note)

distribution_util.append_class_fun_doc(QuantizedDistribution.log_prob,
                                       doc_str=_log_prob_note)

_cdf_base_note = """

For whole numbers `y`,

```
cdf(y) := P[Y <= y]
        = 1, if y >= upper_cutoff,
        = 0, if y < lower_cutoff,
        = P[X <= y], otherwise.
```