From b6ab104f774829bbbdfbf194426b1044018f4ab3 Mon Sep 17 00:00:00 2001 From: Ammar Eltigani Date: Tue, 28 Jun 2022 16:37:33 -0700 Subject: [PATCH 01/12] deprecated and replaced StrategyExecutor class with @cirq.transformer --- .../cirq/contrib/acquaintance/executor.py | 46 ++++++++++++++++++- .../contrib/acquaintance/executor_test.py | 17 +++++-- 2 files changed, 58 insertions(+), 5 deletions(-) diff --git a/cirq-core/cirq/contrib/acquaintance/executor.py b/cirq-core/cirq/contrib/acquaintance/executor.py index 9939b36628a..1c10a62e8fc 100644 --- a/cirq-core/cirq/contrib/acquaintance/executor.py +++ b/cirq-core/cirq/contrib/acquaintance/executor.py @@ -12,6 +12,8 @@ # See the License for the specific language governing permissions and # limitations under the License. +from argparse import ArgumentError +from optparse import Option from typing import DefaultDict, Dict, Sequence, TYPE_CHECKING, Optional import abc @@ -63,7 +65,12 @@ def get_operations( def __call__(self, *args, **kwargs): return StrategyExecutor(self)(*args, **kwargs) - +@cirq._compat.deprecated_class( + deadline='v1.0', + fix='Use cirq.contrib.acquaintance.executor.strategy_executor instead.', + # or shoudl it be cirq.contrib.acquaintance.strategy_executor with + # strategy_executor imported in __init__.py ? +) class StrategyExecutor(circuits.PointOptimizer): """Executes an acquaintance strategy.""" @@ -99,6 +106,43 @@ def optimization_at( 'PermutationGate.' ) +@cirq.transformer +def strategy_executor( + circuit: 'cirq.Circuit', + *, + execution_strategy: ExecutionStrategy = None, + context: Optional['cirq.TransformerContext'] = None + ) -> None: + """ Executes an acquaintance strategy""" + + if execution_strategy == None: raise ArgumentError( + 'Execution strategy cannot be None' + ) + + mapping = execution_strategy.initial_mapping.copy() + + def map_func(op: 'cirq.Operation', index) -> 'cirq.OP_TREE': + if isinstance(op.gate, AcquaintanceOpportunityGate): + logical_indices = tuple(mapping[q] for q in op.qubits) + logical_operations = execution_strategy.get_operations(logical_indices, op.qubits) + clear_span = int(not execution_strategy.keep_acquaintance) + new_operation = logical_operations.on(*logical_indices) + + return new_operation if clear_span else [op, new_operation] + + if isinstance(op, ops.GateOperation) and isinstance(op.gate, PermutationGate): + op.gate.update_mapping(mapping, op.qubits) + + return op + + expose_acquaintance_gates(circuit) + return cirq.map_operations( + circuit=circuit, + map_func=map_func, + deep=context.deep if context else False, + tags_to_ignore=context.tags_to_ignore if context else () + ) + class AcquaintanceOperation(ops.GateOperation): """Represents an a acquaintance opportunity between a particular set of diff --git a/cirq-core/cirq/contrib/acquaintance/executor_test.py b/cirq-core/cirq/contrib/acquaintance/executor_test.py index eae7acbb0f0..b9fdcb1e5ac 100644 --- a/cirq-core/cirq/contrib/acquaintance/executor_test.py +++ b/cirq-core/cirq/contrib/acquaintance/executor_test.py @@ -48,7 +48,10 @@ def test_executor_explicit(): } initial_mapping = {q: i for i, q in enumerate(sorted(qubits))} execution_strategy = cca.GreedyExecutionStrategy(gates, initial_mapping) - executor = cca.StrategyExecutor(execution_strategy) + with cirq.testing.assert_deprecated( + "Use cirq.contrib.acquaintance.strategy_executor", deadline='v1.0' + ): + executor = cca.StrategyExecutor(execution_strategy) with pytest.raises(NotImplementedError): bad_gates = {(0,): ExampleGate(['0']), (0, 1): ExampleGate(['0', '1'])} @@ -56,14 +59,20 @@ def test_executor_explicit(): with pytest.raises(TypeError): bad_strategy = cirq.Circuit(cirq.X(qubits[0])) - executor(bad_strategy) + with cirq.testing.assert_deprecated( + "Use cirq.contrib.acquaintance.strategy_executor", deadline='v1.0' + ): + executor(bad_strategy) with pytest.raises(TypeError): op = cirq.X(qubits[0]) bad_strategy = cirq.Circuit(op) - executor.optimization_at(bad_strategy, 0, op) + with cirq.testing.assert_deprecated( + "Use cirq.contrib.acquaintance.strategy_executor", deadline='v1.0' + ): + executor.optimization_at(bad_strategy, 0, op) + executor(circuit) - executor(circuit) expected_text_diagram = """ 0: ───0───1───╲0╱─────────────────1───3───╲0╱─────────────────3───5───╲0╱─────────────────5───7───╲0╱───────────────── │ │ │ │ │ │ │ │ │ │ │ │ From e562b6f818e093185532dd8eaa319a4b77255dd5 Mon Sep 17 00:00:00 2001 From: Ammar Eltigani Date: Wed, 29 Jun 2022 12:17:56 -0700 Subject: [PATCH 02/12] added deprec --- .../cirq/contrib/acquaintance/__init__.py | 1 + .../cirq/contrib/acquaintance/executor.py | 39 +++++++++++-------- .../contrib/acquaintance/executor_test.py | 12 ++++-- 3 files changed, 32 insertions(+), 20 deletions(-) diff --git a/cirq-core/cirq/contrib/acquaintance/__init__.py b/cirq-core/cirq/contrib/acquaintance/__init__.py index 56093a761f0..17e8b6f044a 100644 --- a/cirq-core/cirq/contrib/acquaintance/__init__.py +++ b/cirq-core/cirq/contrib/acquaintance/__init__.py @@ -22,6 +22,7 @@ AcquaintanceOperation, GreedyExecutionStrategy, StrategyExecutor, + strategy_executor, ) from cirq.contrib.acquaintance.gates import acquaint, AcquaintanceOpportunityGate, SwapNetworkGate diff --git a/cirq-core/cirq/contrib/acquaintance/executor.py b/cirq-core/cirq/contrib/acquaintance/executor.py index 1c10a62e8fc..469cbab9d41 100644 --- a/cirq-core/cirq/contrib/acquaintance/executor.py +++ b/cirq-core/cirq/contrib/acquaintance/executor.py @@ -18,6 +18,8 @@ import abc from collections import defaultdict +import cirq + from cirq import circuits, devices, ops, protocols @@ -63,13 +65,12 @@ def get_operations( """Gets the logical operations to apply to qubits.""" def __call__(self, *args, **kwargs): - return StrategyExecutor(self)(*args, **kwargs) + return StrategyExecutor(self)(*args, **kwargs) + @cirq._compat.deprecated_class( deadline='v1.0', - fix='Use cirq.contrib.acquaintance.executor.strategy_executor instead.', - # or shoudl it be cirq.contrib.acquaintance.strategy_executor with - # strategy_executor imported in __init__.py ? + fix='Use cirq.contrib.acquaintance.strategy_executor.', ) class StrategyExecutor(circuits.PointOptimizer): """Executes an acquaintance strategy.""" @@ -106,18 +107,18 @@ def optimization_at( 'PermutationGate.' ) + @cirq.transformer def strategy_executor( - circuit: 'cirq.Circuit', + circuit: 'cirq.Circuit', *, execution_strategy: ExecutionStrategy = None, - context: Optional['cirq.TransformerContext'] = None - ) -> None: - """ Executes an acquaintance strategy""" + context: Optional['cirq.TransformerContext'] = None, +) -> None: + """Executes an acquaintance strategy""" - if execution_strategy == None: raise ArgumentError( - 'Execution strategy cannot be None' - ) + if execution_strategy is None: + raise ArgumentError(execution_strategy, 'Execution strategy cannot be None') mapping = execution_strategy.initial_mapping.copy() @@ -127,22 +128,26 @@ def map_func(op: 'cirq.Operation', index) -> 'cirq.OP_TREE': logical_operations = execution_strategy.get_operations(logical_indices, op.qubits) clear_span = int(not execution_strategy.keep_acquaintance) new_operation = logical_operations.on(*logical_indices) - return new_operation if clear_span else [op, new_operation] - + if isinstance(op, ops.GateOperation) and isinstance(op.gate, PermutationGate): op.gate.update_mapping(mapping, op.qubits) + return op - return op - + raise TypeError( + 'Can only execute a strategy consisting of gates that ' + 'are instances of AcquaintanceOpportunityGate or ' + 'PermutationGate.' + ) + expose_acquaintance_gates(circuit) return cirq.map_operations( circuit=circuit, map_func=map_func, deep=context.deep if context else False, - tags_to_ignore=context.tags_to_ignore if context else () + tags_to_ignore=context.tags_to_ignore if context else (), ) - + class AcquaintanceOperation(ops.GateOperation): """Represents an a acquaintance opportunity between a particular set of diff --git a/cirq-core/cirq/contrib/acquaintance/executor_test.py b/cirq-core/cirq/contrib/acquaintance/executor_test.py index b9fdcb1e5ac..d6fea30637a 100644 --- a/cirq-core/cirq/contrib/acquaintance/executor_test.py +++ b/cirq-core/cirq/contrib/acquaintance/executor_test.py @@ -47,15 +47,18 @@ def test_executor_explicit(): for i, j in (ij, ij[::-1]) } initial_mapping = {q: i for i, q in enumerate(sorted(qubits))} - execution_strategy = cca.GreedyExecutionStrategy(gates, initial_mapping) with cirq.testing.assert_deprecated( "Use cirq.contrib.acquaintance.strategy_executor", deadline='v1.0' ): + execution_strategy = cca.GreedyExecutionStrategy(gates, initial_mapping) executor = cca.StrategyExecutor(execution_strategy) with pytest.raises(NotImplementedError): bad_gates = {(0,): ExampleGate(['0']), (0, 1): ExampleGate(['0', '1'])} - cca.GreedyExecutionStrategy(bad_gates, initial_mapping) + with cirq.testing.assert_deprecated( + "Use cirq.contrib.acquaintance.strategy_executor", deadline='v1.0' + ): + cca.GreedyExecutionStrategy(bad_gates, initial_mapping) with pytest.raises(TypeError): bad_strategy = cirq.Circuit(cirq.X(qubits[0])) @@ -123,7 +126,10 @@ def test_executor_random( expected_unitary = logical_circuit.unitary() initial_mapping = {q: q for q in qubits} - final_mapping = cca.GreedyExecutionStrategy(gates, initial_mapping)(circuit) + with cirq.testing.assert_deprecated( + "Use cirq.contrib.acquaintance.strategy_executor", deadline='v1.0' + ): + final_mapping = cca.GreedyExecutionStrategy(gates, initial_mapping)(circuit) permutation = {q.x: qq.x for q, qq in final_mapping.items()} circuit.append(cca.LinearPermutationGate(num_qubits, permutation)(*qubits)) actual_unitary = circuit.unitary() From a5e394607f3a28765f37844ec37c416542e9f6b4 Mon Sep 17 00:00:00 2001 From: Ammar Eltigani Date: Wed, 29 Jun 2022 13:10:03 -0700 Subject: [PATCH 03/12] formatting --- cirq-core/cirq/contrib/acquaintance/executor.py | 7 +++---- cirq-core/cirq/contrib/acquaintance/executor_test.py | 8 ++++---- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/cirq-core/cirq/contrib/acquaintance/executor.py b/cirq-core/cirq/contrib/acquaintance/executor.py index 469cbab9d41..be08a0d7321 100644 --- a/cirq-core/cirq/contrib/acquaintance/executor.py +++ b/cirq-core/cirq/contrib/acquaintance/executor.py @@ -65,12 +65,11 @@ def get_operations( """Gets the logical operations to apply to qubits.""" def __call__(self, *args, **kwargs): - return StrategyExecutor(self)(*args, **kwargs) + return StrategyExecutor(self)(*args, **kwargs) @cirq._compat.deprecated_class( - deadline='v1.0', - fix='Use cirq.contrib.acquaintance.strategy_executor.', + deadline='v1.0', fix='Use cirq.contrib.acquaintance.strategy_executor.' ) class StrategyExecutor(circuits.PointOptimizer): """Executes an acquaintance strategy.""" @@ -139,7 +138,7 @@ def map_func(op: 'cirq.Operation', index) -> 'cirq.OP_TREE': 'are instances of AcquaintanceOpportunityGate or ' 'PermutationGate.' ) - + expose_acquaintance_gates(circuit) return cirq.map_operations( circuit=circuit, diff --git a/cirq-core/cirq/contrib/acquaintance/executor_test.py b/cirq-core/cirq/contrib/acquaintance/executor_test.py index 1c6160423da..7608c2c3d6b 100644 --- a/cirq-core/cirq/contrib/acquaintance/executor_test.py +++ b/cirq-core/cirq/contrib/acquaintance/executor_test.py @@ -48,8 +48,8 @@ def test_executor_explicit(): } initial_mapping = {q: i for i, q in enumerate(sorted(qubits))} with cirq.testing.assert_deprecated( - "Use cirq.contrib.acquaintance.strategy_executor", deadline='v1.0' - ): + "Use cirq.contrib.acquaintance.strategy_executor", deadline='v1.0' + ): execution_strategy = cca.GreedyExecutionStrategy(gates, initial_mapping) executor = cca.StrategyExecutor(execution_strategy) @@ -127,8 +127,8 @@ def test_executor_random( initial_mapping = {q: q for q in qubits} with cirq.testing.assert_deprecated( - "Use cirq.contrib.acquaintance.strategy_executor", deadline='v1.0' - ): + "Use cirq.contrib.acquaintance.strategy_executor", deadline='v1.0' + ): final_mapping = cca.GreedyExecutionStrategy(gates, initial_mapping)(circuit) permutation = {q.x: qq.x for q, qq in final_mapping.items()} circuit.append(cca.LinearPermutationGate(num_qubits, permutation)(*qubits)) From e0e006e87b1ca9e848e62b71459f52040e9ce2a8 Mon Sep 17 00:00:00 2001 From: Ammar Eltigani Date: Wed, 29 Jun 2022 13:14:19 -0700 Subject: [PATCH 04/12] formatting --- cirq-core/cirq/contrib/acquaintance/executor.py | 1 - 1 file changed, 1 deletion(-) diff --git a/cirq-core/cirq/contrib/acquaintance/executor.py b/cirq-core/cirq/contrib/acquaintance/executor.py index be08a0d7321..0b124314173 100644 --- a/cirq-core/cirq/contrib/acquaintance/executor.py +++ b/cirq-core/cirq/contrib/acquaintance/executor.py @@ -13,7 +13,6 @@ # limitations under the License. from argparse import ArgumentError -from optparse import Option from typing import DefaultDict, Dict, Sequence, TYPE_CHECKING, Optional import abc From db08b818e06bbaceebf19af627e5ebaf9ad73a0f Mon Sep 17 00:00:00 2001 From: Ammar Eltigani Date: Wed, 29 Jun 2022 16:49:27 -0700 Subject: [PATCH 05/12] test_executor_random does not pass with new strategy_executor --- .../cirq/contrib/acquaintance/executor.py | 47 ++++++++++++++----- .../contrib/acquaintance/executor_test.py | 40 ++++++++++++---- 2 files changed, 65 insertions(+), 22 deletions(-) diff --git a/cirq-core/cirq/contrib/acquaintance/executor.py b/cirq-core/cirq/contrib/acquaintance/executor.py index 0b124314173..51ab0762ec8 100644 --- a/cirq-core/cirq/contrib/acquaintance/executor.py +++ b/cirq-core/cirq/contrib/acquaintance/executor.py @@ -19,8 +19,7 @@ from collections import defaultdict import cirq - -from cirq import circuits, devices, ops, protocols +from cirq import circuits, devices, ops, protocols, transformers from cirq.contrib.acquaintance.gates import AcquaintanceOpportunityGate from cirq.contrib.acquaintance.permutation import ( @@ -32,9 +31,6 @@ ) from cirq.contrib.acquaintance.mutation_utils import expose_acquaintance_gates -if TYPE_CHECKING: - import cirq - class ExecutionStrategy(metaclass=abc.ABCMeta): """Tells StrategyExecutor how to execute an acquaintance strategy. @@ -64,7 +60,15 @@ def get_operations( """Gets the logical operations to apply to qubits.""" def __call__(self, *args, **kwargs): + if args[0] is None: + raise ValueError( + ( + "To call ExecutionStrategy, an argument of type " + "'cirq.Circuit' must be passed in as the first non-keyword argument" + ) + ) return StrategyExecutor(self)(*args, **kwargs) + # return strategy_executor(args[0], execution_strategy=self, *args[1:], **kwargs) @cirq._compat.deprecated_class( @@ -112,23 +116,39 @@ def strategy_executor( *, execution_strategy: ExecutionStrategy = None, context: Optional['cirq.TransformerContext'] = None, -) -> None: - """Executes an acquaintance strategy""" +) -> LogicalMapping: + """Executes an acquaintance strategy. + + Args: + circuit: Input circuit to transform. + execution_strategy: 'ExecutionStrategy' tells what gates to + implement at the available acquaintance opportunities + context: `cirq.TransformerContext` storing common configurable + options for transformers. + + Raises: + ValueError: if execution_strategy is None + TypeError: if execution_strategy neither an instance of + AcquaintanceOpportunityGate or PermutationGate + + Returns: + mapping: LogicalMapping mapping from qubits to logical indices + + """ if execution_strategy is None: - raise ArgumentError(execution_strategy, 'Execution strategy cannot be None') + raise ValueError('execution_strategy cannot be None') - mapping = execution_strategy.initial_mapping.copy() + mapping = execution_strategy.initial_mapping def map_func(op: 'cirq.Operation', index) -> 'cirq.OP_TREE': if isinstance(op.gate, AcquaintanceOpportunityGate): logical_indices = tuple(mapping[q] for q in op.qubits) logical_operations = execution_strategy.get_operations(logical_indices, op.qubits) clear_span = int(not execution_strategy.keep_acquaintance) - new_operation = logical_operations.on(*logical_indices) - return new_operation if clear_span else [op, new_operation] + return logical_operations if clear_span else [op, logical_operations] - if isinstance(op, ops.GateOperation) and isinstance(op.gate, PermutationGate): + if isinstance(op.gate, PermutationGate): op.gate.update_mapping(mapping, op.qubits) return op @@ -139,12 +159,13 @@ def map_func(op: 'cirq.Operation', index) -> 'cirq.OP_TREE': ) expose_acquaintance_gates(circuit) - return cirq.map_operations( + transformers.map_operations( circuit=circuit, map_func=map_func, deep=context.deep if context else False, tags_to_ignore=context.tags_to_ignore if context else (), ) + return mapping class AcquaintanceOperation(ops.GateOperation): diff --git a/cirq-core/cirq/contrib/acquaintance/executor_test.py b/cirq-core/cirq/contrib/acquaintance/executor_test.py index 7608c2c3d6b..713e42d27c6 100644 --- a/cirq-core/cirq/contrib/acquaintance/executor_test.py +++ b/cirq-core/cirq/contrib/acquaintance/executor_test.py @@ -47,18 +47,15 @@ def test_executor_explicit(): for i, j in (ij, ij[::-1]) } initial_mapping = {q: i for i, q in enumerate(sorted(qubits))} + execution_strategy = cca.GreedyExecutionStrategy(gates, initial_mapping) with cirq.testing.assert_deprecated( "Use cirq.contrib.acquaintance.strategy_executor", deadline='v1.0' ): - execution_strategy = cca.GreedyExecutionStrategy(gates, initial_mapping) executor = cca.StrategyExecutor(execution_strategy) with pytest.raises(NotImplementedError): bad_gates = {(0,): ExampleGate(['0']), (0, 1): ExampleGate(['0', '1'])} - with cirq.testing.assert_deprecated( - "Use cirq.contrib.acquaintance.strategy_executor", deadline='v1.0' - ): - cca.GreedyExecutionStrategy(bad_gates, initial_mapping) + cca.GreedyExecutionStrategy(bad_gates, initial_mapping) with pytest.raises(TypeError): bad_strategy = cirq.Circuit(cirq.X(qubits[0])) @@ -116,27 +113,52 @@ def random_diagonal_gates( for _ in range(2) ], ) + + +# test_executor_random for StrategyExecutor def test_executor_random( num_qubits: int, acquaintance_size: int, gates: Dict[Tuple[cirq.Qid, ...], cirq.Gate] ): qubits = cirq.LineQubit.range(num_qubits) circuit = cca.complete_acquaintance_strategy(qubits, acquaintance_size) + print("Before mapping") + print(circuit) logical_circuit = cirq.Circuit([g(*Q) for Q, g in gates.items()]) expected_unitary = logical_circuit.unitary() initial_mapping = {q: q for q in qubits} - with cirq.testing.assert_deprecated( - "Use cirq.contrib.acquaintance.strategy_executor", deadline='v1.0' - ): - final_mapping = cca.GreedyExecutionStrategy(gates, initial_mapping)(circuit) + final_mapping = cca.GreedyExecutionStrategy(gates, initial_mapping)(circuit) permutation = {q.x: qq.x for q, qq in final_mapping.items()} circuit.append(cca.LinearPermutationGate(num_qubits, permutation)(*qubits)) + print("\nAfter mapping") + print(circuit) actual_unitary = circuit.unitary() np.testing.assert_allclose(actual=actual_unitary, desired=expected_unitary, verbose=True) +# # test_executor_random for StrategyExecutor +# def test_executor_random( +# num_qubits: int, acquaintance_size: int, gates: Dict[Tuple[cirq.Qid, ...], cirq.Gate] +# ): +# qubits = cirq.LineQubit.range(num_qubits) +# circuit = cca.complete_acquaintance_strategy(qubits, acquaintance_size) +# logical_circuit = cirq.Circuit([g(*Q) for Q, g in gates.items()]) +# expected_unitary = logical_circuit.unitary() + +# initial_mapping = {q: q for q in qubits} +# with cirq.testing.assert_deprecated( +# "Use cirq.contrib.acquaintance.strategy_executor", deadline='v1.0' +# ): +# final_mapping = cca.GreedyExecutionStrategy(gates, initial_mapping)(circuit) +# permutation = {q.x: qq.x for q, qq in final_mapping.items()} +# circuit.append(cca.LinearPermutationGate(num_qubits, permutation)(*qubits)) +# actual_unitary = circuit.unitary() + +# np.testing.assert_allclose(actual=actual_unitary, desired=expected_unitary, verbose=True) + + def test_acquaintance_operation(): n = 5 physical_qubits = tuple(cirq.LineQubit.range(n)) From f2dcfca40d31424748ab2e733401215e08bc3284 Mon Sep 17 00:00:00 2001 From: Ammar Eltigani Date: Wed, 29 Jun 2022 16:53:37 -0700 Subject: [PATCH 06/12] formatting --- cirq-core/cirq/contrib/acquaintance/executor.py | 1 - 1 file changed, 1 deletion(-) diff --git a/cirq-core/cirq/contrib/acquaintance/executor.py b/cirq-core/cirq/contrib/acquaintance/executor.py index 51ab0762ec8..310778561d3 100644 --- a/cirq-core/cirq/contrib/acquaintance/executor.py +++ b/cirq-core/cirq/contrib/acquaintance/executor.py @@ -12,7 +12,6 @@ # See the License for the specific language governing permissions and # limitations under the License. -from argparse import ArgumentError from typing import DefaultDict, Dict, Sequence, TYPE_CHECKING, Optional import abc From 9c11e99b81cf60ab77953fe51085c421a9c0ae38 Mon Sep 17 00:00:00 2001 From: Ammar Eltigani Date: Wed, 29 Jun 2022 22:41:27 -0700 Subject: [PATCH 07/12] fixed circuit mutation using GreedyExecutionStrategy --- .../cirq/contrib/acquaintance/__init__.py | 2 +- .../cirq/contrib/acquaintance/executor.py | 78 +++++++++---------- .../contrib/acquaintance/executor_test.py | 30 +------ 3 files changed, 40 insertions(+), 70 deletions(-) diff --git a/cirq-core/cirq/contrib/acquaintance/__init__.py b/cirq-core/cirq/contrib/acquaintance/__init__.py index 17e8b6f044a..9ca4d0b67a4 100644 --- a/cirq-core/cirq/contrib/acquaintance/__init__.py +++ b/cirq-core/cirq/contrib/acquaintance/__init__.py @@ -22,7 +22,7 @@ AcquaintanceOperation, GreedyExecutionStrategy, StrategyExecutor, - strategy_executor, + StrategyExecutorTransformer, ) from cirq.contrib.acquaintance.gates import acquaint, AcquaintanceOpportunityGate, SwapNetworkGate diff --git a/cirq-core/cirq/contrib/acquaintance/executor.py b/cirq-core/cirq/contrib/acquaintance/executor.py index 310778561d3..85f5d32ca3b 100644 --- a/cirq-core/cirq/contrib/acquaintance/executor.py +++ b/cirq-core/cirq/contrib/acquaintance/executor.py @@ -66,8 +66,8 @@ def __call__(self, *args, **kwargs): "'cirq.Circuit' must be passed in as the first non-keyword argument" ) ) - return StrategyExecutor(self)(*args, **kwargs) - # return strategy_executor(args[0], execution_strategy=self, *args[1:], **kwargs) + strategy = StrategyExecutorTransformer(self) + return strategy(*args, **kwargs), strategy.get_mapping() @cirq._compat.deprecated_class( @@ -109,46 +109,53 @@ def optimization_at( ) -@cirq.transformer -def strategy_executor( - circuit: 'cirq.Circuit', - *, - execution_strategy: ExecutionStrategy = None, - context: Optional['cirq.TransformerContext'] = None, -) -> LogicalMapping: - """Executes an acquaintance strategy. +class StrategyExecutorTransformer: + """Executes an acquaintance strategy.""" - Args: - circuit: Input circuit to transform. - execution_strategy: 'ExecutionStrategy' tells what gates to - implement at the available acquaintance opportunities - context: `cirq.TransformerContext` storing common configurable - options for transformers. + def __init__(self, execution_strategy: ExecutionStrategy) -> None: + self.execution_strategy = execution_strategy + self.mapping = execution_strategy.initial_mapping.copy() - Raises: - ValueError: if execution_strategy is None - TypeError: if execution_strategy neither an instance of - AcquaintanceOpportunityGate or PermutationGate + def __call__( + self, circuit: 'cirq.Circuit', context: Optional['cirq.TransformerContext'] = None + ) -> 'cirq.Cirquit': + """ + Args: + circuit: Input circuit to transform. + context: `cirq.TransformerContext` storing common configurable + options for transformers. - Returns: - mapping: LogicalMapping mapping from qubits to logical indices + Raises: + ValueError: if execution_strategy is None + TypeError: if execution_strategy neither an instance of + AcquaintanceOpportunityGate or PermutationGate - """ + Returns: + mapping: LogicalMapping mapping from qubits to logical indices + """ - if execution_strategy is None: - raise ValueError('execution_strategy cannot be None') + if self.execution_strategy is None: + raise ValueError('execution_strategy cannot be None') + expose_acquaintance_gates(circuit) + return transformers.map_operations( + circuit=circuit, + map_func=self.map_func, + deep=context.deep if context else False, + tags_to_ignore=context.tags_to_ignore if context else (), + ) - mapping = execution_strategy.initial_mapping + def get_mapping(self): + return self.mapping.copy() - def map_func(op: 'cirq.Operation', index) -> 'cirq.OP_TREE': + def map_func(self, op: 'cirq.Operation', index) -> 'cirq.OP_TREE': if isinstance(op.gate, AcquaintanceOpportunityGate): - logical_indices = tuple(mapping[q] for q in op.qubits) - logical_operations = execution_strategy.get_operations(logical_indices, op.qubits) - clear_span = int(not execution_strategy.keep_acquaintance) + logical_indices = tuple(self.mapping[q] for q in op.qubits) + logical_operations = self.execution_strategy.get_operations(logical_indices, op.qubits) + clear_span = int(not self.execution_strategy.keep_acquaintance) return logical_operations if clear_span else [op, logical_operations] if isinstance(op.gate, PermutationGate): - op.gate.update_mapping(mapping, op.qubits) + op.gate.update_mapping(self.mapping, op.qubits) return op raise TypeError( @@ -157,15 +164,6 @@ def map_func(op: 'cirq.Operation', index) -> 'cirq.OP_TREE': 'PermutationGate.' ) - expose_acquaintance_gates(circuit) - transformers.map_operations( - circuit=circuit, - map_func=map_func, - deep=context.deep if context else False, - tags_to_ignore=context.tags_to_ignore if context else (), - ) - return mapping - class AcquaintanceOperation(ops.GateOperation): """Represents an a acquaintance opportunity between a particular set of diff --git a/cirq-core/cirq/contrib/acquaintance/executor_test.py b/cirq-core/cirq/contrib/acquaintance/executor_test.py index 713e42d27c6..ca7e40f1659 100644 --- a/cirq-core/cirq/contrib/acquaintance/executor_test.py +++ b/cirq-core/cirq/contrib/acquaintance/executor_test.py @@ -113,52 +113,24 @@ def random_diagonal_gates( for _ in range(2) ], ) - - -# test_executor_random for StrategyExecutor def test_executor_random( num_qubits: int, acquaintance_size: int, gates: Dict[Tuple[cirq.Qid, ...], cirq.Gate] ): qubits = cirq.LineQubit.range(num_qubits) circuit = cca.complete_acquaintance_strategy(qubits, acquaintance_size) - print("Before mapping") - print(circuit) logical_circuit = cirq.Circuit([g(*Q) for Q, g in gates.items()]) expected_unitary = logical_circuit.unitary() initial_mapping = {q: q for q in qubits} - final_mapping = cca.GreedyExecutionStrategy(gates, initial_mapping)(circuit) + circuit, final_mapping = cca.GreedyExecutionStrategy(gates, initial_mapping)(circuit) permutation = {q.x: qq.x for q, qq in final_mapping.items()} circuit.append(cca.LinearPermutationGate(num_qubits, permutation)(*qubits)) - print("\nAfter mapping") - print(circuit) actual_unitary = circuit.unitary() np.testing.assert_allclose(actual=actual_unitary, desired=expected_unitary, verbose=True) -# # test_executor_random for StrategyExecutor -# def test_executor_random( -# num_qubits: int, acquaintance_size: int, gates: Dict[Tuple[cirq.Qid, ...], cirq.Gate] -# ): -# qubits = cirq.LineQubit.range(num_qubits) -# circuit = cca.complete_acquaintance_strategy(qubits, acquaintance_size) -# logical_circuit = cirq.Circuit([g(*Q) for Q, g in gates.items()]) -# expected_unitary = logical_circuit.unitary() - -# initial_mapping = {q: q for q in qubits} -# with cirq.testing.assert_deprecated( -# "Use cirq.contrib.acquaintance.strategy_executor", deadline='v1.0' -# ): -# final_mapping = cca.GreedyExecutionStrategy(gates, initial_mapping)(circuit) -# permutation = {q.x: qq.x for q, qq in final_mapping.items()} -# circuit.append(cca.LinearPermutationGate(num_qubits, permutation)(*qubits)) -# actual_unitary = circuit.unitary() - -# np.testing.assert_allclose(actual=actual_unitary, desired=expected_unitary, verbose=True) - - def test_acquaintance_operation(): n = 5 physical_qubits = tuple(cirq.LineQubit.range(n)) From 1828d619278ba1f841592e15de38c832ec078d47 Mon Sep 17 00:00:00 2001 From: Ammar Eltigani Date: Thu, 30 Jun 2022 10:41:07 -0700 Subject: [PATCH 08/12] ready to submit --- .../cirq/contrib/acquaintance/executor.py | 23 +++++++++++++------ .../contrib/acquaintance/executor_test.py | 14 ++++------- 2 files changed, 20 insertions(+), 17 deletions(-) diff --git a/cirq-core/cirq/contrib/acquaintance/executor.py b/cirq-core/cirq/contrib/acquaintance/executor.py index 85f5d32ca3b..5bfd9e1aa54 100644 --- a/cirq-core/cirq/contrib/acquaintance/executor.py +++ b/cirq-core/cirq/contrib/acquaintance/executor.py @@ -12,13 +12,13 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import DefaultDict, Dict, Sequence, TYPE_CHECKING, Optional +from typing import DefaultDict, Dict, Sequence, Optional import abc from collections import defaultdict -import cirq from cirq import circuits, devices, ops, protocols, transformers +import cirq from cirq.contrib.acquaintance.gates import AcquaintanceOpportunityGate from cirq.contrib.acquaintance.permutation import ( @@ -59,15 +59,21 @@ def get_operations( """Gets the logical operations to apply to qubits.""" def __call__(self, *args, **kwargs): - if args[0] is None: + """Returns a copy of the modified circuit and the final mapping of + logical indices to qubits + """ + if not isinstance(args[0], circuits.AbstractCircuit): raise ValueError( ( "To call ExecutionStrategy, an argument of type " "'cirq.Circuit' must be passed in as the first non-keyword argument" ) ) + input_circuit = args[0] strategy = StrategyExecutorTransformer(self) - return strategy(*args, **kwargs), strategy.get_mapping() + final_circuit = strategy(input_circuit, **kwargs) + input_circuit._moments = final_circuit._moments + return strategy.get_mapping() @cirq._compat.deprecated_class( @@ -118,8 +124,10 @@ def __init__(self, execution_strategy: ExecutionStrategy) -> None: def __call__( self, circuit: 'cirq.Circuit', context: Optional['cirq.TransformerContext'] = None - ) -> 'cirq.Cirquit': - """ + ) -> circuits.AbstractCircuit: + """Executes an acquaintance strategy using primitive map function and + mutates initial mapping. + Args: circuit: Input circuit to transform. context: `cirq.TransformerContext` storing common configurable @@ -131,7 +139,8 @@ def __call__( AcquaintanceOpportunityGate or PermutationGate Returns: - mapping: LogicalMapping mapping from qubits to logical indices + A copy of the modified circuit after executing an acquaintance + strategy on all instances of AcquaintanceOpportunityGate """ if self.execution_strategy is None: diff --git a/cirq-core/cirq/contrib/acquaintance/executor_test.py b/cirq-core/cirq/contrib/acquaintance/executor_test.py index ca7e40f1659..72423c71cf8 100644 --- a/cirq-core/cirq/contrib/acquaintance/executor_test.py +++ b/cirq-core/cirq/contrib/acquaintance/executor_test.py @@ -59,20 +59,14 @@ def test_executor_explicit(): with pytest.raises(TypeError): bad_strategy = cirq.Circuit(cirq.X(qubits[0])) - with cirq.testing.assert_deprecated( - "Use cirq.contrib.acquaintance.strategy_executor", deadline='v1.0' - ): - executor(bad_strategy) + executor(bad_strategy) with pytest.raises(TypeError): op = cirq.X(qubits[0]) bad_strategy = cirq.Circuit(op) - with cirq.testing.assert_deprecated( - "Use cirq.contrib.acquaintance.strategy_executor", deadline='v1.0' - ): - executor.optimization_at(bad_strategy, 0, op) - executor(circuit) + executor.optimization_at(bad_strategy, 0, op) + executor(circuit) expected_text_diagram = """ 0: ───0───1───╲0╱─────────────────1───3───╲0╱─────────────────3───5───╲0╱─────────────────5───7───╲0╱───────────────── │ │ │ │ │ │ │ │ │ │ │ │ @@ -123,7 +117,7 @@ def test_executor_random( expected_unitary = logical_circuit.unitary() initial_mapping = {q: q for q in qubits} - circuit, final_mapping = cca.GreedyExecutionStrategy(gates, initial_mapping)(circuit) + final_mapping = cca.GreedyExecutionStrategy(gates, initial_mapping)(circuit) permutation = {q.x: qq.x for q, qq in final_mapping.items()} circuit.append(cca.LinearPermutationGate(num_qubits, permutation)(*qubits)) actual_unitary = circuit.unitary() From 7f037e1290051215d23d798566d0950adf38c569 Mon Sep 17 00:00:00 2001 From: Ammar Eltigani Date: Thu, 30 Jun 2022 14:14:45 -0700 Subject: [PATCH 09/12] added test and addressed minor comments --- .../cirq/contrib/acquaintance/executor.py | 44 ++++++++-------- .../contrib/acquaintance/executor_test.py | 51 ++++++++++++++++++- 2 files changed, 73 insertions(+), 22 deletions(-) diff --git a/cirq-core/cirq/contrib/acquaintance/executor.py b/cirq-core/cirq/contrib/acquaintance/executor.py index 5bfd9e1aa54..8ac9d82c874 100644 --- a/cirq-core/cirq/contrib/acquaintance/executor.py +++ b/cirq-core/cirq/contrib/acquaintance/executor.py @@ -12,13 +12,12 @@ # See the License for the specific language governing permissions and # limitations under the License. -from typing import DefaultDict, Dict, Sequence, Optional +from typing import DefaultDict, Dict, Sequence, TYPE_CHECKING, Optional import abc from collections import defaultdict -from cirq import circuits, devices, ops, protocols, transformers -import cirq +from cirq import circuits, devices, ops, protocols, transformers, _compat from cirq.contrib.acquaintance.gates import AcquaintanceOpportunityGate from cirq.contrib.acquaintance.permutation import ( @@ -30,6 +29,9 @@ ) from cirq.contrib.acquaintance.mutation_utils import expose_acquaintance_gates +if TYPE_CHECKING: + import cirq + class ExecutionStrategy(metaclass=abc.ABCMeta): """Tells StrategyExecutor how to execute an acquaintance strategy. @@ -59,14 +61,14 @@ def get_operations( """Gets the logical operations to apply to qubits.""" def __call__(self, *args, **kwargs): - """Returns a copy of the modified circuit and the final mapping of - logical indices to qubits + """Returns the final mapping of logical indices to qubits after + executing an acquaintance strategy. """ - if not isinstance(args[0], circuits.AbstractCircuit): + if len(args) < 1 or not isinstance(args[0], circuits.AbstractCircuit): raise ValueError( ( "To call ExecutionStrategy, an argument of type " - "'cirq.Circuit' must be passed in as the first non-keyword argument" + "'cirq.AbstractCircuit' must be passed in as the first non-keyword argument" ) ) input_circuit = args[0] @@ -76,8 +78,8 @@ def __call__(self, *args, **kwargs): return strategy.get_mapping() -@cirq._compat.deprecated_class( - deadline='v1.0', fix='Use cirq.contrib.acquaintance.strategy_executor.' +@_compat.deprecated_class( + deadline='v1.0', fix='Use cirq.contrib.acquaintance.StrategyExecutorTransformer' ) class StrategyExecutor(circuits.PointOptimizer): """Executes an acquaintance strategy.""" @@ -119,12 +121,20 @@ class StrategyExecutorTransformer: """Executes an acquaintance strategy.""" def __init__(self, execution_strategy: ExecutionStrategy) -> None: + """Initializes transformer. + + Raises: + ValueError: if execution_strategy is None. + """ + + if execution_strategy is None: + raise ValueError('execution_strategy cannot be None') self.execution_strategy = execution_strategy self.mapping = execution_strategy.initial_mapping.copy() def __call__( - self, circuit: 'cirq.Circuit', context: Optional['cirq.TransformerContext'] = None - ) -> circuits.AbstractCircuit: + self, circuit: 'cirq.AbstractCircuit', context: Optional['cirq.TransformerContext'] = None + ) -> circuits.Circuit: """Executes an acquaintance strategy using primitive map function and mutates initial mapping. @@ -133,27 +143,20 @@ def __call__( context: `cirq.TransformerContext` storing common configurable options for transformers. - Raises: - ValueError: if execution_strategy is None - TypeError: if execution_strategy neither an instance of - AcquaintanceOpportunityGate or PermutationGate - Returns: A copy of the modified circuit after executing an acquaintance strategy on all instances of AcquaintanceOpportunityGate """ - if self.execution_strategy is None: - raise ValueError('execution_strategy cannot be None') expose_acquaintance_gates(circuit) - return transformers.map_operations( + return transformers.map_operations_and_unroll( circuit=circuit, map_func=self.map_func, deep=context.deep if context else False, tags_to_ignore=context.tags_to_ignore if context else (), ) - def get_mapping(self): + def get_mapping(self) -> LogicalMapping: return self.mapping.copy() def map_func(self, op: 'cirq.Operation', index) -> 'cirq.OP_TREE': @@ -161,6 +164,7 @@ def map_func(self, op: 'cirq.Operation', index) -> 'cirq.OP_TREE': logical_indices = tuple(self.mapping[q] for q in op.qubits) logical_operations = self.execution_strategy.get_operations(logical_indices, op.qubits) clear_span = int(not self.execution_strategy.keep_acquaintance) + return logical_operations if clear_span else [op, logical_operations] if isinstance(op.gate, PermutationGate): diff --git a/cirq-core/cirq/contrib/acquaintance/executor_test.py b/cirq-core/cirq/contrib/acquaintance/executor_test.py index 72423c71cf8..cb04e0fde69 100644 --- a/cirq-core/cirq/contrib/acquaintance/executor_test.py +++ b/cirq-core/cirq/contrib/acquaintance/executor_test.py @@ -14,7 +14,7 @@ from itertools import combinations from string import ascii_lowercase -from typing import Sequence, Dict, Tuple +from typing import Sequence, Dict, Tuple, Union import numpy as np import pytest @@ -49,7 +49,7 @@ def test_executor_explicit(): initial_mapping = {q: i for i, q in enumerate(sorted(qubits))} execution_strategy = cca.GreedyExecutionStrategy(gates, initial_mapping) with cirq.testing.assert_deprecated( - "Use cirq.contrib.acquaintance.strategy_executor", deadline='v1.0' + "Use cirq.contrib.acquaintance.StrategyExecutorTransformer", deadline='v1.0' ): executor = cca.StrategyExecutor(execution_strategy) @@ -87,6 +87,53 @@ def test_executor_explicit(): ct.assert_has_diagram(circuit, expected_text_diagram) +def test_executor_transformer_explicit(): + num_qubits = 8 + qubits = cirq.LineQubit.range(num_qubits) + circuit = cca.complete_acquaintance_strategy(qubits, 2) + + gates = { + (i, j): ExampleGate([str(k) for k in ij]) + for ij in combinations(range(num_qubits), 2) + for i, j in (ij, ij[::-1]) + } + initial_mapping = {q: i for i, q in enumerate(sorted(qubits))} + execution_strategy = cca.GreedyExecutionStrategy(gates, initial_mapping) + executor = cca.StrategyExecutorTransformer(execution_strategy) + + with pytest.raises(NotImplementedError): + bad_gates = {(0,): ExampleGate(['0']), (0, 1): ExampleGate(['0', '1'])} + cca.GreedyExecutionStrategy(bad_gates, initial_mapping) + + with pytest.raises(TypeError): + bad_strategy = cirq.Circuit(cirq.X(qubits[0])) + executor(bad_strategy) + + with pytest.raises(TypeError): + op = cirq.X(qubits[0]) + executor.map_func(op, 0) + + circuit = executor(circuit) + expected_text_diagram = """ +0: ───0───1───╲0╱─────────────────1───3───╲0╱─────────────────3───5───╲0╱─────────────────5───7───╲0╱───────────────── + │ │ │ │ │ │ │ │ │ │ │ │ +1: ───1───0───╱1╲───0───3───╲0╱───3───1───╱1╲───1───5───╲0╱───5───3───╱1╲───3───7───╲0╱───7───5───╱1╲───5───6───╲0╱─── + │ │ │ │ │ │ │ │ │ │ │ │ +2: ───2───3───╲0╱───3───0───╱1╲───0───5───╲0╱───5───1───╱1╲───1───7───╲0╱───7───3───╱1╲───3───6───╲0╱───6───5───╱1╲─── + │ │ │ │ │ │ │ │ │ │ │ │ +3: ───3───2───╱1╲───2───5───╲0╱───5───0───╱1╲───0───7───╲0╱───7───1───╱1╲───1───6───╲0╱───6───3───╱1╲───3───4───╲0╱─── + │ │ │ │ │ │ │ │ │ │ │ │ +4: ───4───5───╲0╱───5───2───╱1╲───2───7───╲0╱───7───0───╱1╲───0───6───╲0╱───6───1───╱1╲───1───4───╲0╱───4───3───╱1╲─── + │ │ │ │ │ │ │ │ │ │ │ │ +5: ───5───4───╱1╲───4───7───╲0╱───7───2───╱1╲───2───6───╲0╱───6───0───╱1╲───0───4───╲0╱───4───1───╱1╲───1───2───╲0╱─── + │ │ │ │ │ │ │ │ │ │ │ │ +6: ───6───7───╲0╱───7───4───╱1╲───4───6───╲0╱───6───2───╱1╲───2───4───╲0╱───4───0───╱1╲───0───2───╲0╱───2───1───╱1╲─── + │ │ │ │ │ │ │ │ │ │ │ │ +7: ───7───6───╱1╲─────────────────6───4───╱1╲─────────────────4───2───╱1╲─────────────────2───0───╱1╲───────────────── + """.strip() + ct.assert_has_diagram(circuit, expected_text_diagram) + + def random_diagonal_gates( num_qubits: int, acquaintance_size: int ) -> Dict[Tuple[cirq.Qid, ...], cirq.Gate]: From a16462f68853a81e740a273393cc7b2cf009ac88 Mon Sep 17 00:00:00 2001 From: Ammar Eltigani Date: Thu, 30 Jun 2022 16:14:18 -0700 Subject: [PATCH 10/12] passes all tests and coverage --- .../cirq/contrib/acquaintance/executor.py | 8 +- .../contrib/acquaintance/executor_test.py | 86 ++++++------------- 2 files changed, 31 insertions(+), 63 deletions(-) diff --git a/cirq-core/cirq/contrib/acquaintance/executor.py b/cirq-core/cirq/contrib/acquaintance/executor.py index 8ac9d82c874..2526df7b2a2 100644 --- a/cirq-core/cirq/contrib/acquaintance/executor.py +++ b/cirq-core/cirq/contrib/acquaintance/executor.py @@ -64,11 +64,11 @@ def __call__(self, *args, **kwargs): """Returns the final mapping of logical indices to qubits after executing an acquaintance strategy. """ - if len(args) < 1 or not isinstance(args[0], circuits.AbstractCircuit): + if len(args) < 1 or not isinstance(args[0], circuits.Circuit): raise ValueError( ( "To call ExecutionStrategy, an argument of type " - "'cirq.AbstractCircuit' must be passed in as the first non-keyword argument" + "circuits.Circuit must be passed in as the first non-keyword argument" ) ) input_circuit = args[0] @@ -133,13 +133,13 @@ def __init__(self, execution_strategy: ExecutionStrategy) -> None: self.mapping = execution_strategy.initial_mapping.copy() def __call__( - self, circuit: 'cirq.AbstractCircuit', context: Optional['cirq.TransformerContext'] = None + self, circuit: circuits.Circuit, context: Optional['cirq.TransformerContext'] = None ) -> circuits.Circuit: """Executes an acquaintance strategy using primitive map function and mutates initial mapping. Args: - circuit: Input circuit to transform. + circuit: 'circuits.Circuit' input circuit to transform. context: `cirq.TransformerContext` storing common configurable options for transformers. diff --git a/cirq-core/cirq/contrib/acquaintance/executor_test.py b/cirq-core/cirq/contrib/acquaintance/executor_test.py index cb04e0fde69..6e8fb83fcaf 100644 --- a/cirq-core/cirq/contrib/acquaintance/executor_test.py +++ b/cirq-core/cirq/contrib/acquaintance/executor_test.py @@ -14,7 +14,7 @@ from itertools import combinations from string import ascii_lowercase -from typing import Sequence, Dict, Tuple, Union +from typing import Sequence, Dict, Tuple import numpy as np import pytest @@ -36,7 +36,11 @@ def _circuit_diagram_info_(self, args: cirq.CircuitDiagramInfoArgs): return self._wire_symbols -def test_executor_explicit(): +@pytest.mark.parametrize( + 'StrategyType, is_deprecated', + [[cca.StrategyExecutor, True], [cca.StrategyExecutorTransformer, False]], +) +def test_executor_explicit(StrategyType, is_deprecated): num_qubits = 8 qubits = cirq.LineQubit.range(num_qubits) circuit = cca.complete_acquaintance_strategy(qubits, 2) @@ -48,58 +52,20 @@ def test_executor_explicit(): } initial_mapping = {q: i for i, q in enumerate(sorted(qubits))} execution_strategy = cca.GreedyExecutionStrategy(gates, initial_mapping) - with cirq.testing.assert_deprecated( - "Use cirq.contrib.acquaintance.StrategyExecutorTransformer", deadline='v1.0' - ): - executor = cca.StrategyExecutor(execution_strategy) - with pytest.raises(NotImplementedError): - bad_gates = {(0,): ExampleGate(['0']), (0, 1): ExampleGate(['0', '1'])} - cca.GreedyExecutionStrategy(bad_gates, initial_mapping) - - with pytest.raises(TypeError): - bad_strategy = cirq.Circuit(cirq.X(qubits[0])) - executor(bad_strategy) - - with pytest.raises(TypeError): - op = cirq.X(qubits[0]) - bad_strategy = cirq.Circuit(op) - executor.optimization_at(bad_strategy, 0, op) - - executor(circuit) - expected_text_diagram = """ -0: ───0───1───╲0╱─────────────────1───3───╲0╱─────────────────3───5───╲0╱─────────────────5───7───╲0╱───────────────── - │ │ │ │ │ │ │ │ │ │ │ │ -1: ───1───0───╱1╲───0───3───╲0╱───3───1───╱1╲───1───5───╲0╱───5───3───╱1╲───3───7───╲0╱───7───5───╱1╲───5───6───╲0╱─── - │ │ │ │ │ │ │ │ │ │ │ │ -2: ───2───3───╲0╱───3───0───╱1╲───0───5───╲0╱───5───1───╱1╲───1───7───╲0╱───7───3───╱1╲───3───6───╲0╱───6───5───╱1╲─── - │ │ │ │ │ │ │ │ │ │ │ │ -3: ───3───2───╱1╲───2───5───╲0╱───5───0───╱1╲───0───7───╲0╱───7───1───╱1╲───1───6───╲0╱───6───3───╱1╲───3───4───╲0╱─── - │ │ │ │ │ │ │ │ │ │ │ │ -4: ───4───5───╲0╱───5───2───╱1╲───2───7───╲0╱───7───0───╱1╲───0───6───╲0╱───6───1───╱1╲───1───4───╲0╱───4───3───╱1╲─── - │ │ │ │ │ │ │ │ │ │ │ │ -5: ───5───4───╱1╲───4───7───╲0╱───7───2───╱1╲───2───6───╲0╱───6───0───╱1╲───0───4───╲0╱───4───1───╱1╲───1───2───╲0╱─── - │ │ │ │ │ │ │ │ │ │ │ │ -6: ───6───7───╲0╱───7───4───╱1╲───4───6───╲0╱───6───2───╱1╲───2───4───╲0╱───4───0───╱1╲───0───2───╲0╱───2───1───╱1╲─── - │ │ │ │ │ │ │ │ │ │ │ │ -7: ───7───6───╱1╲─────────────────6───4───╱1╲─────────────────4───2───╱1╲─────────────────2───0───╱1╲───────────────── - """.strip() - ct.assert_has_diagram(circuit, expected_text_diagram) - - -def test_executor_transformer_explicit(): - num_qubits = 8 - qubits = cirq.LineQubit.range(num_qubits) - circuit = cca.complete_acquaintance_strategy(qubits, 2) - - gates = { - (i, j): ExampleGate([str(k) for k in ij]) - for ij in combinations(range(num_qubits), 2) - for i, j in (ij, ij[::-1]) - } - initial_mapping = {q: i for i, q in enumerate(sorted(qubits))} - execution_strategy = cca.GreedyExecutionStrategy(gates, initial_mapping) - executor = cca.StrategyExecutorTransformer(execution_strategy) + if is_deprecated: + with cirq.testing.assert_deprecated( + "Use cirq.contrib.acquaintance.StrategyExecutorTransformer", deadline='v1.0' + ): + executor = StrategyType(execution_strategy) + with pytest.raises(TypeError): + op = cirq.X(qubits[0]) + bad_strategy = cirq.Circuit(op) + executor.optimization_at(bad_strategy, 0, op) + else: + with pytest.raises(ValueError): + executor = StrategyType(None) + executor = StrategyType(execution_strategy) with pytest.raises(NotImplementedError): bad_gates = {(0,): ExampleGate(['0']), (0, 1): ExampleGate(['0', '1'])} @@ -109,11 +75,10 @@ def test_executor_transformer_explicit(): bad_strategy = cirq.Circuit(cirq.X(qubits[0])) executor(bad_strategy) - with pytest.raises(TypeError): - op = cirq.X(qubits[0]) - executor.map_func(op, 0) - - circuit = executor(circuit) + if is_deprecated: + executor(circuit) + else: + circuit = executor(circuit) expected_text_diagram = """ 0: ───0───1───╲0╱─────────────────1───3───╲0╱─────────────────3───5───╲0╱─────────────────5───7───╲0╱───────────────── │ │ │ │ │ │ │ │ │ │ │ │ @@ -162,8 +127,11 @@ def test_executor_random( logical_circuit = cirq.Circuit([g(*Q) for Q, g in gates.items()]) expected_unitary = logical_circuit.unitary() - initial_mapping = {q: q for q in qubits} + + with pytest.raises(ValueError): + cca.GreedyExecutionStrategy(gates, initial_mapping)() + final_mapping = cca.GreedyExecutionStrategy(gates, initial_mapping)(circuit) permutation = {q.x: qq.x for q, qq in final_mapping.items()} circuit.append(cca.LinearPermutationGate(num_qubits, permutation)(*qubits)) From 7852f81631732985b1fd5de73c3e375ec47688d5 Mon Sep 17 00:00:00 2001 From: Ammar Eltigani Date: Fri, 1 Jul 2022 14:10:02 -0700 Subject: [PATCH 11/12] fixed final round of comments --- .../cirq/contrib/acquaintance/executor.py | 35 +++++++++++-------- 1 file changed, 21 insertions(+), 14 deletions(-) diff --git a/cirq-core/cirq/contrib/acquaintance/executor.py b/cirq-core/cirq/contrib/acquaintance/executor.py index 2526df7b2a2..25d25574e16 100644 --- a/cirq-core/cirq/contrib/acquaintance/executor.py +++ b/cirq-core/cirq/contrib/acquaintance/executor.py @@ -64,18 +64,18 @@ def __call__(self, *args, **kwargs): """Returns the final mapping of logical indices to qubits after executing an acquaintance strategy. """ - if len(args) < 1 or not isinstance(args[0], circuits.Circuit): + if len(args) < 1 or not isinstance(args[0], circuits.AbstractCircuit): raise ValueError( ( "To call ExecutionStrategy, an argument of type " - "circuits.Circuit must be passed in as the first non-keyword argument" + "circuits.AbstractCircuit must be passed in as the first non-keyword argument" ) ) input_circuit = args[0] strategy = StrategyExecutorTransformer(self) final_circuit = strategy(input_circuit, **kwargs) input_circuit._moments = final_circuit._moments - return strategy.get_mapping() + return strategy.mapping() @_compat.deprecated_class( @@ -117,12 +117,16 @@ def optimization_at( ) +@transformers.transformer class StrategyExecutorTransformer: """Executes an acquaintance strategy.""" def __init__(self, execution_strategy: ExecutionStrategy) -> None: """Initializes transformer. + Args: + execution_strategy: The `ExecutionStrategy` to execute. + Raises: ValueError: if execution_strategy is None. """ @@ -130,16 +134,16 @@ def __init__(self, execution_strategy: ExecutionStrategy) -> None: if execution_strategy is None: raise ValueError('execution_strategy cannot be None') self.execution_strategy = execution_strategy - self.mapping = execution_strategy.initial_mapping.copy() + self._mapping = execution_strategy.initial_mapping.copy() def __call__( - self, circuit: circuits.Circuit, context: Optional['cirq.TransformerContext'] = None + self, circuit: circuits.AbstractCircuit, context: Optional['cirq.TransformerContext'] = None ) -> circuits.Circuit: - """Executes an acquaintance strategy using primitive map function and - mutates initial mapping. + """Executes an acquaintance strategy using cirq.map_operations_and_unroll and + mutates initial mapping. Args: - circuit: 'circuits.Circuit' input circuit to transform. + circuit: 'cirq.Circuit' input circuit to transform. context: `cirq.TransformerContext` storing common configurable options for transformers. @@ -148,27 +152,30 @@ def __call__( strategy on all instances of AcquaintanceOpportunityGate """ + circuit = transformers.expand_composite( + circuit, no_decomp=expose_acquaintance_gates.no_decomp + ) expose_acquaintance_gates(circuit) return transformers.map_operations_and_unroll( circuit=circuit, - map_func=self.map_func, + map_func=self._map_func, deep=context.deep if context else False, tags_to_ignore=context.tags_to_ignore if context else (), ) - def get_mapping(self) -> LogicalMapping: - return self.mapping.copy() + def mapping(self) -> LogicalMapping: + return self._mapping - def map_func(self, op: 'cirq.Operation', index) -> 'cirq.OP_TREE': + def _map_func(self, op: 'cirq.Operation', index) -> 'cirq.OP_TREE': if isinstance(op.gate, AcquaintanceOpportunityGate): - logical_indices = tuple(self.mapping[q] for q in op.qubits) + logical_indices = tuple(self._mapping[q] for q in op.qubits) logical_operations = self.execution_strategy.get_operations(logical_indices, op.qubits) clear_span = int(not self.execution_strategy.keep_acquaintance) return logical_operations if clear_span else [op, logical_operations] if isinstance(op.gate, PermutationGate): - op.gate.update_mapping(self.mapping, op.qubits) + op.gate.update_mapping(self._mapping, op.qubits) return op raise TypeError( From d5c148e11e071ba0a030136167835c6b13c0e8d8 Mon Sep 17 00:00:00 2001 From: Ammar Eltigani Date: Fri, 1 Jul 2022 15:12:57 -0700 Subject: [PATCH 12/12] again --- cirq-core/cirq/contrib/acquaintance/executor.py | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/cirq-core/cirq/contrib/acquaintance/executor.py b/cirq-core/cirq/contrib/acquaintance/executor.py index 25d25574e16..b8437d909e8 100644 --- a/cirq-core/cirq/contrib/acquaintance/executor.py +++ b/cirq-core/cirq/contrib/acquaintance/executor.py @@ -75,7 +75,7 @@ def __call__(self, *args, **kwargs): strategy = StrategyExecutorTransformer(self) final_circuit = strategy(input_circuit, **kwargs) input_circuit._moments = final_circuit._moments - return strategy.mapping() + return strategy.mapping @_compat.deprecated_class( @@ -155,14 +155,14 @@ def __call__( circuit = transformers.expand_composite( circuit, no_decomp=expose_acquaintance_gates.no_decomp ) - expose_acquaintance_gates(circuit) return transformers.map_operations_and_unroll( circuit=circuit, map_func=self._map_func, deep=context.deep if context else False, tags_to_ignore=context.tags_to_ignore if context else (), - ) + ).unfreeze(copy=False) + @property def mapping(self) -> LogicalMapping: return self._mapping