REF: Refactor PET model#204
Conversation
2fb555e to
90eb886
Compare
|
Had another look at this. I had introduced a |
| ) | ||
|
|
||
| # ToDo | ||
| # Are timepoints and xlim features that all PET models require ?? |
There was a problem hiding this comment.
I would say so, since we are dealing with motion correction, and hence would need temporal information. Currently, the PET model needs both the sampling midpoints and the scan’s total duration to work, so enforcing their presence in the base class keeps the API consistent. When new PET models that do not need temporal information are introduced, we can revisit this requirement; until then, providing dataset.midframe and dataset.total_duration (as done in the unit tests) is the intended usage.
There was a problem hiding this comment.
OK, I see that this is related to #204 (review). I think the naming should then be made consistent across the PET data class and the model class. Also, I do not see why the model is given the timepoints, if these are hold by the PET data class, i.e. from #204 (review)
(...) midframe is where the dataset stores real-world frame timing; timepoints/_x is the copy of those timings handed to the model
then we should not be providing the model with a copy of them.
If we make the parallel with the DWI class, the gradients are obtained from the dataset, e.g.:
gradient = self._dataset.gradients[:, index]
Also, can the xlim name be made somehow more descriptive or is it a name that is commonly used within the PET domain?
| raise NotImplementedError("Fitting with held-out data is not supported") | ||
|
|
||
| # ToDo | ||
| # Does not make sense to make timepoints be a kwarg if it is provided as a named parameter to __init__ |
|
|
||
| # ToDo | ||
| # What is the gtab equivalent of PET ? | ||
| model_str = getattr(self, "_model_class", "") |
There was a problem hiding this comment.
This may not be necessary.
|
|
||
| # ToDo | ||
| # What are the gtab (and S0 if any) equivalent of PET ? | ||
| if n_models == 1: |
There was a problem hiding this comment.
|
|
||
| # ToDo | ||
| # Does not make sense to make timepoints be a kwarg if it is provided as a named parameter to __init__ | ||
| timepoints = kwargs.get("timepoints", None) or self._x |
There was a problem hiding this comment.
| # Does the below apply to PET ? Martin has the return None statement | ||
| # if index is None: | ||
| # raise RuntimeError( | ||
| # f"Model {self.__class__.__name__} does not allow locking.") |
90eb886 to
afb11da
Compare
|
The file |
afb11da to
cbf417d
Compare
|
Re #204 (comment) Compared to afb11da, in cbf417d I am discarding the idea of keeping a parallel of the dMRI base model in the PET base model. The PET model tests pass and the notebook runs now. It gives a result that is almost the same as in #203 (comment), with only minor differences in X and Y rotation start/ends. However, the parallelization was lost in the refactoring and the notebook takes almost 2 hours now compared to 30 minutes. So I need to look into that. |
cbf417d to
10af03f
Compare
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #204 +/- ##
==========================================
- Coverage 82.17% 82.10% -0.08%
==========================================
Files 37 37
Lines 2037 2045 +8
Branches 225 224 -1
==========================================
+ Hits 1674 1679 +5
- Misses 317 320 +3
Partials 46 46 ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
6318c50 to
908faa8
Compare
|
Re #204 (comment), the throttle issue was due to this line: nifreeze/src/nifreeze/model/pet.py Line 199 in 6318c50 which I had borrowed from nifreeze/src/nifreeze/model/dmri.py Line 107 in cb13535 Now changed to nifreeze/src/nifreeze/model/pet.py Line 199 in 908faa8 To match more closely nifreeze/src/nifreeze/model/dmri.py Line 107 in cb13535 And the speed is back. Results in plots, as I was seeing yesterday (cf. the above comment), almost the same with minor differences:
|
4751545 to
19d5956
Compare
| # Calculate index coordinates in the B-Spline grid | ||
| self._n_ctrl = n_ctrl or (len(timepoints) // 4) + 1 | ||
| def _preproces_data(self) -> np.ndarray: | ||
| # ToDo |
There was a problem hiding this comment.
If I recall correctly, the idea there is to preprocess only the data that is required; e.g. if we are using only a subset of the volumes, then we shouldn't preprocess the entire data, e.g.
nifreeze/src/nifreeze/model/dmri.py
Lines 122 to 124 in 4cfaadb
The data, _, gtab = self._dataset[idxmask] is commented out in here immediately after the ToDo to as a pointer to that idea.
|
|
||
| # Convert data into V (voxels) x T (timepoints) | ||
| data = data.reshape((-1, data.shape[-1])) if brainmask is None else data[brainmask] | ||
| # ToDo |
There was a problem hiding this comment.
Allowing _fit to override timepoints through kwargs duplicates information that was already validated and stored on self._x during initialization. Dropping that extra kwarg simplifies the API and avoids inconsistent inputs
There was a problem hiding this comment.
Not sure I understand: maybe the reply is answering a question elsewhere?
56cf1b8 to
a4643c5
Compare
21026bb to
7d22d9e
Compare
93c2fd2 to
485d8f4
Compare
|
Some questions that will need to be addressed at some point: #318 (comment) Especially relevant here (or for an immediately subsequent PR) is the need of the |
eb11211 to
515ee05
Compare
c0e5c8c to
70ad152
Compare
Refactor PET model: use a base class that contains the essential
properties for a PET model and create a derived class that implements
the B-Spline correction.
In the base class:
- Remove the `timepoints` attribute: it corresponds to the PET dataset
`midframe` attribute, and thus, there is no need to duplicate it in
the model.
- Remove the `xlim` attribute: it corresponds to the PET dataset
`total_duration` attribute, and thus, there is no need to duplicate it
in the model.
- Remove the checks related to the above attributes: checking the
coherence of their values is not the model's responsibility, and
should be done when instantiating the dataset.
Make the model use the frame index instead of the `midframe` value as
the argument of the `fit_predict` function. This increases makes the PET
model be consistent with the approach used in the code base as a
principle for generalization in the framework.
The index was also marked as an integer by the type annotations (as it
should be expected), whereas `midframe` values that were being provided
are typically floating point values.
Change the tests and the PET notebook accordingly.
In the derived, BSpline class:
- Remove the unnecessary explicit `float` casting around the number of
control points: the `dtype="float32"` specifier creates a float array.
Fixes:
```
Expected type 'str | Buffer | SupportsFloat | SupportsIndex', got 'Literal[0] | None | {__eq__} | int' instead
```
raised by the IDE.
Take advantage of the commit to use `np.asarray` to avoid extra copies
when not necessary.
Cast explicitly to `int` when getting the frame indices in the
estimator's `run` method to avoid type checking errors:
```
src/nifreeze/estimator.py:274: error:
Argument 1 to "fit_predict" of "BSplinePETModel" has incompatible type
"signedinteger[_32Bit | _64Bit]"; expected "int | None" [arg-type]
```
raised for example at:
https://github.com/nipreps/nifreeze/actions/runs/20402438629/job/58626813972#step:8:35
70ad152 to
7c7c058
Compare
|
OK, I think we have reached a point where this needs to be merged to move forward. So, I am going to go ahead and merge this. The summary of the status:
This is caused by the This requires refactoring the estimator, which is a WIP in PR #203. Thus, solving this is left for that PR.
|
|
There are some warnings related to DIPY and unrelated to this patch set that are being reported by the CI: They should either be ignored or DIPY be bumped to be fixed: dipy/dipy@c122e00 Merging. |
|
In the commit message:
|


Refactor PET model: use a base class that contains the essential properties for a PET model and create a derived class that implements the B-Spline correction.
In the base class:
timepointsattribute: it corresponds to the PET datasetmidframeattribute, and thus, there is no need to duplicate it in the model.xlimattribute: it corresponds to the PET datasettotal_durationattribute, and thus, there is no need to duplicate it in the model.Make the model use the frame index instead of the
midframevalue as the argument of thefit_predictfunction. This increases makes the PET model be consistent with the approach used in the code base as a principle for generalization in the framework.The index was also marked as an integer by the type annotations (as it should be expected), whereas
midframevalues that were being provided are typically floating point values.Change the tests and the PET notebook accordingly.
In the derived, BSpline class:
floatcasting around the number of control points: thedtype="float32"specifier creates a float array.Fixes:
Take advantage of the commit to use
np.asarrayto avoid extra copies when not necessary.Cast explicitly to
intwhen getting the frame indices in the estimator'srunmethod to avoid type checking errors:raised for example at:
https://github.com/nipreps/nifreeze/actions/runs/20402438629/job/58626813972#step:8:35