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

fix: mangle OpenQASM keywords #28

Merged
84 changes: 83 additions & 1 deletion src/autoqasm/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,63 @@
from autoqasm.program.gate_calibrations import GateCalibration
from autoqasm.types import QubitIdentifierType as Qubit

reserved_keywords = [
Copy link
Contributor

Choose a reason for hiding this comment

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

Do you have a reference/link to the source of this list?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Copy link
Contributor

Choose a reason for hiding this comment

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

Could you please include a permalink reference as a comment in the code here? Perhaps you can use this URL: https://github.com/openqasm/openqasm/blob/909379e450f5e1b6cdd67b8a96683caca7b13e4d/source/grammar/qasm3Lexer.g4

Also, it would be ideal to move this list (and the function is_reserved_keyword) to a separate file (e.g. reserved_keywords.py) and then just import the function is_reserved_keyword here.

Copy link
Contributor

@yitchen-tim yitchen-tim Jun 10, 2024

Choose a reason for hiding this comment

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

Thanks, I think in long term, it would be better that this list is not hard-coded. Rather, the list depends on the qasm3lexer from the lexer file. But I think it does not necessary need to be implement in this PR. Could you add the reference link as an inline comment?

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thanks, I think in long term, it would be better that this list is not hard-coded. Rather, the list depends on the qasm3lexer from the lexer file. But I think it does not necessary need to be implement in this PR. Could you add the reference link as an inline comment?

Yes, initially, I had done it that way.

atharva-satpute marked this conversation as resolved.
Show resolved Hide resolved
"angle",
"array",
"barrier",
"bit",
"bool",
"box",
"cal",
"case",
"complex",
"const",
"creg",
"ctrl",
"default",
"defcal",
"defcalgrammar",
"delay",
"duration",
"durationof",
"end",
"euler",
"extern",
"false",
"float",
"frame",
"gate",
"gphase",
"im",
"include",
"input",
"int",
"inv",
"let",
"OPENQASM",
"measure",
"mutable",
"negctrl",
"output",
"pi",
"port",
"pragma",
"qreg", # For backward compatibility
atharva-satpute marked this conversation as resolved.
Show resolved Hide resolved
"qubit",
"readonly",
"reset",
"return",
"sizeof",
"stretch",
"switch",
"tau",
"true",
"U",
"uint",
"void",
"waveform",
]


def main(
func: Callable | None = None,
Expand Down Expand Up @@ -400,6 +457,19 @@ def _convert_subroutine(
return program_conversion_context.return_variable


def is_reserved_keyword(name: str) -> bool:
"""
Method to check whether or not 'name' is a reserved keyword

Args:
name (str): Name of the variable to be checked

Returns:
bool: True, if 'name' is a reserved keyword, False otherwise
"""
return name in reserved_keywords


def _wrap_for_oqpy_subroutine(f: Callable, options: converter.ConversionOptions) -> Callable:
"""Wraps the given function into a callable expected by oqpy.subroutine.

Expand All @@ -419,6 +489,12 @@ def _wrap_for_oqpy_subroutine(f: Callable, options: converter.ConversionOptions)
def _func(*args, **kwargs) -> Any:
inner_program: oqpy.Program = args[0]
with aq_program.get_program_conversion_context().push_oqpy_program(inner_program):
# Bind args and kwargs to '_func' signature
sig = inspect.signature(_func)
bound_args = sig.bind(*args, **kwargs)
bound_args.apply_defaults()
args = bound_args.args
kwargs = bound_args.kwargs
result = aq_transpiler.converted_call(f, args[1:], kwargs, options=options)
inner_program.autodeclare()
return result
Expand All @@ -440,9 +516,15 @@ def _func(*args, **kwargs) -> Any:
f'Parameter "{param.name}" for subroutine "{_func.__name__}" '
"is missing a required type hint."
)
# Check whether 'param.name' is a OpenQasm keyword
atharva-satpute marked this conversation as resolved.
Show resolved Hide resolved
if is_reserved_keyword(param.name):
_name = f"{param.name}_"
_func.__annotations__.pop(param.name)
else:
_name = param.name

new_param = inspect.Parameter(
name=param.name,
name=_name,
kind=param.kind,
annotation=aq_types.map_parameter_type(param.annotation),
)
Expand Down
8 changes: 4 additions & 4 deletions test/unit_tests/autoqasm/test_parameters.py
Original file line number Diff line number Diff line change
Expand Up @@ -401,8 +401,8 @@ def sub(float[64] alpha, float[64] theta) {
rx(theta) __qubits__[0];
rx(alpha) __qubits__[1];
}
def rx_alpha(int[32] qubit) {
rx(alpha) __qubits__[qubit];
def rx_alpha(int[32] qubit_) {
rx(alpha) __qubits__[qubit_];
}
float alpha = 0.5;
float beta = 1.5;
Expand All @@ -427,8 +427,8 @@ def parametric(alpha: float, beta: float):
bound_prog = parametric.build().make_bound_program({"beta": np.pi})

expected = """OPENQASM 3.0;
def rx_alpha(int[32] qubit, float[64] theta) {
rx(theta) __qubits__[qubit];
def rx_alpha(int[32] qubit_, float[64] theta) {
rx(theta) __qubits__[qubit_];
}
input float alpha;
float beta = 3.141592653589793;
Expand Down
4 changes: 2 additions & 2 deletions test/unit_tests/autoqasm/test_program.py
Original file line number Diff line number Diff line change
Expand Up @@ -95,8 +95,8 @@ def zne() -> aq.BitVar:
def expected(scale, angle):
return (
"""OPENQASM 3.0;
def circuit(float[64] angle) {
rx(angle) __qubits__[0];
def circuit(float[64] angle_) {
rx(angle_) __qubits__[0];
cnot __qubits__[0], __qubits__[1];
}
output bit return_value;
Expand Down
12 changes: 6 additions & 6 deletions test/unit_tests/autoqasm/test_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -309,7 +309,7 @@ def main():
annotation_test(True)

expected = """OPENQASM 3.0;
def annotation_test(bool input) {
def annotation_test(bool input_) {
}
annotation_test(true);"""

Expand All @@ -328,7 +328,7 @@ def main():
annotation_test(1)

expected = """OPENQASM 3.0;
def annotation_test(int[32] input) {
def annotation_test(int[32] input_) {
}
annotation_test(1);"""

Expand All @@ -347,7 +347,7 @@ def main():
annotation_test(1.0)

expected = """OPENQASM 3.0;
def annotation_test(float[64] input) {
def annotation_test(float[64] input_) {
rmshaffer marked this conversation as resolved.
Show resolved Hide resolved
}
annotation_test(1.0);"""

Expand All @@ -366,7 +366,7 @@ def main():
annotation_test(1)

expected = """OPENQASM 3.0;
def annotation_test(qubit input) {
def annotation_test(qubit input_) {
}
qubit[2] __qubits__;
annotation_test(__qubits__[1]);"""
Expand Down Expand Up @@ -403,7 +403,7 @@ def main():
annotation_test(a)

expected = """OPENQASM 3.0;
def annotation_test(bit input) {
def annotation_test(bit input_) {
}
bit a = 1;
annotation_test(a);"""
Expand All @@ -423,7 +423,7 @@ def main():
annotation_test(aq.BitVar(1))

expected = """OPENQASM 3.0;
def annotation_test(bit input) {
def annotation_test(bit input_) {
}
bit __bit_0__ = 1;
annotation_test(__bit_0__);"""
Expand Down
Loading