Making internal time representation integer in units of nanoseconds.#129
Making internal time representation integer in units of nanoseconds.#129btasdelen wants to merge 1 commit intoimr-framework:devfrom
Conversation
|
I think one thing to be aware of is that you can still incur floating point errors with rounding operations while migrating. That is, with floats you have issues like Other than that, I think even with an incremental migration it would make sense to already remove |
|
@FrankZijlstra Yes, but aim is to have it almost always as integer, and only convert to float when it is absolutely necessary, or user asked for the time vector. That should alleviate that kind of issues as well. Having a dictionary or a tuple for waveform (example usage |
|
Hi @btasdelen , thank you for this. Moving to integer representation of nanoseconds is a great idea. Do you have a more complete plan mapped out i.e. how many of us will work on this translation and corresponding responsibilities? |
|
Any progress on this topic? Just out of curiosity.Using nanoseconds as time unit is a great idea. |
|
@zhengliuer Currently, there is some work going on to refactor core components of PyPulseq. I was putting this off until the refactoring is done to avoid conflicts, but I can revive this if the refactor takes longer than anticipated. |
|
All right! Looking forward to that. |
|
@zhengliuer out of curiosity, do you have an issue that you think this PR will solve? That may inform the implementation. |
I am just worrying about some float precision error, especially doing time checking. |
|
I encountered another problem related to the time representation when extracting the waveforms from a Sequence object. In Arbitrary gradients should be detected here: pypulseq/pypulseq/Sequence/sequence.py Line 1523 in 80f5d58 but due to rounding issues, my arbitrary spiral readout gradients are not detected as arbitrary gradients. This is one of several reasons why |
|
An overarching problem here is that the Perhaps we should redefine I see more checks in |
Definitely a good idea to define a common constant for all functions for now. Presumably a value of 1e-10 is also perfectly adequate, right? This does not mean that we reject the proposed idea of @btasdelen. We can still tackle this during a potential code refactoring (see my post in the PyPulseq Slack channel today) |
|
Maybe two-three I have been putting this off, because with the current state of the codebase, there are many parts that need to meticulously modified and kept track of to make sure nothing is left off as float time representation. A more object-oriented refactor of the base would make it easier to implement as well. |
Yes, I agree that this would make sense. In essence we would have to determine the minimum/maximum value we expect to encounter and look up the eps value there, e.g.: Note that this still needs a fudge-factor because rounding errors can compound. To be safe I'd suggest a factor 10. So with a max value of |
|
Hi! I was unaware of this PR but I am playing with a thought of switching the time axis of Matlab's Pulseq to unsigned int64 and store all times in picoseconds internally. I don't think nanoseconds are accurate enough for all kinds of strange hardware platforms. The maximum value of int64 is 18446744073709551615, which will limit the duration of the Pulseq sequence to 18446744 seconds, which is 5124 hours or 213 days. Actually, using a signed int64 may still be good enough and may avoid some unintended overflows, which are more likely to happen with unsigned int math... |
|
Hi @m-a-x-i-m-z! This one fell through the cracks for some years, because there were many pressing improvements that needed to be done first. After merging 1.5.1, I believe we are in a much better shape to do this, since we have much more extensive unit tests to ensure we do not break anything. Making the time precision picoseconds sounds good to me. I hope no one has to run sequences that long :). |
Motivation: My observation is one of the most bug prone part of the code base is where we deal with floating point operations on time. This is already discussed in several places: #114 and pulseq/pulseq#25. In summary, due to the finite (and variable) floating point precision, every floating point operation is prone to errors. However, this is a no issue for many operations, as the precision is much more than needed. This is the case for gradient and RF waveforms.
However, unlike waveforms, time vectors are not bounded, they may need to represent very little fractions (less than a microsecond) and very large numbers (hours, days?) as a single variable. Floating point is not designed for this, as it loses precision as the number itself is getting large.
This is where the fixed-point representation comes in. Luck for us, we can just use nanoseconds as the time unit, this way we can store it as a 64-bit signed integer, which should both give us a physically meaningful unit for the representation, while providing a 1ns of fine step size and ability to represent very large numbers without lose of precision. 64-bit signed integer gives us more than 290 years in range, so it should be fine.
Aim of this PR: Before committing to the idea and spending too much time on it, I modified a small part of the
waveforms_and_times()to show-case how a potential migration would look like. There are many moving parts that need to be taken care of, so it would make more sense to migrate it over time, while making sure everything works correctly. Note that, this commit still stores the time as afloat, because making it integer requires me to change the data types. If this plan makes sense to people, I can start to apply the changes under this PR.