Ejemplo n.º 1
0
 def _compare_multiply_to_superop(self, rep, dim, samples, unitary=False):
     """Test channel scalar multiplication is equivalent to SuperOp"""
     for _ in range(samples):
         if unitary:
             mat1 = self.rand_matrix(dim, dim)
             sop1 = np.kron(np.conj(mat1), mat1)
         else:
             sop1 = self.rand_matrix(dim * dim, dim * dim)
         val = 2 * (np.random.rand() - 0.5)
         targ = SuperOp(val * sop1)
         channel = SuperOp(rep(SuperOp(sop1)).multiply(val))
         self.assertEqual(channel, targ)
Ejemplo n.º 2
0
 def test_ptm_to_superop(self):
     """Test PTM to SuperOp transformation."""
     # Test unitary channels
     for ptm, sop in zip(self.unitary_ptm, self.unitary_sop):
         chan1 = SuperOp(sop)
         chan2 = SuperOp(PTM(ptm))
         self.assertEqual(chan1, chan2)
     # Test depolarizing channels
     for p in [0.25, 0.5, 0.75, 1]:
         chan1 = SuperOp(self.depol_sop(p))
         chan2 = SuperOp(PTM(self.depol_ptm(p)))
         self.assertEqual(chan1, chan2)
Ejemplo n.º 3
0
 def _superop_to_other(self, rep, qubits_test_cases, repetitions):
     """Test SuperOp 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 = SuperOp(mat)
             rho1 = chan1._evolve(rho)
             chan2 = rep(chan1)
             rho2 = chan2._evolve(rho)
             self.assertAllClose(rho1, rho2)
Ejemplo n.º 4
0
 def test_chi_to_superop(self):
     """Test Chi to SuperOp transformation."""
     # Test unitary channels
     for chi, sop in zip(self.unitary_chi, self.unitary_sop):
         chan1 = SuperOp(sop)
         chan2 = SuperOp(Chi(chi))
         self.assertEqual(chan1, chan2)
     # Test depolarizing channels
     for p in [0.25, 0.5, 0.75, 1]:
         chan1 = SuperOp(self.depol_sop(p))
         chan2 = SuperOp(Chi(self.depol_chi(p)))
         self.assertEqual(chan1, chan2)
Ejemplo n.º 5
0
 def test_stinespring_to_superop(self):
     """Test Stinespring to SuperOp transformation."""
     # Test unitary channels
     for mat, sop in zip(self.unitary_mat, self.unitary_sop):
         chan1 = SuperOp(sop)
         chan2 = SuperOp(Kraus(mat))
         self.assertEqual(chan1, chan2)
     # Test depolarizing channels
     for p in [0.25, 0.5, 0.75, 1]:
         chan1 = SuperOp(self.depol_sop(p))
         chan2 = SuperOp(Stinespring(self.depol_stine(p)))
         self.assertEqual(chan1, chan2)
Ejemplo n.º 6
0
 def test_superop_to_ptm(self):
     """Test SuperOp to PTM transformation."""
     # Test unitary channels
     for sop, ptm in zip(self.unitary_sop, self.unitary_ptm):
         chan1 = PTM(ptm)
         chan2 = PTM(SuperOp(sop))
         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(SuperOp(self.depol_sop(p)))
         self.assertEqual(chan1, chan2)
Ejemplo n.º 7
0
 def test_superop_to_superop(self):
     """Test SuperOp to SuperOp transformation."""
     # Test unitary channels
     for sop in self.unitary_sop:
         chan1 = SuperOp(sop)
         chan2 = SuperOp(chan1)
         self.assertEqual(chan1, chan2)
     # Test depolarizing channels
     for p in [0, 0.25, 0.5, 0.75, 1]:
         chan1 = SuperOp(self.depol_sop(p))
         chan2 = SuperOp(chan1)
         self.assertEqual(chan1, chan2)
Ejemplo n.º 8
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)
Ejemplo n.º 9
0
 def test_superop_to_stinespring(self):
     """Test SuperOp to Stinespring transformation."""
     # Test unitary channels
     for mat, sop in zip(self.unitary_mat, self.unitary_sop):
         chan1 = Stinespring(mat)
         chan2 = Stinespring(SuperOp(sop))
         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(SuperOp(self.depol_sop(p))))
         self.assertEqual(output, target)
Ejemplo n.º 10
0
 def test_superop_to_stinespring(self):
     """Test SuperOp to Stinespring transformation."""
     # Test unitary channels
     for mat, sop in zip(self.unitary_mat, self.unitary_sop):
         chan1 = Stinespring(mat)
         chan2 = Stinespring(SuperOp(sop))
         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(SuperOp(self.depol_sop(p)))
         self.assertAllClose(chan._evolve(rho), targ)
Ejemplo n.º 11
0
 def _compare_subtract_to_superop(self, rep, dim, samples, unitary=False):
     """Test channel subtraction is equivalent to SuperOp"""
     for _ in range(samples):
         if unitary:
             mat1 = self.rand_matrix(dim, dim)
             mat2 = self.rand_matrix(dim, dim)
             sop1 = np.kron(np.conj(mat1), mat1)
             sop2 = np.kron(np.conj(mat2), mat2)
         else:
             sop1 = self.rand_matrix(dim * dim, dim * dim)
             sop2 = self.rand_matrix(dim * dim, dim * dim)
         targ = SuperOp(sop1 - sop2)
         channel = SuperOp(rep(SuperOp(sop1))._add(rep(-SuperOp(sop2))))
         self.assertEqual(channel, targ)
Ejemplo n.º 12
0
 def test_compose_both_kraus(self):
     """Test composition of two kraus errors"""
     A0 = np.array([[1, 0], [0, np.sqrt(1 - 0.3)]], dtype=complex)
     A1 = np.array([[0, 0], [0, np.sqrt(0.3)]], dtype=complex)
     B0 = np.array([[1, 0], [0, np.sqrt(1 - 0.5)]], dtype=complex)
     B1 = np.array([[0, 0], [0, np.sqrt(0.5)]], dtype=complex)
     # Use quantum channels for reference
     target = SuperOp(Kraus([A0, A1]).compose(Kraus([B0, B1])))
     error = QuantumError([A0, A1]).compose(QuantumError([B0, B1]))
     kraus, p = error.error_term(0)
     self.assertEqual(p, 1)
     self.assertEqual(kraus[0]['name'], 'kraus')
     self.assertEqual(kraus[0]['qubits'], [0])
     error_superop = SuperOp(Kraus(kraus[0]['params']))
     self.assertEqual(target, error_superop, msg="Incorrect compose kraus")
Ejemplo n.º 13
0
 def test_expand_both_kraus(self):
     """Test expand of two kraus errors"""
     a_0 = np.array([[1, 0], [0, np.sqrt(1 - 0.3)]], dtype=complex)
     a_1 = np.array([[0, 0], [0, np.sqrt(0.3)]], dtype=complex)
     b_0 = np.array([[1, 0], [0, np.sqrt(1 - 0.5)]], dtype=complex)
     b_1 = np.array([[0, 0], [0, np.sqrt(0.5)]], dtype=complex)
     # Use quantum channels for reference
     target = SuperOp(Kraus([a_0, a_1]).expand(Kraus([b_0, b_1])))
     error = QuantumError([a_0, a_1]).expand(QuantumError([b_0, b_1]))
     kraus, prob = error.error_term(0)
     self.assertEqual(prob, 1)
     self.assertEqual(kraus[0]['name'], 'kraus')
     self.assertEqual(kraus[0]['qubits'], [0, 1])
     error_superop = SuperOp(Kraus(kraus[0]['params']))
     self.assertEqual(target, error_superop, msg="Incorrect expand kraus")
Ejemplo n.º 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:
            PTM: 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 PTM(SuperOp(self).compose(other, qargs=qargs, front=front))

        # Convert other to PTM
        if not isinstance(other, PTM):
            other = PTM(other)
        input_dims, output_dims = self._get_compose_dims(other, qargs, front)
        if front:
            data = np.dot(self._data, other.data)
        else:
            data = np.dot(other.data, self._data)
        return PTM(data, input_dims, output_dims)
Ejemplo n.º 15
0
    def compose(self, other, qargs=None, front=False):
        if qargs is None:
            qargs = getattr(other, "qargs", None)
        if qargs is not None:
            return Choi(SuperOp(self).compose(other, qargs=qargs, front=front))

        if not isinstance(other, Choi):
            other = Choi(other)
        new_shape = self._op_shape.compose(other._op_shape, qargs, front)
        output_dim, input_dim = new_shape.shape

        if front:
            first = np.reshape(other._data, other._bipartite_shape)
            second = np.reshape(self._data, self._bipartite_shape)
        else:
            first = np.reshape(self._data, self._bipartite_shape)
            second = np.reshape(other._data, other._bipartite_shape)

        # Contract Choi matrices for composition
        data = np.reshape(
            np.einsum("iAjB,AkBl->ikjl", first, second),
            (input_dim * output_dim, input_dim * output_dim),
        )
        ret = Choi(data)
        ret._op_shape = new_shape
        return ret
Ejemplo n.º 16
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))
Ejemplo n.º 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 Kraus(
                SuperOp(self).compose(other, qargs=qargs, front=front))

        if not isinstance(other, Kraus):
            other = Kraus(other)
        new_shape = self._op_shape.compose(other._op_shape, qargs, front)
        input_dims = new_shape.dims_r()
        output_dims = new_shape.dims_l()

        if front:
            ka_l, ka_r = self._data
            kb_l, kb_r = other._data
        else:
            ka_l, ka_r = other._data
            kb_l, kb_r = self._data

        kab_l = [np.dot(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
        elif ka_r is None:
            kab_r = [np.dot(a, b) for a in ka_l for b in kb_r]
        elif kb_r is None:
            kab_r = [np.dot(a, b) for a in ka_r for b in kb_l]
        else:
            kab_r = [np.dot(a, b) for a in ka_r for b in kb_r]
        ret = Kraus((kab_l, kab_r), input_dims, output_dims)
        ret._op_shape = new_shape
        return ret
Ejemplo n.º 18
0
    def compose(self, other, qargs=None, front=False):
        """Return the composition channel self∘other.

        Args:
            other (QuantumChannel): a quantum channel subclass.
            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:
            Stinespring: The composition channel as a Stinespring object.

        Raises:
            QiskitError: if other cannot be converted to a channel or
            has incompatible dimensions.
        """
        if qargs is not None:
            return Stinespring(
                SuperOp(self).compose(other, qargs=qargs, front=front))

        # Convert other to Kraus
        if not isinstance(other, Kraus):
            other = Kraus(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 compose two channels in Stinespring
        # representation we convert to the Kraus representation
        return Stinespring(Kraus(self).compose(other, front=front))
Ejemplo n.º 19
0
    def _append_instruction(self, other, qargs=None):
        """Update the current Statevector by applying an instruction."""

        # Try evolving by a matrix operator (unitary-like evolution)
        mat = Operator._instruction_to_matrix(other)
        if mat is not None:
            self._data = self._evolve_operator(Operator(mat), qargs=qargs).data
            return
        # Otherwise try evolving by a Superoperator
        chan = SuperOp._instruction_to_superop(other)
        if chan is not None:
            # Evolve current state by the superoperator
            self._data = chan._evolve(self, qargs=qargs).data
            return
        # If the instruction doesn't have a matrix defined we use its
        # circuit decomposition definition if it exists, otherwise we
        # cannot compose this gate and raise an error.
        if other.definition is None:
            raise QiskitError('Cannot apply Instruction: {}'.format(
                other.name))
        for instr, qregs, cregs in other.definition:
            if cregs:
                raise QiskitError(
                    'Cannot apply instruction with classical registers: {}'.
                    format(instr.name))
            # Get the integer position of the flat register
            if qargs is None:
                new_qargs = [tup.index for tup in qregs]
            else:
                new_qargs = [qargs[tup.index] for tup in qregs]
            self._append_instruction(instr, qargs=new_qargs)
Ejemplo n.º 20
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))
Ejemplo n.º 21
0
 def _compare_subtract_operator_to_superop(self,
                                           rep,
                                           dim,
                                           samples,
                                           unitary=False):
     """Test channel addition is equivalent to SuperOp"""
     for _ in range(samples):
         if unitary:
             mat1 = self.rand_matrix(dim, dim)
             sop1 = np.kron(np.conj(mat1), mat1)
         else:
             sop1 = self.rand_matrix(dim * dim, dim * dim)
         mat2 = self.rand_matrix(dim, dim)
         target = SuperOp(sop1) - SuperOp(Operator(mat2))
         channel = SuperOp(rep(SuperOp(sop1)) - Operator(mat2))
         self.assertEqual(channel, target)
Ejemplo n.º 22
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))
Ejemplo n.º 23
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.

        Raises:
            QiskitError: if the operator dimension does not match the
            specified QuantumState subsystem dimensions.
        """
        # If subsystem evolution we use the SuperOp representation
        if qargs is not None:
            return SuperOp(self)._evolve(state, qargs)
        # Otherwise we compute full evolution directly
        state = self._format_state(state, density_matrix=True)
        if state.shape[0] != self._input_dim:
            raise QiskitError(
                "QuantumChannel input dimension is not equal to state dimension."
            )
        return np.einsum('AB,AiBj->ij', state,
                         np.reshape(self._data, self._bipartite_shape))
Ejemplo n.º 24
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:
            Stinespring: The quantum channel self @ other.

        Raises:
            QiskitError: if other cannot be converted to a Stinespring or 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 None:
            qargs = getattr(other, 'qargs', None)
        if qargs is not None:
            return Stinespring(
                SuperOp(self).compose(other, qargs=qargs, front=front))

        # Otherwise we convert via Kraus representation rather than
        # superoperator to avoid unnecessary representation conversions
        return Stinespring(Kraus(self).compose(other, front=front))
Ejemplo n.º 25
0
 def test_superop_compose(self):
     """Test compose of SuperOp matrices is correct."""
     mats = [self.matI, self.matX, self.matY, self.matZ, self.matH]
     chans = [
         SuperOp(mat)
         for mat in [self.sopI, self.sopX, self.sopY, self.sopZ, self.sopH]
     ]
     self._compare_compose_to_unitary(chans, mats)
Ejemplo n.º 26
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))
 def test_superop_compose(self):
     """Test compose of SuperOp matrices is correct."""
     mats = [self.UI, self.UX, self.UY, self.UZ, self.UH]
     chans = [
         SuperOp(mat)
         for mat in [self.sopI, self.sopX, self.sopY, self.sopZ, self.sopH]
     ]
     self._compare_compose_to_operator(chans, mats)
 def compose(self, other, qargs=None, front=False):
     if qargs is None:
         qargs = getattr(other, "qargs", None)
     if qargs is not None:
         return Stinespring(
             SuperOp(self).compose(other, qargs=qargs, front=front))
     # Otherwise we convert via Kraus representation rather than
     # superoperator to avoid unnecessary representation conversions
     return Stinespring(Kraus(self).compose(other, front=front))
Ejemplo n.º 29
0
def reset_superop(num_qubits):
    """Return a N-qubit reset SuperOp."""
    reset = SuperOp(
        np.array([[1, 0, 0, 1], [0, 0, 0, 0], [0, 0, 0, 0], [0, 0, 0, 0]]))
    if num_qubits == 1:
        return reset
    reset_n = reset
    for _ in range(num_qubits - 1):
        reset_n.tensor(reset)
    return reset_n
Ejemplo n.º 30
0
 def to_channel(self):
     """Convet the QuantumError to a SuperOp quantum channel."""
     # Initialize as an empty superoperator of the correct size
     dim = 2**self.number_of_qubits
     channel = SuperOp(np.zeros([dim * dim, dim * dim]))
     for circuit, prob in zip(self._noise_circuits,
                              self._noise_probabilities):
         component = prob * circuit2superop(circuit, self.number_of_qubits)
         channel = channel + component
     return channel