From 87dc25631a69f2f84c64fe3edd82e1ac843dd185 Mon Sep 17 00:00:00 2001 From: Asa Kosto Date: Tue, 30 Jan 2024 14:58:27 -0700 Subject: [PATCH 1/5] added test file --- tests/data/qasm/3q_rz.qasm | 22 ++++++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 tests/data/qasm/3q_rz.qasm diff --git a/tests/data/qasm/3q_rz.qasm b/tests/data/qasm/3q_rz.qasm new file mode 100644 index 0000000..7551736 --- /dev/null +++ b/tests/data/qasm/3q_rz.qasm @@ -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]; From 76d15f75022b7106acbe59dcaef23acea3741a25 Mon Sep 17 00:00:00 2001 From: Asa Kosto Date: Tue, 30 Jan 2024 20:35:05 -0700 Subject: [PATCH 2/5] fixed exec order problem in both 2q and 3q example files --- pytket/phir/phirgen_parallel.py | 42 +++++++++++-------- ...z.qasm => rz_exec_order_three_qubits.qasm} | 0 tests/test_parallelization.py | 23 ++++++++++ tests/test_utils.py | 1 + 4 files changed, 48 insertions(+), 18 deletions(-) rename tests/data/qasm/{3q_rz.qasm => rz_exec_order_three_qubits.qasm} (100%) diff --git a/pytket/phir/phirgen_parallel.py b/pytket/phir/phirgen_parallel.py index 2d3a08d..99bd0f3 100644 --- a/pytket/phir/phirgen_parallel.py +++ b/pytket/phir/phirgen_parallel.py @@ -31,25 +31,26 @@ def exec_order_preserved_helper( - ordered_dict: OrderedDict["UnitID", int], group_number: int, qubit_last_group: int + 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 + if (group_number not in group_exec_order) or ( + qubit_last_group not in group_exec_order + ): + return True group_eligible = group_number > qubit_last_group if not group_eligible: 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( +def process_sub_commands( # noqa: PLR0914 sub_commands: dict["UnitID", list[tk.Command]], max_parallel_sq_gates: int ) -> dict[int, list[tk.Command]]: """Create parallelizable groups of sub-commands.""" @@ -61,25 +62,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) + rz_group_number: int = -3 # set to 0 when first RZ gate is assigned (-3 + 3 = 0) + r1xy_group_number: int = ( + -2 # 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) + other_group_number: int = ( + -1 # set to 2 when first other gate is assigned (-1 + 3 = 2) ) - num_scs_per_qubit = {} + num_scs_per_qubit: dict["UnitID", int] = {} + group_exec_order: list[int] = [] 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 max_len = max(num_scs_per_qubit.values()) for index in range(max_len): for qubit in sub_commands: + # make sure the qubits are inserted into + # qubits2groups in the order in which they appear + if qubit not in qubits2groups: + 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] @@ -99,7 +104,7 @@ def process_sub_commands( # 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] + group_exec_order, 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 @@ -126,6 +131,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 diff --git a/tests/data/qasm/3q_rz.qasm b/tests/data/qasm/rz_exec_order_three_qubits.qasm similarity index 100% rename from tests/data/qasm/3q_rz.qasm rename to tests/data/qasm/rz_exec_order_three_qubits.qasm diff --git a/tests/test_parallelization.py b/tests/test_parallelization.py index 05fa31e..797f74a 100644 --- a/tests/test_parallelization.py +++ b/tests/test_parallelization.py @@ -105,6 +105,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) diff --git a/tests/test_utils.py b/tests/test_utils.py index 3e250eb..33e1ab8 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -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() class WatFile(Enum): From 2f51b3224a0457cfae948e329248e431b67af263 Mon Sep 17 00:00:00 2001 From: Asa Kosto Date: Tue, 6 Feb 2024 19:34:38 -0700 Subject: [PATCH 3/5] misc fixes --- tests/test_utils.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/tests/test_utils.py b/tests/test_utils.py index f807843..2ed0877 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -47,12 +47,9 @@ class QasmFile(Enum): classical_ordering = auto() single_qubit_parallel_test = auto() exec_order_two_qubits = auto() -<<<<<<< HEAD rz_exec_order_three_qubits = auto() -======= cond_barrier = auto() arbitrary_qreg_names = auto() ->>>>>>> main class WatFile(Enum): From 89becb42e1087bda39de2b588ebf375d58b8fc92 Mon Sep 17 00:00:00 2001 From: Asa Kosto Date: Wed, 7 Feb 2024 08:20:29 -0700 Subject: [PATCH 4/5] cleanup and suggestions --- .pre-commit-config.yaml | 4 +-- pytket/phir/phirgen_parallel.py | 44 ++++++++++++++------------------- requirements.txt | 2 +- 3 files changed, 22 insertions(+), 28 deletions(-) diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index bcc44f8..84dc384 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -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 @@ -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] diff --git a/pytket/phir/phirgen_parallel.py b/pytket/phir/phirgen_parallel.py index 99bd0f3..6f19952 100644 --- a/pytket/phir/phirgen_parallel.py +++ b/pytket/phir/phirgen_parallel.py @@ -30,12 +30,12 @@ logger = logging.getLogger(__name__) -def exec_order_preserved_helper( +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 + """A helper to determine whether current group number a qubit is eligible + or is later in execution than the last group in which a qubit was used. + """ # noqa: D205 if (group_number not in group_exec_order) or ( qubit_last_group not in group_exec_order ): @@ -50,7 +50,7 @@ def exec_order_preserved_helper( ) -def process_sub_commands( # noqa: PLR0914 +def process_sub_commands( sub_commands: dict["UnitID", list[tk.Command]], max_parallel_sq_gates: int ) -> dict[int, list[tk.Command]]: """Create parallelizable groups of sub-commands.""" @@ -62,32 +62,29 @@ def process_sub_commands( # noqa: PLR0914 # 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: int = -3 # set to 0 when first RZ gate is assigned (-3 + 3 = 0) - r1xy_group_number: int = ( - -2 # set to 1 when first R1XY gate is assigned (-2 + 3 = 1) - ) - other_group_number: int = ( - -1 # set to 2 when first other gate is assigned (-1 + 3 = 2) - ) + 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 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 + 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: @@ -101,18 +98,15 @@ def process_sub_commands( # noqa: PLR0914 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( - group_exec_order, 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 ): diff --git a/requirements.txt b/requirements.txt index 76c57a8..2f6d840 100644 --- a/requirements.txt +++ b/requirements.txt @@ -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 From d413f700c11b606235a6919e82e8ef2305f10c7d Mon Sep 17 00:00:00 2001 From: Asa Kosto Date: Wed, 7 Feb 2024 10:25:07 -0700 Subject: [PATCH 5/5] more suggestions --- pytket/phir/phirgen_parallel.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/pytket/phir/phirgen_parallel.py b/pytket/phir/phirgen_parallel.py index 6f19952..72d051f 100644 --- a/pytket/phir/phirgen_parallel.py +++ b/pytket/phir/phirgen_parallel.py @@ -33,15 +33,12 @@ def exec_order_preserved( group_exec_order: list[int], group_number: int, qubit_last_group: int ) -> bool: - """A helper to determine whether current group number a qubit is eligible - or is later in execution than the last group in which a qubit was used. - """ # noqa: D205 + """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 - group_eligible = group_number > qubit_last_group - if not group_eligible: + if group_number <= qubit_last_group: return False # 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