Skip to content

Commit

Permalink
Deprecate cirq.ConvertToSqrtIswapGates and cirq.MergeInteractionsToSq…
Browse files Browse the repository at this point in the history
…rtIswap (quantumlib#5040)
  • Loading branch information
tanujkhattar authored and rht committed May 1, 2023
1 parent 8435378 commit 22d720a
Show file tree
Hide file tree
Showing 7 changed files with 168 additions and 56 deletions.
4 changes: 4 additions & 0 deletions cirq-core/cirq/optimizers/merge_interactions.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,10 @@
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."""
Expand Down
32 changes: 24 additions & 8 deletions cirq-core/cirq/optimizers/merge_interactions_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,9 @@

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'):
with cirq.testing.assert_deprecated(
"Use cirq.optimize_for_target_gateset", deadline='v1.0', count=2
):
opt = cirq.MergeInteractions()
opt.optimize_circuit(actual)

Expand All @@ -46,7 +48,9 @@ def assert_optimization_not_broken(circuit):
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'):
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()

Expand Down Expand Up @@ -161,7 +165,9 @@ 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'):
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

Expand All @@ -170,7 +176,9 @@ 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'):
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
Expand All @@ -182,7 +190,9 @@ def test_not_decompose_czs():
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'):
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

Expand All @@ -200,7 +210,9 @@ def test_not_decompose_czs():
),
)
def test_decompose_partial_czs(circuit):
with cirq.testing.assert_deprecated("Use cirq.optimize_for_target_gateset", deadline='v1.0'):
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)

Expand All @@ -219,7 +231,9 @@ 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'):
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)

Expand Down Expand Up @@ -253,7 +267,9 @@ def clean_up(operations):
yield operations
yield Marker()(a, b)

with cirq.testing.assert_deprecated("Use cirq.optimize_for_target_gateset", deadline='v1.0'):
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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,18 @@

import numpy as np

from cirq import ops
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
Expand Down
74 changes: 59 additions & 15 deletions cirq-core/cirq/optimizers/merge_interactions_to_sqrt_iswap_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,11 @@ def assert_optimizes(before: cirq.Circuit, expected: cirq.Circuit, **kwargs):
``MergeInteractionsToSqrtIswap`` constructor.
"""
actual = before.copy()
opt = cirq.MergeInteractionsToSqrtIswap(**kwargs)
opt.optimize_circuit(actual)
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] = [
Expand All @@ -57,15 +60,23 @@ def assert_optimization_not_broken(circuit: cirq.Circuit, **kwargs):
circuit."""
u_before = circuit.unitary(sorted(circuit.all_qubits()))
c_sqrt_iswap = circuit.copy()
cirq.MergeInteractionsToSqrtIswap(**kwargs).optimize_circuit(c_sqrt_iswap)
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()
cirq.MergeInteractionsToSqrtIswap(use_sqrt_iswap_inv=True).optimize_circuit(c_sqrt_iswap_inv)
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)
Expand Down Expand Up @@ -230,79 +241,112 @@ def test_optimizes_single_iswap():
a, b = cirq.LineQubit.range(2)
c = cirq.Circuit(cirq.ISWAP(a, b))
assert_optimization_not_broken(c)
cirq.MergeInteractionsToSqrtIswap().optimize_circuit(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)
cirq.MergeInteractionsToSqrtIswap().optimize_circuit(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'):
cirq.MergeInteractionsToSqrtIswap(required_sqrt_iswap_count=4)
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)
cirq.MergeInteractionsToSqrtIswap(required_sqrt_iswap_count=0).optimize_circuit(c)
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'):
cirq.MergeInteractionsToSqrtIswap(required_sqrt_iswap_count=0).optimize_circuit(c)
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)
cirq.MergeInteractionsToSqrtIswap(required_sqrt_iswap_count=1).optimize_circuit(c)
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'):
cirq.MergeInteractionsToSqrtIswap(required_sqrt_iswap_count=1).optimize_circuit(c)
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)
cirq.MergeInteractionsToSqrtIswap(required_sqrt_iswap_count=2).optimize_circuit(c)
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'):
cirq.MergeInteractionsToSqrtIswap(required_sqrt_iswap_count=2).optimize_circuit(c)
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)
cirq.MergeInteractionsToSqrtIswap(required_sqrt_iswap_count=3).optimize_circuit(c)
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)
cirq.MergeInteractionsToSqrtIswap(required_sqrt_iswap_count=3).optimize_circuit(c)
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
4 changes: 4 additions & 0 deletions cirq-google/cirq_google/optimizers/convert_to_sqrt_iswap.py
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,10 @@ def _near_mod_2pi(e, t, atol=1e-8):
return _near_mod_n(e, t, 2 * np.pi, atol=atol)


@cirq._compat.deprecated_class(
deadline='v1.0',
fix='Use cirq.optimize_for_target_gateset and cirq.SqrtIswapTargetGateset instead.',
)
class ConvertToSqrtIswapGates(cirq.PointOptimizer):
"""Attempts to convert gates into ISWAP**-0.5 gates.
Expand Down
Loading

0 comments on commit 22d720a

Please sign in to comment.