def measure_state_vector( state_vector: np.ndarray, indices: Sequence[int], *, # Force keyword args qid_shape: Optional[Tuple[int, ...]] = None, out: np.ndarray = None, seed: 'cirq.RANDOM_STATE_OR_SEED_LIKE' = None, ) -> Tuple[List[int], np.ndarray]: """Performs a measurement of the state in the computational basis. This does not modify `state` unless the optional `out` is `state`. Args: state_vector: The state to be measured. This state vector is assumed to be normalized. The state vector must be of size 2 ** integer. The state vector can be of shape (2 ** integer) or (2, 2, ..., 2). indices: Which qubits are measured. The `state_vector` is assumed to be supplied in big endian order. That is the xth index of v, when expressed as a bitstring, has the largest values in the 0th index. qid_shape: The qid shape of the `state_vector`. Specify this argument when using qudits. out: An optional place to store the result. If `out` is the same as the `state_vector` parameter, then `state_vector` will be modified inline. If `out` is not None, then the result is put into `out`. If `out` is None a new value will be allocated. In all of these case out will be the same as the returned ndarray of the method. The shape and dtype of `out` will match that of `state_vector` if `out` is None, otherwise it will match the shape and dtype of `out`. seed: A seed for the pseudorandom number generator. Returns: A tuple of a list and an numpy array. The list is an array of booleans corresponding to the measurement values (ordered by the indices). The numpy array is the post measurement state vector. This state vector has the same shape and dtype as the input `state_vector`. Raises: ValueError if the size of state is not a power of 2. IndexError if the indices are out of range for the number of qubits corresponding to the state. """ shape = qis.validate_qid_shape(state_vector, qid_shape) num_qubits = len(shape) qis.validate_indices(num_qubits, indices) if len(indices) == 0: if out is None: out = np.copy(state_vector) elif out is not state_vector: np.copyto(dst=out, src=state_vector) # Final else: if out is state then state will be modified in place. return ([], out) prng = value.parse_random_state(seed) # Cache initial shape. initial_shape = state_vector.shape # Calculate the measurement probabilities and then make the measurement. probs = _probs(state_vector, indices, shape) result = prng.choice(len(probs), p=probs) ###measurement_bits = [(1 & (result >> i)) for i in range(len(indices))] # Convert to individual qudit measurements. meas_shape = tuple(shape[i] for i in indices) measurement_bits = value.big_endian_int_to_digits(result, base=meas_shape) # Calculate the slice for the measurement result. result_slice = linalg.slice_for_qubits_equal_to( indices, big_endian_qureg_value=result, qid_shape=shape) # Create a mask which is False for only the slice. mask = np.ones(shape, dtype=bool) mask[result_slice] = False if out is None: out = np.copy(state_vector) elif out is not state_vector: np.copyto(dst=out, src=state_vector) # Final else: if out is state then state will be modified in place. # Potentially reshape to tensor, and then set masked values to 0. out.shape = shape out[mask] = 0 # Restore original shape (if necessary) and renormalize. out.shape = initial_shape out /= np.sqrt(probs[result]) return measurement_bits, out
def sample_state_vector( state_vector: np.ndarray, indices: List[int], *, # Force keyword args qid_shape: Optional[Tuple[int, ...]] = None, repetitions: int = 1, seed: 'cirq.RANDOM_STATE_OR_SEED_LIKE' = None, ) -> np.ndarray: """Samples repeatedly from measurements in the computational basis. Note that this does not modify the passed in state. Args: state_vector: The multi-qubit state vector to be sampled. This is an array of 2 to the power of the number of qubit complex numbers, and so state must be of size ``2**integer``. The `state_vector` can be a vector of size ``2**integer`` or a tensor of shape ``(2, 2, ..., 2)``. indices: Which qubits are measured. The `state_vector` is assumed to be supplied in big endian order. That is the xth index of v, when expressed as a bitstring, has its largest values in the 0th index. qid_shape: The qid shape of the `state_vector`. Specify this argument when using qudits. repetitions: The number of times to sample. seed: A seed for the pseudorandom number generator. Returns: Measurement results with True corresponding to the ``|1⟩`` state. The outer list is for repetitions, and the inner corresponds to measurements ordered by the supplied qubits. These lists are wrapped as an numpy ndarray. Raises: ValueError: ``repetitions`` is less than one or size of `state_vector` is not a power of 2. IndexError: An index from ``indices`` is out of range, given the number of qubits corresponding to the state. """ if repetitions < 0: raise ValueError( f'Number of repetitions cannot be negative. Was {repetitions}') shape = qis.validate_qid_shape(state_vector, qid_shape) num_qubits = len(shape) qis.validate_indices(num_qubits, indices) if repetitions == 0 or len(indices) == 0: return np.zeros(shape=(repetitions, len(indices)), dtype=np.uint8) prng = value.parse_random_state(seed) # Calculate the measurement probabilities. probs = _probs(state_vector, indices, shape) # We now have the probability vector, correctly ordered, so sample over # it. Note that we us ints here, since numpy's choice does not allow for # choosing from a list of tuples or list of lists. result = prng.choice(len(probs), size=repetitions, p=probs) # Convert to individual qudit measurements. meas_shape = tuple(shape[i] for i in indices) return np.array( [ value.big_endian_int_to_digits(result[i], base=meas_shape) for i in range(len(result)) ], dtype=np.uint8, )