示例#1
0
 def test_adjoint(self):
     """Test adjoint method."""
     matr = self.rand_matrix(2, 2, real=True)
     mati = self.rand_matrix(2, 2, real=True)
     op = Operator(matr + 1j * mati)
     uni_adj = op.adjoint()
     self.assertEqual(uni_adj, Operator(matr.T - 1j * mati.T))
示例#2
0
    def test_evolve_subsystem(self):
        """Test subsystem evolve method for operators."""
        # Test evolving single-qubit of 3-qubit system
        for _ in range(5):
            rho = self.rand_rho(8)
            state = DensityMatrix(rho)
            op0 = random_unitary(2)
            op1 = random_unitary(2)
            op2 = random_unitary(2)

            # Test evolve on 1-qubit
            op = op0
            op_full = Operator(np.eye(4)).tensor(op)
            target = DensityMatrix(
                np.dot(op_full.data, rho).dot(op_full.adjoint().data))
            self.assertEqual(state.evolve(op, qargs=[0]), target)

            # Evolve on qubit 1
            op_full = Operator(np.eye(2)).tensor(op).tensor(np.eye(2))
            target = DensityMatrix(
                np.dot(op_full.data, rho).dot(op_full.adjoint().data))
            self.assertEqual(state.evolve(op, qargs=[1]), target)

            # Evolve on qubit 2
            op_full = op.tensor(np.eye(4))
            target = DensityMatrix(
                np.dot(op_full.data, rho).dot(op_full.adjoint().data))
            self.assertEqual(state.evolve(op, qargs=[2]), target)

            # Test evolve on 2-qubits
            op = op1.tensor(op0)

            # Evolve on qubits [0, 2]
            op_full = op1.tensor(np.eye(2)).tensor(op0)
            target = DensityMatrix(
                np.dot(op_full.data, rho).dot(op_full.adjoint().data))
            self.assertEqual(state.evolve(op, qargs=[0, 2]), target)

            # Evolve on qubits [2, 0]
            op_full = op0.tensor(np.eye(2)).tensor(op1)
            target = DensityMatrix(
                np.dot(op_full.data, rho).dot(op_full.adjoint().data))
            self.assertEqual(state.evolve(op, qargs=[2, 0]), target)

            # Test evolve on 3-qubits
            op = op2.tensor(op1).tensor(op0)

            # Evolve on qubits [0, 1, 2]
            op_full = op
            target = DensityMatrix(
                np.dot(op_full.data, rho).dot(op_full.adjoint().data))
            self.assertEqual(state.evolve(op, qargs=[0, 1, 2]), target)

            # Evolve on qubits [2, 1, 0]
            op_full = op0.tensor(op1).tensor(op2)
            target = DensityMatrix(
                np.dot(op_full.data, rho).dot(op_full.adjoint().data))
            self.assertEqual(state.evolve(op, qargs=[2, 1, 0]), target)
示例#3
0
    def expectation_value(self, oper, qargs=None):
        """Compute the expectation value of an operator.

        Args:
            oper (Operator): an operator to evaluate expval.
            qargs (None or list): subsystems to apply the operator on.

        Returns:
            complex: the expectation value.
        """
        if not isinstance(oper, Operator):
            oper = Operator(oper)
        return np.trace(Operator(self).dot(oper.adjoint(), qargs=qargs).data)
示例#4
0
 def _evolve_operator(self, other, qargs=None):
     """Evolve density matrix by an operator"""
     if not isinstance(other, Operator):
         other = Operator(other)
     if qargs is None:
         # Evolution on full matrix
         if self._dim != other._input_dim:
             raise QiskitError(
                 "Operator input dimension is not equal to density matrix dimension."
             )
         mat = np.dot(other.data, self.data).dot(other.adjoint().data)
         return DensityMatrix(mat, dims=other.output_dims())
     # Otherwise we are applying an operator only to subsystems
     # Check dimensions of subsystems match the operator
     if self.dims(qargs) != other.input_dims():
         raise QiskitError(
             "Operator input dimensions are not equal to statevector subsystem dimensions."
         )
     # Reshape statevector and operator
     tensor = np.reshape(self.data, self._shape)
     # Construct list of tensor indices of statevector to be contracted
     num_indices = len(self.dims())
     indices = [num_indices - 1 - qubit for qubit in qargs]
     # Left multiple by mat
     mat = np.reshape(other.data, other._shape)
     tensor = Operator._einsum_matmul(tensor, mat, indices)
     # Right multiply by mat ** dagger
     adj = other.adjoint()
     mat_adj = np.reshape(adj.data, adj._shape)
     tensor = Operator._einsum_matmul(tensor, mat_adj, indices, num_indices,
                                      True)
     # Replace evolved dimensions
     new_dims = list(self.dims())
     for i, qubit in enumerate(qargs):
         new_dims[qubit] = other._output_dims[i]
     new_dim = np.product(new_dims)
     return DensityMatrix(np.reshape(tensor, (new_dim, new_dim)),
                          dims=new_dims)
示例#5
0
    def expectation_value(self, oper, qargs=None):
        """Compute the expectation value of an operator.

        Args:
            oper (Operator): an operator to evaluate expval.
            qargs (None or list): subsystems to apply the operator on.

        Returns:
            complex: the expectation value.
        """
        if isinstance(oper, Pauli):
            return self._expectation_value_pauli(oper)

        if isinstance(oper, SparsePauliOp):
            return sum([
                coeff * self._expectation_value_pauli(Pauli((z, x)))
                for z, x, coeff in zip(oper.table.Z, oper.table.X, oper.coeffs)
            ])

        if not isinstance(oper, Operator):
            oper = Operator(oper)
        return np.trace(Operator(self).dot(oper.adjoint(), qargs=qargs).data)
示例#6
0
def process_fidelity(channel,
                     target=None,
                     require_cp=True,
                     require_tp=False,
                     require_cptp=False):
    r"""Return the process fidelity of a noisy quantum channel.

    This process fidelity :math:`F_{\text{pro}}` is given by

    .. math::
        F_{\text{pro}}(\mathcal{E}, U)
            = \frac{Tr[S_U^\dagger S_{\mathcal{E}}]}{d^2}

    where :math:`S_{\mathcal{E}}, S_{U}` are the
    :class:`~qiskit.quantum_info.SuperOp` matrices for the input quantum
    *channel* :math:`\cal{E}` and *target* unitary :math:`U` respectively,
    and :math:`d` is the dimension of the *channel*.

    Args:
        channel (QuantumChannel): noisy quantum channel.
        target (Operator or None): target unitary operator.
            If `None` target is the identity operator [Default: None].
        require_cp (bool): require channel to be completely-positive
            [Default: True].
        require_tp (bool): require channel to be trace-preserving
            [Default: False].
        require_cptp (bool): (DEPRECATED) require input channels to be
            CPTP [Default: False].

    Returns:
        float: The process fidelity :math:`F_{\text{pro}}`.

    Raises:
        QiskitError: if the channel and target do not have the same dimensions,
                     or have different input and output dimensions.
        QiskitError: if the channel and target or are not completely-positive
                     (with ``require_cp=True``) or not trace-preserving
                     (with ``require_tp=True``).
    """
    # Format inputs
    if isinstance(channel, (list, np.ndarray, Operator, Pauli)):
        channel = Operator(channel)
    else:
        channel = SuperOp(channel)
    input_dim, output_dim = channel.dim
    if input_dim != output_dim:
        raise QiskitError(
            'Quantum channel must have equal input and output dimensions.')

    if target is not None:
        # Multiple channel by adjoint of target
        target = Operator(target)
        if (input_dim, output_dim) != target.dim:
            raise QiskitError(
                'Quantum channel and target must have the same dimensions.')
        channel = channel @ target.adjoint()

    # Validate complete-positivity and trace-preserving
    if require_cptp:
        # require_cptp kwarg is DEPRECATED
        # Remove in future qiskit version
        warnings.warn(
            "Please use `require_cp=True, require_tp=True` "
            "instead of `require_cptp=True`.", DeprecationWarning)
        require_cp = True
        require_tp = True
    if isinstance(channel, Operator) and (require_cp or require_tp):
        is_unitary = channel.is_unitary()
        # Validate as unitary
        if require_cp and not is_unitary:
            raise QiskitError('channel is not completely-positive')
        if require_tp and not is_unitary:
            raise QiskitError('channel is not trace-preserving')
    else:
        # Validate as QuantumChannel
        if require_cp and not channel.is_cp():
            raise QiskitError('channel is not completely-positive')
        if require_tp and not channel.is_tp():
            raise QiskitError('channel is not trace-preserving')

    # Compute process fidelity with identity channel
    if isinstance(channel, Operator):
        # |Tr[U]/dim| ** 2
        fid = np.abs(np.trace(channel.data) / input_dim)**2
    else:
        # Tr[S] / (dim ** 2)
        fid = np.trace(channel.data) / (input_dim**2)
    return float(np.real(fid))
示例#7
0
def process_fidelity(channel, target=None, require_cp=True, require_tp=False):
    r"""Return the process fidelity of a noisy quantum channel.

    This process fidelity :math:`F_{\text{pro}}` is given by

    .. math::
        F_{\text{pro}}(\mathcal{E}, U)
            = \frac{Tr[S_U^\dagger S_{\mathcal{E}}]}{d^2}

    where :math:`S_{\mathcal{E}}, S_{U}` are the
    :class:`~qiskit.quantum_info.SuperOp` matrices for the input quantum
    *channel* :math:`\cal{E}` and *target* unitary :math:`U` respectively,
    and :math:`d` is the dimension of the *channel*.

    Args:
        channel (QuantumChannel or Operator): noisy quantum channel.
        target (Operator or None): target unitary operator.
            If `None` target is the identity operator [Default: None].
        require_cp (bool): require channel to be completely-positive
            [Default: True].
        require_tp (bool): require channel to be trace-preserving
            [Default: False].

    Returns:
        float: The process fidelity :math:`F_{\text{pro}}`.

    Raises:
        QiskitError: if the channel and target do not have the same dimensions,
                     or have different input and output dimensions.
        QiskitError: if the channel and target or are not completely-positive
                     (with ``require_cp=True``) or not trace-preserving
                     (with ``require_tp=True``).
    """
    # Format inputs
    if isinstance(channel, (list, np.ndarray, Operator, Pauli, Clifford)):
        channel = Operator(channel)
    else:
        channel = SuperOp(channel)

    if target is not None:
        try:
            target = Operator(target)
        except QiskitError:
            raise QiskitError('Target channel is not a unitary channel.')
        if channel.dim != target.dim:
            raise QiskitError(
                'Input quantum channel and target unitary must have the same '
                'dimensions ({} != {}).'.format(channel.dim, target.dim))

    # Validate complete-positivity and trace-preserving
    if require_cp or require_tp:
        # Validate target channel
        if target is not None:
            is_unitary = target.is_unitary()
            if require_cp and not is_unitary:
                raise QiskitError(
                    'Target unitary channel is not completely-positive')
            if require_tp and not is_unitary:
                raise QiskitError(
                    'Target unitary channel is not trace-preserving')
        # Validate input channel
        if isinstance(channel, Operator):
            is_unitary = channel.is_unitary()
            # Validate as unitary
            if require_cp and not is_unitary:
                raise QiskitError(
                    'Input quantum channel is not completely-positive')
            if require_tp and not is_unitary:
                raise QiskitError(
                    'Input quantum channel is not trace-preserving')
        else:
            # Validate as QuantumChannel
            if require_cp and not channel.is_cp():
                raise QiskitError(
                    'Input quantum channel is not completely-positive')
            if require_tp and not channel.is_tp():
                raise QiskitError(
                    'Input quantum channel is not trace-preserving')

    # Compute process fidelity with identity channel
    if target is not None:
        channel = channel.compose(target.adjoint())
    input_dim, _ = channel.dim
    if isinstance(channel, Operator):
        # |Tr[U]/dim| ** 2
        fid = np.abs(np.trace(channel.data) / input_dim)**2
    else:
        # Tr[S] / (dim ** 2)
        fid = np.trace(SuperOp(channel).data) / (input_dim**2)
    return float(np.real(fid))