Ejemplo n.º 1
0
def find_local_equivalents(unitary1: np.ndarray, unitary2: np.ndarray):
    """
    Given two unitaries with the same interaction coefficients but different
    local unitary rotations determine the local unitaries that turns
    one type of gate into another.

    1) perform the kak decomp on each unitary and confirm interaction terms
       are equivalent
    2) identify the elements of SU(2) to transform unitary2 into unitary1

    Args:
        unitary1: unitary that we target
        unitary2: unitary that we transform the local gates to the target
    Returns:
        four 2x2 unitaries.  first two are pre-rotations last two are post
        rotations.
    """
    kak_u1 = cirq.kak_decomposition(unitary1)
    kak_u2 = cirq.kak_decomposition(unitary2)

    u_0 = kak_u1.single_qubit_operations_after[
        0] @ kak_u2.single_qubit_operations_after[0].conj().T
    u_1 = kak_u1.single_qubit_operations_after[
        1] @ kak_u2.single_qubit_operations_after[1].conj().T

    v_0 = (kak_u2.single_qubit_operations_before[0].conj().T
           @ kak_u1.single_qubit_operations_before[0])
    v_1 = (kak_u2.single_qubit_operations_before[1].conj().T
           @ kak_u1.single_qubit_operations_before[1])

    return v_0, v_1, u_0, u_1
Ejemplo n.º 2
0
def _find_local_equivalents(target_unitary: np.ndarray,
                            source_unitary: np.ndarray):
    """Determine the local 1q rotations that turn one equivalent 2q unitary into the other.

    Given two 2q unitaries with the same interaction coefficients but different local unitary
    rotations determine the local unitaries that turns one type of gate into another.

    1) Perform the KAK Decomposition on each unitary and confirm interaction terms are equivalent.
    2) Identify the elements of SU(2) to transform source_unitary into target_unitary

    Args:
        target_unitary: The unitary that we need to transform `source_unitary` to.
        source_unitary: The unitary that we need to transform by adding local gates, and make it
            equivalent to the target_unitary.

    Returns:
        Four 2x2 unitaries. The first two are pre-rotations and last two are post rotations.
    """
    kak_u1 = cirq.kak_decomposition(target_unitary)
    kak_u2 = cirq.kak_decomposition(source_unitary)

    u_0 = kak_u1.single_qubit_operations_after[
        0] @ kak_u2.single_qubit_operations_after[0].conj().T
    u_1 = kak_u1.single_qubit_operations_after[
        1] @ kak_u2.single_qubit_operations_after[1].conj().T

    v_0 = (kak_u2.single_qubit_operations_before[0].conj().T
           @ kak_u1.single_qubit_operations_before[0])
    v_1 = (kak_u2.single_qubit_operations_before[1].conj().T
           @ kak_u1.single_qubit_operations_before[1])

    return v_0, v_1, u_0, u_1
Ejemplo n.º 3
0
def _decompose_xx_yy(
    desired_interaction: Union[cirq.Operation, np.ndarray,
                               'cirq.SupportsUnitary'],
    *,
    available_gate: cirq.Gate,
    atol: float = 1e-8,
    qubits: Optional[Sequence[cirq.Qid]] = None,
) -> cirq.Circuit:
    if qubits is None:
        if isinstance(desired_interaction, cirq.Operation):
            qubits = desired_interaction.qubits
        else:
            qubits = cirq.LineQubit.range(2)
    kak = cirq.kak_decomposition(desired_interaction)

    x, y, z = kak.interaction_coefficients
    if abs(z) > atol:
        raise ValueError(
            f'zz term present in {desired_interaction!r} -> {kak}')

    if isinstance(available_gate, cirq.ISwapPowGate):
        available_gate = cirq.FSimGate(-np.pi / 2 * available_gate.exponent, 0)

    if min(x, y) > np.pi / 4 - atol and abs(
            available_gate.phi) < atol and abs(available_gate.theta -
                                               np.pi / 4) < atol:
        ops = [
            available_gate(*qubits),
            cirq.Y.on_each(*qubits),
            available_gate(*qubits),
        ]
    elif min(x, y) > np.pi / 4 - atol and abs(
            available_gate.phi) < atol and abs(available_gate.theta +
                                               np.pi / 4) < atol:
        ops = [
            available_gate(*qubits),
            available_gate(*qubits),
        ]
    else:
        ops = _decompose_xx_yy_into_two_fsims_ignoring_single_qubit_ops(
            qubits=qubits,
            fsim_gate=available_gate,
            canonical_x_kak_coefficient=x,
            canonical_y_kak_coefficient=y,
        )
    result = _fix_single_qubit_gates_around_kak_interaction(
        desired=kak,
        operations=ops,
        qubits=qubits,
    )
    output = cirq.Circuit(result)
    cirq.testing.assert_allclose_up_to_global_phase(
        output.unitary(qubit_order=qubits),
        cirq.unitary(desired_interaction),
        atol=1e-4)
    return output
Ejemplo n.º 4
0
def _outer_locals_for_unitary(
    target: np.ndarray, base: np.ndarray
) -> Tuple[_SingleQubitGatePair, _SingleQubitGatePair, np.ndarray]:
    """Local unitaries mapping between locally equivalent 2-local unitaries.

    Finds the left and right 1-local unitaries kL, kR such that

    U_target = kL @ U_base @ kR

    Args:
        target: The unitary to which we want to map.
        base: The base unitary which maps to target.

    Returns:
        kR: The right 1-local unitaries in the equation above, expressed as
            2-tuples of (2x2) single qubit unitaries.
        kL: The left 1-local unitaries in the equation above, expressed as
            2-tuples of (2x2) single qubit unitaries.
        actual: The outcome of kL @ base @ kR
    """
    target_decomp = cirq.kak_decomposition(target)
    base_decomp = cirq.kak_decomposition(base)

    # From the KAK decomposition, we have
    # kLt At kRt = kL kLb Ab KRb kR
    # If At=Ab, we can solve for kL and kR as
    # kLt = kL kLb --> kL = kLt kLb^\dagger
    # kRt = kRb kR --> kR = kRb\dagger kRt

    # 0 and 1 are qubit indices.
    kLt0, kLt1 = target_decomp.single_qubit_operations_after
    kLb0, kLb1 = base_decomp.single_qubit_operations_after
    kL = kLt0 @ kLb0.conj().T, kLt1 @ kLb1.conj().T

    kRt0, kRt1 = target_decomp.single_qubit_operations_before
    kRb0, kRb1 = base_decomp.single_qubit_operations_before
    kR = kRb0.conj().T @ kRt0, kRb1.conj().T @ kRt1

    actual = np.kron(*kL) @ base
    actual = actual @ np.kron(*kR)
    actual *= np.conj(target_decomp.global_phase)

    return kR, kL, actual
Ejemplo n.º 5
0
def test_scatter_plot_normalized_kak_interaction_coefficients():
    a, b = cirq.LineQubit.range(2)
    data = [
        cirq.kak_decomposition(cirq.unitary(cirq.CZ)),
        cirq.unitary(cirq.CZ),
        cirq.CZ,
        cirq.Circuit(cirq.H(a), cirq.CNOT(a, b)),
    ]
    ax = cirq.scatter_plot_normalized_kak_interaction_coefficients(data)
    assert ax is not None
    ax2 = cirq.scatter_plot_normalized_kak_interaction_coefficients(
        data, s=1, c='blue', ax=ax, include_frame=False, label=f'test')
    assert ax2 is ax
Ejemplo n.º 6
0
def test_decompose_xx_yy_into_two_fsims_ignoring_single_qubit_ops_fail():
    c = _decompose_xx_yy_into_two_fsims_ignoring_single_qubit_ops(
        qubits=cirq.LineQubit.range(2),
        fsim_gate=cirq.FSimGate(theta=np.pi / 2, phi=0),
        canonical_x_kak_coefficient=np.pi / 4,
        canonical_y_kak_coefficient=np.pi / 8)
    np.testing.assert_allclose(
        cirq.kak_decomposition(cirq.Circuit(c)).interaction_coefficients,
        [np.pi / 4, np.pi / 8, 0])

    with pytest.raises(ValueError, match='Failed to synthesize'):
        _ = _decompose_xx_yy_into_two_fsims_ignoring_single_qubit_ops(
            qubits=cirq.LineQubit.range(2),
            fsim_gate=cirq.FSimGate(theta=np.pi / 5, phi=0),
            canonical_x_kak_coefficient=np.pi / 4,
            canonical_y_kak_coefficient=np.pi / 8)
Ejemplo n.º 7
0
    def _decompose_two_qubit(self, operation: cirq.Operation) -> cirq.OP_TREE:
        """Decomposes a two qubit gate into XXPow, YYPow, and ZZPow plus single qubit gates."""
        mat = cirq.unitary(operation)
        kak = cirq.kak_decomposition(mat, check_preconditions=False)

        for qubit, mat in zip(operation.qubits, kak.single_qubit_operations_before):
            gates = cirq.single_qubit_matrix_to_gates(mat, self.atol)
            for gate in gates:
                yield gate(qubit)

        two_qubit_gates = [cirq.XX, cirq.YY, cirq.ZZ]
        for two_qubit_gate, coefficient in zip(two_qubit_gates, kak.interaction_coefficients):
            yield (two_qubit_gate ** (-coefficient * 2 / np.pi))(*operation.qubits)

        for qubit, mat in zip(operation.qubits, kak.single_qubit_operations_after):
            for gate in cirq.single_qubit_matrix_to_gates(mat, self.atol):
                yield gate(qubit)
Ejemplo n.º 8
0
def perturbations_unitary(u, amount=1e-10):
    """Returns several unitaries in the neighborhood of u to test for numerical
    corner cases near critical values."""
    kak = cirq.kak_decomposition(u)
    yield u
    for i in range(3):
        for neg in (-1, 1):
            perturb_xyz = list(kak.interaction_coefficients)
            perturb_xyz[i] += neg * amount
            yield cirq.unitary(
                cirq.KakDecomposition(
                    global_phase=kak.global_phase,
                    single_qubit_operations_before=kak.single_qubit_operations_before,
                    single_qubit_operations_after=kak.single_qubit_operations_after,
                    interaction_coefficients=tuple(perturb_xyz),
                )
            )
Ejemplo n.º 9
0
def test_kak_decomposition_invalid_object():
    with pytest.raises(TypeError, match='unitary effect'):
        _ = cirq.kak_decomposition('test')

    with pytest.raises(ValueError, match='4x4 unitary matrix'):
        _ = cirq.kak_decomposition(np.eye(3))

    with pytest.raises(ValueError, match='4x4 unitary matrix'):
        _ = cirq.kak_decomposition(np.eye(8))

    with pytest.raises(ValueError, match='4x4 unitary matrix'):
        _ = cirq.kak_decomposition(np.ones((4, 4)))

    with pytest.raises(ValueError, match='4x4 unitary matrix'):
        _ = cirq.kak_decomposition(np.zeros((4, 4)))

    nil = cirq.kak_decomposition(np.zeros((4, 4)), check_preconditions=False)
    np.testing.assert_allclose(cirq.unitary(nil), np.eye(4), atol=1e-8)
Ejemplo n.º 10
0
def test_kak_decompose(unitary: np.ndarray):
    kak = cirq.kak_decomposition(unitary)
    circuit = cirq.Circuit(kak._decompose_(cirq.LineQubit.range(2)))
    np.testing.assert_allclose(cirq.unitary(circuit), unitary, atol=1e-8)
    assert len(circuit) == 5
    assert len(list(circuit.all_operations())) == 8
Ejemplo n.º 11
0
def test_kak_decomposition_unitary_object():
    op = cirq.ISWAP(*cirq.LineQubit.range(2))**0.5
    kak = cirq.kak_decomposition(op)
    np.testing.assert_allclose(cirq.unitary(kak), cirq.unitary(op), atol=1e-8)
    assert cirq.kak_decomposition(kak) is kak
Ejemplo n.º 12
0
def test_kak_decomposition(target):
    kak = cirq.kak_decomposition(target)
    np.testing.assert_allclose(cirq.unitary(kak), target, atol=1e-8)
Ejemplo n.º 13
0
def test_kak_decomposition(m):
    g, (a1, a0), (x, y, z), (b1, b0) = cirq.kak_decomposition(m)
    m2 = recompose_kak(g, (a1, a0), (x, y, z), (b1, b0))
    assert np.allclose(m, m2)
Ejemplo n.º 14
0
def time_kak_decomposition(target):
    """Benchmark kak_decomposition
    kak_decomposition is benchmarked because it was historically slow.
    See https://github.com/quantumlib/Cirq/issues/3840 for status of other benchmarks.
    """
    cirq.kak_decomposition(target)