-
Notifications
You must be signed in to change notification settings - Fork 672
Simple OpenQASM 3.0 expressions and casts #7593
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
base: master
Are you sure you want to change the base?
Changes from 250 commits
50f106f
c2a0c05
08db20c
a78d950
5ad362d
6790579
8cbb4e8
492e1b2
314185d
267ac8f
b3a7dc1
545ff3a
4f44322
64fd014
3bd7808
e280871
e459523
2ef8a2c
85ef639
bfac0fa
c7d9a93
e7baeeb
61436fd
031eb57
aa7236c
cdfde44
b6b374b
4dad404
cd1e498
2b4ed11
09e2497
23809b8
3c72546
a4e755e
5599e8d
7c5f143
fccaf9c
243083b
bf6d549
365a13e
a0e45bd
d3fb7e0
ea82c08
7144a5d
a82c789
b400a98
46ce06e
6922c4c
de6b9fc
6c69441
e6ec23f
3ae407d
38db04c
91975ee
e184762
bd2c94b
aceda7b
2875e2f
024a302
d569a6b
e98d3f8
7accb46
c68d233
2741c93
5a1da57
b52988f
dc31195
eca761f
3dd22dc
47ee8af
8dc7e85
b578c3c
f35e12e
83a9403
6b7e452
1d9fac8
eeee2ad
5c28e3a
2084a72
6a18692
7c39931
5e655b2
f1cb5f2
12debf7
304c39f
01452e9
faf577a
74e14b1
814e174
de262fe
607660e
1101c53
051e2c7
6f88838
3a55fb9
0cee688
3171c55
a532497
d0c69f9
f278d41
738d88a
8a26746
90624e9
9f7ea0d
7b7c3dc
d3e04d1
227b92e
42b4101
66626a1
ed86157
4200cc9
fdf3f2e
8da0c44
060695d
0f12c0d
f63357b
43aac0d
ebf99f4
0a6e16b
78bd2e8
810e49e
c21cd0e
65e5a34
4dc3ef6
8d79e52
b8cd2c1
e6bcaeb
e56247f
d719646
d500ebf
48528e8
b07957e
17bcaa5
0c158bd
893f382
0180fa7
4e9d700
0bfd973
c8ad00c
93ece37
915a771
a2c6b7c
4bb9491
9024ab3
9c93b87
3c721bf
834bc92
7c14166
61dbbeb
ba72812
5f585df
ead176e
924752a
8a0aa2c
18c4e61
269da10
0d26031
7e33347
2795e76
ad87f48
6bcf4c4
4adf9ea
d3aaeb4
f548a19
12908c5
faea18f
5d002ec
7f3c659
7e4d8da
ac17c5b
054f1cf
684f519
7422d86
6ec0a16
c03323e
f337a01
85cd9ad
d95017e
af8c89d
92c15d3
85a819b
06aa886
f713b0d
2dfcfa5
81572c8
3f053bb
776d6c4
afb2969
6546d4c
b771aa5
db29b72
e7c5c75
219fe4d
aa0883a
2be2c10
d274a8e
6e57e3f
5d5c632
199f46e
5ece1da
ac9a5dd
0c30fa2
36e1c11
b7b98c4
8c34b18
650fdf9
d8ce679
c5d1355
b886aea
c8c24be
30a3227
2ed517a
6ede81d
897bce9
5d2a43e
33c9e04
a484ea8
a6c1cc8
e804d6b
d3b6f76
988c5d1
0b66c1d
927766f
e1ee664
08d61e0
33e0aea
70fa8aa
30d3180
3afd579
22d1ba5
4d74068
467820b
59a1248
a7353d7
0e16fdc
4a02d0b
98a3042
fc33e3b
f5bfd74
98458c4
ce3b163
10bf4d2
0fe1c7d
2849bd4
28ccbf5
0692899
51a9975
1f7fd14
3733f58
391c74d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -7,25 +7,37 @@ | |||||
from functools import partial | ||||||
from typing import Any, Iterable | ||||||
|
||||||
import numpy as np | ||||||
from numpy import uint | ||||||
from openqasm3.ast import ( | ||||||
AliasStatement, | ||||||
ArrayLiteral, | ||||||
ArrayType, | ||||||
BinaryExpression, | ||||||
BitstringLiteral, | ||||||
BooleanLiteral, | ||||||
BoolType, | ||||||
Cast, | ||||||
ClassicalAssignment, | ||||||
ClassicalDeclaration, | ||||||
ComplexType, | ||||||
ConstantDeclaration, | ||||||
DurationLiteral, | ||||||
EndStatement, | ||||||
Expression, | ||||||
ExpressionStatement, | ||||||
FloatLiteral, | ||||||
FloatType, | ||||||
Identifier, | ||||||
ImaginaryLiteral, | ||||||
IndexExpression, | ||||||
IntegerLiteral, | ||||||
IntType, | ||||||
QuantumGate, | ||||||
QubitDeclaration, | ||||||
RangeDefinition, | ||||||
UintType, | ||||||
UnaryExpression, | ||||||
) | ||||||
from openqasm3.visitor import QASMNode | ||||||
|
||||||
|
@@ -66,6 +78,64 @@ | |||||
"CRZ": ops.CRZ, | ||||||
} | ||||||
|
||||||
EQUALS = "=" | ||||||
ARROW = "->" | ||||||
PLUS = "+" | ||||||
DOUBLE_PLUS = "++" | ||||||
MINUS = "-" | ||||||
ASTERISK = "*" | ||||||
DOUBLE_ASTERISK = "**" | ||||||
SLASH = "/" | ||||||
PERCENT = "%" | ||||||
PIPE = "|" | ||||||
DOUBLE_PIPE = "||" | ||||||
AMPERSAND = "&" | ||||||
DOUBLE_AMPERSAND = "&&" | ||||||
CARET = "^" | ||||||
AT = "@" | ||||||
TILDE = "~" | ||||||
EXCLAMATION_POINT = "!" | ||||||
EQUALITY_OPERATORS = ["==", "!=", "~="] | ||||||
COMPOUND_ASSIGNMENT_OPERATORS = [ | ||||||
"+=", | ||||||
"-=", | ||||||
"*=", | ||||||
"/=", | ||||||
"&=", | ||||||
"|=", | ||||||
"^=", | ||||||
"<<=", | ||||||
">>=", | ||||||
"%=", | ||||||
"**=", | ||||||
] | ||||||
COMPARISON_OPERATORS = [">", "<", ">=", "<="] | ||||||
BIT_SHIFT_OPERATORS = [">>", "<<"] | ||||||
|
||||||
NON_ASSIGNMENT_CLASSICAL_OPERATORS = ( | ||||||
EQUALITY_OPERATORS | ||||||
+ COMPARISON_OPERATORS | ||||||
+ BIT_SHIFT_OPERATORS | ||||||
+ [ | ||||||
PLUS, | ||||||
MINUS, | ||||||
ASTERISK, | ||||||
DOUBLE_ASTERISK, | ||||||
SLASH, | ||||||
PERCENT, | ||||||
PIPE, | ||||||
DOUBLE_PIPE, | ||||||
AMPERSAND, | ||||||
DOUBLE_AMPERSAND, | ||||||
CARET, | ||||||
AT, | ||||||
TILDE, | ||||||
EXCLAMATION_POINT, | ||||||
] | ||||||
) | ||||||
|
||||||
ASSIGNMENT_CLASSICAL_OPERATORS = [EQUALS, DOUBLE_PLUS] + COMPOUND_ASSIGNMENT_OPERATORS | ||||||
|
||||||
|
||||||
@dataclass | ||||||
class Variable: | ||||||
|
@@ -130,7 +200,7 @@ def update_var( | |||||
value: any, | ||||||
name: str, | ||||||
node: QASMNode, | ||||||
): | ||||||
): # pylint: disable=too-many-branches | ||||||
""" | ||||||
Updates a variable, or raises if it is constant. | ||||||
Args: | ||||||
|
@@ -144,8 +214,40 @@ def update_var( | |||||
f"Attempt to mutate a constant {name} on line {node.span.start_line} that was " | ||||||
f"defined on line {self.vars[name].line}" | ||||||
) | ||||||
self.vars[name].val = value | ||||||
self.vars[name].line = node.span.start_line | ||||||
if node.op.name in ASSIGNMENT_CLASSICAL_OPERATORS: | ||||||
if node.op.name == "=": | ||||||
self.vars[name].val = value | ||||||
if node.op.name == "++": | ||||||
self.vars[name].val = self.vars[name].val + 1 | ||||||
if node.op.name == "+=": | ||||||
self.vars[name].val += value | ||||||
if node.op.name == "-=": | ||||||
self.vars[name].val -= value | ||||||
if node.op.name == "*=": | ||||||
self.vars[name].val = self.vars[name].val * value | ||||||
if node.op.name == "/=": | ||||||
self.vars[name].val = self.vars[name].val / value | ||||||
if node.op.name == "&=": | ||||||
self.vars[name].val = self.vars[name].val & value | ||||||
if node.op.name == "|=": | ||||||
self.vars[name].val = self.vars[name].val | value | ||||||
if node.op.name == "^=": | ||||||
self.vars[name].val = self.vars[name].val ^ value | ||||||
if node.op.name == "<<=": | ||||||
self.vars[name].val = self.vars[name].val << value | ||||||
if node.op.name == ">>=": | ||||||
self.vars[name].val = self.vars[name].val >> value | ||||||
if node.op.name == "%=": | ||||||
self.vars[name].val = self.vars[name].val % value | ||||||
if node.op.name == "**=": | ||||||
self.vars[name].val = self.vars[name].val ** value | ||||||
astralcai marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
self.vars[name].line = node.span.start_line | ||||||
else: | ||||||
# we shouldn't ever get thi error if the parser did its job right | ||||||
comp-phys-marc marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
raise SyntaxError( # pragma: no cover | ||||||
f"Invalid operator {node.op.name} encountered in assignment expression " | ||||||
f"on line {node.span.start_line}." | ||||||
) # pragma: no cover | ||||||
else: | ||||||
raise TypeError(f"Attempt to use undeclared variable {name} in {self.name}") | ||||||
|
||||||
|
@@ -205,6 +307,24 @@ def _resolve_name(node: QASMNode): | |||||
return node.name if isinstance(node.name, str) else node.name.name | ||||||
|
||||||
|
||||||
def preprocess_operands(operand): | ||||||
""" | ||||||
Interprets a string operand as an appropriate type. | ||||||
|
||||||
Args: | ||||||
operand (str): the string operand to interpret. | ||||||
|
||||||
Returns: | ||||||
The interpreted operand as an appropriate type. | ||||||
""" | ||||||
if isinstance(operand, str): | ||||||
if operand.isdigit(): | ||||||
operand = int(operand) | ||||||
elif operand.isnumeric(): | ||||||
operand = float(operand) | ||||||
return operand | ||||||
|
||||||
|
||||||
class EndProgram(Exception): | ||||||
"""Exception raised when it encounters an end statement in the QASM circuit.""" | ||||||
|
||||||
|
@@ -335,7 +455,6 @@ def visit_classical_assignment(self, node: QASMNode, context: Context): | |||||
# references to an unresolved value see a func for now | ||||||
name = _resolve_name(node.lvalue) | ||||||
res = self.visit(node.rvalue, context) | ||||||
# TODO: different types of assignments | ||||||
context.update_var(res, name, node) | ||||||
|
||||||
@visit.register(AliasStatement) | ||||||
|
@@ -513,6 +632,145 @@ def apply_modifier(self, mod: QuantumGate, previous: Operator, context: Context, | |||||
|
||||||
return next, wires | ||||||
|
||||||
@visit.register(ExpressionStatement) | ||||||
def visit_expression_statement(self, node: ExpressionStatement, context: Context): | ||||||
""" | ||||||
Registers an expression statement. | ||||||
Args: | ||||||
node (ExpressionStatement): The expression statement. | ||||||
context (Context): The current context. | ||||||
""" | ||||||
return self.visit(node.expression, context) | ||||||
|
||||||
@visit.register(Cast) | ||||||
def visit_cast(self, node: Cast, context: Context): | ||||||
""" | ||||||
Registers a Cast expression. | ||||||
|
||||||
Args: | ||||||
node (Cast): The Cast expression. | ||||||
context (Context): The current context. | ||||||
|
||||||
Returns: | ||||||
Any: The argument cast to the appropriate type. | ||||||
|
||||||
Raises: | ||||||
TypeError: If the cast cannot be made. | ||||||
""" | ||||||
arg = self.visit(node.argument, context) | ||||||
ret = arg | ||||||
try: | ||||||
if isinstance(node.type, IntType): | ||||||
astralcai marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||
ret = int(arg) | ||||||
if isinstance(node.type, UintType): | ||||||
ret = uint(arg) | ||||||
if isinstance(node.type, FloatType): | ||||||
ret = float(arg) | ||||||
if isinstance(node.type, ComplexType): | ||||||
ret = complex(arg) | ||||||
if isinstance(node.type, BoolType): | ||||||
ret = bool(arg) | ||||||
if isinstance(node.type, ArrayType): | ||||||
ret = arg.val | ||||||
# TODO: durations, angles, etc. | ||||||
except TypeError as e: | ||||||
raise TypeError( | ||||||
f"Unable to cast {arg.__class__.__name__} to {node.type.__class__.__name__}: {str(e)}" | ||||||
) from e | ||||||
return ret | ||||||
|
||||||
@visit.register(BinaryExpression) | ||||||
def visit_binary_expression( | ||||||
self, node: BinaryExpression, context: Context | ||||||
): # pylint: disable=too-many-branches | ||||||
""" | ||||||
Registers a binary expression. | ||||||
|
||||||
Args: | ||||||
node (BinaryExpression): The binary expression. | ||||||
context (Context): The current context. | ||||||
|
||||||
Returns: | ||||||
The result of the evaluated expression. | ||||||
""" | ||||||
lhs = preprocess_operands(self.visit(node.lhs, context)) | ||||||
rhs = preprocess_operands(self.visit(node.rhs, context)) | ||||||
ret = None | ||||||
if node.op.name in NON_ASSIGNMENT_CLASSICAL_OPERATORS: | ||||||
match node.op.name: | ||||||
case "==": | ||||||
ret = lhs == rhs | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why not
Suggested change
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That is a fine suggestion. No problem with that. |
||||||
case "!=": | ||||||
ret = lhs != rhs | ||||||
case "~=": | ||||||
ret = np.isclose(lhs, rhs) | ||||||
case ">": | ||||||
ret = lhs > rhs | ||||||
case "<": | ||||||
ret = lhs < rhs | ||||||
case ">=": | ||||||
ret = lhs >= rhs | ||||||
case "<=": | ||||||
ret = lhs <= rhs | ||||||
case ">>": | ||||||
ret = lhs >> rhs | ||||||
case "<<": | ||||||
ret = lhs << rhs | ||||||
case "+": | ||||||
ret = lhs + rhs | ||||||
case "-": | ||||||
ret = lhs - rhs | ||||||
case "*": | ||||||
ret = lhs * rhs | ||||||
case "**": | ||||||
ret = lhs**rhs | ||||||
case "/": | ||||||
ret = lhs / rhs | ||||||
case "%": | ||||||
ret = lhs % rhs | ||||||
case "|": | ||||||
ret = lhs | rhs | ||||||
case "||": | ||||||
ret = lhs or rhs | ||||||
case "&": | ||||||
ret = lhs & rhs | ||||||
case "&&": | ||||||
ret = lhs and rhs | ||||||
case "^": | ||||||
ret = lhs ^ rhs | ||||||
return ret | ||||||
# we shouldn't ever get thi error if the parser did its job right | ||||||
raise SyntaxError( # pragma: no cover | ||||||
f"Invalid operator {node.op.name} encountered in binary expression " | ||||||
f"on line {node.span.start_line}." | ||||||
) # pragma: no cover | ||||||
|
||||||
@visit.register(UnaryExpression) | ||||||
def visit_unary_expression(self, node: UnaryExpression, context: Context): | ||||||
""" | ||||||
Registers a unary expression. | ||||||
|
||||||
Args: | ||||||
node (UnaryExpression): The unary expression. | ||||||
context (Context): The current context. | ||||||
|
||||||
Returns: | ||||||
The result of the evaluated expression. | ||||||
""" | ||||||
if node.op.name in NON_ASSIGNMENT_CLASSICAL_OPERATORS: | ||||||
operand = preprocess_operands(self.visit(node.expression, context)) | ||||||
if node.op.name == "!": | ||||||
return not operand | ||||||
if node.op.name == "-": | ||||||
return -operand | ||||||
if node.op.name == "~": | ||||||
return ~operand # pylint: disable=invalid-unary-operand-type | ||||||
# we shouldn't ever get thi error if the parser did its job right | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
raise SyntaxError( # pragma: no cover | ||||||
f"Invalid operator {node.op.name} encountered in unary expression " | ||||||
f"on line {node.span.start_line}." | ||||||
) # pragma: no cover | ||||||
|
||||||
@visit.register(IndexExpression) | ||||||
def visit_index_expression( | ||||||
self, node: IndexExpression, context: Context, aliasing: bool = False | ||||||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,25 @@ | ||
int a = 0; | ||
int b = 1; | ||
int c = 2; | ||
int d = 3; | ||
int e = 4; | ||
bool f = true; | ||
bool g = true; | ||
bit i = "10"; | ||
bit j = "10"; | ||
bit k = "11"; | ||
int l = 5; | ||
int m = 6; | ||
int n = 2; | ||
a = 1; | ||
b += 2; | ||
c -= 3; | ||
d *= 4; | ||
e /= 5; | ||
f &= true; | ||
g |= false; | ||
i ^= 9; | ||
j <<= 10; | ||
k >>= 1; | ||
l %= 2; | ||
m **= 13; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
const int lhs = 0; | ||
const int rhs = 1; | ||
bool a = lhs == rhs; | ||
bool b = lhs != rhs; | ||
bool c = lhs > rhs; | ||
bool d = lhs < rhs; | ||
bool e = lhs >= rhs; | ||
bool f = lhs <= rhs; | ||
|
||
bit lh = "10"; | ||
bit rh = "01"; | ||
bit g = lh >> rh; | ||
bit h = lh << rh; | ||
bit i = lh | rh; | ||
bit j = lh ^ rh; | ||
bit k = lh & rh; | ||
|
||
int l = 3; | ||
int r = 2; | ||
int m = l + r; | ||
int n = l / r; | ||
int o = l - r; | ||
int p = l * r; | ||
int q = l ** r; | ||
int s = l % r; | ||
|
||
bool left = true; | ||
bool right = false; | ||
int t = left || right; | ||
int u = left && right; | ||
|
||
bool v = lhs ~= rhs; |
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.
Personally I don't like it when a function has too many levels of nesting. Maybe you can start with
so that the bulk of your logic is not nested