Skip to content

Commit

Permalink
add alias suport
Browse files Browse the repository at this point in the history
  • Loading branch information
TheGupta2012 committed Oct 9, 2024
1 parent 20bbada commit 4cff332
Show file tree
Hide file tree
Showing 3 changed files with 98 additions and 74 deletions.
50 changes: 37 additions & 13 deletions pyqasm/visitor.py
Original file line number Diff line number Diff line change
Expand Up @@ -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] = {}
Expand Down Expand Up @@ -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]
Expand All @@ -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):
Expand All @@ -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)]]
Expand Down Expand Up @@ -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)
Expand All @@ -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}];"
Expand All @@ -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(
Expand All @@ -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(
Expand All @@ -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)

Expand Down
1 change: 1 addition & 0 deletions tests/resources/gates.py
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
121 changes: 60 additions & 61 deletions tests/test_alias.py
Original file line number Diff line number Diff line change
Expand Up @@ -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():
Expand Down Expand Up @@ -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():
Expand All @@ -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():
Expand All @@ -171,7 +170,7 @@ def test_invalid_alias_redefinition():
x alias;
"""
_ = validate(qasm3_alias_program)
validate(qasm3_alias_program)


def test_alias_defined_before():
Expand All @@ -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():
Expand All @@ -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():
Expand Down Expand Up @@ -301,4 +300,4 @@ def test_unsupported_alias():
# h q[2];
# }
# """
# _ = validate(qasm)
# validate(qasm)

0 comments on commit 4cff332

Please sign in to comment.