Exemple #1
0
    def compose(self, other, qargs=None, front=False):
        """Return the composition channel self∘other.

        Args:
            other (QuantumChannel): a quantum channel.
            qargs (list): a list of subsystem positions to compose other on.
            front (bool): If False compose in standard order other(self(input))
                          otherwise compose in reverse order self(other(input))
                          [default: False]

        Returns:
            Chi: The composition channel as a Chi object.

        Raises:
            QiskitError: if other is not a QuantumChannel subclass, or
            has incompatible dimensions.
        """
        if qargs is not None:
            return Chi(SuperOp(self).compose(other, qargs=qargs, front=front))

        # Convert other to Choi since we convert via Choi
        if not isinstance(other, Choi):
            other = Choi(other)
        # Check dimensions match up
        if front and self._input_dim != other._output_dim:
            raise QiskitError(
                'input_dim of self must match output_dim of other')
        if not front and self._output_dim != other._input_dim:
            raise QiskitError(
                'input_dim of other must match output_dim of self')
        # Since we cannot directly add two channels in the Chi
        # representation we convert to the Choi representation
        return Chi(Choi(self).compose(other, front=front))
Exemple #2
0
 def test_unitary_to_choi(self):
     """Test UnitaryChannel to Choi transformation."""
     # Test unitary channels
     for mat, choi in zip(self.unitary_mat, self.unitary_choi):
         chan1 = Choi(choi)
         chan2 = Choi(UnitaryChannel(mat))
         self.assertEqual(chan1, chan2)
Exemple #3
0
    def _chanmul(self, other, qargs=None, left_multiply=False):
        """Multiply two quantum channels.

        Args:
            other (QuantumChannel): a quantum channel.
            qargs (list): a list of subsystem positions to compose other on.
            left_multiply (bool): If True return other * self
                                  If False return self * other [Default:False]

        Returns:
            Choi: The composition channel as a Chi object.

        Raises:
            QiskitError: if other is not a QuantumChannel subclass, or
            has incompatible dimensions.
        """
        if qargs is not None:
            return Chi(
                SuperOp(self)._chanmul(other,
                                       qargs=qargs,
                                       left_multiply=left_multiply))

        # Convert other to Choi since we convert via Choi
        if not isinstance(other, Choi):
            other = Choi(other)
        # Check dimensions match up
        if not left_multiply and self._input_dim != other._output_dim:
            raise QiskitError(
                'input_dim of self must match output_dim of other')
        if left_multiply and self._output_dim != other._input_dim:
            raise QiskitError(
                'input_dim of other must match output_dim of self')
        # Since we cannot directly multiply two channels in the Chi
        # representation we convert to the Choi representation
        return Chi(Choi(self)._chanmul(other, left_multiply=left_multiply))
Exemple #4
0
 def test_operator_to_choi(self):
     """Test Operator to Choi transformation."""
     # Test unitary channels
     for mat, choi in zip(self.unitary_mat, self.unitary_choi):
         chan1 = Choi(choi)
         chan2 = Choi(Operator(mat))
         self.assertEqual(chan1, chan2)
Exemple #5
0
 def test_choi_to_ptm(self):
     """Test Choi to PTM transformation."""
     # Test unitary channels
     for choi, ptm in zip(self.unitary_choi, self.unitary_ptm):
         chan1 = PTM(ptm)
         chan2 = PTM(Choi(choi))
         self.assertEqual(chan1, chan2)
     # Test depolarizing channels
     for p in [0.25, 0.5, 0.75, 1]:
         chan1 = PTM(self.depol_ptm(p))
         chan2 = PTM(Choi(self.depol_choi(p)))
         self.assertEqual(chan1, chan2)
Exemple #6
0
 def test_kraus_to_choi(self):
     """Test Kraus to Choi transformation."""
     # Test unitary channels
     for mat, choi in zip(self.unitary_mat, self.unitary_choi):
         chan1 = Choi(choi)
         chan2 = Choi(Kraus(mat))
         self.assertEqual(chan1, chan2)
     # Test depolarizing channels
     for p in [0.25, 0.5, 0.75, 1]:
         chan1 = Choi(self.depol_choi(p))
         chan2 = Choi(Kraus(self.depol_kraus(p)))
         self.assertEqual(chan1, chan2)
Exemple #7
0
 def test_stinespring_to_choi(self):
     """Test Stinespring to Choi transformation."""
     # Test unitary channels
     for mat, choi in zip(self.unitary_mat, self.unitary_choi):
         chan1 = Choi(choi)
         chan2 = Choi(Stinespring(mat))
         self.assertEqual(chan1, chan2)
     # Test depolarizing channels
     for p in [0.25, 0.5, 0.75, 1]:
         chan1 = Choi(self.depol_choi(p))
         chan2 = Choi(Stinespring(self.depol_stine(p)))
         self.assertEqual(chan1, chan2)
Exemple #8
0
 def test_chi_to_choi(self):
     """Test Chi to Choi transformation."""
     # Test unitary channels
     for chi, choi in zip(self.unitary_chi, self.unitary_choi):
         chan1 = Choi(choi)
         chan2 = Choi(Chi(chi))
         self.assertEqual(chan1, chan2)
     # Test depolarizing channels
     for p in [0.25, 0.5, 0.75, 1]:
         chan1 = Choi(self.depol_choi(p))
         chan2 = Choi(Chi(self.depol_chi(p)))
         self.assertEqual(chan1, chan2)
Exemple #9
0
 def test_superop_to_choi(self):
     """Test SuperOp to Choi transformation."""
     # Test unitary channels
     for choi, sop in zip(self.unitary_choi, self.unitary_sop):
         chan1 = Choi(choi)
         chan2 = Choi(SuperOp(sop))
         self.assertEqual(chan1, chan2)
     # Test depolarizing channels
     for p in [0, 0.25, 0.5, 0.75, 1]:
         chan1 = Choi(self.depol_choi(p))
         chan2 = Choi(SuperOp(self.depol_sop(p)))
         self.assertEqual(chan1, chan2)
Exemple #10
0
 def test_ptm_to_choi(self):
     """Test PTM to Choi transformation."""
     # Test unitary channels
     for ptm, choi in zip(self.unitary_ptm, self.unitary_choi):
         chan1 = Choi(choi)
         chan2 = Choi(PTM(ptm))
         self.assertEqual(chan1, chan2)
     # Test depolarizing channels
     for p in [0.25, 0.5, 0.75, 1]:
         chan1 = Choi(self.depol_choi(p))
         chan2 = Choi(PTM(self.depol_ptm(p)))
         self.assertEqual(chan1, chan2)
Exemple #11
0
 def test_choi_to_stinespring(self):
     """Test Choi to Stinespring transformation."""
     # Test unitary channels
     for mat, choi in zip(self.unitary_mat, self.unitary_choi):
         chan1 = Kraus(mat)
         chan2 = Kraus(Choi(choi))
         self.assertTrue(
             matrix_equal(chan2.data[0], chan1.data[0], ignore_phase=True))
     # Test depolarizing channels
     rho = np.diag([1, 0])
     for p in [0.25, 0.5, 0.75, 1]:
         targ = Stinespring(self.depol_stine(p))._evolve(rho)
         chan = Stinespring(Choi(self.depol_choi(p)))
         self.assertAllClose(chan._evolve(rho), targ)
Exemple #12
0
 def test_choi_to_stinespring(self):
     """Test Choi to Stinespring transformation."""
     # Test unitary channels
     for mat, choi in zip(self.unitary_mat, self.unitary_choi):
         chan1 = Kraus(mat)
         chan2 = Kraus(Choi(choi))
         self.assertTrue(
             matrix_equal(chan2.data[0], chan1.data[0], ignore_phase=True))
     # Test depolarizing channels
     rho = DensityMatrix(np.diag([1, 0]))
     for p in [0.25, 0.5, 0.75, 1]:
         target = rho.evolve(Stinespring(self.depol_stine(p)))
         output = rho.evolve(Stinespring(Choi(self.depol_choi(p))))
         self.assertEqual(output, target)
Exemple #13
0
    def _multiply(self, other):
        """Return the QuantumChannel other * self.

        Args:
            other (complex): a complex number.

        Returns:
            Stinespring: the scalar multiplication other * self.

        Raises:
            QiskitError: if other is not a valid scalar.
        """
        if not isinstance(other, Number):
            raise QiskitError("other is not a number")

        ret = copy.copy(self)
        # 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
            ret._data = Stinespring(Choi(self)._multiply(other))._data
            return ret
        # If the number is real we can update the Kraus operators
        # directly
        num = np.sqrt(other)
        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]
        ret._data = (stine_l, stine_r)
        return ret
Exemple #14
0
    def compose(self, other, qargs=None, front=False):
        """Return the composed quantum channel self @ other.

        Args:
            other (QuantumChannel): a quantum channel.
            qargs (list or None): a list of subsystem positions to apply
                                  other on. If None apply on all
                                  subsystems [default: None].
            front (bool): If True compose using right operator multiplication,
                          instead of left multiplication [default: False].

        Returns:
            Chi: The quantum channel self @ other.

        Raises:
            QiskitError: if other has incompatible dimensions.

        Additional Information:
            Composition (``@``) is defined as `left` matrix multiplication for
            :class:`SuperOp` matrices. That is that ``A @ B`` is equal to ``B * A``.
            Setting ``front=True`` returns `right` matrix multiplication
            ``A * B`` and is equivalent to the :meth:`dot` method.
        """
        if qargs is not None:
            return Chi(SuperOp(self).compose(other, qargs=qargs, front=front))
        # If no qargs we compose via Choi representation to avoid an additional
        # representation conversion to SuperOp and then convert back to Chi
        return Chi(Choi(self).compose(other, front=front))
Exemple #15
0
    def _multiply(self, other):
        """Return the QuantumChannel other * self.

        Args:
            other (complex): a complex number.

        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")

        ret = copy.copy(self)
        # 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
            ret._data = Kraus(Choi(self)._multiply(other))._data
            return ret
        # If the number is real we can update the Kraus operators
        # directly
        val = np.sqrt(other)
        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]]
        ret._data = (kraus_l, kraus_r)
        return ret
Exemple #16
0
 def test_choi_compose(self):
     """Test compose of Choi matrices is correct."""
     mats = [self.matI, self.matX, self.matY, self.matZ, self.matH]
     chans = [
         Choi(mat) for mat in
         [self.choiI, self.choiX, self.choiY, self.choiZ, self.choiH]
     ]
     self._compare_compose_to_unitary(chans, mats)
Exemple #17
0
 def compose(self, other, qargs=None, front=False):
     if qargs is None:
         qargs = getattr(other, "qargs", None)
     if qargs is not None:
         return Chi(SuperOp(self).compose(other, qargs=qargs, front=front))
     # If no qargs we compose via Choi representation to avoid an additional
     # representation conversion to SuperOp and then convert back to Chi
     return Chi(Choi(self).compose(other, front=front))
Exemple #18
0
 def test_choi_to_operator(self):
     """Test Choi to Operator transformation."""
     # Test unitary channels
     for mat, choi in zip(self.unitary_mat, self.unitary_choi):
         chan1 = Operator(mat)
         chan2 = Operator(Choi(choi))
         self.assertTrue(
             matrix_equal(chan2.data, chan1.data, ignore_phase=True))
Exemple #19
0
 def test_choi_to_unitary(self):
     """Test Choi to UnitaryChannel transformation."""
     # Test unitary channels
     for mat, choi in zip(self.unitary_mat, self.unitary_choi):
         chan1 = UnitaryChannel(mat)
         chan2 = UnitaryChannel(Choi(choi))
         self.assertTrue(
             matrix_equal(chan2.data, chan1.data, ignore_phase=True))
 def test_choi_compose(self):
     """Test compose of Choi matrices is correct."""
     mats = [self.UI, self.UX, self.UY, self.UZ, self.UH]
     chans = [
         Choi(mat) for mat in
         [self.choiI, self.choiX, self.choiY, self.choiZ, self.choiH]
     ]
     self._compare_compose_to_operator(chans, mats)
Exemple #21
0
 def _choi_to_other_noncp(self, rep, qubits_test_cases, repetitions):
     """Test CP Choi to Other evolution."""
     for nq in qubits_test_cases:
         dim = 2**nq
         for _ in range(repetitions):
             rho = self.rand_rho(dim)
             mat = self.rand_matrix(dim**2, dim**2)
             chan = Choi(mat)
             rho1 = DensityMatrix(rho).evolve(chan).data
             rho2 = DensityMatrix(rho).evolve(rep(chan)).data
             assert_allclose(rho1, rho2)
Exemple #22
0
    def _evolve(self, state, qargs=None):
        """Evolve a quantum state by the QuantumChannel.

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

        Returns:
            DensityMatrix: the output quantum state as a density matrix.
        """
        return Choi(self)._evolve(state, qargs)
Exemple #23
0
 def _choi_to_other_noncp(self, rep, qubits_test_cases, repetitions):
     """Test CP Choi to Other evolution."""
     for nq in qubits_test_cases:
         dim = 2**nq
         for _ in range(repetitions):
             rho = self.rand_rho(dim)
             mat = self.rand_matrix(dim**2, dim**2)
             chan1 = Choi(mat)
             rho1 = chan1._evolve(rho)
             chan2 = rep(chan1)
             rho2 = chan2._evolve(rho)
             self.assertAllClose(rho1, rho2)
Exemple #24
0
    def _add(self, other):
        """Return the QuantumChannel self + other.

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

        Returns:
            Stinespring: the linear addition channel self + other.

        Raises:
            QiskitError: if other cannot be converted to a channel or
            has incompatible dimensions.
        """
        # Since we cannot directly add two channels in the Stinespring
        # representation we convert to the Choi representation
        return Stinespring(Choi(self).add(other))
Exemple #25
0
    def subtract(self, other):
        """Return the QuantumChannel self - other.

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

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

        Raises:
            QiskitError: if other cannot be converted to a channel or
            has incompatible dimensions.
        """
        # Since we cannot directly subtract two channels in the Stinespring
        # representation we convert to the Choi representation
        return Stinespring(Choi(self).subtract(other))
Exemple #26
0
    def _add(self, other):
        """Return the QuantumChannel self + other.

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

        Returns:
            Kraus: the linear addition channel self + other.

        Raises:
            QiskitError: if other cannot be converted to a channel, or
            has incompatible dimensions.
        """
        # 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))
Exemple #27
0
    def _multiply(self, other):
        if not isinstance(other, Number):
            raise QiskitError("other is not a number")

        ret = copy.copy(self)
        # 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
            ret._data = Kraus(Choi(self)._multiply(other))._data
            return ret
        # If the number is real we can update the Kraus operators
        # directly
        val = np.sqrt(other)
        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]]
        ret._data = (kraus_l, kraus_r)
        return ret
Exemple #28
0
    def _add(self, other, qargs=None):
        """Return the QuantumChannel self + other.

        If ``qargs`` are specified the other operator will be added
        assuming it is identity on all other subsystems.

        Args:
            other (QuantumChannel): a quantum channel subclass.
            qargs (None or list): optional subsystems to add on
                                  (Default: None)

        Returns:
            Stinespring: the linear addition channel self + other.

        Raises:
            QiskitError: if other cannot be converted to a channel or
                         has incompatible dimensions.
        """
        # Since we cannot directly add two channels in the Stinespring
        # representation we convert to the Choi representation
        return Stinespring(Choi(self)._add(other, qargs=qargs))
    def _multiply(self, other):
        if not isinstance(other, Number):
            raise QiskitError("other is not a number")

        ret = copy.copy(self)
        # 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
            ret._data = Stinespring(Choi(self)._multiply(other))._data
            return ret
        # If the number is real we can update the Kraus operators
        # directly
        num = np.sqrt(other)
        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]
        ret._data = (stine_l, stine_r)
        return ret
 def test_choi_conjugate(self):
     """Test conjugate of Choi matrices is correct."""
     mats = self.unitaries
     chans = [Choi(mat) for mat in self.chois]
     self._compare_conjugate_to_operator(chans, mats)