diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index a91458da..ade1112a 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -19,8 +19,10 @@ jobs: test: runs-on: ubuntu-latest + continue-on-error: true strategy: matrix: + # We test NumPy dev on 3.11 python-version: ['3.8', '3.9', '3.10', '3.11', '3.12'] requires: ['requirements.txt'] include: @@ -37,9 +39,13 @@ jobs: allow-prereleases: true - name: Install run: | + set -eo pipefail python -m pip install --upgrade pip python -m pip install -r ${{ matrix.requires }} python -m pip install -r requirements-dev.txt + if [[ "${{ matrix.python-version }}" == "3.11" ]]; then + python -m pip install --only-binary numpy --pre --extra-index-url https://pypi.anaconda.org/scientific-python-nightly-wheels/simple "numpy>=2.1.0.dev0" + fi python -m pip install . - name: Lint run: | diff --git a/nitime/algorithms/event_related.py b/nitime/algorithms/event_related.py index acf7d34b..e0141a39 100644 --- a/nitime/algorithms/event_related.py +++ b/nitime/algorithms/event_related.py @@ -10,7 +10,7 @@ def fir(timeseries, design): - """ + r""" Calculate the FIR (finite impulse response) HRF, according to [Burock2000]_ Parameters diff --git a/nitime/algorithms/tests/test_spectral.py b/nitime/algorithms/tests/test_spectral.py index 6c00a581..20d95df5 100644 --- a/nitime/algorithms/tests/test_spectral.py +++ b/nitime/algorithms/tests/test_spectral.py @@ -246,8 +246,7 @@ def test_mtm_lin_combo(): mtm_cross = tsa.mtm_cross_spectrum( spec1, spec2, (weights[0], weights[1]), sides=sides ) - npt.assert_(mtm_cross.dtype in np.sctypes['complex'], - 'Wrong dtype for crossspectrum') + assert mtm_cross.dtype == np.complex128, 'Wrong dtype for crossspectrum' npt.assert_(len(mtm_cross) == 51, 'Wrong length for halfband spectrum') sides = 'twosided' @@ -260,8 +259,7 @@ def test_mtm_lin_combo(): mtm_auto = tsa.mtm_cross_spectrum( spec1, spec1, weights[0], sides=sides ) - npt.assert_(mtm_auto.dtype in np.sctypes['float'], - 'Wrong dtype for autospectrum') + assert mtm_auto.dtype == np.float64, 'Wrong dtype for autospectrum' npt.assert_(len(mtm_auto) == 51, 'Wrong length for halfband spectrum') sides = 'twosided' diff --git a/nitime/index_utils.py b/nitime/index_utils.py index be5de816..e845c60d 100644 --- a/nitime/index_utils.py +++ b/nitime/index_utils.py @@ -6,11 +6,11 @@ 'tril_indices_from', 'triu_indices', 'triu_indices_from', ] -from numpy.core.numeric import asanyarray, subtract, arange, \ +from numpy import asanyarray, subtract, arange, \ greater_equal, multiply, ones, asarray, where -# Need to import numpy for the doctests! -import numpy as np +# Need to import numpy for the doctests! +import numpy as np def tri(N, M=None, k=0, dtype=float): """ diff --git a/nitime/tests/test_timeseries.py b/nitime/tests/test_timeseries.py index 7aaeac93..edf05ea1 100644 --- a/nitime/tests/test_timeseries.py +++ b/nitime/tests/test_timeseries.py @@ -916,16 +916,20 @@ def test_index_int64(): assert repr(b[0]) == repr(b[np.int32(0)]) -def test_timearray_math_functions(): +@pytest.mark.parametrize('f', ['min', 'max', 'mean', 'ptp', 'sum']) +@pytest.mark.parametrize('tu', ['s', 'ms', 'ps', 'D']) +def test_timearray_math_functions(f, tu): "Calling TimeArray.min() .max(), mean() should return TimeArrays" a = np.arange(2, 11) - for f in ['min', 'max', 'mean', 'ptp', 'sum']: - for tu in ['s', 'ms', 'ps', 'D']: - b = ts.TimeArray(a, time_unit=tu) - npt.assert_(getattr(b, f)().__class__ == ts.TimeArray) - npt.assert_(getattr(b, f)().time_unit == b.time_unit) - # comparison with unitless should convert to the TimeArray's units - npt.assert_(getattr(b, f)() == getattr(a, f)()) + b = ts.TimeArray(a, time_unit=tu) + if f == "ptp" and ts._NP_2: + want = np.ptp(a) + else: + want = getattr(a, f)() + npt.assert_(getattr(b, f)().__class__ == ts.TimeArray) + npt.assert_(getattr(b, f)().time_unit == b.time_unit) + # comparison with unitless should convert to the TimeArray's units + npt.assert_(getattr(b, f)() == want) def test_timearray_var_prod(): diff --git a/nitime/tests/test_utils.py b/nitime/tests/test_utils.py index 7770227f..d31c7491 100644 --- a/nitime/tests/test_utils.py +++ b/nitime/tests/test_utils.py @@ -230,6 +230,8 @@ def test_detect_lines(): """ Tests detect_lines utility in the reliable low-SNR scenario. """ + np.random.seed(0) + N = 1000 fft_pow = int( np.ceil(np.log2(N) + 2) ) NW = 4 @@ -286,19 +288,21 @@ def test_detect_lines_2dmode(): Test multi-sequence operation """ + # This seed affects not just the signal we generate below, but then also + # detect_lines->dpss_windows->tridi_inverse_iteration + np.random.seed(0) + N = 1000 sig = np.cos( 2*np.pi*np.arange(N) * 20./N ) + np.random.randn(N) * .01 - sig2d = np.row_stack( (sig, sig, sig) ) + sig2d = np.vstack( (sig, sig, sig) ) lines = utils.detect_lines(sig2d, (4, 8), low_bias=True, NFFT=2**12) npt.assert_(len(lines)==3, 'Detect lines failed multi-sequence mode') - consistent1 = (lines[0][0] == lines[1][0]).all() and \ - (lines[1][0] == lines[2][0]).all() - consistent2 = (lines[0][1] == lines[1][1]).all() and \ - (lines[1][1] == lines[2][1]).all() - - npt.assert_(consistent1 and consistent2, 'Inconsistent results') + npt.assert_allclose(lines[0][0], lines[1][0]) + npt.assert_allclose(lines[0][0], lines[2][0]) + npt.assert_allclose(lines[0][1], lines[1][1]) + npt.assert_allclose(lines[0][1], lines[2][1]) diff --git a/nitime/timeseries.py b/nitime/timeseries.py index 4fd44a92..c13ced22 100644 --- a/nitime/timeseries.py +++ b/nitime/timeseries.py @@ -33,6 +33,11 @@ # Our own from nitime import descriptors as desc +try: + _NP_2 = int(np.__version__.split(".")[0]) >= 2 +except Exception: + _NP_2 = True + #----------------------------------------------------------------------------- # Module globals #----------------------------------------------------------------------------- @@ -112,7 +117,9 @@ def __new__(cls, data, time_unit=None, copy=True): which are SI units of time. Default: 's' copy : bool, optional - Whether to create this instance by copy of a + Whether to create this instance by copy of a. If False, + a copy will not be forced but might still be required depending + on the data array. Note ---- @@ -152,7 +159,7 @@ class instance, or an int64 array in the base unit of the module e_s += 'TimeArray in object, or int64 times, in %s' % base_unit raise ValueError(e_s) - time = np.array(data, copy=False) + time = np.asarray(data) else: if isinstance(data, TimeInterface): time = data.copy() @@ -309,7 +316,11 @@ def mean(self, *args, **kwargs): return ret def ptp(self, *args, **kwargs): - ret = TimeArray(np.ndarray.ptp(self, *args, **kwargs), + if _NP_2: + ptp = np.ptp + else: + ptp = np.ndarray.ptp + ret = TimeArray(ptp(self, *args, **kwargs), time_unit=base_unit) ret.convert_unit(self.time_unit) return ret diff --git a/pyproject.toml b/pyproject.toml index 2b605570..0ba485b9 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -62,6 +62,12 @@ skip = "pp* cp38-*_aarch64 cp38-musllinux_*" # don't bother unless someone asks archs = ["native"] +test-requires = [ + "pytest", + "nitime[full]", # Enable all optional behavior +] +test-command = "pytest -rsx --pyargs nitime" + [tool.cibuildwheel.linux] archs = ["x86_64", "aarch64"]