Skip to content

Commit ca62e5b

Browse files
authored
doc(gh-2187): docstrings for Dirichlet and Geometric (#2227)
* doc(gh-2187): documentation for dirichlet * doc(gh-2187): documentation for dirichlet & geometric * doc(gh-2187): documentation for dirichlet & geometric * doc(gh-2187): addressed review comments * doc(gh-2187): fixed linting issue * doc(gh-2187): addressed review comments * doc(gh-2187): addressed review comments * doc(gh-2187): fix typo
1 parent 0b935d4 commit ca62e5b

2 files changed

Lines changed: 235 additions & 0 deletions

File tree

numpyro/distributions/continuous.py

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -456,6 +456,30 @@ def entropy(self) -> ArrayLike:
456456

457457

458458
class Dirichlet(Distribution):
459+
r"""Dirichlet distribution parameterized by concentration (:attr:`concentration`).
460+
461+
The probability density function (PDF) is defined as:
462+
463+
.. math::
464+
f(\mathbf{x}; \boldsymbol{\alpha}) = \frac{\Gamma(\alpha_0)}{\prod_{i=1}^{K}\Gamma(\alpha_i)}
465+
\prod_{i=1}^{K}x_i^{\alpha_i-1},
466+
467+
where :math:`\alpha_0 = \sum_{i=1}^{K}\alpha_i`,
468+
each concentration parameter satisfies :math:`\alpha_i > 0`, and
469+
:math:`\mathbf{x}` lies on the probability simplex in
470+
:math:`\mathbb{R}^{K}`:
471+
472+
.. math::
473+
x_i \geq 0, \qquad \sum_{i=1}^{K}x_i = 1.
474+
475+
:param concentration: Positive concentration parameters. The final
476+
dimension determines the event size (:math:`\alpha`)
477+
:type concentration: ArrayLike
478+
:param validate_args: Whether to validate input constraints, defaults to
479+
``None``.
480+
:type validate_args: bool, optional
481+
"""
482+
459483
arg_constraints = {
460484
"concentration": constraints.independent(constraints.positive, 1)
461485
}
@@ -481,6 +505,15 @@ def __init__(
481505
)
482506

483507
def sample(self, key: jax.Array, sample_shape: tuple[int, ...] = ()) -> ArrayLike:
508+
r"""Generates samples using :func:`~jax.random.dirichlet`.
509+
510+
:param key: JAX PRNGKey for reproducibility.
511+
:type key: jax.Array
512+
:param sample_shape: The shape of the samples to be generated.
513+
:type sample_shape: tuple[int, ...]
514+
:return: Samples from the Dirichlet distribution of shape ``sample_shape + batch_shape + event_shape``.
515+
:rtype: ArrayLike
516+
"""
484517
assert is_prng_key(key)
485518
shape = sample_shape + self.batch_shape
486519
samples = random.dirichlet(key, self.concentration, shape=shape)
@@ -490,6 +523,18 @@ def sample(self, key: jax.Array, sample_shape: tuple[int, ...] = ()) -> ArrayLik
490523

491524
@validate_sample
492525
def log_prob(self, value: ArrayLike) -> ArrayLike:
526+
r"""Calculates the log of the probability density function.
527+
528+
.. math::
529+
\log f(\mathbf{x}; \boldsymbol{\alpha}) = \log\Gamma(\alpha_0)
530+
- \sum_{i=1}^{K}\log\Gamma(\alpha_i)
531+
+ \sum_{i=1}^{K}(\alpha_i - 1)\log x_i.
532+
533+
:param value: Values at which to evaluate the log density.
534+
:type value: ArrayLike
535+
:return: Log probability density.
536+
:rtype: ArrayLike
537+
"""
493538
normalize_term = jnp.sum(gammaln(self.concentration), axis=-1) - gammaln(
494539
jnp.sum(self.concentration, axis=-1)
495540
)
@@ -500,10 +545,25 @@ def log_prob(self, value: ArrayLike) -> ArrayLike:
500545

501546
@property
502547
def mean(self) -> ArrayLike:
548+
r"""Calculates the mean of the Dirichlet distribution per element.
549+
550+
.. math::
551+
\mathbb{E}[X_i] = \frac{\alpha_i}{\alpha_0},
552+
553+
where :math:`\alpha_0 = \sum_{j=1}^K\alpha_j`,
554+
"""
503555
return self.concentration / jnp.sum(self.concentration, axis=-1, keepdims=True)
504556

505557
@property
506558
def variance(self) -> ArrayLike:
559+
r"""Calculates the variance of the Dirichlet distribution.
560+
561+
.. math::
562+
\mathrm{Var}(X_i) = \frac{\alpha_i(\alpha_0-\alpha_i)}
563+
{\alpha_0^2(\alpha_0+1)},
564+
565+
where :math:`\alpha_0 = \sum_{j=1}^K\alpha_j`
566+
"""
507567
con0 = jnp.sum(self.concentration, axis=-1, keepdims=True)
508568
return self.concentration * (con0 - self.concentration) / (con0**2 * (con0 + 1))
509569

@@ -514,6 +574,18 @@ def infer_shapes(concentration):
514574
return batch_shape, event_shape
515575

516576
def entropy(self) -> ArrayLike:
577+
r"""Entropy of the Dirichlet distribution.
578+
579+
.. math::
580+
H(X) = \sum_{i=1}^{K} \ln \Gamma(\alpha_i)
581+
- \ln \Gamma(\alpha_0)
582+
+ (\alpha_0-K)\psi(\alpha_0)
583+
- \sum_{i=1}^{K}(\alpha_i-1)\psi(\alpha_i),
584+
585+
where :math:`\alpha_0 = \sum_{i=1}^{K}\alpha_i`,
586+
:math:`B(\boldsymbol{\alpha})` is the multivariate beta function, and
587+
:math:`\psi` is the digamma function.
588+
"""
517589
(n,) = self.event_shape
518590
total = self.concentration.sum(axis=-1)
519591
return (

numpyro/distributions/discrete.py

Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1814,6 +1814,27 @@ def __init__(
18141814

18151815

18161816
class GeometricProbs(Distribution):
1817+
r"""A Geometric discrete random variable representing the number of failures
1818+
before the first success, parameterized by the success probability
1819+
(:attr:`probs`).
1820+
1821+
The probability mass function (PMF) is defined as:
1822+
1823+
.. math::
1824+
1825+
P(X = k; p) = p(1-p)^k
1826+
1827+
where :math:`p \in (0,1]` is the probability of success on each independent trial.
1828+
:math:`k \in \{0, 1, 2, \ldots\}` is the number of failures before first success.
1829+
Equivalently, the first success occurs on trial :math:`k+1`.
1830+
1831+
:param probs: Probability of success on each trial (:math:`p`).
1832+
:type probs: ArrayLike
1833+
:param validate_args: Whether to validate input constraints, defaults to
1834+
``None``.
1835+
:type validate_args: bool, optional
1836+
"""
1837+
18171838
arg_constraints = {"probs": constraints.unit_interval}
18181839
support = constraints.nonnegative_integer
18191840

@@ -1824,6 +1845,21 @@ def __init__(self, probs: ArrayLike, *, validate_args: Optional[bool] = None):
18241845
)
18251846

18261847
def sample(self, key: jax.Array, sample_shape: tuple[int, ...] = ()) -> ArrayLike:
1848+
r"""Generates samples using inverse CDF method.
1849+
1850+
For a uniform random variable :math:`U \sim \mathrm{Uniform}[0, 1)`,
1851+
a Geometric sample is obtained as:
1852+
1853+
.. math::
1854+
X = \left\lfloor \frac{\log(1-U)}{\log(1-p)} \right\rfloor.
1855+
1856+
:param key: JAX PRNGKey for reproducibility.
1857+
:type key: jax.Array
1858+
:param sample_shape: The shape of the samples to be generated.
1859+
:type sample_shape: tuple[int, ...]
1860+
:return: Samples from Geometric distribution of shape ``sample_shape + batch_shape``.
1861+
:rtype: ArrayLike
1862+
"""
18271863
assert is_prng_key(key)
18281864
probs = self.probs
18291865
dtype = jnp.result_type(probs)
@@ -1833,28 +1869,78 @@ def sample(self, key: jax.Array, sample_shape: tuple[int, ...] = ()) -> ArrayLik
18331869

18341870
@validate_sample
18351871
def log_prob(self, value: ArrayLike) -> ArrayLike:
1872+
r"""Calculates the log of the probability mass function.
1873+
1874+
.. math::
1875+
\log P(X = k; p) = k\log(1-p) + \log p.
1876+
1877+
:param value: Values at which to evaluate the log density. Values must be nonnegative integers.
1878+
:type value: ArrayLike
1879+
:return: Log probability mass.
1880+
:rtype: ArrayLike
1881+
"""
18361882
probs = jnp.where((self.probs == 1) & (value == 0), 0, self.probs)
18371883
return value * jnp.log1p(-probs) + jnp.log(probs)
18381884

18391885
@lazy_property
18401886
def logits(self) -> ArrayLike:
1887+
r"""Calculates the logits corresponding to the success probability.
1888+
1889+
.. math::
1890+
\ell = \log\left(\frac{p}{1-p}\right).
1891+
"""
18411892
return _to_logits_bernoulli(self.probs)
18421893

18431894
@property
18441895
def mean(self) -> ArrayLike:
1896+
r"""Calculates the mean of the Geometric distribution.
1897+
1898+
.. math::
1899+
\mathbb{E}[X] = \frac{1-p}{p}.
1900+
"""
18451901
return 1.0 / self.probs - 1.0
18461902

18471903
@property
18481904
def variance(self) -> ArrayLike:
1905+
r"""Calculates the variance of the Geometric distribution.
1906+
1907+
.. math::
1908+
\operatorname{Var}(X) = \frac{1-p}{p^2}.
1909+
"""
18491910
return (1.0 / self.probs - 1.0) / self.probs
18501911

18511912
def entropy(self) -> ArrayLike:
1913+
r"""Entropy of the Geometric distribution.
1914+
1915+
.. math::
1916+
H(X) = -\log p - \frac{1-p}{p}\log(1-p).
1917+
1918+
:return: Entropy of the Geometric distribution.
1919+
:rtype: ArrayLike
1920+
"""
18521921
return -(1 - self.probs) * jnp.log1p(-self.probs) / self.probs - jnp.log(
18531922
self.probs
18541923
)
18551924

18561925

18571926
class GeometricLogits(Distribution):
1927+
r"""Geometric distribution parameterized by logits (:attr:`logits`).
1928+
1929+
.. math::
1930+
P(X = k \mid \ell) = \sigma(\ell)
1931+
\left(1-\sigma(\ell)\right)^k,
1932+
\qquad k \in \{0, 1, 2, \ldots\}.
1933+
1934+
where :math:`\ell` denote the logits parameter,
1935+
:math:`p = \sigma(\ell) = \displaystyle\frac{1}{1+\exp(-\ell)}` is the probability of success.
1936+
1937+
:param logits: Logits of success on each trial (:math:`logits`).
1938+
:type logits: ArrayLike
1939+
:param validate_args: Whether to validate input constraints, defaults to
1940+
``None``.
1941+
:type validate_args: bool, optional
1942+
"""
1943+
18581944
arg_constraints = {"logits": constraints.real}
18591945
support = constraints.nonnegative_integer
18601946

@@ -1866,9 +1952,25 @@ def __init__(self, logits: ArrayLike, *, validate_args: Optional[bool] = None):
18661952

18671953
@lazy_property
18681954
def probs(self) -> ArrayLike:
1955+
r"""The success probability obtained by applying the sigmoid function
1956+
to the logits.
1957+
1958+
.. math::
1959+
p = \sigma(\ell) = \frac{1}{1+\exp(-\ell)}.
1960+
"""
18691961
return _to_probs_bernoulli(self.logits)
18701962

18711963
def sample(self, key: jax.Array, sample_shape: tuple[int, ...] = ()) -> ArrayLike:
1964+
r"""Generates samples using inverse CDF technique in logit space.
1965+
1966+
:param key: JAX pseudo-random number generator key.
1967+
:type key: jax.Array
1968+
:param sample_shape: Sample dimensions to prepend to the batch shape.
1969+
:type sample_shape: tuple[int, ...]
1970+
:return: Samples from the Geometric distribution of shape
1971+
``sample_shape + batch_shape``.
1972+
:rtype: ArrayLike
1973+
"""
18721974
assert is_prng_key(key)
18731975
logits = self.logits
18741976
dtype = jnp.result_type(logits)
@@ -1878,17 +1980,62 @@ def sample(self, key: jax.Array, sample_shape: tuple[int, ...] = ()) -> ArrayLik
18781980

18791981
@validate_sample
18801982
def log_prob(self, value: ArrayLike) -> ArrayLike:
1983+
r"""Calculates the log probability mass function.
1984+
1985+
.. math::
1986+
\log P(X = k; \ell) = \ell - (k + 1) \operatorname{softplus}(\ell),
1987+
1988+
where, :math:`\operatorname{softplus}` is :func:`~jax.nn.softplus`.
1989+
1990+
:param value: Number of failures before the first success. Values must
1991+
be nonnegative integers.
1992+
:type value: ArrayLike
1993+
:return: Log probability mass.
1994+
:rtype: ArrayLike
1995+
"""
18811996
return (-value - 1) * softplus(self.logits) + self.logits
18821997

18831998
@property
18841999
def mean(self) -> ArrayLike:
2000+
r"""Calculates the mean of the Geometric distribution.
2001+
2002+
.. math::
2003+
E[X] = \frac{1}{p}-1,
2004+
2005+
where :math:`p=\sigma(\ell)`.
2006+
"""
18852007
return 1.0 / self.probs - 1.0
18862008

18872009
@property
18882010
def variance(self) -> ArrayLike:
2011+
r"""Calculates the variance of the Geometric distribution.
2012+
2013+
.. math::
2014+
\operatorname{Var}(X) = \frac{1-p}{p^2},
2015+
2016+
implemented as,
2017+
2018+
.. math::
2019+
\operatorname{Var}(X) = \frac{1/p-1}{p},
2020+
2021+
where :math:`p=\sigma(\ell)`.
2022+
"""
18892023
return (1.0 / self.probs - 1.0) / self.probs
18902024

18912025
def entropy(self) -> ArrayLike:
2026+
r"""Calculates the entropy of the Geometric distribution.
2027+
2028+
.. math::
2029+
H(X) = -\frac{1-p}{p}\ln{q}-\ln{p},
2030+
2031+
where, :math:`\ln{p}=-\operatorname{softplus}(-\ell)`, :math:`\ln{q}=-\operatorname{softplus}(\ell)`,
2032+
and :math:`p=\operatorname{expit}(\ell)`. Implementation uses :func:`~jax.nn.softplus`
2033+
and :func:`~jax.scipy.special.expit` for :math:`\operatorname{softplus}`
2034+
and :math:`\operatorname{expit}`, respectively.
2035+
2036+
:return: Entropy of the Geometric distribution.
2037+
:rtype: ArrayLike
2038+
"""
18922039
logq = -jax.nn.softplus(self.logits)
18932040
logp = -jax.nn.softplus(-self.logits)
18942041
p = jax.scipy.special.expit(self.logits)
@@ -1902,6 +2049,22 @@ def Geometric(
19022049
*,
19032050
validate_args: Optional[bool] = None,
19042051
) -> Union[GeometricProbs, GeometricLogits]:
2052+
r"""Geometric distribution parameterized by either probabilities
2053+
or logits.
2054+
2055+
Exactly one of :attr:`probs` or :attr:`logits` must be specified.
2056+
2057+
:param probs: Probability of success on each independent trial (:math:`p`).
2058+
:type probs: ArrayLike, optional
2059+
:param logits: Logits of success on each independent trial.
2060+
:type logits: ArrayLike, optional
2061+
:param validate_args: Whether to validate input constraints, defaults to
2062+
``None``.
2063+
:type validate_args: bool, optional
2064+
:return: A probability- or logit-parameterized Geometric distribution.
2065+
:rtype: Union[GeometricProbs, GeometricLogits]
2066+
:raises ValueError: If both or neither of :attr:`probs` and :attr:`logits` are specified.
2067+
"""
19052068
assert_one_of(probs=probs, logits=logits)
19062069
if probs is not None:
19072070
return GeometricProbs(probs, validate_args=validate_args)

0 commit comments

Comments
 (0)