Example #1
0
def drop_negligible_operations(
    circuit: 'cirq.AbstractCircuit',
    *,
    context: Optional['cirq.TransformerContext'] = None,
    atol: float = 1e-8,
) -> 'cirq.Circuit':
    """Removes operations with tiny effects.

    An operation `op` is considered to have a tiny effect if
    `cirq.trace_distance_bound(op) <= atol`.

    Args:
          circuit: Input circuit to transform.
          context: `cirq.TransformerContext` storing common configurable options for transformers.
          atol: Absolute tolerance to determine if an operation `op` is negligible --
                i.e. if `cirq.trace_distance_bound(op) <= atol`.

    Returns:
          Copy of the transformed input circuit.
    """
    if context is None:
        context = transformer_api.TransformerContext()

    def map_func(op: 'cirq.Operation', _: int) -> 'cirq.OP_TREE':
        return (
            op if protocols.is_measurement(op) or protocols.trace_distance_bound(op) > atol else []
        )

    return transformer_primitives.map_operations(
        circuit, map_func, tags_to_ignore=context.tags_to_ignore, deep=context.deep
    ).unfreeze(copy=False)
def drop_terminal_measurements(
    circuit: 'cirq.AbstractCircuit',
    *,
    context: Optional['cirq.TransformerContext'] = transformer_api.
    TransformerContext(deep=True),
) -> 'cirq.Circuit':
    """Removes terminal measurements from a circuit.

    This transformer is helpful when trying to capture the final state vector
    of a circuit with many terminal measurements, as simulating the circuit
    with those measurements in place would otherwise collapse the final state.

    Args:
        circuit: The circuit to transform. It will not be modified.
        context: `cirq.TransformerContext` storing common configurable options
            for transformers. The default has `deep=True`, as "terminal
            measurements" is ill-defined without inspecting subcircuits;
            passing a context with `deep=False` will return an error.
    Returns:
        A copy of the circuit, with identity or X gates in place of terminal
        measurements.
    Raises:
        ValueError: if the circuit contains non-terminal measurements, or if
            the provided context has`deep=False`.
    """

    if context is None or not context.deep:
        raise ValueError(
            'Context has `deep=False`, but `deep=True` is required to drop terminal measurements.'
        )

    if not circuit.are_all_measurements_terminal():
        raise ValueError('Circuit contains a non-terminal measurement.')

    def flip_inversion(op: 'cirq.Operation', _) -> 'cirq.OP_TREE':
        if isinstance(op.gate, ops.MeasurementGate):
            return [
                ops.X(q) if b else ops.I(q)
                for q, b in zip(op.qubits, op.gate.full_invert_mask())
            ]
        return op

    ignored = () if context is None else context.tags_to_ignore
    return transformer_primitives.map_operations(
        circuit,
        flip_inversion,
        deep=context.deep if context else True,
        tags_to_ignore=ignored).unfreeze()
def dephase_measurements(
    circuit: 'cirq.AbstractCircuit',
    *,
    context: Optional['cirq.TransformerContext'] = transformer_api.
    TransformerContext(deep=True),
) -> 'cirq.Circuit':
    """Changes all measurements to a dephase operation.

    This transformer is useful when using a density matrix simulator, when
    wishing to calculate the final density matrix of a circuit and not simulate
    the measurements themselves.

    Args:
        circuit: The circuit to transform. It will not be modified.
        context: `cirq.TransformerContext` storing common configurable options
            for transformers. The default has `deep=True` to ensure
            measurements at all levels are dephased.
    Returns:
        A copy of the circuit, with dephase operations in place of all
        measurements.
    Raises:
        ValueError: If the circuit contains classical controls. In this case,
            it is required to change these to quantum controls via
            `cirq.defer_measurements` first. Since deferral adds ancilla qubits
            to the circuit, this is not done automatically, to prevent
            surprises.
    """
    def dephase(op: 'cirq.Operation', _) -> 'cirq.OP_TREE':
        gate = op.gate
        if isinstance(gate, ops.MeasurementGate):
            key = value.MeasurementKey.parse_serialized(gate.key)
            return ops.KrausChannel.from_channel(ops.phase_damp(1),
                                                 key=key).on_each(op.qubits)
        elif isinstance(op, ops.ClassicallyControlledOperation):
            raise ValueError(
                'Use cirq.defer_measurements first to remove classical controls.'
            )
        return op

    ignored = () if context is None else context.tags_to_ignore
    return transformer_primitives.map_operations(
        circuit,
        dephase,
        deep=context.deep if context else True,
        tags_to_ignore=ignored).unfreeze()
Example #4
0
def eject_z(
    circuit: 'cirq.AbstractCircuit',
    *,
    context: Optional['cirq.TransformerContext'] = None,
    atol: float = 0.0,
    eject_parameterized: bool = False,
) -> 'cirq.Circuit':
    """Pushes Z gates towards the end of the circuit.

    As the Z gates get pushed they may absorb other Z gates, get absorbed into
    measurements, cross CZ gates, cross PhasedXPowGate (aka W) gates (by phasing them), etc.

    Args:
          circuit: Input circuit to transform.
          context: `cirq.TransformerContext` storing common configurable options for transformers.
          atol: Maximum absolute error tolerance. The optimization is
               permitted to simply drop negligible combinations of Z gates,
               with a threshold determined by this tolerance.
          eject_parameterized: If True, the optimization will attempt to eject
              parameterized Z gates as well.  This may result in other gates
              parameterized by symbolic expressions.
    Returns:
        Copy of the transformed input circuit.
    """
    # Tracks qubit phases (in half turns; multiply by pi to get radians).
    qubit_phase: Dict[ops.Qid, float] = defaultdict(lambda: 0)
    tags_to_ignore = set(context.tags_to_ignore) if context else set()
    phased_xz_replacements: Dict[Tuple[int, ops.Operation],
                                 ops.PhasedXZGate] = {}
    last_phased_xz_op: Dict[ops.Qid, Optional[Tuple[
        int, ops.Operation]]] = defaultdict(lambda: None)

    def dump_tracked_phase(qubits: Iterable[ops.Qid]) -> 'cirq.OP_TREE':
        """Zeroes qubit_phase entries by emitting Z gates."""
        for q in qubits:
            p, key = qubit_phase[q], last_phased_xz_op[q]
            qubit_phase[q] = 0
            if not (key or single_qubit_decompositions.is_negligible_turn(
                    p, atol)):
                yield ops.Z(q)**(p * 2)
            elif key:
                phased_xz_replacements[key] = phased_xz_replacements[
                    key].with_z_exponent(p * 2)

    def map_func(op: 'cirq.Operation', moment_index: int) -> 'cirq.OP_TREE':
        last_phased_xz_op.update({q: None for q in op.qubits})

        if tags_to_ignore & set(op.tags):
            # Op marked with no-compile, dump phases and do not cross.
            return [dump_tracked_phase(op.qubits), op]

        gate = op.gate
        # Return if circuit operation.
        if gate is None:
            return [dump_tracked_phase(op.qubits), op]

        # Swap phases if `op` is a swap operation.
        if _is_swaplike(gate):
            a, b = op.qubits
            qubit_phase[a], qubit_phase[b] = qubit_phase[b], qubit_phase[a]
            return op

        # Z gate before measurement is a no-op. Drop tracked phase.
        if isinstance(gate, ops.MeasurementGate):
            for q in op.qubits:
                qubit_phase[q] = 0
            return op

        # Move Z gates into tracked qubit phases.
        if isinstance(gate, ops.ZPowGate) and (
                eject_parameterized or not protocols.is_parameterized(gate)):
            qubit_phase[op.qubits[0]] += gate.exponent / 2
            return []

        # Try to move the tracked phases over the operation via protocols.phase_by(op)
        phased_op = op
        for i, p in enumerate([qubit_phase[q] for q in op.qubits]):
            if not single_qubit_decompositions.is_negligible_turn(p, atol):
                phased_op = protocols.phase_by(phased_op, -p, i, default=None)
        if phased_op is None:
            return [dump_tracked_phase(op.qubits), op]

        gate = phased_op.gate
        if isinstance(gate, ops.PhasedXZGate) and (
                eject_parameterized
                or not protocols.is_parameterized(gate.z_exponent)):
            qubit = phased_op.qubits[0]
            qubit_phase[qubit] += gate.z_exponent / 2
            gate = gate.with_z_exponent(0)
            phased_op = gate.on(qubit)
            phased_xz_replacements[moment_index, phased_op] = gate
            last_phased_xz_op[qubit] = (moment_index, phased_op)
        return phased_op

    circuit = transformer_primitives.map_operations(
        circuit, map_func).unfreeze(copy=False)
    circuit.append(dump_tracked_phase(qubit_phase.keys()))
    circuit.batch_replace((m, op, g.on(*op.qubits))
                          for (m, op), g in phased_xz_replacements.items())
    return transformer_primitives.unroll_circuit_op(circuit)