Пример #1
0
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 parameterizations 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)),
    )
Пример #2
0
 def _is_parameterized_(self) -> bool:
     return protocols.is_parameterized(self.sub_operation)
Пример #3
0
 def _is_parameterized_(self) -> bool:
     return any(
         protocols.is_parameterized(angle)
         for angle in self._diag_angles_radians)
Пример #4
0
 def _is_parameterized_(self):
     return protocols.is_parameterized(self.sub_gate)
Пример #5
0
 def _is_parameterized_(self) -> bool:
     """See `cirq.SupportsParameterization`."""
     return protocols.is_parameterized(self._exponent) or protocols.is_parameterized(
         self._phase_exponent
     )
Пример #6
0
 def _is_parameterized_(self) -> bool:
     return protocols.is_parameterized(self._gate)
Пример #7
0
 def _is_parameterized_(self) -> bool:
     return protocols.is_parameterized(self.sub_operation) or any(
         protocols.is_parameterized(tag) for tag in self.tags)
Пример #8
0
def assert_all_implemented_act_on_effects_match_unitary(
        val: Any,
        assert_tableau_implemented: bool = False,
        assert_ch_form_implemented: bool = False) -> None:
    """Uses val's effect on final_state_vector to check act_on(val)'s behavior.

    Checks that act_on with CliffordTableau or StabilizerStateCHForm behaves
    consistently with act_on through final state vector. Does not work with
    Operations or Gates expecting non-qubit Qids. If either of the
    assert_*_implmented args is true, fails if the corresponding method is not
    implemented for the test circuit.

    Args:
        val: A gate or operation that may be an input to protocols.act_on.
        assert_tableau_implemented: asserts that protocols.act_on() works with
          val and ActOnCliffordTableauArgs inputs.
        assert_ch_form_implemented: asserts that protocols.act_on() works with
          val and ActOnStabilizerStateChFormArgs inputs.
    """

    # pylint: disable=unused-variable
    __tracebackhide__ = True
    # pylint: enable=unused-variable

    num_qubits_val = protocols.num_qubits(val)

    if (protocols.is_parameterized(val) or not protocols.has_unitary(val)
            or protocols.qid_shape(val) != (2, ) * num_qubits_val):
        if assert_tableau_implemented or assert_ch_form_implemented:
            assert False, ("Could not assert if any act_on methods were "
                           "implemented. Operating on qudits or with a "
                           "non-unitary or parameterized operation is "
                           "unsupported.\n\nval: {!r}".format(val))
        return None

    qubits = LineQubit.range(protocols.num_qubits(val) * 2)
    qubit_map = {qubit: i for i, qubit in enumerate(qubits)}

    circuit = Circuit()
    for i in range(num_qubits_val):
        circuit.append([
            common_gates.H(qubits[i]),
            common_gates.CNOT(qubits[i], qubits[-i - 1])
        ])
    if hasattr(val, "on"):
        circuit.append(val.on(*qubits[:num_qubits_val]))
    else:
        circuit.append(val.with_qubits(*qubits[:num_qubits_val]))

    state_vector = np.reshape(final_state_vector(circuit, qubit_order=qubits),
                              protocols.qid_shape(qubits))

    tableau = _final_clifford_tableau(circuit, qubit_map)
    if tableau is None:
        assert (
            not assert_tableau_implemented
        ), "Failed to generate final tableau for the test circuit.\n\nval: {!r}".format(
            val)
    else:
        assert all(
            state_vector_has_stabilizer(state_vector, stab)
            for stab in tableau.stabilizers()), (
                "act_on clifford tableau is not consistent with "
                "final_state_vector simulation.\n\nval: {!r}".format(val))

    stabilizer_ch_form = _final_stabilizer_state_ch_form(circuit, qubit_map)
    if stabilizer_ch_form is None:
        assert not assert_ch_form_implemented, ("Failed to generate final "
                                                "stabilizer state CH form "
                                                "for the test circuit."
                                                "\n\nval: {!r}".format(val))
    else:
        np.testing.assert_allclose(
            np.reshape(stabilizer_ch_form.state_vector(),
                       protocols.qid_shape(qubits)),
            state_vector,
            atol=1e-07,
        )
Пример #9
0
 def _is_parameterized_(self) -> bool:
     return protocols.is_parameterized(
         self.probability) or protocols.is_parameterized(self.sub_gate)
Пример #10
0
 def _is_parameterized_(self) -> bool:
     return protocols.is_parameterized(self._exponent)
Пример #11
0
 def _is_parameterized_(self):
     return protocols.is_parameterized(self.duration)
Пример #12
0
def rx(rads: value.TParamVal) -> XPowGate:
    """Returns a gate with the matrix e^{-i X rads / 2}."""
    pi = sympy.pi if protocols.is_parameterized(rads) else np.pi
    return XPowGate(exponent=rads / pi, global_shift=-0.5)
Пример #13
0
def riswap(rads: value.TParamVal) -> ISwapPowGate:
    """Returns gate with matrix exp(+i angle_rads (X⊗X + Y⊗Y) / 2)."""
    pi = sympy.pi if protocols.is_parameterized(rads) else np.pi
    return ISwapPowGate()**(2 * rads / pi)
 def _is_parameterized_(self):
     return protocols.is_parameterized(self.coefficient)
Пример #15
0
 def _is_parameterized_(self) -> bool:
     return protocols.is_parameterized(self.exponent_neg) or protocols.is_parameterized(
         self.exponent_pos
     )