示例#1
0
def find_terminal_measurements(
    circuit: 'cirq.AbstractCircuit',
) -> List[Tuple[int, 'cirq.Operation']]:
    """Finds all terminal measurements in the given circuit.

    A measurement is terminal if there are no other operations acting on the measured qubits
    after the measurement operation occurs in the circuit.

    Args:
        circuit: The circuit to find terminal measurements in.

    Returns:
        List of terminal measurements, each specified as (moment_index, measurement_operation).
    """

    open_qubits: Set['cirq.Qid'] = set(circuit.all_qubits())
    seen_control_keys: Set['cirq.MeasurementKey'] = set()
    terminal_measurements: List[Tuple[int, 'cirq.Operation']] = []
    for i in range(len(circuit) - 1, -1, -1):
        moment = circuit[i]
        for q in open_qubits:
            op = moment.operation_at(q)
            if (
                op is not None
                and open_qubits.issuperset(op.qubits)
                and protocols.is_measurement(op)
                and not (seen_control_keys & protocols.measurement_key_objs(op))
            ):
                terminal_measurements.append((i, op))
        open_qubits -= moment.qubits
        seen_control_keys |= protocols.control_keys(moment)
        if not open_qubits:
            break
    return terminal_measurements
示例#2
0
def _assert_meets_standards_helper(
    val: Any,
    *,
    ignoring_global_phase: bool,
    setup_code: str,
    global_vals: Optional[Dict[str, Any]],
    local_vals: Optional[Dict[str, Any]],
) -> None:
    __tracebackhide__ = True  # pylint: disable=unused-variable

    assert_consistent_resolve_parameters(val)
    assert_specifies_has_unitary_if_unitary(val)
    assert_has_consistent_qid_shape(val)
    assert_has_consistent_apply_unitary(val)
    assert_all_implemented_act_on_effects_match_unitary(val)
    assert_qasm_is_consistent_with_unitary(val)
    assert_has_consistent_trace_distance_bound(val)
    assert_decompose_is_consistent_with_unitary(val, ignoring_global_phase=ignoring_global_phase)
    assert_phase_by_is_consistent_with_unitary(val)
    assert_pauli_expansion_is_consistent_with_unitary(val)
    assert_equivalent_repr(
        val, setup_code=setup_code, global_vals=global_vals, local_vals=local_vals
    )
    assert protocols.measurement_key_objs(val) == protocols.measurement_key_names(val)
    if isinstance(val, ops.EigenGate):
        assert_eigen_shifts_is_consistent_with_eigen_components(val)
    if isinstance(val, ops.Gate):
        assert_controlled_and_controlled_by_identical(val)
示例#3
0
 def _measurement_key_objs_(self) -> AbstractSet['cirq.MeasurementKey']:
     if self._cached_measurement_key_objs is None:
         circuit_keys = protocols.measurement_key_objs(self.circuit)
         if circuit_keys and self.use_repetition_ids:
             self._ensure_deterministic_loop_count()
             if self.repetition_ids is not None:
                 circuit_keys = {
                     key.with_key_path_prefix(repetition_id)
                     for repetition_id in self.repetition_ids
                     for key in circuit_keys
                 }
         circuit_keys = {
             key.with_key_path_prefix(*self.parent_path)
             for key in circuit_keys
         }
         object.__setattr__(
             self,
             '_cached_measurement_key_objs',
             {
                 protocols.with_measurement_key_mapping(
                     key, self.measurement_key_map)
                 for key in circuit_keys
             },
         )
     return self._cached_measurement_key_objs  # type: ignore
示例#4
0
    def with_operation(self, operation: 'cirq.Operation') -> 'cirq.Moment':
        """Returns an equal moment, but with the given op added.

        Args:
            operation: The operation to append.

        Returns:
            The new moment.

        Raises:
            ValueError: If the operation given overlaps a current operation in the moment.
        """
        if any(q in self._qubits for q in operation.qubits):
            raise ValueError(f'Overlapping operations: {operation}')

        # Use private variables to facilitate a quick copy.
        m = Moment()
        m._operations = self._operations + (operation, )
        m._sorted_operations = None
        m._qubits = self._qubits.union(operation.qubits)
        m._qubit_to_op = {
            **self._qubit_to_op,
            **{q: operation
               for q in operation.qubits}
        }

        m._measurement_key_objs = self._measurement_key_objs_().union(
            protocols.measurement_key_objs(operation))
        m._control_keys = self._control_keys_().union(
            protocols.control_keys(operation))

        return m
示例#5
0
def _op_info_with_fallback(
    op: 'cirq.Operation', args: 'cirq.CircuitDiagramInfoArgs'
) -> 'cirq.CircuitDiagramInfo':
    info = protocols.circuit_diagram_info(op, args, None)
    rows: List[LabelEntity] = list(op.qubits)
    if args.label_map is not None:
        rows += protocols.measurement_key_objs(op) & args.label_map.keys()
        rows += protocols.control_keys(op) & args.label_map.keys()
    if info is not None:
        if max(1, len(rows)) != len(info.wire_symbols):
            raise ValueError(f'Wanted diagram info from {op!r} for {rows!r}) but got {info!r}')
        return info

    # Use the untagged operation's __str__.
    name = str(op.untagged)

    # Representation usually looks like 'gate(qubit1, qubit2, etc)'.
    # Try to cut off the qubit part, since that would be redundant.
    redundant_tail = f"({', '.join(str(e) for e in op.qubits)})"
    if name.endswith(redundant_tail):
        name = name[: -len(redundant_tail)]

    # Add tags onto the representation, if they exist
    if op.tags:
        name += f'{list(op.tags)}'

    # Include ordering in the qubit labels.
    symbols = (name,) + tuple(f'#{i + 1}' for i in range(1, len(op.qubits)))

    return protocols.CircuitDiagramInfo(wire_symbols=symbols)
示例#6
0
 def _measurement_key_objs_(self) -> AbstractSet['cirq.MeasurementKey']:
     if self._measurement_key_objs is None:
         self._measurement_key_objs = {
             key
             for op in self.operations
             for key in protocols.measurement_key_objs(op)
         }
     return self._measurement_key_objs
示例#7
0
文件: raw_types.py 项目: daxfohl/Cirq
    def _commutes_(
        self,
        other: Any,
        *,
        atol: Union[int,
                    float] = 1e-8) -> Union[bool, NotImplementedType, None]:
        """Determine if this Operation commutes with the object"""
        if not isinstance(other, Operation):
            return NotImplemented

        self_keys = protocols.measurement_key_objs(self)
        other_keys = protocols.measurement_key_objs(other)
        if (not self_keys.isdisjoint(other_keys)
                or not protocols.control_keys(self).isdisjoint(other_keys)
                or not protocols.control_keys(other).isdisjoint(self_keys)):
            return False

        if hasattr(other, 'qubits') and set(self.qubits).isdisjoint(
                other.qubits):
            return True

        from cirq import circuits

        # Remove the classical controls to validate the quantum commutativity. This can be done
        # because during execution, the two operations will either both be run, in which case they
        # behave like the suboperations, so if the suboperations commute then these commute. Or
        # one of them is cold in which case it behaves like the identity, which always commutes.
        self_raw = self.without_classical_controls()
        other_raw = other.without_classical_controls()
        circuit12 = circuits.Circuit(self_raw, other_raw)
        circuit21 = circuits.Circuit(other_raw, self_raw)

        # Don't create gigantic matrices.
        shape = protocols.qid_shape_protocol.qid_shape(circuit12)
        if np.prod(shape, dtype=np.int64) > 2**10:
            return NotImplemented  # coverage: ignore

        m12 = protocols.unitary_protocol.unitary(circuit12, default=None)
        m21 = protocols.unitary_protocol.unitary(circuit21, default=None)
        if m12 is None or m21 is None:
            return NotImplemented

        return np.allclose(m12, m21, atol=atol)
示例#8
0
 def add_op_to_moment(self, moment_index: int,
                      op: 'cirq.Operation') -> None:
     self.ops_by_index[moment_index][op] = 0
     for q in op.qubits:
         if moment_index > self.qubit_indexes[q][-1]:
             self.qubit_indexes[q].append(moment_index)
         else:
             bisect.insort(self.qubit_indexes[q], moment_index)
     for mkey in protocols.measurement_key_objs(op):
         bisect.insort(self.mkey_indexes[mkey], moment_index)
     for ckey in protocols.control_keys(op):
         bisect.insort(self.ckey_indexes[ckey], moment_index)
示例#9
0
 def remove_op_from_moment(self, moment_index: int,
                           op: 'cirq.Operation') -> None:
     self.ops_by_index[moment_index].pop(op)
     for q in op.qubits:
         if self.qubit_indexes[q][-1] == moment_index:
             self.qubit_indexes[q].pop()
         else:
             self.qubit_indexes[q].remove(moment_index)
     for mkey in protocols.measurement_key_objs(op):
         self.mkey_indexes[mkey].remove(moment_index)
     for ckey in protocols.control_keys(op):
         self.ckey_indexes[ckey].remove(moment_index)
示例#10
0
    def __post_init__(self):
        if not isinstance(self.circuit, circuits.FrozenCircuit):
            raise TypeError(
                f'Expected circuit of type FrozenCircuit, got: {type(self.circuit)!r}'
            )

        # Ensure that the circuit is invertible if the repetitions are negative.
        if self.repetitions < 0:
            try:
                protocols.inverse(self.circuit.unfreeze())
            except TypeError:
                raise ValueError(
                    'repetitions are negative but the circuit is not invertible'
                )

        # Initialize repetition_ids to default, if unspecified. Else, validate their length.
        loop_size = abs(self.repetitions)
        if not self.repetition_ids:
            object.__setattr__(self, 'repetition_ids',
                               self._default_repetition_ids())
        elif len(self.repetition_ids) != loop_size:
            raise ValueError(
                f'Expected repetition_ids to be a list of length {loop_size}, '
                f'got: {self.repetition_ids}')

        # Disallow mapping to keys containing the `MEASUREMENT_KEY_SEPARATOR`
        for mapped_key in self.measurement_key_map.values():
            if value.MEASUREMENT_KEY_SEPARATOR in mapped_key:
                raise ValueError(
                    f'Mapping to invalid key: {mapped_key}. "{value.MEASUREMENT_KEY_SEPARATOR}" '
                    'is not allowed for measurement keys in a CircuitOperation'
                )

        # Disallow qid mapping dimension conflicts.
        for q, q_new in self.qubit_map.items():
            if q_new.dimension != q.dimension:
                raise ValueError(
                    f'Qid dimension conflict.\nFrom qid: {q}\nTo qid: {q_new}')

        if self.repeat_until:
            if self.use_repetition_ids or self.repetitions != 1:
                raise ValueError('Cannot use repetitions with repeat_until')
            if protocols.measurement_key_objs(
                    self._mapped_single_loop()).isdisjoint(
                        self.repeat_until.keys):
                raise ValueError(
                    'Infinite loop: condition is not modified in subcircuit.')

        # Ensure that param_resolver is converted to an actual ParamResolver.
        object.__setattr__(self, 'param_resolver',
                           study.ParamResolver(self.param_resolver))
示例#11
0
 def _measurement_key_objs(self) -> FrozenSet['cirq.MeasurementKey']:
     circuit_keys = protocols.measurement_key_objs(self.circuit)
     if circuit_keys and self.use_repetition_ids:
         self._ensure_deterministic_loop_count()
         if self.repetition_ids is not None:
             circuit_keys = frozenset(
                 key.with_key_path_prefix(repetition_id)
                 for repetition_id in self.repetition_ids
                 for key in circuit_keys)
     circuit_keys = frozenset(
         key.with_key_path_prefix(*self.parent_path)
         for key in circuit_keys)
     return frozenset(
         protocols.with_measurement_key_mapping(
             key, self.measurement_key_map) for key in circuit_keys)
示例#12
0
    def get_mergeable_ops(
            self, op: 'cirq.Operation',
            op_qs: Set['cirq.Qid']) -> Tuple[int, List['cirq.Operation']]:
        # Find the index of previous moment which can be merged with `op`.
        idx = max([self.qubit_indexes[q][-1] for q in op_qs], default=-1)
        idx = max([idx] + [
            self.mkey_indexes[ckey][-1] for ckey in protocols.control_keys(op)
        ])
        idx = max([idx] + [
            self.ckey_indexes[mkey][-1]
            for mkey in protocols.measurement_key_objs(op)
        ])
        # Return the set of overlapping ops in moment with index `idx`.
        if idx == -1:
            return idx, []

        return idx, [
            left_op for left_op in self.ops_by_index[idx]
            if not op_qs.isdisjoint(left_op.qubits)
        ]
示例#13
0
    def __init__(
        self,
        sub_operation: 'cirq.Operation',
        conditions: Sequence[Union[str, 'cirq.MeasurementKey',
                                   'cirq.Condition', sympy.Basic]],
    ):
        """Initializes a `ClassicallyControlledOperation`.

        Multiple consecutive `ClassicallyControlledOperation` layers are
        squashed when possible, so one should not depend on a specific number
        of layers.

        Args:
            sub_operation: The operation to gate with a classical control
                condition.
            conditions: A sequence of measurement keys, or strings that can be
                parsed into measurement keys.

        Raises:
            ValueError: If an unsupported gate is being classically
                controlled.
        """
        if protocols.measurement_key_objs(sub_operation):
            raise ValueError(
                f'Cannot conditionally run operations with measurements: {sub_operation}'
            )
        conditions = tuple(conditions)
        if isinstance(sub_operation, ClassicallyControlledOperation):
            conditions += sub_operation._conditions
            sub_operation = sub_operation._sub_operation
        conds: List['cirq.Condition'] = []
        for c in conditions:
            if isinstance(c, str):
                c = value.MeasurementKey.parse_serialized(c)
            if isinstance(c, value.MeasurementKey):
                c = value.KeyCondition(c)
            if isinstance(c, sympy.Basic):
                c = value.SympyCondition(c)
            conds.append(cast('cirq.Condition', c))
        self._conditions: Tuple['cirq.Condition', ...] = tuple(conds)
        self._sub_operation: 'cirq.Operation' = sub_operation
示例#14
0
    def with_operations(self, *contents: 'cirq.OP_TREE') -> 'cirq.Moment':
        """Returns a new moment with the given contents added.

        Args:
            *contents: New operations to add to this moment.

        Returns:
            The new moment.

        Raises:
            ValueError: If the contents given overlaps a current operation in the moment.
        """
        flattened_contents = tuple(op_tree.flatten_to_ops(contents))

        m = Moment()
        # Use private variables to facilitate a quick copy.
        m._qubit_to_op = self._qubit_to_op.copy()
        qubits = set(self._qubits)
        for op in flattened_contents:
            if any(q in qubits for q in op.qubits):
                raise ValueError(f'Overlapping operations: {op}')
            qubits.update(op.qubits)
            for q in op.qubits:
                m._qubit_to_op[q] = op
        m._qubits = frozenset(qubits)

        m._operations = self._operations + flattened_contents
        m._sorted_operations = None
        m._measurement_key_objs = self._measurement_key_objs_().union(
            set(
                itertools.chain(*(protocols.measurement_key_objs(op)
                                  for op in flattened_contents))))
        m._control_keys = self._control_keys_().union(
            set(
                itertools.chain(*(protocols.control_keys(op)
                                  for op in flattened_contents))))

        return m
示例#15
0
 def _measurement_key_objs_(self) -> AbstractSet[value.MeasurementKey]:
     if self._cached_measurement_key_objs is None:
         circuit_keys = protocols.measurement_key_objs(self.circuit)
         if self.repetition_ids is not None:
             circuit_keys = {
                 key.with_key_path_prefix(repetition_id)
                 for repetition_id in self.repetition_ids
                 for key in circuit_keys
             }
         circuit_keys = {
             protocols.with_key_path_prefix(key, self.parent_path)
             for key in circuit_keys
         }
         object.__setattr__(
             self,
             '_cached_measurement_key_objs',
             {
                 protocols.with_measurement_key_mapping(
                     key, self.measurement_key_map)
                 for key in circuit_keys
             },
         )
     return self._cached_measurement_key_objs  # type: ignore
示例#16
0
 def _measurement_key_objs_(self) -> AbstractSet['cirq.MeasurementKey']:
     return protocols.measurement_key_objs(self.sub_operation)
示例#17
0
    def __init__(
        self,
        circuit: 'cirq.FrozenCircuit',
        repetitions: int = 1,
        qubit_map: Optional[Dict['cirq.Qid', 'cirq.Qid']] = None,
        measurement_key_map: Optional[Dict[str, str]] = None,
        param_resolver: Optional[study.ParamResolverOrSimilarType] = None,
        repetition_ids: Optional[Sequence[str]] = None,
        parent_path: Tuple[str, ...] = (),
        extern_keys: FrozenSet['cirq.MeasurementKey'] = frozenset(),
        use_repetition_ids: bool = True,
        repeat_until: Optional['cirq.Condition'] = None,
    ):
        """Initializes a CircuitOperation.

        Args:
            circuit: The FrozenCircuit wrapped by this operation.
            repetitions: How many times the circuit should be repeated. This can be
                integer, or a sympy expression. If sympy, the expression must
                resolve to an integer, or float within 0.001 of integer, at
                runtime.
            qubit_map: Remappings for qubits in the circuit.
            measurement_key_map: Remappings for measurement keys in the circuit.
                The keys and values should be unindexed (i.e. without repetition_ids).
                The values cannot contain the `MEASUREMENT_KEY_SEPARATOR`.
            param_resolver: Resolved values for parameters in the circuit.
            repetition_ids: List of identifiers for each repetition of the
                CircuitOperation. If populated, the length should be equal to the
                repetitions. If not populated and abs(`repetitions`) > 1, it is
                initialized to strings for numbers in `range(repetitions)`.
            parent_path: A tuple of identifiers for any parent CircuitOperations
                containing this one.
            extern_keys: The set of measurement keys defined at extern scope. The
                values here are used by decomposition and simulation routines to
                cache which external measurement keys exist as possible binding
                targets for unbound `ClassicallyControlledOperation` keys. This
                field is not intended to be set or changed manually, and should be
                empty in circuits that aren't in the middle of decomposition.
            use_repetition_ids: When True, any measurement key in the subcircuit
                will have its path prepended with the repetition id for each
                repetition. When False, this will not happen and the measurement
                key will be repeated.
            repeat_until: A condition that will be tested after each iteration of
                the subcircuit. The subcircuit will repeat until condition returns
                True, but will always run at least once, and the measurement key
                need not be defined prior to the subcircuit (but must be defined in
                a measurement within the subcircuit). This field is incompatible
                with repetitions or repetition_ids.

        Raises:
            TypeError: if repetitions is not an integer or sympy expression, or if
                the provided circuit is not a FrozenCircuit.
            ValueError: if any of the following conditions is met.
                - Negative repetitions on non-invertible circuit
                - Number of repetition IDs does not match repetitions
                - Repetition IDs used with parameterized repetitions
                - Conflicting qubit dimensions in qubit_map
                - Measurement key map has invalid key names
                - repeat_until used with other repetition controls
                - Key(s) in repeat_until are not modified by circuit
        """
        # This fields is exclusively for use in decomposition. It should not be
        # referenced outside this class.
        self._extern_keys = extern_keys

        # All other fields are pseudo-private: read access is allowed via the
        # @property methods, but mutation is prohibited.
        self._param_resolver = study.ParamResolver(param_resolver)
        self._parent_path = parent_path

        self._circuit = circuit
        if not isinstance(self._circuit, circuits.FrozenCircuit):
            raise TypeError(
                f'Expected circuit of type FrozenCircuit, got: {type(self._circuit)!r}'
            )

        # Ensure that the circuit is invertible if the repetitions are negative.
        self._repetitions = repetitions
        self._repetition_ids = None if repetition_ids is None else list(
            repetition_ids)
        self._use_repetition_ids = use_repetition_ids
        if isinstance(self._repetitions, float):
            if math.isclose(self._repetitions, round(self._repetitions)):
                self._repetitions = round(self._repetitions)
        if isinstance(self._repetitions, INT_CLASSES):
            if self._repetitions < 0:
                try:
                    protocols.inverse(self._circuit.unfreeze())
                except TypeError:
                    raise ValueError(
                        'repetitions are negative but the circuit is not invertible'
                    )

            # Initialize repetition_ids to default, if unspecified. Else, validate their length.
            loop_size = abs(self._repetitions)
            if not self._repetition_ids:
                self._repetition_ids = self._default_repetition_ids()
            elif len(self._repetition_ids) != loop_size:
                raise ValueError(
                    f'Expected repetition_ids to be a list of length {loop_size}, '
                    f'got: {self._repetition_ids}')
        elif isinstance(self._repetitions, sympy.Expr):
            if self._repetition_ids is not None:
                raise ValueError(
                    'Cannot use repetition ids with parameterized repetitions')
        else:
            raise TypeError(f'Only integer or sympy repetitions are allowed.\n'
                            f'User provided: {self._repetitions}')

        # Disallow qid mapping dimension conflicts.
        self._qubit_map = dict(qubit_map or {})
        for q, q_new in self._qubit_map.items():
            if q_new.dimension != q.dimension:
                raise ValueError(
                    f'Qid dimension conflict.\nFrom qid: {q}\nTo qid: {q_new}')

        self._measurement_key_map = dict(measurement_key_map or {})
        # Disallow mapping to keys containing the `MEASUREMENT_KEY_SEPARATOR`
        for mapped_key in self._measurement_key_map.values():
            if value.MEASUREMENT_KEY_SEPARATOR in mapped_key:
                raise ValueError(
                    f'Mapping to invalid key: {mapped_key}. "{value.MEASUREMENT_KEY_SEPARATOR}" '
                    'is not allowed for measurement keys in a CircuitOperation'
                )

        self._repeat_until = repeat_until
        if self._repeat_until:
            if self._use_repetition_ids or self._repetitions != 1:
                raise ValueError('Cannot use repetitions with repeat_until')
            if protocols.measurement_key_objs(
                    self._mapped_single_loop()).isdisjoint(
                        self._repeat_until.keys):
                raise ValueError(
                    'Infinite loop: condition is not modified in subcircuit.')
示例#18
0
 def _measurement_key_objs_(self) -> FrozenSet['cirq.MeasurementKey']:
     if self._measurement_key_objs is None:
         self._measurement_key_objs = frozenset(
             key for op in self.operations
             for key in protocols.measurement_key_objs(op))
     return self._measurement_key_objs