Exemplo n.º 1
0
 def test_power_except(self):
     """Test power method raises exceptions."""
     chan = SuperOp(self.depol_sop(1))
     # Non-integer power raises error
     self.assertRaises(QiskitError, chan.power, 0.5)
Exemplo n.º 2
0
 def test_add_except(self):
     """Test add method raises exceptions."""
     chan1 = SuperOp(self.sopI)
     chan2 = SuperOp(np.eye(16))
     self.assertRaises(QiskitError, chan1._add, chan2)
     self.assertRaises(QiskitError, chan1._add, 5)
Exemplo n.º 3
0
    def test_compose_front(self):
        """Test front compose method."""
        # DEPRECATED
        # UnitaryChannel evolution
        chan1 = SuperOp(self.sopX)
        chan2 = SuperOp(self.sopY)
        chan = chan1.compose(chan2, front=True)
        targ = SuperOp(self.sopZ)
        self.assertEqual(chan, targ)

        # 50% depolarizing channel
        chan1 = SuperOp(self.depol_sop(0.5))
        chan = chan1.compose(chan1, front=True)
        targ = SuperOp(self.depol_sop(0.75))
        self.assertEqual(chan, targ)

        # Random superoperator
        mat1 = self.rand_matrix(4, 4)
        mat2 = self.rand_matrix(4, 4)
        chan1 = SuperOp(mat1)
        chan2 = SuperOp(mat2)
        targ = SuperOp(np.dot(mat2, mat1))
        self.assertEqual(chan2.compose(chan1, front=True), targ)
        targ = SuperOp(np.dot(mat1, mat2))
        self.assertEqual(chan1.compose(chan2, front=True), targ)

        # Compose different dimensions
        chan1 = SuperOp(self.rand_matrix(16, 4))
        chan2 = SuperOp(self.rand_matrix(4, 16))
        chan = chan1.compose(chan2, front=True)
        self.assertEqual(chan.dim, (4, 4))
        chan = chan2.compose(chan1, front=True)
        self.assertEqual(chan.dim, (2, 2))
Exemplo n.º 4
0
    def test_compose_front_inplace(self):
        """Test inplace front compose method."""
        # UnitaryChannel evolution
        chan1 = SuperOp(self.sopX)
        chan2 = SuperOp(self.sopY)
        targ = SuperOp(self.sopZ)
        chan1.compose(chan2, inplace=True, front=True)
        self.assertEqual(chan1, targ)

        # 50% depolarizing channel
        chan1 = SuperOp(self.depol_sop(0.5))
        chan1.compose(chan1, inplace=True, front=True)
        targ = SuperOp(self.depol_sop(0.75))
        self.assertEqual(chan1, targ)

        # Random superoperator
        mat1 = self.rand_matrix(4, 4)
        mat2 = self.rand_matrix(4, 4)

        targ = SuperOp(np.dot(mat2, mat1))
        chan1 = SuperOp(mat1)
        chan2 = SuperOp(mat2)
        chan2.compose(chan1, inplace=True, front=True)
        self.assertEqual(chan2, targ)
        targ = SuperOp(np.dot(mat1, mat2))
        chan1 = SuperOp(mat1)
        chan2 = SuperOp(mat2)
        chan1.compose(chan2, inplace=True, front=True)
        self.assertEqual(chan1, targ)

        # Compose different dimensions
        chan1 = SuperOp(self.rand_matrix(16, 4), input_dim=2, output_dim=4)
        chan2 = SuperOp(self.rand_matrix(4, 16), output_dim=2)
        chan1.compose(chan2, inplace=True, front=True)
        self.assertEqual(chan1.dims, (4, 4))
        chan1 = SuperOp(self.rand_matrix(16, 4), input_dim=2, output_dim=4)
        chan2 = SuperOp(self.rand_matrix(4, 16), output_dim=2)
        chan2.compose(chan1, inplace=True, front=True)
        self.assertEqual(chan2.dims, (2, 2))
Exemplo n.º 5
0
 def test_transpose(self):
     """Test transpose method."""
     mat = self.rand_matrix(4, 4)
     chan = SuperOp(mat)
     targ = SuperOp(np.transpose(mat))
     self.assertEqual(chan.transpose(), targ)
Exemplo n.º 6
0
 def test_compose_except(self):
     """Test compose different dimension exception"""
     self.assertRaises(QiskitError,
                       SuperOp(np.eye(4)).compose, SuperOp(np.eye(16)))
     self.assertRaises(QiskitError, SuperOp(np.eye(4)).compose, 2)
Exemplo n.º 7
0
 def test_circuit_init(self):
     """Test initialization from a circuit."""
     circuit, target = self.simple_circuit_no_measure()
     op = SuperOp(circuit)
     target = SuperOp(target)
     self.assertEqual(op, target)
Exemplo n.º 8
0
 def test_is_cptp(self):
     """Test is_cptp method."""
     self.assertTrue(SuperOp(self.depol_sop(0.25)).is_cptp())
     # Non-CPTP should return false
     self.assertFalse(
         SuperOp(1.25 * self.sopI - 0.25 * self.depol_sop(1)).is_cptp())
Exemplo n.º 9
0
 def fidelity(channel):  # fidelity w.r.t. identity omitting the N^-2 factor
     return float(np.abs(np.trace(SuperOp(channel))))
Exemplo n.º 10
0
 def test_multiply(self):
     """Test multiply method."""
     chan = SuperOp(self.sopI)
     val = 0.5
     targ = SuperOp(val * self.sopI)
     self.assertEqual(chan.multiply(val), targ)
Exemplo n.º 11
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))
Exemplo n.º 12
0
    def test_compose_front_subsystem(self):
        """Test subsystem front compose method."""
        # 3-qubit operator
        mat = self.rand_matrix(64, 64)
        mat_a = self.rand_matrix(4, 4)
        mat_b = self.rand_matrix(4, 4)
        mat_c = self.rand_matrix(4, 4)
        iden = SuperOp(np.eye(4))
        op = SuperOp(mat)
        op1 = SuperOp(mat_a)
        op2 = SuperOp(mat_b).tensor(SuperOp(mat_a))
        op3 = SuperOp(mat_c).tensor(SuperOp(mat_b)).tensor(SuperOp(mat_a))

        # op3 qubits=[0, 1, 2]
        full_op = SuperOp(mat_c).tensor(SuperOp(mat_b)).tensor(SuperOp(mat_a))
        targ = np.dot(mat, full_op.data)
        self.assertEqual(op.compose(op3, qubits=[0, 1, 2], front=True),
                         SuperOp(targ))
        # op3 qubits=[2, 1, 0]
        full_op = SuperOp(mat_a).tensor(SuperOp(mat_b)).tensor(SuperOp(mat_c))
        targ = np.dot(mat, full_op.data)
        self.assertEqual(op.compose(op3, qubits=[2, 1, 0], front=True),
                         SuperOp(targ))

        # op2 qubits=[0, 1]
        full_op = iden.tensor(SuperOp(mat_b)).tensor(SuperOp(mat_a))
        targ = np.dot(mat, full_op.data)
        self.assertEqual(op.compose(op2, qubits=[0, 1], front=True),
                         SuperOp(targ))
        # op2 qubits=[2, 0]
        full_op = SuperOp(mat_a).tensor(iden).tensor(SuperOp(mat_b))
        targ = np.dot(mat, full_op.data)
        self.assertEqual(op.compose(op2, qubits=[2, 0], front=True),
                         SuperOp(targ))

        # op1 qubits=[0]
        full_op = iden.tensor(iden).tensor(SuperOp(mat_a))
        targ = np.dot(mat, full_op.data)
        self.assertEqual(op.compose(op1, qubits=[0], front=True),
                         SuperOp(targ))

        # op1 qubits=[1]
        full_op = iden.tensor(SuperOp(mat_a)).tensor(iden)
        targ = np.dot(mat, full_op.data)
        self.assertEqual(op.compose(op1, qubits=[1], front=True),
                         SuperOp(targ))

        # op1 qubits=[2]
        full_op = SuperOp(mat_a).tensor(iden).tensor(iden)
        targ = np.dot(mat, full_op.data)
        self.assertEqual(op.compose(op1, qubits=[2], front=True),
                         SuperOp(targ))
Exemplo n.º 13
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))
Exemplo n.º 14
0
 def test_negate(self):
     """Test negate method"""
     chan = SuperOp(self.sopI)
     targ = SuperOp(-self.sopI)
     self.assertEqual(-chan, targ)
Exemplo n.º 15
0
 def test_subtract_except(self):
     """Test subtract method raises exceptions."""
     chan1 = SuperOp(self.sopI)
     chan2 = SuperOp(np.eye(16))
     self.assertRaises(QiskitError, chan1.subtract, chan2)
     self.assertRaises(QiskitError, chan1.subtract, 5)
Exemplo n.º 16
0
 def test_equal(self):
     """Test __eq__ method"""
     mat = self.rand_matrix(4, 4)
     self.assertEqual(SuperOp(mat), SuperOp(mat))
Exemplo n.º 17
0
 def test_multiply_except(self):
     """Test multiply method raises exceptions."""
     chan = SuperOp(self.sopI)
     self.assertRaises(QiskitError, chan.multiply, 's')
     self.assertRaises(QiskitError, chan.multiply, chan)
Exemplo n.º 18
0
 def test_conjugate(self):
     """Test conjugate method."""
     mat = self.rand_matrix(4, 4)
     chan = SuperOp(mat)
     targ = SuperOp(np.conjugate(mat))
     self.assertEqual(chan.conjugate(), targ)
Exemplo n.º 19
0
def qchannel_to_qiskit(representation):
    """
    Create a qiskit representation of quantum channel from a myqlm representation
    of a quantum channel.

    Args:
        representation: (QuantumChannel) myqlm representation of a quantum channel.

    Returns:
        (Kraus|Choi|Chi|SuperOp|PTM): qiskit representation of a quantum channel.
    """

    rep = representation.representation
    # Find what representation it is.
    # Then create the corresponding matrix and shape it like qiskit is expecting it.
    # Finally, create the qiskit representation from that matrix.
    if rep in (RepresentationType.PTM, RepresentationType.CHOI):
        matri = representation.matrix
        data_re = []
        data_im = []
        for i in range(matri.nRows):
            for j in range(matri.nCols):
                data_re.append(matri.data[i * matri.nRows + j].re + 0.j)
                data_im.append(matri.data[i * matri.nRows + j].im)
        data = np.array(data_re)
        data.imag = np.array(data_im)
        data = data.reshape((matri.nRows, matri.nCols))
        return PTM(data) if (rep == RepresentationType.PTM) else Choi(data)
    if rep in (RepresentationType.CHI, RepresentationType.SUPEROP):
        final_data = []
        for matri in representation.basis:
            data_re = []
            data_im = []
            for i in range(matri.nRows):
                for j in range(matri.nCols):
                    data_re.append(matri.data[i * matri.nRows + j].re + 0.j)
                    data_im.append(matri.data[i * matri.nRows + j].im)
            data = np.array(data_re)
            data.imag = np.array(data_im)
            data = data.reshape((matri.nRows, matri.nCols))
            final_data.append(data)
        if rep == RepresentationType.CHI:
            return Chi(final_data) if len(final_data) > 1 else Chi(
                final_data[0])
        return SuperOp(final_data) if len(final_data) > 1 else SuperOp(
            final_data[0])
    if rep == RepresentationType.KRAUS:
        final_data = []
        for matri in representation.kraus_ops:
            data_re = []
            data_im = []
            for i in range(matri.nRows):
                for j in range(matri.nCols):
                    data_re.append(matri.data[i * matri.nRows + j].re + 0.j)
                    data_im.append(matri.data[i * matri.nRows + j].im)
            data = np.array(data_re)
            data.imag = np.array(data_im)
            data = data.reshape((matri.nRows, matri.nCols))
            final_data.append(data)
        return Kraus(final_data)
    return None
Exemplo n.º 20
0
 def test_adjoint(self):
     """Test adjoint method."""
     mat = self.rand_matrix(4, 4)
     chan = SuperOp(mat)
     targ = SuperOp(np.transpose(np.conj(mat)))
     self.assertEqual(chan.adjoint(), targ)
Exemplo n.º 21
0
 def simple_circuit_no_measure(self):
     """Return a unitary circuit and the corresponding unitary array."""
     # Override OperatorTestCase to return a SuperOp
     circ, target = super().simple_circuit_no_measure()
     return circ, SuperOp(target)
Exemplo n.º 22
0
    def test_dot(self):
        """Test dot method."""
        # UnitaryChannel evolution
        chan1 = SuperOp(self.sopX)
        chan2 = SuperOp(self.sopY)
        targ = SuperOp(self.sopZ)
        self.assertEqual(chan1.dot(chan2), targ)
        self.assertEqual(chan1 * chan2, targ)

        # 50% depolarizing channel
        chan1 = SuperOp(self.depol_sop(0.5))
        targ = SuperOp(self.depol_sop(0.75))
        self.assertEqual(chan1.dot(chan1), targ)
        self.assertEqual(chan1 * chan1, targ)

        # Random superoperator
        mat1 = self.rand_matrix(4, 4)
        mat2 = self.rand_matrix(4, 4)
        chan1 = SuperOp(mat1)
        chan2 = SuperOp(mat2)
        targ = SuperOp(np.dot(mat2, mat1))
        self.assertEqual(chan2.dot(chan1), targ)
        self.assertEqual(chan2 * chan1, targ)
        targ = SuperOp(np.dot(mat1, mat2))
        self.assertEqual(chan1 * chan2, targ)

        # Compose different dimensions
        chan1 = SuperOp(self.rand_matrix(16, 4))
        chan2 = SuperOp(self.rand_matrix(4, 16))
        chan = chan1.dot(chan2)
        self.assertEqual(chan.dim, (4, 4))
        chan = chan2.dot(chan1)
        self.assertEqual(chan.dim, (2, 2))
Exemplo n.º 23
0
def process_fidelity(channel, target=None, require_cp=True, require_tp=True):
    r"""Return the process fidelity of a noisy quantum channel.


    The process fidelity :math:`F_{\text{pro}}(\mathcal{E}, \methcal{F})`
    between two quantum channels :math:`\mathcal{E}, \mathcal{F}` is given by

    .. math:
        F_{\text{pro}}(\mathcal{E}, \mathcal{F})
            = F(\rho_{\mathcal{E}}, \rho_{\mathcal{F}})

    where :math:`F` is the :func:`~qiskit.quantum_info.state_fidelity`,
    :math:`\rho_{\mathcal{E}} = \Lambda_{\mathcal{E}} / d` is the
    normalized :class:`~qiskit.quantum_info.Choi` matrix for the channel
    :math:`\mathcal{E}`, and :math:`d` is the input dimension of
    :math:`\mathcal{E}`.

    When the target channel is unitary this is equivalent to

    .. 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:`\mathcal{E}` and *target* unitary :math:`U` respectively,
    and :math:`d` is the input dimension of the channel.

    Args:
        channel (Operator or QuantumChannel): input quantum channel.
        target (Operator or QuantumChannel or None): target quantum channel.
            If `None` target is the identity operator [Default: None].
        require_cp (bool): check if input and target channels are
                           completely-positive and if non-CP log warning
                           containing negative eigenvalues of Choi-matrix
                           [Default: True].
        require_tp (bool): check if input and target channels are
                           trace-preserving and if non-TP log warning
                           containing negative eigenvalues of partial
                           Choi-matrix :math:`Tr_{\mbox{out}}[\mathcal{E}] - I`
                           [Default: True].

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

    Raises:
        QiskitError: if the channel and target do not have the same dimensions.
    """
    # Format inputs
    channel = _input_formatter(channel, SuperOp, 'process_fidelity', 'channel')
    target = _input_formatter(target, Operator, 'process_fidelity', 'target')

    if target:
        # Validate dimensions
        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
    for label, chan in [('Input', channel), ('Target', target)]:
        if chan is not None and require_cp:
            cp_cond = _cp_condition(chan)
            neg = cp_cond < -1 * chan.atol
            if np.any(neg):
                logger.warning(
                    '%s channel is not CP. Choi-matrix has negative eigenvalues: %s',
                    label, cp_cond[neg])
        if chan is not None and require_tp:
            tp_cond = _tp_condition(chan)
            non_zero = np.logical_not(
                np.isclose(tp_cond, 0, atol=chan.atol, rtol=chan.rtol))
            if np.any(non_zero):
                logger.warning(
                    '%s channel is not TP. Tr_2[Choi] - I has non-zero eigenvalues: %s',
                    label, tp_cond[non_zero])

    if isinstance(target, Operator):
        # Compute fidelity with unitary target by applying the inverse
        # to channel and computing fidelity with the identity
        channel = channel @ target.adjoint()
        target = None

    input_dim, _ = channel.dim
    if target is None:
        # 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(SuperOp(channel).data) / (input_dim**2)
        return float(np.real(fid))

    # For comparing two non-unitary channels we compute the state fidelity of
    # the normalized Choi-matrices. This is equivalent to the previous definition
    # when the target is a unitary channel.
    state1 = DensityMatrix(Choi(channel).data / input_dim)
    state2 = DensityMatrix(Choi(target).data / input_dim)
    return state_fidelity(state1, state2, validate=False)
Exemplo n.º 24
0
    def test_dot_subsystem(self):
        """Test subsystem dot method."""
        # 3-qubit operator
        mat = self.rand_matrix(64, 64)
        mat_a = self.rand_matrix(4, 4)
        mat_b = self.rand_matrix(4, 4)
        mat_c = self.rand_matrix(4, 4)
        iden = SuperOp(np.eye(4))
        op = SuperOp(mat)
        op1 = SuperOp(mat_a)
        op2 = SuperOp(mat_b).tensor(SuperOp(mat_a))
        op3 = SuperOp(mat_c).tensor(SuperOp(mat_b)).tensor(SuperOp(mat_a))

        # op3 qargs=[0, 1, 2]
        full_op = SuperOp(mat_c).tensor(SuperOp(mat_b)).tensor(SuperOp(mat_a))
        targ = np.dot(mat, full_op.data)
        self.assertEqual(op.dot(op3, qargs=[0, 1, 2]), SuperOp(targ))
        self.assertEqual(op * op3([0, 1, 2]), SuperOp(targ))
        # op3 qargs=[2, 1, 0]
        full_op = SuperOp(mat_a).tensor(SuperOp(mat_b)).tensor(SuperOp(mat_c))
        targ = np.dot(mat, full_op.data)
        self.assertEqual(op.dot(op3, qargs=[2, 1, 0]), SuperOp(targ))
        self.assertEqual(op * op3([2, 1, 0]), SuperOp(targ))

        # op2 qargs=[0, 1]
        full_op = iden.tensor(SuperOp(mat_b)).tensor(SuperOp(mat_a))
        targ = np.dot(mat, full_op.data)
        self.assertEqual(op.dot(op2, qargs=[0, 1]), SuperOp(targ))
        self.assertEqual(op * op2([0, 1]), SuperOp(targ))
        # op2 qargs=[2, 0]
        full_op = SuperOp(mat_a).tensor(iden).tensor(SuperOp(mat_b))
        targ = np.dot(mat, full_op.data)
        self.assertEqual(op.dot(op2, qargs=[2, 0]), SuperOp(targ))
        self.assertEqual(op * op2([2, 0]), SuperOp(targ))

        # op1 qargs=[0]
        full_op = iden.tensor(iden).tensor(SuperOp(mat_a))
        targ = np.dot(mat, full_op.data)
        self.assertEqual(op.dot(op1, qargs=[0]), SuperOp(targ))
        self.assertEqual(op * op1([0]), SuperOp(targ))
        # op1 qargs=[1]
        full_op = iden.tensor(SuperOp(mat_a)).tensor(iden)
        targ = np.dot(mat, full_op.data)
        self.assertEqual(op.dot(op1, qargs=[1]), SuperOp(targ))
        self.assertEqual(op * op1([1]), SuperOp(targ))

        # op1 qargs=[2]
        full_op = SuperOp(mat_a).tensor(iden).tensor(iden)
        targ = np.dot(mat, full_op.data)
        self.assertEqual(op.dot(op1, qargs=[2]), SuperOp(targ))
        self.assertEqual(op * op1([2]), SuperOp(targ))
Exemplo n.º 25
0
def process_fidelity(channel, target=None, require_cp=True, require_tp=False):
    r"""Return the process fidelity of a noisy quantum channel.


    The process fidelity :math:`F_{\text{pro}}(\mathcal{E}, \methcal{F})`
    between two quantum channels :math:`\mathcal{E}, \mathcal{F}` is given by

    .. math:
        F_{\text{pro}}(\mathcal{E}, \mathcal{F})
            = F(\rho_{\mathcal{E}}, \rho_{\mathcal{F}})

    where :math:`F` is the :func:`~qiskit.quantum_info.state_fidelity`,
    :math:`\rho_{\mathcal{E}} = \Lambda_{\mathcal{E}} / d` is the
    normalized :class:`~qiskit.quantum_info.Choi` matrix for the channel
    :math:`\mathcal{E}`, and :math:`d` is the input dimension of
    :math:`\mathcal{E}`.

    When the target channel is unitary this is equivalent to

    .. 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:`\mathcal{E}` and *target* unitary :math:`U` respectively,
    and :math:`d` is the input dimension of the channel.

    Args:
        channel (Operator or QuantumChannel): input quantum channel.
        target (Operator or QuantumChannel or None): target quantum channel.
            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.
        QiskitError: if the channel and target are not completely-positive
                     (with ``require_cp=True``) or not trace-preserving
                     (with ``require_tp=True``).
    """
    # Format inputs
    channel = _input_formatter(channel, SuperOp, 'process_fidelity', 'channel')
    target = _input_formatter(target, Operator, 'process_fidelity', 'target')

    if target:
        # Validate dimensions
        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
    for label, chan in [('Input', channel), ('Target', target)]:
        if isinstance(chan, Operator) and (require_cp or require_tp):
            is_unitary = chan.is_unitary()
            # Validate as unitary
            if require_cp and not is_unitary:
                raise QiskitError(
                    '{} channel is not completely-positive'.format(label))
            if require_tp and not is_unitary:
                raise QiskitError(
                    '{} channel is not trace-preserving'.format(label))
        elif chan is not None:
            # Validate as QuantumChannel
            if require_cp and not chan.is_cp():
                raise QiskitError(
                    '{} channel is not completely-positive'.format(label))
            if require_tp and not chan.is_tp():
                raise QiskitError(
                    '{} channel is not trace-preserving'.format(label))

    if isinstance(target, Operator):
        # Compute fidelity with unitary target by applying the inverse
        # to channel and computing fidelity with the identity
        channel = channel @ target.adjoint()
        target = None

    input_dim, _ = channel.dim
    if target is None:
        # 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(SuperOp(channel).data) / (input_dim**2)
        return float(np.real(fid))

    # For comparing two non-unitary channels we compute the state fidelity of
    # the normalized Choi-matrices. This is equivalent to the previous definition
    # when the target is a unitary channel.
    state1 = DensityMatrix(Choi(channel).data / input_dim)
    state2 = DensityMatrix(Choi(target).data / input_dim)
    return state_fidelity(state1, state2, validate=False)