Ejemplo n.º 1
0
    def get_amplitudes(
        self,
        circuit: Circuit,
        states: List[str],
        valid_check: bool = True,
        **kwargs: KwargTypes,
    ) -> Dict[str, complex]:
        """
        Compute the complex coefficients of the final state.

        Supported `kwargs` are as for `BraketBackend.process_circuits`.

        :param states: classical states of interest, as binary strings of '0' and '1'

        :returns: final complex amplitudes if initial state is all-zeros
        """
        if not self.supports_amplitude:
            raise RuntimeError("Backend does not support amplitude")
        if valid_check:
            self._check_all_circuits([circuit], nomeasure_warn=False)
        bkcirc = self._to_bkcirc(circuit)
        restype = ResultType.Amplitude(states)
        bkcirc.add_result_type(restype)
        task = self._run(bkcirc, n_shots=0, **kwargs)
        res = task.result()
        amplitudes = res.get_value_by_result_type(restype)
        cdict = {}
        for k, v in amplitudes.items():
            # The amazon/sv1 simulator gives us 2-element lists [re, im].
            # The local simulator gives us numpy.complex128.
            cdict[k] = complex(*v) if type(v) is list else complex(v)
        return cdict
Ejemplo n.º 2
0
    def state_vector() -> ResultType:
        """Registers this function into the circuit class.

        Returns:
            ResultType: state vector as a requested result type

        Examples:
            >>> circ = Circuit().state_vector()
        """
        return ResultType.StateVector()
Ejemplo n.º 3
0
def _get_result(completed_task: Union[AwsQuantumTask, LocalQuantumTask],
                want_state: bool) -> Dict[str, BackendResult]:
    result = completed_task.result()
    kwargs = {}
    if want_state:
        kwargs["state"] = result.get_value_by_result_type(
            ResultType.StateVector())
    else:
        kwargs["shots"] = OutcomeArray.from_readouts(result.measurements)
    return {"result": BackendResult(**kwargs)}
Ejemplo n.º 4
0
    def amplitude(state: List[str]) -> ResultType:
        """Registers this function into the circuit class.

        Args:
            state (List[str]): list of quantum states as strings with "0" and "1"

        Returns:
            ResultType: state vector as a requested result type

        Examples:
            >>> circ = Circuit().amplitude(state=["01", "10"])
        """
        return ResultType.Amplitude(state=state)
Ejemplo n.º 5
0
 def process_circuits(
     self,
     circuits: Iterable[Circuit],
     n_shots: Optional[int] = None,
     valid_check: bool = True,
     **kwargs: KwargTypes,
 ) -> List[ResultHandle]:
     """
     Supported `kwargs`: none
     """
     if not self.supports_shots and not self.supports_state:
         raise RuntimeError("Backend does not support shots or state")
     if n_shots is None:
         n_shots = 0
     want_state = n_shots == 0
     if (not want_state) and (n_shots < self._sample_min_shots
                              or n_shots > self._sample_max_shots):
         raise ValueError(
             "For sampling, n_shots must be between "
             f"{self._sample_min_shots} and {self._sample_max_shots}. "
             "For statevector simulation, omit this parameter.")
     if valid_check:
         self._check_all_circuits(circuits, nomeasure_warn=False)
     handles = []
     for circ in circuits:
         bkcirc = self._to_bkcirc(circ)
         if want_state:
             bkcirc.add_result_type(ResultType.StateVector())
         if not bkcirc.instructions and len(circ.bits) == 0:
             task = None
         else:
             task = self._run(bkcirc, n_shots=n_shots)
         if self._device_type == _DeviceType.LOCAL:
             # Results are available now. Put them in the cache.
             if task is not None:
                 assert task.state() == "COMPLETED"
                 results = _get_result(task, want_state)
             else:
                 results = {
                     "result": self.empty_result(circ, n_shots=n_shots)
                 }
         else:
             # Task is asynchronous. Must wait for results.
             results = {}
         if task is not None:
             handle = ResultHandle(task.id, want_state)
         else:
             handle = ResultHandle(str(uuid4()), False)
         self._cache[handle] = results
         handles.append(handle)
     return handles
Ejemplo n.º 6
0
    def density_matrix(target: QubitSetInput = None) -> ResultType:
        """Registers this function into the circuit class.
        Args:
            target (int, Qubit, or iterable of int / Qubit, optional): The target qubits
                of the reduced density matrix. Default is `None`, and the
                full density matrix is returned.

        Returns:
            ResultType: density matrix as a requested result type

        Examples:
            >>> circ = Circuit().density_matrix(target=[0, 1])
        """
        return ResultType.DensityMatrix(target=target)
Ejemplo n.º 7
0
    def probability(target: QubitSetInput = None) -> ResultType:
        """Registers this function into the circuit class.

        Args:
            target (int, Qubit, or iterable of int / Qubit, optional): The target qubits that the
                result type is requested for. Default is `None`, which means all qubits for the
                circuit.

        Returns:
            ResultType: probability as a requested result type

        Examples:
            >>> circ = Circuit().probability(target=[0, 1])
        """
        return ResultType.Probability(target=target)
Ejemplo n.º 8
0
    def variance(observable: Observable, target: QubitSetInput = None) -> ResultType:
        """Registers this function into the circuit class.

        Args:
            observable (Observable): the observable for the result type
            target (int, Qubit, or iterable of int / Qubit, optional): Target qubits that the
                result type is requested for. Default is `None`, which means the observable must
                only operate on 1 qubit and it will be applied to all qubits in parallel

        Returns:
            ResultType: variance as a requested result type

        Examples:
            >>> circ = Circuit().variance(observable=Observable.Z(), target=0)
        """
        return ResultType.Variance(observable=observable, target=target)
Ejemplo n.º 9
0
    def sample(observable: Observable, target: QubitSetInput = None) -> ResultType:
        """Registers this function into the circuit class.

        Args:
            observable (Observable): the observable for the result type
            target (QubitSetInput): Target qubits that the
                result type is requested for. Default is `None`, which means the observable must
                operate only on 1 qubit and it is applied to all qubits in parallel.

        Returns:
            ResultType: sample as a requested result type

        Examples:
            >>> circ = Circuit().sample(observable=Observable.Z(), target=0)
        """
        return ResultType.Sample(observable=observable, target=target)
Ejemplo n.º 10
0
 def _get_variance(
     self,
     bkcirc: braket.circuits.Circuit,
     observable: Observable,
     target: QubitSet,
     n_shots: int,
     **kwargs: KwargTypes,
 ) -> np.float64:
     if not self.supports_variance:
         raise RuntimeError("Backend does not support variance")
     if n_shots < self._variance_min_shots or n_shots > self._variance_max_shots:
         raise ValueError(
             f"n_shots must be between {self._variance_min_shots} and "
             f"{self._variance_max_shots}")
     restype = ResultType.Variance(observable, target=target)
     bkcirc.add_result_type(restype)
     task = self._run(bkcirc, n_shots=n_shots, **kwargs)
     res = task.result()
     return res.get_value_by_result_type(restype)  # type: ignore
Ejemplo n.º 11
0
    def get_probabilities(
        self,
        circuit: Circuit,
        qubits: Union[Iterable[int], None] = None,
        n_shots: int = 0,
        valid_check: bool = True,
        **kwargs: KwargTypes,
    ) -> np.ndarray:
        """
        Compute the (exact or empirical) probability distribution of outcomes.

        If `n_shots > 0` the probabilities are calculated empirically by measurements.
        If `n_shots = 0` (if supported) they are calculated exactly by simulation.

        Supported `kwargs` are as for `BraketBackend.process_circuits`.

        The order is big-endian with respect to the order of qubits in the argument.
        For example, if qubits=[0,1] then the order of probabilities is [p(0,0), p(0,1),
        p(1,0), p(1,1)], while if qubits=[1,0] the order is [p(0,0), p(1,0), p(0,1),
        p(1,1)], where p(i,j) is the probability of qubit 0 being in state i and qubit 1
        being in state j.

        :param qubits: qubits of interest

        :returns: list of probabilities of outcomes if initial state is all-zeros
        """
        if not self.supports_probability:
            raise RuntimeError("Backend does not support probability")
        if (n_shots < self._probability_min_shots
                or n_shots > self._probability_max_shots):
            raise ValueError(
                f"n_shots must be between {self._probability_min_shots} and "
                f"{self._probability_max_shots}")
        if valid_check:
            self._check_all_circuits([circuit], nomeasure_warn=False)
        bkcirc = self._to_bkcirc(circuit)
        restype = ResultType.Probability(target=qubits)
        bkcirc.add_result_type(restype)
        task = self._run(bkcirc, n_shots=n_shots, **kwargs)
        res = task.result()
        return res.get_value_by_result_type(restype)  # type: ignore
Ejemplo n.º 12
0
    def add_result_type(
        self,
        result_type: ResultType,
        target: QubitSetInput = None,
        target_mapping: Dict[QubitInput, QubitInput] = {},
    ) -> Circuit:
        """
        Add a requested result type to `self`, returns `self` for chaining ability.

        Args:
            result_type (ResultType): `ResultType` to add into `self`.
            target (int, Qubit, or iterable of int / Qubit, optional): Target qubits for the
                `result_type`.
                Default = `None`.
            target_mapping (dictionary[int or Qubit, int or Qubit], optional): A dictionary of
                qubit mappings to apply to the `result_type.target`. Key is the qubit in
                `result_type.target` and the value is what the key will be changed to.
                Default = `{}`.


        Note: target and target_mapping will only be applied to those requested result types with
        the attribute `target`. The result_type will be appended to the end of the list of
        `circuit.result_types` only if it does not already exist in `circuit.result_types`

        Returns:
            Circuit: self

        Raises:
            TypeError: If both `target_mapping` and `target` are supplied.
            ValueError: If the observable specified for a qubit is different from what is
                specified by the result types already added to the circuit. Only one observable
                is allowed for a qubit.

        Examples:
            >>> result_type = ResultType.Probability(target=[0, 1])
            >>> circ = Circuit().add_result_type(result_type)
            >>> print(circ.result_types[0])
            Probability(target=QubitSet([Qubit(0), Qubit(1)]))

            >>> result_type = ResultType.Probability(target=[0, 1])
            >>> circ = Circuit().add_result_type(result_type, target_mapping={0: 10, 1: 11})
            >>> print(circ.result_types[0])
            Probability(target=QubitSet([Qubit(10), Qubit(11)]))

            >>> result_type = ResultType.Probability(target=[0, 1])
            >>> circ = Circuit().add_result_type(result_type, target=[10, 11])
            >>> print(circ.result_types[0])
            Probability(target=QubitSet([Qubit(10), Qubit(11)]))

            >>> result_type = ResultType.StateVector()
            >>> circ = Circuit().add_result_type(result_type)
            >>> print(circ.result_types[0])
            StateVector()
        """
        if target_mapping and target is not None:
            raise TypeError("Only one of 'target_mapping' or 'target' can be supplied.")

        if not target_mapping and not target:
            # Nothing has been supplied, add result_type
            result_type_to_add = result_type
        elif target_mapping:
            # Target mapping has been supplied, copy result_type
            result_type_to_add = result_type.copy(target_mapping=target_mapping)
        else:
            # ResultType with target
            result_type_to_add = result_type.copy(target=target)

        if result_type_to_add not in self._result_types:
            self._add_to_qubit_observable_mapping(result_type_to_add)
            self._add_to_qubit_observable_set(result_type_to_add)
            self._result_types.append(result_type_to_add)
        return self
Ejemplo n.º 13
0
    def __eq__(self, other) -> bool:
        if isinstance(other, StateVector):
            return True
        return False

    def __copy__(self) -> StateVector:
        return type(self)()

    # must redefine __hash__ since __eq__ is overwritten
    # https://docs.python.org/3/reference/datamodel.html#object.__hash__
    def __hash__(self) -> int:
        return super().__hash__()


ResultType.register_result_type(StateVector)


class DensityMatrix(ResultType):
    """
    The full density matrix as a requested result type.
    This is available on simulators only when `shots=0`.
    """

    def __init__(self, target: QubitSetInput = None):
        """
        Args:
            target (int, Qubit, or iterable of int / Qubit, optional): The target qubits
                of the reduced density matrix. Default is `None`, and the
                full density matrix is returned.
Ejemplo n.º 14
0
    def add_result_type(
        self,
        result_type: ResultType,
        target: QubitSetInput = None,
        target_mapping: Dict[QubitInput, QubitInput] = None,
    ) -> Circuit:
        """
        Add a requested result type to `self`, returns `self` for chaining ability.

        Args:
            result_type (ResultType): `ResultType` to add into `self`.
            target (int, Qubit, or iterable of int / Qubit, optional): Target qubits for the
                `result_type`.
                Default = `None`.
            target_mapping (dictionary[int or Qubit, int or Qubit], optional): A dictionary of
                qubit mappings to apply to the `result_type.target`. Key is the qubit in
                `result_type.target` and the value is what the key will be changed to.
                Default = `None`.


        Note: target and target_mapping will only be applied to those requested result types with
        the attribute `target`. The result_type will be appended to the end of the dict keys of
        `circuit.result_types` only if it does not already exist in `circuit.result_types`

        Returns:
            Circuit: self

        Raises:
            TypeError: If both `target_mapping` and `target` are supplied.

        Examples:
            >>> result_type = ResultType.Probability(target=[0, 1])
            >>> circ = Circuit().add_result_type(result_type)
            >>> print(circ.result_types[0])
            Probability(target=QubitSet([Qubit(0), Qubit(1)]))

            >>> result_type = ResultType.Probability(target=[0, 1])
            >>> circ = Circuit().add_result_type(result_type, target_mapping={0: 10, 1: 11})
            >>> print(circ.result_types[0])
            Probability(target=QubitSet([Qubit(10), Qubit(11)]))

            >>> result_type = ResultType.Probability(target=[0, 1])
            >>> circ = Circuit().add_result_type(result_type, target=[10, 11])
            >>> print(circ.result_types[0])
            Probability(target=QubitSet([Qubit(10), Qubit(11)]))

            >>> result_type = ResultType.StateVector()
            >>> circ = Circuit().add_result_type(result_type)
            >>> print(circ.result_types[0])
            StateVector()
        """
        if target_mapping and target is not None:
            raise TypeError("Only one of 'target_mapping' or 'target' can be supplied.")

        if not target_mapping and not target:
            # Nothing has been supplied, add result_type
            result_type_to_add = result_type
        elif target_mapping:
            # Target mapping has been supplied, copy result_type
            result_type_to_add = result_type.copy(target_mapping=target_mapping)
        else:
            # ResultType with target
            result_type_to_add = result_type.copy(target=target)

        if result_type_to_add not in self._result_types:
            observable = Circuit._extract_observable(result_type_to_add)
            if observable and self._observables_simultaneously_measurable:
                # Only check if all observables can be simultaneously measured
                self._add_to_qubit_observable_mapping(observable, result_type_to_add.target)
            self._add_to_qubit_observable_set(result_type_to_add)
            # using dict as an ordered set, value is arbitrary
            self._result_types[result_type_to_add] = None
        return self