def test_inverse(self, class_name, gate_class):
        """Verify self-inverse pair yield identity for all standard gates."""

        free_params = _get_free_params(gate_class)
        n_params = len(free_params)
        float_vector = [0.1 + 0.1*i for i in range(n_params)]

        if class_name in ('MCPhaseGate', 'MCU1Gate'):
            float_vector = float_vector[:-1]
            gate = gate_class(*float_vector, num_ctrl_qubits=2)
        elif class_name in ('MCXGate', 'MCXGrayCode', 'MCXRecursive', 'MCXVChain'):
            num_ctrl_qubits = 3
            float_vector = float_vector[:-1]
            gate = gate_class(num_ctrl_qubits, *float_vector)
        elif class_name == 'MSGate':
            num_qubits = 3
            float_vector = float_vector[:-1]
            gate = gate_class(num_qubits, *float_vector)
        elif class_name == 'PauliGate':
            pauli_string = "IXYZ"
            gate = gate_class(pauli_string)
        else:
            gate = gate_class(*float_vector)

        from qiskit.quantum_info.operators.predicates import is_identity_matrix

        self.assertTrue(is_identity_matrix(Operator(gate).dot(gate.inverse()).data))

        if gate.definition is not None:
            self.assertTrue(is_identity_matrix(Operator(gate).dot(gate.definition.inverse()).data))
            self.assertTrue(is_identity_matrix(Operator(gate).dot(gate.inverse().definition).data))
예제 #2
0
    def ideal(self):
        """Return True if this error object is composed only of identity operations.
        Note that the identity check is best effort and up to global phase."""
        for circ in self.circuits:
            try:
                # Circuit-level identity check for clifford Circuits
                clifford = Clifford(circ)
                if clifford != Clifford(np.eye(2 * circ.num_qubits,
                                               dtype=bool)):
                    return False
            except QiskitError:
                pass

            # Component-wise check for non-Clifford circuits
            for op, _, _ in circ:
                if isinstance(op, IGate):
                    continue
                if isinstance(op, PauliGate):
                    if op.params[0].replace('I', ''):
                        return False
                else:
                    # Convert to Kraus and check if identity
                    kmats = Kraus(op).data
                    if len(kmats) > 1:
                        return False
                    if not is_identity_matrix(kmats[0],
                                              ignore_phase=True,
                                              atol=self.atol,
                                              rtol=self.rtol):
                        return False
        return True
예제 #3
0
 def is_cptp(self):
     """Return True if completely-positive trace-preserving."""
     # We convert to the Choi representation to check if CPTP
     if self._data[1] is not None:
         return False
     check = np.dot(np.transpose(np.conj(self._data[0])), self._data[0])
     return is_identity_matrix(check, rtol=self.rtol, atol=self.atol)
예제 #4
0
 def is_cptp(self):
     """Return True if completely-positive trace-preserving."""
     if self._data[1] is not None:
         return False
     accum = 0j
     for op in self._data[0]:
         accum += np.dot(np.transpose(np.conj(op)), op)
     return is_identity_matrix(accum, rtol=self.rtol, atol=self.atol)
예제 #5
0
 def _is_tp(self):
     """Test if Choi-matrix is trace-preserving (TP)"""
     # Check if the partial trace is the identity matrix
     d_in, d_out = self.dims
     mat = np.trace(
         np.reshape(self._data, (d_in, d_out, d_in, d_out)),
         axis1=1,
         axis2=3)
     return is_identity_matrix(mat, rtol=self.rtol, atol=self.atol)
예제 #6
0
 def is_cptp(self, atol=None, rtol=None):
     """Return True if completely-positive trace-preserving."""
     if atol is None:
         atol = self.atol
     if rtol is None:
         rtol = self.rtol
     if self._data[1] is not None:
         return False
     check = np.dot(np.transpose(np.conj(self._data[0])), self._data[0])
     return is_identity_matrix(check, rtol=self.rtol, atol=self.atol)
예제 #7
0
 def _is_tp(self, choi=None):
     """Test if Choi-matrix is trace-preserving (TP)"""
     if choi is None:
         choi = _to_choi(self.rep, self._data, *self.dim)
     # Check if the partial trace is the identity matrix
     d_in, d_out = self.dim
     mat = np.trace(np.reshape(choi, (d_in, d_out, d_in, d_out)),
                    axis1=1,
                    axis2=3)
     return is_identity_matrix(mat, rtol=self._rtol, atol=self._atol)
예제 #8
0
 def _is_tp_helper(self, choi, atol, rtol):
     """Test if Choi-matrix is trace-preserving (TP)"""
     if atol is None:
         atol = self.atol
     if rtol is None:
         rtol = self.rtol
     # Check if the partial trace is the identity matrix
     d_in, d_out = self.dim
     mat = np.trace(
         np.reshape(choi, (d_in, d_out, d_in, d_out)), axis1=1, axis2=3)
     return is_identity_matrix(mat, rtol=rtol, atol=atol)
예제 #9
0
def mixed_unitary_error(noise_ops, standard_gates=True):
    """
    Mixed unitary quantum error channel.

    The input should be a list of pairs (U[j], p[j]), where `U[j]` is a
    unitary matrix and `p[j]` is a probability. All probabilities must
    sum to 1 for the input ops to be valid.

    Args:
        noise_ops (list[pair[matrix, double]]): unitary error matricies.
        standard_gates (bool): Check if input matrices are standard gates.

    Returns:
        QuantumError: The quantum error object.

    Raises:
        NoiseError: if error parameters are invalid.
    """

    # Error checking
    if not isinstance(noise_ops, (list, tuple, zip)):
        raise NoiseError("Input noise ops is not a list.")

    # Convert to numpy arrays
    noise_ops = [(np.array(op, dtype=complex), p) for op, p in noise_ops]
    if not noise_ops:
        raise NoiseError("Input noise list is empty.")

    # Check for identity unitaries
    prob_identity = 0.
    instructions = []
    instructions_probs = []
    num_qubits = qubits_from_mat(noise_ops[0][0])
    qubits = list(range(num_qubits))
    for unitary, prob in noise_ops:
        # Check unitary
        if qubits_from_mat(unitary) != num_qubits:
            raise NoiseError("Input matrices different size.")
        if not is_unitary_matrix(unitary):
            raise NoiseError("Input matrix is not unitary.")
        if is_identity_matrix(unitary):
            prob_identity += prob
        else:
            instr = make_unitary_instruction(unitary,
                                             qubits,
                                             standard_gates=standard_gates)
            instructions.append(instr)
            instructions_probs.append(prob)
    if prob_identity > 0:
        instructions.append([{"name": "id", "qubits": [0]}])
        instructions_probs.append(prob_identity)
    return QuantumError(zip(instructions, instructions_probs))
예제 #10
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)
예제 #11
0
def mixed_unitary_error(noise_ops, standard_gates=None):
    """
    Return a mixed unitary quantum error channel.

    The input should be a list of pairs ``(U[j], p[j])``, where
    ``U[j]`` is a unitary matrix and ``p[j]`` is a probability. All
    probabilities must sum to 1 for the input ops to be valid.

    Args:
        noise_ops (list[pair[matrix, double]]): unitary error matrices.
        standard_gates (bool): DEPRECATED, Check if input matrices are standard gates.

    Returns:
        QuantumError: The quantum error object.

    Raises:
        NoiseError: if error parameters are invalid.
    """
    if standard_gates is not None:
        warnings.warn(
            '"standard_gates" option has been deprecated as of qiskit-aer 0.10.0'
            ' and will be removed no earlier than 3 months from that release date.'
            ' Use directly init e.g. QuantumError([(IGate(), prob1), (ZGate(), prob2)]) instead.',
            DeprecationWarning,
            stacklevel=2)

    # Error checking
    if not isinstance(noise_ops, (list, tuple, zip)):
        raise NoiseError("Input noise ops is not a list.")

    # Convert to numpy arrays
    noise_ops = [(np.array(op, dtype=complex), p) for op, p in noise_ops]
    if not noise_ops:
        raise NoiseError("Input noise list is empty.")

    # Check for identity unitaries
    prob_identity = 0.
    instructions = []
    instructions_probs = []
    num_qubits = int(np.log2(noise_ops[0][0].shape[0]))
    if noise_ops[0][0].shape != (2**num_qubits, 2**num_qubits):
        raise NoiseError(
            "A unitary matrix in input noise_ops is not a multi-qubit matrix.")
    for unitary, prob in noise_ops:
        # Check unitary
        if unitary.shape != noise_ops[0][0].shape:
            raise NoiseError("Input matrices different size.")
        if not is_unitary_matrix(unitary):
            raise NoiseError("Input matrix is not unitary.")
        if is_identity_matrix(unitary):
            prob_identity += prob
        else:
            if standard_gates:  # TODO: to be removed after deprecation period
                qubits = list(range(num_qubits))
                instr = _make_unitary_instruction(
                    unitary, qubits, standard_gates=standard_gates)
            else:
                instr = UnitaryGate(unitary)
            instructions.append(instr)
            instructions_probs.append(prob)
    if prob_identity > 0:
        if standard_gates:  # TODO: to be removed after deprecation period
            instructions.append([{"name": "id", "qubits": [0]}])
        else:
            instructions.append(IGate())
        instructions_probs.append(prob_identity)
    return QuantumError(zip(instructions, instructions_probs))