From 66d5459bc0d7177f2906b4876df2d2726a671564 Mon Sep 17 00:00:00 2001 From: Alan Bidart Date: Fri, 7 Jun 2024 17:37:55 +0200 Subject: [PATCH 01/12] feature: add support for typecasting --- src/autoqasm/converters/typecast.py | 69 +++++++++++++++++++++++++++ src/autoqasm/operators/__init__.py | 1 + src/autoqasm/operators/typecast.py | 47 ++++++++++++++++++ src/autoqasm/transpiler/transpiler.py | 9 +++- 4 files changed, 125 insertions(+), 1 deletion(-) create mode 100644 src/autoqasm/converters/typecast.py create mode 100644 src/autoqasm/operators/typecast.py diff --git a/src/autoqasm/converters/typecast.py b/src/autoqasm/converters/typecast.py new file mode 100644 index 00000000..25ee376c --- /dev/null +++ b/src/autoqasm/converters/typecast.py @@ -0,0 +1,69 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# http://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. + + +"""Converters for integer casting nodes.""" + +import ast + +import gast +from malt.core import ag_ctx, converter +from malt.pyct import templates + + +class TypecastTransformer(converter.Base): + def visit_Call(self, node: ast.stmt) -> ast.stmt: + """Converts type casting operations to their AutoQASM counterpart. + + Args: + node (ast.stmt): AST node to transform. + + Returns: + ast.stmt: Transformed node. + """ + typecasts_supported = ["int", "float"] + + template = """ + ag__.typecast(type_, argument_) + """ + node = self.generic_visit(node) + if ( + len(node.args) > 1 + and hasattr(node.args[1], "func") + and hasattr(node.args[1].func, "id") + and node.args[1].func.id in typecasts_supported + ): + new_node = templates.replace( + template, + type_=node.args[1].func.id, + argument_=node.args[1].args, + original=node, + ) + new_node = new_node[0].value + else: + new_node = node + return new_node + + +def transform(node: ast.stmt, ctx: ag_ctx.ControlStatusCtx) -> ast.stmt: + """Transform int cast nodes. + + Args: + node (ast.stmt): AST node to transform. + ctx (ag_ctx.ControlStatusCtx): Transformer context. + + Returns: + ast.stmt: Transformed node. + """ + + return TypecastTransformer(ctx).visit(node) diff --git a/src/autoqasm/operators/__init__.py b/src/autoqasm/operators/__init__.py index 04acb310..ced72c5c 100644 --- a/src/autoqasm/operators/__init__.py +++ b/src/autoqasm/operators/__init__.py @@ -39,3 +39,4 @@ from .logical import or_ # noqa: F401 from .return_statements import return_output_from_main # noqa: F401 from .slices import GetItemOpts, get_item, set_item # noqa: F401 +from .typecast import typecast # noqa: F401 diff --git a/src/autoqasm/operators/typecast.py b/src/autoqasm/operators/typecast.py new file mode 100644 index 00000000..f6bb647f --- /dev/null +++ b/src/autoqasm/operators/typecast.py @@ -0,0 +1,47 @@ +# Copyright Amazon.com Inc. or its affiliates. All Rights Reserved. +# +# Licensed under the Apache License, Version 2.0 (the "License"). You +# may not use this file except in compliance with the License. A copy of +# the License is located at +# +# http://aws.amazon.com/apache2.0/ +# +# or in the "license" file accompanying this file. This file is +# distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF +# ANY KIND, either express or implied. See the License for the specific +# language governing permissions and limitations under the License. + + +"""Operators for int cast statements.""" + +from typing import Any + +from autoqasm import types as aq_types + + +def typecast(type_: type, argument_: Any) -> aq_types.IntVar | int: + """Operator declares the `oq` variable, or sets variable's value if it's + already declared. + + Args: + type_ (type): the type for the conversion + argument_ (Any): object to be converted. + + Returns: + IntVar | FloatVar | int | float: IntVar/FloatVar object if argument is QASM type, else int/float. + """ + type_to_aq_type_map = {int: aq_types.IntVar, float: aq_types.FloatVar} + if aq_types.is_qasm_type(argument_): + if ( + argument_.size is not None + and argument_.size > 1 + and isinstance(argument_, aq_types.BitVar) + ): + typecasted_arg = type_to_aq_type_map[type_](argument_[0]) + for index in range(1, argument_.size): + typecasted_arg += type_to_aq_type_map[type_](argument_[index]) * 2**index + return typecasted_arg + else: + return type_to_aq_type_map[type_](argument_) + else: + return type_(*argument_) diff --git a/src/autoqasm/transpiler/transpiler.py b/src/autoqasm/transpiler/transpiler.py index 71b455a1..0729d515 100644 --- a/src/autoqasm/transpiler/transpiler.py +++ b/src/autoqasm/transpiler/transpiler.py @@ -48,7 +48,13 @@ from malt.utils import ag_logging as logging from autoqasm import operators, program, types -from autoqasm.converters import assignments, break_statements, comparisons, return_statements +from autoqasm.converters import ( + assignments, + break_statements, + comparisons, + return_statements, + typecast, +) class PyToOqpy(transpiler.PyToPy): @@ -131,6 +137,7 @@ def transform_ast( node = assignments.transform(node, ctx) node = lists.transform(node, ctx) node = slices.transform(node, ctx) + node = typecast.transform(node, ctx) node = call_trees.transform(node, ctx) node = control_flow.transform(node, ctx) node = conditional_expressions.transform(node, ctx) From 8a09bdd453dba54d4ff0700e72add54de4e67640 Mon Sep 17 00:00:00 2001 From: Alan Bidart Date: Fri, 7 Jun 2024 20:11:18 +0200 Subject: [PATCH 02/12] fix: changes to int typecasting --- src/autoqasm/converters/typecast.py | 18 +++++++----------- src/autoqasm/operators/__init__.py | 2 +- src/autoqasm/operators/typecast.py | 22 +++++----------------- src/autoqasm/transpiler/transpiler.py | 2 +- 4 files changed, 14 insertions(+), 30 deletions(-) diff --git a/src/autoqasm/converters/typecast.py b/src/autoqasm/converters/typecast.py index 25ee376c..1bd4b99b 100644 --- a/src/autoqasm/converters/typecast.py +++ b/src/autoqasm/converters/typecast.py @@ -31,22 +31,18 @@ def visit_Call(self, node: ast.stmt) -> ast.stmt: Returns: ast.stmt: Transformed node. """ - typecasts_supported = ["int", "float"] - - template = """ - ag__.typecast(type_, argument_) - """ + typecasts_supported = ["int"] node = self.generic_visit(node) + if ( - len(node.args) > 1 - and hasattr(node.args[1], "func") - and hasattr(node.args[1].func, "id") - and node.args[1].func.id in typecasts_supported + hasattr(node, "func") + and hasattr(node.func, "id") + and node.func.id in typecasts_supported ): + template = f"ag__.{node.func.id}_typecast(argument_)" new_node = templates.replace( template, - type_=node.args[1].func.id, - argument_=node.args[1].args, + argument_=node.args, original=node, ) new_node = new_node[0].value diff --git a/src/autoqasm/operators/__init__.py b/src/autoqasm/operators/__init__.py index ced72c5c..c7369e2b 100644 --- a/src/autoqasm/operators/__init__.py +++ b/src/autoqasm/operators/__init__.py @@ -39,4 +39,4 @@ from .logical import or_ # noqa: F401 from .return_statements import return_output_from_main # noqa: F401 from .slices import GetItemOpts, get_item, set_item # noqa: F401 -from .typecast import typecast # noqa: F401 +from .typecast import int_typecast # noqa: F401 diff --git a/src/autoqasm/operators/typecast.py b/src/autoqasm/operators/typecast.py index f6bb647f..84ab5b11 100644 --- a/src/autoqasm/operators/typecast.py +++ b/src/autoqasm/operators/typecast.py @@ -19,29 +19,17 @@ from autoqasm import types as aq_types -def typecast(type_: type, argument_: Any) -> aq_types.IntVar | int: +def int_typecast(argument_: Any) -> aq_types.IntVar | int: """Operator declares the `oq` variable, or sets variable's value if it's already declared. Args: - type_ (type): the type for the conversion - argument_ (Any): object to be converted. + argument_ (Any): object to be converted into an int. Returns: - IntVar | FloatVar | int | float: IntVar/FloatVar object if argument is QASM type, else int/float. + IntVar | int : IntVar object if argument is QASM type, else int. """ - type_to_aq_type_map = {int: aq_types.IntVar, float: aq_types.FloatVar} if aq_types.is_qasm_type(argument_): - if ( - argument_.size is not None - and argument_.size > 1 - and isinstance(argument_, aq_types.BitVar) - ): - typecasted_arg = type_to_aq_type_map[type_](argument_[0]) - for index in range(1, argument_.size): - typecasted_arg += type_to_aq_type_map[type_](argument_[index]) * 2**index - return typecasted_arg - else: - return type_to_aq_type_map[type_](argument_) + return aq_types.IntVar(argument_) else: - return type_(*argument_) + return int(*argument_) diff --git a/src/autoqasm/transpiler/transpiler.py b/src/autoqasm/transpiler/transpiler.py index 0729d515..d01ed0f1 100644 --- a/src/autoqasm/transpiler/transpiler.py +++ b/src/autoqasm/transpiler/transpiler.py @@ -134,10 +134,10 @@ def transform_ast( # canonicalization creates. node = continue_statements.transform(node, ctx) node = return_statements.transform(node, ctx) + node = typecast.transform(node, ctx) node = assignments.transform(node, ctx) node = lists.transform(node, ctx) node = slices.transform(node, ctx) - node = typecast.transform(node, ctx) node = call_trees.transform(node, ctx) node = control_flow.transform(node, ctx) node = conditional_expressions.transform(node, ctx) From 3f632298e1da3d0bba47332cd4aa63b968d5f170 Mon Sep 17 00:00:00 2001 From: Alan Bidart Date: Fri, 7 Jun 2024 20:33:05 +0200 Subject: [PATCH 03/12] fix: add support for args and kwargs in typecasting) --- src/autoqasm/operators/typecast.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/autoqasm/operators/typecast.py b/src/autoqasm/operators/typecast.py index 84ab5b11..476ff5a6 100644 --- a/src/autoqasm/operators/typecast.py +++ b/src/autoqasm/operators/typecast.py @@ -19,7 +19,7 @@ from autoqasm import types as aq_types -def int_typecast(argument_: Any) -> aq_types.IntVar | int: +def int_typecast(argument_: Any, *args, **kwargs) -> aq_types.IntVar | int: """Operator declares the `oq` variable, or sets variable's value if it's already declared. @@ -32,4 +32,4 @@ def int_typecast(argument_: Any) -> aq_types.IntVar | int: if aq_types.is_qasm_type(argument_): return aq_types.IntVar(argument_) else: - return int(*argument_) + return int(argument_, *args, **kwargs) From 17cd4880e29b150db745d80f600de7fcc564648b Mon Sep 17 00:00:00 2001 From: Alan Bidart Date: Fri, 7 Jun 2024 20:33:44 +0200 Subject: [PATCH 04/12] fix: add unit tests for int typecasting --- test/unit_tests/autoqasm/test_api.py | 36 ++++++++++++++++++++++++++++ 1 file changed, 36 insertions(+) diff --git a/test/unit_tests/autoqasm/test_api.py b/test/unit_tests/autoqasm/test_api.py index ee7ec1b6..6acb08ba 100644 --- a/test/unit_tests/autoqasm/test_api.py +++ b/test/unit_tests/autoqasm/test_api.py @@ -1266,3 +1266,39 @@ def test(int[32] a, int[32] b) { test(2, 3); test(4, 5);""" assert main.build().to_ir() == expected + + +class TestTypecasting: + def test_int_typecasting_on_measure(self): + @aq.main(num_qubits=2) + def main(): + test = int(measure([0, 1])) + + expected_ir = """OPENQASM 3.0; +qubit[2] __qubits__; +bit[2] __bit_0__ = "00"; +__bit_0__[0] = measure __qubits__[0]; +__bit_0__[1] = measure __qubits__[1]; +int[32] test = __bit_0__;""" + assert main.build().to_ir() == expected_ir + + def test_int_typecasting_on_string(self): + @aq.main(num_qubits=2) + def main(): + test = int("101", 2) + + expected_ir = """OPENQASM 3.0; +qubit[2] __qubits__;""" + assert main.build().to_ir() == expected_ir + + def test_nested_int_typecasting(self): + @aq.main(num_qubits=2) + def main(): + test = 2 * int(measure([0, 1])) + + expected_ir = """OPENQASM 3.0; +qubit[2] __qubits__; +bit[2] __bit_0__ = "00"; +__bit_0__[0] = measure __qubits__[0]; +__bit_0__[1] = measure __qubits__[1];""" + assert main.build().to_ir() == expected_ir From 0ca1252db74a5170526cd243c7c12eefff4b47fe Mon Sep 17 00:00:00 2001 From: Alan Bidart Date: Sat, 8 Jun 2024 01:05:16 +0200 Subject: [PATCH 05/12] fix: update typecasting tests --- src/autoqasm/operators/typecast.py | 1 + test/unit_tests/autoqasm/test_api.py | 12 ++++++++---- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/src/autoqasm/operators/typecast.py b/src/autoqasm/operators/typecast.py index 476ff5a6..2601d915 100644 --- a/src/autoqasm/operators/typecast.py +++ b/src/autoqasm/operators/typecast.py @@ -13,6 +13,7 @@ """Operators for int cast statements.""" +from __future__ import annotations from typing import Any diff --git a/test/unit_tests/autoqasm/test_api.py b/test/unit_tests/autoqasm/test_api.py index 6acb08ba..b525e80e 100644 --- a/test/unit_tests/autoqasm/test_api.py +++ b/test/unit_tests/autoqasm/test_api.py @@ -1272,7 +1272,7 @@ class TestTypecasting: def test_int_typecasting_on_measure(self): @aq.main(num_qubits=2) def main(): - test = int(measure([0, 1])) + test = int(measure([0, 1])) # noqa: F841 expected_ir = """OPENQASM 3.0; qubit[2] __qubits__; @@ -1285,7 +1285,7 @@ def main(): def test_int_typecasting_on_string(self): @aq.main(num_qubits=2) def main(): - test = int("101", 2) + test = int("101", 2) # noqa: F841 expected_ir = """OPENQASM 3.0; qubit[2] __qubits__;""" @@ -1294,11 +1294,15 @@ def main(): def test_nested_int_typecasting(self): @aq.main(num_qubits=2) def main(): - test = 2 * int(measure([0, 1])) + test = 2 * int(measure([0, 1])) # noqa: F841 + return test expected_ir = """OPENQASM 3.0; +int[32] __int_1__ = __bit_0__; +output int[32] test; qubit[2] __qubits__; bit[2] __bit_0__ = "00"; __bit_0__[0] = measure __qubits__[0]; -__bit_0__[1] = measure __qubits__[1];""" +__bit_0__[1] = measure __qubits__[1]; +test = 2 * __int_1__;""" assert main.build().to_ir() == expected_ir From 16fe02645969d2ce0fddad3020e29c2fdbe89545 Mon Sep 17 00:00:00 2001 From: Alan Bidart Date: Mon, 10 Jun 2024 16:12:41 +0200 Subject: [PATCH 06/12] fix: remove unused dependency --- src/autoqasm/converters/typecast.py | 1 - 1 file changed, 1 deletion(-) diff --git a/src/autoqasm/converters/typecast.py b/src/autoqasm/converters/typecast.py index 1bd4b99b..a91d5083 100644 --- a/src/autoqasm/converters/typecast.py +++ b/src/autoqasm/converters/typecast.py @@ -16,7 +16,6 @@ import ast -import gast from malt.core import ag_ctx, converter from malt.pyct import templates From c477d765670250ac970662f162b7cf60d63553aa Mon Sep 17 00:00:00 2001 From: Alan Bidart Date: Mon, 10 Jun 2024 17:02:23 +0200 Subject: [PATCH 07/12] fix: update operator to force casting in the correct location --- src/autoqasm/converters/typecast.py | 9 ++-- src/autoqasm/operators/__init__.py | 2 +- src/autoqasm/operators/typecast.py | 22 ++++++-- test/unit_tests/autoqasm/test_api.py | 40 --------------- test/unit_tests/autoqasm/test_operators.py | 58 ++++++++++++++++++++++ 5 files changed, 82 insertions(+), 49 deletions(-) diff --git a/src/autoqasm/converters/typecast.py b/src/autoqasm/converters/typecast.py index a91d5083..582376b8 100644 --- a/src/autoqasm/converters/typecast.py +++ b/src/autoqasm/converters/typecast.py @@ -19,6 +19,10 @@ from malt.core import ag_ctx, converter from malt.pyct import templates +TYPECASTING_OPERATORS = { + "int": "ag__.int_", +} + class TypecastTransformer(converter.Base): def visit_Call(self, node: ast.stmt) -> ast.stmt: @@ -30,15 +34,14 @@ def visit_Call(self, node: ast.stmt) -> ast.stmt: Returns: ast.stmt: Transformed node. """ - typecasts_supported = ["int"] node = self.generic_visit(node) if ( hasattr(node, "func") and hasattr(node.func, "id") - and node.func.id in typecasts_supported + and node.func.id in TYPECASTING_OPERATORS ): - template = f"ag__.{node.func.id}_typecast(argument_)" + template = f"{TYPECASTING_OPERATORS[node.func.id]}(argument_)" new_node = templates.replace( template, argument_=node.args, diff --git a/src/autoqasm/operators/__init__.py b/src/autoqasm/operators/__init__.py index c7369e2b..1788b5ff 100644 --- a/src/autoqasm/operators/__init__.py +++ b/src/autoqasm/operators/__init__.py @@ -39,4 +39,4 @@ from .logical import or_ # noqa: F401 from .return_statements import return_output_from_main # noqa: F401 from .slices import GetItemOpts, get_item, set_item # noqa: F401 -from .typecast import int_typecast # noqa: F401 +from .typecast import int_ # noqa: F401 diff --git a/src/autoqasm/operators/typecast.py b/src/autoqasm/operators/typecast.py index 2601d915..6628e78b 100644 --- a/src/autoqasm/operators/typecast.py +++ b/src/autoqasm/operators/typecast.py @@ -17,12 +17,12 @@ from typing import Any +from autoqasm import program from autoqasm import types as aq_types -def int_typecast(argument_: Any, *args, **kwargs) -> aq_types.IntVar | int: - """Operator declares the `oq` variable, or sets variable's value if it's - already declared. +def int_(argument_: Any, *args, **kwargs) -> aq_types.IntVar | int: + """Functional form of "int". Args: argument_ (Any): object to be converted into an int. @@ -31,6 +31,18 @@ def int_typecast(argument_: Any, *args, **kwargs) -> aq_types.IntVar | int: IntVar | int : IntVar object if argument is QASM type, else int. """ if aq_types.is_qasm_type(argument_): - return aq_types.IntVar(argument_) + return _oqpy_int(argument_) else: - return int(argument_, *args, **kwargs) + return _py_int(argument_, *args, **kwargs) + + +def _oqpy_int(argument_) -> aq_types.IntVar: + oqpy_program = program.get_program_conversion_context().get_oqpy_program() + result = aq_types.IntVar() + oqpy_program.declare(result) + oqpy_program.set(result, argument_) + return result + + +def _py_int(argument_: Any, *args, **kwargs) -> int: + return int(argument_, *args, **kwargs) diff --git a/test/unit_tests/autoqasm/test_api.py b/test/unit_tests/autoqasm/test_api.py index b525e80e..ee7ec1b6 100644 --- a/test/unit_tests/autoqasm/test_api.py +++ b/test/unit_tests/autoqasm/test_api.py @@ -1266,43 +1266,3 @@ def test(int[32] a, int[32] b) { test(2, 3); test(4, 5);""" assert main.build().to_ir() == expected - - -class TestTypecasting: - def test_int_typecasting_on_measure(self): - @aq.main(num_qubits=2) - def main(): - test = int(measure([0, 1])) # noqa: F841 - - expected_ir = """OPENQASM 3.0; -qubit[2] __qubits__; -bit[2] __bit_0__ = "00"; -__bit_0__[0] = measure __qubits__[0]; -__bit_0__[1] = measure __qubits__[1]; -int[32] test = __bit_0__;""" - assert main.build().to_ir() == expected_ir - - def test_int_typecasting_on_string(self): - @aq.main(num_qubits=2) - def main(): - test = int("101", 2) # noqa: F841 - - expected_ir = """OPENQASM 3.0; -qubit[2] __qubits__;""" - assert main.build().to_ir() == expected_ir - - def test_nested_int_typecasting(self): - @aq.main(num_qubits=2) - def main(): - test = 2 * int(measure([0, 1])) # noqa: F841 - return test - - expected_ir = """OPENQASM 3.0; -int[32] __int_1__ = __bit_0__; -output int[32] test; -qubit[2] __qubits__; -bit[2] __bit_0__ = "00"; -__bit_0__[0] = measure __qubits__[0]; -__bit_0__[1] = measure __qubits__[1]; -test = 2 * __int_1__;""" - assert main.build().to_ir() == expected_ir diff --git a/test/unit_tests/autoqasm/test_operators.py b/test/unit_tests/autoqasm/test_operators.py index ce3ffd9a..be0ee5fa 100644 --- a/test/unit_tests/autoqasm/test_operators.py +++ b/test/unit_tests/autoqasm/test_operators.py @@ -930,3 +930,61 @@ def test_list_ops(): assert np.array_equal(c, [[2, 3, 4], [2, 3, 4]]) assert test_list_ops.build().to_ir() + + +class TestTypecasting: + def test_int_typecasting_on_measure(self): + @aq.main(num_qubits=2) + def main(): + test = int(measure([0, 1])) # noqa: F841 + + expected_ir = """OPENQASM 3.0; +int[32] test; +qubit[2] __qubits__; +bit[2] __bit_0__ = "00"; +__bit_0__[0] = measure __qubits__[0]; +__bit_0__[1] = measure __qubits__[1]; +int[32] __int_1__; +__int_1__ = __bit_0__; +test = __int_1__;""" + assert main.build().to_ir() == expected_ir + + def test_int_typecasting_on_string(self): + @aq.main(num_qubits=2) + def main(): + test = int("101", 2) # noqa: F841 + + expected_ir = """OPENQASM 3.0; +qubit[2] __qubits__;""" + assert main.build().to_ir() == expected_ir + + def test_nested_int_typecasting_without_return(self): + @aq.main(num_qubits=2) + def main(): + test = 2 * int(measure([0, 1])) # noqa: F841 + + expected_ir = """OPENQASM 3.0; +qubit[2] __qubits__; +bit[2] __bit_0__ = "00"; +__bit_0__[0] = measure __qubits__[0]; +__bit_0__[1] = measure __qubits__[1]; +int[32] __int_1__; +__int_1__ = __bit_0__;""" + assert main.build().to_ir() == expected_ir + + def test_nested_int_typecasting_with_return(self): + @aq.main(num_qubits=2) + def main(): + test = 2 * int(measure([0, 1])) # noqa: F841 + return test + + expected_ir = """OPENQASM 3.0; +output int[32] test; +qubit[2] __qubits__; +bit[2] __bit_0__ = "00"; +__bit_0__[0] = measure __qubits__[0]; +__bit_0__[1] = measure __qubits__[1]; +int[32] __int_1__; +__int_1__ = __bit_0__; +test = 2 * __int_1__;""" + assert main.build().to_ir() == expected_ir From d64bfad84fcee00c44c8d10ff73325aefec72616 Mon Sep 17 00:00:00 2001 From: Alan Bidart Date: Mon, 10 Jun 2024 17:08:50 +0200 Subject: [PATCH 08/12] fix: type hint --- src/autoqasm/operators/typecast.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/autoqasm/operators/typecast.py b/src/autoqasm/operators/typecast.py index 6628e78b..37f610b0 100644 --- a/src/autoqasm/operators/typecast.py +++ b/src/autoqasm/operators/typecast.py @@ -36,7 +36,7 @@ def int_(argument_: Any, *args, **kwargs) -> aq_types.IntVar | int: return _py_int(argument_, *args, **kwargs) -def _oqpy_int(argument_) -> aq_types.IntVar: +def _oqpy_int(argument_: Any) -> aq_types.IntVar: oqpy_program = program.get_program_conversion_context().get_oqpy_program() result = aq_types.IntVar() oqpy_program.declare(result) From 638d3ddfe0357f06eccdf8b6e53d9938075d4a15 Mon Sep 17 00:00:00 2001 From: Alan Bidart Date: Wed, 12 Jun 2024 19:01:02 +0200 Subject: [PATCH 09/12] fix: add cosmetic changes --- src/autoqasm/converters/typecast.py | 4 +- src/autoqasm/operators/typecast.py | 18 ++++---- test/unit_tests/autoqasm/test_operators.py | 54 +++++++++++----------- 3 files changed, 39 insertions(+), 37 deletions(-) diff --git a/src/autoqasm/converters/typecast.py b/src/autoqasm/converters/typecast.py index 582376b8..0d8ff292 100644 --- a/src/autoqasm/converters/typecast.py +++ b/src/autoqasm/converters/typecast.py @@ -41,10 +41,10 @@ def visit_Call(self, node: ast.stmt) -> ast.stmt: and hasattr(node.func, "id") and node.func.id in TYPECASTING_OPERATORS ): - template = f"{TYPECASTING_OPERATORS[node.func.id]}(argument_)" + template = f"{TYPECASTING_OPERATORS[node.func.id]}(argument)" new_node = templates.replace( template, - argument_=node.args, + argument=node.args, original=node, ) new_node = new_node[0].value diff --git a/src/autoqasm/operators/typecast.py b/src/autoqasm/operators/typecast.py index 37f610b0..ef808c26 100644 --- a/src/autoqasm/operators/typecast.py +++ b/src/autoqasm/operators/typecast.py @@ -21,28 +21,28 @@ from autoqasm import types as aq_types -def int_(argument_: Any, *args, **kwargs) -> aq_types.IntVar | int: +def int_(argument: Any, *args, **kwargs) -> aq_types.IntVar | int: """Functional form of "int". Args: - argument_ (Any): object to be converted into an int. + argument (Any): object to be converted into an int. Returns: IntVar | int : IntVar object if argument is QASM type, else int. """ - if aq_types.is_qasm_type(argument_): - return _oqpy_int(argument_) + if aq_types.is_qasm_type(argument): + return _oqpy_int(argument) else: - return _py_int(argument_, *args, **kwargs) + return _py_int(argument, *args, **kwargs) -def _oqpy_int(argument_: Any) -> aq_types.IntVar: +def _oqpy_int(argument: Any) -> aq_types.IntVar: oqpy_program = program.get_program_conversion_context().get_oqpy_program() result = aq_types.IntVar() oqpy_program.declare(result) - oqpy_program.set(result, argument_) + oqpy_program.set(result, argument) return result -def _py_int(argument_: Any, *args, **kwargs) -> int: - return int(argument_, *args, **kwargs) +def _py_int(argument: Any, *args, **kwargs) -> int: + return int(argument, *args, **kwargs) diff --git a/test/unit_tests/autoqasm/test_operators.py b/test/unit_tests/autoqasm/test_operators.py index be0ee5fa..99dd137f 100644 --- a/test/unit_tests/autoqasm/test_operators.py +++ b/test/unit_tests/autoqasm/test_operators.py @@ -932,13 +932,12 @@ def test_list_ops(): assert test_list_ops.build().to_ir() -class TestTypecasting: - def test_int_typecasting_on_measure(self): - @aq.main(num_qubits=2) - def main(): - test = int(measure([0, 1])) # noqa: F841 +def test_int_typecasting_on_measure(): + @aq.main(num_qubits=2) + def main(): + test = int(measure([0, 1])) # noqa: F841 - expected_ir = """OPENQASM 3.0; + expected_ir = """OPENQASM 3.0; int[32] test; qubit[2] __qubits__; bit[2] __bit_0__ = "00"; @@ -947,38 +946,41 @@ def main(): int[32] __int_1__; __int_1__ = __bit_0__; test = __int_1__;""" - assert main.build().to_ir() == expected_ir + assert main.build().to_ir() == expected_ir - def test_int_typecasting_on_string(self): - @aq.main(num_qubits=2) - def main(): - test = int("101", 2) # noqa: F841 - expected_ir = """OPENQASM 3.0; +def test_int_typecasting_on_string(): + @aq.main(num_qubits=2) + def main(): + test = int("101", 2) # noqa: F841 + + expected_ir = """OPENQASM 3.0; qubit[2] __qubits__;""" - assert main.build().to_ir() == expected_ir + assert main.build().to_ir() == expected_ir + - def test_nested_int_typecasting_without_return(self): - @aq.main(num_qubits=2) - def main(): - test = 2 * int(measure([0, 1])) # noqa: F841 +def test_nested_int_typecasting_without_return(): + @aq.main(num_qubits=2) + def main(): + test = 2 * int(measure([0, 1])) # noqa: F841 - expected_ir = """OPENQASM 3.0; + expected_ir = """OPENQASM 3.0; qubit[2] __qubits__; bit[2] __bit_0__ = "00"; __bit_0__[0] = measure __qubits__[0]; __bit_0__[1] = measure __qubits__[1]; int[32] __int_1__; __int_1__ = __bit_0__;""" - assert main.build().to_ir() == expected_ir + assert main.build().to_ir() == expected_ir + - def test_nested_int_typecasting_with_return(self): - @aq.main(num_qubits=2) - def main(): - test = 2 * int(measure([0, 1])) # noqa: F841 - return test +def test_nested_int_typecasting_with_return(): + @aq.main(num_qubits=2) + def main(): + test = 2 * int(measure([0, 1])) # noqa: F841 + return test - expected_ir = """OPENQASM 3.0; + expected_ir = """OPENQASM 3.0; output int[32] test; qubit[2] __qubits__; bit[2] __bit_0__ = "00"; @@ -987,4 +989,4 @@ def main(): int[32] __int_1__; __int_1__ = __bit_0__; test = 2 * __int_1__;""" - assert main.build().to_ir() == expected_ir + assert main.build().to_ir() == expected_ir From a1581176bd84a72a9bfeddec4f18a8bd1ec04ef6 Mon Sep 17 00:00:00 2001 From: Ryan Shaffer <3620100+rmshaffer@users.noreply.github.com> Date: Wed, 12 Jun 2024 16:45:02 -0400 Subject: [PATCH 10/12] Update test/unit_tests/autoqasm/test_operators.py --- test/unit_tests/autoqasm/test_operators.py | 1 + 1 file changed, 1 insertion(+) diff --git a/test/unit_tests/autoqasm/test_operators.py b/test/unit_tests/autoqasm/test_operators.py index f1539ce3..f879dfcd 100644 --- a/test/unit_tests/autoqasm/test_operators.py +++ b/test/unit_tests/autoqasm/test_operators.py @@ -989,6 +989,7 @@ def main(): int[32] __int_1__; __int_1__ = __bit_0__; test = 2 * __int_1__;""" + assert main.build().to_ir() == expected_ir def test_integer_division_on_intvars(): From f71190d78d58284238dcb7ea7ee06155b15159d3 Mon Sep 17 00:00:00 2001 From: Alan Bidart Date: Thu, 13 Jun 2024 12:10:40 +0200 Subject: [PATCH 11/12] fix: add xfail test --- test/unit_tests/autoqasm/test_operators.py | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/test/unit_tests/autoqasm/test_operators.py b/test/unit_tests/autoqasm/test_operators.py index f879dfcd..ae213eb2 100644 --- a/test/unit_tests/autoqasm/test_operators.py +++ b/test/unit_tests/autoqasm/test_operators.py @@ -959,6 +959,7 @@ def main(): assert main.build().to_ir() == expected_ir +@pytest.mark.xfail(reason="Bug: assignments do not work as expected when operators are nested") def test_nested_int_typecasting_without_return(): @aq.main(num_qubits=2) def main(): @@ -970,7 +971,8 @@ def main(): __bit_0__[0] = measure __qubits__[0]; __bit_0__[1] = measure __qubits__[1]; int[32] __int_1__; -__int_1__ = __bit_0__;""" +__int_1__ = __bit_0__; +test = 2 * __int_1__;""" assert main.build().to_ir() == expected_ir From f50afecbe9d4c2fccae806aa63088ab68aebae9d Mon Sep 17 00:00:00 2001 From: Alan Bidart Date: Thu, 13 Jun 2024 19:31:26 +0200 Subject: [PATCH 12/12] fix: clarify test name --- test/unit_tests/autoqasm/test_operators.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/unit_tests/autoqasm/test_operators.py b/test/unit_tests/autoqasm/test_operators.py index ae213eb2..fc53cad1 100644 --- a/test/unit_tests/autoqasm/test_operators.py +++ b/test/unit_tests/autoqasm/test_operators.py @@ -949,7 +949,7 @@ def main(): assert main.build().to_ir() == expected_ir -def test_int_typecasting_on_string(): +def test_int_typecasting_on_python_string_not_captured(): @aq.main(num_qubits=2) def main(): test = int("101", 2) # noqa: F841