diff --git a/.github/workflows/test-ci.yml b/.github/workflows/test-ci.yml index 42e6df2f..e5882e6a 100644 --- a/.github/workflows/test-ci.yml +++ b/.github/workflows/test-ci.yml @@ -81,7 +81,7 @@ jobs: shell: bash run: | export COVERAGE_FILE=coverage_${{ matrix.backend }} - pytest --backend ${{ matrix.backend }} --ref ${{ env.ref_backend }} --cov --disable-pytest-warnings --cov-branch --cov-report=term + pytest -k='operators' --backend ${{ matrix.backend }} --ref ${{ env.ref_backend }} --cov --disable-pytest-warnings --cov-branch --cov-report=term - name: Upload coverage uses: actions/upload-artifact@v3 with: @@ -111,7 +111,9 @@ jobs: pip install torch --index-url https://download.pytorch.org/whl/cu118 pip install finufft + - name: Install backend + if: ${{ matrix.backend == 'gpunufft' || matrix.backend == 'cufinufft' }} shell: bash run: | source $RUNNER_WORKSPACE/venv/bin/activate @@ -120,13 +122,20 @@ jobs: export LD_LIBRARY_PATH=/usr/local/cuda-11.8/lib/{LD_LIBRARY_PATH:+:${LD_LIBRARY_PATH}} pip install ${{ matrix.backend }} + - name: Install pynfft + if: ${{ matrix.backend == 'pynfft' }} + shell: bash + run: | + source $RUNNER_WORKSPACE/venv/bin/activate + pip install pynfft2 + - name: Run Tests shell: bash run: | cd $RUNNER_WORKSPACE/mri-nufft source $RUNNER_WORKSPACE/venv/bin/activate export COVERAGE_FILE=coverage_${{ matrix.backend }} - python -m pytest --ref ${{ env.ref_backend }} --backend ${{ matrix.backend }} --disable-pytest-warnings --cov --cov-branch --cov-report=term + python -m pytest -k='operators' --ref ${{ env.ref_backend }} --backend ${{ matrix.backend }} --disable-pytest-warnings --cov --cov-branch --cov-report=term - name: Upload coverage if: success() @@ -168,7 +177,7 @@ jobs: shell: bash run: | export COVERAGE_FILE=coverage_plots - pytest examples --cov --cov-branch --cov-report=term + pytest examples tests -k="not operators" --cov --cov-branch --cov-report=term - name: Upload coverage if: success() diff --git a/tests/test_batch.py b/tests/operators/test_batch.py similarity index 100% rename from tests/test_batch.py rename to tests/operators/test_batch.py diff --git a/tests/test_bindings.py b/tests/operators/test_bindings.py similarity index 100% rename from tests/test_bindings.py rename to tests/operators/test_bindings.py diff --git a/tests/operators/test_gpunufft.py b/tests/operators/test_gpunufft.py new file mode 100644 index 00000000..4ccfdcb8 --- /dev/null +++ b/tests/operators/test_gpunufft.py @@ -0,0 +1,40 @@ +"""Specific test for gpunufft.""" + +import numpy as np +import numpy.testing as npt +from pytest_cases import parametrize, parametrize_with_cases + +from case_trajectories import CasesTrajectories +from helpers import assert_correlate +from mrinufft.density import cell_count, voronoi, pipe +from mrinufft.density.utils import normalize_weights +from mrinufft._utils import proper_trajectory + + +def radial_distance(traj, shape): + """Compute the radial distance of a trajectory.""" + proper_traj = proper_trajectory(traj, normalize="unit") + weights = np.linalg.norm(proper_traj, axis=-1) + return weights + + +@parametrize("osf", [1, 1.25, 2]) +@parametrize_with_cases( + "traj, shape", + cases=[ + CasesTrajectories.case_nyquist_radial2D, + CasesTrajectories.case_nyquist_radial3D, + ], +) +@parametrize(backend=["gpunufft"]) +def test_pipe(backend, traj, shape, osf): + """Test the pipe method.""" + distance = radial_distance(traj, shape) + result = pipe(traj, shape, osf=osf, num_iterations=10) + result = result / np.mean(result) + distance = distance / np.mean(distance) + if osf != 2: + # If OSF < 2, we dont perfectly estimate + assert_correlate(result, distance, slope=1, slope_err=None, r_value_err=0.2) + else: + assert_correlate(result, distance, slope=1, slope_err=0.1, r_value_err=0.1) diff --git a/tests/test_interfaces.py b/tests/operators/test_interfaces.py similarity index 96% rename from tests/test_interfaces.py rename to tests/operators/test_interfaces.py index 3fa10bfc..c8789b63 100644 --- a/tests/test_interfaces.py +++ b/tests/operators/test_interfaces.py @@ -1,6 +1,7 @@ """Test the interfaces module.""" import numpy as np +import pytest from pytest_cases import parametrize_with_cases, parametrize, fixture from mrinufft import get_operator from case_trajectories import CasesTrajectories @@ -43,6 +44,8 @@ def operator( n_coils=1, ): """Generate an operator.""" + if backend in ["pynfft", "sigpy"] and kspace_locs.shape[-1] == 3: + pytest.skip("3D for slow cpu is not tested") return get_operator(backend)(kspace_locs, shape, n_coils=n_coils, smaps=None) diff --git a/tests/test_stacked.py b/tests/operators/test_stacked.py similarity index 100% rename from tests/test_stacked.py rename to tests/operators/test_stacked.py diff --git a/tests/test_stacked_gpu.py b/tests/operators/test_stacked_gpu.py similarity index 100% rename from tests/test_stacked_gpu.py rename to tests/operators/test_stacked_gpu.py diff --git a/tests/test_density.py b/tests/test_density.py index 2cba4c9b..35281859 100644 --- a/tests/test_density.py +++ b/tests/test_density.py @@ -61,25 +61,3 @@ def test_voronoi(traj, shape): result = result / np.mean(result) distance = distance / np.mean(distance) assert_correlate(result, distance, slope=1) - - -@parametrize("osf", [1, 1.25, 2]) -@parametrize_with_cases( - "traj, shape", - cases=[ - CasesTrajectories.case_nyquist_radial2D, - CasesTrajectories.case_nyquist_radial3D, - ], -) -@parametrize(backend=["gpunufft"]) -def test_pipe(backend, traj, shape, osf): - """Test the pipe method.""" - distance = radial_distance(traj, shape) - result = pipe(traj, shape, osf=osf, num_iterations=10) - result = result / np.mean(result) - distance = distance / np.mean(distance) - if osf != 2: - # If OSF < 2, we dont perfectly estimate - assert_correlate(result, distance, slope=1, slope_err=None, r_value_err=0.2) - else: - assert_correlate(result, distance, slope=1, slope_err=0.1, r_value_err=0.1)