From c51dc79a1e2edbc68f1fa96f72bbf226e1752c15 Mon Sep 17 00:00:00 2001 From: Ciaran Ryan-Anderson Date: Wed, 3 Jan 2024 10:45:58 -0700 Subject: [PATCH 1/6] Fixed buggy yielding of Sequence/QParallel blocks --- .../phir_classical_interpreter.py | 42 +++++++++---------- python/pecos/machines/generic_machine.py | 4 +- tests/integration/phir/bell_qparallel.json | 22 ++++++++++ tests/integration/test_phir.py | 12 ++++++ 4 files changed, 56 insertions(+), 24 deletions(-) create mode 100644 tests/integration/phir/bell_qparallel.json diff --git a/python/pecos/classical_interpreters/phir_classical_interpreter.py b/python/pecos/classical_interpreters/phir_classical_interpreter.py index fb8244da..9029cf1c 100644 --- a/python/pecos/classical_interpreters/phir_classical_interpreter.py +++ b/python/pecos/classical_interpreters/phir_classical_interpreter.py @@ -141,19 +141,36 @@ 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.block.SeqBlock): + yield from self._block_seq(op.ops, op_buffer) + + elif isinstance(op, pt.block.IfBlock): + if self.eval_expr(op.condition): + yield from self._block_seq(op.true_branch, op_buffer) + + elif op.false_branch: + yield from self._block_seq(op.false_branch, op_buffer) + + else: # For case of no false_branch + # self.execute([]) + pass 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: yield op_buffer + def _block_seq(self, seq, op_buffer): + for ops in self.execute(seq): + op_buffer.extend(ops) + yield op_buffer + op_buffer.clear() + def get_cval(self, cvar): cid = self.program.csym2id[cvar] return self.cenv[cid] @@ -279,25 +296,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: diff --git a/python/pecos/machines/generic_machine.py b/python/pecos/machines/generic_machine.py index 5137ab9e..f36dfa5f 100644 --- a/python/pecos/machines/generic_machine.py +++ b/python/pecos/machines/generic_machine.py @@ -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.""" @@ -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)), ] diff --git a/tests/integration/phir/bell_qparallel.json b/tests/integration/phir/bell_qparallel.json new file mode 100644 index 00000000..0bc1b7c8 --- /dev/null +++ b/tests/integration/phir/bell_qparallel.json @@ -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]]} + ] +} diff --git a/tests/integration/test_phir.py b/tests/integration/test_phir.py index aae33b5f..b4cd6384 100644 --- a/tests/integration/test_phir.py +++ b/tests/integration/test_phir.py @@ -260,3 +260,15 @@ def test_qparallel(): m = results["m"] assert m.count("1111") == len(m) + + +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) From 97c7927f1bb2d1ebfe2d197f8a7777f889886425 Mon Sep 17 00:00:00 2001 From: Ciaran Ryan-Anderson Date: Wed, 3 Jan 2024 13:31:24 -0700 Subject: [PATCH 2/6] Add a test of the sequence of op buffers. --- python/pecos/reps/pypmir/op_types.py | 7 ++- tests/integration/test_blocks.py | 77 ++++++++++++++++++++++++++++ 2 files changed, 83 insertions(+), 1 deletion(-) create mode 100644 tests/integration/test_blocks.py diff --git a/python/pecos/reps/pypmir/op_types.py b/python/pecos/reps/pypmir/op_types.py index dcae250d..45cbc1a0 100644 --- a/python/pecos/reps/pypmir/op_types.py +++ b/python/pecos/reps/pypmir/op_types.py @@ -13,6 +13,8 @@ class Op: + """Parent class of operations.""" + def __init__( self, name: str, @@ -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): @@ -61,6 +63,9 @@ def __init__( ) self.angles = angles + def __str__(self): + return self.__repr__() + def __repr__(self): return ( f" Date: Wed, 3 Jan 2024 16:02:08 -0700 Subject: [PATCH 3/6] Reworked recursive iteration through blocks to be more elegant --- .../phir_classical_interpreter.py | 41 ++- python/pecos/engines/hybrid_engine.py | 2 + tests/integration/test_blocks.py | 77 ------ tests/unit/test_blocks.py | 241 ++++++++++++++++++ 4 files changed, 262 insertions(+), 99 deletions(-) delete mode 100644 tests/integration/test_blocks.py create mode 100644 tests/unit/test_blocks.py diff --git a/python/pecos/classical_interpreters/phir_classical_interpreter.py b/python/pecos/classical_interpreters/phir_classical_interpreter.py index 9029cf1c..6d2ced0c 100644 --- a/python/pecos/classical_interpreters/phir_classical_interpreter.py +++ b/python/pecos/classical_interpreters/phir_classical_interpreter.py @@ -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) @@ -141,20 +158,6 @@ def execute(self, sequence: Sequence) -> Generator[list, Any, None]: elif isinstance(op, pt.opt.COp): self.handle_cops(op) - elif isinstance(op, pt.block.SeqBlock): - yield from self._block_seq(op.ops, op_buffer) - - elif isinstance(op, pt.block.IfBlock): - if self.eval_expr(op.condition): - yield from self._block_seq(op.true_branch, op_buffer) - - elif op.false_branch: - yield from self._block_seq(op.false_branch, op_buffer) - - else: # For case of no false_branch - # self.execute([]) - pass - elif isinstance(op, pt.opt.MOp): op_buffer.append(op) @@ -165,12 +168,6 @@ def execute(self, sequence: Sequence) -> Generator[list, Any, None]: if op_buffer: yield op_buffer - def _block_seq(self, seq, op_buffer): - for ops in self.execute(seq): - op_buffer.extend(ops) - yield op_buffer - op_buffer.clear() - def get_cval(self, cvar): cid = self.program.csym2id[cvar] return self.cenv[cid] diff --git a/python/pecos/engines/hybrid_engine.py b/python/pecos/engines/hybrid_engine.py index db5f3408..b80beb6a 100644 --- a/python/pecos/engines/hybrid_engine.py +++ b/python/pecos/engines/hybrid_engine.py @@ -32,6 +32,8 @@ class HybridEngine: + """Engine that runs hybrid quantum/classical programs.""" + def __init__( self, cinterp: ClassicalInterpreter | None = None, diff --git a/tests/integration/test_blocks.py b/tests/integration/test_blocks.py deleted file mode 100644 index d5aa580f..00000000 --- a/tests/integration/test_blocks.py +++ /dev/null @@ -1,77 +0,0 @@ -# Copyright 2023 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. -"""Tests to ensure the sequence of operations are as expected.""" -from pecos.classical_interpreters.phir_classical_interpreter import PHIRClassicalInterpreter - - -def get_seq(program): - """Get the sequences of operations produced by using the PHIR interpreter.""" - interp = PHIRClassicalInterpreter() - interp.init(program) - - ops_seq = [] - for buffered_ops in interp.execute(interp.program.ops): - subseq = [] - - # Create a simple identifier for operations - for op in buffered_ops: - op_ident = [op.name] - if hasattr(op, "metadata") and op.metadata.get("angles") is not None: - op_ident.append(op.metadata["angles"]) - if hasattr(op, "args") and op.args is not None: - op_ident.append(op.args) - if hasattr(op, "returns") and op.returns is not None: - op_ident.append(op.returns) - subseq.append(tuple(op_ident)) - ops_seq.append(subseq) - return ops_seq - - -def test_seq(): - program = { - "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": "sequence", - "ops": [ - {"qop": "R1XY", "angles": [[0.5, 0.5], "pi"], "args": [["q", 0]]}, - {"qop": "Measure", "args": [["q", 0], ["q", 1]], "returns": [["m", 0], ["m", 1]]}, - {"qop": "R1XY", "angles": [[0.5, 0.5], "pi"], "args": [["q", 0]]}, - ], - }, - {"qop": "Measure", "args": [["q", 0], ["q", 1]], "returns": [["m", 0], ["m", 1]]}, - {"qop": "X", "args": [["q", 0]]}, - ], - } - - seq = get_seq(program) - - assert seq == [ - [ - ("RZ", [3.141592653589793], [0, 1]), - ("R1XY", [1.5707963267948966, 1.5707963267948966], [0]), - ("Measure", [0, 1], [["m", 0], ["m", 1]]), - ], - [ - ("R1XY", [1.5707963267948966, 1.5707963267948966], [0]), - ], - [ - ("Measure", [0, 1], [["m", 0], ["m", 1]]), - ], - [ - ("X", [0]), - ], - ] diff --git a/tests/unit/test_blocks.py b/tests/unit/test_blocks.py new file mode 100644 index 00000000..d1fad48d --- /dev/null +++ b/tests/unit/test_blocks.py @@ -0,0 +1,241 @@ +# 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. +"""Tests to ensure the sequence of operations are as expected.""" +from pecos.classical_interpreters.phir_classical_interpreter import PHIRClassicalInterpreter + + +def get_seq(program): + """Get the sequences of operations produced by using the PHIR interpreter.""" + interp = PHIRClassicalInterpreter() + interp.init(program) + + ops_seq = [] + for buffered_ops in interp.execute(interp.program.ops): + subseq = [] + + # Create a simple identifier for operations + for op in buffered_ops: + op_ident = [op.name] + if hasattr(op, "metadata") and op.metadata.get("angles") is not None: + op_ident.append(op.metadata["angles"]) + if hasattr(op, "args") and op.args is not None: + op_ident.append(op.args) + if hasattr(op, "returns") and op.returns is not None: + op_ident.append(op.returns) + subseq.append(tuple(op_ident)) + ops_seq.append(subseq) + return ops_seq + + +def test_seq(): + """Test that the expected sequence of operations is returned with a program using Sequence Blocks.""" + program = { + "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": "sequence", + "ops": [ + {"qop": "R1XY", "angles": [[0.5, 0.5], "pi"], "args": [["q", 0]]}, + {"qop": "Measure", "args": [["q", 0], ["q", 1]], "returns": [["m", 0], ["m", 1]]}, + {"qop": "R1XY", "angles": [[0.5, 0.5], "pi"], "args": [["q", 0]]}, + ], + }, + {"qop": "Measure", "args": [["q", 0], ["q", 1]], "returns": [["m", 0], ["m", 1]]}, + {"qop": "X", "args": [["q", 0]]}, + ], + } + + assert get_seq(program) == [ + [ + ("RZ", [3.141592653589793], [0, 1]), + ("R1XY", [1.5707963267948966, 1.5707963267948966], [0]), + ("Measure", [0, 1], [["m", 0], ["m", 1]]), + ], + [ + ("R1XY", [1.5707963267948966, 1.5707963267948966], [0]), + ("Measure", [0, 1], [["m", 0], ["m", 1]]), + ], + [ + ("X", [0]), + ], + ] + + +def test_qparallel(): + """Test that the expected sequence of operations is returned with a program using Sequence Blocks.""" + program = { + "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": "Measure", "args": [["q", 0], ["q", 1]], "returns": [["m", 0], ["m", 1]]}, + {"qop": "R1XY", "angles": [[0.5, 0.5], "pi"], "args": [["q", 0]]}, + ], + }, + {"qop": "Measure", "args": [["q", 0], ["q", 1]], "returns": [["m", 0], ["m", 1]]}, + {"qop": "X", "args": [["q", 0]]}, + ], + } + + assert get_seq(program) == [ + [ + ("RZ", [3.141592653589793], [0, 1]), + ("R1XY", [1.5707963267948966, 1.5707963267948966], [0]), + ("Measure", [0, 1], [["m", 0], ["m", 1]]), + ], + [ + ("R1XY", [1.5707963267948966, 1.5707963267948966], [0]), + ("Measure", [0, 1], [["m", 0], ["m", 1]]), + ], + [ + ("X", [0]), + ], + ] + + +def test_if_true(): + program = { + "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": "v", "size": 1}, + {"data": "cvar_define", "data_type": "u32", "variable": "m", "size": 2}, + {"cop": "=", "returns": [["v", 0]], "args": [1]}, + {"qop": "H", "angles": None, "args": [["q", 0]]}, + {"qop": "CX", "angles": None, "args": [[["q", 0], ["q", 1]]]}, + {"qop": "Measure", "returns": [["m", 1]], "args": [["q", 1]]}, + { + "block": "if", + "condition": {"cop": "==", "args": [["v", 0], 1]}, + "true_branch": [ + {"qop": "X", "angles": None, "args": [["q", 0]]}, + ], + "false_branch": [ + {"qop": "Y", "angles": None, "args": [["q", 0]]}, + ], + }, + {"qop": "Measure", "returns": [["m", 0]], "args": [["q", 0]]}, + ], + } + + assert get_seq(program) == [ + [ + ("H", [0]), + ("CX", [[0, 1]]), + ("Measure", [1], [["m", 1]]), + ], + [ + ("X", [0]), + ( + "Measure", + [0], + [["m", 0]], + ), + ], + ] + + +def test_if_false(): + program = { + "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": "v", "size": 1}, + {"data": "cvar_define", "data_type": "u32", "variable": "m", "size": 2}, + {"cop": "=", "returns": [["v", 0]], "args": [1]}, + {"qop": "H", "angles": None, "args": [["q", 0]]}, + {"qop": "CX", "angles": None, "args": [[["q", 0], ["q", 1]]]}, + {"qop": "Measure", "returns": [["m", 1]], "args": [["q", 1]]}, + { + "block": "if", + "condition": {"cop": "==", "args": [["v", 0], 0]}, + "true_branch": [ + {"qop": "X", "angles": None, "args": [["q", 0]]}, + ], + "false_branch": [ + {"qop": "Y", "angles": None, "args": [["q", 0]]}, + ], + }, + {"qop": "Measure", "returns": [["m", 0]], "args": [["q", 0]]}, + ], + } + + assert get_seq(program) == [ + [ + ("H", [0]), + ("CX", [[0, 1]]), + ("Measure", [1], [["m", 1]]), + ], + [ + ("Y", [0]), + ( + "Measure", + [0], + [["m", 0]], + ), + ], + ] + + +def test_if_no_false(): + program = { + "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": "v", "size": 1}, + {"data": "cvar_define", "data_type": "u32", "variable": "m", "size": 2}, + {"cop": "=", "returns": [["v", 0]], "args": [1]}, + {"qop": "H", "angles": None, "args": [["q", 0]]}, + {"qop": "CX", "angles": None, "args": [[["q", 0], ["q", 1]]]}, + {"qop": "Measure", "returns": [["m", 1]], "args": [["q", 1]]}, + { + "block": "if", + "condition": {"cop": "==", "args": [["v", 0], 0]}, + "true_branch": [ + {"qop": "X", "angles": None, "args": [["q", 0]]}, + ], + }, + {"qop": "Measure", "returns": [["m", 0]], "args": [["q", 0]]}, + ], + } + + assert get_seq(program) == [ + [ + ("H", [0]), + ("CX", [[0, 1]]), + ("Measure", [1], [["m", 1]]), + ], + [ + ( + "Measure", + [0], + [["m", 0]], + ), + ], + ] From b3829b4e454029642ab674577f76db1b2a8d3dc0 Mon Sep 17 00:00:00 2001 From: Ciaran Ryan-Anderson Date: Wed, 3 Jan 2024 17:06:15 -0700 Subject: [PATCH 4/6] Marked test using state-vec sim as optional dependency --- tests/integration/test_phir.py | 1 + 1 file changed, 1 insertion(+) diff --git a/tests/integration/test_phir.py b/tests/integration/test_phir.py index b4cd6384..8a0e31bd 100644 --- a/tests/integration/test_phir.py +++ b/tests/integration/test_phir.py @@ -262,6 +262,7 @@ def test_qparallel(): 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.""" From 2d70dc2b15121d7188d35a898561e3bc7b640403 Mon Sep 17 00:00:00 2001 From: Ciaran Ryan-Anderson Date: Wed, 3 Jan 2024 17:32:28 -0700 Subject: [PATCH 5/6] Added Clifford Bell qparallel test and silence tz warning --- pyproject.toml | 3 --- pytest.ini | 1 + .../phir/bell_qparallel_cliff.json | 22 +++++++++++++++++++ tests/integration/test_phir.py | 13 +++++++++++ 4 files changed, 36 insertions(+), 3 deletions(-) create mode 100644 tests/integration/phir/bell_qparallel_cliff.json diff --git a/pyproject.toml b/pyproject.toml index 07debada..5a80dc68 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -99,9 +99,6 @@ where = ["python"] include = ["pecos*"] namespaces = true -[tool.pytest.ini_options] -filterwarnings = ["ignore:::dateutil.tz.tz.*"] - # Linting and autorefactoring tools # --------------------------------- diff --git a/pytest.ini b/pytest.ini index 37c8f463..7a24d31d 100644 --- a/pytest.ini +++ b/pytest.ini @@ -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.* diff --git a/tests/integration/phir/bell_qparallel_cliff.json b/tests/integration/phir/bell_qparallel_cliff.json new file mode 100644 index 00000000..c0e2386a --- /dev/null +++ b/tests/integration/phir/bell_qparallel_cliff.json @@ -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]]} + ] +} diff --git a/tests/integration/test_phir.py b/tests/integration/test_phir.py index 8a0e31bd..07eba624 100644 --- a/tests/integration/test_phir.py +++ b/tests/integration/test_phir.py @@ -273,3 +273,16 @@ def test_bell_qparallel(): 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) From 8738b58b28dbe7166c345a091b1bfff0c017e8f8 Mon Sep 17 00:00:00 2001 From: Ciaran Ryan-Anderson Date: Wed, 3 Jan 2024 17:46:55 -0700 Subject: [PATCH 6/6] Bumping version to 0.5.0.dev5 and updating requirements --- pyproject.toml | 2 +- requirements.txt | 12 ++++++------ tests/end2end/__init__.py | 11 +++++++++++ 3 files changed, 18 insertions(+), 7 deletions(-) create mode 100644 tests/end2end/__init__.py diff --git a/pyproject.toml b/pyproject.toml index 5a80dc68..573a03e2 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -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"}, ] diff --git a/requirements.txt b/requirements.txt index d2c16a29..5e5c3bd1 100644 --- a/requirements.txt +++ b/requirements.txt @@ -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 diff --git a/tests/end2end/__init__.py b/tests/end2end/__init__.py new file mode 100644 index 00000000..d383878a --- /dev/null +++ b/tests/end2end/__init__.py @@ -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"""