예제 #1
0
    def test_input_validation(self):
        match = r"`order` must be either 1, 3, or 5."
        with pytest.raises(ValueError, match=match):
            NumericalInverseHermite(StandardNormal(), order=2)

        match = "`cdf` required but not found"
        with pytest.raises(ValueError, match=match):
            stats.NumericalInverseHermite("norm")

        match = "could not convert string to float"
        with pytest.raises(ValueError, match=match):
            stats.NumericalInverseHermite(StandardNormal(),
                                          u_resolution='ekki')

        match = "`max_intervals' must be..."
        with pytest.raises(ValueError, match=match):
            stats.NumericalInverseHermite(StandardNormal(), max_intervals=-1)

        match = "`qmc_engine` must be an instance of..."
        with pytest.raises(ValueError, match=match):
            fni = stats.NumericalInverseHermite(StandardNormal())
            fni.qrvs(qmc_engine=0)

        if NumpyVersion(np.__version__) >= '1.18.0':
            # issues with QMCEngines and old NumPy
            fni = stats.NumericalInverseHermite(StandardNormal())

            match = "`d` must be consistent with dimension of `qmc_engine`."
            with pytest.raises(ValueError, match=match):
                fni.qrvs(d=3, qmc_engine=stats.qmc.Halton(2))
예제 #2
0
    def test_custom_distribution(self):
        dist1 = StandardNormal()
        fni1 = stats.NumericalInverseHermite(dist1)

        dist2 = stats.norm()
        fni2 = stats.NumericalInverseHermite(dist2)

        assert_allclose(fni1.rvs(random_state=0), fni2.rvs(random_state=0))
예제 #3
0
    def test_inaccurate_CDF(self):
        # CDF function with inaccurate tail cannot be inverted; see gh-13319
        # https://github.com/scipy/scipy/pull/13319#discussion_r626188955
        shapes = (2.3098496451481823, 0.6268795430096368)
        match = ("98 : one or more intervals very short; possibly due to "
                 "numerical problems with a pole or very flat tail")

        # fails with default tol
        with pytest.warns(RuntimeWarning, match=match):
            stats.NumericalInverseHermite(stats.beta(*shapes))

        # no error with coarser tol
        stats.NumericalInverseHermite(stats.beta(*shapes), u_resolution=1e-8)
예제 #4
0
    def test_basic_all_scipy_dists(self, distname, shapes):
        slow_dists = {'ksone', 'kstwo', 'levy_stable', 'skewnorm'}
        fail_dists = {
            'beta', 'gausshyper', 'geninvgauss', 'ncf', 'nct', 'norminvgauss',
            'genhyperbolic', 'studentized_range', 'vonmises', 'kappa4',
            'invgauss', 'wald'
        }

        if distname in slow_dists:
            pytest.skip("Distribution is too slow")
        if distname in fail_dists:
            # specific reasons documented in gh-13319
            # https://github.com/scipy/scipy/pull/13319#discussion_r626188955
            pytest.xfail("Fails - usually due to inaccurate CDF/PDF")

        np.random.seed(0)

        dist = getattr(stats, distname)(*shapes)

        with np.testing.suppress_warnings() as sup:
            sup.filter(RuntimeWarning, "overflow encountered")
            sup.filter(RuntimeWarning, "divide by zero")
            sup.filter(RuntimeWarning, "invalid value encountered")
            fni = stats.NumericalInverseHermite(dist)

        x = np.random.rand(10)
        p_tol = np.max(np.abs(dist.ppf(x) - fni.ppf(x)) / np.abs(dist.ppf(x)))
        u_tol = np.max(np.abs(dist.cdf(fni.ppf(x)) - x))

        assert p_tol < 1e-8
        assert u_tol < 1e-12
예제 #5
0
    def test_QRVS_size_tuple(self):
        # QMCEngine samples are always of shape (n, d). When `size` is a tuple,
        # we set `n = prod(size)` in the call to qmc_engine.random, transform
        # the sample, and reshape it to the final dimensions. When we reshape,
        # we need to be careful, because the _columns_ of the sample returned
        # by a QMCEngine are "independent"-ish, but the elements within the
        # columns are not. We need to make sure that this doesn't get mixed up
        # by reshaping: qrvs[..., i] should remain "independent"-ish of
        # qrvs[..., i+1], but the elements within qrvs[..., i] should be
        # transformed from the same low-discrepancy sequence.
        if NumpyVersion(np.__version__) <= '1.18.0':
            pytest.skip("QMC doesn't play well with old NumPy")

        dist = StandardNormal()
        fni = stats.NumericalInverseHermite(dist)

        size = (3, 4)
        d = 5
        qrng = stats.qmc.Halton(d, seed=0)
        qrng2 = stats.qmc.Halton(d, seed=0)

        uniform = qrng2.random(np.prod(size))

        qrvs = fni.qrvs(size=size, d=d, qmc_engine=qrng)
        qrvs2 = stats.norm.ppf(uniform)

        for i in range(d):
            sample = qrvs[..., i]
            sample2 = qrvs2[:, i].reshape(size)
            assert_allclose(sample, sample2, atol=1e-12)
예제 #6
0
    def test_QRVS(self, qrng, size_in, size_out, d_in, d_out):
        dist = StandardNormal()
        fni = stats.NumericalInverseHermite(dist)

        # If d and qrng.d are inconsistent, an error is raised
        if d_in is not None and qrng is not None and qrng.d != d_in:
            match = "`d` must be consistent with dimension of `qmc_engine`."
            with pytest.raises(ValueError, match=match):
                fni.qrvs(size_in, d=d_in, qmc_engine=qrng)
            return

        # Sometimes d is really determined by qrng
        if d_in is None and qrng is not None and qrng.d != 1:
            d_out = (qrng.d, )

        shape_expected = size_out + d_out

        qrng2 = deepcopy(qrng)
        qrvs = fni.qrvs(size=size_in, d=d_in, qmc_engine=qrng)
        if size_in is not None:
            assert (qrvs.shape == shape_expected)

        if qrng2 is not None:
            uniform = qrng2.random(np.prod(size_in) or 1)
            qrvs2 = stats.norm.ppf(uniform).reshape(shape_expected)
            assert_allclose(qrvs, qrvs2, atol=1e-12)
예제 #7
0
 def setup(self, dist, order):
     self.urng = np.random.default_rng(0xb235b58c1f616c59c18d8568f77d44d1)
     with np.testing.suppress_warnings() as sup:
         sup.filter(RuntimeWarning)
         try:
             self.rng = stats.NumericalInverseHermite(
                 dist, order=order, random_state=self.urng)
         except stats.UNURANError:
             raise NotImplementedError(f"setup failed for {dist}")
예제 #8
0
    def test_RVS(self, rng, size_in, size_out):
        dist = StandardNormal()
        fni = stats.NumericalInverseHermite(dist)

        rng2 = deepcopy(rng)
        rvs = fni.rvs(size=size_in, random_state=rng)
        if size_in is not None:
            assert (rvs.shape == size_out)

        if rng2 is not None:
            rng2 = check_random_state(rng2)
            uniform = rng2.uniform(size=size_in)
            rvs2 = stats.norm.ppf(uniform)
            assert_allclose(rvs, rvs2)
예제 #9
0
    def time_fni(self, distcase):
        distname, shapes = distcase
        slow_dists = {'ksone', 'kstwo', 'levy_stable', 'skewnorm'}
        fail_dists = {'beta', 'gausshyper', 'geninvgauss', 'ncf', 'nct',
                      'norminvgauss', 'genhyperbolic', 'studentized_range'}

        if distname in slow_dists or distname in fail_dists:
            raise NotImplementedError("skipped")

        dist = getattr(stats, distname)(*shapes)

        with np.testing.suppress_warnings() as sup:
            sup.filter(RuntimeWarning, "overflow encountered")
            sup.filter(RuntimeWarning, "divide by zero")
            sup.filter(RuntimeWarning, "invalid value encountered")
            stats.NumericalInverseHermite(dist)
예제 #10
0
 def time_hinv_setup(self, dist, order):
     with np.testing.suppress_warnings() as sup:
         sup.filter(RuntimeWarning)
         stats.NumericalInverseHermite(dist,
                                       order=order,
                                       random_state=self.urng)