Skip to content

Commit

Permalink
Merge pull request #124 from CQCL/3q-rz-bug
Browse files Browse the repository at this point in the history
Exec order of sub commands bug fixes
  • Loading branch information
Asa-Kosto-QTM committed Feb 7, 2024
2 parents eb0a537 + d413f70 commit d18614e
Show file tree
Hide file tree
Showing 6 changed files with 84 additions and 41 deletions.
4 changes: 2 additions & 2 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ repos:
- id: debug-statements

- repo: https://github.com/crate-ci/typos
rev: v1.18.0
rev: v1.18.1
hooks:
- id: typos

Expand All @@ -25,7 +25,7 @@ repos:
- black==23.10.1

- repo: https://github.com/astral-sh/ruff-pre-commit
rev: v0.2.0
rev: v0.2.1
hooks:
- id: ruff
args: [--fix, --exit-non-zero-on-fix]
Expand Down
73 changes: 35 additions & 38 deletions pytket/phir/phirgen_parallel.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,23 +30,21 @@
logger = logging.getLogger(__name__)


def exec_order_preserved_helper(
ordered_dict: OrderedDict["UnitID", int], group_number: int, qubit_last_group: int
def exec_order_preserved(
group_exec_order: list[int], group_number: int, qubit_last_group: int
) -> bool:
"""A helper to determine whether order is preserved when adding qubits to groups."""
# determine whether the current group number is later in execution
# than the last group in which a qubit was used
group_eligible = group_number > qubit_last_group
if not group_eligible:
"""Determine whether order is preserved when adding qubits to groups."""
if (group_number not in group_exec_order) or (
qubit_last_group not in group_exec_order
):
return True
if group_number <= qubit_last_group:
return False
for group in ordered_dict.values():
if group == qubit_last_group:
order_preserved = False
break
if group == group_number:
order_preserved = True
break
return order_preserved
# if the group that the qubit is eligible for is later in the exec_order list than
# the last group in which it was used, it can be parallelized
return group_exec_order.index(group_number) > group_exec_order.index(
qubit_last_group
)


def process_sub_commands(
Expand All @@ -61,28 +59,29 @@ def process_sub_commands(
# and different gate types don't go in the same group
# RZ gates go in (mod 3)=0 groups, R1XY gates go in (mod 3)=1 groups,
# and all other gates will go in (mod 3)=2 groups
rz_group_number = -3 # will be set to 0 when first RZ gate is assigned (-3 + 3 = 0)
r1xy_group_number = (
-2 # will be set to 1 when first R1XY gate is assigned (-2 + 3 = 1)
)
other_group_number = (
-1 # will be set to 2 when first other gate is assigned (-1 + 3 = 2)
)
num_scs_per_qubit = {}

for qubit in sub_commands:
num_scs_per_qubit[qubit] = len(sub_commands[qubit])
# set every qubit's group id to be -4
# prevents KeyError in check for group number
# will get set to a valid group number the first time the qubit is used
qubits2groups[qubit] = -4
rz_group_number = -3 # set to 0 when first RZ gate is assigned (-3 + 3 = 0)
r1xy_group_number = -2 # set to 1 when first R1XY gate is assigned (-2 + 3 = 1)
other_group_number = -1 # set to 2 when first other gate is assigned (-1 + 3 = 2)
num_scs_per_qubit: dict["UnitID", int] = {}
group_exec_order: list[int] = []

for qubit, cmds in sub_commands.items():
num_scs_per_qubit[qubit] = len(cmds)

max_len = max(num_scs_per_qubit.values())

for index in range(max_len):
for qubit in sub_commands:
for qubit, cmds in sub_commands.items():
# make sure the qubits are inserted into
# qubits2groups in the order in which they appear
if qubit not in qubits2groups:
# set every qubit's group id to be a default value -4
# prevents KeyError in check for group number
# will get set to a valid group number the first time the qubit is used
qubits2groups[qubit] = -4
# check to make sure you are not accessing beyond the end of the list
if index < num_scs_per_qubit[qubit]:
sc = sub_commands[qubit][index]
sc = cmds[index]
gate = sc.op.type
match gate:
case tk.OpType.Rz:
Expand All @@ -96,18 +95,15 @@ def process_sub_commands(
valid_pll_op = False
# does a group exist for that gate type?
group_available = group_number in groups
# is that group later in execution than the
# most recent group for an op on that qubit?
order_preserved = exec_order_preserved_helper(
qubits2groups, group_number, qubits2groups[qubit]
)
# is the group size still under the maximum allowed parallel ops?
group_size = len(groups[group_number]) if group_number in groups else 0
group_not_too_large = group_size < max_parallel_sq_gates
# is the op parallelizable (only RZ or R1XY)?
if (
group_available
and order_preserved
and exec_order_preserved(
group_exec_order, group_number, qubits2groups[qubit]
)
and group_not_too_large
and valid_pll_op
):
Expand All @@ -126,6 +122,7 @@ def process_sub_commands(
other_group_number += 3
group_number = other_group_number
groups[group_number] = [sc]
group_exec_order.append(group_number)
# prevent the group number from ever decrementing
if group_number > qubits2groups[qubit]:
qubits2groups[qubit] = group_number
Expand Down
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ pre-commit==3.6.0
pydata_sphinx_theme==0.15.2
pytest==8.0.0
pytket==1.24.0
ruff==0.2.0
ruff==0.2.1
setuptools_scm==8.0.4
sphinx==7.2.6
wasmtime==17.0.0
Expand Down
22 changes: 22 additions & 0 deletions tests/data/qasm/rz_exec_order_three_qubits.qasm
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
OPENQASM 2.0;
include "qelib1.inc";
include "hqslib1_dev.inc";

qreg q[3];
creg c[3];

ZZ q[1],q[2];

ry(0.5*pi) q[0];
ry(0.5*pi) q[2];

barrier q[0],q[1],q[2];

ry(3.5*pi) q[0];
ry(0.5*pi) q[2];


barrier q[0],q[1],q[2];
measure q[0] -> c[0];
measure q[1] -> c[1];
measure q[2] -> c[2];
23 changes: 23 additions & 0 deletions tests/test_parallelization.py
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,29 @@ def test_single_qubit_circuit_with_parallel() -> None:
)


def test_three_qubit_rz_exec_order_preserved() -> None:
"""Test that the order of gating is preserved in a 3 qubit circuit with RZ gates."""
phir = get_phir_json(QasmFile.rz_exec_order_three_qubits, rebase=True)
# verify that the parallel RZ gates are executed before the second R1XY gate
assert phir["ops"][8] == {
"qop": "R1XY",
"angles": [[0.5, 0.5], "pi"],
"args": [["q", 0]],
}
assert phir["ops"][10] == {
"block": "qparallel",
"ops": [
{"qop": "RZ", "angles": [[0.5], "pi"], "args": [["q", 1]]},
{"qop": "RZ", "angles": [[1.5], "pi"], "args": [["q", 2]]},
],
}
assert phir["ops"][12] == {
"qop": "R1XY",
"angles": [[0.5, 0.5], "pi"],
"args": [["q", 2]],
}


def test_two_qubit_exec_order_preserved() -> None:
"""Test that the order of gating in preserved in a 2 qubit circuit."""
phir = get_phir_json(QasmFile.exec_order_two_qubits, rebase=True)
Expand Down
1 change: 1 addition & 0 deletions tests/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ class QasmFile(Enum):
classical_ordering = auto()
single_qubit_parallel_test = auto()
exec_order_two_qubits = auto()
rz_exec_order_three_qubits = auto()
cond_barrier = auto()
arbitrary_qreg_names = auto()

Expand Down

0 comments on commit d18614e

Please sign in to comment.