Ejemplo n.º 1
0
def _fix_single_qubit_gates_around_kak_interaction(
    *,
    desired: 'cirq.KakDecomposition',
    operations: List['cirq.Operation'],
    qubits: Sequence['cirq.Qid'],
) -> Iterator['cirq.Operation']:
    """Adds single qubit operations to complete a desired interaction.

    Args:
        desired: The kak decomposition of the desired operation.
        qubits: The pair of qubits that is being operated on.
        operations: A list of operations that composes into the desired kak
            interaction coefficients, but may not have the desired before/after
            single qubit operations or the desired global phase.

    Returns:
        A list of operations whose kak decomposition approximately equals the
        desired kak decomposition.
    """
    actual = linalg.kak_decomposition(circuits.Circuit(operations))

    def dag(a: np.ndarray) -> np.ndarray:
        return np.transpose(np.conjugate(a))

    for k in range(2):
        g = ops.MatrixGate(
            dag(actual.single_qubit_operations_before[k])
            @ desired.single_qubit_operations_before[k])
        yield g(qubits[k])
    yield from operations
    for k in range(2):
        g = ops.MatrixGate(desired.single_qubit_operations_after[k] @ dag(
            actual.single_qubit_operations_after[k]))
        yield g(qubits[k])
    yield ops.GlobalPhaseOperation(desired.global_phase / actual.global_phase)
Ejemplo n.º 2
0
 def _decompose_(self, qubits):
     from cirq import ops
     a, b = qubits
     return [
         ops.GlobalPhaseOperation(self.global_phase),
         ops.MatrixGate(self.single_qubit_operations_before[0]).on(a),
         ops.MatrixGate(self.single_qubit_operations_before[1]).on(b),
         np.exp(1j * ops.X(a) * ops.X(b) * self.interaction_coefficients[0]),
         np.exp(1j * ops.Y(a) * ops.Y(b) * self.interaction_coefficients[1]),
         np.exp(1j * ops.Z(a) * ops.Z(b) * self.interaction_coefficients[2]),
         ops.MatrixGate(self.single_qubit_operations_after[0]).on(a),
         ops.MatrixGate(self.single_qubit_operations_after[1]).on(b),
     ]
Ejemplo n.º 3
0
def cphase_to_sqrt_iswap(a, b, turns):
    """Implement a C-Phase gate using two sqrt ISWAP gates and single-qubit
    operations. The circuit is equivalent to cirq.CZPowGate(exponent=turns).

    Output unitary:
    [1   0   0   0],
    [0   1   0   0],
    [0   0   1   0],
    [0   0   0   e^{i turns pi}].

    Args:
        a: the first qubit
        b: the second qubit
        turns: Exponent specifying the evolution time in number of rotations.
    """
    theta = (turns % 2) * np.pi
    if 0 <= theta <= np.pi:
        sign = 1.
        theta_prime = theta
    elif np.pi < theta < 2 * np.pi:
        sign = -1.
        theta_prime = 2 * np.pi - theta

    if np.isclose(theta, np.pi):
        # If we are close to pi, just set values manually to avoid possible
        # numerical errors with arcsin of greater than 1.0 (Ahem, Windows).
        phi = np.pi / 2
        xi = np.pi / 2
    else:
        phi = np.arcsin(np.sqrt(2) * np.sin(theta_prime / 4))
        xi = np.arctan(np.tan(phi) / np.sqrt(2))

    yield ops.rz(sign * 0.5 * theta_prime).on(a)
    yield ops.rz(sign * 0.5 * theta_prime).on(b)
    yield ops.rx(xi).on(a)
    yield ops.X(b)**(-sign * 0.5)
    yield SQRT_ISWAP_INV(a, b)
    yield ops.rx(-2 * phi).on(a)
    yield SQRT_ISWAP(a, b)

    yield ops.rx(xi).on(a)
    yield ops.X(b)**(sign * 0.5)
    # Corrects global phase
    yield ops.GlobalPhaseOperation(np.exp(sign * theta_prime * 0.25j))
def decompose_cphase_into_two_fsim(cphase_gate: 'cirq.CZPowGate',
                                   *,
                                   fsim_gate: 'cirq.FSimGate',
                                   qubits: Optional[
                                       Sequence['cirq.Qid']] = None,
                                   atol: float = 1e-8) -> 'cirq.OP_TREE':
    """Decomposes CZPowGate into two FSimGates.

    This function implements the decomposition described in section VII F I
    of https://arxiv.org/abs/1910.11333.

    The decomposition results in exactly two FSimGates and a few single-qubit
    rotations. It is feasible if and only if one of the following conditions
    is met:

        |sin(θ)| <= |sin(δ/4)| <= |sin(φ/2)|
        |sin(φ/2)| <= |sin(δ/4)| <= |sin(θ)|

    where:

         θ = fsim_gate.theta,
         φ = fsim_gate.phi,
         δ = -π * cphase_gate.exponent.

    Note that the gate parametrizations are non-injective. For the
    decomposition to be feasible it is sufficient that one of the
    parameter values which correspond to the provided gate satisfies
    the constraints. This function will find and use the appropriate
    value whenever it exists.

    The constraints above imply that certain FSimGates are not suitable
    for use in this decomposition regardless of the target CZPowGate. We
    reject such gates based on how close |sin(θ)| is to |sin(φ/2)|, see
    atol argument below.

    This implementation accounts for the global phase.

    Args:
        cphase_gate: The CZPowGate to synthesize.
        fsim_gate: The only two qubit gate that is permitted to appear in the
            output.
        qubits: The qubits to apply the resulting operations to. If not set,
            defaults `cirq.LineQubit.range(2)`.
        atol: Tolerance used to determine whether fsim_gate is valid. The gate
            is invalid if the squares of the sines of the theta angle and half
            the phi angle are too close.

    Returns:
        Operations equivalent to cphase_gate and consisting solely of two copies
        of fsim_gate and a few single-qubit rotations.

    Raises:
        ValueError under any of the following circumstances:
         * cphase_gate or fsim_gate is parametrized,
         * cphase_gate and fsim_gate do not satisfy the conditions above,
         * fsim_gate has invalid angles (see atol argument above),
         * incorrect number of qubits are provided.
    """
    if protocols.is_parameterized(cphase_gate):
        raise ValueError('Cannot decompose a parametrized gate.')
    if protocols.is_parameterized(fsim_gate):
        raise ValueError('Cannot decompose into a parametrized gate.')
    if qubits is None:
        qubits = devices.LineQubit.range(2)
    if len(qubits) != 2:
        raise ValueError(f'Expected a pair of qubits, but got {qubits!r}.')
    q0, q1 = qubits

    theta = fsim_gate.theta
    phi = fsim_gate.phi

    sin_half_phi = np.sin(phi / 2)
    cos_half_phi = np.cos(phi / 2)
    sin_theta = np.sin(theta)
    cos_theta = np.cos(theta)

    #
    # Step 1: find alpha
    #
    denominator = (sin_theta - sin_half_phi) * (sin_theta + sin_half_phi)
    if abs(denominator) < atol:
        raise ValueError(
            f'{fsim_gate} cannot be used to decompose CZPowGate because '
            'sin(theta)**2 is too close to sin(phi/2)**2 '
            f'(difference is {denominator}).')

    # Parametrization of CZPowGate by a real angle is a non-injective function
    # with the preimage of cphase_gate infinite. However, it is sufficient to
    # check just two of the angles against the constraints of the decomposition.
    canonical_delta = -np.pi * (cphase_gate.exponent % 2)
    for delta in (canonical_delta, canonical_delta + 2 * np.pi):
        sin_quarter_delta = np.sin(delta / 4)
        numerator = (sin_quarter_delta - sin_half_phi) * (sin_quarter_delta +
                                                          sin_half_phi)
        sin_alpha_squared = numerator / denominator
        if 0 <= sin_alpha_squared <= 1:
            break
    else:
        intervals = compute_cphase_exponents_for_fsim_decomposition(fsim_gate)
        raise ValueError(
            f'{cphase_gate} cannot be decomposed into two {fsim_gate}. Valid '
            f'intervals for canonical exponent of CZPowGate: {intervals}.')
    assert 0 <= sin_alpha_squared <= 1
    alpha = np.arcsin(np.sqrt(sin_alpha_squared))

    #
    # Step 2: find xi and eta
    #
    tan_alpha = np.tan(alpha)
    xi = np.arctan2(tan_alpha * cos_theta, cos_half_phi)
    eta = np.arctan2(tan_alpha * sin_theta, sin_half_phi)
    if delta < 0:
        eta += np.pi

    #
    # Step 3: synthesize output circuit
    #
    return (
        # Local X rotations to convert Γ1⊗I − iZ⊗Γ2 into exp(-i Z⊗Z δ/4)
        ops.rx(xi).on(q0),
        ops.rx(eta).on(q1),

        # Y(θ, φ) := exp(-i X⊗X θ/2) exp(-i Y⊗Y θ/2) exp(-i Z⊗Z φ/4)
        fsim_gate.on(q0, q1),
        ops.rz(phi / 2).on(q0),
        ops.rz(phi / 2).on(q1),
        ops.GlobalPhaseOperation(np.exp(1j * phi / 4)),

        # exp(i X1 α)
        ops.rx(-2 * alpha).on(q0),

        # Y(-θ, φ) := exp(i X⊗X θ/2) exp(i Y⊗Y θ/2) exp(-i Z⊗Z φ/4)
        ops.Z(q0),
        fsim_gate.on(q0, q1),
        ops.rz(phi / 2).on(q0),
        ops.rz(phi / 2).on(q1),
        ops.GlobalPhaseOperation(np.exp(1j * phi / 4)),
        ops.Z(q0),

        # Local X rotations to convert Γ1⊗I − iZ⊗Γ2 into exp(-i Z⊗Z δ/4)
        ops.rx(-eta).on(q1),
        ops.rx(xi).on(q0),

        # Local Z rotations to convert exp(-i Z⊗Z δ/4) into desired CPhase.
        ops.rz(-delta / 2).on(q0),
        ops.rz(-delta / 2).on(q1),
        ops.GlobalPhaseOperation(np.exp(-1j * delta / 4)),
    )
Ejemplo n.º 5
0
def generate_all_scalar_cell_makers() -> Iterator[CellMaker]:
    yield _scalar("NeGate", ops.GlobalPhaseOperation(-1))
    yield _scalar("i", ops.GlobalPhaseOperation(1j))
    yield _scalar("-i", ops.GlobalPhaseOperation(-1j))
    yield _scalar("√i", ops.GlobalPhaseOperation(1j**0.5))
    yield _scalar("√-i", ops.GlobalPhaseOperation((-1j)**0.5))