Repository: scipy/scipy
File: scipy/integrate/tests/test_quadrature.py (Python)

Code before:
        with pytest.raises(ValueError, match="At least one point is required"):
            cumulative_trapezoid(y=xp.asarray([]))


class CommonTrapezoidSimpsonTests:
    def test_simple(self, xp):
        x = xp.arange(-10, 10, .1)
        r = self.quadrature_func(xp.exp(-.5 * x ** 2) / xp.sqrt(2 * xp.asarray(xp.pi)),
                                 dx=0.1)
        # check integral of normal equals 1
        xp_assert_close(r, xp.asarray(1.0))

    def test_ndim(self, xp):
        x = xp.linspace(0, 1, 3)
        y = xp.linspace(0, 2, 8)
        z = xp.linspace(0, 3, 13)

        wx = xp.ones_like(x) * (x[1] - x[0])
        wx = xpx.at(wx)[0].divide(2)
        wx = xpx.at(wx)[-1].divide(2)
        wy = xp.ones_like(y) * (y[1] - y[0])
        wy = xpx.at(wy)[0].divide(2)
        wy = xpx.at(wy)[-1].divide(2)
        wz = xp.ones_like(z) * (z[1] - z[0])
        wz = xpx.at(wz)[0].divide(2)
        wz = xpx.at(wz)[-1].divide(2)

        q = x[:, None, None] + y[None,:, None] + z[None, None,:]

        qx = xp.sum(q * wx[:, None, None], axis=0)
        qy = xp.sum(q * wy[None, :, None], axis=1)
        qz = xp.sum(q * wz[None, None, :], axis=2)

        # n-d `x`
        r = self.quadrature_func(q, x=x[:, None, None], axis=0)
        xp_assert_close(r, qx)
        r = self.quadrature_func(q, x=y[None,:, None], axis=1)
        xp_assert_close(r, qy)
        r = self.quadrature_func(q, x=z[None, None,:], axis=2)
        xp_assert_close(r, qz)

        # 1-d `x`
        r = self.quadrature_func(q, x=x, axis=0)
        xp_assert_close(r, qx)
        r = self.quadrature_func(q, x=y, axis=1)
        xp_assert_close(r, qy)
        r = self.quadrature_func(q, x=z, axis=2)
        xp_assert_close(r, qz)

@make_xp_test_case(simpson)
class TestSimpson2(CommonTrapezoidSimpsonTests):

Review comment (suggestion):
The original PR copy-pasted these tests of `trapezoid` for use with `simpson`. This reoganizes the classes so no copy-pasting is needed. It also avoids `parametrize`ing over the functions, which is not ideal when using `make_xp_test_case`.

Code after:
        y_expected = xp.asarray([0, -1.5, -2., -1.5, 0.])
        xp_assert_close(y_int, y_expected)

        y_int = cumulative_trapezoid(y, dx=3)
        y_expected = xp.asarray([-4.5, -6., -4.5, 0.])
        xp_assert_close(y_int, y_expected)

        y_int = cumulative_trapezoid(y, dx=3, initial=0)
        y_expected = xp.asarray([0, -4.5, -6., -4.5, 0.])
        xp_assert_close(y_int, y_expected)

    @pytest.mark.parametrize(
        "initial", [1, 0.5]
    )
    def test_initial_error(self, initial, xp):
        """If initial is not None or 0, a ValueError is raised."""
        y = xp.linspace(0, 10, num=10)
        with pytest.raises(ValueError, match="`initial`"):
            cumulative_trapezoid(y, initial=initial)

    def test_zero_len_y(self, xp):
        with pytest.raises(ValueError, match="At least one point is required"):
            cumulative_trapezoid(y=xp.asarray([]))


class CommonTrapezoidSimpsonTests:
    def test_simple(self, xp):
        x = xp.arange(-10, 10, .1)
        r = self.quadrature_func(xp.exp(-.5 * x ** 2) / xp.sqrt(2 * xp.asarray(xp.pi)),
                                 dx=0.1)
        # check integral of normal equals 1
        xp_assert_close(r, xp.asarray(1.0))

    def test_ndim(self, xp):
        x = xp.linspace(0, 1, 3)
        y = xp.linspace(0, 2, 8)
        z = xp.linspace(0, 3, 13)

        wx = xp.ones_like(x) * (x[1] - x[0])
        wx = xpx.at(wx)[0].divide(2)
        wx = xpx.at(wx)[-1].divide(2)
        wy = xp.ones_like(y) * (y[1] - y[0])
        wy = xpx.at(wy)[0].divide(2)
        wy = xpx.at(wy)[-1].divide(2)
        wz = xp.ones_like(z) * (z[1] - z[0])
        wz = xpx.at(wz)[0].divide(2)
        wz = xpx.at(wz)[-1].divide(2)

        q = x[:, None, None] + y[None,:, None] + z[None, None,:]

        qx = xp.sum(q * wx[:, None, None], axis=0)