Skip to content

Commit

Permalink
Bug out of order seq (#35)
Browse files Browse the repository at this point in the history
* Fixed buggy yielding of Sequence/QParallel blocks

* Add a test of the sequence of op buffers.

* Reworked recursive iteration through blocks to be more elegant

* Marked test using state-vec sim as optional dependency

* Added Clifford Bell qparallel test and silence tz warning

* Bumping version to 0.5.0.dev5 and updating requirements
  • Loading branch information
qciaran committed Jan 4, 2024
1 parent e98abf3 commit a40d0a7
Show file tree
Hide file tree
Showing 12 changed files with 360 additions and 38 deletions.
5 changes: 1 addition & 4 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ build-backend = "setuptools.build_meta"

[project]
name = "quantum-pecos"
version = "0.5.0.dev4"
version = "0.5.0.dev5"
authors = [
{name = "The PECOS Developers"},
]
Expand Down Expand Up @@ -99,9 +99,6 @@ where = ["python"]
include = ["pecos*"]
namespaces = true

[tool.pytest.ini_options]
filterwarnings = ["ignore:::dateutil.tz.tz.*"]

# Linting and autorefactoring tools
# ---------------------------------

Expand Down
1 change: 1 addition & 0 deletions pytest.ini
Original file line number Diff line number Diff line change
Expand Up @@ -19,3 +19,4 @@ markers =
# TODO: comment this to deal with ProjectQ gate warnings
filterwarnings =
ignore::PendingDeprecationWarning:projectq.ops._gates
ignore::DeprecationWarning:dateutil.tz.tz.*
45 changes: 20 additions & 25 deletions python/pecos/classical_interpreters/phir_classical_interpreter.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,12 +125,29 @@ def initialize_cenv(self) -> None:
self.cenv.append(dtype(0))
self.cid2dtype.append(dtype)

def execute(self, sequence: Sequence) -> Generator[list, Any, None]:
def _flatten_blocks(self, seq: Sequence):
"""Flattens the ops of blocks to be processed by the execute() method."""
for op in seq:
if isinstance(op, pt.block.SeqBlock):
yield from self._flatten_blocks(op.ops)

elif isinstance(op, pt.block.IfBlock):
if self.eval_expr(op.condition):
yield from self._flatten_blocks(op.true_branch)
elif op.false_branch:
yield from self._flatten_blocks(op.false_branch)
else: # For case of no false_branch (no else)
pass

else:
yield op

def execute(self, seq: Sequence) -> Generator[list, Any, None]:
"""A generator that runs through and executes classical logic and yields other operations via a buffer."""

op_buffer = []

for op in sequence:
for op in self._flatten_blocks(seq):
if isinstance(op, pt.opt.QOp):
op_buffer.append(op)

Expand All @@ -141,14 +158,11 @@ def execute(self, sequence: Sequence) -> Generator[list, Any, None]:
elif isinstance(op, pt.opt.COp):
self.handle_cops(op)

elif isinstance(op, pt.block.Block):
yield from self.execute_block(op)

elif isinstance(op, pt.opt.MOp):
op_buffer.append(op)

else:
msg = f"Statement not recognized: {op}"
msg = f"Statement not recognized: {op} of type: {type(op)}"
raise TypeError(msg)

if op_buffer:
Expand Down Expand Up @@ -279,25 +293,6 @@ def handle_cops(self, op):
msg = f"Unsupported COp: {op}"
raise Exception(msg)

def execute_block(self, op):
"""Execute a block of ops."""
if isinstance(op, pt.block.IfBlock):
if self.eval_expr(op.condition):
yield from self.execute(op.true_branch)

elif op.false_branch:
yield from self.execute(op.false_branch)

else:
yield from self.execute([])

elif isinstance(op, pt.block.SeqBlock):
yield from self.execute(op.ops)

else:
msg = f"block not implemented! {op}"
raise NotImplementedError(msg)

def receive_results(self, qsim_results: list[dict]):
"""Receive measurement results and assign as needed."""
for meas in qsim_results:
Expand Down
2 changes: 2 additions & 0 deletions python/pecos/engines/hybrid_engine.py
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,8 @@


class HybridEngine:
"""Engine that runs hybrid quantum/classical programs."""

def __init__(
self,
cinterp: ClassicalInterpreter | None = None,
Expand Down
4 changes: 2 additions & 2 deletions python/pecos/machines/generic_machine.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,7 @@ def process(self, op_buffer: list[QOp | MOp]) -> list:
def leak(self, qubits: set) -> list[QOp]:
"""Starts tracking qubits as leaked qubits and calls the quantum simulation appropriately to trigger leakage."""
self.leaked_qubits |= qubits
return [QOp(name="Init", args=list(qubits), metadata={})]
return [QOp(name="Init", args=list(qubits))]

def unleak(self, qubits: set) -> None:
"""Untrack qubits as leaked qubits and calls the quantum simulation appropriately to trigger leakage."""
Expand All @@ -58,5 +58,5 @@ def unleak(self, qubits: set) -> None:
def meas_leaked(self, qubits: set) -> list[QOp]:
self.leaked_qubits -= qubits
return [
QOp(name="Init -Z", args=list(qubits), metadata={}),
QOp(name="Init -Z", args=list(qubits)),
]
7 changes: 6 additions & 1 deletion python/pecos/reps/pypmir/op_types.py
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@


class Op:
"""Parent class of operations."""

def __init__(
self,
name: str,
Expand All @@ -39,7 +41,7 @@ def __init__(
raise TypeError(msg)

def __str__(self) -> str:
return f"{self.name}, {self.args}, {self.returns}, {self.metadata}, {self.angles}"
return f"{self.name}, {self.args}, {self.returns}, {self.metadata}"


class QOp(Op):
Expand All @@ -61,6 +63,9 @@ def __init__(
)
self.angles = angles

def __str__(self):
return self.__repr__()

def __repr__(self):
return (
f"<QOP: {self.name} angles: {self.angles} args: {self.args} returns: {self.returns} "
Expand Down
12 changes: 6 additions & 6 deletions requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,23 +8,23 @@ annotated-types==0.6.0
colorama==0.4.6
contourpy==1.2.0
cycler==0.12.1
fonttools==4.46.0
fonttools==4.47.0
iniconfig==2.0.0
kiwisolver==1.4.5
markdown-it-py==3.0.0
matplotlib==3.8.2
mdurl==0.1.2
networkx==2.8.8
numpy==1.26.2
numpy==1.26.3
packaging==23.2
phir==0.2.1
pillow==10.1.0
pillow==10.2.0
pluggy==1.3.0
pydantic==2.5.2
pydantic-core==2.14.5
pydantic==2.5.3
pydantic-core==2.14.6
pygments==2.17.2
pyparsing==3.1.1
pytest==7.4.3
pytest==7.4.4
python-dateutil==2.8.2
rich==13.7.0
scipy==1.11.4
Expand Down
11 changes: 11 additions & 0 deletions tests/end2end/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# Copyright 2024 The PECOS developers
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with
# the License.You may obtain a copy of the License at
#
# https://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
# specific language governing permissions and limitations under the License.
"""End-to-end tests"""
22 changes: 22 additions & 0 deletions tests/integration/phir/bell_qparallel.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"format": "PHIR/JSON",
"version": "0.1.0",
"metadata": {"source": "pytket-phir v0.2.0", "strict_parallelism": "true"},
"ops": [
{"data": "qvar_define", "data_type": "qubits", "variable": "q", "size": 2},
{"data": "cvar_define", "data_type": "u32", "variable": "m", "size": 2},
{"qop": "RZ", "angles": [[1.0], "pi"], "args": [["q", 0], ["q", 1]]},
{"block": "qparallel", "ops": [
{"qop": "R1XY", "angles": [[0.5, 0.5], "pi"], "args": [["q", 0]]},
{"qop": "R1XY", "angles": [[1.5, 0.5], "pi"], "args": [["q", 1]]}
]},
{"qop": "RZ", "angles": [[1.0], "pi"], "args": [["q", 0]]},
{"qop": "RZZ", "angles": [[0.5], "pi"], "args": [[["q", 0], ["q", 1]]]},
{"block": "qparallel", "ops": [
{"qop": "RZ", "angles": [[1.5], "pi"], "args": [["q", 0]]},
{"qop": "RZ", "angles": [[0.5], "pi"], "args": [["q", 1]]}
]},
{"qop": "R1XY", "angles": [[1.5, 0.5], "pi"], "args": [["q", 1]]},
{"qop": "Measure", "args": [["q", 0], ["q", 1]], "returns": [["m", 0], ["m", 1]]}
]
}
22 changes: 22 additions & 0 deletions tests/integration/phir/bell_qparallel_cliff.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
{
"format": "PHIR/JSON",
"version": "0.1.0",
"metadata": {"source": "pytket-phir v0.2.0", "strict_parallelism": "true"},
"ops": [
{"data": "qvar_define", "data_type": "qubits", "variable": "q", "size": 2},
{"data": "cvar_define", "data_type": "u32", "variable": "m", "size": 2},
{"qop": "Z", "args": [["q", 0], ["q", 1]]},
{"block": "qparallel", "ops": [
{"qop": "SY", "args": [["q", 0]]},
{"qop": "SYdg", "args": [["q", 1]]}
]},
{"qop": "Z", "args": [["q", 0]]},
{"qop": "SZZ", "args": [[["q", 0], ["q", 1]]]},
{"block": "qparallel", "ops": [
{"qop": "SZdg", "args": [["q", 0]]},
{"qop": "SZ", "args": [["q", 1]]}
]},
{"qop": "SYdg", "args": [["q", 1]]},
{"qop": "Measure", "args": [["q", 0], ["q", 1]], "returns": [["m", 0], ["m", 1]]}
]
}
26 changes: 26 additions & 0 deletions tests/integration/test_phir.py
Original file line number Diff line number Diff line change
Expand Up @@ -260,3 +260,29 @@ def test_qparallel():

m = results["m"]
assert m.count("1111") == len(m)


@pytest.mark.optional_dependency() # uses projectq / state-vector
def test_bell_qparallel():
"""Testing a program creating and measuring a Bell state and using qparallel blocks returns expected results."""

results = HybridEngine(qsim="state-vector").run(
program=json.load(Path.open(this_dir / "phir" / "bell_qparallel.json")),
shots=20,
)

m = results["m"]
assert m.count("00") + m.count("11") == len(m)


def test_bell_qparallel_cliff():
"""Testing a program creating and measuring a Bell state and using qparallel blocks returns expected results (with
Clifford circuits and stabilizer sim)."""

results = HybridEngine(qsim="stabilizer").run(
program=json.load(Path.open(this_dir / "phir" / "bell_qparallel_cliff.json")),
shots=20,
)

m = results["m"]
assert m.count("00") + m.count("11") == len(m)
Loading

0 comments on commit a40d0a7

Please sign in to comment.