Skip to content

Commit

Permalink
Fix CartesianSamplingOp (#483)
Browse files Browse the repository at this point in the history
  • Loading branch information
ckolbPTB authored Nov 9, 2024
1 parent dcb5e34 commit c268ad2
Show file tree
Hide file tree
Showing 2 changed files with 21 additions and 6 deletions.
10 changes: 6 additions & 4 deletions src/mrpro/operators/CartesianSamplingOp.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,26 +47,28 @@ def __init__(self, encoding_matrix: SpatialDimension[int], traj: KTrajectory) ->
kx_idx = ktraj_tensor[-1, ...].round().to(dtype=torch.int64) + sorted_grid_shape.x // 2
else:
sorted_grid_shape.x = ktraj_tensor.shape[-1]
kx_idx = repeat(torch.arange(ktraj_tensor.shape[-1]), 'k0->other k1 k2 k0', other=1, k2=1, k1=1)
kx_idx = repeat(torch.arange(ktraj_tensor.shape[-1]), 'k0->other k2 k1 k0', other=1, k2=1, k1=1)

if traj_type_kzyx[-2] == TrajType.ONGRID: # ky
ky_idx = ktraj_tensor[-2, ...].round().to(dtype=torch.int64) + sorted_grid_shape.y // 2
else:
sorted_grid_shape.y = ktraj_tensor.shape[-2]
ky_idx = repeat(torch.arange(ktraj_tensor.shape[-2]), 'k1->other k1 k2 k0', other=1, k2=1, k0=1)
ky_idx = repeat(torch.arange(ktraj_tensor.shape[-2]), 'k1->other k2 k1 k0', other=1, k2=1, k0=1)

if traj_type_kzyx[-3] == TrajType.ONGRID: # kz
kz_idx = ktraj_tensor[-3, ...].round().to(dtype=torch.int64) + sorted_grid_shape.z // 2
else:
sorted_grid_shape.z = ktraj_tensor.shape[-3]
kz_idx = repeat(torch.arange(ktraj_tensor.shape[-3]), 'k2->other k1 k2 k0', other=1, k1=1, k0=1)
kz_idx = repeat(torch.arange(ktraj_tensor.shape[-3]), 'k2->other k2 k1 k0', other=1, k1=1, k0=1)

# 1D indices into a flattened tensor.
kidx = kz_idx * sorted_grid_shape.y * sorted_grid_shape.x + ky_idx * sorted_grid_shape.x + kx_idx
kidx = rearrange(kidx, '... kz ky kx -> ... 1 (kz ky kx)')
self.register_buffer('_fft_idx', kidx)
# we can skip the indexing if the data is already sorted
self._needs_indexing = not torch.all(torch.diff(kidx) == 1)
self._needs_indexing = (
not torch.all(torch.diff(kidx) == 1) or traj.broadcasted_shape[-3:] != sorted_grid_shape.zyx
)

self._trajectory_shape = traj.broadcasted_shape
self._sorted_grid_shape = sorted_grid_shape
Expand Down
17 changes: 15 additions & 2 deletions tests/operators/test_cartesian_sampling_op.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import pytest
import torch
from einops import rearrange
from mrpro.data import KTrajectory, SpatialDimension
from mrpro.operators import CartesianSamplingOp

Expand Down Expand Up @@ -59,6 +60,9 @@ def test_cart_sampling_op_data_match():
'regular_undersampling',
'random_undersampling',
'different_random_undersampling',
'cartesian_and_non_cartesian',
'kx_ky_along_k0',
'kx_ky_along_k0_undersampling',
],
)
def test_cart_sampling_op_fwd_adj(sampling):
Expand All @@ -70,8 +74,8 @@ def test_cart_sampling_op_fwd_adj(sampling):
nky = (2, 1, 40, 1)
nkz = (2, 20, 1, 1)
sx = 'uf'
sy = 'uf'
sz = 'uf'
sy = 'nuf' if sampling == 'cartesian_and_non_cartesian' else 'uf'
sz = 'nuf' if sampling == 'cartesian_and_non_cartesian' else 'uf'
trajectory_tensor = create_traj(k_shape, nkx, nky, nkz, sx, sy, sz).as_tensor()

# Subsample data and trajectory
Expand All @@ -94,6 +98,15 @@ def test_cart_sampling_op_fwd_adj(sampling):
for traj_one_other in trajectory_tensor.unbind(1)
]
trajectory = KTrajectory.from_tensor(torch.stack(traj_list, dim=1))
case 'cartesian_and_non_cartesian':
trajectory = KTrajectory.from_tensor(trajectory_tensor)
case 'kx_ky_along_k0':
trajectory_tensor = rearrange(trajectory_tensor, '... k1 k0->... 1 (k1 k0)')
trajectory = KTrajectory.from_tensor(trajectory_tensor)
case 'kx_ky_along_k0_undersampling':
trajectory_tensor = rearrange(trajectory_tensor, '... k1 k0->... 1 (k1 k0)')
random_idx = torch.randperm(trajectory_tensor.shape[-1])
trajectory = KTrajectory.from_tensor(trajectory_tensor[..., random_idx[: trajectory_tensor.shape[-1] // 2]])
case _:
raise NotImplementedError(f'Test {sampling} not implemented.')

Expand Down

3 comments on commit c268ad2

@github-actions
Copy link
Contributor

Choose a reason for hiding this comment

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

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.py128298%174, 214
   CsmData.py29390%15, 82–84
   DcfData.py45882%18, 66, 78–83
   IData.py67987%119, 125, 129, 159–167
   IHeader.py75791%75, 109, 127–131
   KHeader.py1651790%25, 127–131, 158, 210, 221, 228–229, 232, 239, 278–289
   KNoise.py311552%39–52, 56–61
   KTrajectory.py69593%178–182
   MoveDataMixin.py1271886%15, 110, 126, 140–142, 203, 265–267, 280, 359, 379–380, 382, 397–398, 400
   QData.py39782%42, 65–73
   Rotation.py6743695%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, 1608, 1690, 1846, 1881, 1885, 1996
   SpatialDimension.py2462391%34, 104, 119, 126, 132, 167, 177, 294–296, 309–311, 345, 363, 376, 389, 402, 415, 424–425, 440, 449
   TrajectoryDescription.py14193%23
   acq_filters.py10190%47
src/mrpro/data/_kdata
   KData.py1051685%108–109, 118, 126, 180–181, 216, 221–222, 241–252
   KDataRemoveOsMixin.py29293%44, 46
   KDataSelectMixin.py21290%48, 64
   KDataSplitMixin.py49394%51, 81, 90
src/mrpro/data/traj_calculators
   KTrajectoryCalculator.py25292%23, 45
   KTrajectoryIsmrmrd.py13285%41, 50
   KTrajectoryPulseq.py29197%54
src/mrpro/operators
   CartesianSamplingOp.py50492%49–50, 90, 116
   ConstraintsOp.py60297%46, 48
   EndomorphOperator.py65297%228, 234
   FiniteDifferenceOp.py27293%40, 105
   FourierOp.py77199%131
   Functional.py71593%20–22, 117, 119
   GridSamplingOp.py136993%72–73, 82–83, 90–91, 94, 96, 98
   LinearOperator.py1711293%55, 91, 190, 220, 261, 270, 278, 287, 295, 320, 418, 423
   LinearOperatorMatrix.py1581690%82, 119, 152, 161, 166, 175–178, 191–194, 203, 215, 304, 331, 359
   MultiIdentityOp.py13285%43, 48
   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
   modify_acq_info.py17194%12
   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
TOTAL473435593% 

Tests Skipped Failures Errors Time
1952 0 💤 0 ❌ 0 🔥 1m 28s ⏱️

@github-actions
Copy link
Contributor

Choose a reason for hiding this comment

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

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.py128298%174, 214
   CsmData.py29390%15, 82–84
   DcfData.py45882%18, 66, 78–83
   IData.py67987%119, 125, 129, 159–167
   IHeader.py75791%75, 109, 127–131
   KHeader.py1651790%25, 127–131, 158, 210, 221, 228–229, 232, 239, 278–289
   KNoise.py311552%39–52, 56–61
   KTrajectory.py69593%178–182
   MoveDataMixin.py1271886%15, 110, 126, 140–142, 203, 265–267, 280, 359, 379–380, 382, 397–398, 400
   QData.py39782%42, 65–73
   Rotation.py6743695%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, 1608, 1690, 1846, 1881, 1885, 1996
   SpatialDimension.py2462391%34, 104, 119, 126, 132, 167, 177, 294–296, 309–311, 345, 363, 376, 389, 402, 415, 424–425, 440, 449
   TrajectoryDescription.py14193%23
   acq_filters.py10190%47
src/mrpro/data/_kdata
   KData.py1051685%108–109, 118, 126, 180–181, 216, 221–222, 241–252
   KDataRemoveOsMixin.py29293%44, 46
   KDataSelectMixin.py21290%48, 64
   KDataSplitMixin.py49394%51, 81, 90
src/mrpro/data/traj_calculators
   KTrajectoryCalculator.py25292%23, 45
   KTrajectoryIsmrmrd.py13285%41, 50
   KTrajectoryPulseq.py29197%54
src/mrpro/operators
   CartesianSamplingOp.py50492%49–50, 90, 116
   ConstraintsOp.py60297%46, 48
   EndomorphOperator.py65297%228, 234
   FiniteDifferenceOp.py27293%40, 105
   FourierOp.py77199%131
   Functional.py71593%20–22, 117, 119
   GridSamplingOp.py136993%72–73, 82–83, 90–91, 94, 96, 98
   LinearOperator.py1711293%55, 91, 190, 220, 261, 270, 278, 287, 295, 320, 418, 423
   LinearOperatorMatrix.py1581690%82, 119, 152, 161, 166, 175–178, 191–194, 203, 215, 304, 331, 359
   MultiIdentityOp.py13285%43, 48
   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
   modify_acq_info.py17194%12
   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
TOTAL473435593% 

Tests Skipped Failures Errors Time
1952 0 💤 0 ❌ 0 🔥 1m 29s ⏱️

@github-actions
Copy link
Contributor

Choose a reason for hiding this comment

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

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.py128298%174, 214
   CsmData.py29390%15, 82–84
   DcfData.py45882%18, 66, 78–83
   IData.py67987%119, 125, 129, 159–167
   IHeader.py75791%75, 109, 127–131
   KHeader.py1651790%25, 127–131, 158, 210, 221, 228–229, 232, 239, 278–289
   KNoise.py311552%39–52, 56–61
   KTrajectory.py69593%178–182
   MoveDataMixin.py1271886%15, 110, 126, 140–142, 203, 265–267, 280, 359, 379–380, 382, 397–398, 400
   QData.py39782%42, 65–73
   Rotation.py6743695%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, 1608, 1690, 1846, 1881, 1885, 1996
   SpatialDimension.py2462391%34, 104, 119, 126, 132, 167, 177, 294–296, 309–311, 345, 363, 376, 389, 402, 415, 424–425, 440, 449
   TrajectoryDescription.py14193%23
   acq_filters.py10190%47
src/mrpro/data/_kdata
   KData.py1051685%108–109, 118, 126, 180–181, 216, 221–222, 241–252
   KDataRemoveOsMixin.py29293%44, 46
   KDataSelectMixin.py21290%48, 64
   KDataSplitMixin.py49394%51, 81, 90
src/mrpro/data/traj_calculators
   KTrajectoryCalculator.py25292%23, 45
   KTrajectoryIsmrmrd.py13285%41, 50
   KTrajectoryPulseq.py29197%54
src/mrpro/operators
   CartesianSamplingOp.py50492%49–50, 90, 116
   ConstraintsOp.py60297%46, 48
   EndomorphOperator.py65297%228, 234
   FiniteDifferenceOp.py27293%40, 105
   FourierOp.py77199%131
   Functional.py71593%20–22, 117, 119
   GridSamplingOp.py136993%72–73, 82–83, 90–91, 94, 96, 98
   LinearOperator.py1711293%55, 91, 190, 220, 261, 270, 278, 287, 295, 320, 418, 423
   LinearOperatorMatrix.py1581690%82, 119, 152, 161, 166, 175–178, 191–194, 203, 215, 304, 331, 359
   MultiIdentityOp.py13285%43, 48
   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
   modify_acq_info.py17194%12
   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
TOTAL473435593% 

Tests Skipped Failures Errors Time
1952 0 💤 0 ❌ 0 🔥 1m 38s ⏱️

Please sign in to comment.