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

[Stretch] Support Float type in classical expressions. #13832

Merged
merged 65 commits into from
Mar 1, 2025

Conversation

kevinhartman
Copy link
Contributor

@kevinhartman kevinhartman commented Feb 12, 2025

Summary

Adds a new Float type to the classical expression system. This is necessary to express timing relationships between durations and stretches in expressions.

In a follow-up(s), I'll add Duration and Stretch types as well as binary operators for +, -, * and /, and then plumb all of this into the existing circuit variable tracking infra, integrate with the Delay instruction, support serialization, etc.

Details and comments

Currently based on #13811. Here's a readable diff in the meantime.

  • The Float type is considered of an "unspecified width", in accordance with the machine-precision float in OpenQASM3. If we add a fixed-width float (similar to what we've got for Uint), then it shouldn't be difficult to add an optional constructor argument to Float for width, defaulted to None. I didn't opt to do this since I imagine fixed-width floats aren't desperately needed for stretches, and they would've taken too long to stamp out.
  • expr.lift now supports Python floats.
    • Floats can indeed be const.
  • Qiskit is stricter than QASM, in that its type system considers casts from Uint or Bool to Float to be unsafe.
    • You can still do it, but it requires an explicit cast node.
  • For this reason, Float cannot be mixed with Uint in binary operations. You must cast one of them, first.
  • Floats are only directly usable with the existing binary relation operations. The rest forbid direct use of Float for one reason or another (e.g. not losslessly coercable to Bool). The arithmetic operations to come (+, -, * and /) will support them.

To-do

  • QPY support.
  • QASM support.

Types that have some natural order no longer have an ordering
when one of them is strictly greater but has an incompatible
const-ness (i.e. when the greater type is const but the other
type is not).
We need to reject types with const=True in QPY until it supports them.

For now, I've also made the Index and shift operator constructors
lift their RHS to the same const-ness as the target to make it
less likely that existing users of expr run into issues when
serializing to older QPY versions.
This is probably a better default in general, since we
don't really have much use for const except for timing
stuff.
Since we're going for using a Cast node when const-ness
differs, this will be fine.
@kevinhartman kevinhartman requested a review from a team as a code owner February 12, 2025 21:39
@qiskit-bot
Copy link
Collaborator

One or more of the following people are relevant to this code:

  • @Qiskit/terra-core
  • @mtreinish
  • @nkanazawa1989

@coveralls
Copy link

coveralls commented Feb 13, 2025

Pull Request Test Coverage Report for Build 13598643113

Warning: This coverage report may be inaccurate.

This pull request's base commit is no longer the HEAD commit of its target branch. This means it includes changes from outside the original pull request, including, potentially, unrelated coverage changes.

Details

  • 79 of 80 (98.75%) changed or added relevant lines in 12 files are covered.
  • 1117 unchanged lines in 43 files lost coverage.
  • Overall coverage decreased (-1.2%) to 86.972%

Changes Missing Coverage Covered Lines Changed/Added Lines %
qiskit/circuit/classical/expr/constructors.py 21 22 95.45%
Files with Coverage Reduction New Missed Lines %
qiskit/primitives/backend_estimator_v2.py 1 97.71%
qiskit/pulse/instructions/acquire.py 1 89.13%
qiskit/pulse/library/symbolic_pulses.py 1 92.75%
qiskit/pulse/library/waveform.py 1 97.83%
qiskit/pulse/schedule.py 1 88.21%
qiskit/result/counts.py 1 98.78%
qiskit/result/distributions/quasi.py 1 97.96%
qiskit/utils/units.py 1 93.55%
crates/accelerate/src/unitary_synthesis.rs 2 94.29%
qiskit/providers/models/init.py 2 77.78%
Totals Coverage Status
Change from base Build 13580209501: -1.2%
Covered Lines: 76079
Relevant Lines: 87475

💛 - Coveralls

By making const-ness a property of expressions, we don't need
any special serialization in QPY. That's because we assume that
all `Var` expressions are non-const, and all `Value` expressions
are const. And the const-ness of any expression is defined by
the const-ness of its operands, e.g. when QPY reconstructs a
binary operand, the constructed expression's `const` attribute
gets set to `True` if both of the operands are `const`, which
ultimately flows bottom-up from the `Var` and `Value` leaf nodes.
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.

github says I have 12 comments, but I think most of them are either ludicrously minor or just jokes

I think there was only two real ones (though they appear in a couple of places each) - questions about the exception handling and one about maybe making the float width explicit.

@kevinhartman kevinhartman removed the on hold Can not fix yet label Feb 28, 2025
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.

Thanks for this. I think I shook out a bug in an esoteric combination of arguments, but that's it here.

Comment on lines -58 to +62
if kind is CastKind.DANGEROUS:
raise TypeError(f"cannot cast '{expr}' to '{type}' without loss of precision")
raise TypeError(f"no cast is defined to take '{expr}' to '{type}'")
return None
Copy link
Member

Choose a reason for hiding this comment

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

Checking this branch out locally, I can see several uses of _coerce_lossless in this file that appear to have unchecked return values after this change.

In particular: _equal_like, _binary_relation, and _shift_like (but I might have missed one or two).

I think the _equal_like and _binary_relation ones shake out ok because of type.kind checks higher up, but it would be good to put a one-liner comment into each explaining that.

The _shift_like one has a potential bug:

from qiskit.circuit.classical import expr, types

expr.shift_left(expr.lift(5.0), 3, types.Uint(8))

is an AttributeError.

I don't know why I gave shift_left and shift_right a cast field within them - I think I must have been attempting to save some typing with inserting an extra expr.lift on the left operand when passing an integer literal and trying to fix the width (since the left- and right operands don't need to have equal widths in shifts, so there's no inference) - but at any rate, now it's possible to fail the cast in there.

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. I've fixed this and added a test in 0f14cca

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.

ace, assuming this passes the tests, I think this one's good to go too, thanks.

Sorry in advance for the large question left on the next one...

@jakelishman jakelishman enabled auto-merge March 1, 2025 00:10
@jakelishman jakelishman added this pull request to the merge queue Mar 1, 2025
Merged via the queue into Qiskit:main with commit 8a2197a Mar 1, 2025
20 checks passed
@kevinhartman kevinhartman added the mod: circuit Related to the core of the `QuantumCircuit` class or the circuit library label Mar 3, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
mod: circuit Related to the core of the `QuantumCircuit` class or the circuit library
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants