From 649f418bd65446b52308bea3370ffe4c5b876355 Mon Sep 17 00:00:00 2001 From: Mathias Weiden Date: Tue, 13 Aug 2024 22:54:56 -0700 Subject: [PATCH 01/29] Added workflow_registry and register_workflow --- bqskit/compiler/register.py | 80 +++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 bqskit/compiler/register.py diff --git a/bqskit/compiler/register.py b/bqskit/compiler/register.py new file mode 100644 index 000000000..038d8bc7a --- /dev/null +++ b/bqskit/compiler/register.py @@ -0,0 +1,80 @@ +"""This module defines a global `worflow_registery` to modify workflows.""" +from __future__ import annotations + +from typing import Optional + +import logging + +from bqskit.compiler.machine import MachineModel +from bqskit.compiler.basepass import BasePass +from bqskit.compiler.workflow import WorkflowLike +from bqskit.compiler.workflow import Workflow + + +_logger = logging.getLogger(__name__) + + +workflow_registry: dict[MachineModel, dict[int, WorkflowLike]] = {} +""" +The workflow_registry enables MachineModel specific workflows to be registered +for used in the `bqskit.compile` method. + +The workflow_registry maps MachineModels a dictionary of Workflows which +are indexed by optimization level. This object should not be accessed directly +by the user, but instead through the `register_workflow` function. + +Examples: + model_t = SpecificMachineModel(num_qudits, radixes) + workflow = [QuickPartitioner(3), NewFangledOptimization()] + register_workflow(model_t, workflow, level) + ... + new_circuit = compile(circuit, model_t, optimization_level=level) +""" + + +def register_workflow( + machine: MachineModel, + workflow: WorkflowLike, + optimization_level: Optional[int] = 1, +) -> None: + """ + Register a workflow for a given machine model. + + Args: + machine (MachineModel): The machine to register the workflow for. + + workflow (list[BasePass]): The workflow or list of passes that whill + be executed if the MachineModel in a call to `compile` matches + `machine`. If `machine` is already registered, a warning will be + logged. + + optimization_level (Optional[int]): The optimization level with + which to register the workflow. If no level is provided, the + Workflow will be registered as level 1. (Default: 1) + + Raises: + TypeError: If `machine` is not a MachineModel. + + TypeError: If `workflow` is not a list of BasePass objects. + """ + if not isinstance(machine, MachineModel): + m = f'`machine` must be a MachineModel, got {type(machine)}.' + raise TypeError(m) + + if isinstance(workflow, BasePass): + workflow = Workflow(workflow) + + for p in workflow: + if not isinstance(p, BasePass): + m = 'All elements of `workflow` must be BasePass objects. Got ' + m += f'{type(p)}.' + raise TypeError(m) + + global workflow_registry + + if machine in workflow_registry: + if optimization_level in workflow_registry[machine]: + m = f'Overwritting workflow for {machine} at level ' + m += f'{optimization_level}.' + _logger.warn(m) + workflow_registry[machine].update({optimization_level: workflow}) \ No newline at end of file From b4e53664a0626deb3e8783a9122dc11882cc59a3 Mon Sep 17 00:00:00 2001 From: Mathias Weiden Date: Tue, 13 Aug 2024 22:55:21 -0700 Subject: [PATCH 02/29] compile checks workflow_registry --- bqskit/compiler/compile.py | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/bqskit/compiler/compile.py b/bqskit/compiler/compile.py index 4824c42ad..c791d2d61 100644 --- a/bqskit/compiler/compile.py +++ b/bqskit/compiler/compile.py @@ -15,6 +15,7 @@ from bqskit.compiler.compiler import Compiler from bqskit.compiler.machine import MachineModel from bqskit.compiler.passdata import PassData +from bqskit.compiler.register import workflow_registry from bqskit.compiler.workflow import Workflow from bqskit.compiler.workflow import WorkflowLike from bqskit.ir.circuit import Circuit @@ -668,6 +669,14 @@ def build_workflow( """Build a BQSKit Off-the-Shelf workflow, see :func:`compile` for info.""" if model is None: model = MachineModel(input.num_qudits, radixes=input.radixes) + + # Use a registered workflow if model is found in the registry for a given + # optimization_level + for machine_model in workflow_registry: + gate_set_match = machine_model.gate_set == model.gate_set + opt_lvl_found = optimization_level in workflow_registry[machine_model] + if gate_set_match and opt_lvl_found: + return workflow_registry[machine_model][optimization_level] if isinstance(input, Circuit): if input.num_qudits > max_synthesis_size: From 5912fdf6b64f88767c6d273314d42ea7e638b8c7 Mon Sep 17 00:00:00 2001 From: Mathias Weiden Date: Tue, 13 Aug 2024 23:11:50 -0700 Subject: [PATCH 03/29] Pre-commit --- bqskit/compiler/compile.py | 2 +- bqskit/compiler/register.py | 48 ++++++++++++++++++------------------- 2 files changed, 24 insertions(+), 26 deletions(-) diff --git a/bqskit/compiler/compile.py b/bqskit/compiler/compile.py index c791d2d61..f8d568116 100644 --- a/bqskit/compiler/compile.py +++ b/bqskit/compiler/compile.py @@ -669,7 +669,7 @@ def build_workflow( """Build a BQSKit Off-the-Shelf workflow, see :func:`compile` for info.""" if model is None: model = MachineModel(input.num_qudits, radixes=input.radixes) - + # Use a registered workflow if model is found in the registry for a given # optimization_level for machine_model in workflow_registry: diff --git a/bqskit/compiler/register.py b/bqskit/compiler/register.py index 038d8bc7a..0a78fd51a 100644 --- a/bqskit/compiler/register.py +++ b/bqskit/compiler/register.py @@ -1,20 +1,3 @@ -"""This module defines a global `worflow_registery` to modify workflows.""" -from __future__ import annotations - -from typing import Optional - -import logging - -from bqskit.compiler.machine import MachineModel -from bqskit.compiler.basepass import BasePass -from bqskit.compiler.workflow import WorkflowLike -from bqskit.compiler.workflow import Workflow - - -_logger = logging.getLogger(__name__) - - -workflow_registry: dict[MachineModel, dict[int, WorkflowLike]] = {} """ The workflow_registry enables MachineModel specific workflows to be registered for used in the `bqskit.compile` method. @@ -30,12 +13,26 @@ ... new_circuit = compile(circuit, model_t, optimization_level=level) """ +from __future__ import annotations + +import logging + +from bqskit.compiler.basepass import BasePass +from bqskit.compiler.machine import MachineModel +from bqskit.compiler.workflow import Workflow +from bqskit.compiler.workflow import WorkflowLike + + +_logger = logging.getLogger(__name__) + + +workflow_registry: dict[MachineModel, dict[int, Workflow]] = {} def register_workflow( machine: MachineModel, workflow: WorkflowLike, - optimization_level: Optional[int] = 1, + optimization_level: int = 1, ) -> None: """ Register a workflow for a given machine model. @@ -47,11 +44,11 @@ def register_workflow( be executed if the MachineModel in a call to `compile` matches `machine`. If `machine` is already registered, a warning will be logged. - - optimization_level (Optional[int]): The optimization level with + + optimization_level (Optional[int]): The optimization level with which to register the workflow. If no level is provided, the Workflow will be registered as level 1. (Default: 1) - + Raises: TypeError: If `machine` is not a MachineModel. @@ -60,9 +57,8 @@ def register_workflow( if not isinstance(machine, MachineModel): m = f'`machine` must be a MachineModel, got {type(machine)}.' raise TypeError(m) - - if isinstance(workflow, BasePass): - workflow = Workflow(workflow) + + workflow = Workflow(workflow) for p in workflow: if not isinstance(p, BasePass): @@ -77,4 +73,6 @@ def register_workflow( m = f'Overwritting workflow for {machine} at level ' m += f'{optimization_level}.' _logger.warn(m) - workflow_registry[machine].update({optimization_level: workflow}) \ No newline at end of file + workflow_registry[machine].update({optimization_level: workflow}) + else: + workflow_registry[machine] = {optimization_level: workflow} From 18929e17e90226b26baf27613e14cd01c72121ad Mon Sep 17 00:00:00 2001 From: Mathias Weiden Date: Fri, 16 Aug 2024 15:03:12 -0700 Subject: [PATCH 04/29] Workflows can be registered through GateSets --- bqskit/compiler/compile.py | 15 ++++++++++----- bqskit/compiler/register.py | 37 +++++++++++++++++++++---------------- 2 files changed, 31 insertions(+), 21 deletions(-) diff --git a/bqskit/compiler/compile.py b/bqskit/compiler/compile.py index f8d568116..f5b6ca16c 100644 --- a/bqskit/compiler/compile.py +++ b/bqskit/compiler/compile.py @@ -13,6 +13,7 @@ import numpy as np from bqskit.compiler.compiler import Compiler +from bqskit.compiler.gateset import GateSet from bqskit.compiler.machine import MachineModel from bqskit.compiler.passdata import PassData from bqskit.compiler.register import workflow_registry @@ -672,11 +673,15 @@ def build_workflow( # Use a registered workflow if model is found in the registry for a given # optimization_level - for machine_model in workflow_registry: - gate_set_match = machine_model.gate_set == model.gate_set - opt_lvl_found = optimization_level in workflow_registry[machine_model] - if gate_set_match and opt_lvl_found: - return workflow_registry[machine_model][optimization_level] + for machine_or_gateset in workflow_registry: + if isinstance(machine_or_gateset, GateSet): + gate_set = machine_or_gateset + else: + gate_set = machine_or_gateset.gate_set + gs_match = gate_set == model.gate_set + ol_found = optimization_level in workflow_registry[machine_or_gateset] + if gs_match and ol_found: + return workflow_registry[machine_or_gateset][optimization_level] if isinstance(input, Circuit): if input.num_qudits > max_synthesis_size: diff --git a/bqskit/compiler/register.py b/bqskit/compiler/register.py index 0a78fd51a..acc91d6bb 100644 --- a/bqskit/compiler/register.py +++ b/bqskit/compiler/register.py @@ -1,12 +1,12 @@ """ -The workflow_registry enables MachineModel specific workflows to be registered -for used in the `bqskit.compile` method. +The workflow_registry enables MachineModel or GateSet specific workflows to be +registered for used in the `bqskit.compile` method. The workflow_registry maps MachineModels a dictionary of Workflows which are indexed by optimization level. This object should not be accessed directly by the user, but instead through the `register_workflow` function. -Examples: +Example: model_t = SpecificMachineModel(num_qudits, radixes) workflow = [QuickPartitioner(3), NewFangledOptimization()] register_workflow(model_t, workflow, level) @@ -18,6 +18,7 @@ import logging from bqskit.compiler.basepass import BasePass +from bqskit.compiler.gateset import GateSet from bqskit.compiler.machine import MachineModel from bqskit.compiler.workflow import Workflow from bqskit.compiler.workflow import WorkflowLike @@ -26,11 +27,11 @@ _logger = logging.getLogger(__name__) -workflow_registry: dict[MachineModel, dict[int, Workflow]] = {} +workflow_registry: dict[MachineModel | GateSet, dict[int, Workflow]] = {} def register_workflow( - machine: MachineModel, + machine_or_gateset: MachineModel | GateSet, workflow: WorkflowLike, optimization_level: int = 1, ) -> None: @@ -38,7 +39,9 @@ def register_workflow( Register a workflow for a given machine model. Args: - machine (MachineModel): The machine to register the workflow for. + machine_or_gateset (MachineModel | GateSet): A MachineModel or GateSet + to register the workflow for. If a circuit is compiled targeting + this machine or gate set, the registered workflow will be used. workflow (list[BasePass]): The workflow or list of passes that whill be executed if the MachineModel in a call to `compile` matches @@ -50,12 +53,14 @@ def register_workflow( Workflow will be registered as level 1. (Default: 1) Raises: - TypeError: If `machine` is not a MachineModel. + TypeError: If `machine_or_gateset` is not a MachineModel or GateSet. TypeError: If `workflow` is not a list of BasePass objects. """ - if not isinstance(machine, MachineModel): - m = f'`machine` must be a MachineModel, got {type(machine)}.' + if not isinstance(machine_or_gateset, MachineModel) and not \ + isinstance(machine_or_gateset, GateSet): + m = '`machine_or_gateset` must be a MachineModel or ' + m += f'GateSet, got {type(machine_or_gateset)}.' raise TypeError(m) workflow = Workflow(workflow) @@ -67,12 +72,12 @@ def register_workflow( raise TypeError(m) global workflow_registry - - if machine in workflow_registry: - if optimization_level in workflow_registry[machine]: - m = f'Overwritting workflow for {machine} at level ' - m += f'{optimization_level}.' + new_workflow = workflow_registry[machine_or_gateset] + if machine_or_gateset in workflow_registry: + if optimization_level in workflow_registry[machine_or_gateset]: + m = f'Overwritting workflow for {machine_or_gateset} ' + m += f'at level {optimization_level}.' _logger.warn(m) - workflow_registry[machine].update({optimization_level: workflow}) + workflow_registry[machine_or_gateset].update(new_workflow) else: - workflow_registry[machine] = {optimization_level: workflow} + workflow_registry[machine_or_gateset] = new_workflow From bb3febb78fb0a9f680ce68bb59696985c216f0fb Mon Sep 17 00:00:00 2001 From: Mathias Weiden Date: Fri, 16 Aug 2024 15:31:15 -0700 Subject: [PATCH 05/29] GateSets are hashable --- bqskit/compiler/gateset.py | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/bqskit/compiler/gateset.py b/bqskit/compiler/gateset.py index d8111ec4a..63672e768 100644 --- a/bqskit/compiler/gateset.py +++ b/bqskit/compiler/gateset.py @@ -230,6 +230,10 @@ def __str__(self) -> str: def __repr__(self) -> str: """Detailed representation of the GateSet.""" return self._gates.__repr__().replace('frozenset', 'GateSet') + + def __hash__(self) -> int: + """Hash of the GateSet.""" + return self.__repr__().__hash__() GateSetLike = Union[GateSet, Iterable[Gate], Gate] From 3694184b5625c910bfc0c5ac8aa58eeca2d00d5d Mon Sep 17 00:00:00 2001 From: Mathias Weiden Date: Fri, 16 Aug 2024 15:31:54 -0700 Subject: [PATCH 06/29] Check for Gate sequences --- bqskit/compiler/register.py | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/bqskit/compiler/register.py b/bqskit/compiler/register.py index acc91d6bb..37b6b4aec 100644 --- a/bqskit/compiler/register.py +++ b/bqskit/compiler/register.py @@ -23,6 +23,7 @@ from bqskit.compiler.workflow import Workflow from bqskit.compiler.workflow import WorkflowLike +from bqskit.ir.gate import Gate _logger = logging.getLogger(__name__) @@ -57,11 +58,13 @@ def register_workflow( TypeError: If `workflow` is not a list of BasePass objects. """ - if not isinstance(machine_or_gateset, MachineModel) and not \ - isinstance(machine_or_gateset, GateSet): - m = '`machine_or_gateset` must be a MachineModel or ' - m += f'GateSet, got {type(machine_or_gateset)}.' - raise TypeError(m) + if not isinstance(machine_or_gateset, MachineModel): + if all(isinstance(g, Gate) for g in machine_or_gateset): + machine_or_gateset = GateSet(machine_or_gateset) + else: + m = '`machine_or_gateset` must be a MachineModel or ' + m += f'GateSet, got {type(machine_or_gateset)}.' + raise TypeError(m) workflow = Workflow(workflow) @@ -72,7 +75,7 @@ def register_workflow( raise TypeError(m) global workflow_registry - new_workflow = workflow_registry[machine_or_gateset] + new_workflow = {optimization_level: workflow} if machine_or_gateset in workflow_registry: if optimization_level in workflow_registry[machine_or_gateset]: m = f'Overwritting workflow for {machine_or_gateset} ' From 888e0b21b6b4633845dca78b34cc44b8064492b8 Mon Sep 17 00:00:00 2001 From: Mathias Weiden Date: Fri, 16 Aug 2024 15:32:09 -0700 Subject: [PATCH 07/29] Tests for register_workflow --- tests/compiler/test_register.py | 95 +++++++++++++++++++++++++++++++++ 1 file changed, 95 insertions(+) create mode 100644 tests/compiler/test_register.py diff --git a/tests/compiler/test_register.py b/tests/compiler/test_register.py new file mode 100644 index 000000000..02a4e5256 --- /dev/null +++ b/tests/compiler/test_register.py @@ -0,0 +1,95 @@ +"""This file tests the register_workflow function.""" +from __future__ import annotations + +from itertools import combinations + +from random import choice + +from bqskit.compiler import compile +from bqskit.compiler.machine import MachineModel +from bqskit.compiler.register import workflow_registry +from bqskit.compiler.register import register_workflow +from bqskit.compiler.workflow import Workflow + +from bqskit.ir import Circuit +from bqskit.ir import Gate +from bqskit.ir.gates import CZGate +from bqskit.ir.gates import HGate +from bqskit.ir.gates import RZGate + +from bqskit.passes import QuickPartitioner +from bqskit.passes import ScanningGateRemovalPass + + +def machine_match(mach_a: MachineModel, mach_b: MachineModel) -> bool: + if mach_a.num_qudits != mach_b.num_qudits: + return False + if mach_a.radixes != mach_b.radixes: + return False + if mach_a.coupling_graph != mach_b.coupling_graph: + return False + if mach_a.gate_set != mach_b.gate_set: + return False + return True + + +def workflow_match(workflow_a: Workflow, workflow_b: Workflow) -> bool: + if len(workflow_a) != len(workflow_b): + return False + for a, b in zip(workflow_a, workflow_b): + if a.name != b.name: + return False + return True + + +def simple_circuit(num_qudits: int, gate_set: list[Gate]) -> Circuit: + circ = Circuit(num_qudits) + gate = choice(gate_set) + if gate.num_qudits == 1: + loc = choice(range(num_qudits)) + else: + loc = choice(list(combinations(range(num_qudits), 2))) + gate_inv = gate.get_inverse() + circ.append_gate(gate, loc) + circ.append_gate(gate_inv, loc) + return circ + + +class TestRegisterWorkflow: + + def test_register_workflow(self) -> None: + assert workflow_registry == {} + machine = MachineModel(3) + workflow = [QuickPartitioner(), ScanningGateRemovalPass()] + register_workflow(machine, workflow) + assert machine in workflow_registry + assert 1 in workflow_registry[machine] + assert workflow_match(workflow_registry[machine][1], workflow) + + def test_custom_compile_machine(self) -> None: + gateset = [CZGate(), HGate(), RZGate()] + num_qudits = 3 + machine = MachineModel(num_qudits, gate_set=gateset) + workflow = [QuickPartitioner(2)] + register_workflow(machine, workflow) + circuit = simple_circuit(num_qudits, gateset) + result = compile(circuit, machine) + assert result.get_unitary() == circuit.get_unitary() + assert result.num_operations > 0 + assert result.gate_counts != circuit.gate_counts + result.unfold_all() + assert result.gate_counts == circuit.gate_counts + + def test_custom_compile_gateset(self) -> None: + gateset = [CZGate(), HGate(), RZGate()] + num_qudits = 3 + machine = MachineModel(num_qudits, gate_set=gateset) + workflow = [QuickPartitioner(2)] + register_workflow(gateset, workflow) + circuit = simple_circuit(num_qudits, gateset) + result = compile(circuit, machine) + assert result.get_unitary() == circuit.get_unitary() + assert result.num_operations > 0 + assert result.gate_counts != circuit.gate_counts + result.unfold_all() + assert result.gate_counts == circuit.gate_counts \ No newline at end of file From f007e6bf01db410a8c3309492ce4eff5e09e8efe Mon Sep 17 00:00:00 2001 From: Mathias Weiden Date: Fri, 16 Aug 2024 15:40:32 -0700 Subject: [PATCH 08/29] pre-commit --- bqskit/compiler/gateset.py | 2 +- bqskit/compiler/register.py | 13 ++++++++----- tests/compiler/test_register.py | 19 ++++++++++++------- 3 files changed, 21 insertions(+), 13 deletions(-) diff --git a/bqskit/compiler/gateset.py b/bqskit/compiler/gateset.py index 63672e768..05f735df5 100644 --- a/bqskit/compiler/gateset.py +++ b/bqskit/compiler/gateset.py @@ -230,7 +230,7 @@ def __str__(self) -> str: def __repr__(self) -> str: """Detailed representation of the GateSet.""" return self._gates.__repr__().replace('frozenset', 'GateSet') - + def __hash__(self) -> int: """Hash of the GateSet.""" return self.__repr__().__hash__() diff --git a/bqskit/compiler/register.py b/bqskit/compiler/register.py index 37b6b4aec..c655a5821 100644 --- a/bqskit/compiler/register.py +++ b/bqskit/compiler/register.py @@ -19,10 +19,10 @@ from bqskit.compiler.basepass import BasePass from bqskit.compiler.gateset import GateSet +from bqskit.compiler.gateset import GateSetLike from bqskit.compiler.machine import MachineModel from bqskit.compiler.workflow import Workflow from bqskit.compiler.workflow import WorkflowLike - from bqskit.ir.gate import Gate _logger = logging.getLogger(__name__) @@ -32,7 +32,7 @@ def register_workflow( - machine_or_gateset: MachineModel | GateSet, + machine_or_gateset: MachineModel | GateSetLike, workflow: WorkflowLike, optimization_level: int = 1, ) -> None: @@ -40,9 +40,10 @@ def register_workflow( Register a workflow for a given machine model. Args: - machine_or_gateset (MachineModel | GateSet): A MachineModel or GateSet - to register the workflow for. If a circuit is compiled targeting - this machine or gate set, the registered workflow will be used. + machine_or_gateset (MachineModel | GateSetLike): A MachineModel or + GateSetLike to register the workflow under. If a circuit is + compiled targeting this machine or gate set, the registered + workflow will be used. workflow (list[BasePass]): The workflow or list of passes that whill be executed if the MachineModel in a call to `compile` matches @@ -59,6 +60,8 @@ def register_workflow( TypeError: If `workflow` is not a list of BasePass objects. """ if not isinstance(machine_or_gateset, MachineModel): + if isinstance(machine_or_gateset, Gate): + machine_or_gateset = [machine_or_gateset] if all(isinstance(g, Gate) for g in machine_or_gateset): machine_or_gateset = GateSet(machine_or_gateset) else: diff --git a/tests/compiler/test_register.py b/tests/compiler/test_register.py index 02a4e5256..414d90a06 100644 --- a/tests/compiler/test_register.py +++ b/tests/compiler/test_register.py @@ -2,21 +2,19 @@ from __future__ import annotations from itertools import combinations - from random import choice from bqskit.compiler import compile from bqskit.compiler.machine import MachineModel -from bqskit.compiler.register import workflow_registry from bqskit.compiler.register import register_workflow +from bqskit.compiler.register import workflow_registry from bqskit.compiler.workflow import Workflow - +from bqskit.compiler.workflow import WorkflowLike from bqskit.ir import Circuit from bqskit.ir import Gate from bqskit.ir.gates import CZGate from bqskit.ir.gates import HGate from bqskit.ir.gates import RZGate - from bqskit.passes import QuickPartitioner from bqskit.passes import ScanningGateRemovalPass @@ -33,7 +31,14 @@ def machine_match(mach_a: MachineModel, mach_b: MachineModel) -> bool: return True -def workflow_match(workflow_a: Workflow, workflow_b: Workflow) -> bool: +def workflow_match( + workflow_a: WorkflowLike, + workflow_b: WorkflowLike, +) -> bool: + if not isinstance(workflow_a, Workflow): + workflow_a = Workflow(workflow_a) + if not isinstance(workflow_b, Workflow): + workflow_b = Workflow(workflow_b) if len(workflow_a) != len(workflow_b): return False for a, b in zip(workflow_a, workflow_b): @@ -48,7 +53,7 @@ def simple_circuit(num_qudits: int, gate_set: list[Gate]) -> Circuit: if gate.num_qudits == 1: loc = choice(range(num_qudits)) else: - loc = choice(list(combinations(range(num_qudits), 2))) + loc = choice(list(combinations(range(num_qudits), 2))) # type: ignore gate_inv = gate.get_inverse() circ.append_gate(gate, loc) circ.append_gate(gate_inv, loc) @@ -92,4 +97,4 @@ def test_custom_compile_gateset(self) -> None: assert result.num_operations > 0 assert result.gate_counts != circuit.gate_counts result.unfold_all() - assert result.gate_counts == circuit.gate_counts \ No newline at end of file + assert result.gate_counts == circuit.gate_counts From 0a33d182fecade311ee4fcfb4dae84aee94ba5a5 Mon Sep 17 00:00:00 2001 From: Mathias Weiden Date: Fri, 16 Aug 2024 15:50:33 -0700 Subject: [PATCH 09/29] Added test for optimization_level=2 --- tests/compiler/test_register.py | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/tests/compiler/test_register.py b/tests/compiler/test_register.py index 414d90a06..b3131ce99 100644 --- a/tests/compiler/test_register.py +++ b/tests/compiler/test_register.py @@ -15,6 +15,8 @@ from bqskit.ir.gates import CZGate from bqskit.ir.gates import HGate from bqskit.ir.gates import RZGate +from bqskit.ir.gates import U3Gate +from bqskit.passes import QSearchSynthesisPass from bqskit.passes import QuickPartitioner from bqskit.passes import ScanningGateRemovalPass @@ -98,3 +100,15 @@ def test_custom_compile_gateset(self) -> None: assert result.gate_counts != circuit.gate_counts result.unfold_all() assert result.gate_counts == circuit.gate_counts + + def test_custom_opt_level(self) -> None: + gateset = [CZGate(), HGate(), RZGate()] + num_qudits = 3 + machine = MachineModel(num_qudits, gate_set=gateset) + workflow = [QSearchSynthesisPass()] + register_workflow(gateset, workflow, 2) + circuit = simple_circuit(num_qudits, gateset) + result = compile(circuit, machine, optimization_level=2) + assert result.get_unitary() == circuit.get_unitary() + assert result.gate_counts != circuit.gate_counts + assert U3Gate() in result.gate_set From a8b5e25521b143a7fe952b91e6640f7b3bfa0836 Mon Sep 17 00:00:00 2001 From: Mathias Weiden Date: Mon, 19 Aug 2024 20:45:41 -0700 Subject: [PATCH 10/29] Renamed workflow_registry -> _workflow_registry --- bqskit/compiler/compile.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/bqskit/compiler/compile.py b/bqskit/compiler/compile.py index f5b6ca16c..941f29060 100644 --- a/bqskit/compiler/compile.py +++ b/bqskit/compiler/compile.py @@ -16,7 +16,7 @@ from bqskit.compiler.gateset import GateSet from bqskit.compiler.machine import MachineModel from bqskit.compiler.passdata import PassData -from bqskit.compiler.register import workflow_registry +from bqskit.compiler.register import _workflow_registry from bqskit.compiler.workflow import Workflow from bqskit.compiler.workflow import WorkflowLike from bqskit.ir.circuit import Circuit @@ -673,15 +673,15 @@ def build_workflow( # Use a registered workflow if model is found in the registry for a given # optimization_level - for machine_or_gateset in workflow_registry: + for machine_or_gateset in _workflow_registry: if isinstance(machine_or_gateset, GateSet): gate_set = machine_or_gateset else: gate_set = machine_or_gateset.gate_set gs_match = gate_set == model.gate_set - ol_found = optimization_level in workflow_registry[machine_or_gateset] + ol_found = optimization_level in _workflow_registry[machine_or_gateset] if gs_match and ol_found: - return workflow_registry[machine_or_gateset][optimization_level] + return _workflow_registry[machine_or_gateset][optimization_level] if isinstance(input, Circuit): if input.num_qudits > max_synthesis_size: From ae23a051a6877e5151f45c53583f86369e419ef4 Mon Sep 17 00:00:00 2001 From: Mathias Weiden Date: Mon, 19 Aug 2024 20:46:00 -0700 Subject: [PATCH 11/29] Added clear_register --- bqskit/compiler/register.py | 26 ++++++++++++++++++-------- tests/compiler/test_register.py | 33 ++++++++++++++++++++++++--------- 2 files changed, 42 insertions(+), 17 deletions(-) diff --git a/bqskit/compiler/register.py b/bqskit/compiler/register.py index c655a5821..8668cec79 100644 --- a/bqskit/compiler/register.py +++ b/bqskit/compiler/register.py @@ -1,8 +1,8 @@ """ -The workflow_registry enables MachineModel or GateSet specific workflows to be +The _workflow_registry enables MachineModel or GateSet specific workflows to be registered for used in the `bqskit.compile` method. -The workflow_registry maps MachineModels a dictionary of Workflows which +The _workflow_registry maps MachineModels a dictionary of Workflows which are indexed by optimization level. This object should not be accessed directly by the user, but instead through the `register_workflow` function. @@ -28,7 +28,7 @@ _logger = logging.getLogger(__name__) -workflow_registry: dict[MachineModel | GateSet, dict[int, Workflow]] = {} +_workflow_registry: dict[MachineModel | GateSet, dict[int, Workflow]] = {} def register_workflow( @@ -77,13 +77,23 @@ def register_workflow( m += f'{type(p)}.' raise TypeError(m) - global workflow_registry + global _workflow_registry new_workflow = {optimization_level: workflow} - if machine_or_gateset in workflow_registry: - if optimization_level in workflow_registry[machine_or_gateset]: + if machine_or_gateset in _workflow_registry: + if optimization_level in _workflow_registry[machine_or_gateset]: m = f'Overwritting workflow for {machine_or_gateset} ' m += f'at level {optimization_level}.' _logger.warn(m) - workflow_registry[machine_or_gateset].update(new_workflow) + _workflow_registry[machine_or_gateset].update(new_workflow) else: - workflow_registry[machine_or_gateset] = new_workflow + _workflow_registry[machine_or_gateset] = new_workflow + + +def clear_registry() -> None: + """ + Clear the workflow registry. + + This will remove all registered workflows from the registry. + """ + global _workflow_registry + _workflow_registry.clear() diff --git a/tests/compiler/test_register.py b/tests/compiler/test_register.py index b3131ce99..31e544d4c 100644 --- a/tests/compiler/test_register.py +++ b/tests/compiler/test_register.py @@ -4,10 +4,14 @@ from itertools import combinations from random import choice +import pytest +from numpy import allclose + from bqskit.compiler import compile from bqskit.compiler.machine import MachineModel +from bqskit.compiler.register import _workflow_registry +from bqskit.compiler.register import clear_registry from bqskit.compiler.register import register_workflow -from bqskit.compiler.register import workflow_registry from bqskit.compiler.workflow import Workflow from bqskit.compiler.workflow import WorkflowLike from bqskit.ir import Circuit @@ -33,6 +37,10 @@ def machine_match(mach_a: MachineModel, mach_b: MachineModel) -> bool: return True +def unitary_match(unit_a: Circuit, unit_b: Circuit) -> bool: + return allclose(unit_a.get_unitary(), unit_b.get_unitary(), atol=1e-5) + + def workflow_match( workflow_a: WorkflowLike, workflow_b: WorkflowLike, @@ -64,14 +72,21 @@ def simple_circuit(num_qudits: int, gate_set: list[Gate]) -> Circuit: class TestRegisterWorkflow: + @pytest.fixture(autouse=True) + def setup(self) -> None: + # _workflow_registry.clear() + clear_registry() + def test_register_workflow(self) -> None: - assert workflow_registry == {} - machine = MachineModel(3) + assert _workflow_registry == {} + gateset = [CZGate(), HGate(), RZGate()] + num_qudits = 3 + machine = MachineModel(num_qudits, gate_set=gateset) workflow = [QuickPartitioner(), ScanningGateRemovalPass()] register_workflow(machine, workflow) - assert machine in workflow_registry - assert 1 in workflow_registry[machine] - assert workflow_match(workflow_registry[machine][1], workflow) + assert machine in _workflow_registry + assert 1 in _workflow_registry[machine] + assert workflow_match(_workflow_registry[machine][1], workflow) def test_custom_compile_machine(self) -> None: gateset = [CZGate(), HGate(), RZGate()] @@ -81,7 +96,7 @@ def test_custom_compile_machine(self) -> None: register_workflow(machine, workflow) circuit = simple_circuit(num_qudits, gateset) result = compile(circuit, machine) - assert result.get_unitary() == circuit.get_unitary() + assert unitary_match(result, circuit) assert result.num_operations > 0 assert result.gate_counts != circuit.gate_counts result.unfold_all() @@ -95,7 +110,7 @@ def test_custom_compile_gateset(self) -> None: register_workflow(gateset, workflow) circuit = simple_circuit(num_qudits, gateset) result = compile(circuit, machine) - assert result.get_unitary() == circuit.get_unitary() + assert unitary_match(result, circuit) assert result.num_operations > 0 assert result.gate_counts != circuit.gate_counts result.unfold_all() @@ -109,6 +124,6 @@ def test_custom_opt_level(self) -> None: register_workflow(gateset, workflow, 2) circuit = simple_circuit(num_qudits, gateset) result = compile(circuit, machine, optimization_level=2) - assert result.get_unitary() == circuit.get_unitary() + assert unitary_match(result, circuit) assert result.gate_counts != circuit.gate_counts assert U3Gate() in result.gate_set From 6e59f4b1eb366285937aaaa54d1f4eac8f67f91e Mon Sep 17 00:00:00 2001 From: Mathias Weiden Date: Mon, 19 Aug 2024 20:48:33 -0700 Subject: [PATCH 12/29] register -> registry --- bqskit/compiler/compile.py | 2 +- bqskit/compiler/{register.py => registry.py} | 0 tests/compiler/{test_register.py => test_registry.py} | 6 +++--- 3 files changed, 4 insertions(+), 4 deletions(-) rename bqskit/compiler/{register.py => registry.py} (100%) rename tests/compiler/{test_register.py => test_registry.py} (96%) diff --git a/bqskit/compiler/compile.py b/bqskit/compiler/compile.py index 941f29060..c6b9f8bab 100644 --- a/bqskit/compiler/compile.py +++ b/bqskit/compiler/compile.py @@ -16,7 +16,7 @@ from bqskit.compiler.gateset import GateSet from bqskit.compiler.machine import MachineModel from bqskit.compiler.passdata import PassData -from bqskit.compiler.register import _workflow_registry +from bqskit.compiler.registry import _workflow_registry from bqskit.compiler.workflow import Workflow from bqskit.compiler.workflow import WorkflowLike from bqskit.ir.circuit import Circuit diff --git a/bqskit/compiler/register.py b/bqskit/compiler/registry.py similarity index 100% rename from bqskit/compiler/register.py rename to bqskit/compiler/registry.py diff --git a/tests/compiler/test_register.py b/tests/compiler/test_registry.py similarity index 96% rename from tests/compiler/test_register.py rename to tests/compiler/test_registry.py index 31e544d4c..ca037bca9 100644 --- a/tests/compiler/test_register.py +++ b/tests/compiler/test_registry.py @@ -9,9 +9,9 @@ from bqskit.compiler import compile from bqskit.compiler.machine import MachineModel -from bqskit.compiler.register import _workflow_registry -from bqskit.compiler.register import clear_registry -from bqskit.compiler.register import register_workflow +from bqskit.compiler.registry import _workflow_registry +from bqskit.compiler.registry import clear_registry +from bqskit.compiler.registry import register_workflow from bqskit.compiler.workflow import Workflow from bqskit.compiler.workflow import WorkflowLike from bqskit.ir import Circuit From 92364bde212c4ee7fe81ce47590a2f638546c355 Mon Sep 17 00:00:00 2001 From: Mathias Weiden Date: Wed, 28 Aug 2024 08:36:53 -0700 Subject: [PATCH 13/29] Fixed spelling error --- bqskit/compiler/registry.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bqskit/compiler/registry.py b/bqskit/compiler/registry.py index 8668cec79..50cb00523 100644 --- a/bqskit/compiler/registry.py +++ b/bqskit/compiler/registry.py @@ -45,7 +45,7 @@ def register_workflow( compiled targeting this machine or gate set, the registered workflow will be used. - workflow (list[BasePass]): The workflow or list of passes that whill + workflow (list[BasePass]): The workflow or list of passes that will be executed if the MachineModel in a call to `compile` matches `machine`. If `machine` is already registered, a warning will be logged. From a74a8aa3ee8f02d2de31b3651b70ebdf2f00f2fd Mon Sep 17 00:00:00 2001 From: Mathias Weiden Date: Wed, 28 Aug 2024 08:45:30 -0700 Subject: [PATCH 14/29] Added is_workflow static function --- bqskit/compiler/workflow.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/bqskit/compiler/workflow.py b/bqskit/compiler/workflow.py index 6134d07aa..0771ce6d5 100644 --- a/bqskit/compiler/workflow.py +++ b/bqskit/compiler/workflow.py @@ -87,6 +87,12 @@ def name(self) -> str: """The name of the pass.""" return self._name or self.__class__.__name__ + @staticmethod + def is_workflow(workflow: WorkflowLike) -> bool: + if not is_iterable(workflow): + return isinstance(workflow, BasePass) + return all(isinstance(p, BasePass) for p in workflow) + def __str__(self) -> str: name_seq = f'Workflow: {self.name}\n\t' pass_strs = [ From 6ff5f9ab1f149128899c7adc3716874a73fd0e7b Mon Sep 17 00:00:00 2001 From: Mathias Weiden Date: Wed, 28 Aug 2024 08:45:53 -0700 Subject: [PATCH 15/29] Workflow checking is done in Workflow construction --- bqskit/compiler/registry.py | 6 ------ 1 file changed, 6 deletions(-) diff --git a/bqskit/compiler/registry.py b/bqskit/compiler/registry.py index 50cb00523..b21abba7b 100644 --- a/bqskit/compiler/registry.py +++ b/bqskit/compiler/registry.py @@ -71,12 +71,6 @@ def register_workflow( workflow = Workflow(workflow) - for p in workflow: - if not isinstance(p, BasePass): - m = 'All elements of `workflow` must be BasePass objects. Got ' - m += f'{type(p)}.' - raise TypeError(m) - global _workflow_registry new_workflow = {optimization_level: workflow} if machine_or_gateset in _workflow_registry: From 3725b2209f809b97ca8176fac330f88608ad97ba Mon Sep 17 00:00:00 2001 From: Mathias Weiden Date: Wed, 28 Aug 2024 08:47:44 -0700 Subject: [PATCH 16/29] No default optimization level --- bqskit/compiler/registry.py | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/bqskit/compiler/registry.py b/bqskit/compiler/registry.py index b21abba7b..8b46daa7e 100644 --- a/bqskit/compiler/registry.py +++ b/bqskit/compiler/registry.py @@ -34,7 +34,7 @@ def register_workflow( machine_or_gateset: MachineModel | GateSetLike, workflow: WorkflowLike, - optimization_level: int = 1, + optimization_level: int, ) -> None: """ Register a workflow for a given machine model. @@ -50,9 +50,9 @@ def register_workflow( `machine`. If `machine` is already registered, a warning will be logged. - optimization_level (Optional[int]): The optimization level with - which to register the workflow. If no level is provided, the - Workflow will be registered as level 1. (Default: 1) + optimization_level ptional[int): The optimization level with which + to register the workflow. If no level is provided, the Workflow + will be registered as level 1. Raises: TypeError: If `machine_or_gateset` is not a MachineModel or GateSet. From 6b7da3c6d336c97e75e6faf16698b7077e9db680 Mon Sep 17 00:00:00 2001 From: Mathias Weiden Date: Wed, 28 Aug 2024 09:01:16 -0700 Subject: [PATCH 17/29] Permutation robust Gateset hash --- bqskit/compiler/gateset.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/bqskit/compiler/gateset.py b/bqskit/compiler/gateset.py index 05f735df5..be50ce692 100644 --- a/bqskit/compiler/gateset.py +++ b/bqskit/compiler/gateset.py @@ -233,7 +233,7 @@ def __repr__(self) -> str: def __hash__(self) -> int: """Hash of the GateSet.""" - return self.__repr__().__hash__() + return hash(tuple(sorted([g.name for g in self._gates]))) GateSetLike = Union[GateSet, Iterable[Gate], Gate] From 99dfee1afca9bcd9b98b12055ab01bf9fbb41b34 Mon Sep 17 00:00:00 2001 From: Mathias Weiden Date: Wed, 28 Aug 2024 09:04:05 -0700 Subject: [PATCH 18/29] Gateset hash test --- tests/compiler/test_gateset.py | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/tests/compiler/test_gateset.py b/tests/compiler/test_gateset.py index 009003a95..4f89b8fca 100644 --- a/tests/compiler/test_gateset.py +++ b/tests/compiler/test_gateset.py @@ -522,3 +522,16 @@ def test_gate_set_repr() -> None: repr(gate_set) == 'GateSet({CNOTGate, U3Gate})' or repr(gate_set) == 'GateSet({U3Gate, CNOTGate})' ) + + +def test_gate_set_hash() -> None: + gate_set_1 = GateSet({CNOTGate(), U3Gate()}) + gate_set_2 = GateSet({U3Gate(), CNOTGate()}) + gate_set_3 = GateSet({U3Gate(), CNOTGate(), RZGate()}) + + h1 = hash(gate_set_1) + h2 = hash(gate_set_2) + h3 = hash(gate_set_3) + + assert h1 == h2 + assert h1 != h3 From 1e02804fb18f8b2bcaf88fe064497daa7ad3881c Mon Sep 17 00:00:00 2001 From: Mathias Weiden Date: Wed, 28 Aug 2024 09:12:54 -0700 Subject: [PATCH 19/29] Moved documentation --- bqskit/compiler/registry.py | 44 ++++++++++++++++++------------------- 1 file changed, 21 insertions(+), 23 deletions(-) diff --git a/bqskit/compiler/registry.py b/bqskit/compiler/registry.py index 8b46daa7e..579d5579f 100644 --- a/bqskit/compiler/registry.py +++ b/bqskit/compiler/registry.py @@ -1,23 +1,8 @@ -""" -The _workflow_registry enables MachineModel or GateSet specific workflows to be -registered for used in the `bqskit.compile` method. - -The _workflow_registry maps MachineModels a dictionary of Workflows which -are indexed by optimization level. This object should not be accessed directly -by the user, but instead through the `register_workflow` function. - -Example: - model_t = SpecificMachineModel(num_qudits, radixes) - workflow = [QuickPartitioner(3), NewFangledOptimization()] - register_workflow(model_t, workflow, level) - ... - new_circuit = compile(circuit, model_t, optimization_level=level) -""" +"""Register GateSet or MachineModel specific default workflows.""" from __future__ import annotations -import logging +import warnings -from bqskit.compiler.basepass import BasePass from bqskit.compiler.gateset import GateSet from bqskit.compiler.gateset import GateSetLike from bqskit.compiler.machine import MachineModel @@ -25,8 +10,6 @@ from bqskit.compiler.workflow import WorkflowLike from bqskit.ir.gate import Gate -_logger = logging.getLogger(__name__) - _workflow_registry: dict[MachineModel | GateSet, dict[int, Workflow]] = {} @@ -39,6 +22,12 @@ def register_workflow( """ Register a workflow for a given machine model. + The _workflow_registry enables MachineModel or GateSet specific workflows + to be registered for use in the `bqskit.compile` method. _workflow_registry + maps MachineModels a dictionary of Workflows which are indexed by + optimization level. This object should not be accessed directly by the user, + but instead through the `register_workflow` function. + Args: machine_or_gateset (MachineModel | GateSetLike): A MachineModel or GateSetLike to register the workflow under. If a circuit is @@ -54,10 +43,17 @@ def register_workflow( to register the workflow. If no level is provided, the Workflow will be registered as level 1. + Example: + model_t = SpecificMachineModel(num_qudits, radixes) + workflow = [QuickPartitioner(3), NewFangledOptimization()] + register_workflow(model_t, workflow, level) + ... + new_circuit = compile(circuit, model_t, optimization_level=level) + Raises: TypeError: If `machine_or_gateset` is not a MachineModel or GateSet. - TypeError: If `workflow` is not a list of BasePass objects. + Warning: If a workflow for a given optimization_level is overwritten. """ if not isinstance(machine_or_gateset, MachineModel): if isinstance(machine_or_gateset, Gate): @@ -75,9 +71,11 @@ def register_workflow( new_workflow = {optimization_level: workflow} if machine_or_gateset in _workflow_registry: if optimization_level in _workflow_registry[machine_or_gateset]: - m = f'Overwritting workflow for {machine_or_gateset} ' - m += f'at level {optimization_level}.' - _logger.warn(m) + m = f'Overwritting workflow for {machine_or_gateset} at level ' + m += f'{optimization_level}. If multiple Namespace packages are ' + m += 'installed, ensure that their __init__.py files do not ' + m += 'attempt to overwrite the same default Workflows.' + warnings.warn(m) _workflow_registry[machine_or_gateset].update(new_workflow) else: _workflow_registry[machine_or_gateset] = new_workflow From b6219a91ef6f1e2a5a2b89acff9a85c77854cce3 Mon Sep 17 00:00:00 2001 From: Mathias Weiden Date: Wed, 28 Aug 2024 09:26:34 -0700 Subject: [PATCH 20/29] Removed clear_registry function --- bqskit/compiler/registry.py | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/bqskit/compiler/registry.py b/bqskit/compiler/registry.py index 579d5579f..94ac532be 100644 --- a/bqskit/compiler/registry.py +++ b/bqskit/compiler/registry.py @@ -24,7 +24,7 @@ def register_workflow( The _workflow_registry enables MachineModel or GateSet specific workflows to be registered for use in the `bqskit.compile` method. _workflow_registry - maps MachineModels a dictionary of Workflows which are indexed by + maps MachineModels a dictionary of Workflows which are indexed by optimization level. This object should not be accessed directly by the user, but instead through the `register_workflow` function. @@ -79,13 +79,3 @@ def register_workflow( _workflow_registry[machine_or_gateset].update(new_workflow) else: _workflow_registry[machine_or_gateset] = new_workflow - - -def clear_registry() -> None: - """ - Clear the workflow registry. - - This will remove all registered workflows from the registry. - """ - global _workflow_registry - _workflow_registry.clear() From 6e31654f3fd378bd236574764f284ca9b4647e21 Mon Sep 17 00:00:00 2001 From: Mathias Weiden Date: Wed, 28 Aug 2024 09:26:55 -0700 Subject: [PATCH 21/29] Removed clear_registry function --- tests/compiler/test_registry.py | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/tests/compiler/test_registry.py b/tests/compiler/test_registry.py index ca037bca9..0c557be1c 100644 --- a/tests/compiler/test_registry.py +++ b/tests/compiler/test_registry.py @@ -10,7 +10,6 @@ from bqskit.compiler import compile from bqskit.compiler.machine import MachineModel from bqskit.compiler.registry import _workflow_registry -from bqskit.compiler.registry import clear_registry from bqskit.compiler.registry import register_workflow from bqskit.compiler.workflow import Workflow from bqskit.compiler.workflow import WorkflowLike @@ -74,26 +73,30 @@ class TestRegisterWorkflow: @pytest.fixture(autouse=True) def setup(self) -> None: - # _workflow_registry.clear() - clear_registry() + assert _workflow_registry == _workflow_registry + global _workflow_registry + _workflow_registry.clear() def test_register_workflow(self) -> None: + global _workflow_registry assert _workflow_registry == {} gateset = [CZGate(), HGate(), RZGate()] num_qudits = 3 machine = MachineModel(num_qudits, gate_set=gateset) workflow = [QuickPartitioner(), ScanningGateRemovalPass()] - register_workflow(machine, workflow) + register_workflow(machine, workflow, 1) assert machine in _workflow_registry assert 1 in _workflow_registry[machine] assert workflow_match(_workflow_registry[machine][1], workflow) def test_custom_compile_machine(self) -> None: + global _workflow_registry + assert _workflow_registry == {} gateset = [CZGate(), HGate(), RZGate()] num_qudits = 3 machine = MachineModel(num_qudits, gate_set=gateset) workflow = [QuickPartitioner(2)] - register_workflow(machine, workflow) + register_workflow(machine, workflow, 1) circuit = simple_circuit(num_qudits, gateset) result = compile(circuit, machine) assert unitary_match(result, circuit) @@ -103,11 +106,13 @@ def test_custom_compile_machine(self) -> None: assert result.gate_counts == circuit.gate_counts def test_custom_compile_gateset(self) -> None: + global _workflow_registry + assert _workflow_registry == {} gateset = [CZGate(), HGate(), RZGate()] num_qudits = 3 machine = MachineModel(num_qudits, gate_set=gateset) workflow = [QuickPartitioner(2)] - register_workflow(gateset, workflow) + register_workflow(gateset, workflow, 1) circuit = simple_circuit(num_qudits, gateset) result = compile(circuit, machine) assert unitary_match(result, circuit) @@ -117,6 +122,8 @@ def test_custom_compile_gateset(self) -> None: assert result.gate_counts == circuit.gate_counts def test_custom_opt_level(self) -> None: + global _workflow_registry + assert _workflow_registry == {} gateset = [CZGate(), HGate(), RZGate()] num_qudits = 3 machine = MachineModel(num_qudits, gate_set=gateset) From 6e010107a73e644d78a40dad2710f6a0690a928f Mon Sep 17 00:00:00 2001 From: Mathias Weiden Date: Wed, 28 Aug 2024 09:32:04 -0700 Subject: [PATCH 22/29] Changed test --- tests/compiler/test_registry.py | 1 - 1 file changed, 1 deletion(-) diff --git a/tests/compiler/test_registry.py b/tests/compiler/test_registry.py index 0c557be1c..cce2eaab6 100644 --- a/tests/compiler/test_registry.py +++ b/tests/compiler/test_registry.py @@ -73,7 +73,6 @@ class TestRegisterWorkflow: @pytest.fixture(autouse=True) def setup(self) -> None: - assert _workflow_registry == _workflow_registry global _workflow_registry _workflow_registry.clear() From 46972c184fb4574ca8d70745cf021a3cac05ccb3 Mon Sep 17 00:00:00 2001 From: Mathias Weiden Date: Wed, 28 Aug 2024 09:35:31 -0700 Subject: [PATCH 23/29] Fixed import global conflict --- tests/compiler/test_registry.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/compiler/test_registry.py b/tests/compiler/test_registry.py index cce2eaab6..b5a3d501f 100644 --- a/tests/compiler/test_registry.py +++ b/tests/compiler/test_registry.py @@ -73,7 +73,7 @@ class TestRegisterWorkflow: @pytest.fixture(autouse=True) def setup(self) -> None: - global _workflow_registry + # global _workflow_registry _workflow_registry.clear() def test_register_workflow(self) -> None: From 433a10817b6a2a7ff3fd80c05426e92cd3b773c1 Mon Sep 17 00:00:00 2001 From: Mathias Weiden Date: Wed, 28 Aug 2024 10:56:45 -0700 Subject: [PATCH 24/29] MachineModels registered in _compile_registry --- bqskit/compiler/registry.py | 55 +++++++++++++------------------------ 1 file changed, 19 insertions(+), 36 deletions(-) diff --git a/bqskit/compiler/registry.py b/bqskit/compiler/registry.py index 94ac532be..6097a4e78 100644 --- a/bqskit/compiler/registry.py +++ b/bqskit/compiler/registry.py @@ -1,43 +1,38 @@ -"""Register GateSet or MachineModel specific default workflows.""" +"""Register MachineModel specific default workflows.""" from __future__ import annotations import warnings -from bqskit.compiler.gateset import GateSet -from bqskit.compiler.gateset import GateSetLike from bqskit.compiler.machine import MachineModel from bqskit.compiler.workflow import Workflow from bqskit.compiler.workflow import WorkflowLike -from bqskit.ir.gate import Gate -_workflow_registry: dict[MachineModel | GateSet, dict[int, Workflow]] = {} +_compile_registry: dict[MachineModel, dict[int, Workflow]] = {} def register_workflow( - machine_or_gateset: MachineModel | GateSetLike, + key: MachineModel, workflow: WorkflowLike, optimization_level: int, ) -> None: """ - Register a workflow for a given machine model. + Register a workflow for a given MachineModel. - The _workflow_registry enables MachineModel or GateSet specific workflows - to be registered for use in the `bqskit.compile` method. _workflow_registry - maps MachineModels a dictionary of Workflows which are indexed by - optimization level. This object should not be accessed directly by the user, - but instead through the `register_workflow` function. + The _compile_registry enables MachineModel specific workflows to be + registered for use in the `bqskit.compile` method. _compile_registry maps + MachineModels a dictionary of Workflows which are indexed by optimization + level. This object should not be accessed directly by the user, but + instead through the `register_workflow` function. Args: - machine_or_gateset (MachineModel | GateSetLike): A MachineModel or - GateSetLike to register the workflow under. If a circuit is - compiled targeting this machine or gate set, the registered - workflow will be used. + key (MachineModel): A MachineModel to register the workflow under. + If a circuit is compiled targeting this machine or gate set, the + registered workflow will be used. workflow (list[BasePass]): The workflow or list of passes that will be executed if the MachineModel in a call to `compile` matches - `machine`. If `machine` is already registered, a warning will be - logged. + `key`. If `key` is already registered, a warning will be logged. optimization_level ptional[int): The optimization level with which to register the workflow. If no level is provided, the Workflow @@ -51,31 +46,19 @@ def register_workflow( new_circuit = compile(circuit, model_t, optimization_level=level) Raises: - TypeError: If `machine_or_gateset` is not a MachineModel or GateSet. - Warning: If a workflow for a given optimization_level is overwritten. """ - if not isinstance(machine_or_gateset, MachineModel): - if isinstance(machine_or_gateset, Gate): - machine_or_gateset = [machine_or_gateset] - if all(isinstance(g, Gate) for g in machine_or_gateset): - machine_or_gateset = GateSet(machine_or_gateset) - else: - m = '`machine_or_gateset` must be a MachineModel or ' - m += f'GateSet, got {type(machine_or_gateset)}.' - raise TypeError(m) - workflow = Workflow(workflow) - global _workflow_registry + global _compile_registry new_workflow = {optimization_level: workflow} - if machine_or_gateset in _workflow_registry: - if optimization_level in _workflow_registry[machine_or_gateset]: - m = f'Overwritting workflow for {machine_or_gateset} at level ' + if key in _compile_registry: + if optimization_level in _compile_registry[key]: + m = f'Overwritting workflow for {key} at level ' m += f'{optimization_level}. If multiple Namespace packages are ' m += 'installed, ensure that their __init__.py files do not ' m += 'attempt to overwrite the same default Workflows.' warnings.warn(m) - _workflow_registry[machine_or_gateset].update(new_workflow) + _compile_registry[key].update(new_workflow) else: - _workflow_registry[machine_or_gateset] = new_workflow + _compile_registry[key] = new_workflow From 74e53a00c95dc62de6cc236ae99b334d413f3142 Mon Sep 17 00:00:00 2001 From: Mathias Weiden Date: Wed, 28 Aug 2024 10:57:49 -0700 Subject: [PATCH 25/29] only considers registered MachineModels --- bqskit/compiler/compile.py | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/bqskit/compiler/compile.py b/bqskit/compiler/compile.py index c6b9f8bab..4cea33922 100644 --- a/bqskit/compiler/compile.py +++ b/bqskit/compiler/compile.py @@ -16,7 +16,7 @@ from bqskit.compiler.gateset import GateSet from bqskit.compiler.machine import MachineModel from bqskit.compiler.passdata import PassData -from bqskit.compiler.registry import _workflow_registry +from bqskit.compiler.registry import _compile_registry from bqskit.compiler.workflow import Workflow from bqskit.compiler.workflow import WorkflowLike from bqskit.ir.circuit import Circuit @@ -673,15 +673,9 @@ def build_workflow( # Use a registered workflow if model is found in the registry for a given # optimization_level - for machine_or_gateset in _workflow_registry: - if isinstance(machine_or_gateset, GateSet): - gate_set = machine_or_gateset - else: - gate_set = machine_or_gateset.gate_set - gs_match = gate_set == model.gate_set - ol_found = optimization_level in _workflow_registry[machine_or_gateset] - if gs_match and ol_found: - return _workflow_registry[machine_or_gateset][optimization_level] + if model in _compile_registry: + if optimization_level in _compile_registry[model]: + return _compile_registry[model][optimization_level] if isinstance(input, Circuit): if input.num_qudits > max_synthesis_size: From cfa229d3e7d966f659acca5538cea4bb3e302f19 Mon Sep 17 00:00:00 2001 From: Mathias Weiden Date: Wed, 28 Aug 2024 11:03:08 -0700 Subject: [PATCH 26/29] Updated tests for _compile_registry --- tests/compiler/test_registry.py | 42 ++++++++++----------------------- 1 file changed, 13 insertions(+), 29 deletions(-) diff --git a/tests/compiler/test_registry.py b/tests/compiler/test_registry.py index b5a3d501f..792828e83 100644 --- a/tests/compiler/test_registry.py +++ b/tests/compiler/test_registry.py @@ -9,7 +9,7 @@ from bqskit.compiler import compile from bqskit.compiler.machine import MachineModel -from bqskit.compiler.registry import _workflow_registry +from bqskit.compiler.registry import _compile_registry from bqskit.compiler.registry import register_workflow from bqskit.compiler.workflow import Workflow from bqskit.compiler.workflow import WorkflowLike @@ -73,24 +73,24 @@ class TestRegisterWorkflow: @pytest.fixture(autouse=True) def setup(self) -> None: - # global _workflow_registry - _workflow_registry.clear() + # global _compile_registry + _compile_registry.clear() def test_register_workflow(self) -> None: - global _workflow_registry - assert _workflow_registry == {} + global _compile_registry + assert _compile_registry == {} gateset = [CZGate(), HGate(), RZGate()] num_qudits = 3 machine = MachineModel(num_qudits, gate_set=gateset) workflow = [QuickPartitioner(), ScanningGateRemovalPass()] register_workflow(machine, workflow, 1) - assert machine in _workflow_registry - assert 1 in _workflow_registry[machine] - assert workflow_match(_workflow_registry[machine][1], workflow) + assert machine in _compile_registry + assert 1 in _compile_registry[machine] + assert workflow_match(_compile_registry[machine][1], workflow) def test_custom_compile_machine(self) -> None: - global _workflow_registry - assert _workflow_registry == {} + global _compile_registry + assert _compile_registry == {} gateset = [CZGate(), HGate(), RZGate()] num_qudits = 3 machine = MachineModel(num_qudits, gate_set=gateset) @@ -104,30 +104,14 @@ def test_custom_compile_machine(self) -> None: result.unfold_all() assert result.gate_counts == circuit.gate_counts - def test_custom_compile_gateset(self) -> None: - global _workflow_registry - assert _workflow_registry == {} - gateset = [CZGate(), HGate(), RZGate()] - num_qudits = 3 - machine = MachineModel(num_qudits, gate_set=gateset) - workflow = [QuickPartitioner(2)] - register_workflow(gateset, workflow, 1) - circuit = simple_circuit(num_qudits, gateset) - result = compile(circuit, machine) - assert unitary_match(result, circuit) - assert result.num_operations > 0 - assert result.gate_counts != circuit.gate_counts - result.unfold_all() - assert result.gate_counts == circuit.gate_counts - def test_custom_opt_level(self) -> None: - global _workflow_registry - assert _workflow_registry == {} + global _compile_registry + assert _compile_registry == {} gateset = [CZGate(), HGate(), RZGate()] num_qudits = 3 machine = MachineModel(num_qudits, gate_set=gateset) workflow = [QSearchSynthesisPass()] - register_workflow(gateset, workflow, 2) + register_workflow(machine, workflow, 2) circuit = simple_circuit(num_qudits, gateset) result = compile(circuit, machine, optimization_level=2) assert unitary_match(result, circuit) From 47fa2ed2e4de08c0b597fb001fd069d199f5aa36 Mon Sep 17 00:00:00 2001 From: Mathias Weiden Date: Wed, 28 Aug 2024 11:04:39 -0700 Subject: [PATCH 27/29] Removed unused import --- bqskit/compiler/compile.py | 1 - 1 file changed, 1 deletion(-) diff --git a/bqskit/compiler/compile.py b/bqskit/compiler/compile.py index 4cea33922..cac057bd3 100644 --- a/bqskit/compiler/compile.py +++ b/bqskit/compiler/compile.py @@ -13,7 +13,6 @@ import numpy as np from bqskit.compiler.compiler import Compiler -from bqskit.compiler.gateset import GateSet from bqskit.compiler.machine import MachineModel from bqskit.compiler.passdata import PassData from bqskit.compiler.registry import _compile_registry From bae9fee7d3809f6c850bbefd0d1efdb77fc56b1e Mon Sep 17 00:00:00 2001 From: Mathias Weiden Date: Wed, 28 Aug 2024 11:05:22 -0700 Subject: [PATCH 28/29] pre-commit --- bqskit/compiler/registry.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/bqskit/compiler/registry.py b/bqskit/compiler/registry.py index 6097a4e78..b3cb1b4d2 100644 --- a/bqskit/compiler/registry.py +++ b/bqskit/compiler/registry.py @@ -19,14 +19,14 @@ def register_workflow( """ Register a workflow for a given MachineModel. - The _compile_registry enables MachineModel specific workflows to be + The _compile_registry enables MachineModel specific workflows to be registered for use in the `bqskit.compile` method. _compile_registry maps MachineModels a dictionary of Workflows which are indexed by optimization - level. This object should not be accessed directly by the user, but + level. This object should not be accessed directly by the user, but instead through the `register_workflow` function. Args: - key (MachineModel): A MachineModel to register the workflow under. + key (MachineModel): A MachineModel to register the workflow under. If a circuit is compiled targeting this machine or gate set, the registered workflow will be used. From 1f94658cfaf620b2a2eb394e08c4f1500f6970bb Mon Sep 17 00:00:00 2001 From: Mathias Weiden Date: Wed, 28 Aug 2024 11:11:26 -0700 Subject: [PATCH 29/29] Fixed imports --- tests/compiler/test_registry.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tests/compiler/test_registry.py b/tests/compiler/test_registry.py index 792828e83..6371211c9 100644 --- a/tests/compiler/test_registry.py +++ b/tests/compiler/test_registry.py @@ -7,7 +7,7 @@ import pytest from numpy import allclose -from bqskit.compiler import compile +from bqskit.compiler.compile import compile from bqskit.compiler.machine import MachineModel from bqskit.compiler.registry import _compile_registry from bqskit.compiler.registry import register_workflow