Skip to content

Commit

Permalink
Rename two_qubit_matrix related functions (quantumlib#5070)
Browse files Browse the repository at this point in the history
  • Loading branch information
tonybruguier authored Mar 13, 2022
1 parent 740022d commit c67ac9c
Show file tree
Hide file tree
Showing 12 changed files with 120 additions and 28 deletions.
2 changes: 2 additions & 0 deletions cirq/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -401,6 +401,8 @@
TransformerLogger,
three_qubit_matrix_to_operations,
transformer,
two_qubit_matrix_to_cz_operations,
two_qubit_matrix_to_diagonal_and_cz_operations,
two_qubit_matrix_to_diagonal_and_operations,
two_qubit_matrix_to_operations,
two_qubit_matrix_to_sqrt_iswap_operations,
Expand Down
2 changes: 1 addition & 1 deletion cirq/neutral_atoms/convert_to_neutral_atom_gates.py
Original file line number Diff line number Diff line change
Expand Up @@ -60,7 +60,7 @@ def _convert_one(self, op: ops.Operation) -> ops.OP_TREE:
gates = transformers.single_qubit_matrix_to_phased_x_z(mat)
return [g.on(op.qubits[0]) for g in gates]
if mat is not None and len(op.qubits) == 2:
return transformers.two_qubit_matrix_to_operations(
return transformers.two_qubit_matrix_to_cz_operations(
op.qubits[0], op.qubits[1], mat, allow_partial_czs=False, clean_operations=True
)

Expand Down
2 changes: 2 additions & 0 deletions cirq/optimizers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -82,6 +82,8 @@
single_qubit_matrix_to_phxz,
single_qubit_op_to_framed_phase_form,
three_qubit_matrix_to_operations,
two_qubit_matrix_to_cz_operations,
two_qubit_matrix_to_diagonal_and_cz_operations,
two_qubit_matrix_to_diagonal_and_operations,
two_qubit_matrix_to_operations,
two_qubit_matrix_to_sqrt_iswap_operations,
Expand Down
2 changes: 1 addition & 1 deletion cirq/optimizers/convert_to_cz_and_single_gates.py
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ def _decompose_two_qubit_unitaries(self, op: ops.Operation) -> ops.OP_TREE:
if len(op.qubits) == 2:
mat = protocols.unitary(op, None)
if mat is not None:
return two_qubit_to_cz.two_qubit_matrix_to_operations(
return two_qubit_to_cz.two_qubit_matrix_to_cz_operations(
op.qubits[0], op.qubits[1], mat, allow_partial_czs=self.allow_partial_czs
)
return NotImplemented
Expand Down
8 changes: 4 additions & 4 deletions cirq/optimizers/merge_interactions.py
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ def optimization_at(
return None

# Find a (possibly ideal) decomposition of the merged operations.
new_operations = self._two_qubit_matrix_to_operations(op.qubits[0], op.qubits[1], matrix)
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]
)
Expand All @@ -98,7 +98,7 @@ def _may_keep_old_op(self, old_op: 'cirq.Operation') -> bool:
without decomposition."""

@abc.abstractmethod
def _two_qubit_matrix_to_operations(
def _two_qubit_matrix_to_cz_operations(
self,
q0: 'cirq.Qid',
q1: 'cirq.Qid',
Expand Down Expand Up @@ -247,7 +247,7 @@ def _may_keep_old_op(self, old_op: 'cirq.Operation') -> bool:
without decomposition."""
return old_op in self.gateset

def _two_qubit_matrix_to_operations(
def _two_qubit_matrix_to_cz_operations(
self,
q0: 'cirq.Qid',
q1: 'cirq.Qid',
Expand All @@ -264,6 +264,6 @@ def _two_qubit_matrix_to_operations(
Returns:
A list of operations implementing the matrix.
"""
return two_qubit_to_cz.two_qubit_matrix_to_operations(
return two_qubit_to_cz.two_qubit_matrix_to_cz_operations(
q0, q1, mat, self.allow_partial_czs, self.tolerance, False
)
2 changes: 1 addition & 1 deletion cirq/optimizers/merge_interactions_to_sqrt_iswap.py
Original file line number Diff line number Diff line change
Expand Up @@ -85,7 +85,7 @@ def _may_keep_old_op(self, old_op: 'cirq.Operation') -> bool:
without decomposition."""
return old_op in self.gateset

def _two_qubit_matrix_to_operations(
def _two_qubit_matrix_to_cz_operations(
self,
q0: 'cirq.Qid',
q1: 'cirq.Qid',
Expand Down
2 changes: 2 additions & 0 deletions cirq/transformers/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,8 @@
single_qubit_matrix_to_phxz,
single_qubit_op_to_framed_phase_form,
three_qubit_matrix_to_operations,
two_qubit_matrix_to_cz_operations,
two_qubit_matrix_to_diagonal_and_cz_operations,
two_qubit_matrix_to_diagonal_and_operations,
two_qubit_matrix_to_operations,
two_qubit_matrix_to_sqrt_iswap_operations,
Expand Down
4 changes: 3 additions & 1 deletion cirq/transformers/analytical_decompositions/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -42,8 +42,10 @@
)

from cirq.transformers.analytical_decompositions.two_qubit_to_cz import (
two_qubit_matrix_to_operations,
two_qubit_matrix_to_cz_operations,
two_qubit_matrix_to_diagonal_and_cz_operations,
two_qubit_matrix_to_diagonal_and_operations,
two_qubit_matrix_to_operations,
)

from cirq.transformers.analytical_decompositions.two_qubit_to_fsim import (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -183,17 +183,19 @@ def _two_qubit_multiplexor_to_ops(
if diagonal is not None:
v = diagonal @ v

d_v, circuit_u1u2_r = opt.two_qubit_matrix_to_diagonal_and_operations(q1, q2, v, atol=atol)
d_v, circuit_u1u2_r = opt.two_qubit_matrix_to_diagonal_and_cz_operations(q1, q2, v, atol=atol)

w = d_v @ w

# if it's interesting to extract the diagonal then let's do it
if shift_left:
d_w, circuit_u1u2_l = opt.two_qubit_matrix_to_diagonal_and_operations(q1, q2, w, atol=atol)
d_w, circuit_u1u2_l = opt.two_qubit_matrix_to_diagonal_and_cz_operations(
q1, q2, w, atol=atol
)
# if we are at the end of the circuit, then just fall back to KAK
else:
d_w = None
circuit_u1u2_l = opt.two_qubit_matrix_to_operations(
circuit_u1u2_l = opt.two_qubit_matrix_to_cz_operations(
q1, q2, w, allow_partial_czs=False, atol=atol
)

Expand Down
66 changes: 64 additions & 2 deletions cirq/transformers/analytical_decompositions/two_qubit_to_cz.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

import numpy as np

from cirq import _compat
from cirq.linalg import predicates
from cirq.linalg.decompositions import num_cnots_required, extract_right_diag

Expand All @@ -31,6 +32,7 @@
import cirq


@_compat.deprecated(fix='Please use cirq.two_qubit_matrix_to_cz_operations', deadline='v0.15')
def two_qubit_matrix_to_operations(
q0: 'cirq.Qid',
q1: 'cirq.Qid',
Expand All @@ -41,6 +43,32 @@ def two_qubit_matrix_to_operations(
) -> List[ops.Operation]:
"""Decomposes a two-qubit operation into Z/XY/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.
allow_partial_czs: Enables the use of Partial-CZ gates.
atol: A limit on the amount of absolute error introduced by the
construction.
clean_operations: Enables optimizing resulting operation list by
merging operations and ejecting phased Paulis and Z operations.
Returns:
A list of operations implementing the matrix.
"""
return two_qubit_matrix_to_cz_operations(q0, q1, mat, allow_partial_czs, atol, clean_operations)


def two_qubit_matrix_to_cz_operations(
q0: 'cirq.Qid',
q1: 'cirq.Qid',
mat: np.ndarray,
allow_partial_czs: bool,
atol: float = 1e-8,
clean_operations: bool = True,
) -> List[ops.Operation]:
"""Decomposes a two-qubit operation into Z/XY/CZ gates.
Args:
q0: The first qubit being operated on.
q1: The other qubit being operated on.
Expand All @@ -61,6 +89,9 @@ def two_qubit_matrix_to_operations(
return operations


@_compat.deprecated(
fix='Please use cirq.two_qubit_matrix_to_diagonal_and_cz_operations', deadline='v0.15'
)
def two_qubit_matrix_to_diagonal_and_operations(
q0: 'cirq.Qid',
q1: 'cirq.Qid',
Expand All @@ -71,6 +102,37 @@ def two_qubit_matrix_to_diagonal_and_operations(
) -> Tuple[np.ndarray, List['cirq.Operation']]:
"""Decomposes a 2-qubit unitary to a diagonal and the remaining operations.
For a 2-qubit unitary V, return ops, a list of operations and
D diagonal unitary, so that:
V = cirq.Circuit(ops) @ D
Args:
q0: The first qubit being operated on.
q1: The other qubit being operated on.
mat: the input unitary
allow_partial_czs: Enables the use of Partial-CZ gates.
atol: A limit on the amount of absolute error introduced by the
construction.
clean_operations: Enables optimizing resulting operation list by
merging operations and ejecting phased Paulis and Z operations.
Returns:
tuple(ops,D): operations `ops`, and the diagonal `D`
"""
return two_qubit_matrix_to_diagonal_and_cz_operations(
q0, q1, mat, allow_partial_czs, atol, clean_operations
)


def two_qubit_matrix_to_diagonal_and_cz_operations(
q0: 'cirq.Qid',
q1: 'cirq.Qid',
mat: np.ndarray,
allow_partial_czs: bool = False,
atol: float = 1e-8,
clean_operations: bool = True,
) -> Tuple[np.ndarray, List['cirq.Operation']]:
"""Decomposes a 2-qubit unitary to a diagonal and the remaining operations.
For a 2-qubit unitary V, return ops, a list of operations and
D diagonal unitary, so that:
V = cirq.Circuit(ops) @ D
Expand All @@ -94,7 +156,7 @@ def two_qubit_matrix_to_diagonal_and_operations(
right_diag = extract_right_diag(mat)
two_cnot_unitary = mat @ right_diag
# note that this implies that two_cnot_unitary @ d = mat
return right_diag.conj().T, two_qubit_matrix_to_operations(
return right_diag.conj().T, two_qubit_matrix_to_cz_operations(
q0,
q1,
two_cnot_unitary,
Expand All @@ -103,7 +165,7 @@ def two_qubit_matrix_to_diagonal_and_operations(
clean_operations=clean_operations,
)

return np.eye(4), two_qubit_matrix_to_operations(
return np.eye(4), two_qubit_matrix_to_cz_operations(
q0,
q1,
mat,
Expand Down
48 changes: 34 additions & 14 deletions cirq/transformers/analytical_decompositions/two_qubit_to_cz_test.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@
from cirq.transformers.analytical_decompositions.two_qubit_to_cz import (
_parity_interaction,
_is_trivial_angle,
two_qubit_matrix_to_diagonal_and_operations,
two_qubit_matrix_to_diagonal_and_cz_operations,
)
from cirq.testing import random_two_qubit_circuit_with_czs

Expand All @@ -34,7 +34,7 @@ def test_deprecated_submodule():
with cirq.testing.assert_deprecated(
"Use cirq.transformers.analytical_decompositions.two_qubit_to_cz instead", deadline="v0.16"
):
_ = cirq.optimizers.two_qubit_decompositions.two_qubit_matrix_to_operations
_ = cirq.optimizers.two_qubit_decompositions.two_qubit_matrix_to_cz_operations


@pytest.mark.parametrize(
Expand Down Expand Up @@ -121,6 +121,26 @@ def assert_ops_implement_unitary(q0, q1, operations, intended_effect, atol=0.01)
assert cirq.allclose_up_to_global_phase(actual_effect, intended_effect, atol=atol)


def test_two_qubit_matrix_to_operations_deprecated():
q0 = cirq.NamedQubit('q0')
q1 = cirq.NamedQubit('q1')
effect = np.array(
[
[0, 0, 0, 1],
[0, 0, 1, 0],
[0, 1, 0, 0],
[1, 0, 0, 0j],
]
)

with cirq.testing.assert_deprecated('two_qubit_matrix_to_cz_operations', deadline='v0.15'):
_ = cirq.two_qubit_matrix_to_operations(q0, q1, effect, True)
with cirq.testing.assert_deprecated(
'two_qubit_matrix_to_diagonal_and_cz_operations', deadline='v0.15'
):
_ = cirq.two_qubit_matrix_to_diagonal_and_operations(q0, q1, effect, True)


@pytest.mark.parametrize(
'max_partial_cz_depth,max_full_cz_depth,effect',
[
Expand Down Expand Up @@ -207,8 +227,8 @@ def test_two_to_ops_equivalent_and_bounded_for_known_and_random(
q0 = cirq.NamedQubit('q0')
q1 = cirq.NamedQubit('q1')

operations_with_partial = cirq.two_qubit_matrix_to_operations(q0, q1, effect, True)
operations_with_full = cirq.two_qubit_matrix_to_operations(q0, q1, effect, False)
operations_with_partial = cirq.two_qubit_matrix_to_cz_operations(q0, q1, effect, True)
operations_with_full = cirq.two_qubit_matrix_to_cz_operations(q0, q1, effect, False)

assert_ops_implement_unitary(q0, q1, operations_with_partial, effect)
assert_ops_implement_unitary(q0, q1, operations_with_full, effect)
Expand All @@ -231,34 +251,34 @@ def test_kak_decomposition_depth_full_cz():

# Random.
u = cirq.testing.random_unitary(4)
operations_with_full = cirq.two_qubit_matrix_to_operations(a, b, u, False)
operations_with_full = cirq.two_qubit_matrix_to_cz_operations(a, b, u, False)
c = cirq.Circuit(operations_with_full)
# 3 CZ, 3+1 PhasedX, 1 Z
assert len(c) <= 8

# Double-axis interaction.
u = cirq.unitary(cirq.Circuit(cirq.CNOT(a, b), cirq.CNOT(b, a)))
operations_with_part = cirq.two_qubit_matrix_to_operations(a, b, u, False)
operations_with_part = cirq.two_qubit_matrix_to_cz_operations(a, b, u, False)
c = cirq.Circuit(operations_with_part)
# 2 CZ, 2+1 PhasedX, 1 Z
assert len(c) <= 6

# Test unoptimized/un-cleaned length of Double-axis interaction.
u = cirq.unitary(cirq.Circuit(cirq.CNOT(a, b), cirq.CNOT(b, a)))
operations_with_part = cirq.two_qubit_matrix_to_operations(a, b, u, False, 1e-8, False)
operations_with_part = cirq.two_qubit_matrix_to_cz_operations(a, b, u, False, 1e-8, False)
c = cirq.Circuit(operations_with_part)
assert len(c) > 6 # Length should be 13 with extra Pauli gates

# Partial single-axis interaction.
u = cirq.unitary(cirq.CNOT ** 0.1)
operations_with_part = cirq.two_qubit_matrix_to_operations(a, b, u, False)
operations_with_part = cirq.two_qubit_matrix_to_cz_operations(a, b, u, False)
c = cirq.Circuit(operations_with_part)
# 2 CZ, 2+1 PhasedX, 1 Z
assert len(c) <= 6

# Full single-axis interaction.
u = cirq.unitary(cirq.ControlledGate(cirq.Y))
operations_with_part = cirq.two_qubit_matrix_to_operations(a, b, u, False)
operations_with_part = cirq.two_qubit_matrix_to_cz_operations(a, b, u, False)
c = cirq.Circuit(operations_with_part)
# 1 CZ, 1+1 PhasedX, 1 Z
assert len(c) <= 4
Expand All @@ -269,28 +289,28 @@ def test_kak_decomposition_depth_partial_cz():

# Random.
u = cirq.testing.random_unitary(4)
operations_with_full = cirq.two_qubit_matrix_to_operations(a, b, u, True)
operations_with_full = cirq.two_qubit_matrix_to_cz_operations(a, b, u, True)
c = cirq.Circuit(operations_with_full)
# 3 CP, 3+1 PhasedX, 1 Z
assert len(c) <= 8

# Double-axis interaction.
u = cirq.unitary(cirq.Circuit(cirq.CNOT(a, b), cirq.CNOT(b, a)))
operations_with_part = cirq.two_qubit_matrix_to_operations(a, b, u, True)
operations_with_part = cirq.two_qubit_matrix_to_cz_operations(a, b, u, True)
c = cirq.Circuit(operations_with_part)
# 2 CP, 2+1 PhasedX, 1 Z
assert len(c) <= 6

# Partial single-axis interaction.
u = cirq.unitary(cirq.CNOT ** 0.1)
operations_with_part = cirq.two_qubit_matrix_to_operations(a, b, u, True)
operations_with_part = cirq.two_qubit_matrix_to_cz_operations(a, b, u, True)
c = cirq.Circuit(operations_with_part)
# 1 CP, 1+1 PhasedX, 1 Z
assert len(c) <= 4

# Full single-axis interaction.
u = cirq.unitary(cirq.ControlledGate(cirq.Y))
operations_with_part = cirq.two_qubit_matrix_to_operations(a, b, u, True)
operations_with_part = cirq.two_qubit_matrix_to_cz_operations(a, b, u, True)
c = cirq.Circuit(operations_with_part)
# 1 CP, 1+1 PhasedX, 1 Z
assert len(c) <= 4
Expand All @@ -306,7 +326,7 @@ def test_kak_decomposition_depth_partial_cz():
)
def test_decompose_to_diagonal_and_circuit(v):
b, c = cirq.LineQubit.range(2)
diagonal, ops = two_qubit_matrix_to_diagonal_and_operations(b, c, v)
diagonal, ops = two_qubit_matrix_to_diagonal_and_cz_operations(b, c, v)
assert cirq.is_diagonal(diagonal)
combined_circuit = cirq.Circuit(cirq.MatrixGate(diagonal)(b, c), ops)
circuit_unitary = combined_circuit.unitary(qubits_that_should_be_present=[b, c])
Expand Down
2 changes: 1 addition & 1 deletion cirq/transformers/target_gatesets/cz_gateset.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ def __init__(self, *, atol: float = 1e-8, allow_partial_czs: bool = False) -> No
def _decompose_two_qubit_operation(self, op: 'cirq.Operation', _) -> 'cirq.OP_TREE':
if not protocols.has_unitary(op):
return NotImplemented
return two_qubit_to_cz.two_qubit_matrix_to_operations(
return two_qubit_to_cz.two_qubit_matrix_to_cz_operations(
op.qubits[0],
op.qubits[1],
protocols.unitary(op),
Expand Down

0 comments on commit c67ac9c

Please sign in to comment.