Ejemplo n.º 1
0
def test_pop_measurements_and_add_measurements():
    """Tests popping measurements from a circuit.."""
    # Test circuit:
    # 0: ───H───T───@───M───
    #               │   │
    # 1: ───H───M───┼───┼───
    #               │   │
    # 2: ───H───────X───M───
    qreg = LineQubit.range(3)
    circ = Circuit(
        [ops.H.on_each(qreg)],
        [ops.T.on(qreg[0])],
        [ops.measure(qreg[1])],
        [ops.CNOT.on(qreg[0], qreg[2])],
        [ops.measure(qreg[0], qreg[2])],
    )
    copy = deepcopy(circ)
    measurements = _pop_measurements(copy)
    correct = Circuit(
        [ops.H.on_each(qreg)],
        [ops.T.on(qreg[0])],
        [ops.CNOT.on(qreg[0], qreg[2])],
    )
    assert _equal(copy, correct)
    _append_measurements(copy, measurements)
    assert _equal(copy, circ)
Ejemplo n.º 2
0
def _apply_fold_mask(
    circuit: Circuit,
    num_folds_mask: List[int],
    squash_moments: Optional[bool] = True,
) -> Circuit:
    r"""Applies local unitary folding to the gates of the input circuit
    according to the input num_folds_mask.

    More precicely, G_j^\dag G_j is applied after the j_th gate G_j of
    the input circuit an integer number of times given by num_folds_mask[j].

    The gate ordering is equal to the one used in the `all_operations()`
    method of the :class:`cirq.Circuit` class: gates from earlier moments
    come first and gates within the same moment follow the order in which
    they were given to the moment's constructor.

    Args:
        circuit: The quantum circuit to fold.
        num_folds_mask: The list of integers indicating how many times
            the corresponding gates of 'circuit' should be folded.
        squash_moments: If True or None, all gates (including folded gates) are
            placed as early as possible in the circuit. If False, new moments
            are created for folded gates. This option only applies to QPROGRAM
            types which have a "moment" or "time" structure. Default is True.

    Returns: The folded quantum circuit.
    """
    _check_foldable(circuit)
    circ_copy = deepcopy(circuit)
    measurements = _pop_measurements(circ_copy)

    num_gates = len(list(circ_copy.all_operations()))
    if num_gates != len(num_folds_mask):
        raise ValueError(
            "The circuit and the folding mask have incompatible sizes."
            f" The number of gates is {num_gates}"
            f" but len(num_folds_mask) is {len(num_folds_mask)}.")

    folded_circuit = circ_copy[:0]
    if squash_moments:
        for op, num_folds in zip(circ_copy.all_operations(), num_folds_mask):
            folded_circuit.append([op] + num_folds * [inverse(op), op])
    else:
        mask_index = 0
        for moment in circ_copy:
            folded_moment = Circuit(moment)
            for op in moment:
                num_folds = num_folds_mask[mask_index]
                folded_moment.append(num_folds * [inverse(op), op])
                mask_index += 1
            # New folded gates are only squashed with respect to folded_moment
            # while folded_circuit is not squashed.
            folded_circuit.append(folded_moment)

    _append_measurements(folded_circuit, measurements)
    return folded_circuit
Ejemplo n.º 3
0
def fold_global(circuit: Circuit, scale_factor: float,
                **kwargs: Any) -> Circuit:
    """Returns a new circuit obtained by folding the global unitary of the
    input circuit.

    The returned folded circuit has a number of gates approximately equal to
    scale_factor * len(circuit).

    Args:
        circuit: Circuit to fold.
        scale_factor: Factor to scale the circuit by.

    Keyword Args:
        squash_moments (bool): If True, all gates (including folded gates) are
            placed as early as possible in the circuit. If False, new moments
            are created for folded gates. This option only applies to QPROGRAM
            types which have a "moment" or "time" structure. Default is True.
        return_mitiq (bool): If True, returns a Mitiq circuit instead of
            the input circuit type (if different). Default is False.

    Returns:
        folded: the folded quantum circuit as a QPROGRAM.
    """
    _check_foldable(circuit)

    if not (scale_factor >= 1):
        raise ValueError("The scale factor must be a real number >= 1.")

    folded = deepcopy(circuit)
    measurements = _pop_measurements(folded)
    base_circuit = deepcopy(folded)

    # Determine the number of global folds and the final fractional scale
    num_global_folds, fraction_scale = divmod(scale_factor - 1, 2)
    # Do the global folds
    for _ in range(int(num_global_folds)):
        folded += Circuit(inverse(base_circuit), base_circuit)

    # Fold remaining gates until the scale is reached
    operations = list(base_circuit.all_operations())
    num_to_fold = int(round(fraction_scale * len(operations) / 2))

    if num_to_fold > 0:
        folded += Circuit([inverse(operations[-num_to_fold:])],
                          [operations[-num_to_fold:]])

    _append_measurements(folded, measurements)
    if not (kwargs.get("squash_moments") is False):
        folded = _squash_moments(folded)
    return folded
Ejemplo n.º 4
0
def fold_all(
        circuit: Circuit,
        scale_factor: float,
        exclude: FrozenSet[Any] = frozenset(),
) -> Circuit:
    """Returns a circuit with all gates folded locally.

    Args:
        circuit: Circuit to fold.
        scale_factor: Approximate factor by which noise is scaled in the
            circuit. Each gate is folded round((scale_factor - 1.0) / 2.0)
            times. For example::

                scale_factor | num_folds
                ------------------------
                1.0          | 0
                3.0          | 1
                5.0          | 2

        exclude: Do not fold these gates. Supported gate keys are listed in
            the following table.::

                Gate key    | Gate
                -------------------------
                "H"         | Hadamard
                "X"         | Pauli X
                "Y"         | Pauli Y
                "Z"         | Pauli Z
                "I"         | Identity
                "CNOT"      | CNOT
                "CZ"        | CZ gate
                "TOFFOLI"   | Toffoli gate
                "single"    | All single qubit gates
                "double"    | All two-qubit gates
                "triple"    | All three-qubit gates
    """
    if not 1.0 <= scale_factor:
        raise ValueError(
            f"Requires scale_factor >= 1 but scale_factor = {scale_factor}.")
    _check_foldable(circuit)

    folded = deepcopy(circuit)
    measurements = _pop_measurements(folded)

    folded = _fold_all(folded, round((scale_factor - 1.0) / 2.0), exclude)

    _append_measurements(folded, measurements)
    return folded
Ejemplo n.º 5
0
def _check_foldable(circuit: Circuit) -> None:
    """Raises an error if the input circuit cannot be folded.

    Args:
        circuit: Checks whether this circuit is able to be folded.

    Raises:
        UnfoldableCircuitError:
            * If the circuit has intermediate measurements.
            * If the circuit has non-unitary channels which are not terminal
              measurements.
    """
    if not circuit.are_all_measurements_terminal():
        raise UnfoldableCircuitError(
            "Circuit contains intermediate measurements and cannot be folded.")

    if not has_unitary(circuit):
        circ_measurements = _pop_measurements(circuit)
        if inverse(circuit, default=None) is None:
            raise UnfoldableCircuitError(
                "Circuit contains non-invertible channels which are not"
                "terminal measurements and cannot be folded.")
        else:
            _append_measurements(circuit, circ_measurements)