From 54dafc822fc31d78ef80b3f47e3c40b589eafa93 Mon Sep 17 00:00:00 2001 From: Mathias Weiden Date: Tue, 17 Oct 2023 13:51:38 -0700 Subject: [PATCH 1/3] pass down foreach --- bqskit/passes/control/foreach.py | 9 +++++++ tests/passes/control/test_foreachblock.py | 33 +++++++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/bqskit/passes/control/foreach.py b/bqskit/passes/control/foreach.py index 8c3c9da02..7eb7b69ad 100644 --- a/bqskit/passes/control/foreach.py +++ b/bqskit/passes/control/foreach.py @@ -38,6 +38,11 @@ class ForEachBlockPass(BasePass): pass_down_key_prefix = 'ForEachBlockPass_pass_down_' """If a key exists in the pass data with this prefix, pass it to blocks.""" + pass_down_block_specific_key_prefix = ( + 'ForEachBlockPass_specific_pass_down_' + ) + """Key for injecting a block specific pass data.""" + def __init__( self, loop_body: WorkflowLike, @@ -197,6 +202,10 @@ async def run(self, circuit: Circuit, data: PassData) -> None: for key in data: if key.startswith(self.pass_down_key_prefix): block_data[key] = data[key] + elif key.startswith( + self.pass_down_block_specific_key_prefix, + ) and i in data[key]: + block_data[key] = data[key][i] block_data.seed = data.seed subcircuits.append(subcircuit) diff --git a/tests/passes/control/test_foreachblock.py b/tests/passes/control/test_foreachblock.py index 605392b85..23526adfd 100644 --- a/tests/passes/control/test_foreachblock.py +++ b/tests/passes/control/test_foreachblock.py @@ -5,8 +5,10 @@ from bqskit.compiler.basepass import BasePass from bqskit.compiler.compiler import Compiler from bqskit.compiler.passdata import PassData +from bqskit.compiler.workflow import Workflow from bqskit.ir.circuit import Circuit from bqskit.ir.gates import CircuitGate +from bqskit.ir.gates import CNOTGate from bqskit.ir.gates import HGate from bqskit.ir.gates import XGate from bqskit.ir.gates import YGate @@ -14,6 +16,9 @@ from bqskit.ir.operation import Operation from bqskit.passes import UnfoldPass from bqskit.passes.control.foreach import ForEachBlockPass +from bqskit.passes.partitioning.quick import QuickPartitioner +from bqskit.passes.synthesis.qsearch import QSearchSynthesisPass +from bqskit.passes.util.update import UpdateDataPass @pytest.fixture @@ -130,3 +135,31 @@ def test_no_hang_on_empty_collection(compiler: Compiler) -> None: circuit.append_gate(XGate(), 0) feb_pass = ForEachBlockPass(RemoveXGatePass(), collection_filter=empty_coll) compiler.compile(circuit, feb_pass) + + +def test_pass_down_seeds(compiler: Compiler) -> None: + circuit = Circuit(3) + circuit.append_gate(CNOTGate(), (0, 1)) + circuit.append_gate(CNOTGate(), (1, 2)) + + seed = Circuit(3) + seed.append_gate(CNOTGate(), (0, 1)) + seed.append_gate(CNOTGate(), (1, 2)) + + # Manually sed seed for block 0 + seeds = {0: [seed]} + + key = 'ForEachBlockPass_specific_pass_down_seed_circuits' + + partitioner = QuickPartitioner() + updater = UpdateDataPass(key, seeds) + qsearch = QSearchSynthesisPass() + foreach = ForEachBlockPass(qsearch) + unfolder = UnfoldPass() + workflow = Workflow([partitioner, updater, foreach, unfolder]) + compiled, data = compiler.compile( + circuit, workflow, request_data=True, + ) + dist = compiled.get_unitary().get_distance_from(circuit.get_unitary()) + assert dist <= 1e-5 + assert key in data From 5e1319f3906e24d0e78bde32985fa4fadd7031b5 Mon Sep 17 00:00:00 2001 From: Mathias Weiden Date: Tue, 17 Oct 2023 15:42:25 -0700 Subject: [PATCH 2/3] changed doc in foreach, more checks in foreach test --- bqskit/passes/control/foreach.py | 15 +++++++++- tests/passes/control/test_foreachblock.py | 36 ++++++++++++++++------- 2 files changed, 40 insertions(+), 11 deletions(-) diff --git a/bqskit/passes/control/foreach.py b/bqskit/passes/control/foreach.py index 7eb7b69ad..5533d4639 100644 --- a/bqskit/passes/control/foreach.py +++ b/bqskit/passes/control/foreach.py @@ -41,7 +41,20 @@ class ForEachBlockPass(BasePass): pass_down_block_specific_key_prefix = ( 'ForEachBlockPass_specific_pass_down_' ) - """Key for injecting a block specific pass data.""" + """ + Data specific to the processing of individual blocks in a partitioned + circuit can be injected into the `PassData` in `run` by using this prefix. + + The expected type of the associated value is `dict[int, Any]`, where + integer (sub-)keys correspond to block numbers in a partitioned quantum + circuit. + + Pseudocode example for seed circuits: + seeds = {block_id: [seed_circuit_a, seed_circuit_b, ...], ...} + key = self.pass_down_block_specific_key_prefix + 'seed_circuits' + seed_updater = UpdateDataPass(key, seeds) + workflow = Workflow([..., seed_updater, ForEachBlockPass(...), ...]) + """ def __init__( self, diff --git a/tests/passes/control/test_foreachblock.py b/tests/passes/control/test_foreachblock.py index 23526adfd..a0578bcfb 100644 --- a/tests/passes/control/test_foreachblock.py +++ b/tests/passes/control/test_foreachblock.py @@ -138,28 +138,44 @@ def test_no_hang_on_empty_collection(compiler: Compiler) -> None: def test_pass_down_seeds(compiler: Compiler) -> None: - circuit = Circuit(3) - circuit.append_gate(CNOTGate(), (0, 1)) + circuit = Circuit(4) + circuit.append_gate(CNOTGate(), (0, 2)) circuit.append_gate(CNOTGate(), (1, 2)) + circuit.append_gate(CNOTGate(), (1, 3)) + circuit.append_gate(CNOTGate(), (2, 3)) seed = Circuit(3) - seed.append_gate(CNOTGate(), (0, 1)) + seed.append_gate(CNOTGate(), (0, 2)) seed.append_gate(CNOTGate(), (1, 2)) - # Manually sed seed for block 0 - seeds = {0: [seed]} - - key = 'ForEachBlockPass_specific_pass_down_seed_circuits' + # Manually set seed for blocks 0 and 1 + seeds = {0: [seed], 1: [seed]} partitioner = QuickPartitioner() - updater = UpdateDataPass(key, seeds) qsearch = QSearchSynthesisPass() foreach = ForEachBlockPass(qsearch) unfolder = UnfoldPass() - workflow = Workflow([partitioner, updater, foreach, unfolder]) + + key = foreach.pass_down_block_specific_key_prefix + 'seed_circuits' + updater = UpdateDataPass(key, seeds) + + # For checking specific pass down data exists + workflow_1 = Workflow([partitioner, updater]) + # For checking specific pass down data can be used + workflow_2 = Workflow([partitioner, updater, foreach, unfolder]) + + # Check existence of block specific keys + partitioned, data = compiler.compile( + circuit, workflow_1, request_data=True, + ) + for i, block in enumerate(partitioned): + data[key][i] == seeds[i] + + # Check that block specific data is usable compiled, data = compiler.compile( - circuit, workflow, request_data=True, + circuit, workflow_2, request_data=True, ) dist = compiled.get_unitary().get_distance_from(circuit.get_unitary()) assert dist <= 1e-5 assert key in data + assert data[key] == seeds From d36bb67be925503193536f7bbf8177c40205f6cb Mon Sep 17 00:00:00 2001 From: Mathias Weiden Date: Wed, 18 Oct 2023 10:35:45 -0700 Subject: [PATCH 3/3] better foreach specific test --- tests/passes/control/test_foreachblock.py | 55 ++++++++++++----------- 1 file changed, 28 insertions(+), 27 deletions(-) diff --git a/tests/passes/control/test_foreachblock.py b/tests/passes/control/test_foreachblock.py index a0578bcfb..408d80b68 100644 --- a/tests/passes/control/test_foreachblock.py +++ b/tests/passes/control/test_foreachblock.py @@ -17,7 +17,6 @@ from bqskit.passes import UnfoldPass from bqskit.passes.control.foreach import ForEachBlockPass from bqskit.passes.partitioning.quick import QuickPartitioner -from bqskit.passes.synthesis.qsearch import QSearchSynthesisPass from bqskit.passes.util.update import UpdateDataPass @@ -52,6 +51,18 @@ async def run(self, circuit: Circuit, data: PassData) -> None: circuit.append_gate(HGate(), 1) +class TestForEachPassDownSpecificSeeds(BasePass): + async def run(self, circuit: Circuit, data: PassData) -> None: + key = ForEachBlockPass.pass_down_block_specific_key_prefix + key += 'test_info' + assert key in data + assert data[key] == 'a' or data[key] == 'b' + if data[key] == 'a': + data['test_response'] = 'a' + else: + data['test_response'] = 'b' + + def never_replace(c: Circuit, o: Operation) -> bool: return False @@ -144,38 +155,28 @@ def test_pass_down_seeds(compiler: Compiler) -> None: circuit.append_gate(CNOTGate(), (1, 3)) circuit.append_gate(CNOTGate(), (2, 3)) - seed = Circuit(3) - seed.append_gate(CNOTGate(), (0, 2)) - seed.append_gate(CNOTGate(), (1, 2)) - # Manually set seed for blocks 0 and 1 - seeds = {0: [seed], 1: [seed]} + input_info = {0: 'a', 1: 'b'} partitioner = QuickPartitioner() - qsearch = QSearchSynthesisPass() - foreach = ForEachBlockPass(qsearch) - unfolder = UnfoldPass() + check_specific = TestForEachPassDownSpecificSeeds() + foreach = ForEachBlockPass(check_specific) - key = foreach.pass_down_block_specific_key_prefix + 'seed_circuits' - updater = UpdateDataPass(key, seeds) + key = foreach.pass_down_block_specific_key_prefix + 'test_info' + updater = UpdateDataPass(key, input_info) # For checking specific pass down data exists - workflow_1 = Workflow([partitioner, updater]) - # For checking specific pass down data can be used - workflow_2 = Workflow([partitioner, updater, foreach, unfolder]) + workflow = Workflow([partitioner, updater, foreach]) - # Check existence of block specific keys - partitioned, data = compiler.compile( - circuit, workflow_1, request_data=True, - ) - for i, block in enumerate(partitioned): - data[key][i] == seeds[i] - - # Check that block specific data is usable + # Check usability of block specific keys compiled, data = compiler.compile( - circuit, workflow_2, request_data=True, + circuit, workflow, request_data=True, ) - dist = compiled.get_unitary().get_distance_from(circuit.get_unitary()) - assert dist <= 1e-5 - assert key in data - assert data[key] == seeds + + block0_data = data['ForEachBlockPass_data'][0][0] + block1_data = data['ForEachBlockPass_data'][0][1] + response_key = 'test_response' + assert response_key in block0_data + assert response_key in block1_data + assert block0_data[response_key] == 'a' + assert block1_data[response_key] == 'b'