Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add support for density compensation estimation with cufinufft #195

Draft
wants to merge 58 commits into
base: master
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from 22 commits
Commits
Show all changes
58 commits
Select commit Hold shift + click to select a range
8ca012c
Added
Apr 26, 2024
3a980c2
Merge branch 'mind-inria:master' into master
chaithyagr Apr 26, 2024
093edfb
Fix
Apr 26, 2024
f8a6a4a
Merge branch 'master' of github.com:chaithyagr/mri-nufft
Apr 26, 2024
74c1ecd
Remove bymistake add
Apr 26, 2024
0250aa8
Fix
Apr 26, 2024
060a8bd
Fixed lint
Apr 26, 2024
aecb844
Lint
Apr 26, 2024
3130bc1
Added refbackend
Apr 26, 2024
bc014b8
Fix NDFT
Apr 26, 2024
0cc73c4
feat: use finufft as ref backend.
paquiteau Apr 29, 2024
21e090f
feat(tests): move ndft vs nufft tests to own file.
paquiteau Apr 29, 2024
6869a4a
Merge branch 'master' of github.com:mind-inria/mri-nufft
Apr 29, 2024
f8364d4
Merge branch 'master' of github.com:mind-inria/mri-nufft
Apr 30, 2024
23a63da
Merge branch 'master' of github.com:mind-inria/mri-nufft
Jun 17, 2024
3709e74
Merge branch 'master' of github.com:mind-inria/mri-nufft
Jul 1, 2024
8d6b9b4
Merge branch 'master' of github.com:chaithyagr/mri-nufft
chaithyagr Aug 1, 2024
398fb28
Merge branch 'master' of github.com:mind-inria/mri-nufft
chaithyagr Sep 5, 2024
bcf7ce3
git Merge branch 'master' of github.com:mind-inria/mri-nufft
chaithyagr Sep 19, 2024
3da762f
Add support for pipe
chaithyagr Sep 20, 2024
d644cf6
\!docs_build try to run cufinufft tests
chaithyagr Sep 20, 2024
0dca8f6
\!docs_build fix style
chaithyagr Sep 20, 2024
643e1e9
Added next235 for stability
chaithyagr Sep 23, 2024
af6bbfa
Fix lint
chaithyagr Sep 23, 2024
02c834f
Fix CUPY
chaithyagr Sep 23, 2024
d50f427
Merge branch 'master' into cufinufft
chaithyagr Oct 24, 2024
8cfd427
WIP
chaithyagr Oct 24, 2024
ab6eaa4
merge
chaithyagr Oct 24, 2024
3c3f1c8
Updates
chaithyagr Oct 24, 2024
bb28eb9
fix back learn examples
chaithyagr Oct 25, 2024
cdf75af
move tto flatiron
chaithyagr Oct 25, 2024
986fb96
fix black
chaithyagr Oct 25, 2024
d4edc58
Move to test on GPU
chaithyagr Nov 8, 2024
1d01484
Update pyproject toml and use it in test-ci, to prevent duplication o…
chaithyagr Nov 13, 2024
9714ca9
Make CI build shorter
chaithyagr Nov 13, 2024
aba2c8f
Merge branch 'master' into cufinufft
chaithyagr Nov 13, 2024
78c60f9
Test run to run
chaithyagr Nov 13, 2024
d5fc2f6
Merge branch 'cufinufft' of github.com:chaithyagr/mri-nufft into cufi…
chaithyagr Nov 13, 2024
d844816
\!docs_build Added
chaithyagr Nov 13, 2024
2d58e02
Merge branch 'master' into cufinufft
chaithyagr Nov 14, 2024
7a6f4a0
adding density normalization
Dec 5, 2024
ddebaf5
Merge branch 'master' into cufinufft
chaithyagr Dec 6, 2024
cc08826
Merge branch 'master' into cufinufft
chaithyagr Dec 11, 2024
a94d530
Add support for 2.3.1
chaithyagr Dec 11, 2024
e5e7d10
Merge branch 'master' into cufinufft
chaithyagr Dec 20, 2024
67d6c75
Multiple fixes
chaithyagr Dec 20, 2024
906f363
Prevent circular import
chaithyagr Dec 20, 2024
9917045
fix Lint
chaithyagr Dec 20, 2024
119ee92
Fix
chaithyagr Dec 20, 2024
ba53be9
Some fixes and updates
chaithyagr Dec 20, 2024
3ef45a6
Update test-ci.yml
chaithyagr Jan 2, 2025
7989caa
Update test-ci.yml
chaithyagr Jan 2, 2025
f29a020
Updates
chaithyagr Jan 2, 2025
57b1ffa
Merge branch 'cufinufft' of github.com:chaithyagr/mri-nufft into cufi…
chaithyagr Jan 2, 2025
477349e
Make more fixes
chaithyagr Jan 2, 2025
5f21f6e
Careful repro test
chaithyagr Jan 2, 2025
134f70a
Fixes
chaithyagr Jan 6, 2025
35ca5b8
Fixes
chaithyagr Jan 6, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 9 additions & 4 deletions .github/workflows/test-ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,8 @@ jobs:
elif [[ ${{ matrix.backend }} == "tensorflow" ]]; then
pip install tensorflow-mri==0.21.0 tensorflow-probability==0.17.0 tensorflow-io==0.27.0 matplotlib==3.7
elif [[ ${{ matrix.backend }} == "cufinufft" ]]; then
pip install "cufinufft<2.3"
git clone https://github.com/chaithyagr/finufft --branch fix_spreadinterponly
pip install finufft/python/cufinufft
else
pip install ${{ matrix.backend }}
fi
Expand Down Expand Up @@ -215,7 +216,9 @@ jobs:
export PATH=/usr/local/cuda-12.1/bin/:${PATH}
export LD_LIBRARY_PATH=/usr/local/cuda-12.1/lib64/:${LD_LIBRARY_PATH}
pip install cupy-cuda12x torch
python -m pip install gpuNUFFT "cufinufft<2.3" sigpy scikit-image
python -m pip install gpuNUFFT sigpy scikit-image
git clone https://github.com/chaithyagr/finufft --branch fix_spreadinterponly
pip install finufft/python/cufinufft

- name: Run examples
shell: bash
Expand Down Expand Up @@ -326,8 +329,10 @@ jobs:
export PATH=/usr/local/cuda-12.1/bin/:${PATH}
export LD_LIBRARY_PATH=/usr/local/cuda-12.1/lib64/:${LD_LIBRARY_PATH}
pip install cupy-cuda12x torch
python -m pip install gpuNUFFT "cufinufft<2.3"

python -m pip install gpuNUFFT
git clone https://github.com/chaithyagr/finufft --branch fix_spreadinterponly
pip install finufft/python/cufinufft

- name: Build API documentation
run: |
python -m sphinx docs docs_build
Expand Down
20 changes: 19 additions & 1 deletion examples/GPU/example_density.py
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Since there is two backend, you could plot the different density compensation vectors to show the differences (as cufinufft and gpunufft does not use the same interpolation kernel)

Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@
# If this method is widely used in the literature, there exists no convergence guarantees for it.
#
# .. note::
# The Pipe method is currently only implemented for gpuNUFFT.
# The Pipe method is currently only implemented for gpuNUFFT and cufinufft backend.

# %%
flat_traj = traj.reshape(-1, 2)
Expand All @@ -158,3 +158,21 @@
axs[2].imshow(abs(adjoint_manual))
axs[2].set_title("Pipe density compensation")
print(nufft.density)

# %%
# We can also do density compensation using cufinufft backend

# %%
flat_traj = traj.reshape(-1, 2)
nufft = get_operator("cufinufft")(
traj, shape=mri_2D.shape, density={"name": "pipe", "osf": 2}
)
adjoint_manual = nufft.adj_op(kspace)
fig, axs = plt.subplots(1, 3, figsize=(15, 5))
axs[0].imshow(abs(mri_2D))
axs[0].set_title("Ground Truth")
axs[1].imshow(abs(adjoint))
axs[1].set_title("no density compensation")
axs[2].imshow(abs(adjoint_manual))
axs[2].set_title("Pipe density compensation")
print(nufft.density)
2 changes: 1 addition & 1 deletion examples/GPU/example_learn_samples_multicoil.py
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ def __init__(self, inital_trajectory, n_coils, img_size=(256, 256)):
squeeze_dims=False,
)
# A simple density compensated adjoint SENSE operator with sensitivity maps `smaps`.
self.sense_op = get_operator("gpunufft", wrt_data=True, wrt_traj=True)(
self.sense_op = get_operator("cufinufft", wrt_data=True, wrt_traj=True)(
sample_points,
shape=img_size,
density=True,
Expand Down
4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,8 +13,8 @@ dynamic = ["version"]

gpunufft = ["gpuNUFFT>=0.9.0", "cupy-cuda12x"]
torchkbnufft = ["torchkbnufft", "cupy-cuda12x"]
cufinufft = ["cufinufft<2.3", "cupy-cuda12x"]
finufft = ["finufft"]
cufinufft = ["cufinufft>=2.3", "cupy-cuda12x"]
finufft = ["finufft>=2.3"]
pynfft = ["pynfft2>=1.4.3", "numpy>=2.0.0"]
pynufft = ["pynufft"]
io = ["pymapvbvd"]
Expand Down
53 changes: 48 additions & 5 deletions src/mrinufft/operators/interfaces/cufinufft.py
Original file line number Diff line number Diff line change
Expand Up @@ -40,11 +40,6 @@
DTYPE_R2C = {"float32": "complex64", "float64": "complex128"}


def _error_check(ier, msg):
if ier != 0:
raise RuntimeError(msg)


class RawCufinufftPlan:
"""Light wrapper around the guru interface of finufft."""

Expand Down Expand Up @@ -836,3 +831,51 @@ def toggle_grad_traj(self):
if self.uses_sense:
self.smaps = self.smaps.conj()
self.raw_op.toggle_grad_traj()

@classmethod
def pipe(
cls,
kspace_loc,
volume_shape,
num_iterations=10,
osf=2,
normalize=True,
**kwargs,
):
"""Compute the density compensation weights for a given set of kspace locations.

Parameters
----------
kspace_loc: np.ndarray
the kspace locations
volume_shape: np.ndarray
the volume shape
num_iterations: int default 10
the number of iterations for density estimation
osf: float or int
The oversampling factor the volume shape
normalize: bool
Whether to normalize the density compensation.
We normalize such that the energy of PSF = 1
"""
if CUFINUFFT_AVAILABLE is False:
raise ValueError(
"gpuNUFFT is not available, cannot " "estimate the density compensation"
)
volume_shape = np.array([int(osf * i) for i in volume_shape])
grid_op = MRICufiNUFFT(
samples=kspace_loc,
shape=volume_shape,
upsampfac=1,
gpu_spreadinterponly=1,
gpu_kerevalmeth=0,
**kwargs,
)
density_comp = cp.ones(kspace_loc.shape[0], dtype=grid_op.cpx_dtype)
for _ in range(num_iterations):
density_comp /= cp.abs(
grid_op.op(
grid_op.adj_op(density_comp.astype(grid_op.cpx_dtype))
).squeeze()
)
return density_comp.squeeze()
29 changes: 18 additions & 11 deletions tests/operators/test_density_for_op.py
Original file line number Diff line number Diff line change
Expand Up @@ -25,21 +25,28 @@ def radial_distance(traj, shape):
CasesTrajectories.case_nyquist_radial3D,
],
)
@parametrize(backend=["gpunufft", "tensorflow"])
@parametrize(backend=["gpunufft", "tensorflow", "cufinufft"])
def test_pipe(backend, traj, shape, osf):
"""Test the pipe method."""
distance = radial_distance(traj, shape)
if osf != 2 and backend == "tensorflow":
pytest.skip("OSF < 2 not supported for tensorflow.")
result = pipe(traj, shape, backend, osf=osf, num_iterations=10)
result = pipe(traj, shape, backend=backend, osf=osf, num_iterations=10)
if backend == "cufinufft":
result = result.get()
result = result / np.mean(result)
distance = distance / np.mean(distance)
if backend == "tensorflow":
# If tensorflow, we dont perfectly estimate, but we still want to ensure
# we can get density
assert_correlate(result, distance, slope=1, slope_err=None, r_value_err=0.5)
elif 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)
r_err = 0.2
slope_err = None
if osf == 2:
r_err = 0.1
slope_err = 0.1
if backend == "cufinufft":
r_err *= 2
slope_err = slope_err * 2 if slope_err is not None else None
if len(shape) == 3:
r_err *= 2
slope_err = slope_err * 2 if slope_err is not None else None
elif backend == "tensorflow":
r_err = 0.5
assert_correlate(result, distance, slope=1, slope_err=slope_err, r_value_err=r_err)
Loading