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

Take NonUniformFastFourierOp out of FourierOp #463

Open
wants to merge 42 commits into
base: main
Choose a base branch
from

Conversation

ckolbPTB
Copy link
Collaborator

@ckolbPTB ckolbPTB commented Oct 22, 2024

  • NonUniformFastFourierOp separate to FourierOp
  • nufft_typ1/2-functions created in init and used in forward/adjoint (easy to switch to finufft)
  • reshaping of trajectory done in init
  • data is reshaped to (dim with separate trajectory)(dim with same trajectory)(nufft dims) (possible speed up)
  • multiple other dimensions are possible in forward/adjoint
  • kzyx and k210 do not have to aligned but if they are not FFTOp and NUFFTOp cannot be used together in FourierOp

Still to do:

  • make sure operator can be moved between cpu and gpu

Copy link
Contributor

github-actions bot commented Oct 22, 2024

Coverage

Coverage Report
FileStmtsMissCoverMissing
src/mrpro/algorithms/csm
   inati.py24196%44
   walsh.py16194%34
src/mrpro/algorithms/dcf
   dcf_voronoi.py53492%15, 48–49, 76
src/mrpro/algorithms/optimizers
   adam.py20195%69
src/mrpro/algorithms/reconstruction
   DirectReconstruction.py281643%51–71, 85
   IterativeSENSEReconstruction.py13192%76
   Reconstruction.py502256%42, 54–56, 80–87, 104–113
   RegularizedIterativeSENSEReconstruction.py411759%96–100, 114–139
src/mrpro/data
   AcqInfo.py128398%26, 169, 207
   CsmData.py29390%15, 82–84
   DcfData.py45882%18, 66, 78–83
   IData.py67987%119, 125, 129, 159–167
   IHeader.py75791%75, 109, 127–131
   KData.py2142588%111–112, 127, 134, 144, 152, 206–207, 245, 250–251, 270–281, 435, 437, 500, 515, 552, 583, 592
   KHeader.py1531789%25, 119–123, 150, 199, 210, 217–218, 221, 228, 260–271
   KNoise.py311552%39–52, 56–61
   KTrajectory.py811285%108–113, 116–118, 203–207
   MoveDataMixin.py1401887%15, 113, 129, 143–145, 207, 323–325, 338, 417, 437–438, 440, 455–456, 458
   QData.py39782%42, 65–73
   Rotation.py6743595%100, 198, 335, 433, 477, 495, 581, 583, 592, 626, 628, 691, 768, 773, 776, 791, 808, 813, 889, 1077, 1082, 1085, 1109, 1113, 1240, 1242, 1250–1251, 1315, 1397, 1690, 1846, 1881, 1885, 1996
   SpatialDimension.py2322191%34, 104, 141, 148, 154, 274–276, 289–291, 325, 343, 356, 369, 382, 395, 404–405, 420, 429
   acq_filters.py12192%47
src/mrpro/data/traj_calculators
   KTrajectoryCalculator.py25292%23, 45
   KTrajectoryIsmrmrd.py13285%41, 50
   KTrajectoryPulseq.py23196%55
src/mrpro/operators
   CartesianSamplingOp.py89397%118, 157, 280
   ConstraintsOp.py60297%46, 48
   EndomorphOperator.py65297%228, 234
   FiniteDifferenceOp.py27293%40, 105
   FourierOp.py73297%230, 235
   Functional.py71593%20–22, 117, 119
   GridSamplingOp.py136993%72–73, 82–83, 90–91, 94, 96, 98
   LinearOperator.py1681094%55, 91, 190, 220, 261, 270, 278, 287, 295, 320
   LinearOperatorMatrix.py1581690%82, 119, 152, 161, 166, 175–178, 191–194, 203, 215, 304, 331, 359
   MultiIdentityOp.py13285%43, 48
   NonUniformFastFourierOp.py169498%89, 384, 407, 412
   Operator.py78297%25, 74
   ProximableFunctionalSeparableSum.py39392%50, 103, 110
   SliceProjectionOp.py173895%44, 61, 63, 69, 206, 227, 260, 300
   WaveletOp.py120596%152, 170, 205, 210, 233
   ZeroPadOp.py16194%30
src/mrpro/utils
   filters.py62297%44, 49
   reshape.py60198%191
   slice_profiles.py46687%20, 36, 113–116, 149
   sliding_window.py34197%34
   split_idx.py10280%43, 47
   summarize_tensorvalues.py11918%20–29
   typing.py181139%8–23
   zero_pad_or_crop.py31681%26, 30, 54, 57, 60, 63
TOTAL497636393% 

Tests Skipped Failures Errors Time
2315 0 💤 0 ❌ 0 🔥 1m 17s ⏱️

Copy link
Contributor

github-actions bot commented Oct 22, 2024

📚 Documentation

📁 Download as zip
🔍 View online

@fzimmermann89
Copy link
Member

Mid-term, I would still really like to decouple us from torchkbnufft and have a NufftOperator option in the FourierOperator,
where you can either specify give a TorchKBNufft class or a Finufft class which all implement a common interface.

Also, long-term, I would like to fix the axes sorting code, such that for example a RPE with fft direction along k1 and kz works, i.e. a mismatch in the order of the axes.

@fzimmermann89
Copy link
Member

fzimmermann89 commented Oct 22, 2024

Not sure about if we should pass on traj as KTrajectory

If not to difficult, yes. Or at least allow both, KTrajectory and tensor inputs.

and if it should be the original one or already matching recon_ and encoding_matrix.

In case cartesian fft removes stuff? Does it matter?
Also, does our current code even work in that case?
Shouldnt for that to work the order of NUFFT and FFT be swapped?

@ckolbPTB
Copy link
Collaborator Author

In case cartesian fft removes stuff? Does it matter?
Also, does our current code even work in that case?
Shouldnt for that to work the order of NUFFT and FFT be swapped?

FFT and NUFFT always act on separate dimensions so it does not matter if FFT changes stuff. Only case where it matters would be if the non-uniform trajectory changes along a uniform dimension. But the NUFFT acts along the original k-space uniform dimension so this should be fine.

@fzimmermann89
Copy link
Member

For this or another PR:
Now that we have both operators separated, I think it would be good to have a test that tests nufft with cartesian coordinates vs an FFT.

@ckolbPTB
Copy link
Collaborator Author

@fzimmermann89 I added a test to verify against the analytic solution. Is this what you had in mind?

Copy link
Member

@fzimmermann89 fzimmermann89 left a comment

Choose a reason for hiding this comment

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

Testing with analytical solution is even better!

Final remarks:

I would remove .forward from the operator calls.
It should generally be call that is called on pytorch modules (which all our operator are) as you can in general set global debugging hooks that only work in that case.
I think we should not give a bad example here.

We could either set fast_fourier_op and nufft op always, just with empty dims, or set them to identity op (which we didn't have when we wrote this)
I am wondering why mypy is not complaining about them possibly being None (or I missed something).

"""
super().__init__()

def get_traj(traj: KTrajectory, dims: Sequence[int]):
Copy link
Member

Choose a reason for hiding this comment

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

now that the NufftOp can be created separately, there are some cases not handled:
What if dim is empty?
What if dim contains invalid axes?
What if dim contains positive axes?

Copy link
Member

Choose a reason for hiding this comment

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

I would say dim is empty, the operator should do nothing.
We should add that dim talks about kx ky kz and not k0 k1 k2 here.
So maybe not dim bit direction?

Copy link
Member

Choose a reason for hiding this comment

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

also, what about fastfourierop with empty dim?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I would say dim is empty, the operator should do nothing.

added

also, what about fastfourierop with empty dim?

should be fixed in separate PR

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

What if dim contains invalid axes?
What if dim contains positive axes?

We could transform them to negative axes based on the dimensions of traj and then verify the result is within (-3,-2,-1)?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I guess we have a similar problem with your FFTOp where we also assume that kz ky kx and k2 k1 k0 align, i.e. if I set dim = -1 then I expect to carry out the FFT along k0 with the parameters of (k)x.

We could either say that FFTOp and NUFFTOp work in k2 k1 k0 space and we have to provide matrix sizes and trajectory and so on in this space. Then we could do the magic of figuring all this out in the FourierOp. This would leave the FFTOp to be more intuitive but would mean that for the NUFFTOp users would have to figure out the traj by themselves.

Other option would be to provide dim_kzkykx and dim_k2k1k0 to both operators which would make FFTOp less intuitive but mean that NUFFTOp could get the original trajectory as input.

Copy link
Member

@fzimmermann89 fzimmermann89 Oct 25, 2024

Choose a reason for hiding this comment

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

I see basically the same two options here :

  • nuffOp gets data and omega in shape (batch_different_traj, batch_same_traj, samples), all reshaping is done in FourierOp.

  • NufftOp to gets 'x','y','z' as a direction argument. We transform it any ways always to x,y,z where we use it.
    This would mean a minimal change to the code. Nufft does not need dim_k2k1k0 at all.
    (Midterm, it would be nice to support in FourierOp also RPE with read-out in z direction, but saved in k0. For this, FFTOp would need both dim and direction. But as I said, this is IMHO a separate piece of work for the future)

Not really sure which I prefer, I think a bit the second for know, as it is easier to implement and switch to the first one if it required later on without losing work. Am I missing substantial downsides of the second option?

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

I think Nufft and Fft need k2 k1 k0 because we need to know which dimensions of kdata to combine to samples and which to use as batch. currently we assume this is the same as kz ky kx and hence do the rearrange of kdata in forward/adjoint using nufft_dims which is in kz ky kx space although we are actually rearranging k2 k1 k0 dimensions.

I would prefer it if FftOp and NufftOp used a similar interface. This I think would be much easier to understand by users.

Copy link
Member

Choose a reason for hiding this comment

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

Ok, so option two would be to provide direction Literal['x', 'y', 'z',-3,-2,-1] and dim Literal[-3,-2,-1,'k0,'k1',k2'] to nufft.
FFTOp would still only get dim:int. Maye at some point we can add direction here as well to support the mixed case.

Option one is still all reshaping in FourierOp

Copy link
Collaborator Author

Choose a reason for hiding this comment

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

A bit more work on this topic:
Now nufft_directions:Sequence[Literal['x', 'y', 'z',-3,-2,-1]] is provided by the user to define along which directions the nufft should be performed. Based on this ky, kx and kz are selected for Nufft. In a second step it is checked which k0, k1, k2 dimensions ky, kx and kz define (nufft_dims). These are then automatically selected.

In the forward and adjoint this means that the image is permuted/reshaped based on nufft_directions, the k-space trajectory and k-space data based on nufft_dims.

This means users don't have to worry about k2-k1-k0-space but can define everything in x-y-z/kx-ky-kz-space.

It also means that

support in FourierOp also RPE with read-out in z direction, but saved in k0

also works now.

The interface is also now more similar to FFT where we either allow SpatialDimension or Sequence[int] for the recon and encoding matrix.

@ckolbPTB ckolbPTB mentioned this pull request Nov 26, 2024
@fzimmermann89 fzimmermann89 mentioned this pull request Dec 8, 2024
@ckolbPTB ckolbPTB mentioned this pull request Dec 10, 2024
23 tasks
@fzimmermann89
Copy link
Member

fzimmermann89 commented Jan 6, 2025

is the misalignment k210 and kzyx still a problem ?

regarding review, let me know if i should have another look at this.
(i will not get to reviewing this today, maybe for next meeting, sorry.)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants