From 636b5091169dce6ae7da1ad6d4bdd6ae4dd88f57 Mon Sep 17 00:00:00 2001 From: Asa-Kosto-QTM <108833721+Asa-Kosto-QTM@users.noreply.github.com> Date: Fri, 26 Jan 2024 14:59:44 -0700 Subject: [PATCH] stricter ordering checks (#112) Co-authored-by: Kartik Singhal --- pytket/phir/api.py | 1 + pytket/phir/cli.py | 6 ++--- pytket/phir/phirgen_parallel.py | 26 ++++++++++++++++++++-- tests/data/qasm/exec_order_two_qubits.qasm | 19 ++++++++++++++++ tests/test_parallelization.py | 21 +++++++++++++++++ tests/test_utils.py | 1 + 6 files changed, 69 insertions(+), 5 deletions(-) create mode 100644 tests/data/qasm/exec_order_two_qubits.qasm diff --git a/pytket/phir/api.py b/pytket/phir/api.py index fa4b8ef..f718dc2 100644 --- a/pytket/phir/api.py +++ b/pytket/phir/api.py @@ -69,6 +69,7 @@ def pytket_to_phir(circuit: "Circuit", qtm_machine: QtmMachine | None = None) -> else: phir_json = genphir(placed, machine_ops=bool(machine)) if logger.getEffectiveLevel() <= logging.INFO: + print("PHIR JSON:") print(PHIRModel.model_validate_json(phir_json)) return phir_json diff --git a/pytket/phir/cli.py b/pytket/phir/cli.py index fb57894..4caa5b6 100644 --- a/pytket/phir/cli.py +++ b/pytket/phir/cli.py @@ -9,6 +9,7 @@ # mypy: disable-error-code="misc" # ruff: noqa: T201 +import logging from argparse import ArgumentParser from importlib.metadata import version @@ -70,10 +71,9 @@ def main() -> None: case "H1-2": machine = QtmMachine.H1_2 - phir = pytket_to_phir(circuit, machine) if args.verbose: - print("\nPHIR to be simulated:") - print(phir) + logging.basicConfig(level=logging.INFO) + phir = pytket_to_phir(circuit, machine) print("\nPECOS results:") print( diff --git a/pytket/phir/phirgen_parallel.py b/pytket/phir/phirgen_parallel.py index eeb1984..2d3a08d 100644 --- a/pytket/phir/phirgen_parallel.py +++ b/pytket/phir/phirgen_parallel.py @@ -10,6 +10,7 @@ import json import logging +from collections import OrderedDict from typing import TYPE_CHECKING import pytket.circuit as tk @@ -29,6 +30,25 @@ logger = logging.getLogger(__name__) +def exec_order_preserved_helper( + ordered_dict: OrderedDict["UnitID", 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: + 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 + + def process_sub_commands( sub_commands: dict["UnitID", list[tk.Command]], max_parallel_sq_gates: int ) -> dict[int, list[tk.Command]]: @@ -36,7 +56,7 @@ def process_sub_commands( groups: dict[ int, list[tk.Command] ] = {} # be sure to order by group number into a list when returning - qubits2groups = {} # track the most recent group in which a qubit was used + qubits2groups = OrderedDict() # the most recent group in which a qubit was used # group numbers for each gate are incremented by 3 so they don't overlap # 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, @@ -78,7 +98,9 @@ def process_sub_commands( 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 = group_number > qubits2groups[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 diff --git a/tests/data/qasm/exec_order_two_qubits.qasm b/tests/data/qasm/exec_order_two_qubits.qasm new file mode 100644 index 0000000..c9ede63 --- /dev/null +++ b/tests/data/qasm/exec_order_two_qubits.qasm @@ -0,0 +1,19 @@ +OPENQASM 2.0; +include "qelib1.inc"; +include "hqslib1_dev.inc"; +qreg q[2]; +creg c[2]; +ry(3.5*pi) q[0]; +rx(3.5*pi) q[1]; +ZZ q[0],q[1]; +rx(0.5*pi) q[0]; +barrier q[0],q[1]; +rx(3.5*pi) q[0]; +rz(3.5*pi) q[1]; +rx(1.0*pi) q[1]; +ZZ q[0],q[1]; +ry(0.5*pi) q[0]; +ry(3.5*pi) q[1]; +barrier q[0],q[1]; +measure q[0] -> c[0]; +measure q[1] -> c[1]; diff --git a/tests/test_parallelization.py b/tests/test_parallelization.py index 0cc51e4..05fa31e 100644 --- a/tests/test_parallelization.py +++ b/tests/test_parallelization.py @@ -103,3 +103,24 @@ def test_single_qubit_circuit_with_parallel() -> None: phir_with_parallel_phirgen["ops"][i]["args"] == phir_with_standard_phirgen["ops"][i]["args"] ) + + +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) + # for this test, verify that the RX gates are NOT parallelized + # in the following section of the qasm file (lines 11-13): + # rx(3.5*pi) q[0]; + assert phir["ops"][18] == { + "qop": "R1XY", + "angles": [[3.5, 0.0], "pi"], + "args": [["q", 0]], + } + # rz(3.5*pi) q[1]; + assert phir["ops"][20] == {"qop": "RZ", "angles": [[3.5], "pi"], "args": [["q", 1]]} + # rx(1.0*pi) q[1]; + assert phir["ops"][24] == { + "qop": "R1XY", + "angles": [[1.0, 0.0], "pi"], + "args": [["q", 1]], + } diff --git a/tests/test_utils.py b/tests/test_utils.py index 4c35a60..3e250eb 100644 --- a/tests/test_utils.py +++ b/tests/test_utils.py @@ -46,6 +46,7 @@ class QasmFile(Enum): rxrz = auto() classical_ordering = auto() single_qubit_parallel_test = auto() + exec_order_two_qubits = auto() class WatFile(Enum):