예제 #1
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))
예제 #2
0
 def test_operator_to_kraus(self):
     """Test Operator to Kraus transformation."""
     # Test unitary channels
     for mat in self.unitary_mat:
         chan1 = Kraus(mat)
         chan2 = Kraus(Operator(mat))
         self.assertEqual(chan1, chan2)
예제 #3
0
 def test_dot_both_kraus(self):
     """Test dot 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([b_0, b_1]).compose(Kraus([a_0, a_1])))
     # dot method
     error = QuantumError([a_0, a_1]).dot(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])
     error_superop = SuperOp(Kraus(kraus[0]['params']))
     self.assertEqual(target,
                      error_superop,
                      msg="Incorrect kraus dot method")
     # * method
     error = QuantumError([a_0, a_1]) * 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])
     error_superop = SuperOp(Kraus(kraus[0]['params']))
     self.assertEqual(target,
                      error_superop,
                      msg="Incorrect kraus dot method")
예제 #4
0
 def test_unitary_to_kraus(self):
     """Test UnitaryChannel to Kraus transformation."""
     # Test unitary channels
     for mat in self.unitary_mat:
         chan1 = Kraus(mat)
         chan2 = Kraus(UnitaryChannel(mat))
         self.assertEqual(chan1, chan2)
예제 #5
0
 def test_kraus_to_operator(self):
     """Test Kraus to Operator transformation."""
     for mat in self.unitary_mat:
         chan1 = Operator(mat)
         chan2 = Operator(Kraus(mat))
         self.assertTrue(
             matrix_equal(chan2.data, chan1.data, ignore_phase=True))
     self.assertRaises(QiskitError, Operator, Kraus(self.depol_kraus(0.5)))
예제 #6
0
 def test_kraus_to_unitary(self):
     """Test Kraus to UnitaryChannel transformation."""
     for mat in self.unitary_mat:
         chan1 = UnitaryChannel(mat)
         chan2 = UnitaryChannel(Kraus(mat))
         self.assertTrue(
             matrix_equal(chan2.data, chan1.data, ignore_phase=True))
     self.assertRaises(QiskitError, UnitaryChannel,
                       Kraus(self.depol_kraus(0.5)))
예제 #7
0
 def test_to_quantumchannel_kraus(self):
     """Test to_quantumchannel for Kraus inputs."""
     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)
     target = SuperOp(Kraus([a_0, a_1])).tensor(SuperOp(Kraus([b_0, b_1])))
     error = QuantumError([a_0, a_1]).tensor(QuantumError([b_0, b_1]))
     self.assertEqual(target, error.to_quantumchannel())
예제 #8
0
 def test_to_channel_kraus(self):
     """Test to_channel for Kraus inputs."""
     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)
     target = SuperOp(Kraus([A0, A1])).tensor(SuperOp(Kraus([B0, B1])))
     error = QuantumError([A0, A1]).tensor(QuantumError([B0, B1]))
     self.assertEqual(target, error.to_channel())
예제 #9
0
 def test_kraus_to_ptm(self):
     """Test Kraus to PTM transformation."""
     # Test unitary channels
     for mat, ptm in zip(self.unitary_mat, self.unitary_ptm):
         chan1 = PTM(ptm)
         chan2 = PTM(Kraus(mat))
         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(Kraus(self.depol_kraus(p)))
         self.assertEqual(chan1, chan2)
예제 #10
0
 def test_stinespring_to_kraus(self):
     """Test Stinespring to Kraus transformation."""
     # Test unitary channels
     for mat in self.unitary_mat:
         chan1 = Kraus(mat)
         chan2 = Kraus(Stinespring(mat))
         self.assertEqual(chan1, chan2)
     # Test depolarizing channels
     for p in [0.25, 0.5, 0.75, 1]:
         chan1 = Kraus(self.depol_kraus(p))
         chan2 = Kraus(Stinespring(self.depol_stine(p)))
         self.assertEqual(chan1, chan2)
예제 #11
0
 def test_kraus_to_chi(self):
     """Test Kraus to Chi transformation."""
     # Test unitary channels
     for mat, chi in zip(self.unitary_mat, self.unitary_chi):
         chan1 = Chi(chi)
         chan2 = Chi(Kraus(mat))
         self.assertEqual(chan1, chan2)
     # Test depolarizing channels
     for p in [0.25, 0.5, 0.75, 1]:
         chan1 = Chi(self.depol_chi(p))
         chan2 = Chi(Kraus(self.depol_kraus(p)))
         self.assertEqual(chan1, chan2)
예제 #12
0
 def test_kraus_to_superop(self):
     """Test Kraus 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(Kraus(self.depol_kraus(p)))
         self.assertEqual(chan1, chan2)
예제 #13
0
 def _kraus_to_other_single(self, rep, qubits_test_cases, repetitions):
     """Test single Kraus to Other evolution."""
     for nq in qubits_test_cases:
         dim = 2**nq
         for _ in range(repetitions):
             rho = self.rand_rho(dim)
             kraus = self.rand_kraus(dim, dim, dim**2)
             chan1 = Kraus(kraus)
             rho1 = chan1._evolve(rho)
             chan2 = rep(chan1)
             rho2 = chan2._evolve(rho)
             self.assertAllClose(rho1, rho2)
예제 #14
0
 def test_ptm_to_stinespring(self):
     """Test PTM to Stinespring transformation."""
     # Test unitary channels
     for mat, ptm in zip(self.unitary_mat, self.unitary_ptm):
         chan1 = Kraus(mat)
         chan2 = Kraus(PTM(ptm))
         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(PTM(self.depol_ptm(p))))
         self.assertEqual(output, target)
예제 #15
0
 def test_chi_to_kraus(self):
     """Test Chi to Kraus transformation."""
     # Test unitary channels
     for mat, chi in zip(self.unitary_mat, self.unitary_chi):
         chan1 = Kraus(mat)
         chan2 = Kraus(Chi(chi))
         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(Kraus(self.depol_kraus(p)))
         output = rho.evolve(Kraus(Chi(self.depol_chi(p))))
         self.assertEqual(output, target)
예제 #16
0
 def test_kraus_to_stinespring(self):
     """Test Kraus to Stinespring transformation."""
     # Test unitary channels
     for mat in self.unitary_mat:
         chan1 = Stinespring(mat)
         chan2 = Stinespring(Kraus(mat))
         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(Kraus(self.depol_kraus(p)))
         self.assertAllClose(chan._evolve(rho), targ)
예제 #17
0
 def test_ptm_to_stinespring(self):
     """Test PTM to Stinespring transformation."""
     # Test unitary channels
     for mat, ptm in zip(self.unitary_mat, self.unitary_ptm):
         chan1 = Kraus(mat)
         chan2 = Kraus(PTM(ptm))
         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(PTM(self.depol_ptm(p)))
         self.assertAllClose(chan._evolve(rho), targ)
예제 #18
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")
예제 #19
0
def standard_instruction_channel(instr):
    """Return the SuperOp channel for a standard instruction."""
    warnings.warn(
        'standard_instruction_channel has been deprecated as of qiskit-aer 0.10.0'
        ' and will be removed no earlier than 3 months from that release date.',
        DeprecationWarning, stacklevel=2)
    # Check if standard operator
    oper = standard_instruction_operator(instr)
    if oper is not None:
        return SuperOp(oper)

    # Convert to dict (for QobjInstruction types)
    if hasattr(instr, 'as_dict'):
        instr = instr.as_dict()
    # Get name and parameters
    name = instr.get('name', "")

    # Check if reset instruction
    if name == 'reset':
        # params should be the number of qubits being reset
        num_qubits = len(instr['qubits'])
        with warnings.catch_warnings():
            warnings.simplefilter("ignore")
            res = reset_superop(num_qubits)
        return res
    # Check if Kraus instruction
    if name == 'kraus':
        params = instr['params']
        return SuperOp(Kraus(params))
    return None
예제 #20
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))
 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))
예제 #22
0
def make_kraus_instruction(mats, qubits):
    """Return a qobj instruction for a Kraus error.

    Args:
        mats (list[matrix]): A list of square or diagonal Kraus matrices.
        qubits (list[int]): The qubits the matrix is applied to.
    Returns:
        dict: The qobj instruction object.

    Raises:
        NoiseError: if the input is not a CPTP Kraus channel.
    """
    kraus = Kraus(mats)
    if not kraus.is_cptp() or kraus._input_dim != kraus._output_dim:
        raise NoiseError("Input Kraus matrices are not a CPTP channel.")
    if isinstance(qubits, int):
        qubits = [qubits]
    return [{"name": "kraus", "qubits": qubits, "params": kraus.data}]
예제 #23
0
 def _kraus_to_other_single(self, rep, qubits_test_cases, repetitions):
     """Test single Kraus to Other evolution."""
     for nq in qubits_test_cases:
         dim = 2**nq
         for _ in range(repetitions):
             rho = self.rand_rho(dim)
             kraus = self.rand_kraus(dim, dim, dim**2)
             chan = Kraus(kraus)
             rho1 = DensityMatrix(rho).evolve(chan).data
             rho2 = DensityMatrix(rho).evolve(rep(chan)).data
             assert_allclose(rho1, rho2)
예제 #24
0
 def _kraus_to_other_double(self, rep, qubits_test_cases, repetitions):
     """Test double Kraus to Other evolution."""
     for nq in qubits_test_cases:
         dim = 2**nq
         for _ in range(repetitions):
             rho = self.rand_rho(dim)
             kraus_l = self.rand_kraus(dim, dim, dim**2)
             kraus_r = self.rand_kraus(dim, dim, dim**2)
             chan = Kraus((kraus_l, kraus_r))
             rho1 = DensityMatrix(rho).evolve(chan).data
             rho2 = DensityMatrix(rho).evolve(rep(chan)).data
             self.assertAllClose(rho1, rho2)
예제 #25
0
def make_kraus_instruction(mats, qubits):
    """Return a qobj instruction for a Kraus error.

    Args:
        mats (list[matrix]): A list of square or diagonal Kraus matrices.
        qubits (list[int]): The qubits the matrix is applied to.
    Returns:
        dict: The qobj instruction object.

    Raises:
        NoiseError: if the input is not a CPTP Kraus channel.
    """
    warnings.warn(
        'make_kraus_instruction has been deprecated as of qiskit-aer 0.10.0'
        ' and will be removed no earlier than 3 months from that release date.',
        DeprecationWarning, stacklevel=2)

    kraus = Kraus(mats)
    if not kraus.is_cptp() or kraus._input_dim != kraus._output_dim:
        raise NoiseError("Input Kraus matrices are not a CPTP channel.")
    if isinstance(qubits, int):
        qubits = [qubits]
    return [{"name": "kraus", "qubits": qubits, "params": kraus.data}]
예제 #26
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:
            Stinespring: The composition channel as a Stinespring object.

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

        # Convert other to Kraus
        if not isinstance(other, Kraus):
            other = Kraus(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 compose two channels in Stinespring
        # representation we convert to the Kraus representation
        return Stinespring(
            Kraus(self)._chanmul(other, left_multiply=left_multiply))
예제 #27
0
 def test_chi_to_kraus(self):
     """Test Chi to Kraus transformation."""
     # Test unitary channels
     for mat, chi in zip(self.unitary_mat, self.unitary_chi):
         chan1 = Kraus(mat)
         chan2 = Kraus(Chi(chi))
         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 = Kraus(self.depol_kraus(p))._evolve(rho)
         chan = Kraus(Chi(self.depol_chi(p)))
         self.assertAllClose(chan._evolve(rho), targ)
예제 #28
0
def circuit2superop(circuit, min_qubits=1):
    """Return the SuperOp for a standard instruction."""
    # Get number of qubits
    max_qubits = 1
    for instr in circuit:
        qubits = []
        if hasattr(instr, 'qubits'):
            qubits = instr.qubits
        elif isinstance(instr, dict):
            qubits = instr.get('qubits', [])
        max_qubits = max(max_qubits, 1 + max(qubits))

    num_qubits = max(max_qubits, min_qubits)

    # Initialize N-qubit identity superoperator
    superop = SuperOp(np.eye(4**num_qubits))
    # compose each circuit element with the superoperator
    for instr in circuit:
        name = None
        qubits = None
        params = None
        if isinstance(instr, dict):
            # Parse from plain dictionary qobj instruction
            name = instr['name']
            qubits = instr.get('qubits')
            params = instr.get('params')
        else:
            # Parse from QasmQobjInstruction
            if hasattr(instr, 'name'):
                name = instr.name
            if hasattr(instr, 'qubits'):
                qubits = instr.qubits
            if hasattr(instr, 'params'):
                params = instr.params
        if name is 'reset':
            instr_op = reset_superop(len(qubits))
        elif name is 'kraus':
            instr_op = Kraus(params)
        else:
            instr_op = SuperOp(standard_instruction_operator(name, params))
        superop = superop.compose(instr_op, qubits=qubits)
    return superop
예제 #29
0
def standard_instruction_channel(instr):
    """Return the SuperOp channel for a standard instruction."""
    # Check if standard operator
    oper = standard_instruction_operator(instr)
    if oper is not None:
        return SuperOp(oper)

    # Convert to dict (for QobjInstruction types)
    if hasattr(instr, 'as_dict'):
        instr = instr.as_dict()
    # Get name and parameters
    name = instr.get('name', "")

    # Check if reset instruction
    if name == 'reset':
        # params should be the number of qubits being reset
        num_qubits = len(instr['qubits'])
        return reset_superop(num_qubits)
    # Check if Kraus instruction
    if name == 'kraus':
        params = instr['params']
        return SuperOp(Kraus(params))
    return None
예제 #30
0
def kraus2instructions(kraus_ops, standard_gates, atol=ATOL_DEFAULT):
    """
    Convert a list of Kraus matrices into qobj circuits.

    If any Kraus operators are a unitary matrix they will be converted
    into unitary qobj instructions. Identity unitary matrices will also be
    converted into identity qobj instructions.

    Args:
        kraus_ops (list[matrix]): A list of Kraus matrices for a CPTP map.
        standard_gates (bool): Check if the matrix instruction is a
                               standard instruction (default: True).
        atol (double): Threshold for testing if probabilities are zero.


    Returns:
        list: A list of pairs (p, circuit) where `circuit` is a list of qobj
        instructions, and `p` is the probability of that circuit for the
        given error.

    Raises:
        NoiseError: If the input Kraus channel is not CPTP.
    """
    # Check threshold
    if atol < 0:
        raise NoiseError("atol cannot be negative")
    if atol > 1e-5:
        raise NoiseError(
            "atol value is too large. It should be close to zero.")

    # Check CPTP
    if not Kraus(kraus_ops).is_cptp(atol=atol):
        raise NoiseError("Input Kraus channel is not CPTP.")

    # Get number of qubits
    num_qubits = int(np.log2(len(kraus_ops[0])))
    if len(kraus_ops[0]) != 2**num_qubits:
        raise NoiseError("Input Kraus channel is not a multi-qubit channel.")

    # Check if each matrix is a:
    # 1. scaled identity matrix
    # 2. scaled non-identity unitary matrix
    # 3. a non-unitary Kraus operator

    # Probabilities
    prob_identity = 0
    prob_unitary = 0  # total probability of all unitary ops (including id)
    prob_kraus = 0  # total probability of non-unitary ops
    probabilities = []  # initialize with probability of Identity

    # Matrices
    unitaries = []  # non-identity unitaries
    non_unitaries = []  # non-unitary Kraus matrices

    for mat in kraus_ops:
        # Get the value of the maximum diagonal element
        # of op.H * op for rescaling
        prob = abs(max(np.diag(np.conj(np.transpose(mat)).dot(mat))))
        if prob > 0.0:
            if abs(prob - 1) > 0.0:
                # Rescale the operator by square root of prob
                rescaled_mat = np.array(mat) / np.sqrt(prob)
            else:
                rescaled_mat = mat
            # Check if identity operator
            if is_identity_matrix(rescaled_mat, ignore_phase=True):
                prob_identity += prob
                prob_unitary += prob
            # Check if unitary
            elif is_unitary_matrix(rescaled_mat):
                probabilities.append(prob)
                prob_unitary += prob
                unitaries.append(rescaled_mat)
            # Non-unitary op
            else:
                non_unitaries.append(mat)

    # Check probabilities
    prob_kraus = 1 - prob_unitary
    if prob_unitary - 1 > atol:
        raise NoiseError("Invalid kraus matrices: unitary probability "
                         "{} > 1".format(prob_unitary))
    if prob_unitary < -atol:
        raise NoiseError("Invalid kraus matrices: unitary probability "
                         "{} < 1".format(prob_unitary))
    if prob_identity - 1 > atol:
        raise NoiseError("Invalid kraus matrices: identity probability "
                         "{} > 1".format(prob_identity))
    if prob_identity < -atol:
        raise NoiseError("Invalid kraus matrices: identity probability "
                         "{} < 1".format(prob_identity))
    if prob_kraus - 1 > atol:
        raise NoiseError("Invalid kraus matrices: non-unitary probability "
                         "{} > 1".format(prob_kraus))
    if prob_kraus < -atol:
        raise NoiseError("Invalid kraus matrices: non-unitary probability "
                         "{} < 1".format(prob_kraus))

    # Build qobj instructions
    instructions = []
    qubits = list(range(num_qubits))

    # Add unitary instructions
    for unitary in unitaries:
        instructions.append(
            make_unitary_instruction(
                unitary, qubits, standard_gates=standard_gates))

    # Add identity instruction
    if prob_identity > atol:
        if abs(prob_identity - 1) < atol:
            probabilities.append(1)
        else:
            probabilities.append(prob_identity)
        instructions.append([{"name": "id", "qubits": [0]}])

    # Add Kraus
    if prob_kraus < atol:
        # No Kraus operators
        return zip(instructions, probabilities)
    if prob_kraus < 1:
        # Rescale kraus operators by probabilities
        non_unitaries = [
            np.array(op) / np.sqrt(prob_kraus) for op in non_unitaries
        ]
    instructions.append(make_kraus_instruction(non_unitaries, qubits))
    probabilities.append(prob_kraus)
    # Normalize probabilities to account for any rounding errors
    probabilities = list(np.array(probabilities) / np.sum(probabilities))
    return zip(instructions, probabilities)