Skip to content

Commit

Permalink
Prevent parallel blocks in 1-qubit circuits (#109)
Browse files Browse the repository at this point in the history
Co-authored-by: Kartik Singhal <kartik.singhal@quantinuum.com>

Fixes #106
  • Loading branch information
Asa-Kosto-QTM authored Jan 25, 2024
1 parent b3f8f34 commit 13a18e3
Show file tree
Hide file tree
Showing 7 changed files with 186 additions and 116 deletions.
3 changes: 2 additions & 1 deletion pytket/phir/api.py
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,8 @@ def pytket_to_phir(
# The function is called, but the output is just filled with 0s
logger.debug("Performing placement and routing...")
placed = place_and_route(shards, machine)
if machine:
# safety check: never run with parallelization on a 1 qubit circuit
if machine and len(circuit.qubits) > 1:
phir_json = genphir_parallel(placed, machine)
else:
phir_json = genphir(placed, machine_ops=bool(machine))
Expand Down
4 changes: 3 additions & 1 deletion pytket/phir/phirgen_parallel.py
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,9 @@ def process_sub_commands(
other_group_number += 3
group_number = other_group_number
groups[group_number] = [sc]
qubits2groups[qubit] = group_number
# prevent the group number from ever decrementing
if group_number > qubits2groups[qubit]:
qubits2groups[qubit] = group_number

return dict(groups.items())

Expand Down
15 changes: 15 additions & 0 deletions tests/data/qasm/single_qubit_parallel_test.qasm
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
OPENQASM 2.0;
include "qelib1.inc";
include "hqslib1_dev.inc";

qreg q[1];
creg c[1];
ry(3.5*pi) q[0];
rz(0.5*pi) q[0];
ry(3.5*pi) q[0];
barrier q[0];
ry(3.5*pi) q[0];
rz(3.5*pi) q[0];
ry(0.5*pi) q[0];
barrier q[0];
measure q[0] -> c[0];
115 changes: 1 addition & 114 deletions tests/test_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,16 @@

# mypy: disable-error-code="misc"

import base64
import hashlib
import json
import logging
from pathlib import Path
from tempfile import NamedTemporaryFile

import pytest

from pytket.circuit import Bit, Circuit
from pytket.phir.api import pytket_to_phir, qasm_to_phir
from pytket.phir.qtm_machine import QtmMachine
from pytket.wasm.wasm import WasmFileHandler

from .test_utils import QasmFile, WatFile, get_qasm_as_circuit, get_wat_as_wasm_bytes
from .test_utils import QasmFile, get_qasm_as_circuit

logger = logging.getLogger(__name__)

Expand Down Expand Up @@ -91,111 +86,3 @@ def test_qasm_to_phir(self) -> None:
"""

assert qasm_to_phir(qasm, QtmMachine.H1_1)

def test_qasm_to_phir_with_wasm(self) -> None:
"""Test the qasm string entrypoint works with WASM."""
qasm = """
OPENQASM 2.0;
include "qelib1.inc";
qreg q[2];
h q;
ZZ q[1],q[0];
creg cr[3];
creg cs[3];
creg co[3];
measure q[0]->cr[0];
measure q[1]->cr[1];
cs = cr;
co = add(cr, cs);
"""

wasm_bytes = get_wat_as_wasm_bytes(WatFile.add)

wasm_uid = hashlib.sha256(base64.b64encode(wasm_bytes)).hexdigest()

phir_str = qasm_to_phir(qasm, QtmMachine.H1_1, wasm_bytes=wasm_bytes)
phir = json.loads(phir_str)

expected_metadata = {"ff_object": (f"WASM module uid: {wasm_uid}")}

assert phir["ops"][21] == {
"metadata": expected_metadata,
"cop": "ffcall",
"function": "add",
"args": ["cr", "cs"],
"returns": ["co"],
}

def test_pytket_with_wasm(self) -> None:
wasm_bytes = get_wat_as_wasm_bytes(WatFile.testfile)
phir_str: str
try:
wasm_file = NamedTemporaryFile(suffix=".wasm", delete=False)
wasm_file.write(wasm_bytes)
wasm_file.flush()
wasm_file.close()

w = WasmFileHandler(wasm_file.name)

c = Circuit(6, 6)
c0 = c.add_c_register("c0", 3)
c1 = c.add_c_register("c1", 4)
c2 = c.add_c_register("c2", 5)

c.add_wasm_to_reg("multi", w, [c0, c1], [c2])
c.add_wasm_to_reg("add_one", w, [c2], [c2])
c.add_wasm_to_reg("no_return", w, [c2], [])
c.add_wasm_to_reg("no_parameters", w, [], [c2])

c.add_wasm_to_reg("add_one", w, [c0], [c0], condition=c1[0])

phir_str = pytket_to_phir(c, QtmMachine.H1_1)
finally:
Path.unlink(Path(wasm_file.name))

phir = json.loads(phir_str)

expected_metadata = {"ff_object": (f"WASM module uid: {w!s}")}

assert phir["ops"][4] == {
"metadata": expected_metadata,
"cop": "ffcall",
"function": "multi",
"args": ["c0", "c1"],
"returns": ["c2"],
}
assert phir["ops"][7] == {
"metadata": expected_metadata,
"cop": "ffcall",
"function": "add_one",
"args": ["c2"],
"returns": ["c2"],
}
assert phir["ops"][9] == {
"block": "if",
"condition": {"cop": "==", "args": [["c1", 0], 1]},
"true_branch": [
{
"metadata": expected_metadata,
"cop": "ffcall",
"returns": ["c0"],
"function": "add_one",
"args": ["c1", "c0"],
}
],
}
assert phir["ops"][12] == {
"metadata": expected_metadata,
"cop": "ffcall",
"function": "no_return",
"args": ["c2"],
}
assert phir["ops"][15] == {
"metadata": expected_metadata,
"cop": "ffcall",
"function": "no_parameters",
"args": [],
"returns": ["c2"],
}
29 changes: 29 additions & 0 deletions tests/test_parallelization.py
Original file line number Diff line number Diff line change
Expand Up @@ -74,3 +74,32 @@ def test_parallel_subcommand_relative_ordering() -> None:
assert thrd_sc["angles"] == [[0.5, 0.0], "pi"]
assert frth_sc["qop"] == "RZ"
assert frth_sc["angles"] == [[3.5], "pi"]


def test_single_qubit_circuit_with_parallel() -> None:
"""Make sure there are no parallel blocks present in the 1qubit circuit."""
phir_with_parallel_phirgen = get_phir_json(
QasmFile.single_qubit_parallel_test, rebase=True
)
phir_with_standard_phirgen = get_phir_json(
QasmFile.single_qubit_parallel_test, rebase=False
)
assert len(phir_with_parallel_phirgen) == len(phir_with_standard_phirgen)
# since the rebasing converts to the native gate set,
# the names and angle foramts of the qops will not match.
# for example Ry gets converted to R1XY
# compare angles and args instead

ops = (3, 5, 7, 11, 13, 15)

for i, qop in zip(ops, ("R1XY", "RZ", "R1XY", "R1XY", "RZ", "R1XY"), strict=True):
assert phir_with_parallel_phirgen["ops"][i]["qop"] == qop

for i, qop in zip(ops, ("RY", "RZ", "RY", "RY", "RZ", "RY"), strict=True):
assert phir_with_standard_phirgen["ops"][i]["qop"] == qop

for i in ops:
assert (
phir_with_parallel_phirgen["ops"][i]["args"]
== phir_with_standard_phirgen["ops"][i]["args"]
)
1 change: 1 addition & 0 deletions tests/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ class QasmFile(Enum):
tk2_diff_angles = auto()
rxrz = auto()
classical_ordering = auto()
single_qubit_parallel_test = auto()


class WatFile(Enum):
Expand Down
135 changes: 135 additions & 0 deletions tests/test_wasm.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
##############################################################################
#
# Copyright (c) 2024 Quantinuum LLC All rights reserved.
# Use of this source code is governed by a BSD-style
# license that can be found in the LICENSE file.
#
##############################################################################

# mypy: disable-error-code="misc"

import base64
import hashlib
import json
import logging
from pathlib import Path
from tempfile import NamedTemporaryFile

from pytket.circuit import Circuit
from pytket.phir.api import pytket_to_phir, qasm_to_phir
from pytket.phir.qtm_machine import QtmMachine
from pytket.wasm.wasm import WasmFileHandler

from .test_utils import WatFile, get_wat_as_wasm_bytes

logger = logging.getLogger(__name__)


class TestWASM:
def test_qasm_to_phir_with_wasm(self) -> None:
"""Test the qasm string entrypoint works with WASM."""
qasm = """
OPENQASM 2.0;
include "qelib1.inc";
qreg q[2];
h q;
ZZ q[1],q[0];
creg cr[3];
creg cs[3];
creg co[3];
measure q[0]->cr[0];
measure q[1]->cr[1];
cs = cr;
co = add(cr, cs);
"""

wasm_bytes = get_wat_as_wasm_bytes(WatFile.add)

wasm_uid = hashlib.sha256(base64.b64encode(wasm_bytes)).hexdigest()

phir_str = qasm_to_phir(qasm, QtmMachine.H1_1, wasm_bytes=wasm_bytes)
phir = json.loads(phir_str)

expected_metadata = {"ff_object": (f"WASM module uid: {wasm_uid}")}

assert phir["ops"][21] == {
"metadata": expected_metadata,
"cop": "ffcall",
"function": "add",
"args": ["cr", "cs"],
"returns": ["co"],
}

def test_pytket_with_wasm(self) -> None:
wasm_bytes = get_wat_as_wasm_bytes(WatFile.testfile)
phir_str: str
try:
wasm_file = NamedTemporaryFile(suffix=".wasm", delete=False)
wasm_file.write(wasm_bytes)
wasm_file.flush()
wasm_file.close()

w = WasmFileHandler(wasm_file.name)

c = Circuit(6, 6)
c0 = c.add_c_register("c0", 3)
c1 = c.add_c_register("c1", 4)
c2 = c.add_c_register("c2", 5)

c.add_wasm_to_reg("multi", w, [c0, c1], [c2])
c.add_wasm_to_reg("add_one", w, [c2], [c2])
c.add_wasm_to_reg("no_return", w, [c2], [])
c.add_wasm_to_reg("no_parameters", w, [], [c2])

c.add_wasm_to_reg("add_one", w, [c0], [c0], condition=c1[0])

phir_str = pytket_to_phir(c, QtmMachine.H1_1)
finally:
Path.unlink(Path(wasm_file.name))

phir = json.loads(phir_str)

expected_metadata = {"ff_object": (f"WASM module uid: {w!s}")}

assert phir["ops"][4] == {
"metadata": expected_metadata,
"cop": "ffcall",
"function": "multi",
"args": ["c0", "c1"],
"returns": ["c2"],
}
assert phir["ops"][7] == {
"metadata": expected_metadata,
"cop": "ffcall",
"function": "add_one",
"args": ["c2"],
"returns": ["c2"],
}
assert phir["ops"][9] == {
"block": "if",
"condition": {"cop": "==", "args": [["c1", 0], 1]},
"true_branch": [
{
"metadata": expected_metadata,
"cop": "ffcall",
"returns": ["c0"],
"function": "add_one",
"args": ["c1", "c0"],
}
],
}
assert phir["ops"][12] == {
"metadata": expected_metadata,
"cop": "ffcall",
"function": "no_return",
"args": ["c2"],
}
assert phir["ops"][15] == {
"metadata": expected_metadata,
"cop": "ffcall",
"function": "no_parameters",
"args": [],
"returns": ["c2"],
}

0 comments on commit 13a18e3

Please sign in to comment.