Beispiel #1
0
def stratified_circuit(
        circuit: 'cirq.AbstractCircuit',
        *,
        context: Optional['cirq.TransformerContext'] = None,
        categories: Iterable[Category] = (),
) -> 'cirq.Circuit':
    """Repacks avoiding simultaneous operations with different classes.

    This transforms the given circuit to ensure that no operations of different categories are
    found in the same moment. Makes no optimality guarantees.
    Tagged Operations marked with any of `context.tags_to_ignore` will be treated as a separate
    category will be left in their original moments without stratification.

    Args:
        circuit: The circuit whose operations should be re-arranged. Will not be modified.
        context: `cirq.TransformerContext` storing common configurable options for transformers.
        categories: A list of classifiers picking out certain operations. There are several ways
            to specify a classifier. You can pass in a gate instance (e.g. `cirq.X`),
            a gate type (e.g. `cirq.XPowGate`), an operation instance
            (e.g. `cirq.X(cirq.LineQubit(0))`), an operation type (e.g.`cirq.CircuitOperation`),
            or an arbitrary operation predicate (e.g. `lambda op: len(op.qubits) == 2`).

    Returns:
        A copy of the original circuit, but with re-arranged operations.
    """

    # Normalize categories into classifier functions.
    classifiers = [
        _category_to_classifier(category) for category in categories
    ]
    # Make the classifiers exhaustive by adding an "everything else" bucket.
    and_the_rest = lambda op: all(not classifier(op)
                                  for classifier in classifiers)
    classifiers_and_the_rest = [*classifiers, and_the_rest]

    # Try the algorithm with each permutation of the classifiers.
    classifiers_permutations = list(
        itertools.permutations(classifiers_and_the_rest))
    reversed_circuit = circuit[::-1]
    solutions = []
    for c in classifiers_permutations:
        solutions.append(
            _stratify_circuit(
                circuit,
                classifiers=list(c),
                context=context or transformer_api.TransformerContext(),
            ))
        # Do the same thing, except this time in reverse. This helps for some
        # circuits because it inserts operations at the end instead of at the
        # beginning.
        solutions.append(
            _stratify_circuit(
                reversed_circuit,
                classifiers=list(c),
                context=context or transformer_api.TransformerContext(),
            )[::-1])

    # Return the shortest circuit.
    return min(solutions, key=lambda c: len(c))
Beispiel #2
0
def align_left(
        circuit: 'cirq.AbstractCircuit',
        *,
        context: Optional['cirq.TransformerContext'] = None) -> 'cirq.Circuit':
    """Align gates to the left of the circuit.

    Note that tagged operations with tag in `context.tags_to_ignore` will continue to stay in their
    original position and will not be aligned.

    Args:
          circuit: Input circuit to transform.
          context: `cirq.TransformerContext` storing common configurable options for transformers.

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

    ret = circuits.Circuit()
    for i, moment in enumerate(circuit):
        for op in moment:
            if isinstance(op, ops.TaggedOperation) and set(
                    op.tags).intersection(context.tags_to_ignore):
                ret.append([circuits.Moment()] * (i + 1 - len(ret)))
                ret[i] = ret[i].with_operation(op)
            else:
                ret.append(op)
    return ret
Beispiel #3
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)
Beispiel #4
0
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 drop_empty_moments(
        circuit: 'cirq.AbstractCircuit',
        *,
        context: Optional['cirq.TransformerContext'] = None) -> 'cirq.Circuit':
    """Removes empty moments from a circuit.

    Args:
          circuit: Input circuit to transform.
          context: `cirq.TransformerContext` storing common configurable options for transformers.

    Returns:
          Copy of the transformed input circuit.
    """
    if context is None:
        context = transformer_api.TransformerContext()
    return transformer_primitives.map_moments(
        circuit.unfreeze(False),
        lambda m, _: m if m else [],
        deep=context.deep,
        tags_to_ignore=context.tags_to_ignore,
    )
Beispiel #6
0
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()
Beispiel #7
0
def synchronize_terminal_measurements(
    circuit: 'cirq.AbstractCircuit',
    *,
    context: Optional['cirq.TransformerContext'] = None,
    after_other_operations: bool = True,
) -> 'cirq.Circuit':
    """Move measurements to the end of the circuit.

    Move all measurements in a circuit to the final moment, if it can accommodate them (without
    overlapping with other operations). If `after_other_operations` is true, then a new moment will
    be added to the end of the circuit containing all the measurements that should be brought
    forward.

    Args:
          circuit: Input circuit to transform.
          context: `cirq.TransformerContext` storing common configurable options for transformers.
          after_other_operations: Set by default. If the circuit's final moment contains
                non-measurement operations and this is set then a new empty moment is appended to
                the circuit before pushing measurements to the end.
    Returns:
          Copy of the transformed input circuit.
    """
    if context is None:
        context = transformer_api.TransformerContext()
    terminal_measurements = [
        (i, op)
        for i, op in find_terminal_measurements(circuit)
        if set(op.tags).isdisjoint(context.tags_to_ignore)
    ]
    ret = circuit.unfreeze(copy=True)
    if not terminal_measurements:
        return ret

    ret.batch_remove(terminal_measurements)
    if ret[-1] and after_other_operations:
        ret.append(circuits.Moment())
    ret[-1] = ret[-1].with_operations(op for _, op in terminal_measurements)
    return ret