-
Notifications
You must be signed in to change notification settings - Fork 68
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
Making internal time representation integer in units of nanoseconds. #129
base: dev
Are you sure you want to change the base?
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 |
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.