Exemplo n.º 1
0
def _to_choi(rep, data, input_dim, output_dim):
    """Transform a QuantumChannel to the Choi representation."""
    if rep == 'Choi':
        return data
    if rep == 'UnitaryChannel':
        return _from_unitary('Choi', data, input_dim, output_dim)
    if rep == 'SuperOp':
        return _superop_to_choi(data, input_dim, output_dim)
    if rep == 'Kraus':
        return _kraus_to_choi(data, input_dim, output_dim)
    if rep == 'Chi':
        return _chi_to_choi(data, input_dim, output_dim)
    if rep == 'PTM':
        data = _ptm_to_superop(data, input_dim, output_dim)
        return _superop_to_choi(data, input_dim, output_dim)
    if rep == 'Stinespring':
        return _stinespring_to_choi(data, input_dim, output_dim)
    raise QiskitError('Invalid QuantumChannel {}'.format(rep))
Exemplo n.º 2
0
    def _evolve_subsystem(self, state, qargs):
        """Evolve a quantum state by the operator.

        Args:
            state (QuantumState): The input statevector or density matrix.
            qargs (list): a list of QuantumState subsystem positions to apply
                           the operator on.

        Returns:
            QuantumState: the output quantum state.

        Raises:
            QiskitError: if the operator dimension does not match the
            specified QuantumState subsystem dimensions.
        """
        mat = np.reshape(self.data, self._shape)
        # Hack to assume state is a N-qubit state until a proper class for states
        # is in place
        state_size = len(state)
        state_dims = self._automatic_dims(None, state_size)
        if self.input_dims() != len(qargs) * (2, ):
            raise QiskitError(
                "Operator input dimensions are not compatible with state subsystem dimensions."
            )
        if state.ndim == 1:
            # Return evolved statevector
            tensor = np.reshape(state, state_dims)
            indices = [len(state_dims) - 1 - qubit for qubit in qargs]
            tensor = self._einsum_matmul(tensor, mat, indices)
            return np.reshape(tensor, state_size)
        # Return evolved density matrix
        tensor = np.reshape(state, 2 * state_dims)
        indices = [len(state_dims) - 1 - qubit for qubit in qargs]
        right_shift = len(state_dims)
        # Left multiply by operator
        tensor = self._einsum_matmul(tensor, mat, indices)
        # Right multiply by adjoint operator
        # We implement the transpose by doing left multiplication instead of right
        # in the _einsum_matmul function
        tensor = self._einsum_matmul(tensor,
                                     np.conj(mat),
                                     indices,
                                     shift=right_shift)
        return np.reshape(tensor, [state_size, state_size])
Exemplo n.º 3
0
    def subtract(self, other):
        """Return the operator self - other.

        Args:
            other (Operator): an operator object.

        Returns:
            Operator: the operator self - other.

        Raises:
            QiskitError: if other is not an operator, or has incompatible
            dimensions.
        """
        if not isinstance(other, Operator):
            other = Operator(other)
        if self.dim != other.dim:
            raise QiskitError("other operator has different dimensions.")
        return Operator(self.data - other.data, self.input_dims(),
                        self.output_dims())
Exemplo n.º 4
0
def job_monitor(job, interval=None, monitor_async=False):
    """Monitor the status of a IBMQJob instance.

    Args:
        job (BaseJob): Job to monitor.
        interval (int): Time interval between status queries.
        monitor_async (bool): Monitor asyncronously (in Jupyter only).

    Raises:
        QiskitError: When trying to run async outside of Jupyter
        ImportError: ipywidgets not available for notebook.
    """
    if interval is None:
        _interval_set = False
        interval = 2
    else:
        _interval_set = True
    if _NOTEBOOK_ENV:
        if monitor_async:
            try:
                import ipywidgets as widgets  # pylint: disable=import-error
            except ImportError:
                raise ImportError('These functions  need ipywidgets. '
                                  'Run "pip install ipywidgets" before.')
            from qiskit.tools.jupyter.jupyter_magics import _html_checker  # pylint: disable=C0412

            style = "font-size:16px;"
            header = "<p style='{style}'>Job Status: %s </p>".format(
                style=style)
            status = widgets.HTML(value=header % job.status().value)
            display(status)

            thread = threading.Thread(target=_html_checker,
                                      args=(job, interval, status, header))
            thread.start()
        else:
            _text_checker(job, interval, _interval_set)

    else:
        if monitor_async:
            raise QiskitError(
                'monitor_async only available in Jupyter notebooks.')
        _text_checker(job, interval, _interval_set)
Exemplo n.º 5
0
    def subtract(self, other):
        """Return the QuantumChannel self - other.

        Args:
            other (QuantumChannel): a quantum channel.

        Returns:
            Chi: the linear subtraction self - other as Chi object.

        Raises:
            QiskitError: if other is not a QuantumChannel subclass, or
            has incompatible dimensions.
        """
        if not isinstance(other, Chi):
            other = Chi(other)
        if self.dim != other.dim:
            raise QiskitError("other QuantumChannel dimensions are not equal")
        return Chi(self._data - other.data, self._input_dims,
                   self._output_dims)
Exemplo n.º 6
0
    def multiply(self, other, inplace=False):
        """Return the QuantumChannel self + other.

        Args:
            other (complex): a complex number
            inplace (bool): If True modify the current object inplace
                           [Default: False]

        Returns:
            Stinespring: the scalar multiplication other * self as a
            Stinespring object.

        Raises:
            QiskitError: if other is not a valid scalar.
        """
        if not isinstance(other, Number):
            raise QiskitError("other is not a number")
        # If the number is complex or negative we need to convert to
        # general Stinespring representation so we first convert to
        # the Choi representation
        if isinstance(other, complex) or other < 1:
            # Convert to Choi-matrix
            tmp = Stinespring(Choi(self).multiply(other, inplace=True))
            if inplace:
                self._data = tmp._data
                self._input_dim = tmp._input_dim
                self._output_dim = tmp._output_dim
                return self
            return tmp
        # If the number is real we can update the Kraus operators
        # directly
        num = np.sqrt(other)
        if inplace:
            self._data[0] *= num
            if self._data[1] is not None:
                self._data[1] *= num
            return self
        stine_l, stine_r = self._data
        stine_l = num * self._data[0]
        stine_r = None
        if self._data[1] is not None:
            stine_r = num * self._data[1]
        return Stinespring((stine_l, stine_r), *self.dims)
Exemplo n.º 7
0
    def add(self, other):
        """Return the QuantumChannel self + other.

        Args:
            other (QuantumChannel): a quantum channel subclass.

        Returns:
            Kraus: the linear addition self + other as a Kraus object.

        Raises:
            QiskitError: if other is not a QuantumChannel subclass, or
            has incompatible dimensions.
        """
        if not issubclass(other.__class__, QuantumChannel):
            raise QiskitError('other is not a QuantumChannel subclass')
        # Since we cannot directly add two channels in the Kraus
        # representation we try and use the other channels method
        # or convert to the Choi representation
        return Kraus(Choi(self).add(other))
Exemplo n.º 8
0
    def _get_backend_instance(self, backend_cls):
        """
        Return an instance of a backend from its class.

        Args:
            backend_cls (class): Backend class.
        Returns:
            BaseBackend: a backend instance.
        Raises:
            QiskitError: if the backend could not be instantiated.
        """
        # Verify that the backend can be instantiated.
        try:
            backend_instance = backend_cls(provider=self)
        except Exception as err:
            raise QiskitError('Backend %s could not be instantiated: %s' %
                              (backend_cls, err))

        return backend_instance
Exemplo n.º 9
0
    def add(self, other):
        """Return the QuantumChannel self + other.

        Args:
            other (QuantumChannel): a quantum channel.

        Returns:
            Choi: the linear addition self + other as a Choi object.

        Raises:
            QiskitError: if other cannot be converted to a channel or
            has incompatible dimensions.
        """
        if not isinstance(other, Choi):
            other = Choi(other)
        if self.dim != other.dim:
            raise QiskitError("other QuantumChannel dimensions are not equal")
        return Choi(self._data + other.data, self._input_dims,
                    self._output_dims)
Exemplo n.º 10
0
    def subtract(self, other):
        """Return the QuantumChannel self - other.

        Args:
            other (QuantumChannel): a quantum channel.

        Returns:
            PTM: the linear subtraction self - other as PTM object.

        Raises:
            QiskitError: if other cannot be converted to a channel or
            has incompatible dimensions.
        """
        if not isinstance(other, PTM):
            other = PTM(other)
        if self.dim != other.dim:
            raise QiskitError("other QuantumChannel dimensions are not equal")
        return PTM(self._data - other.data, self._input_dims,
                   self._output_dims)
Exemplo n.º 11
0
    def multiply(self, other, inplace=False):
        """Return the QuantumChannel self + other.

        Args:
            other (complex): a complex number
            inplace (bool): If True modify the current object inplace
                           [Default: False]

        Returns:
            Kraus: the scalar multiplication other * self as a Kraus object.

        Raises:
            QiskitError: if other is not a valid scalar.
        """
        if not isinstance(other, Number):
            raise QiskitError("other is not a number")
        # If the number is complex we need to convert to general
        # kraus channel so we multiply via Choi representation
        if isinstance(other, complex) or other < 0:
            # Convert to Choi-matrix
            tmp = Kraus(Choi(self).multiply(other, inplace=True))
            if inplace:
                self._data = tmp._data
                self._input_dim = tmp._input_dim
                self._output_dim = tmp._output_dim
                return self
            return tmp
        # If the number is real we can update the Kraus operators
        # directly
        val = np.sqrt(other)
        if inplace:
            for j, _ in enumerate(self._data[0]):
                self._data[0][j] *= val
            if self._data[1] is not None:
                for j, _ in enumerate(self._data[1]):
                    self._data[1][j] *= val
            return self
        kraus_r = None
        kraus_l = [val * k for k in self._data[0]]
        if self._data[1] is not None:
            kraus_r = [val * k for k in self._data[1]]
        return Kraus((kraus_l, kraus_r), self._input_dim, self._output_dim)
Exemplo n.º 12
0
    def subtract(self, other):
        """Return the QuantumChannel self - other.

        Args:
            other (QuantumChannel): a quantum channel.

        Returns:
            SuperOp: the linear subtraction self - other as SuperOp object.

        Raises:
            QiskitError: if other cannot be converted to a channel or
            has incompatible dimensions.
        """
        # Convert other to SuperOp
        if not isinstance(other, SuperOp):
            other = SuperOp(other)
        if self.dim != other.dim:
            raise QiskitError("other QuantumChannel dimensions are not equal")
        return SuperOp(self._data - other.data, self.input_dims(),
                       self.output_dims())
Exemplo n.º 13
0
    def _tensor_product(self, other, reverse=False):
        """Return the tensor product channel.

        Args:
            other (QuantumChannel): a quantum channel subclass.
            reverse (bool): If False return self ⊗ other, if True return
                            if True return (other ⊗ self) [Default: False
        Returns:
            Kraus: the tensor product channel as a Kraus object.

        Raises:
            QiskitError: if other is not a QuantumChannel subclass.
        """
        # Convert other to Kraus
        if not issubclass(other.__class__, QuantumChannel):
            raise QiskitError('other is not a QuantumChannel subclass')
        if not isinstance(other, Kraus):
            other = Kraus(other)

        # Get tensor matrix
        ka_l, ka_r = self._data
        kb_l, kb_r = other._data
        if reverse:
            kab_l = [np.kron(b, a) for a in ka_l for b in kb_l]
        else:
            kab_l = [np.kron(a, b) for a in ka_l for b in kb_l]
        if ka_r is None and kb_r is None:
            kab_r = None
        else:
            if ka_r is None:
                ka_r = ka_l
            if kb_r is None:
                kb_r = kb_l
            if reverse:
                kab_r = [np.kron(b, a) for a in ka_r for b in kb_r]
            else:
                kab_r = [np.kron(a, b) for a in ka_r for b in kb_r]
        data = (kab_l, kab_r)
        input_dim = self._input_dim * other._input_dim
        output_dim = self._output_dim * other._output_dim
        return Kraus(data, input_dim, output_dim)
Exemplo n.º 14
0
    def multiply(self, other, inplace=False):
        """Return the QuantumChannel self + other.

        Args:
            other (complex): a complex number
            inplace (bool): If True modify the current object inplace
                           [Default: False]

        Returns:
            SuperOp: the scalar multiplication other * self as a SuperOp object.

        Raises:
            QiskitError: if other is not a valid scalar.
        """
        if not isinstance(other, Number):
            raise QiskitError("other is not a number")
        if inplace:
            self._data *= other
            return self
        input_dim, output_dim = self.dims
        return SuperOp(other * self._data, input_dim, output_dim)
Exemplo n.º 15
0
    def _evolve(self, state, qubits=None):
        """Evolve a quantum state by the QuantumChannel.

        Args:
            state (QuantumState): The input statevector or density matrix.
            qubits (list): a list of QuantumState subsystem positions to apply
                           the operator on.

        Returns:
            QuantumState: the output quantum state.

        Raises:
            QiskitError: if the operator dimension does not match the
            specified QuantumState subsystem dimensions.
        """
        # If subsystem evolution we use the SuperOp representation
        if qubits is not None:
            return SuperOp(self)._evolve(state, qubits)

        # Otherwise we compute full evolution directly
        state = self._format_state(state)
        if state.shape[0] != self._input_dim:
            raise QiskitError(
                "QuantumChannel input dimension is not equal to state dimension."
            )
        if state.ndim == 1 and self._data[1] is None and \
           self._data[0].shape[0] // self._output_dim == 1:
            # If the shape of the stinespring operator is equal to the output_dim
            # evolution of a state vector psi -> stine.psi
            return np.dot(self._data[0], state)
        # Otherwise we always return a density matrix
        state = self._format_state(state, density_matrix=True)
        stine_l, stine_r = self._data
        if stine_r is None:
            stine_r = stine_l
        din, dout = self.dim
        dtr = stine_l.shape[0] // dout
        shape = (dout, dtr, din)
        return np.einsum('iAB,BC,jAC->ij', np.reshape(stine_l, shape), state,
                         np.reshape(np.conjugate(stine_r), shape))
Exemplo n.º 16
0
    def _tensor_product(self, other, reverse=False):
        """Return the tensor product channel.

        Args:
            other (QuantumChannel): a quantum channel subclass.
            reverse (bool): If False return self ⊗ other, if True return
                            if True return (other ⊗ self) [Default: False
        Returns:
            Choi: the tensor product channel as a Choi object.

        Raises:
            QiskitError: if other is not a QuantumChannel subclass.
        """
        # Convert other to Choi
        if not issubclass(other.__class__, QuantumChannel):
            raise QiskitError('other is not a QuantumChannel subclass')
        if not isinstance(other, Choi):
            other = Choi(other)

        # Reshuffle indicies
        a_in, a_out = self.dims
        b_in, b_out = other.dims

        # Combined channel dimensions
        input_dim = a_in * b_in
        output_dim = a_out * b_out

        if reverse:
            data = _bipartite_tensor(
                other.data,
                self._data,
                shape1=other._bipartite_shape,
                shape2=self._bipartite_shape)
        else:
            data = _bipartite_tensor(
                self._data,
                other.data,
                shape1=self._bipartite_shape,
                shape2=other._bipartite_shape)
        return Choi(data, input_dim, output_dim)
Exemplo n.º 17
0
def _from_operator(rep, data, input_dim, output_dim):
    """Transform Operator representation to other representation."""
    if rep == 'Operator':
        return data
    if rep == 'SuperOp':
        return np.kron(np.conj(data), data)
    if rep == 'Choi':
        vec = np.ravel(data, order='F')
        return np.outer(vec, np.conj(vec))
    if rep == 'Kraus':
        return ([data], None)
    if rep == 'Stinespring':
        return (data, None)
    if rep == 'Chi':
        _check_nqubit_dim(input_dim, output_dim)
        data = _from_operator('Choi', data, input_dim, output_dim)
        return _choi_to_chi(data, input_dim, output_dim)
    if rep == 'PTM':
        _check_nqubit_dim(input_dim, output_dim)
        data = _from_operator('SuperOp', data, input_dim, output_dim)
        return _superop_to_ptm(data, input_dim, output_dim)
    raise QiskitError('Invalid QuantumChannel {}'.format(rep))
Exemplo n.º 18
0
def least_busy(backends):
    """
    Return the least busy available backend for those that
    have a `pending_jobs` in their `status`. Backends such as
    local backends that do not have this are not considered.

    Args:
        backends (list[BaseBackend]): backends to choose from

    Returns:
        BaseBackend: the the least busy backend

    Raises:
        QiskitError: if passing a list of backend names that is
            either empty or none have attribute ``pending_jobs``
    """
    try:
        return min([b for b in backends if b.status().operational],
                   key=lambda b: b.status().pending_jobs)
    except (ValueError, TypeError):
        raise QiskitError(
            "Can only find least_busy backend from a non-empty list.")
Exemplo n.º 19
0
 def __init__(self, data, input_dim=None, output_dim=None):
     # Check if input is a quantum channel object
     # If so we disregard the dimension kwargs
     if issubclass(data.__class__, QuantumChannel):
         input_dim, output_dim = data.dims
         super_mat = _to_superop(data.rep, data._data, input_dim,
                                 output_dim)
     else:
         # We initialize directly from superoperator matrix
         super_mat = np.array(data, dtype=complex)
         # Determine input and output dimensions
         dout, din = super_mat.shape
         if output_dim is None:
             output_dim = int(np.sqrt(dout))
         if input_dim is None:
             input_dim = int(np.sqrt(din))
         # Check dimensions
         if output_dim**2 != dout or input_dim**2 != din:
             raise QiskitError(
                 "Invalid input and output dimension for superoperator input."
             )
     super().__init__('SuperOp', super_mat, input_dim, output_dim)
Exemplo n.º 20
0
    def subscribe(self, event, callback):
        """Subscribes to an event, so when it's emitted all the callbacks subscribed,
        will be executed. We are not allowing double registration.

        Args
            event (string): The event to subscribed in the form of:
                            "terra.<component>.<method>.<action>"
            callback (callable): The callback that will be executed when an event is
                                  emitted.
        """
        if not callable(callback):
            raise QiskitError("Callback is not a callable!")

        if event not in self._subscribers:
            self._subscribers[event] = []

        new_subscription = self._Subscription(event, callback)
        if new_subscription in self._subscribers[event]:
            # We are not allowing double subscription
            return False

        self._subscribers[event].append(new_subscription)
        return True
Exemplo n.º 21
0
    def _evolve(self, state, qubits=None):
        """Evolve a quantum state by the QuantumChannel.

        Args:
            state (QuantumState): The input statevector or density matrix.
            qubits (list): a list of QuantumState subsystem positions to apply
                           the operator on.

        Returns:
            QuantumState: the output quantum state.

        Raises:
            QiskitError: if the operator dimension does not match the
            specified QuantumState subsystem dimensions.
        """
        # If subsystem evolution we use the SuperOp representation
        if qubits is not None:
            return SuperOp(self)._evolve(state, qubits)

        # Otherwise we compute full evolution directly
        state = self._format_state(state)
        if state.shape[0] != self._input_dim:
            raise QiskitError(
                "QuantumChannel input dimension is not equal to state dimension."
            )
        if state.ndim == 1 and self._data[1] is None and len(
                self._data[0]) == 1:
            # If we only have a single Kraus operator we can implement unitary-type
            # evolution of a state vector psi -> K[0].psi
            return np.dot(self._data[0][0], state)
        # Otherwise we always return a density matrix
        state = self._format_state(state, density_matrix=True)
        kraus_l, kraus_r = self._data
        if kraus_r is None:
            kraus_r = kraus_l
        return np.einsum('AiB,BC,AjC->ij', kraus_l, state,
                         np.conjugate(kraus_r))
Exemplo n.º 22
0
    def _tensor_product(self, other, inplace=False, reverse=False):
        """Return the tensor product channel.

        Args:
            other (QuantumChannel): a quantum channel subclass
            inplace (bool): If True modify the current object inplace
                            [default: False]
            reverse (bool): If False return self ⊗ other, if True return
                            if True return (other ⊗ self) [Default: False
        Returns:
            UnitaryChannel: the tensor product channel as a UnitaryChannel object.

        Raises:
            QiskitError: if other is not a QuantumChannel subclass.
        """
        if not issubclass(other.__class__, QuantumChannel):
            raise QiskitError('Other is not a channel rep')
        # Convert to UnitaryChannel matrix
        if not isinstance(other, UnitaryChannel):
            other = UnitaryChannel(other)

        # Combined channel dimensions
        a_in, a_out = self.dims
        b_in, b_out = other.dims
        input_dim = a_in * b_in
        output_dim = a_out * b_out
        if reverse:
            data = np.kron(other._data, self._data)
        else:
            data = np.kron(self._data, other._data)
        if inplace:
            self._data = data
            self._input_dim = input_dim
            self._output_dim = output_dim
            return self
        # Not inplace so return new object
        return UnitaryChannel(data, input_dim, output_dim)
Exemplo n.º 23
0
    def __init__(self, data, input_dims=None, output_dims=None):
        """Initialize a quantum channel Chi-matrix operator.

        Args:
            data (QuantumCircuit or
                  Instruction or
                  BaseOperator or
                  matrix): data to initialize superoperator.
            input_dims (tuple): the input subsystem dimensions.
                                [Default: None]
            output_dims (tuple): the output subsystem dimensions.
                                 [Default: None]

        Raises:
            QiskitError: if input data is not an N-qubit channel or
            cannot be initialized as a Chi-matrix.

        Additional Information
        ----------------------
        If the input or output dimensions are None, they will be
        automatically determined from the input data. The Chi matrix
        representation is only valid for N-qubit channels.
        """
        # If the input is a raw list or matrix we assume that it is
        # already a Chi matrix.
        if isinstance(data, (list, np.ndarray)):
            # Initialize from raw numpy or list matrix.
            chi_mat = np.array(data, dtype=complex)
            # Determine input and output dimensions
            dim_l, dim_r = chi_mat.shape
            if dim_l != dim_r:
                raise QiskitError('Invalid Chi-matrix input.')
            if input_dims:
                input_dim = np.product(input_dims)
            if output_dims:
                output_dim = np.product(input_dims)
            if output_dims is None and input_dims is None:
                output_dim = int(np.sqrt(dim_l))
                input_dim = dim_l // output_dim
            elif input_dims is None:
                input_dim = dim_l // output_dim
            elif output_dims is None:
                output_dim = dim_l // input_dim
            # Check dimensions
            if input_dim * output_dim != dim_l:
                raise QiskitError("Invalid shape for Chi-matrix input.")
        else:
            # Otherwise we initialize by conversion from another Qiskit
            # object into the QuantumChannel.
            if isinstance(data, (QuantumCircuit, Instruction)):
                # If the input is a Terra QuantumCircuit or Instruction we
                # convert it to a SuperOp
                data = SuperOp._instruction_to_superop(data)
            else:
                # We use the QuantumChannel init transform to intialize
                # other objects into a QuantumChannel or Operator object.
                data = self._init_transformer(data)
            input_dim, output_dim = data.dim
            # Now that the input is an operator we convert it to a Chi object
            chi_mat = _to_chi(data.rep, data._data, input_dim, output_dim)
            if input_dims is None:
                input_dims = data.input_dims()
            if output_dims is None:
                output_dims = data.output_dims()
        # Check input is N-qubit channel
        n_qubits = int(np.log2(input_dim))
        if 2**n_qubits != input_dim:
            raise QiskitError("Input is not an n-qubit Chi matrix.")
        # Check and format input and output dimensions
        input_dims = self._automatic_dims(input_dims, input_dim)
        output_dims = self._automatic_dims(output_dims, output_dim)
        super().__init__('Chi', chi_mat, input_dims, output_dims)
 def _check_dups(self, qubits):
     """Raise exception if list of qubits contains duplicates."""
     squbits = set(qubits)
     if len(squbits) != len(qubits):
         raise QiskitError("duplicate qubit arguments")
Exemplo n.º 25
0
    def __init__(self, data, input_dims=None, output_dims=None):
        """Initialize a quantum channel Kraus operator.

        Args:
            data (QuantumCircuit or
                  Instruction or
                  BaseOperator or
                  matrix): data to initialize superoperator.
            input_dims (tuple): the input subsystem dimensions.
                                [Default: None]
            output_dims (tuple): the output subsystem dimensions.
                                 [Default: None]

        Raises:
            QiskitError: if input data cannot be initialized as a
            a list of Kraus matrices.

        Additional Information
        ----------------------
        If the input or output dimensions are None, they will be
        automatically determined from the input data. If the input data is
        a list of Numpy arrays of shape (2**N, 2**N) qubit systems will be used. If
        the input does not correspond to an N-qubit channel, it will assign a
        single subsystem with dimension specifed by the shape of the input.
        """
        # If the input is a list or tuple we assume it is a list of Kraus
        # matrices, if it is a numpy array we assume that it is a single Kraus
        # operator
        if isinstance(data, (list, tuple, np.ndarray)):
            # Check if it is a single unitary matrix A for channel:
            # E(rho) = A * rho * A^\dagger
            if isinstance(data, np.ndarray) or np.array(data).ndim == 2:
                # Convert single Kraus op to general Kraus pair
                kraus = ([np.array(data, dtype=complex)], None)
                shape = kraus[0][0].shape

            # Check if single Kraus set [A_i] for channel:
            # E(rho) = sum_i A_i * rho * A_i^dagger
            elif isinstance(data, list) and len(data) > 0:
                # Get dimensions from first Kraus op
                kraus = [np.array(data[0], dtype=complex)]
                shape = kraus[0].shape
                # Iterate over remaining ops and check they are same shape
                for i in data[1:]:
                    op = np.array(i, dtype=complex)
                    if op.shape != shape:
                        raise QiskitError(
                            "Kraus operators are different dimensions.")
                    kraus.append(op)
                # Convert single Kraus set to general Kraus pair
                kraus = (kraus, None)

            # Check if generalized Kraus set ([A_i], [B_i]) for channel:
            # E(rho) = sum_i A_i * rho * B_i^dagger
            elif isinstance(data,
                            tuple) and len(data) == 2 and len(data[0]) > 0:
                kraus_left = [np.array(data[0][0], dtype=complex)]
                shape = kraus_left[0].shape
                for i in data[0][1:]:
                    op = np.array(i, dtype=complex)
                    if op.shape != shape:
                        raise QiskitError(
                            "Kraus operators are different dimensions.")
                    kraus_left.append(op)
                if data[1] is None:
                    kraus = (kraus_left, None)
                else:
                    kraus_right = []
                    for i in data[1]:
                        op = np.array(i, dtype=complex)
                        if op.shape != shape:
                            raise QiskitError(
                                "Kraus operators are different dimensions.")
                        kraus_right.append(op)
                    kraus = (kraus_left, kraus_right)
            else:
                raise QiskitError("Invalid input for Kraus channel.")
        else:
            # Otherwise we initialize by conversion from another Qiskit
            # object into the QuantumChannel.
            if isinstance(data, (QuantumCircuit, Instruction)):
                # If the input is a Terra QuantumCircuit or Instruction we
                # convert it to a SuperOp
                data = SuperOp._instruction_to_superop(data)
            else:
                # We use the QuantumChannel init transform to intialize
                # other objects into a QuantumChannel or Operator object.
                data = self._init_transformer(data)
            input_dim, output_dim = data.dim
            # Now that the input is an operator we convert it to a Kraus
            kraus = _to_kraus(data.rep, data._data, input_dim, output_dim)
            if input_dims is None:
                input_dims = data.input_dims()
            if output_dims is None:
                output_dims = data.output_dims()

        output_dim, input_dim = kraus[0][0].shape
        # Check and format input and output dimensions
        input_dims = self._automatic_dims(input_dims, input_dim)
        output_dims = self._automatic_dims(output_dims, output_dim)
        # Initialize either single or general Kraus
        if kraus[1] is None or np.allclose(kraus[0], kraus[1]):
            # Standard Kraus map
            super().__init__('Kraus', (kraus[0], None), input_dims,
                             output_dims)
        else:
            # General (non-CPTP) Kraus map
            super().__init__('Kraus', kraus, input_dims, output_dims)
Exemplo n.º 26
0
    def _tensor_product(self, other, reverse=False):
        """Return the tensor product channel.

        Args:
            other (QuantumChannel): a quantum channel subclass.
            reverse (bool): If False return self ⊗ other, if True return
                            if True return (other ⊗ self) [Default: False]
        Returns:
            Stinespring: the tensor product channel as a Stinespring object.

        Raises:
            QiskitError: if other is not a Stinespring object
        """
        # Convert other to Stinespring
        if not issubclass(other.__class__, QuantumChannel):
            raise QiskitError('other is not a QuantumChannel subclass')
        if not isinstance(other, Stinespring):
            other = Stinespring(other)

        # Tensor stinespring ops
        sa_l, sa_r = self._data
        sb_l, sb_r = other._data

        # Reshuffle tensor dimensions
        din_a, dout_a = self.dims
        din_b, dout_b = other.dims
        dtr_a = sa_l.shape[0] // dout_a
        dtr_b = sb_l.shape[0] // dout_b
        if reverse:
            shape_in = (dout_b, dtr_b, dout_a, dtr_a, din_b * din_a)
            shape_out = (dout_b * dtr_b * dout_a * dtr_a, din_b * din_a)
        else:
            shape_in = (dout_a, dtr_a, dout_b, dtr_b, din_a * din_b)
            shape_out = (dout_a * dtr_a * dout_b * dtr_b, din_a * din_b)

        # Compute left stinepsring op
        if reverse:
            sab_l = np.kron(sb_l, sa_l)
        else:
            sab_l = np.kron(sa_l, sb_l)
        # Reravel indicies
        sab_l = np.reshape(
            np.transpose(np.reshape(sab_l, shape_in), (0, 2, 1, 3, 4)),
            shape_out)

        # Compute right stinespring op
        if sa_r is None and sb_r is None:
            sab_r = None
        else:
            if sa_r is None:
                sa_r = sa_l
            elif sb_r is None:
                sb_r = sb_l
            if reverse:
                sab_r = np.kron(sb_r, sa_r)
            else:
                sab_r = np.kron(sa_r, sb_r)
            # Reravel indicies
            sab_r = np.reshape(
                np.transpose(np.reshape(sab_r, shape_in), (0, 2, 1, 3, 4)),
                shape_out)
        return Stinespring((sab_l, sab_r), din_a * din_b, dout_a * dout_b)
Exemplo n.º 27
0
 def add(self, gate):
     """Add instruction to set."""
     if not isinstance(gate, Instruction):
         raise QiskitError("attempt to add non-Instruction" +
                           " to InstructionSet")
     self.instructions.append(gate)
Exemplo n.º 28
0
 def check_circuit(self):
     """Raise exception if self.circuit is None."""
     if self.circuit is None:
         raise QiskitError("Instruction's circuit not assigned")
Exemplo n.º 29
0
    def __init__(self, data, input_dim=None, output_dim=None):
        # Check if input is a quantum channel object
        # If so we disregard the dimension kwargs
        if issubclass(data.__class__, QuantumChannel):
            input_dim, output_dim = data.dims
            kraus = _to_kraus(data.rep, data._data, input_dim, output_dim)

        else:
            # Check if it is a single unitary matrix A for channel:
            # E(rho) = A * rho * A^\dagger
            if isinstance(data, np.ndarray) or np.array(data).ndim == 2:
                # Convert single Kraus op to general Kraus pair
                kraus = ([np.array(data, dtype=complex)], None)
                shape = kraus[0][0].shape

            # Check if single Kraus set [A_i] for channel:
            # E(rho) = sum_i A_i * rho * A_i^dagger
            elif isinstance(data, list) and len(data) > 0:
                # Get dimensions from first Kraus op
                kraus = [np.array(data[0], dtype=complex)]
                shape = kraus[0].shape
                # Iterate over remaining ops and check they are same shape
                for i in data[1:]:
                    op = np.array(i, dtype=complex)
                    if op.shape != shape:
                        raise QiskitError(
                            "Kraus operators are different dimensions.")
                    kraus.append(op)
                # Convert single Kraus set to general Kraus pair
                kraus = (kraus, None)

            # Check if generalized Kraus set ([A_i], [B_i]) for channel:
            # E(rho) = sum_i A_i * rho * B_i^dagger
            elif isinstance(data,
                            tuple) and len(data) == 2 and len(data[0]) > 0:
                kraus_left = [np.array(data[0][0], dtype=complex)]
                shape = kraus_left[0].shape
                for i in data[0][1:]:
                    op = np.array(i, dtype=complex)
                    if op.shape != shape:
                        raise QiskitError(
                            "Kraus operators are different dimensions.")
                    kraus_left.append(op)
                if data[1] is None:
                    kraus = (kraus_left, None)
                else:
                    kraus_right = []
                    for i in data[1]:
                        op = np.array(i, dtype=complex)
                        if op.shape != shape:
                            raise QiskitError(
                                "Kraus operators are different dimensions.")
                        kraus_right.append(op)
                    kraus = (kraus_left, kraus_right)
            else:
                raise QiskitError("Invalid input for Kraus channel.")
        dout, din = kraus[0][0].shape
        if (input_dim and input_dim != din) or (output_dim
                                                and output_dim != dout):
            raise QiskitError("Invalid dimensions for Kraus input.")

        if kraus[1] is None or np.allclose(kraus[0], kraus[1]):
            # Standard Kraus map
            super().__init__('Kraus', (kraus[0], None),
                             input_dim=din,
                             output_dim=dout)
        else:
            # General (non-CPTP) Kraus map
            super().__init__('Kraus', kraus, input_dim=din, output_dim=dout)
Exemplo n.º 30
0
    def __init__(self, data, input_dims=None, output_dims=None):
        """Initialize a Kraus quantum channel operator."""
        if issubclass(data.__class__, BaseOperator):
            # If not a channel we use `to_operator` method to get
            # the unitary-representation matrix for input
            if not issubclass(data.__class__, QuantumChannel):
                data = data.to_operator()
            input_dim, output_dim = data.dim
            kraus = _to_kraus(data.rep, data._data, input_dim, output_dim)
            if input_dims is None:
                input_dims = data.input_dims()
            if output_dims is None:
                output_dims = data.output_dims()
        elif isinstance(data, (list, tuple, np.ndarray)):
            # Check if it is a single unitary matrix A for channel:
            # E(rho) = A * rho * A^\dagger
            if isinstance(data, np.ndarray) or np.array(data).ndim == 2:
                # Convert single Kraus op to general Kraus pair
                kraus = ([np.array(data, dtype=complex)], None)
                shape = kraus[0][0].shape

            # Check if single Kraus set [A_i] for channel:
            # E(rho) = sum_i A_i * rho * A_i^dagger
            elif isinstance(data, list) and len(data) > 0:
                # Get dimensions from first Kraus op
                kraus = [np.array(data[0], dtype=complex)]
                shape = kraus[0].shape
                # Iterate over remaining ops and check they are same shape
                for i in data[1:]:
                    op = np.array(i, dtype=complex)
                    if op.shape != shape:
                        raise QiskitError(
                            "Kraus operators are different dimensions.")
                    kraus.append(op)
                # Convert single Kraus set to general Kraus pair
                kraus = (kraus, None)

            # Check if generalized Kraus set ([A_i], [B_i]) for channel:
            # E(rho) = sum_i A_i * rho * B_i^dagger
            elif isinstance(data,
                            tuple) and len(data) == 2 and len(data[0]) > 0:
                kraus_left = [np.array(data[0][0], dtype=complex)]
                shape = kraus_left[0].shape
                for i in data[0][1:]:
                    op = np.array(i, dtype=complex)
                    if op.shape != shape:
                        raise QiskitError(
                            "Kraus operators are different dimensions.")
                    kraus_left.append(op)
                if data[1] is None:
                    kraus = (kraus_left, None)
                else:
                    kraus_right = []
                    for i in data[1]:
                        op = np.array(i, dtype=complex)
                        if op.shape != shape:
                            raise QiskitError(
                                "Kraus operators are different dimensions.")
                        kraus_right.append(op)
                    kraus = (kraus_left, kraus_right)
            else:
                raise QiskitError("Invalid input for Kraus channel.")
        else:
            raise QiskitError("Invalid input data format for Krausß")

        output_dim, input_dim = kraus[0][0].shape
        # Check and format input and output dimensions
        input_dims = self._automatic_dims(input_dims, input_dim)
        output_dims = self._automatic_dims(output_dims, output_dim)
        # Initialize either single or general Kraus
        if kraus[1] is None or np.allclose(kraus[0], kraus[1]):
            # Standard Kraus map
            super().__init__('Kraus', (kraus[0], None), input_dims,
                             output_dims)
        else:
            # General (non-CPTP) Kraus map
            super().__init__('Kraus', kraus, input_dims, output_dims)