Repository: scipy/scipy
File: scipy/signal/tests/test_waveforms.py (Python)

Code before:
        )
        assert waveform.dtype == xp.result_type(t_dtype, width_dtype)

    def test_periodic(self, xp):
        t = xp.linspace(0, 2*xp.pi, 100, dtype=xp.float64)
        y1 = sawtooth(t, width=0.4)
        y2 = sawtooth(t + 2*xp.pi, width=0.4)
        xp_assert_close(y1, y2)

    def test_known_values(self, xp):
        t = xp.asarray([0, xp.pi, 2*xp.pi])
        y = sawtooth(t)
        xp_assert_close(y, xp.asarray([-1., 0., -1.]))

    def test_invalid_width_nan(self, xp):
        t = xp.linspace(0, 2*xp.pi, 10)
        y = sawtooth(t, width=1.5)
        assert xp.all(xp.isnan(y))

    def test_triangle_symmetry(self, xp):
        t = xp.linspace(0, 2*xp.pi, 1000)
        y = sawtooth(t, width=0.5)
        xp_assert_close(y, xp.flip(y))


@make_xp_test_case(square)
class TestSquareWaveform:
    def test_unique(self, xp):
        t = xp.linspace(0, 2*np.pi, 1000)
        y = square(t)
        assert y.shape == t.shape
        unique = np.unique(_xp_copy_to_numpy(y))
        assert set(unique).issubset({-1.0, 1.0})

    @pytest.mark.xfail_xp_backends("cupy", reason="cupy/cupy/issues/9541")
    @pytest.mark.parametrize("t_dtype", ["float32", "float64"])
    @pytest.mark.parametrize("duty_dtype", ["float32", "float64"])
    def test_dtype(self, t_dtype, duty_dtype, xp):
        t_dtype = getattr(xp, t_dtype)
        duty_dtype = getattr(xp, duty_dtype)
        waveform = square(xp.asarray(1, dtype=t_dtype),
                          duty=xp.asarray(0.5, dtype=duty_dtype))
        assert waveform.dtype == xp.result_type(t_dtype, duty_dtype)

    @pytest.mark.xfail_xp_backends("cupy", reason="cupy/cupy/issues/9541")
    @pytest.mark.parametrize("dtype", [None, "float32", "float64"])
    @pytest.mark.parametrize("duty", [0.1, 0.25, 0.5, 0.75])
    def test_duty_cycle_fraction(self, dtype, duty, xp):
        dtype = dtype if dtype is None else getattr(xp, dtype)  # duty is Python float
        # imperfect arithmetic w/ JAX `float32` causes trouble with duty=0.5
        # t = xp.linspace(0, 2*xp.pi, 10000, endpoint=False, dtype=dtype)

Review comment (suggestion):
Maybe also:

```suggestion
        xp_assert_close(y, xp.flip(y))
        xp_assert_close(signal.sawtooth(-t, 0)[1:-1], signal.sawtooth(t)[1:-1])
```
Or it could be a separate test. Or omitted : )

Code after:
            xp.asarray(1, dtype=t_dtype), width=xp.asarray(1, dtype=width_dtype)
        )
        assert waveform.dtype == xp.result_type(t_dtype, width_dtype)

    def test_periodic(self, xp):
        t = xp.linspace(0, 2*xp.pi, 100, dtype=xp.float64)
        y1 = sawtooth(t, width=0.4)
        y2 = sawtooth(t + 2*xp.pi, width=0.4)
        xp_assert_close(y1, y2)

    def test_known_values(self, xp):
        eps = xp.finfo(xp_default_dtype(xp)).eps
        t = xp.asarray([0, xp.pi, 2*xp.pi - 10*eps, 2*xp.pi])
        y = sawtooth(t)
        xp_assert_close(y, xp.asarray([-1., 0., 1., -1.]))

    def test_invalid_width_nan(self, xp):
        t = xp.linspace(0, 2*xp.pi, 10)
        y = sawtooth(t, width=1.5)
        assert xp.all(xp.isnan(y))

    @pytest.mark.xfail_xp_backends("cupy", reason="cupy/cupy/issues/9568")
    def test_triangle_symmetry(self, xp):
        t = xp.linspace(0, 2*xp.pi, 1000)
        y = sawtooth(t, width=0.5)
        xp_assert_close(y, xp.flip(y))
        xp_assert_close(sawtooth(-t, 0.)[1:-1], sawtooth(t)[1:-1])


@make_xp_test_case(square)
class TestSquareWaveform:
    def test_unique(self, xp):
        t = xp.linspace(0, 2*np.pi, 1000)
        y = square(t)
        assert y.shape == t.shape
        unique = np.unique(_xp_copy_to_numpy(y))
        assert set(unique).issubset({-1.0, 1.0})

    @pytest.mark.xfail_xp_backends("cupy", reason="cupy/cupy/issues/9541")
    @pytest.mark.parametrize("t_dtype", ["float32", "float64"])
    @pytest.mark.parametrize("duty_dtype", ["float32", "float64"])
    def test_dtype(self, t_dtype, duty_dtype, xp):
        t_dtype = getattr(xp, t_dtype)
        duty_dtype = getattr(xp, duty_dtype)
        waveform = square(xp.asarray(1, dtype=t_dtype),
                          duty=xp.asarray(0.5, dtype=duty_dtype))
        assert waveform.dtype == xp.result_type(t_dtype, duty_dtype)

    @pytest.mark.xfail_xp_backends("cupy", reason="cupy/cupy/issues/9541")
    @pytest.mark.parametrize("dtype", [None, "float32", "float64"])
    @pytest.mark.parametrize("duty", [0.1, 0.25, 0.5, 0.75])