def has_mixture(val: Any, *, allow_decompose: bool = True) -> bool: """Returns whether the value has a mixture representation. Args: val: The value to check. allow_decompose: Used by internal methods to stop redundant decompositions from being performed (e.g. there's no need to decompose an object to check if it is unitary as part of determining if the object is a quantum channel, when the quantum channel check will already be doing a more general decomposition check). Defaults to True. When false, the decomposition strategy for determining the result is skipped. Returns: If `val` has a `_has_mixture_` method and its result is not NotImplemented, that result is returned. Otherwise, if the value has a `_mixture_` method return True if that has a non-default value. Returns False if neither function exists. """ mixture_getter = getattr(val, '_has_mixture_', None) result = NotImplemented if mixture_getter is None else mixture_getter() if result is not NotImplemented: return result if has_unitary(val, allow_decompose=False): return True if allow_decompose: operations, _, _ = _try_decompose_into_operations_and_qubits(val) if operations is not None: return all(has_mixture(val) for val in operations) # No _has_mixture_ or _has_unitary_ function, use _mixture_ instead. return mixture(val, None) is not None
def measurement_keys(val: Any, *, allow_decompose: bool = True) -> AbstractSet[str]: """Gets the measurement keys of measurements within the given value. Args: val: The value which has the measurement key. allow_decompose: Defaults to True. When true, composite operations that don't directly specify their measurement keys will be decomposed in order to find measurement keys within the decomposed operations. If not set, composite operations will appear to have no measurement keys. Used by internal methods to stop redundant decompositions from being performed. Returns: The measurement keys of the value. If the value has no measurement, the result is the empty tuple. """ getter = getattr(val, '_measurement_keys_', None) result = NotImplemented if getter is None else getter() if result is not NotImplemented and result is not None: return set(result) getter = getattr(val, '_measurement_key_', None) result = NotImplemented if getter is None else getter() if result is not NotImplemented and result is not None: return {result} if allow_decompose: operations, _, _ = _try_decompose_into_operations_and_qubits(val) if operations is not None: return {key for op in operations for key in measurement_keys(op)} return set()
def measurement_keys(val: Any, *, allow_decompose: bool = True) -> Tuple[str, ...]: """Gets the measurement keys of measurements within the given value. Args: val: The value which has the measurement key. allow_decompose: Defaults to True. When true, composite operations that don't directly specify their measurement keys will be decomposed in order to find measurement keys within the decomposed operations. If not set, composite operations will appear to have no measurement keys. Returns: The measurement keys of the value. If the value has no measurement, the result is the empty tuple. """ getter = getattr(val, '_measurement_keys_', None) result = NotImplemented if getter is None else getter() if result is not NotImplemented and result is not None: return tuple(result) getter = getattr(val, '_measurement_key_', None) result = NotImplemented if getter is None else getter() if result is not NotImplemented and result is not None: return result, if allow_decompose: operations, _, _ = _try_decompose_into_operations_and_qubits(val) if operations is not None: return tuple(key for op in operations for key in measurement_keys(op)) return ()
def _strat_act_on_state_vector_from_apply_decompose( val: Any, args: ActOnStateVectorArgs, ) -> bool: operations, qubits, _ = _try_decompose_into_operations_and_qubits(val) if operations is None: return NotImplemented return _act_all_on_state_vector(operations, qubits, args)
def strat_act_on_from_apply_decompose(val: Any, args: 'cirq.SimulationState', qubits: Sequence['cirq.Qid']) -> bool: operations, qubits1, _ = _try_decompose_into_operations_and_qubits(val) assert len(qubits1) == len(qubits) qubit_map = {q: qubits[i] for i, q in enumerate(qubits1)} if operations is None: return NotImplemented for operation in operations: operation = operation.with_qubits( *[qubit_map[q] for q in operation.qubits]) protocols.act_on(operation, args) return True
def has_channel(val: Any, *, allow_decompose: bool = True) -> bool: """Returns whether the value has a channel representation. Args: val: The value to check. allow_decompose: Used by internal methods to stop redundant decompositions from being performed (e.g. there's no need to decompose an object to check if it is unitary as part of determining if the object is a quantum channel, when the quantum channel check will already be doing a more general decomposition check). Defaults to True. When False, the decomposition strategy for determining the result is skipped. Returns: If `val` has a `_has_kraus_` method and its result is not NotImplemented, that result is returned. Otherwise, if `val` has a `_has_mixture_` method and its result is not NotImplemented, that result is returned. Otherwise if `val` has a `_has_unitary_` method and its results is not NotImplemented, that result is returned. Otherwise, if the value has a _kraus_ method return if that has a non-default value. Returns False if none of these functions exists. """ channel_getter = getattr(val, '_has_channel_', None) if channel_getter is not None: warnings.warn( '_has_channel_ is deprecated and will be removed in cirq 0.13, rename to _has_kraus_', DeprecationWarning, ) kraus_getter = getattr(val, '_has_kraus_', None) result = NotImplemented if kraus_getter is None else kraus_getter() if result is not NotImplemented: return result result = has_mixture(val, allow_decompose=False) if result is not NotImplemented and result: return result result = NotImplemented if channel_getter is None else channel_getter() if result is not NotImplemented: return result if allow_decompose: operations, _, _ = _try_decompose_into_operations_and_qubits(val) if operations is not None: return all(has_channel(val) for val in operations) # No has methods, use `_kraus_` or delegates instead. return channel(val, None) is not None
def strat_act_on_from_apply_decompose( val: Any, args: ActOnArgs, ) -> bool: operations, qubits, _ = _try_decompose_into_operations_and_qubits(val) if operations is None: return NotImplemented assert len(qubits) == len(args.axes) qubit_map = {q: args.axes[i] for i, q in enumerate(qubits)} old_axes = args.axes try: for operation in operations: args.axes = tuple(qubit_map[q] for q in operation.qubits) protocols.act_on(operation, args) finally: args.axes = old_axes return True
def _strat_unitary_from_decompose(val: Any) -> Optional[np.ndarray]: """Attempts to compute a value's unitary via its _decompose_ method.""" # Check if there's a decomposition. operations, qubits, val_qid_shape = _try_decompose_into_operations_and_qubits( val) if operations is None: return NotImplemented # Apply sub-operations' unitary effects to an identity matrix. state = qis.eye_tensor(val_qid_shape, dtype=np.complex128) buffer = np.empty_like(state) result = apply_unitaries( operations, qubits, ApplyUnitaryArgs(state, buffer, range(len(val_qid_shape))), None) # Package result. if result is None: return None state_len = np.prod(val_qid_shape, dtype=np.int64) return result.reshape((state_len, state_len))
def _is_any_measurement(vals: List[Any], allow_decompose: bool) -> bool: """Given a list of objects, returns True if any of them is a measurement. If `allow_decompose` is True, decomposes the objects and runs the measurement checks on the constituent decomposed operations. But a decompose operation is only called if all cheaper checks are done. A BFS for searching measurements, where "depth" is each level of decompose. """ vals_to_decompose = [] # type: List[Any] while vals: val = vals.pop(0) result = _is_measurement_from_magic_method(val) if result is not NotImplemented: if result is True: return True if result is False: # Do not try any other strategies if `val` was explicitly marked as # "not measurement". continue keys = _measurement_key_names_from_magic_methods(val) if keys is not NotImplemented and bool(keys) is True: return True if allow_decompose: vals_to_decompose.append(val) # If vals has finished iterating over, keep decomposing from vals_to_decompose until vals # is populated with something. while not vals: if not vals_to_decompose: # Nothing left to process, this is not a measurement. return False operations, _, _ = _try_decompose_into_operations_and_qubits( vals_to_decompose.pop(0)) if operations: # Reverse the decomposed operations because measurements are typically at later # moments. vals = operations[::-1] return False
def _strat_apply_unitary_from_decompose( val: Any, args: ApplyUnitaryArgs) -> Optional[np.ndarray]: operations, qubits, _ = _try_decompose_into_operations_and_qubits(val) if operations is None: return NotImplemented return apply_unitaries(operations, qubits, args, None)
def _strat_has_unitary_from_decompose(val: Any) -> Optional[bool]: """Attempts to infer a value's unitary-ness via its _decompose_ method.""" operations, _, _ = _try_decompose_into_operations_and_qubits(val) if operations is None: return None return all(has_unitary(op) for op in operations)