imr-framework / pypulseq

Pulseq in Python
https://pypulseq.readthedocs.io
GNU Affero General Public License v3.0
111 stars 58 forks source link

Various bug fixes, features, and cleanups #159

Closed FrankZijlstra closed 6 months ago

FrankZijlstra commented 6 months ago

Bug fixes:

Features:

Cleanups:

Notes:

btasdelen commented 6 months ago

@FrankZijlstra I have some questions:

FrankZijlstra commented 6 months ago
* Is it safe to remove version check in `read`? I guess yes, `first` and `last` are not a part of the .seq file, so actually has nothing to do with the version. Just wanted to double-check.

The check is only there to calculate old_data, but that's already contained in the gradient library anyway. So with the fix in event_lib that ignores old_data, this check is unnecessary.

* Would it make sense to add `first` and `last` when we are first adding the blocks during reading the file? That would avoid `get_blocks`.

The problem is that the calculation can depend on the previous block, and it needs the gradients definitions and waveforms, which aren't necessarily loaded once you get to the [BLOCKS] section. I think the alternative is to look only for arbitrary waveforms/extended trapezoids in the gradients after everything is read, then look up which blocks they're used in, then do the first/last calculations. But it's not a major issue, the current implementation just makes reading a bit slower.

The bigger issue I found is that setting first/last during reading is not always consistent with creating a sequence. For example, make_arbitrary_grad sets first and last irrespective of preceding blocks (as it obviously can't know at that point). But even when that gradient is added to the sequence, first/last is unchanged. During reading a different implementation (the odd_step1 part) sets first/last. I'm not sure if this can actually be fixed in general as the information on first/last is lost. For example, one could manually set first/last of a gradient while creating the sequence, write it to a file, and then it's impossible to restore the first/last values when reading it back in.

The first/last values of these gradients are only relevant for functions like calculate_kspace and plotting, for example. Running the sequence will not be affected by these.

* Are you sure we need to update the cache every time first and last is added? I thought about that and first put it there, but then realized it is only needed when the same shape occurs twice in the same block. I thought we are directly overwriting the cache anyway, maybe I missed something.

You might be right on this one, I hadn't realized the cached blocks could be edited in-place (should check that this is not happening anywhere else in the codebase). I'll go over it another time to see if this change was necessary.

* Minor comment: There is no harm adding the `last` if `first` is found, but I supposed if `first` is there, `last` also must be there as they are updated together.

I think you're referring to this?

if hasattr(grad, "first"):
grad_prev_last[j] = grad.last

The reason this is necessary is because grad_prev_last is used to set grad.first, but grad_prev_last was only set when the library is updated. It's possible for a later block to use the same gradient (which now has first/last already set, and thus previously wouldn't update grad_prev_last), and then the next block to have a gradient without first/last set, where first is set to grad_prev_last. I have a little testcase for this, which I will be compiling into some unit tests when I have the time...

btasdelen commented 6 months ago

Thanks for the detailed explanations! There is a conflict due to me merging your previous PR, so we need to solve that, too.

I also checked the Opts.defaults, I think the implementation is nice. I will try it with a couple of my own sequences and test if it works as expected, as well.

FrankZijlstra commented 6 months ago

I updated the block cache code, now it avoids the new get_block call altogether and just updates the cache in-place.

Added a few more small things:

btasdelen commented 6 months ago

@FrankZijlstra GitHub says there are still conflicts, so I could not merge.

FrankZijlstra commented 6 months ago

That's weird, for me it says "This branch has no conflicts with the base branch".

btasdelen commented 6 months ago

image

Well, I guess you can go ahead and merge, because I can't.

Edit: Oh, I can't rebase, but I can merge-commit.