Skip to content
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

Detach prefix util function #6885

Merged
merged 18 commits into from
Aug 19, 2021
Merged

Conversation

nkanazawa1989
Copy link
Contributor

Summary

This is a counterpart of apply_prefix function. The new function returns a tuple of scaled value and prefix from a given float value. This function may be used in qiskit-experiment for unit handling (apply_prefix is already used in some places but we don't have its counterpart).

Details and comments

See unittests

Comment on lines 69 to 70

fixed_point_3n = int(np.floor(np.log10(value) / 3))
Copy link
Member

Choose a reason for hiding this comment

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

This will emit a warning if value is 0 or less, though we probably still want to be able to handle them so they can be used with differences.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Good catch! Thanks @jakelishman :)

@nkanazawa1989 nkanazawa1989 force-pushed the feature/detach_prefix branch from 09f579b to 929fd49 Compare August 12, 2021 05:55
Copy link
Member

@jakelishman jakelishman left a comment

Choose a reason for hiding this comment

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

Mostly just comments about particular exceptions we're throwing here, rather than anything else.

qiskit/utils/units.py Outdated Show resolved Hide resolved
Comment on lines +19 to +20

from qiskit.circuit.parameterexpression import ParameterExpression
Copy link
Member

Choose a reason for hiding this comment

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

What's the use case for allowing ParameterExpression here? It seems more likely that the caller would be responsible for ensuring they're passing floats, rather than us.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That is the best scenario, but we have one exception

delay = Parameter("delay")

qc = QuantumCircuit(1)
qc.delay(delay, 0, unit="us")

qc = transpile(qc, backend)

In this code, we parametrize the delay in units of us. The transpiler needs to convert the SI time unit into dt to submit this circuit to backend. First, it applies prefix, then it multiplies backend._configuration.dt. ParameterExpression supports simple multiplication, thus these operation should be done while the actual value is parametrized. Note that unbound parameter expression cannot be converted into float.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Separate logic is removed with Decimal, but the import is still kept for type annotation.

Comment on lines 71 to 72
# to avoid round-off error of prefactor
_value = Decimal.from_float(value).scaleb(prefactors[unit[0]])
Copy link
Member

Choose a reason for hiding this comment

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

The decimal form doesn't really avoid any more round-off errors than just doing it in floating point, if we take care to only work with positive powers of 10. All positive powers of 10 up to 10^22 have exact representations (float(1e23) differs from the exact value by 2^23), and since floating-point operations guarantee that the result will be the closest float to the infinite-precision answer, there's the same amount of rounding here (conversion to float at the end) as there would be if we did the previous multiply-or-divide trick to keep exact representations.

That said, I don't really mind one way or the other that much. I doubt (hope, even!) that this isn't performance-critical code (where avoiding Decimal would be better), and as you say, we'll never really be able to entirely sensibly handle cases such as apply_prefix(1e-7, 'µs'), since float('1e-7') < Decimal('1e-7'), but float('1e-1') > Decimal('1e-1').

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Umm... this is good point. If we avoid Decimal we don't need to handle exception for ParameterExpression. I'll try multiply-or-divide trick too.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

That worked. 1ba3e95

qiskit/utils/units.py Outdated Show resolved Hide resolved
jakelishman
jakelishman previously approved these changes Aug 18, 2021
Copy link
Member

@jakelishman jakelishman left a comment

Choose a reason for hiding this comment

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

Seems fine now overall. Given that apply_prefix is already here, there's no harm in having its counterpart. There's always more that can be done with float handling, but if this is good enough for the use-cases that you have in mind, it can go in.

Copy link
Member

@mtreinish mtreinish left a comment

Choose a reason for hiding this comment

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

LGTM, just a couple questions inline.

qiskit/utils/units.py Outdated Show resolved Hide resolved
"""Test if same value can be obtained."""
unit = "Hz"

for prefix in ["P", "T", "G", "k", "m", "u", "n", "p", "f"]:
Copy link
Member

Choose a reason for hiding this comment

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

What about µ here?

Copy link
Contributor Author

Choose a reason for hiding this comment

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


for prefix in ["P", "T", "G", "k", "m", "u", "n", "p", "f"]:
scaled_val = apply_prefix(value, prefix + unit)
test_val, _ = detach_prefix(scaled_val)
Copy link
Member

Choose a reason for hiding this comment

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

Shouldn't we validate the returned prefix matches the input from detatch_prefix()? For u and µ we can just check that they both return µ.

Co-authored-by: Matthew Treinish <mtreinish@kortar.org>
mtreinish
mtreinish previously approved these changes Aug 18, 2021
Copy link
Member

@mtreinish mtreinish left a comment

Choose a reason for hiding this comment

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

LGTM, thanks for the quick update

qiskit/utils/units.py Outdated Show resolved Hide resolved
@mtreinish
Copy link
Member

I also think my earlier suggestion might have messed up the docs because that note ends up between Returns and Raises we'll have to see how it comes out and adjust accordingly

@nkanazawa1989
Copy link
Contributor Author

Yeah, Google vs Numpy. I usually use google docstring stlye, but still fine with .. note::

Co-authored-by: Matthew Treinish <mtreinish@kortar.org>
Copy link
Member

@jakelishman jakelishman left a comment

Choose a reason for hiding this comment

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

Looks like the docs are all fixed now, so this should be good.

@jakelishman jakelishman added automerge Changelog: New Feature Include in the "Added" section of the changelog labels Aug 19, 2021
@mergify mergify bot merged commit 1ea5fd9 into Qiskit:main Aug 19, 2021
@kdk kdk added this to the 0.19 milestone Nov 15, 2021
@nkanazawa1989 nkanazawa1989 deleted the feature/detach_prefix branch November 25, 2022 02:51
ElePT pushed a commit to ElePT/qiskit-algorithms that referenced this pull request Jul 27, 2023
* add detach prefix function

* handling of zero, negative, out of range values

* add reno

* black

* update implementation and add precision handling

* fix round off error

* parameter expression handling

* fix import order

* multiply-or-divide trick

* update logic

* Update qiskit/utils/units.py

Co-authored-by: Matthew Treinish <mtreinish@kortar.org>

* add unit prefix test

* Update qiskit/utils/units.py

Co-authored-by: Matthew Treinish <mtreinish@kortar.org>

Co-authored-by: Matthew Treinish <mtreinish@kortar.org>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Changelog: New Feature Include in the "Added" section of the changelog
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants