From a40d0a7a9a53ceeefc6ed2e4afd96918e2f81b2d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ciar=C3=A1n=20Ryan-Anderson?= <70174051+qciaran@users.noreply.github.com> Date: Wed, 3 Jan 2024 17:53:45 -0700 Subject: [PATCH] Bug out of order seq (#35) * 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 --- pyproject.toml | 5 +- pytest.ini | 1 + .../phir_classical_interpreter.py | 45 ++-- python/pecos/engines/hybrid_engine.py | 2 + python/pecos/machines/generic_machine.py | 4 +- python/pecos/reps/pypmir/op_types.py | 7 +- requirements.txt | 12 +- tests/end2end/__init__.py | 11 + tests/integration/phir/bell_qparallel.json | 22 ++ .../phir/bell_qparallel_cliff.json | 22 ++ tests/integration/test_phir.py | 26 ++ tests/unit/test_blocks.py | 241 ++++++++++++++++++ 12 files changed, 360 insertions(+), 38 deletions(-) create mode 100644 tests/end2end/__init__.py create mode 100644 tests/integration/phir/bell_qparallel.json create mode 100644 tests/integration/phir/bell_qparallel_cliff.json create mode 100644 tests/unit/test_blocks.py diff --git a/pyproject.toml b/pyproject.toml index 07debada..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"}, ] @@ -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/python/pecos/classical_interpreters/phir_classical_interpreter.py b/python/pecos/classical_interpreters/phir_classical_interpreter.py index fb8244da..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,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: @@ -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: 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/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/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"