def apply_channel(self, action: Any, axes: Sequence[int], prng) -> Optional[int]: """Apply channel to state. Args: action: The value with a channel to apply. axes: The axes on which to apply the channel. prng: The pseudo random number generator to use. Returns: The kraus index if the operation succeeded, otherwise None. """ kraus_operators = protocols.kraus(action, default=None) if kraus_operators is None: return None def prepare_into_buffer(k: int): linalg.targeted_left_multiply( left_matrix=kraus_tensors[k], right_target=self._state_vector, target_axes=axes, out=self._buffer, ) shape = protocols.qid_shape(action) kraus_tensors = [ e.reshape(shape * 2).astype(self._state_vector.dtype) for e in kraus_operators ] p = prng.random() weight = None fallback_weight = 0 fallback_weight_index = 0 index = None for index in range(len(kraus_tensors)): prepare_into_buffer(index) weight = np.linalg.norm(self._buffer)**2 if weight > fallback_weight: fallback_weight_index = index fallback_weight = weight p -= weight if p < 0: break assert weight is not None, "No Kraus operators" if p >= 0 or weight == 0: # Floating point error resulted in a malformed sample. # Fall back to the most likely case. prepare_into_buffer(fallback_weight_index) weight = fallback_weight index = fallback_weight_index self._buffer /= np.sqrt(weight) self._swap_target_tensor_for(self._buffer) return index
def _kraus_(self): if self._is_parameterized_(): return NotImplemented channel = protocols.kraus(self.sub_gate, None) if channel is None: return NotImplemented do_nothing = np.eye(np.product(protocols.qid_shape(self.sub_gate)), dtype=np.float64) result = [e * np.sqrt(self.probability) for e in channel] result.append(np.sqrt(1 - float(self.probability)) * do_nothing) return result
def _strat_act_on_state_vector_from_channel( action: Any, args: 'cirq.ActOnStateVectorArgs', qubits: Sequence['cirq.Qid']) -> bool: kraus_operators = protocols.kraus(action, default=None) if kraus_operators is None: return NotImplemented def prepare_into_buffer(k: int): linalg.targeted_left_multiply( left_matrix=kraus_tensors[k], right_target=args.target_tensor, target_axes=args.get_axes(qubits), out=args.available_buffer, ) shape = protocols.qid_shape(action) kraus_tensors = [ e.reshape(shape * 2).astype(args.target_tensor.dtype) for e in kraus_operators ] p = args.prng.random() weight = None fallback_weight = 0 fallback_weight_index = 0 for index in range(len(kraus_tensors)): prepare_into_buffer(index) weight = np.linalg.norm(args.available_buffer)**2 if weight > fallback_weight: fallback_weight_index = index fallback_weight = weight p -= weight if p < 0: break assert weight is not None, "No Kraus operators" if p >= 0 or weight == 0: # Floating point error resulted in a malformed sample. # Fall back to the most likely case. prepare_into_buffer(fallback_weight_index) weight = fallback_weight index = fallback_weight_index args.available_buffer /= np.sqrt(weight) args.swap_target_tensor_for(args.available_buffer) if protocols.is_measurement(action): key = protocols.measurement_key_name(action) args._classical_data.record_channel_measurement(key, index) return True
def operation_to_channel_matrix( operation: 'protocols.SupportsChannel') -> np.ndarray: """Returns the matrix representation of a superoperator in standard basis. Let E: L(H1) -> L(H2) denote a linear map which takes linear operators on Hilbert space H1 to linear operators on Hilbert space H2 and let d1 = dim H1 and d2 = dim H2. Also, let Fij denote an operator whose matrix has one in ith row and jth column and zeros everywhere else. Note that d1-by-d1 operators Fij form a basis of L(H1). Similarly, d2-by-d2 operators Fij form a basis of L(H2). This function returns the matrix of E in these bases. Args: operation: Quantum channel. Returns: Matrix representation of operation. """ return kraus_to_channel_matrix(protocols.kraus(operation))
def operation_to_choi(operation: 'protocols.SupportsChannel') -> np.ndarray: r"""Returns the unique Choi matrix associated with a superoperator. Choi matrix J(E) of a linear map E: L(H1) -> L(H2) which takes linear operators on Hilbert space H1 to linear operators on Hilbert space H2 is defined as $$ J(E) = (E \otimes I)(|\phi\rangle\langle\phi|) $$ where $|\phi\rangle = \sum_i|i\rangle|i\rangle$ is the unnormalized maximally entangled state and I: L(H1) -> L(H1) is the identity map. Note that J(E) is a square matrix with d1*d2 rows and columns where d1 = dim H1 and d2 = dim H2. Args: operation: Quantum channel. Returns: Choi matrix corresponding to operation. """ return kraus_to_choi(protocols.kraus(operation))
def entanglement_fidelity(operation: 'cirq.SupportsKraus') -> float: r"""Returns entanglement fidelity of a given quantum channel. Entanglement fidelity $F_e$ of a quantum channel $E: L(H) \to L(H)$ is the overlap between the maximally entangled state $|\phi\rangle = \frac{1}{\sqrt{dim H}} \sum_i|i\rangle|i\rangle$ and the state obtained by sending one half of $|\phi\rangle$ through the channel $E$, i.e. $$ F_e = \langle\phi|(E \otimes I)(|\phi\rangle\langle\phi|)|\phi\rangle $$ where $I: L(H) \to L(H)$ is the identity map. Args: operation: Quantum channel whose entanglement fidelity is to be computed. Returns: Entanglement fidelity of the channel represented by operation. """ f = 0.0 for k in protocols.kraus(operation): f += np.abs(np.trace(k)) ** 2 n_qubits = protocols.num_qubits(operation) return float(f / 4**n_qubits)
def _kraus_(self) -> Union[Tuple[np.ndarray], NotImplementedType]: return protocols.kraus(self.sub_operation, NotImplemented)
def from_channel(channel: 'protocols.SupportsChannel', key: Union[str, value.MeasurementKey, None] = None): """Creates a copy of a channel with the given measurement key.""" return KrausChannel(kraus_ops=list(protocols.kraus(channel)), key=key)
def kraus_tensors(op: 'cirq.Operation') -> Sequence[np.ndarray]: return tuple( np.reshape(k, (2, 2) * len(op.qubits)) for k in protocols.kraus(op))
def from_channel(channel: 'cirq.Gate', key: Union[str, 'cirq.MeasurementKey', None] = None): """Creates a copy of a channel with the given measurement key.""" return KrausChannel(kraus_ops=list(protocols.kraus(channel)), key=key)