From 64bcc590af3d182ec334717274a1193ca25de3ab Mon Sep 17 00:00:00 2001 From: Doug Strain Date: Wed, 6 Jul 2022 13:57:35 -0700 Subject: [PATCH 1/3] Minor updates to development page - Change Foxtail to Sycamore since Foxtail is deprecated. - Add some clarification that instructions are for linux. --- docs/dev/development.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/docs/dev/development.md b/docs/dev/development.md index 3c8695be013..78e67e5da7c 100644 --- a/docs/dev/development.md +++ b/docs/dev/development.md @@ -28,12 +28,12 @@ Note that if you are using PyCharm, you might have to Restart & Invalidate Cache ```bash docker build -t cirq --target cirq_stable . - docker run -it cirq python -c "import cirq_google; print(cirq_google.Foxtail)" + docker run -it cirq python -c "import cirq_google; print(cirq_google.Sycamore23)" ``` ```bash docker build -t cirq_pre --target cirq_pre_release . - docker run -it cirq_pre python -c "import cirq_google; print(cirq_google.Foxtail)" + docker run -it cirq_pre python -c "import cirq_google; print(cirq_google.Sycamore23)" ``` If you want to contribute changes to Cirq, you will instead want to fork the repository and submit pull requests from your fork. @@ -85,6 +85,9 @@ At this point your local git master should be synced with the master from the ma ## Setting up an environment +These instructions are primarily for linux-based environments that use the apt +package manager. + 0. First clone the repository, if you have not already done so. See the previous section for instructions. From 4ba319e06ae4f125909e446cdc25d5c14b7f07df Mon Sep 17 00:00:00 2001 From: Doug Strain Date: Mon, 11 Jul 2022 14:41:50 -0700 Subject: [PATCH 2/3] Another batch of optimizer removals --- cirq-core/cirq/__init__.py | 8 - cirq-core/cirq/optimizers/__init__.py | 16 - .../cirq/optimizers/drop_empty_moments.py | 30 -- .../optimizers/drop_empty_moments_test.py | 33 -- cirq-core/cirq/optimizers/drop_negligible.py | 45 -- .../cirq/optimizers/drop_negligible_test.py | 59 --- .../cirq/optimizers/eject_phased_paulis.py | 50 --- .../optimizers/eject_phased_paulis_test.py | 393 ------------------ cirq-core/cirq/optimizers/eject_z.py | 50 --- cirq-core/cirq/optimizers/eject_z_test.py | 338 --------------- cirq-core/cirq/optimizers/expand_composite.py | 55 --- .../cirq/optimizers/expand_composite_test.py | 181 -------- .../cirq/optimizers/merge_interactions.py | 263 ------------ .../optimizers/merge_interactions_test.py | 257 ------------ .../merge_interactions_to_sqrt_iswap.py | 110 ----- .../merge_interactions_to_sqrt_iswap_test.py | 323 -------------- .../synchronize_terminal_measurements.py | 68 --- .../synchronize_terminal_measurements_test.py | 159 ------- .../cirq/protocols/json_test_data/spec.py | 8 - 19 files changed, 2446 deletions(-) delete mode 100644 cirq-core/cirq/optimizers/drop_empty_moments.py delete mode 100644 cirq-core/cirq/optimizers/drop_empty_moments_test.py delete mode 100644 cirq-core/cirq/optimizers/drop_negligible.py delete mode 100644 cirq-core/cirq/optimizers/drop_negligible_test.py delete mode 100644 cirq-core/cirq/optimizers/eject_phased_paulis.py delete mode 100644 cirq-core/cirq/optimizers/eject_phased_paulis_test.py delete mode 100644 cirq-core/cirq/optimizers/eject_z.py delete mode 100644 cirq-core/cirq/optimizers/eject_z_test.py delete mode 100644 cirq-core/cirq/optimizers/expand_composite.py delete mode 100644 cirq-core/cirq/optimizers/expand_composite_test.py delete mode 100644 cirq-core/cirq/optimizers/merge_interactions.py delete mode 100644 cirq-core/cirq/optimizers/merge_interactions_test.py delete mode 100644 cirq-core/cirq/optimizers/merge_interactions_to_sqrt_iswap.py delete mode 100644 cirq-core/cirq/optimizers/merge_interactions_to_sqrt_iswap_test.py delete mode 100644 cirq-core/cirq/optimizers/synchronize_terminal_measurements.py delete mode 100644 cirq-core/cirq/optimizers/synchronize_terminal_measurements_test.py diff --git a/cirq-core/cirq/__init__.py b/cirq-core/cirq/__init__.py index bfe2db63307..50df98c6a15 100644 --- a/cirq-core/cirq/__init__.py +++ b/cirq-core/cirq/__init__.py @@ -331,17 +331,9 @@ AlignLeft, AlignRight, ConvertToCzAndSingleGates, - DropEmptyMoments, - DropNegligible, - EjectPhasedPaulis, - EjectZ, - ExpandComposite, merge_single_qubit_gates_into_phased_x_z, merge_single_qubit_gates_into_phxz, - MergeInteractions, - MergeInteractionsToSqrtIswap, MergeSingleQubitGates, - SynchronizeTerminalMeasurements, ) from cirq.transformers import ( diff --git a/cirq-core/cirq/optimizers/__init__.py b/cirq-core/cirq/optimizers/__init__.py index ea4d553f0aa..44c55108d61 100644 --- a/cirq-core/cirq/optimizers/__init__.py +++ b/cirq-core/cirq/optimizers/__init__.py @@ -18,30 +18,14 @@ from cirq.optimizers.align_right import AlignRight -from cirq.optimizers.drop_empty_moments import DropEmptyMoments - -from cirq.optimizers.drop_negligible import DropNegligible - from cirq.optimizers.convert_to_cz_and_single_gates import ConvertToCzAndSingleGates -from cirq.optimizers.eject_phased_paulis import EjectPhasedPaulis - -from cirq.optimizers.eject_z import EjectZ - -from cirq.optimizers.expand_composite import ExpandComposite - -from cirq.optimizers.merge_interactions import MergeInteractions - -from cirq.optimizers.merge_interactions_to_sqrt_iswap import MergeInteractionsToSqrtIswap - from cirq.optimizers.merge_single_qubit_gates import ( merge_single_qubit_gates_into_phased_x_z, merge_single_qubit_gates_into_phxz, MergeSingleQubitGates, ) -from cirq.optimizers.synchronize_terminal_measurements import SynchronizeTerminalMeasurements - from cirq.transformers.analytical_decompositions import ( compute_cphase_exponents_for_fsim_decomposition, decompose_cphase_into_two_fsim, diff --git a/cirq-core/cirq/optimizers/drop_empty_moments.py b/cirq-core/cirq/optimizers/drop_empty_moments.py deleted file mode 100644 index af7db5a9e55..00000000000 --- a/cirq-core/cirq/optimizers/drop_empty_moments.py +++ /dev/null @@ -1,30 +0,0 @@ -# Copyright 2018 The Cirq Developers -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""An optimization pass that removes empty moments from a circuit.""" - -from cirq.circuits.circuit import Circuit -from cirq.circuits import circuit as _circuit -from cirq._compat import deprecated_class - - -@deprecated_class(deadline='v1.0', fix='Use cirq.drop_empty_moments instead.') -class DropEmptyMoments: - """Removes empty moments from a circuit.""" - - def __call__(self, circuit: _circuit.Circuit): - self.optimize_circuit(circuit) - - def optimize_circuit(self, circuit: Circuit): - circuit[:] = (m for m in circuit if m.operations) diff --git a/cirq-core/cirq/optimizers/drop_empty_moments_test.py b/cirq-core/cirq/optimizers/drop_empty_moments_test.py deleted file mode 100644 index 7f08449a68f..00000000000 --- a/cirq-core/cirq/optimizers/drop_empty_moments_test.py +++ /dev/null @@ -1,33 +0,0 @@ -# Copyright 2018 The Cirq Developers -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import cirq - - -def assert_optimizes(before, after): - with cirq.testing.assert_deprecated("Use cirq.drop_empty_moments", deadline='v1.0'): - opt = cirq.DropEmptyMoments() - opt.optimize_circuit(before) - assert before == after - - -def test_drop(): - q1 = cirq.NamedQubit('q1') - q2 = cirq.NamedQubit('q2') - assert_optimizes( - before=cirq.Circuit( - [cirq.Moment(), cirq.Moment(), cirq.Moment([cirq.CNOT(q1, q2)]), cirq.Moment()] - ), - after=cirq.Circuit([cirq.Moment([cirq.CNOT(q1, q2)])]), - ) diff --git a/cirq-core/cirq/optimizers/drop_negligible.py b/cirq-core/cirq/optimizers/drop_negligible.py deleted file mode 100644 index b2e6def1024..00000000000 --- a/cirq-core/cirq/optimizers/drop_negligible.py +++ /dev/null @@ -1,45 +0,0 @@ -# Copyright 2018 The Cirq Developers -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""An optimization pass that removes operations with tiny effects.""" - -from typing import List, Tuple, TYPE_CHECKING - -from cirq import protocols -from cirq.circuits import circuit as _circuit -from cirq._compat import deprecated_class - -if TYPE_CHECKING: - from cirq import ops - - -@deprecated_class(deadline='v1.0', fix='Use cirq.drop_negligible_operations instead.') -class DropNegligible: - """An optimization pass that removes operations with tiny effects.""" - - def __init__(self, tolerance: float = 1e-8) -> None: - self.tolerance = tolerance - - def __call__(self, circuit: _circuit.Circuit): - self.optimize_circuit(circuit) - - def optimize_circuit(self, circuit: _circuit.Circuit) -> None: - deletions: List[Tuple[int, ops.Operation]] = [] - for moment_index, moment in enumerate(circuit): - for op in moment.operations: - if protocols.is_measurement(op): - continue - if protocols.trace_distance_bound(op) <= self.tolerance: - deletions.append((moment_index, op)) - circuit.batch_remove(deletions) diff --git a/cirq-core/cirq/optimizers/drop_negligible_test.py b/cirq-core/cirq/optimizers/drop_negligible_test.py deleted file mode 100644 index 47f67072c23..00000000000 --- a/cirq-core/cirq/optimizers/drop_negligible_test.py +++ /dev/null @@ -1,59 +0,0 @@ -# Copyright 2018 The Cirq Developers -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import cirq - - -def assert_optimizes(atol: float, initial_circuit: cirq.Circuit, expected_circuit: cirq.Circuit): - with cirq.testing.assert_deprecated("Use cirq.drop_negligible_operations", deadline='v1.0'): - optimizer = cirq.DropNegligible(atol) - circuit = cirq.Circuit(initial_circuit) - optimizer.optimize_circuit(circuit) - assert circuit == expected_circuit - - -def test_leaves_big(): - a = cirq.NamedQubit('a') - circuit = cirq.Circuit([cirq.Moment([cirq.Z(a) ** 0.1])]) - - assert_optimizes(0.001, initial_circuit=circuit, expected_circuit=circuit) - - -def test_clears_small(): - a = cirq.NamedQubit('a') - circuit = cirq.Circuit([cirq.Moment([cirq.Z(a) ** 0.000001])]) - - assert_optimizes(0.001, initial_circuit=circuit, expected_circuit=cirq.Circuit([cirq.Moment()])) - - -def test_clears_known_empties_even_at_zero_tolerance(): - a, b = cirq.LineQubit.range(2) - circuit = cirq.Circuit( - cirq.Z(a) ** 0, cirq.Y(a) ** 0.0000001, cirq.X(a) ** -0.0000001, cirq.CZ(a, b) ** 0 - ) - assert_optimizes( - 0.001, initial_circuit=circuit, expected_circuit=cirq.Circuit([cirq.Moment()] * 4) - ) - assert_optimizes( - 0, - initial_circuit=circuit, - expected_circuit=cirq.Circuit( - [ - cirq.Moment(), - cirq.Moment([cirq.Y(a) ** 0.0000001]), - cirq.Moment([cirq.X(a) ** -0.0000001]), - cirq.Moment(), - ] - ), - ) diff --git a/cirq-core/cirq/optimizers/eject_phased_paulis.py b/cirq-core/cirq/optimizers/eject_phased_paulis.py deleted file mode 100644 index 468c90c6730..00000000000 --- a/cirq-core/cirq/optimizers/eject_phased_paulis.py +++ /dev/null @@ -1,50 +0,0 @@ -# Copyright 2018 The Cirq Developers -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Pushes 180 degree rotations around axes in the XY plane later in the circuit. -""" - -from cirq import _compat, circuits, transformers - - -@_compat.deprecated_class(deadline='v1.0', fix='Use cirq.eject_phased_paulis instead.') -class EjectPhasedPaulis: - """Pushes X, Y, and PhasedX gates towards the end of the circuit. - - As the gates get pushed, they may absorb Z gates, cancel against other - X, Y, or PhasedX gates with exponent=1, get merged into measurements (as - output bit flips), and cause phase kickback operations across CZs (which can - then be removed by the EjectZ optimization). - """ - - def __init__(self, tolerance: float = 1e-8, eject_parameterized: bool = False) -> None: - """Inits EjectPhasedPaulis. - - Args: - tolerance: Maximum absolute error tolerance. The optimization is - permitted to simply drop negligible combinations gates with a - threshold determined by this tolerance. - eject_parameterized: If True, the optimization will attempt to eject - parameterized gates as well. This may result in other gates - parameterized by symbolic expressions. - """ - self.tolerance = tolerance - self.eject_parameterized = eject_parameterized - - def optimize_circuit(self, circuit: circuits.Circuit): - circuit._moments = [ - *transformers.eject_phased_paulis( - circuit, atol=self.tolerance, eject_parameterized=self.eject_parameterized - ) - ] diff --git a/cirq-core/cirq/optimizers/eject_phased_paulis_test.py b/cirq-core/cirq/optimizers/eject_phased_paulis_test.py deleted file mode 100644 index ba32b436785..00000000000 --- a/cirq-core/cirq/optimizers/eject_phased_paulis_test.py +++ /dev/null @@ -1,393 +0,0 @@ -# Copyright 2018 The Cirq Developers -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -from typing import cast, Iterable - -import numpy as np -import pytest -import sympy - -import cirq - - -def assert_optimizes( - before: cirq.Circuit, - expected: cirq.Circuit, - compare_unitaries: bool = True, - eject_parameterized: bool = False, -): - with cirq.testing.assert_deprecated("Use cirq.eject_phased_paulis", deadline='v1.0'): - opt = cirq.EjectPhasedPaulis(eject_parameterized=eject_parameterized) - - circuit = before.copy() - expected = cirq.drop_empty_moments(expected) - opt.optimize_circuit(circuit) - - # They should have equivalent effects. - if compare_unitaries: - if cirq.is_parameterized(circuit): - for a in (0, 0.1, 0.5, -1.0, np.pi, np.pi / 2): - params: cirq.ParamDictType = {'x': a, 'y': a / 2, 'z': -2 * a} - ( - cirq.testing.assert_circuits_with_terminal_measurements_are_equivalent( - cirq.resolve_parameters(circuit, params), - cirq.resolve_parameters(expected, params), - 1e-8, - ) - ) - else: - ( - cirq.testing.assert_circuits_with_terminal_measurements_are_equivalent( - circuit, expected, 1e-8 - ) - ) - - # And match the expected circuit. - assert circuit == expected, ( - "Circuit wasn't optimized as expected.\n" - "INPUT:\n" - "{}\n" - "\n" - "EXPECTED OUTPUT:\n" - "{}\n" - "\n" - "ACTUAL OUTPUT:\n" - "{}\n" - "\n" - "EXPECTED OUTPUT (detailed):\n" - "{!r}\n" - "\n" - "ACTUAL OUTPUT (detailed):\n" - "{!r}" - ).format(before, expected, circuit, expected, circuit) - - # And it should be idempotent. - opt.optimize_circuit(circuit) - assert circuit == expected - - -def quick_circuit(*moments: Iterable[cirq.OP_TREE]) -> cirq.Circuit: - return cirq.Circuit( - [cirq.Moment(cast(Iterable[cirq.Operation], cirq.flatten_op_tree(m))) for m in moments] - ) - - -def test_absorbs_z(): - q = cirq.NamedQubit('q') - x = sympy.Symbol('x') - - # Full Z. - assert_optimizes( - before=quick_circuit([cirq.PhasedXPowGate(phase_exponent=0.125).on(q)], [cirq.Z(q)]), - expected=quick_circuit([cirq.PhasedXPowGate(phase_exponent=0.625).on(q)], []), - ) - - # Partial Z. - assert_optimizes( - before=quick_circuit([cirq.PhasedXPowGate(phase_exponent=0.125).on(q)], [cirq.S(q)]), - expected=quick_circuit([cirq.PhasedXPowGate(phase_exponent=0.375).on(q)], []), - ) - - # parameterized Z. - assert_optimizes( - before=quick_circuit([cirq.PhasedXPowGate(phase_exponent=0.125).on(q)], [cirq.Z(q) ** x]), - expected=quick_circuit([cirq.PhasedXPowGate(phase_exponent=0.125 + x / 2).on(q)], []), - eject_parameterized=True, - ) - assert_optimizes( - before=quick_circuit( - [cirq.PhasedXPowGate(phase_exponent=0.125).on(q)], [cirq.Z(q) ** (x + 1)] - ), - expected=quick_circuit([cirq.PhasedXPowGate(phase_exponent=0.625 + x / 2).on(q)], []), - eject_parameterized=True, - ) - - # Multiple Zs. - assert_optimizes( - before=quick_circuit( - [cirq.PhasedXPowGate(phase_exponent=0.125).on(q)], [cirq.S(q)], [cirq.T(q) ** -1] - ), - expected=quick_circuit([cirq.PhasedXPowGate(phase_exponent=0.25).on(q)], [], []), - ) - - # Multiple Parameterized Zs. - assert_optimizes( - before=quick_circuit( - [cirq.PhasedXPowGate(phase_exponent=0.125).on(q)], [cirq.S(q) ** x], [cirq.T(q) ** -x] - ), - expected=quick_circuit( - [cirq.PhasedXPowGate(phase_exponent=0.125 + x * 0.125).on(q)], [], [] - ), - eject_parameterized=True, - ) - - # Parameterized Phase and Partial Z - assert_optimizes( - before=quick_circuit([cirq.PhasedXPowGate(phase_exponent=x).on(q)], [cirq.S(q)]), - expected=quick_circuit([cirq.PhasedXPowGate(phase_exponent=x + 0.25).on(q)], []), - eject_parameterized=True, - ) - - -def test_crosses_czs(): - a = cirq.NamedQubit('a') - b = cirq.NamedQubit('b') - x = sympy.Symbol('x') - y = sympy.Symbol('y') - z = sympy.Symbol('z') - - # Full CZ. - assert_optimizes( - before=quick_circuit([cirq.PhasedXPowGate(phase_exponent=0.25).on(a)], [cirq.CZ(a, b)]), - expected=quick_circuit( - [cirq.Z(b)], [cirq.CZ(a, b)], [cirq.PhasedXPowGate(phase_exponent=0.25).on(a)] - ), - ) - assert_optimizes( - before=quick_circuit([cirq.PhasedXPowGate(phase_exponent=0.125).on(a)], [cirq.CZ(b, a)]), - expected=quick_circuit( - [cirq.Z(b)], [cirq.CZ(a, b)], [cirq.PhasedXPowGate(phase_exponent=0.125).on(a)] - ), - ) - assert_optimizes( - before=quick_circuit([cirq.PhasedXPowGate(phase_exponent=x).on(a)], [cirq.CZ(b, a)]), - expected=quick_circuit( - [cirq.Z(b)], [cirq.CZ(a, b)], [cirq.PhasedXPowGate(phase_exponent=x).on(a)] - ), - eject_parameterized=True, - ) - - # Partial CZ. - assert_optimizes( - before=quick_circuit([cirq.X(a)], [cirq.CZ(a, b) ** 0.25]), - expected=quick_circuit([cirq.Z(b) ** 0.25], [cirq.CZ(a, b) ** -0.25], [cirq.X(a)]), - ) - assert_optimizes( - before=quick_circuit([cirq.X(a)], [cirq.CZ(a, b) ** x]), - expected=quick_circuit([cirq.Z(b) ** x], [cirq.CZ(a, b) ** -x], [cirq.X(a)]), - eject_parameterized=True, - ) - - # Double cross. - assert_optimizes( - before=quick_circuit( - [cirq.PhasedXPowGate(phase_exponent=0.125).on(a)], - [cirq.PhasedXPowGate(phase_exponent=0.375).on(b)], - [cirq.CZ(a, b) ** 0.25], - ), - expected=quick_circuit( - [], - [], - [cirq.CZ(a, b) ** 0.25], - [ - cirq.PhasedXPowGate(phase_exponent=0.5).on(b), - cirq.PhasedXPowGate(phase_exponent=0.25).on(a), - ], - ), - ) - assert_optimizes( - before=quick_circuit( - [cirq.PhasedXPowGate(phase_exponent=x).on(a)], - [cirq.PhasedXPowGate(phase_exponent=y).on(b)], - [cirq.CZ(a, b) ** z], - ), - expected=quick_circuit( - [], - [], - [cirq.CZ(a, b) ** z], - [ - cirq.PhasedXPowGate(phase_exponent=y + z / 2).on(b), - cirq.PhasedXPowGate(phase_exponent=x + z / 2).on(a), - ], - ), - eject_parameterized=True, - ) - - -def test_toggles_measurements(): - a = cirq.NamedQubit('a') - b = cirq.NamedQubit('b') - x = sympy.Symbol('x') - - # Single. - assert_optimizes( - before=quick_circuit( - [cirq.PhasedXPowGate(phase_exponent=0.25).on(a)], [cirq.measure(a, b)] - ), - expected=quick_circuit([], [cirq.measure(a, b, invert_mask=(True,))]), - ) - assert_optimizes( - before=quick_circuit( - [cirq.PhasedXPowGate(phase_exponent=0.25).on(b)], [cirq.measure(a, b)] - ), - expected=quick_circuit([], [cirq.measure(a, b, invert_mask=(False, True))]), - ) - assert_optimizes( - before=quick_circuit([cirq.PhasedXPowGate(phase_exponent=x).on(b)], [cirq.measure(a, b)]), - expected=quick_circuit([], [cirq.measure(a, b, invert_mask=(False, True))]), - eject_parameterized=True, - ) - - # Multiple. - assert_optimizes( - before=quick_circuit( - [cirq.PhasedXPowGate(phase_exponent=0.25).on(a)], - [cirq.PhasedXPowGate(phase_exponent=0.25).on(b)], - [cirq.measure(a, b)], - ), - expected=quick_circuit([], [], [cirq.measure(a, b, invert_mask=(True, True))]), - ) - - # Xmon. - assert_optimizes( - before=quick_circuit( - [cirq.PhasedXPowGate(phase_exponent=0.25).on(a)], [cirq.measure(a, b, key='t')] - ), - expected=quick_circuit([], [cirq.measure(a, b, invert_mask=(True,), key='t')]), - ) - - -def test_cancels_other_full_w(): - q = cirq.NamedQubit('q') - x = sympy.Symbol('x') - y = sympy.Symbol('y') - - assert_optimizes( - before=quick_circuit( - [cirq.PhasedXPowGate(phase_exponent=0.25).on(q)], - [cirq.PhasedXPowGate(phase_exponent=0.25).on(q)], - ), - expected=quick_circuit([], []), - ) - - assert_optimizes( - before=quick_circuit( - [cirq.PhasedXPowGate(phase_exponent=x).on(q)], - [cirq.PhasedXPowGate(phase_exponent=x).on(q)], - ), - expected=quick_circuit([], []), - eject_parameterized=True, - ) - - assert_optimizes( - before=quick_circuit( - [cirq.PhasedXPowGate(phase_exponent=0.25).on(q)], - [cirq.PhasedXPowGate(phase_exponent=0.125).on(q)], - ), - expected=quick_circuit([], [cirq.Z(q) ** -0.25]), - ) - - assert_optimizes( - before=quick_circuit([cirq.X(q)], [cirq.PhasedXPowGate(phase_exponent=0.25).on(q)]), - expected=quick_circuit([], [cirq.Z(q) ** 0.5]), - ) - - assert_optimizes( - before=quick_circuit([cirq.PhasedXPowGate(phase_exponent=0.25).on(q)], [cirq.X(q)]), - expected=quick_circuit([], [cirq.Z(q) ** -0.5]), - ) - - assert_optimizes( - before=quick_circuit( - [cirq.PhasedXPowGate(phase_exponent=x).on(q)], - [cirq.PhasedXPowGate(phase_exponent=y).on(q)], - ), - expected=quick_circuit([], [cirq.Z(q) ** (2 * (y - x))]), - eject_parameterized=True, - ) - - -def test_phases_partial_ws(): - q = cirq.NamedQubit('q') - x = sympy.Symbol('x') - y = sympy.Symbol('y') - z = sympy.Symbol('z') - - assert_optimizes( - before=quick_circuit( - [cirq.X(q)], [cirq.PhasedXPowGate(phase_exponent=0.25, exponent=0.5).on(q)] - ), - expected=quick_circuit( - [], [cirq.PhasedXPowGate(phase_exponent=-0.25, exponent=0.5).on(q)], [cirq.X(q)] - ), - ) - - assert_optimizes( - before=quick_circuit([cirq.PhasedXPowGate(phase_exponent=0.25).on(q)], [cirq.X(q) ** 0.5]), - expected=quick_circuit( - [], - [cirq.PhasedXPowGate(phase_exponent=0.5, exponent=0.5).on(q)], - [cirq.PhasedXPowGate(phase_exponent=0.25).on(q)], - ), - ) - - assert_optimizes( - before=quick_circuit( - [cirq.PhasedXPowGate(phase_exponent=0.25).on(q)], - [cirq.PhasedXPowGate(phase_exponent=0.5, exponent=0.75).on(q)], - ), - expected=quick_circuit( - [], [cirq.X(q) ** 0.75], [cirq.PhasedXPowGate(phase_exponent=0.25).on(q)] - ), - ) - - assert_optimizes( - before=quick_circuit( - [cirq.X(q)], [cirq.PhasedXPowGate(exponent=-0.25, phase_exponent=0.5).on(q)] - ), - expected=quick_circuit( - [], [cirq.PhasedXPowGate(exponent=-0.25, phase_exponent=-0.5).on(q)], [cirq.X(q)] - ), - ) - - assert_optimizes( - before=quick_circuit( - [cirq.PhasedXPowGate(phase_exponent=x).on(q)], - [cirq.PhasedXPowGate(phase_exponent=y, exponent=z).on(q)], - ), - expected=quick_circuit( - [], - [cirq.PhasedXPowGate(phase_exponent=2 * x - y, exponent=z).on(q)], - [cirq.PhasedXPowGate(phase_exponent=x).on(q)], - ), - eject_parameterized=True, - ) - - -@pytest.mark.parametrize('sym', [sympy.Symbol('x'), sympy.Symbol('x') + 1]) -def test_blocked_by_unknown_and_symbols(sym): - a = cirq.NamedQubit('a') - b = cirq.NamedQubit('b') - - assert_optimizes( - before=quick_circuit([cirq.X(a)], [cirq.SWAP(a, b)], [cirq.X(a)]), - expected=quick_circuit([cirq.X(a)], [cirq.SWAP(a, b)], [cirq.X(a)]), - ) - - assert_optimizes( - before=quick_circuit([cirq.X(a)], [cirq.Z(a) ** sym], [cirq.X(a)]), - expected=quick_circuit([cirq.X(a)], [cirq.Z(a) ** sym], [cirq.X(a)]), - compare_unitaries=False, - ) - - assert_optimizes( - before=quick_circuit([cirq.X(a)], [cirq.CZ(a, b) ** sym], [cirq.X(a)]), - expected=quick_circuit([cirq.X(a)], [cirq.CZ(a, b) ** sym], [cirq.X(a)]), - compare_unitaries=False, - ) - - -def test_zero_x_rotation(): - a = cirq.NamedQubit('a') - - assert_optimizes(before=quick_circuit([cirq.rx(0)(a)]), expected=quick_circuit([cirq.rx(0)(a)])) diff --git a/cirq-core/cirq/optimizers/eject_z.py b/cirq-core/cirq/optimizers/eject_z.py deleted file mode 100644 index 11954b2f79b..00000000000 --- a/cirq-core/cirq/optimizers/eject_z.py +++ /dev/null @@ -1,50 +0,0 @@ -# Copyright 2018 The Cirq Developers -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""An optimization pass that pushes Z gates later and later in the circuit.""" - -from cirq import circuits, transformers - -# from cirq.transformers import eject_z -from cirq._compat import deprecated_class - - -@deprecated_class(deadline='v1.0', fix='Use cirq.eject_z instead.') -class EjectZ: - """Pushes Z gates towards the end of the circuit. - - As the Z gates get pushed they may absorb other Z gates, get absorbed into - measurements, cross CZ gates, cross W gates (by phasing them), etc. - """ - - def __init__(self, tolerance: float = 0.0, eject_parameterized: bool = False) -> None: - """Inits EjectZ. - - Args: - tolerance: Maximum absolute error tolerance. The optimization is - permitted to simply drop negligible combinations of Z gates, - with a threshold determined by this tolerance. - eject_parameterized: If True, the optimization will attempt to eject - parameterized Z gates as well. This may result in other gates - parameterized by symbolic expressions. - """ - self.tolerance = tolerance - self.eject_parameterized = eject_parameterized - - def optimize_circuit(self, circuit: circuits.Circuit): - circuit._moments = [ - *transformers.eject_z( - circuit, atol=self.tolerance, eject_parameterized=self.eject_parameterized - ) - ] diff --git a/cirq-core/cirq/optimizers/eject_z_test.py b/cirq-core/cirq/optimizers/eject_z_test.py deleted file mode 100644 index c5d1f28309f..00000000000 --- a/cirq-core/cirq/optimizers/eject_z_test.py +++ /dev/null @@ -1,338 +0,0 @@ -# Copyright 2018 The Cirq Developers -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -import pytest -import numpy as np -import sympy - -import cirq - - -def assert_optimizes( - before: cirq.Circuit, expected: cirq.Circuit, eject_parameterized: bool = False -): - with cirq.testing.assert_deprecated("Use cirq.eject_z", deadline='v1.0'): - opt = cirq.EjectZ(eject_parameterized=eject_parameterized) - - if cirq.has_unitary(before): - cirq.testing.assert_circuits_with_terminal_measurements_are_equivalent( - before, expected, atol=1e-8 - ) - - circuit = before.copy() - opt.optimize_circuit(circuit) - opt.optimize_circuit(expected) - - cirq.testing.assert_same_circuits(circuit, expected) - - # And it should be idempotent. - opt.optimize_circuit(circuit) - cirq.testing.assert_same_circuits(circuit, expected) - - -def assert_removes_all_z_gates(circuit: cirq.Circuit, eject_parameterized: bool = True): - with cirq.testing.assert_deprecated("Use cirq.eject_z", deadline='v1.0'): - opt = cirq.EjectZ(eject_parameterized=eject_parameterized) - optimized = circuit.copy() - opt.optimize_circuit(optimized) - for op in optimized.all_operations(): - if isinstance(op.gate, cirq.PhasedXZGate) and ( - eject_parameterized or not cirq.is_parameterized(op.gate.z_exponent) - ): - assert op.gate.z_exponent == 0 - - if cirq.is_parameterized(circuit): - for a in (0, 0.1, 0.5, 1.0, -1.0, 3.0): - ( - cirq.testing.assert_circuits_with_terminal_measurements_are_equivalent( - cirq.resolve_parameters(circuit, {'a': a}), - cirq.resolve_parameters(optimized, {'a': a}), - atol=1e-8, - ) - ) - else: - cirq.testing.assert_circuits_with_terminal_measurements_are_equivalent( - circuit, optimized, atol=1e-8 - ) - - -def test_single_z_stays(): - q = cirq.NamedQubit('q') - assert_optimizes( - before=cirq.Circuit([cirq.Moment([cirq.Z(q) ** 0.5])]), - expected=cirq.Circuit([cirq.Moment([cirq.Z(q) ** 0.5])]), - ) - - -def test_single_phased_xz_stays(): - gate = cirq.PhasedXZGate(axis_phase_exponent=0.2, x_exponent=0.3, z_exponent=0.4) - q = cirq.NamedQubit('q') - assert_optimizes(before=cirq.Circuit(gate(q)), expected=cirq.Circuit(gate(q))) - - -def test_ignores_xz_and_cz(): - a = cirq.NamedQubit('a') - b = cirq.NamedQubit('b') - assert_optimizes( - before=cirq.Circuit( - [ - cirq.Moment([cirq.X(a) ** 0.5]), - cirq.Moment([cirq.Y(b) ** 0.5]), - cirq.Moment([cirq.CZ(a, b) ** 0.25]), - cirq.Moment([cirq.Y(a) ** 0.5]), - cirq.Moment([cirq.X(b) ** 0.5]), - ] - ), - expected=cirq.Circuit( - [ - cirq.Moment([cirq.X(a) ** 0.5]), - cirq.Moment([cirq.Y(b) ** 0.5]), - cirq.Moment([cirq.CZ(a, b) ** 0.25]), - cirq.Moment([cirq.Y(a) ** 0.5]), - cirq.Moment([cirq.X(b) ** 0.5]), - ] - ), - ) - - -def test_early_z(): - q = cirq.NamedQubit('q') - assert_optimizes( - before=cirq.Circuit([cirq.Moment([cirq.Z(q) ** 0.5]), cirq.Moment(), cirq.Moment()]), - expected=cirq.Circuit([cirq.Moment([cirq.Z(q) ** 0.5]), cirq.Moment(), cirq.Moment()]), - ) - - -def test_multi_z_merges(): - q = cirq.NamedQubit('q') - assert_optimizes( - before=cirq.Circuit([cirq.Moment([cirq.Z(q) ** 0.5]), cirq.Moment([cirq.Z(q) ** 0.25])]), - expected=cirq.Circuit([cirq.Moment(), cirq.Moment([cirq.Z(q) ** 0.75])]), - ) - - -def test_z_pushes_past_xy_and_phases_it(): - q = cirq.NamedQubit('q') - assert_optimizes( - before=cirq.Circuit([cirq.Moment([cirq.Z(q) ** 0.5]), cirq.Moment([cirq.Y(q) ** 0.25])]), - expected=cirq.Circuit( - [cirq.Moment(), cirq.Moment([cirq.X(q) ** 0.25]), cirq.Moment([cirq.Z(q) ** 0.5])] - ), - ) - - -def test_z_pushes_past_cz(): - a = cirq.NamedQubit('a') - b = cirq.NamedQubit('b') - assert_optimizes( - before=cirq.Circuit( - [cirq.Moment([cirq.Z(a) ** 0.5]), cirq.Moment([cirq.CZ(a, b) ** 0.25])] - ), - expected=cirq.Circuit( - [cirq.Moment(), cirq.Moment([cirq.CZ(a, b) ** 0.25]), cirq.Moment([cirq.Z(a) ** 0.5])] - ), - ) - - -def test_measurement_consumes_zs(): - q = cirq.NamedQubit('q') - assert_optimizes( - before=cirq.Circuit( - [ - cirq.Moment([cirq.Z(q) ** 0.5]), - cirq.Moment([cirq.Z(q) ** 0.25]), - cirq.Moment([cirq.measure(q)]), - ] - ), - expected=cirq.Circuit([cirq.Moment(), cirq.Moment(), cirq.Moment([cirq.measure(q)])]), - ) - - -def test_unphaseable_causes_earlier_merge_without_size_increase(): - class UnknownGate(cirq.testing.SingleQubitGate): - pass - - u = UnknownGate() - - # pylint: disable=not-callable - q = cirq.NamedQubit('q') - assert_optimizes( - before=cirq.Circuit( - [ - cirq.Moment([cirq.Z(q)]), - cirq.Moment([u(q)]), - cirq.Moment([cirq.Z(q) ** 0.5]), - cirq.Moment([cirq.X(q)]), - cirq.Moment([cirq.Z(q) ** 0.25]), - cirq.Moment([cirq.X(q)]), - cirq.Moment([u(q)]), - ] - ), - expected=cirq.Circuit( - [ - cirq.Moment([cirq.Z(q)]), - cirq.Moment([u(q)]), - cirq.Moment(), - cirq.Moment([cirq.PhasedXPowGate(phase_exponent=-0.5)(q)]), - cirq.Moment(), - cirq.Moment([cirq.PhasedXPowGate(phase_exponent=-0.75).on(q)]), - cirq.Moment([cirq.Z(q) ** 0.75]), - cirq.Moment([u(q)]), - ] - ), - ) - - -@pytest.mark.parametrize('sym', [sympy.Symbol('a'), sympy.Symbol('a') + 1]) -def test_symbols_block(sym): - q = cirq.NamedQubit('q') - assert_optimizes( - before=cirq.Circuit( - [ - cirq.Moment([cirq.Z(q)]), - cirq.Moment([cirq.Z(q) ** sym]), - cirq.Moment([cirq.Z(q) ** 0.25]), - ] - ), - expected=cirq.Circuit( - [cirq.Moment(), cirq.Moment([cirq.Z(q) ** sym]), cirq.Moment([cirq.Z(q) ** 1.25])] - ), - ) - - -@pytest.mark.parametrize('sym', [sympy.Symbol('a'), sympy.Symbol('a') + 1]) -def test_symbols_eject(sym): - q = cirq.NamedQubit('q') - assert_optimizes( - before=cirq.Circuit( - [ - cirq.Moment([cirq.Z(q)]), - cirq.Moment([cirq.Z(q) ** sym]), - cirq.Moment([cirq.Z(q) ** 0.25]), - ] - ), - expected=cirq.Circuit( - [cirq.Moment(), cirq.Moment(), cirq.Moment([cirq.Z(q) ** (sym + 1.25)])] - ), - eject_parameterized=True, - ) - - -def test_removes_zs(): - a = cirq.NamedQubit('a') - b = cirq.NamedQubit('b') - - assert_removes_all_z_gates(cirq.Circuit(cirq.Z(a), cirq.measure(a))) - - assert_removes_all_z_gates(cirq.Circuit(cirq.Z(a), cirq.measure(a, b))) - - assert_removes_all_z_gates(cirq.Circuit(cirq.Z(a), cirq.Z(a), cirq.measure(a))) - - assert_removes_all_z_gates(cirq.Circuit(cirq.Z(a), cirq.measure(a, key='k'))) - - assert_removes_all_z_gates(cirq.Circuit(cirq.Z(a), cirq.X(a), cirq.measure(a))) - - assert_removes_all_z_gates(cirq.Circuit(cirq.Z(a), cirq.X(a), cirq.X(a), cirq.measure(a))) - - assert_removes_all_z_gates( - cirq.Circuit(cirq.Z(a), cirq.Z(b), cirq.CZ(a, b), cirq.CZ(a, b), cirq.measure(a, b)) - ) - - assert_removes_all_z_gates( - cirq.Circuit( - cirq.PhasedXZGate(axis_phase_exponent=0, x_exponent=0, z_exponent=1).on(a), - cirq.measure(a), - ) - ) - - assert_removes_all_z_gates( - cirq.Circuit( - cirq.Z(a) ** sympy.Symbol('a'), - cirq.Z(b) ** (sympy.Symbol('a') + 1), - cirq.CZ(a, b), - cirq.CZ(a, b), - cirq.measure(a, b), - ), - eject_parameterized=True, - ) - - -def test_unknown_operation_blocks(): - q = cirq.NamedQubit('q') - - class UnknownOp(cirq.Operation): - @property - def qubits(self): - return [q] - - def with_qubits(self, *new_qubits): - raise NotImplementedError() - - u = UnknownOp() - - assert_optimizes( - before=cirq.Circuit([cirq.Moment([cirq.Z(q)]), cirq.Moment([u])]), - expected=cirq.Circuit([cirq.Moment([cirq.Z(q)]), cirq.Moment([u])]), - ) - - -def test_swap(): - a, b = cirq.LineQubit.range(2) - original = cirq.Circuit([cirq.rz(0.123).on(a), cirq.SWAP(a, b)]) - optimized = original.copy() - - with cirq.testing.assert_deprecated("Use cirq.eject_z", deadline='v1.0'): - cirq.EjectZ().optimize_circuit(optimized) - optimized = cirq.drop_empty_moments(optimized) - - assert optimized[0].operations == (cirq.SWAP(a, b),) - # Note: EjectZ drops `global_phase` from Rz turning it into a Z - assert optimized[1].operations == (cirq.Z(b) ** (0.123 / np.pi),) - cirq.testing.assert_allclose_up_to_global_phase( - cirq.unitary(original), cirq.unitary(optimized), atol=1e-8 - ) - - -@pytest.mark.parametrize('theta', (np.pi / 2, -np.pi / 2, np.pi / 2 + 5 * np.pi)) -def test_swap_fsim(theta): - a, b = cirq.LineQubit.range(2) - original = cirq.Circuit([cirq.rz(0.123).on(a), cirq.FSimGate(theta=theta, phi=0.123).on(a, b)]) - optimized = original.copy() - - with cirq.testing.assert_deprecated("Use cirq.eject_z", deadline='v1.0'): - cirq.EjectZ().optimize_circuit(optimized) - optimized = cirq.drop_empty_moments(optimized) - - assert optimized[0].operations == (cirq.FSimGate(theta=theta, phi=0.123).on(a, b),) - # Note: EjectZ drops `global_phase` from Rz turning it into a Z - assert optimized[1].operations == (cirq.Z(b) ** (0.123 / np.pi),) - cirq.testing.assert_allclose_up_to_global_phase( - cirq.unitary(original), cirq.unitary(optimized), atol=1e-8 - ) - - -@pytest.mark.parametrize('exponent', (1, -1)) -def test_swap_iswap(exponent): - a, b = cirq.LineQubit.range(2) - original = cirq.Circuit([cirq.rz(0.123).on(a), cirq.ISWAP(a, b) ** exponent]) - optimized = original.copy() - with cirq.testing.assert_deprecated("Use cirq.eject_z", deadline='v1.0'): - cirq.EjectZ().optimize_circuit(optimized) - optimized = cirq.drop_empty_moments(optimized) - - assert optimized[0].operations == (cirq.ISWAP(a, b) ** exponent,) - # Note: EjectZ drops `global_phase` from Rz turning it into a Z - assert optimized[1].operations == (cirq.Z(b) ** (0.123 / np.pi),) - cirq.testing.assert_allclose_up_to_global_phase( - cirq.unitary(original), cirq.unitary(optimized), atol=1e-8 - ) diff --git a/cirq-core/cirq/optimizers/expand_composite.py b/cirq-core/cirq/optimizers/expand_composite.py deleted file mode 100644 index 2f650961f07..00000000000 --- a/cirq-core/cirq/optimizers/expand_composite.py +++ /dev/null @@ -1,55 +0,0 @@ -# Copyright 2018 The Cirq Developers -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""An optimizer that expands composite operations via `cirq.decompose`.""" - -from typing import Callable, Optional, TYPE_CHECKING - -from cirq import ops, protocols -from cirq.circuits.optimization_pass import PointOptimizer, PointOptimizationSummary -from cirq._compat import deprecated_class - -if TYPE_CHECKING: - import cirq - - -@deprecated_class(deadline='v1.0', fix='Use cirq.expand_composite instead.') -class ExpandComposite(PointOptimizer): - """An optimizer that expands composite operations via `cirq.decompose`. - - For each operation in the circuit, this pass examines if the operation can - be decomposed. If it can be, the operation is cleared out and and replaced - with its decomposition using a fixed insertion strategy. - """ - - def __init__(self, no_decomp: Callable[[ops.Operation], bool] = (lambda _: False)) -> None: - """Construct the optimization pass. - - Args: - no_decomp: A predicate that determines whether an operation should - be decomposed or not. Defaults to decomposing everything. - """ - super().__init__() - self.no_decomp = no_decomp - - def optimization_at( - self, circuit: 'cirq.Circuit', index: int, op: 'cirq.Operation' - ) -> Optional['cirq.PointOptimizationSummary']: - decomposition = protocols.decompose(op, keep=self.no_decomp, on_stuck_raise=None) - if decomposition == [op]: - return None - - return PointOptimizationSummary( - clear_span=1, clear_qubits=op.qubits, new_operations=decomposition - ) diff --git a/cirq-core/cirq/optimizers/expand_composite_test.py b/cirq-core/cirq/optimizers/expand_composite_test.py deleted file mode 100644 index d367fa19a4a..00000000000 --- a/cirq-core/cirq/optimizers/expand_composite_test.py +++ /dev/null @@ -1,181 +0,0 @@ -# Copyright 2018 The Cirq Developers -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""Tests for the expand composite optimization pass.""" -import cirq - - -def assert_equal_mod_empty(expected, actual): - actual = cirq.drop_empty_moments(actual) - assert expected == actual, f'EXPECTED {expected} : ACTUAL {actual}' - - -def assert_optimizes(no_decomp, expected, actual): - with cirq.testing.assert_deprecated("Use cirq.expand_composite", deadline='v1.0'): - expander = cirq.ExpandComposite(no_decomp) if no_decomp else cirq.ExpandComposite() - expander(expected) - if actual: - assert_equal_mod_empty(expected, actual) - - -def test_empty_circuit(): - circuit = cirq.Circuit() - assert_optimizes(None, circuit.copy(), circuit) - - -def test_empty_moment(): - circuit = cirq.Circuit([]) - assert_optimizes(None, circuit.copy(), circuit) - - -def test_ignore_non_composite(): - q0, q1 = cirq.LineQubit.range(2) - circuit = cirq.Circuit() - circuit.append([cirq.X(q0), cirq.Y(q1), cirq.CZ(q0, q1), cirq.Z(q0)]) - assert_optimizes(None, circuit.copy(), circuit) - - -def test_composite_default(): - q0, q1 = cirq.LineQubit.range(2) - cnot = cirq.CNOT(q0, q1) - circuit = cirq.Circuit() - circuit.append(cnot) - expected = cirq.Circuit() - expected.append([cirq.Y(q1) ** -0.5, cirq.CZ(q0, q1), cirq.Y(q1) ** 0.5]) - assert_optimizes(None, circuit, expected) - - -def test_multiple_composite_default(): - q0, q1 = cirq.LineQubit.range(2) - cnot = cirq.CNOT(q0, q1) - circuit = cirq.Circuit() - circuit.append([cnot, cnot]) - expected = cirq.Circuit() - decomp = [cirq.Y(q1) ** -0.5, cirq.CZ(q0, q1), cirq.Y(q1) ** 0.5] - expected.append([decomp, decomp]) - assert_optimizes(None, circuit, expected) - assert_equal_mod_empty(expected, circuit) - - -def test_mix_composite_non_composite(): - q0, q1 = cirq.LineQubit.range(2) - - actual = cirq.Circuit(cirq.X(q0), cirq.CNOT(q0, q1), cirq.X(q1)) - - expected = cirq.Circuit( - cirq.X(q0), - cirq.Y(q1) ** -0.5, - cirq.CZ(q0, q1), - cirq.Y(q1) ** 0.5, - cirq.X(q1), - strategy=cirq.InsertStrategy.NEW, - ) - assert_optimizes(None, actual, expected) - - -def test_recursive_composite(): - q0, q1 = cirq.LineQubit.range(2) - swap = cirq.SWAP(q0, q1) - circuit = cirq.Circuit() - circuit.append(swap) - - expected = cirq.Circuit( - cirq.Y(q1) ** -0.5, - cirq.CZ(q0, q1), - cirq.Y(q1) ** 0.5, - cirq.Y(q0) ** -0.5, - cirq.CZ(q1, q0), - cirq.Y(q0) ** 0.5, - cirq.Y(q1) ** -0.5, - cirq.CZ(q0, q1), - cirq.Y(q1) ** 0.5, - ) - assert_optimizes(None, circuit, expected) - - -def test_decompose_returns_not_flat_op_tree(): - class DummyGate(cirq.testing.SingleQubitGate): - def _decompose_(self, qubits): - (q0,) = qubits - # Yield a tuple of gates instead of yielding a gate - yield cirq.X(q0), - - q0 = cirq.NamedQubit('q0') - circuit = cirq.Circuit(DummyGate()(q0)) - - expected = cirq.Circuit(cirq.X(q0)) - assert_optimizes(None, circuit, expected) - - -def test_decompose_returns_deep_op_tree(): - class DummyGate(cirq.testing.TwoQubitGate): - def _decompose_(self, qubits): - q0, q1 = qubits - # Yield a tuple - yield ((cirq.X(q0), cirq.Y(q0)), cirq.Z(q0)) - # Yield nested lists - yield [cirq.X(q0), [cirq.Y(q0), cirq.Z(q0)]] - - def generator(depth): - if depth <= 0: - yield cirq.CZ(q0, q1), cirq.Y(q0) - else: - yield cirq.X(q0), generator(depth - 1) - yield cirq.Z(q0) - - # Yield nested generators - yield generator(2) - - q0, q1 = cirq.LineQubit.range(2) - circuit = cirq.Circuit(DummyGate()(q0, q1)) - - expected = cirq.Circuit( - cirq.X(q0), - cirq.Y(q0), - cirq.Z(q0), # From tuple - cirq.X(q0), - cirq.Y(q0), - cirq.Z(q0), # From nested lists - # From nested generators - cirq.X(q0), - cirq.X(q0), - cirq.CZ(q0, q1), - cirq.Y(q0), - cirq.Z(q0), - cirq.Z(q0), - ) - assert_optimizes(None, circuit, expected) - - -def test_non_recursive_expansion(): - qubits = [cirq.NamedQubit(s) for s in 'xy'] - no_decomp = lambda op: (isinstance(op, cirq.GateOperation) and op.gate == cirq.ISWAP) - unexpanded_circuit = cirq.Circuit(cirq.ISWAP(*qubits)) - - circuit = unexpanded_circuit.__copy__() - assert_optimizes(no_decomp, circuit, unexpanded_circuit) - - no_decomp = lambda op: ( - isinstance(op, cirq.GateOperation) - and isinstance(op.gate, (cirq.CNotPowGate, cirq.HPowGate)) - ) - circuit = unexpanded_circuit.__copy__() - assert_optimizes(no_decomp, circuit, None) - actual_text_diagram = circuit.to_text_diagram().strip() - expected_text_diagram = """ -x: ───@───H───X───S───X───S^-1───H───@─── - │ │ │ │ -y: ───X───────@───────@──────────────X─── - """.strip() - assert actual_text_diagram == expected_text_diagram diff --git a/cirq-core/cirq/optimizers/merge_interactions.py b/cirq-core/cirq/optimizers/merge_interactions.py deleted file mode 100644 index 797930303c0..00000000000 --- a/cirq-core/cirq/optimizers/merge_interactions.py +++ /dev/null @@ -1,263 +0,0 @@ -# Copyright 2018 The Cirq Developers -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""An optimization pass that combines adjacent series of gates on two qubits.""" - -from typing import Callable, List, Optional, Sequence, Tuple, cast, TYPE_CHECKING - -import abc -import numpy as np - -from cirq import circuits, ops, protocols, _compat -from cirq.transformers.analytical_decompositions import two_qubit_to_cz - -if TYPE_CHECKING: - import cirq - - -@_compat.deprecated_class( - deadline='v1.0', - fix='Use cirq.optimize_for_target_gateset and cirq.CompilationTargetGateset instead.', -) -class MergeInteractionsAbc(circuits.PointOptimizer, metaclass=abc.ABCMeta): - """Combines series of adjacent one- and two-qubit, non-parametrized gates - operating on a pair of qubits.""" - - def __init__( - self, - tolerance: float = 1e-8, - post_clean_up: Callable[[Sequence[ops.Operation]], ops.OP_TREE] = lambda op_list: op_list, - ) -> None: - """Inits MergeInteractionsAbc. - - Args: - tolerance: A limit on the amount of absolute error introduced by the - construction. - post_clean_up: This function is called on each set of optimized - operations before they are put into the circuit to replace the - old operations. - """ - super().__init__(post_clean_up=post_clean_up) - self.tolerance = tolerance - - def optimization_at( - self, circuit: circuits.Circuit, index: int, op: ops.Operation - ) -> Optional[circuits.PointOptimizationSummary]: - if len(op.qubits) != 2: - return None - - old_operations, indices, matrix = self._scan_two_qubit_ops_into_matrix( - circuit, index, op.qubits - ) - - old_interaction_count = len( - [old_op for old_op in old_operations if len(old_op.qubits) == 2] - ) - - switch_to_new = False - switch_to_new |= any( - len(old_op.qubits) == 2 and not self._may_keep_old_op(old_op) - for old_op in old_operations - ) - - # This point cannot be optimized using this method - if not switch_to_new and old_interaction_count <= 1: - return None - - # Find a (possibly ideal) decomposition of the merged operations. - new_operations = self._two_qubit_matrix_to_cz_operations(op.qubits[0], op.qubits[1], matrix) - new_interaction_count = len( - [new_op for new_op in new_operations if len(new_op.qubits) == 2] - ) - - switch_to_new |= new_interaction_count < old_interaction_count - - if not switch_to_new: - return None - - return circuits.PointOptimizationSummary( - clear_span=max(indices) + 1 - index, - clear_qubits=op.qubits, - new_operations=new_operations, - ) - - @abc.abstractmethod - def _may_keep_old_op(self, old_op: 'cirq.Operation') -> bool: - """Returns True if the old two-qubit operation may be left unchanged - without decomposition.""" - - @abc.abstractmethod - def _two_qubit_matrix_to_cz_operations( - self, q0: 'cirq.Qid', q1: 'cirq.Qid', mat: np.ndarray - ) -> Sequence['cirq.Operation']: - """Decomposes the merged two-qubit gate unitary into the minimum number - of two-qubit gates. - - Args: - q0: The first qubit being operated on. - q1: The other qubit being operated on. - mat: Defines the operation to apply to the pair of qubits. - - Returns: - A list of operations implementing the matrix. - """ - - def _op_to_matrix( - self, op: ops.Operation, qubits: Tuple['cirq.Qid', ...] - ) -> Optional[np.ndarray]: - """Determines the effect of an operation on the given qubits. - - If the operation is a 1-qubit operation on one of the given qubits, - or a 2-qubit operation on both of the given qubits, and also the - operation has a known matrix, then a matrix is returned. Otherwise None - is returned. - - Args: - op: The operation to understand. - qubits: The qubits we care about. Order determines matrix tensor - order. - - Returns: - None, or else a matrix equivalent to the effect of the operation. - """ - if any(q not in qubits for q in op.qubits): - return None - - q1, q2 = qubits - - matrix = protocols.unitary(op, None) - if matrix is None: - return None - - assert op is not None - if op.qubits == qubits: - return matrix - if op.qubits == (q2, q1): - return _flip_kron_order(matrix) - if op.qubits == (q1,): - return np.kron(matrix, np.eye(2)) - if op.qubits == (q2,): - return np.kron(np.eye(2), matrix) - - return None - - def _scan_two_qubit_ops_into_matrix( - self, circuit: circuits.Circuit, index: Optional[int], qubits: Tuple['cirq.Qid', ...] - ) -> Tuple[Sequence[ops.Operation], List[int], np.ndarray]: - """Accumulates operations affecting the given pair of qubits. - - The scan terminates when it hits the end of the circuit, finds an - operation without a known matrix, or finds an operation that interacts - the given qubits with other qubits. - - Args: - circuit: The circuit to scan for operations. - index: The index to start scanning forward from. - qubits: The pair of qubits we care about. - - Returns: - A tuple containing: - 0. The operations. - 1. The moment indices those operations were on. - 2. A matrix equivalent to the effect of the scanned operations. - """ - - product = np.eye(4, dtype=np.complex128) - all_operations = [] - touched_indices = [] - - while index is not None: - operations = list({circuit.operation_at(q, index) for q in qubits}) - op_data = [self._op_to_matrix(op, qubits) for op in operations if op is not None] - - # Stop at any non-constant or non-local interaction. - if any(e is None for e in op_data): - break - present_ops = [op for op in operations if op] - present_op_data = cast(List[np.ndarray], op_data) - - for op_mat in present_op_data: - product = np.dot(op_mat, product) - all_operations.extend(present_ops) - - touched_indices.append(index) - index = circuit.next_moment_operating_on(qubits, index + 1) - - return all_operations, touched_indices, product - - -def _flip_kron_order(mat4x4: np.ndarray) -> np.ndarray: - """Given M = sum(kron(a_i, b_i)), returns M' = sum(kron(b_i, a_i)).""" - result = np.array([[0] * 4] * 4, dtype=np.complex128) - order = [0, 2, 1, 3] - for i in range(4): - for j in range(4): - result[order[i], order[j]] = mat4x4[i, j] - return result - - -@_compat.deprecated_class( - deadline='v1.0', fix='Use cirq.optimize_for_target_gateset and cirq.CZTargetGateset instead.' -) -class MergeInteractions(MergeInteractionsAbc): - """Combines series of adjacent one- and two-qubit, non-parametrized gates - operating on a pair of qubits and replaces each series with the minimum - number of CZ gates.""" - - def __init__( - self, - tolerance: float = 1e-8, - allow_partial_czs: bool = True, - post_clean_up: Callable[[Sequence[ops.Operation]], ops.OP_TREE] = lambda op_list: op_list, - ) -> None: - """Inits MergeInteractions. - - Args: - tolerance: A limit on the amount of absolute error introduced by the - construction. - allow_partial_czs: Enables the use of Partial-CZ gates. - post_clean_up: This function is called on each set of optimized - operations before they are put into the circuit to replace the - old operations. - """ - super().__init__(tolerance=tolerance, post_clean_up=post_clean_up) - self.allow_partial_czs = allow_partial_czs - self.gateset = ops.Gateset( - ops.CZPowGate if allow_partial_czs else ops.CZ, - ops.GlobalPhaseGate, - unroll_circuit_op=False, - ) - - def _may_keep_old_op(self, old_op: 'cirq.Operation') -> bool: - """Returns True if the old two-qubit operation may be left unchanged - without decomposition.""" - return old_op in self.gateset - - def _two_qubit_matrix_to_cz_operations( - self, q0: 'cirq.Qid', q1: 'cirq.Qid', mat: np.ndarray - ) -> Sequence['cirq.Operation']: - """Decomposes the merged two-qubit gate unitary into the minimum number - of CZ gates. - - Args: - q0: The first qubit being operated on. - q1: The other qubit being operated on. - mat: Defines the operation to apply to the pair of qubits. - - Returns: - A list of operations implementing the matrix. - """ - return two_qubit_to_cz.two_qubit_matrix_to_cz_operations( - q0, q1, mat, self.allow_partial_czs, self.tolerance, False - ) diff --git a/cirq-core/cirq/optimizers/merge_interactions_test.py b/cirq-core/cirq/optimizers/merge_interactions_test.py deleted file mode 100644 index 9156c948311..00000000000 --- a/cirq-core/cirq/optimizers/merge_interactions_test.py +++ /dev/null @@ -1,257 +0,0 @@ -# Copyright 2018 The Cirq Developers -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from typing import List - -import pytest -import sympy - -import cirq - - -def assert_optimizes(before: cirq.Circuit, expected: cirq.Circuit): - actual = cirq.Circuit(before) - with cirq.testing.assert_deprecated( - "Use cirq.optimize_for_target_gateset", deadline='v1.0', count=2 - ): - opt = cirq.MergeInteractions() - opt.optimize_circuit(actual) - - # Ignore differences that would be caught by follow-up optimizations. - followup_transformers: List[cirq.TRANSFORMER] = [ - cirq.merge_single_qubit_gates_to_phased_x_and_z, - cirq.eject_phased_paulis, - cirq.eject_z, - cirq.drop_negligible_operations, - cirq.drop_empty_moments, - ] - for transform in followup_transformers: - actual = transform(actual).unfreeze(copy=False) - expected = transform(expected).unfreeze(copy=False) - - assert actual == expected, f'ACTUAL {actual} : EXPECTED {expected}' - - -def assert_optimization_not_broken(circuit): - """Check that the unitary matrix for the input circuit is the same (up to - global phase and rounding error) as the unitary matrix of the optimized - circuit.""" - u_before = circuit.unitary() - with cirq.testing.assert_deprecated( - "Use cirq.optimize_for_target_gateset", deadline='v1.0', count=2 - ): - cirq.MergeInteractions().optimize_circuit(circuit) - u_after = circuit.unitary() - - cirq.testing.assert_allclose_up_to_global_phase(u_before, u_after, atol=1e-8) - - -def test_clears_paired_cnot(): - a, b = cirq.LineQubit.range(2) - assert_optimizes( - before=cirq.Circuit([cirq.Moment([cirq.CNOT(a, b)]), cirq.Moment([cirq.CNOT(a, b)])]), - expected=cirq.Circuit(), - ) - - -def test_ignores_czs_separated_by_parameterized(): - a, b = cirq.LineQubit.range(2) - assert_optimizes( - before=cirq.Circuit( - [ - cirq.Moment([cirq.CZ(a, b)]), - cirq.Moment([cirq.Z(a) ** sympy.Symbol('boo')]), - cirq.Moment([cirq.CZ(a, b)]), - ] - ), - expected=cirq.Circuit( - [ - cirq.Moment([cirq.CZ(a, b)]), - cirq.Moment([cirq.Z(a) ** sympy.Symbol('boo')]), - cirq.Moment([cirq.CZ(a, b)]), - ] - ), - ) - - -def test_ignores_czs_separated_by_outer_cz(): - q00 = cirq.GridQubit(0, 0) - q01 = cirq.GridQubit(0, 1) - q10 = cirq.GridQubit(1, 0) - assert_optimizes( - before=cirq.Circuit( - [ - cirq.Moment([cirq.CZ(q00, q01)]), - cirq.Moment([cirq.CZ(q00, q10)]), - cirq.Moment([cirq.CZ(q00, q01)]), - ] - ), - expected=cirq.Circuit( - [ - cirq.Moment([cirq.CZ(q00, q01)]), - cirq.Moment([cirq.CZ(q00, q10)]), - cirq.Moment([cirq.CZ(q00, q01)]), - ] - ), - ) - - -def test_cnots_separated_by_single_gates_correct(): - a, b = cirq.LineQubit.range(2) - assert_optimization_not_broken(cirq.Circuit(cirq.CNOT(a, b), cirq.H(b), cirq.CNOT(a, b))) - - -def test_czs_separated_by_single_gates_correct(): - a, b = cirq.LineQubit.range(2) - assert_optimization_not_broken( - cirq.Circuit(cirq.CZ(a, b), cirq.X(b), cirq.X(b), cirq.X(b), cirq.CZ(a, b)) - ) - - -def test_inefficient_circuit_correct(): - t = 0.1 - v = 0.11 - a, b = cirq.LineQubit.range(2) - assert_optimization_not_broken( - cirq.Circuit( - cirq.H(b), - cirq.CNOT(a, b), - cirq.H(b), - cirq.CNOT(a, b), - cirq.CNOT(b, a), - cirq.H(a), - cirq.CNOT(a, b), - cirq.Z(a) ** t, - cirq.Z(b) ** -t, - cirq.CNOT(a, b), - cirq.H(a), - cirq.Z(b) ** v, - cirq.CNOT(a, b), - cirq.Z(a) ** -v, - cirq.Z(b) ** -v, - ) - ) - - -def test_optimizes_single_iswap(): - a, b = cirq.LineQubit.range(2) - c = cirq.Circuit(cirq.ISWAP(a, b)) - assert_optimization_not_broken(c) - with cirq.testing.assert_deprecated( - "Use cirq.optimize_for_target_gateset", deadline='v1.0', count=2 - ): - cirq.MergeInteractions().optimize_circuit(c) - assert len([1 for op in c.all_operations() if len(op.qubits) == 2]) == 2 - - -def test_optimizes_tagged_partial_cz(): - a, b = cirq.LineQubit.range(2) - c = cirq.Circuit((cirq.CZ**0.5)(a, b).with_tags('mytag')) - assert_optimization_not_broken(c) - with cirq.testing.assert_deprecated( - "Use cirq.optimize_for_target_gateset", deadline='v1.0', count=2 - ): - cirq.MergeInteractions(allow_partial_czs=False).optimize_circuit(c) - assert ( - len([1 for op in c.all_operations() if len(op.qubits) == 2]) == 2 - ), 'It should take 2 CZ gates to decompose a CZ**0.5 gate' - - -def test_not_decompose_czs(): - circuit = cirq.Circuit( - cirq.CZPowGate(exponent=1, global_shift=-0.5).on(*cirq.LineQubit.range(2)) - ) - circ_orig = circuit.copy() - with cirq.testing.assert_deprecated( - "Use cirq.optimize_for_target_gateset", deadline='v1.0', count=2 - ): - cirq.MergeInteractions(allow_partial_czs=False).optimize_circuit(circuit) - assert circ_orig == circuit - - -@pytest.mark.parametrize( - 'circuit', - ( - cirq.Circuit(cirq.CZPowGate(exponent=0.1)(*cirq.LineQubit.range(2))), - cirq.Circuit( - cirq.CZPowGate(exponent=0.2)(*cirq.LineQubit.range(2)), - cirq.CZPowGate(exponent=0.3, global_shift=-0.5)(*cirq.LineQubit.range(2)), - ), - ), -) -def test_decompose_partial_czs(circuit): - with cirq.testing.assert_deprecated( - "Use cirq.optimize_for_target_gateset", deadline='v1.0', count=2 - ): - optimizer = cirq.MergeInteractions(allow_partial_czs=False) - optimizer.optimize_circuit(circuit) - - cz_gates = [ - op.gate - for op in circuit.all_operations() - if isinstance(op, cirq.GateOperation) and isinstance(op.gate, cirq.CZPowGate) - ] - num_full_cz = sum(1 for cz in cz_gates if cz.exponent % 2 == 1) - num_part_cz = sum(1 for cz in cz_gates if cz.exponent % 2 != 1) - assert num_full_cz == 2 - assert num_part_cz == 0 - - -def test_not_decompose_partial_czs(): - circuit = cirq.Circuit( - cirq.CZPowGate(exponent=0.1, global_shift=-0.5)(*cirq.LineQubit.range(2)) - ) - with cirq.testing.assert_deprecated( - "Use cirq.optimize_for_target_gateset", deadline='v1.0', count=2 - ): - optimizer = cirq.MergeInteractions(allow_partial_czs=True) - optimizer.optimize_circuit(circuit) - - cz_gates = [ - op.gate - for op in circuit.all_operations() - if isinstance(op, cirq.GateOperation) and isinstance(op.gate, cirq.CZPowGate) - ] - num_full_cz = sum(1 for cz in cz_gates if cz.exponent % 2 == 1) - num_part_cz = sum(1 for cz in cz_gates if cz.exponent % 2 != 1) - assert num_full_cz == 0 - assert num_part_cz == 1 - - -def test_post_clean_up(): - class Marker(cirq.testing.TwoQubitGate): - pass - - a, b = cirq.LineQubit.range(2) - c_orig = cirq.Circuit(cirq.CZ(a, b), cirq.CZ(a, b), cirq.CZ(a, b), cirq.CZ(a, b), cirq.CZ(a, b)) - circuit = cirq.Circuit(c_orig) - - def clean_up(operations): - yield Marker()(a, b) - yield operations - yield Marker()(a, b) - - with cirq.testing.assert_deprecated( - "Use cirq.optimize_for_target_gateset", deadline='v1.0', count=2 - ): - optimizer = cirq.MergeInteractions(allow_partial_czs=False, post_clean_up=clean_up) - optimizer.optimize_circuit(circuit) - circuit = cirq.drop_empty_moments(circuit) - - assert isinstance(circuit[0].operations[0].gate, Marker) - assert isinstance(circuit[-1].operations[0].gate, Marker) - - u_before = c_orig.unitary() - u_after = circuit[1:-1].unitary() - cirq.testing.assert_allclose_up_to_global_phase(u_before, u_after, atol=1e-8) diff --git a/cirq-core/cirq/optimizers/merge_interactions_to_sqrt_iswap.py b/cirq-core/cirq/optimizers/merge_interactions_to_sqrt_iswap.py deleted file mode 100644 index 3f4f8754a93..00000000000 --- a/cirq-core/cirq/optimizers/merge_interactions_to_sqrt_iswap.py +++ /dev/null @@ -1,110 +0,0 @@ -# Copyright 2021 The Cirq Developers -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -"""An optimization pass that combines adjacent series of gates on two qubits and -outputs a circuit with SQRT_ISWAP or SQRT_ISWAP_INV gates.""" - -from typing import Callable, Optional, Sequence, TYPE_CHECKING - -import numpy as np - -from cirq import ops, _compat -from cirq.optimizers import merge_interactions -from cirq.transformers.analytical_decompositions import two_qubit_to_sqrt_iswap - -if TYPE_CHECKING: - import cirq - - -@_compat.deprecated_class( - deadline='v1.0', - fix='Use cirq.optimize_for_target_gateset and cirq.SqrtIswapTargetGateset instead.', -) -class MergeInteractionsToSqrtIswap(merge_interactions.MergeInteractionsAbc): - """Combines series of adjacent one- and two-qubit, non-parametrized gates - operating on a pair of qubits and replaces each series with the minimum - number of SQRT_ISWAP gates. - - See also: ``two_qubit_matrix_to_sqrt_iswap_operations`` - """ - - def __init__( - self, - tolerance: float = 1e-8, - *, - required_sqrt_iswap_count: Optional[int] = None, - use_sqrt_iswap_inv: bool = False, - post_clean_up: Callable[[Sequence[ops.Operation]], ops.OP_TREE] = lambda op_list: op_list, - ) -> None: - """Inits MergeInteractionsToSqrtIswap. - - Args: - tolerance: A limit on the amount of absolute error introduced by the - construction. - required_sqrt_iswap_count: When specified, each merged group of - two-qubit gates will be decomposed into exactly this many - sqrt-iSWAP gates even if fewer is possible (maximum 3). Circuit - optimization will raise a ``ValueError`` if this number is 2 or - lower and synthesis of any set of merged interactions requires - more. - use_sqrt_iswap_inv: If True, optimizes circuits using - ``SQRT_ISWAP_INV`` gates instead of ``SQRT_ISWAP``. - post_clean_up: This function is called on each set of optimized - operations before they are put into the circuit to replace the - old operations. - - Raises: - ValueError: - If ``required_sqrt_iswap_count`` is not one of the supported - values 0, 1, 2, or 3. - """ - if required_sqrt_iswap_count is not None and not 0 <= required_sqrt_iswap_count <= 3: - raise ValueError('the argument `required_sqrt_iswap_count` must be 0, 1, 2, or 3.') - super().__init__(tolerance=tolerance, post_clean_up=post_clean_up) - self.required_sqrt_iswap_count = required_sqrt_iswap_count - self.use_sqrt_iswap_inv = use_sqrt_iswap_inv - self.gateset = ops.Gateset( - ops.SQRT_ISWAP_INV if use_sqrt_iswap_inv else ops.SQRT_ISWAP, - ops.GlobalPhaseGate, - unroll_circuit_op=False, - ) - - def _may_keep_old_op(self, old_op: 'cirq.Operation') -> bool: - """Returns True if the old two-qubit operation may be left unchanged - without decomposition.""" - return old_op in self.gateset - - def _two_qubit_matrix_to_cz_operations( - self, q0: 'cirq.Qid', q1: 'cirq.Qid', mat: np.ndarray - ) -> Sequence['cirq.Operation']: - """Decomposes the merged two-qubit gate unitary into the minimum number - of SQRT_ISWAP gates. - - Args: - q0: The first qubit being operated on. - q1: The other qubit being operated on. - mat: Defines the operation to apply to the pair of qubits. - - Returns: - A list of operations implementing the matrix. - """ - return two_qubit_to_sqrt_iswap.two_qubit_matrix_to_sqrt_iswap_operations( - q0, - q1, - mat, - required_sqrt_iswap_count=self.required_sqrt_iswap_count, - use_sqrt_iswap_inv=self.use_sqrt_iswap_inv, - atol=self.tolerance, - check_preconditions=False, - ) diff --git a/cirq-core/cirq/optimizers/merge_interactions_to_sqrt_iswap_test.py b/cirq-core/cirq/optimizers/merge_interactions_to_sqrt_iswap_test.py deleted file mode 100644 index 119a15637d5..00000000000 --- a/cirq-core/cirq/optimizers/merge_interactions_to_sqrt_iswap_test.py +++ /dev/null @@ -1,323 +0,0 @@ -# Copyright 2021 The Cirq Developers -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -from typing import List - -import pytest - -import cirq - - -def assert_optimizes(before: cirq.Circuit, expected: cirq.Circuit, **kwargs): - """Check that optimizing the circuit ``before`` produces the circuit ``expected``. - - The optimized circuit is cleaned up with follow up optimizations to make the - comparison more robust to extra moments or extra gates nearly equal to - identity that don't matter. - - Args: - before: The input circuit to optimize. - expected: The expected result of optimization to compare against. - **kwargs: Any extra arguments to pass to the - ``MergeInteractionsToSqrtIswap`` constructor. - """ - actual = before.copy() - with cirq.testing.assert_deprecated( - "Use cirq.optimize_for_target_gateset", deadline='v1.0', count=2 - ): - opt = cirq.MergeInteractionsToSqrtIswap(**kwargs) - opt.optimize_circuit(actual) - - # Ignore differences that would be caught by follow-up optimizations. - followup_transformers: List[cirq.TRANSFORMER] = [ - cirq.merge_single_qubit_gates_to_phased_x_and_z, - cirq.eject_phased_paulis, - cirq.eject_z, - cirq.drop_negligible_operations, - cirq.drop_empty_moments, - ] - for transform in followup_transformers: - actual = transform(actual).unfreeze(copy=False) - expected = transform(expected).unfreeze(copy=False) - - assert actual == expected, f'ACTUAL {actual} : EXPECTED {expected}' - - -def assert_optimization_not_broken(circuit: cirq.Circuit, **kwargs): - """Check that the unitary matrix for the input circuit is the same (up to - global phase and rounding error) as the unitary matrix of the optimized - circuit.""" - u_before = circuit.unitary(sorted(circuit.all_qubits())) - c_sqrt_iswap = circuit.copy() - with cirq.testing.assert_deprecated( - "Use cirq.optimize_for_target_gateset", deadline='v1.0', count=2 - ): - cirq.MergeInteractionsToSqrtIswap(**kwargs).optimize_circuit(c_sqrt_iswap) - u_after = c_sqrt_iswap.unitary(sorted(circuit.all_qubits())) - - # Not 1e-8 because of some unaccounted accumulated error in some of Cirq's linalg functions - cirq.testing.assert_allclose_up_to_global_phase(u_before, u_after, atol=1e-6) - - # Also test optimization with SQRT_ISWAP_INV - c_sqrt_iswap_inv = circuit.copy() - with cirq.testing.assert_deprecated( - "Use cirq.optimize_for_target_gateset", deadline='v1.0', count=2 - ): - cirq.MergeInteractionsToSqrtIswap(use_sqrt_iswap_inv=True).optimize_circuit( - c_sqrt_iswap_inv - ) - u_after2 = c_sqrt_iswap_inv.unitary(sorted(circuit.all_qubits())) - - cirq.testing.assert_allclose_up_to_global_phase(u_before, u_after2, atol=1e-6) - - -def test_clears_paired_cnot(): - a, b = cirq.LineQubit.range(2) - assert_optimizes( - before=cirq.Circuit([cirq.Moment([cirq.CNOT(a, b)]), cirq.Moment([cirq.CNOT(a, b)])]), - expected=cirq.Circuit(), - ) - - -def test_simplifies_sqrt_iswap(): - a, b = cirq.LineQubit.range(2) - assert_optimizes( - before=cirq.Circuit( - [ - # SQRT_ISWAP**8 == Identity - cirq.Moment([cirq.SQRT_ISWAP(a, b)]), - cirq.Moment([cirq.SQRT_ISWAP(a, b)]), - cirq.Moment([cirq.SQRT_ISWAP(a, b)]), - cirq.Moment([cirq.SQRT_ISWAP(a, b)]), - cirq.Moment([cirq.SQRT_ISWAP(a, b)]), - cirq.Moment([cirq.SQRT_ISWAP(a, b)]), - cirq.Moment([cirq.SQRT_ISWAP(a, b)]), - cirq.Moment([cirq.SQRT_ISWAP(a, b)]), - cirq.Moment([cirq.SQRT_ISWAP(a, b)]), - ] - ), - expected=cirq.Circuit([cirq.Moment([cirq.SQRT_ISWAP(a, b)])]), - ) - - -def test_simplifies_sqrt_iswap_inv(): - a, b = cirq.LineQubit.range(2) - assert_optimizes( - use_sqrt_iswap_inv=True, - before=cirq.Circuit( - [ - # SQRT_ISWAP**8 == Identity - cirq.Moment([cirq.SQRT_ISWAP(a, b)]), - cirq.Moment([cirq.SQRT_ISWAP(a, b)]), - cirq.Moment([cirq.SQRT_ISWAP(a, b)]), - cirq.Moment([cirq.SQRT_ISWAP(a, b)]), - cirq.Moment([cirq.SQRT_ISWAP(a, b)]), - cirq.Moment([cirq.SQRT_ISWAP_INV(a, b)]), - cirq.Moment([cirq.SQRT_ISWAP(a, b)]), - cirq.Moment([cirq.SQRT_ISWAP(a, b)]), - cirq.Moment([cirq.SQRT_ISWAP(a, b)]), - ] - ), - expected=cirq.Circuit([cirq.Moment([cirq.SQRT_ISWAP_INV(a, b)])]), - ) - - -def test_works_with_tags(): - a, b = cirq.LineQubit.range(2) - assert_optimizes( - before=cirq.Circuit( - [ - cirq.Moment([cirq.SQRT_ISWAP(a, b).with_tags('mytag1')]), - cirq.Moment([cirq.SQRT_ISWAP(a, b).with_tags('mytag2')]), - cirq.Moment([cirq.SQRT_ISWAP_INV(a, b).with_tags('mytag3')]), - ] - ), - expected=cirq.Circuit([cirq.Moment([cirq.SQRT_ISWAP(a, b)])]), - ) - - -def test_no_touch_single_sqrt_iswap(): - a, b = cirq.LineQubit.range(2) - circuit = cirq.Circuit( - [ - cirq.Moment( - [cirq.ISwapPowGate(exponent=0.5, global_shift=-0.5).on(a, b).with_tags('mytag')] - ) - ] - ) - assert_optimizes(before=circuit, expected=circuit) - - -def test_no_touch_single_sqrt_iswap_inv(): - a, b = cirq.LineQubit.range(2) - circuit = cirq.Circuit( - [ - cirq.Moment( - [cirq.ISwapPowGate(exponent=-0.5, global_shift=-0.5).on(a, b).with_tags('mytag')] - ) - ] - ) - assert_optimizes(before=circuit, expected=circuit, use_sqrt_iswap_inv=True) - - -def test_cnots_separated_by_single_gates_correct(): - a, b = cirq.LineQubit.range(2) - assert_optimization_not_broken(cirq.Circuit(cirq.CNOT(a, b), cirq.H(b), cirq.CNOT(a, b))) - - -def test_czs_separated_by_single_gates_correct(): - a, b = cirq.LineQubit.range(2) - assert_optimization_not_broken( - cirq.Circuit(cirq.CZ(a, b), cirq.X(b), cirq.X(b), cirq.X(b), cirq.CZ(a, b)) - ) - - -def test_inefficient_circuit_correct(): - t = 0.1 - v = 0.11 - a, b = cirq.LineQubit.range(2) - assert_optimization_not_broken( - cirq.Circuit( - cirq.H(b), - cirq.CNOT(a, b), - cirq.H(b), - cirq.CNOT(a, b), - cirq.CNOT(b, a), - cirq.H(a), - cirq.CNOT(a, b), - cirq.Z(a) ** t, - cirq.Z(b) ** -t, - cirq.CNOT(a, b), - cirq.H(a), - cirq.Z(b) ** v, - cirq.CNOT(a, b), - cirq.Z(a) ** -v, - cirq.Z(b) ** -v, - ) - ) - - -def test_optimizes_single_iswap(): - a, b = cirq.LineQubit.range(2) - c = cirq.Circuit(cirq.ISWAP(a, b)) - assert_optimization_not_broken(c) - with cirq.testing.assert_deprecated( - "Use cirq.optimize_for_target_gateset", deadline='v1.0', count=2 - ): - cirq.MergeInteractionsToSqrtIswap().optimize_circuit(c) - assert len([1 for op in c.all_operations() if len(op.qubits) == 2]) == 2 - - -def test_optimizes_single_inv_sqrt_iswap(): - a, b = cirq.LineQubit.range(2) - c = cirq.Circuit(cirq.SQRT_ISWAP_INV(a, b)) - assert_optimization_not_broken(c) - with cirq.testing.assert_deprecated( - "Use cirq.optimize_for_target_gateset", deadline='v1.0', count=2 - ): - cirq.MergeInteractionsToSqrtIswap().optimize_circuit(c) - assert len([1 for op in c.all_operations() if len(op.qubits) == 2]) == 1 - - -def test_init_raises(): - with pytest.raises(ValueError, match='must be 0, 1, 2, or 3'): - with cirq.testing.assert_deprecated( - "Use cirq.optimize_for_target_gateset", deadline='v1.0', count=2 - ): - cirq.MergeInteractionsToSqrtIswap(required_sqrt_iswap_count=4) - - -def test_optimizes_single_iswap_require0(): - a, b = cirq.LineQubit.range(2) - c = cirq.Circuit(cirq.CNOT(a, b), cirq.CNOT(a, b)) # Minimum 0 sqrt-iSWAP - assert_optimization_not_broken(c, required_sqrt_iswap_count=0) - with cirq.testing.assert_deprecated( - "Use cirq.optimize_for_target_gateset", deadline='v1.0', count=2 - ): - cirq.MergeInteractionsToSqrtIswap(required_sqrt_iswap_count=0).optimize_circuit(c) - assert len([1 for op in c.all_operations() if len(op.qubits) == 2]) == 0 - - -def test_optimizes_single_iswap_require0_raises(): - a, b = cirq.LineQubit.range(2) - c = cirq.Circuit(cirq.CNOT(a, b)) # Minimum 2 sqrt-iSWAP - with pytest.raises(ValueError, match='cannot be decomposed into exactly 0 sqrt-iSWAP gates'): - with cirq.testing.assert_deprecated( - "Use cirq.optimize_for_target_gateset", deadline='v1.0', count=2 - ): - cirq.MergeInteractionsToSqrtIswap(required_sqrt_iswap_count=0).optimize_circuit(c) - - -def test_optimizes_single_iswap_require1(): - a, b = cirq.LineQubit.range(2) - c = cirq.Circuit(cirq.SQRT_ISWAP_INV(a, b)) # Minimum 1 sqrt-iSWAP - assert_optimization_not_broken(c, required_sqrt_iswap_count=1) - with cirq.testing.assert_deprecated( - "Use cirq.optimize_for_target_gateset", deadline='v1.0', count=2 - ): - cirq.MergeInteractionsToSqrtIswap(required_sqrt_iswap_count=1).optimize_circuit(c) - assert len([1 for op in c.all_operations() if len(op.qubits) == 2]) == 1 - - -def test_optimizes_single_iswap_require1_raises(): - a, b = cirq.LineQubit.range(2) - c = cirq.Circuit(cirq.CNOT(a, b)) # Minimum 2 sqrt-iSWAP - with pytest.raises(ValueError, match='cannot be decomposed into exactly 1 sqrt-iSWAP gates'): - with cirq.testing.assert_deprecated( - "Use cirq.optimize_for_target_gateset", deadline='v1.0', count=2 - ): - cirq.MergeInteractionsToSqrtIswap(required_sqrt_iswap_count=1).optimize_circuit(c) - - -def test_optimizes_single_iswap_require2(): - a, b = cirq.LineQubit.range(2) - c = cirq.Circuit(cirq.SQRT_ISWAP_INV(a, b)) # Minimum 1 sqrt-iSWAP but 2 possible - assert_optimization_not_broken(c, required_sqrt_iswap_count=2) - with cirq.testing.assert_deprecated( - "Use cirq.optimize_for_target_gateset", deadline='v1.0', count=2 - ): - cirq.MergeInteractionsToSqrtIswap(required_sqrt_iswap_count=2).optimize_circuit(c) - assert len([1 for op in c.all_operations() if len(op.qubits) == 2]) == 2 - - -def test_optimizes_single_iswap_require2_raises(): - a, b = cirq.LineQubit.range(2) - c = cirq.Circuit(cirq.SWAP(a, b)) # Minimum 3 sqrt-iSWAP - with pytest.raises(ValueError, match='cannot be decomposed into exactly 2 sqrt-iSWAP gates'): - with cirq.testing.assert_deprecated( - "Use cirq.optimize_for_target_gateset", deadline='v1.0', count=2 - ): - cirq.MergeInteractionsToSqrtIswap(required_sqrt_iswap_count=2).optimize_circuit(c) - - -def test_optimizes_single_iswap_require3(): - a, b = cirq.LineQubit.range(2) - c = cirq.Circuit(cirq.ISWAP(a, b)) # Minimum 2 sqrt-iSWAP but 3 possible - assert_optimization_not_broken(c, required_sqrt_iswap_count=3) - with cirq.testing.assert_deprecated( - "Use cirq.optimize_for_target_gateset", deadline='v1.0', count=2 - ): - cirq.MergeInteractionsToSqrtIswap(required_sqrt_iswap_count=3).optimize_circuit(c) - assert len([1 for op in c.all_operations() if len(op.qubits) == 2]) == 3 - - -def test_optimizes_single_inv_sqrt_iswap_require3(): - a, b = cirq.LineQubit.range(2) - c = cirq.Circuit(cirq.SQRT_ISWAP_INV(a, b)) - assert_optimization_not_broken(c, required_sqrt_iswap_count=3) - with cirq.testing.assert_deprecated( - "Use cirq.optimize_for_target_gateset", deadline='v1.0', count=2 - ): - cirq.MergeInteractionsToSqrtIswap(required_sqrt_iswap_count=3).optimize_circuit(c) - assert len([1 for op in c.all_operations() if len(op.qubits) == 2]) == 3 diff --git a/cirq-core/cirq/optimizers/synchronize_terminal_measurements.py b/cirq-core/cirq/optimizers/synchronize_terminal_measurements.py deleted file mode 100644 index 6b84330711c..00000000000 --- a/cirq-core/cirq/optimizers/synchronize_terminal_measurements.py +++ /dev/null @@ -1,68 +0,0 @@ -# Copyright 2018 The Cirq Developers -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -"""An optimization pass to put as many measurements possible at the end.""" - -from typing import List, Set, Tuple, cast -from cirq import circuits, ops, protocols -from cirq._compat import deprecated_class - - -@deprecated_class(deadline='v1.0', fix='Use cirq.synchronize_terminal_measurements instead.') -class SynchronizeTerminalMeasurements: - """Move measurements to the end of the circuit. - - Move all measurements in a circuit to the final moment if it can accommodate - them (without overlapping with other operations). If - self._after_other_operations is true then a new moment will be added to the - end of the circuit containing all the measurements that should be brought - forward. - """ - - def __init__(self, after_other_operations: bool = True): - """Inits SynchronizeTerminalMeasurements. - - Args: - after_other_operations: Set by default. If the circuit's final - moment contains non-measurement operations and this is set then - a new empty moment is appended to the circuit before pushing - measurements to the end. - """ - self._after_other_operations = after_other_operations - - def __call__(self, circuit: circuits.Circuit): - self.optimize_circuit(circuit) - - def optimize_circuit(self, circuit: circuits.Circuit) -> None: - deletions: List[Tuple[int, ops.Operation]] = [] - terminal_measures: Set[ops.Operation] = set() - qubits = circuit.all_qubits() - for qubit in qubits: - moment_index = cast(int, circuit.prev_moment_operating_on((qubit,))) - op = cast(ops.Operation, circuit.operation_at(qubit, moment_index)) - if protocols.is_measurement(op): - deletions.append((moment_index, op)) - terminal_measures.add(op) - - if not deletions: - return - - circuit.batch_remove(deletions) - if circuit[-1] and self._after_other_operations: - # Can safely add to the end if - # self._after_other_operations is false or we happen to get an - # empty final moment before re-adding all the measurements. - circuit.append(circuits.Moment()) - - for op in terminal_measures: - circuit[-1] = circuit[-1].with_operation(op) diff --git a/cirq-core/cirq/optimizers/synchronize_terminal_measurements_test.py b/cirq-core/cirq/optimizers/synchronize_terminal_measurements_test.py deleted file mode 100644 index 6ed8038339c..00000000000 --- a/cirq-core/cirq/optimizers/synchronize_terminal_measurements_test.py +++ /dev/null @@ -1,159 +0,0 @@ -# Copyright 2018 The Cirq Developers -# -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# https://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. - -import cirq - - -def assert_optimizes(before, after, measure_only_moment=True): - with cirq.testing.assert_deprecated( - "Use cirq.synchronize_terminal_measurements", deadline='v1.0' - ): - opt = cirq.SynchronizeTerminalMeasurements(measure_only_moment) - opt(before) - assert before == after - - -def test_no_move(): - q1 = cirq.NamedQubit('q1') - before = cirq.Circuit([cirq.Moment([cirq.H(q1)])]) - after = before - assert_optimizes(before=before, after=after) - - -def test_simple_align(): - q1 = cirq.NamedQubit('q1') - q2 = cirq.NamedQubit('q2') - before = cirq.Circuit( - [ - cirq.Moment([cirq.H(q1), cirq.H(q2)]), - cirq.Moment([cirq.measure(q1), cirq.Z(q2)]), - cirq.Moment([cirq.measure(q2)]), - ] - ) - after = cirq.Circuit( - [ - cirq.Moment([cirq.H(q1), cirq.H(q2)]), - cirq.Moment([cirq.Z(q2)]), - cirq.Moment([cirq.measure(q1), cirq.measure(q2)]), - ] - ) - assert_optimizes(before=before, after=after) - - -def test_simple_partial_align(): - q1 = cirq.NamedQubit('q1') - q2 = cirq.NamedQubit('q2') - before = cirq.Circuit( - [cirq.Moment([cirq.measure(q1), cirq.Z(q2)]), cirq.Moment([cirq.Z(q1), cirq.measure(q2)])] - ) - after = cirq.Circuit( - [ - cirq.Moment([cirq.measure(q1), cirq.Z(q2)]), - cirq.Moment([cirq.Z(q1)]), - cirq.Moment([cirq.measure(q2)]), - ] - ) - assert_optimizes(before=before, after=after) - - -def test_slide_forward_one(): - q1 = cirq.NamedQubit('q1') - q2 = cirq.NamedQubit('q2') - q3 = cirq.NamedQubit('q3') - before = cirq.Circuit([cirq.Moment([cirq.H(q1), cirq.measure(q2), cirq.measure(q3)])]) - after = cirq.Circuit( - [cirq.Moment([cirq.H(q1)]), cirq.Moment([cirq.measure(q2), cirq.measure(q3)])] - ) - assert_optimizes(before=before, after=after) - - -def test_no_slide_forward_one(): - q1 = cirq.NamedQubit('q1') - q2 = cirq.NamedQubit('q2') - q3 = cirq.NamedQubit('q3') - before = cirq.Circuit([cirq.Moment([cirq.H(q1), cirq.measure(q2), cirq.measure(q3)])]) - after = cirq.Circuit([cirq.Moment([cirq.H(q1), cirq.measure(q2), cirq.measure(q3)])]) - assert_optimizes(before=before, after=after, measure_only_moment=False) - - -def test_blocked_shift_one(): - q1 = cirq.NamedQubit('q1') - q2 = cirq.NamedQubit('q2') - before = cirq.Circuit( - [ - cirq.Moment([cirq.H(q1), cirq.H(q2)]), - cirq.Moment([cirq.measure(q1), cirq.Z(q2)]), - cirq.Moment([cirq.H(q1), cirq.measure(q2)]), - ] - ) - after = cirq.Circuit( - [ - cirq.Moment([cirq.H(q1), cirq.H(q2)]), - cirq.Moment([cirq.measure(q1), cirq.Z(q2)]), - cirq.Moment([cirq.H(q1)]), - cirq.Moment([cirq.measure(q2)]), - ] - ) - assert_optimizes(before=before, after=after) - - -def test_complex_move(): - q1 = cirq.NamedQubit('q1') - q2 = cirq.NamedQubit('q2') - q3 = cirq.NamedQubit('q3') - before = cirq.Circuit( - [ - cirq.Moment([cirq.H(q1), cirq.H(q2)]), - cirq.Moment([cirq.measure(q1), cirq.Z(q2)]), - cirq.Moment([cirq.H(q1), cirq.measure(q2)]), - cirq.Moment([cirq.H(q3)]), - cirq.Moment([cirq.X(q1), cirq.measure(q3)]), - ] - ) - after = cirq.Circuit( - [ - cirq.Moment([cirq.H(q1), cirq.H(q2)]), - cirq.Moment([cirq.measure(q1), cirq.Z(q2)]), - cirq.Moment([cirq.H(q1)]), - cirq.Moment([cirq.H(q3)]), - cirq.Moment([cirq.X(q1)]), - cirq.Moment([cirq.measure(q2), cirq.measure(q3)]), - ] - ) - assert_optimizes(before=before, after=after) - - -def test_complex_move_no_slide(): - q1 = cirq.NamedQubit('q1') - q2 = cirq.NamedQubit('q2') - q3 = cirq.NamedQubit('q3') - before = cirq.Circuit( - [ - cirq.Moment([cirq.H(q1), cirq.H(q2)]), - cirq.Moment([cirq.measure(q1), cirq.Z(q2)]), - cirq.Moment([cirq.H(q1), cirq.measure(q2)]), - cirq.Moment([cirq.H(q3)]), - cirq.Moment([cirq.X(q1), cirq.measure(q3)]), - ] - ) - after = cirq.Circuit( - [ - cirq.Moment([cirq.H(q1), cirq.H(q2)]), - cirq.Moment([cirq.measure(q1), cirq.Z(q2)]), - cirq.Moment([cirq.H(q1)]), - cirq.Moment([cirq.H(q3)]), - cirq.Moment([cirq.X(q1), cirq.measure(q2), cirq.measure(q3)]), - ] - ) - assert_optimizes(before=before, after=after, measure_only_moment=False) diff --git a/cirq-core/cirq/protocols/json_test_data/spec.py b/cirq-core/cirq/protocols/json_test_data/spec.py index e29d5c3d40a..9d6ff77dfcd 100644 --- a/cirq-core/cirq/protocols/json_test_data/spec.py +++ b/cirq-core/cirq/protocols/json_test_data/spec.py @@ -90,17 +90,9 @@ 'AlignRight', 'ConvertToCzAndSingleGates', 'ConvertToIonGates', - 'DropEmptyMoments', - 'DropNegligible', - 'EjectPhasedPaulis', - 'EjectZ', - 'ExpandComposite', 'MEASUREMENT_KEY_SEPARATOR', - 'MergeInteractions', - 'MergeInteractionsToSqrtIswap', 'MergeSingleQubitGates', 'PointOptimizer', - 'SynchronizeTerminalMeasurements', # Transformers 'TransformerLogger', 'TransformerContext', From 55b1c7fa107472210a90fc3672fdf2e87fb883da Mon Sep 17 00:00:00 2001 From: Doug Strain Date: Mon, 11 Jul 2022 17:28:19 -0700 Subject: [PATCH 3/3] Fix formatting of init. --- cirq-core/cirq/__init__.py | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/cirq-core/cirq/__init__.py b/cirq-core/cirq/__init__.py index d0f65db9bff..3a21830e208 100644 --- a/cirq-core/cirq/__init__.py +++ b/cirq-core/cirq/__init__.py @@ -327,11 +327,7 @@ ZZPowGate, ) -from cirq.optimizers import ( - AlignLeft, - AlignRight, - ConvertToCzAndSingleGates, -) +from cirq.optimizers import AlignLeft, AlignRight, ConvertToCzAndSingleGates from cirq.transformers import ( align_left,