|
29 | 29 | from pytensor.graph.basic import Apply, Variable
|
30 | 30 | from pytensor.graph.op import Op
|
31 | 31 | from pytensor.raise_op import Assert
|
| 32 | +from pytensor.tensor import gamma as gammafn |
32 | 33 | from pytensor.tensor import gammaln
|
33 | 34 | from pytensor.tensor.extra_ops import broadcast_shape
|
34 | 35 | from pytensor.tensor.math import betaincinv, gammaincinv, tanh
|
@@ -130,6 +131,7 @@ def polyagamma_cdf(*args, **kwargs):
|
130 | 131 | "Moyal",
|
131 | 132 | "AsymmetricLaplace",
|
132 | 133 | "PolyaGamma",
|
| 134 | + "SkewStudentT", |
133 | 135 | ]
|
134 | 136 |
|
135 | 137 |
|
@@ -1908,6 +1910,138 @@ def icdf(value, nu, mu, sigma):
|
1908 | 1910 | )
|
1909 | 1911 |
|
1910 | 1912 |
|
| 1913 | +class SkewStudentTRV(RandomVariable): |
| 1914 | + name = "skewstudentt" |
| 1915 | + ndim_supp = 0 |
| 1916 | + ndims_params = [0, 0, 0, 0] |
| 1917 | + dtype = "floatX" |
| 1918 | + _print_name = ("SkewStudentT", "\\operatorname{SkewStudentT}") |
| 1919 | + |
| 1920 | + @classmethod |
| 1921 | + def rng_fn(cls, rng, a, b, mu, sigma, size=None) -> np.ndarray: |
| 1922 | + return np.asarray( |
| 1923 | + stats.jf_skew_t.rvs(a=a, b=b, loc=mu, scale=sigma, size=size, random_state=rng) |
| 1924 | + ) |
| 1925 | + |
| 1926 | + |
| 1927 | +skewstudentt = SkewStudentTRV() |
| 1928 | + |
| 1929 | + |
| 1930 | +class SkewStudentT(Continuous): |
| 1931 | + r""" |
| 1932 | + Skewed Student's T distribution log-likelihood. |
| 1933 | +
|
| 1934 | + This follows Jones and Faddy (2003) |
| 1935 | +
|
| 1936 | + The pdf of this distribution is |
| 1937 | +
|
| 1938 | + .. math:: |
| 1939 | +
|
| 1940 | + f(t)=f(t ; a, b)=C_{a, b}^{-1}\left\{1+\frac{t}{\left(a+b+t^2\right)^{1 / 2}}\right\}^{a+1 / 2}\left\{1-\frac{t}{\left(a+b+t^2\right)^{1 / 2}}\right\}^{b+1 / 2} |
| 1941 | +
|
| 1942 | + where |
| 1943 | +
|
| 1944 | + .. math:: |
| 1945 | +
|
| 1946 | + C_{a, b}=2^{a+b-1} B(a, b)(a+b)^{1 / 2} |
| 1947 | +
|
| 1948 | +
|
| 1949 | + ======== ============================================================= |
| 1950 | + Support :math:`x \in [\infty, \infty)` |
| 1951 | + Mean :math:`E(T)=\frac{(a-b) \sqrt{(a+b)}}{2} \frac{\Gamma\left(a-\frac{1}{2}\right) \Gamma\left(b-\frac{1}{2}\right)}{\Gamma(a) \Gamma(b)}` |
| 1952 | + ======== ============================================================= |
| 1953 | +
|
| 1954 | + Parameters |
| 1955 | + ---------- |
| 1956 | + a : tensor_like of float |
| 1957 | + First kurtosis parameter (a > 0). |
| 1958 | + b : tensor_like of float |
| 1959 | + Second kurtosis parameter (b > 0). |
| 1960 | + mu : tensor_like of float |
| 1961 | + Location parameter. |
| 1962 | + sigma : tensor_like of float |
| 1963 | + Scale parameter (sigma > 0). Converges to the standard deviation as a and b |
| 1964 | + become close (only required if lam is not specified). Defaults to 1. |
| 1965 | + lam : tensor_like of float, optional |
| 1966 | + Scale parameter (lam > 0). Converges to the precision as a and b |
| 1967 | + become close (only required if sigma is not specified). Defaults to 1. |
| 1968 | +
|
| 1969 | + """ |
| 1970 | + |
| 1971 | + rv_op = skewstudentt |
| 1972 | + |
| 1973 | + @classmethod |
| 1974 | + def dist(cls, a, b, *, mu=0, sigma=None, lam=None, **kwargs): |
| 1975 | + a = pt.as_tensor_variable(a) |
| 1976 | + b = pt.as_tensor_variable(b) |
| 1977 | + lam, sigma = get_tau_sigma(tau=lam, sigma=sigma) |
| 1978 | + sigma = pt.as_tensor_variable(sigma) |
| 1979 | + |
| 1980 | + return super().dist([a, b, mu, sigma], **kwargs) |
| 1981 | + |
| 1982 | + def support_point(rv, size, a, b, mu, sigma): |
| 1983 | + a, b, mu, _ = pt.broadcast_arrays(a, b, mu, sigma) |
| 1984 | + Et = mu + (a - b) * pt.sqrt(a + b) * gammafn(a - 0.5) * gammafn(b - 0.5) / ( |
| 1985 | + 2 * gammafn(a) * gammafn(b) |
| 1986 | + ) |
| 1987 | + if not rv_size_is_none(size): |
| 1988 | + Et = pt.full(size, Et) |
| 1989 | + return Et |
| 1990 | + |
| 1991 | + def logp(value, a, b, mu, sigma): |
| 1992 | + _, sigma = get_tau_sigma(sigma=sigma) |
| 1993 | + |
| 1994 | + x = (value - mu) / sigma |
| 1995 | + |
| 1996 | + a_ = (a + 0.5) * pt.log(1 + x / pt.sqrt(a + b + x**2)) |
| 1997 | + b_ = (b + 0.5) * pt.log(1 - x / pt.sqrt(a + b + x**2)) |
| 1998 | + c = (a + b - 1) * pt.log(2) + pt.special.betaln(a, b) + 0.5 * pt.log(a + b) |
| 1999 | + |
| 2000 | + res = a_ + b_ - c - pt.log(sigma) |
| 2001 | + |
| 2002 | + return check_parameters( |
| 2003 | + res, |
| 2004 | + a > 0, |
| 2005 | + b > 0, |
| 2006 | + sigma > 0, |
| 2007 | + msg="a > 0, b > 0, sigma > 0", |
| 2008 | + ) |
| 2009 | + |
| 2010 | + def logcdf(value, a, b, mu, sigma): |
| 2011 | + _, sigma = get_tau_sigma(sigma=sigma) |
| 2012 | + |
| 2013 | + x = (value - mu) / sigma |
| 2014 | + |
| 2015 | + y = (1 + x / pt.sqrt(a + b + x**2)) * 0.5 |
| 2016 | + res = pt.log(pt.betainc(a, b, y)) |
| 2017 | + |
| 2018 | + return check_parameters( |
| 2019 | + res, |
| 2020 | + a > 0, |
| 2021 | + b > 0, |
| 2022 | + sigma > 0, |
| 2023 | + msg="a > 0, b > 0, sigma > 0", |
| 2024 | + ) |
| 2025 | + |
| 2026 | + def icdf(value, a, b, mu, sigma): |
| 2027 | + _, sigma = get_tau_sigma(sigma=sigma) |
| 2028 | + |
| 2029 | + bval = betaincinv(a, b, value) |
| 2030 | + num = (2 * bval - 1) * pt.sqrt(a + b) |
| 2031 | + denom = 2 * pt.sqrt(bval * (1 - bval)) |
| 2032 | + res = num / denom |
| 2033 | + |
| 2034 | + res = mu + res * sigma |
| 2035 | + res = check_icdf_value(res, value) |
| 2036 | + return check_icdf_parameters( |
| 2037 | + res, |
| 2038 | + a > 0, |
| 2039 | + b > 0, |
| 2040 | + sigma > 0, |
| 2041 | + msg="a > 0, b > 0, sigma > 0", |
| 2042 | + ) |
| 2043 | + |
| 2044 | + |
1911 | 2045 | class Pareto(BoundedContinuous):
|
1912 | 2046 | r"""
|
1913 | 2047 | Pareto log-likelihood.
|
|
0 commit comments