From 4cff33285f12b940340e5443ab29547e137e2ff2 Mon Sep 17 00:00:00 2001 From: TheGupta2012 Date: Wed, 9 Oct 2024 13:11:25 +0530 Subject: [PATCH] add alias suport --- pyqasm/visitor.py | 50 +++++++++++----- tests/resources/gates.py | 1 + tests/test_alias.py | 121 +++++++++++++++++++-------------------- 3 files changed, 98 insertions(+), 74 deletions(-) diff --git a/pyqasm/visitor.py b/pyqasm/visitor.py index 441113a..3967c4b 100644 --- a/pyqasm/visitor.py +++ b/pyqasm/visitor.py @@ -68,7 +68,9 @@ def __init__(self, module: Qasm3Module, check_only: bool = False): self._context: deque = deque([Context.GLOBAL]) self._qubit_labels: dict[str, int] = {} self._clbit_labels: dict[str, int] = {} + self._alias_qubit_labels: dict[tuple(str, int), tuple(str, int)] = {} self._global_qreg_size_map: dict[str, int] = {} + self._global_alias_size_map: dict[str, int] = {} self._function_qreg_size_map: deque = deque([]) # for nested functions self._function_qreg_transform_map: deque = deque([]) # for nested functions self._global_creg_size_map: dict[str, int] = {} @@ -348,6 +350,8 @@ def _get_op_bits( openqasm_bits = [] visited_bits = set() bit_list = [] + replace_alias = False + if isinstance(operation, qasm3_ast.QuantumMeasurementStatement): assert operation.target is not None bit_list = [operation.measure.qubit] if qubits else [operation.target] @@ -363,10 +367,15 @@ def _get_op_bits( reg_name = bit.name if reg_name not in reg_size_map: - raise_qasm3_error( - f"Missing register declaration for {reg_name} in operation {operation}", - span=operation.span, - ) + # check for aliasing + if qubits and reg_name in self._global_alias_size_map: + replace_alias = True + reg_size_map = self._global_alias_size_map + else: + raise_qasm3_error( + f"Missing register declaration for {reg_name} in operation {operation}", + span=operation.span, + ) self._check_if_name_in_scope(reg_name, operation) if isinstance(bit, qasm3_ast.IndexedIdentifier): @@ -385,6 +394,14 @@ def _get_op_bits( else: bit_ids = list(range(reg_size_map[reg_name])) + if replace_alias: + original_reg_name, _ = self._alias_qubit_labels[(reg_name, bit_ids[0])] + bit_ids = [ + self._alias_qubit_labels[(reg_name, bit_id)][1] # gives (original_reg, index) + for bit_id in bit_ids + ] + reg_name = original_reg_name + new_bits = [ qasm3_ast.IndexedIdentifier( qasm3_ast.Identifier(reg_name), [[qasm3_ast.IntegerLiteral(bit_id)]] @@ -1235,6 +1252,14 @@ def _visit_alias_statement(self, statement: qasm3_ast.AliasStatement) -> None: aliased_reg_name: str = "" aliased_reg_size: int = 0 + # this will only build a global alias map + + # whenever we are referring to qubits , we will first check in the global map of registers + # if the register is present, we will use the global map to get the qubit labels + # if not, we will check the alias map for the labels + + # see self._get_op_bits for details + # Alias should not be redeclared earlier as a variable or a constant if self._check_in_scope(alias_reg_name): raise_qasm3_error(f"Re-declaration of variable '{alias_reg_name}'", span=statement.span) @@ -1256,9 +1281,7 @@ def _visit_alias_statement(self, statement: qasm3_ast.AliasStatement) -> None: aliased_reg_size = self._global_qreg_size_map[aliased_reg_name] if isinstance(value, qasm3_ast.Identifier): # "let alias = q;" for i in range(aliased_reg_size): - self._qubit_labels[f"{alias_reg_name}_{i}"] = self._qubit_labels[ - f"{aliased_reg_name}_{i}" - ] + self._alias_qubit_labels[(alias_reg_name, i)] = (aliased_reg_name, i) alias_reg_size = aliased_reg_size elif isinstance(value, qasm3_ast.IndexExpression): if isinstance(value.index, qasm3_ast.DiscreteSet): # "let alias = q[{0,1}];" @@ -1267,9 +1290,7 @@ def _visit_alias_statement(self, statement: qasm3_ast.AliasStatement) -> None: Qasm3Validator.validate_register_index( qid, self._global_qreg_size_map[aliased_reg_name], qubit=True ) - self._qubit_labels[f"{alias_reg_name}_{i}"] = self._qubit_labels[ - f"{aliased_reg_name}_{qid}" - ] + self._alias_qubit_labels[(alias_reg_name, i)] = (aliased_reg_name, qid) alias_reg_size = len(qids) elif len(value.index) != 1: # like "let alias = q[0,1];"? raise_qasm3_error( @@ -1283,7 +1304,10 @@ def _visit_alias_statement(self, statement: qasm3_ast.AliasStatement) -> None: Qasm3Validator.validate_register_index( qid, self._global_qreg_size_map[aliased_reg_name], qubit=True ) - self._qubit_labels[f"{alias_reg_name}_0"] = value.index[0].value + self._alias_qubit_labels[(alias_reg_name, 0)] = ( + aliased_reg_name, + value.index[0].value, + ) alias_reg_size = 1 elif isinstance(value.index[0], qasm3_ast.RangeDefinition): # "let alias = q[0:1:2];" qids = Qasm3Transformer.get_qubits_from_range_definition( @@ -1292,10 +1316,10 @@ def _visit_alias_statement(self, statement: qasm3_ast.AliasStatement) -> None: is_qubit_reg=True, ) for i, qid in enumerate(qids): - self._qubit_labels[f"{alias_reg_name}_{i}"] = qid + self._alias_qubit_labels[(alias_reg_name, i)] = (aliased_reg_name, qid) alias_reg_size = len(qids) - self._global_qreg_size_map[alias_reg_name] = alias_reg_size + self._global_alias_size_map[alias_reg_name] = alias_reg_size logger.debug("Added labels for aliasing '%s'", target) diff --git a/tests/resources/gates.py b/tests/resources/gates.py index 4b9c754..cd29630 100644 --- a/tests/resources/gates.py +++ b/tests/resources/gates.py @@ -231,6 +231,7 @@ def test_fixture(): include "stdgates.inc"; qubit[2] q1; + ms(0,0,0) q1; u_abc(0.5, 0.5, 0.5) q1[0], q1[1]; // unsupported gate """, "Unsupported / undeclared QASM operation: u_abc", diff --git a/tests/test_alias.py b/tests/test_alias.py index c0ab01c..f5de582 100644 --- a/tests/test_alias.py +++ b/tests/test_alias.py @@ -30,61 +30,60 @@ # from .test_if import compare_reference_ir, resources_file -# def test_alias(): -# """Test converting OpenQASM 3 program with openqasm3.ast.AliasStatement.""" - -# qasm3_alias_program = """ -# OPENQASM 3.0; -# include "stdgates.inc"; - -# qubit[5] q; - -# let myqreg0 = q; -# let myqreg1 = q[1]; -# let myqreg2 = q[1:]; -# let myqreg3 = q[:4]; -# let myqreg4 = q[1:4]; -# let myqreg5 = q[1:2:4]; -# let myqreg6 = q[{0, 1}]; - -# x myqreg0[0]; -# h myqreg1; -# cx myqreg2[0], myqreg2[1]; -# cx myqreg3[2], myqreg3[3]; -# ccx myqreg4; -# swap myqreg5[0], myqreg5[1]; -# cz myqreg6; -# """ -# result = unroll(qasm3_alias_program) -# assert result.num_qubits == 5 -# assert result.num_clbits == 0 -# print(result.unrolled_qasm) -# check_single_qubit_gate_op(result.unrolled_ast, 1, [0], "x") -# check_single_qubit_gate_op(result.unrolled_ast, 1, [1], "h") -# check_two_qubit_gate_op(result.unrolled_ast, 2, [[1, 2], [2, 3]], "cx") -# check_three_qubit_gate_op(result.unrolled_ast, 1, [[1, 2, 3]], "ccx") -# check_two_qubit_gate_op(result.unrolled_ast, 1, [[1, 3]], "swap") -# check_two_qubit_gate_op(result.unrolled_ast, 1, [[0, 1]], "cz") - - -# def test_alias_update(): -# """Test converting OpenQASM 3 program with alias update.""" - -# qasm3_alias_program = """ -# OPENQASM 3.0; -# include "stdgates.inc"; - -# qubit[4] q; - -# let alias = q[1:]; -# let alias = q[2:]; - -# x alias[1]; -# """ -# result = unroll(qasm3_alias_program) -# assert result.num_qubits == 4 -# assert result.num_clbits == 0 -# check_single_qubit_gate_op(result.unrolled_ast, 1, [3], "x") +def test_alias(): + """Test converting OpenQASM 3 program with openqasm3.ast.AliasStatement.""" + + qasm3_alias_program = """ + OPENQASM 3.0; + include "stdgates.inc"; + + qubit[5] q; + + let myqreg0 = q; + let myqreg1 = q[1]; + let myqreg2 = q[1:]; + let myqreg3 = q[:4]; + let myqreg4 = q[1:4]; + let myqreg5 = q[1:2:4]; + let myqreg6 = q[{0, 1}]; + + x myqreg0[0]; + h myqreg1; + cx myqreg2[0], myqreg2[1]; + cx myqreg3[2], myqreg3[3]; + ccx myqreg4; + swap myqreg5[0], myqreg5[1]; + cz myqreg6; + """ + result = unroll(qasm3_alias_program) + assert result.num_qubits == 5 + assert result.num_clbits == 0 + check_single_qubit_gate_op(result.unrolled_ast, 1, [0], "x") + check_single_qubit_gate_op(result.unrolled_ast, 1, [1], "h") + check_two_qubit_gate_op(result.unrolled_ast, 2, [[1, 2], [2, 3]], "cx") + check_three_qubit_gate_op(result.unrolled_ast, 1, [[1, 2, 3]], "ccx") + check_two_qubit_gate_op(result.unrolled_ast, 1, [[1, 3]], "swap") + check_two_qubit_gate_op(result.unrolled_ast, 1, [[0, 1]], "cz") + + +def test_alias_update(): + """Test converting OpenQASM 3 program with alias update.""" + + qasm3_alias_program = """ + OPENQASM 3.0; + include "stdgates.inc"; + + qubit[4] q; + + let alias = q[1:]; + let alias = q[2:]; + + x alias[1]; + """ + result = unroll(qasm3_alias_program) + assert result.num_qubits == 4 + assert result.num_clbits == 0 + check_single_qubit_gate_op(result.unrolled_ast, 1, [3], "x") # def test_valid_alias_redefinition(): @@ -132,7 +131,7 @@ def test_alias_wrong_indexing(): x myqreg[0]; """ - _ = validate(qasm3_alias_program) + validate(qasm3_alias_program) def test_alias_invalid_discrete_indexing(): @@ -151,7 +150,7 @@ def test_alias_invalid_discrete_indexing(): x myqreg[0]; """ - _ = validate(qasm3_alias_program) + validate(qasm3_alias_program) def test_invalid_alias_redefinition(): @@ -171,7 +170,7 @@ def test_invalid_alias_redefinition(): x alias; """ - _ = validate(qasm3_alias_program) + validate(qasm3_alias_program) def test_alias_defined_before(): @@ -188,7 +187,7 @@ def test_alias_defined_before(): let myqreg = q2[1]; """ - _ = validate(qasm3_alias_program) + validate(qasm3_alias_program) def test_unsupported_alias(): @@ -205,7 +204,7 @@ def test_unsupported_alias(): let myqreg = q[0] ++ q[1]; """ - _ = validate(qasm3_alias_program) + validate(qasm3_alias_program) # def test_alias_in_scope_1(): @@ -301,4 +300,4 @@ def test_unsupported_alias(): # h q[2]; # } # """ -# _ = validate(qasm) +# validate(qasm)