Repository: scipy/scipy
File: scipy/stats/tests/test_continuous.py (Python)

Code before:
                                    Y.moment(order), rtol=rtol, atol=atol)
            for order in range(3, 4):
                if distname not in skip_standardized:
                    assert_allclose(X.moment(order, kind='standardized'),
                                    Y.stats('mvsk'[order-1]), rtol=rtol, atol=atol)
            seed = 845298245687345
            assert_allclose(X.sample(shape=10, rng=seed),
                            Y.rvs(size=10, random_state=np.random.default_rng(seed)),
                            rtol=rtol)

    def test_custom(self):
        rng = np.random.default_rng(7548723590230982)

        class MyLogUniform:
            @property
            def __make_distribution_version__(self):
                return "1.16.0"

            @property
            def parameters(self):
                return {'a': {'endpoints': (0, np.inf), 'inclusive': (False, False)},
                        'b': {'endpoints': ('a', np.inf), 'inclusive': (False, False)}}

            @property
            def support(self):
                return {'endpoints': ('a', 'b'), 'inclusive': (True, True)}

            def pdf(self, x, a, b):
                return 1 / (x * (np.log(b) - np.log(a)))

        LogUniform = stats.make_distribution(MyLogUniform())

        X = LogUniform(a=np.exp(1), b=np.exp(3))
        Y = stats.exp(Uniform(a=1., b=3.))
        x = X.sample(shape=10, rng=rng)
        p = X.cdf(x)

        assert_allclose(X.support(), Y.support())
        assert_allclose(X.entropy(), Y.entropy())
        assert_allclose(X.median(), Y.median())
        assert_allclose(X.logpdf(x), Y.logpdf(x))
        assert_allclose(X.pdf(x), Y.pdf(x))
        assert_allclose(X.logcdf(x), Y.logcdf(x))
        assert_allclose(X.cdf(x), Y.cdf(x))
        assert_allclose(X.logccdf(x), Y.logccdf(x))
        assert_allclose(X.ccdf(x), Y.ccdf(x))
        assert_allclose(X.icdf(p), Y.icdf(p))
        assert_allclose(X.iccdf(p), Y.iccdf(p))
        for kind in ['raw', 'central', 'standardized']:
            for order in range(5):
                assert_allclose(X.moment(order, kind=kind),

Review comment (suggestion):
Let's not commit this here; no need to run CI again.
```suggestion
                return {'endpoints': ('a', 'b')}
```
But we could do this to test the default.

I see that this dictionary makes the definition of simple `support` a bit more cumbersome, as you thought. I think that's ok for now. I do think it is good for the complicated cases to follow whatever we did for the parameters, and it's natural for that to be a dictionary. We could always allow the simpler special case (endpoint tuple rather than dictionary) later.

Code after:
                                    Y.moment(order), rtol=rtol, atol=atol)
            for order in range(3, 4):
                if distname not in skip_standardized:
                    assert_allclose(X.moment(order, kind='standardized'),
                                    Y.stats('mvsk'[order-1]), rtol=rtol, atol=atol)
            seed = 845298245687345
            assert_allclose(X.sample(shape=10, rng=seed),
                            Y.rvs(size=10, random_state=np.random.default_rng(seed)),
                            rtol=rtol)

    def test_custom(self):
        rng = np.random.default_rng(7548723590230982)

        class MyLogUniform:
            @property
            def __make_distribution_version__(self):
                return "1.16.0"

            @property
            def parameters(self):
                return {'a': {'endpoints': (0, np.inf), 'inclusive': (False, False)},
                        'b': {'endpoints': ('a', np.inf), 'inclusive': (False, False)}}

            @property
            def support(self):
                return {'endpoints': ('a', 'b')}

            def pdf(self, x, a, b):
                return 1 / (x * (np.log(b) - np.log(a)))

        LogUniform = stats.make_distribution(MyLogUniform())

        X = LogUniform(a=np.exp(1), b=np.exp(3))
        Y = stats.exp(Uniform(a=1., b=3.))
        x = X.sample(shape=10, rng=rng)
        p = X.cdf(x)

        assert_allclose(X.support(), Y.support())
        assert_allclose(X.entropy(), Y.entropy())
        assert_allclose(X.median(), Y.median())
        assert_allclose(X.logpdf(x), Y.logpdf(x))
        assert_allclose(X.pdf(x), Y.pdf(x))
        assert_allclose(X.logcdf(x), Y.logcdf(x))
        assert_allclose(X.cdf(x), Y.cdf(x))
        assert_allclose(X.logccdf(x), Y.logccdf(x))
        assert_allclose(X.ccdf(x), Y.ccdf(x))
        assert_allclose(X.icdf(p), Y.icdf(p))
        assert_allclose(X.iccdf(p), Y.iccdf(p))
        for kind in ['raw', 'central', 'standardized']:
            for order in range(5):
                assert_allclose(X.moment(order, kind=kind),