-
Notifications
You must be signed in to change notification settings - Fork 2.5k
[Stretch] Add +
, -
, *
, /
operators for classical expressions.
#13850
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
Conversation
This reverts commit e2b3221.
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.
I wasn't going to have this, but since we have DANGEROUS Float => Int, and we have Int => Bool, I think this makes the most sense.
A Stretch can always represent a Duration (it's just an expression without any unresolved stretch variables, in this case), so we allow implicit conversion from Duration => Stretch. The reason for a separate Duration type is to support things like Duration / Duration => Float. This is not valid for stretches in OpenQASM (to my knowledge).
Also adds support to expr.lift to create a value expression of type types.Duration from an instance of qiskit.circuit.Duration.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I haven't looked at the test files (need to go to dinner), but here's a super quick scan over the implementation.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks for the changes! I think everything here is all right (though I don't trust that I read every test exactly correctly haha).
""" | ||
left, right = _lift_binary_operands(left, right) | ||
type: types.Type | ||
if left.type.kind is right.type.kind is types.Duration: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ooh, chained is
comparisons. Not a Python feature you see every day haha
@@ -314,6 +314,15 @@ class Op(enum.Enum): | |||
container types (e.g. unsigned integers) as the left operand, and any integer type as the | |||
right-hand operand. In all cases, the output bit width is the same as the input, and zeros | |||
fill in the "exposed" spaces. | |||
|
|||
The binary arithmetic operators :data:`ADD`, :data:`SUB:, :data:`MUL`, and :data:`DIV` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The binary arithmetic operators :data:`ADD`, :data:`SUB:, :data:`MUL`, and :data:`DIV` | |
The binary arithmetic operators :data:`ADD`, :data:`SUB`, :data:`MUL`, and :data:`DIV` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe apply this in the next PR, just so I can get this one in the merge queue already?
opcode, expr.Var(cr, types.Uint(8)), expr.Value(200, types.Uint(8)), types.Uint(8) | ||
), | ||
) | ||
self.assertFalse(function(cr, 200).const) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Super minor, but the expr.Binary
equality check should already be doing the const
checks. Could we have written the tests such that the const
is set explicitly in the expr.Binary
constructor?
(No need to change all the tests now.)
# Multiply timing expressions by non-const floats: | ||
non_const_float = expr.Var.new("a", types.Float()) | ||
with self.assertRaisesRegex(ValueError, "would result in a non-const"): | ||
expr.mul(Duration.dt(1000), non_const_float) | ||
with self.assertRaisesRegex(ValueError, "would result in a non-const"): | ||
expr.mul(non_const_float, Duration.dt(1000)) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In a world where we have a use and a way of functioning with runtime-specified delay
, I can imagine us relaxing this restriction, but I think it's good to assert right now.
Summary
Adds new arithmetic operators for use with numeric and a duration expressions. These are needed to express relationships between stretch variables and durations.
Details and comments
Based on #13844. More readable diff here..Uint
andFloat
, but these cannot be intermixed. You must first cast one to the other explicitly, sinceFloat => Uint
andUint => Float
are both considered dangerous cast kinds. If twoUint
s of different width are used, the wider one is chosen for the expression's output type.Duration
.Duration
andFloat
operands. Currently, aUint
must first be cast to aFloat
to be used as a timing multiplier.Duration
, but theexpr
constructors will raise aValueError
if a user tries to produce such an expression (this was not possible in the original design of const-types, but what we have now feels more future-proof than baking this as a hard requirement into the type system).Var
with aDuration
type, which would not be a constant expression. If desired, we can block users from adding such variables to a circuit.Duration
andFloat
operands, but only where the duration is in theleft
position. It is also supported between twoDuration
types, in which case the result is aFloat
. Dividing aDuration
by a non-const expression will raise aValueError
.To-do