Пример #1
0
def measure_density_matrix(
        density_matrix: np.ndarray,
        indices: List[int],
        qid_shape: Optional[Tuple[int, ...]] = None,
        out: np.ndarray = None) -> Tuple[List[int], np.ndarray]:
    """Performs a measurement of the density matrix in the computational basis.

    This does not modify `density_matrix` unless the optional `out` is
    `density_matrix`.

    Args:
        density_matrix: The density matrix to be measured. This matrix is
            assumed to be positive semidefinite and trace one. The matrix is
            assumed to be of shape (2 ** integer, 2 ** integer) or
            (2, 2, ..., 2).
        indices: Which qubits are measured. The matrix 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 density matrix.  Specify this argument
            when using qudits.
        out: An optional place to store the result. If `out` is the same as
            the `density_matrix` parameter, then `density_matrix` 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 cases `out` will be the same as the returned ndarray of the
            method. The shape and dtype of `out` will match that of
            `density_matrix` if `out` is None, otherwise it will match the
            shape and dtype of `out`.

    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 matrix. This matrix has the same
        shape and dtype as the input matrix.

    Raises:
        ValueError if the dimension of the matrix is not compatible with a
            matrix of n qubits.
        IndexError if the indices are out of range for the number of qubits
            corresponding to the density matrix.
    """
    if qid_shape is None:
        num_qubits = _validate_num_qubits(density_matrix)
        qid_shape = (2, ) * num_qubits
    else:
        _validate_density_matrix_qid_shape(density_matrix, qid_shape)
        num_qubits = len(qid_shape)
    meas_shape = _indices_shape(qid_shape, indices)

    if len(indices) == 0:
        if out is None:
            out = np.copy(density_matrix)
        elif out is not density_matrix:
            np.copyto(dst=out, src=density_matrix)
        return ([], out)
        # Final else: if out is matrix then matrix will be modified in place.

    # Cache initial shape.
    initial_shape = density_matrix.shape

    # Calculate the measurement probabilities and then make the measurement.
    probs = _probs(density_matrix, indices, qid_shape)
    result = np.random.choice(len(probs), p=probs)
    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=qid_shape)
    # Create a mask which is False for only the slice.
    mask = np.ones(qid_shape * 2, dtype=bool)
    # Remove ellipses from last element of
    mask[result_slice * 2] = False

    if out is None:
        out = np.copy(density_matrix)
    elif out is not density_matrix:
        np.copyto(dst=out, src=density_matrix)
    # Final else: if out is matrix then matrix will be modified in place.

    # Potentially reshape to tensor, and then set masked values to 0.
    out.shape = qid_shape * 2
    out[mask] = 0

    # Restore original shape (if necessary) and renormalize.
    out.shape = initial_shape
    out /= probs[result]

    return measurement_bits, out
Пример #2
0
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 a 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
Пример #3
0
def sample_density_matrix(
    density_matrix: np.ndarray,
    indices: Sequence[int],
    *,  # Force keyword arguments
    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 density_matrix.

    Args:
        density_matrix: The density matrix to be measured. This matrix is
            assumed to be positive semidefinite and trace one. The matrix is
            assumed to be of shape (2 ** integer, 2 ** integer) or
            (2, 2, ..., 2).
        indices: Which qubits are measured. The density matrix rows and columns
            are 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 density matrix.  Specify this argument
            when using qudits.
        repetitions: The number of times to sample the density matrix.
        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 a numpy ndarray.

    Raises:
        ValueError: ``repetitions`` is less than one or size of ``matrix`` is
            not a power of 2.
        IndexError: An index from ``indices`` is out of range, given the number
            of qubits corresponding to the density matrix.
    """
    if repetitions < 0:
        raise ValueError(
            f'Number of repetitions cannot be negative. Was {repetitions}')
    if qid_shape is None:
        num_qubits = _validate_num_qubits(density_matrix)
        qid_shape = (2, ) * num_qubits
    else:
        _validate_density_matrix_qid_shape(density_matrix, qid_shape)
        num_qubits = len(qid_shape)
    meas_shape = _indices_shape(qid_shape, indices)

    if repetitions == 0 or len(indices) == 0:
        return np.zeros(shape=(repetitions, len(indices)), dtype=np.int8)

    prng = value.parse_random_state(seed)

    # Calculate the measurement probabilities.
    probs = _probs(density_matrix, indices, qid_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.
    return np.array(
        [
            value.big_endian_int_to_digits(result[i], base=meas_shape)
            for i in range(len(result))
        ],
        dtype=np.int8,
    )
Пример #4
0
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 a 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,
    )
def slice_for_qubits_equal_to(
    target_qubit_axes: Sequence[int],
    little_endian_qureg_value: int = 0,
    *,  # Forces keyword args.
    big_endian_qureg_value: int = 0,
    num_qubits: Optional[int] = None,
    qid_shape: Optional[Tuple[int, ...]] = None,
) -> Tuple[Union[slice, int, 'ellipsis'], ...]:
    """Returns an index corresponding to a desired subset of an np.ndarray.

    It is assumed that the np.ndarray's shape is of the form (2, 2, 2, ..., 2).

    Example:

        ```python
        # A '4 qubit' tensor with values from 0 to 15.
        r = np.array(range(16)).reshape((2,) * 4)

        # We want to index into the subset where qubit #1 and qubit #3 are ON.
        s = cirq.slice_for_qubits_equal_to([1, 3], 0b11)
        print(s)
        # (slice(None, None, None), 1, slice(None, None, None), 1, Ellipsis)

        # Get that subset. It corresponds to numbers of the form 0b*1*1.
        # where here '*' indicates any possible value.
        print(r[s])
        # [[ 5  7]
        #  [13 15]]
        ```

    Args:
        target_qubit_axes: The qubits that are specified by the index bits. All
            other axes of the slice are unconstrained.
        little_endian_qureg_value: An integer whose bits specify what value is
            desired for of the target qubits. The integer is little endian
            w.r.t. the target qubit axes, meaning the low bit of the integer
            determines the desired value of the first targeted qubit, and so
            forth with the k'th targeted qubit's value set to
            bool(qureg_value & (1 << k)).
        big_endian_qureg_value: Same as `little_endian_qureg_value` but big
            endian w.r.t. to target qubit axes, meaning the low bit of the
            integer dertemines the desired value of the last target qubit, and
            so forth.  Specify exactly one of the `*_qureg_value` arguments.
        num_qubits: If specified the slices will extend all the way up to
            this number of qubits, otherwise if it is None, the final element
            return will be Ellipsis. Optional and defaults to using Ellipsis.
        qid_shape: The qid shape of the state vector being sliced.  Specify this
            instead of `num_qubits` when using qids with dimension != 2.  The
            qureg value is interpreted to store digits with corresponding bases
            packed into an int.

    Returns:
        An index object that will slice out a mutable view of the desired subset
        of a tensor.
    """
    qid_shape_specified = qid_shape is not None
    if qid_shape is not None or num_qubits is not None:
        if num_qubits is None:
            num_qubits = len(cast(Tuple[int, ...], qid_shape))
        elif qid_shape is None:
            qid_shape = (2, ) * num_qubits
        if num_qubits != len(cast(Tuple[int, ...], qid_shape)):
            raise ValueError('len(qid_shape) != num_qubits')
    if little_endian_qureg_value and big_endian_qureg_value:
        raise ValueError(
            'Specify exactly one of the arguments little_endian_qureg_value '
            'or big_endian_qureg_value.')
    out_size_specified = num_qubits is not None
    out_size = (cast(int, num_qubits) if out_size_specified else
                max(target_qubit_axes, default=-1) + 1)
    result = cast(List[Union[slice, int, 'ellipsis']],
                  [slice(None)] * out_size)
    if not out_size_specified:
        result.append(Ellipsis)
    if qid_shape is None:
        qid_shape = (2, ) * out_size
    target_shape = tuple(qid_shape[i] for i in target_qubit_axes)
    if big_endian_qureg_value:
        digits = value.big_endian_int_to_digits(big_endian_qureg_value,
                                                base=target_shape)
    else:
        if little_endian_qureg_value < 0 and not qid_shape_specified:
            # Allow negative binary numbers
            little_endian_qureg_value &= (1 << len(target_shape)) - 1
        digits = value.big_endian_int_to_digits(little_endian_qureg_value,
                                                base=target_shape[::-1])[::-1]
    for axis, digit in zip(target_qubit_axes, digits):
        result[axis] = digit
    return tuple(result)