Ejemplo n.º 1
0
    def test_clifford_circuit_noise(self, method, device):
        """Test simulation with mixed Clifford quantum errors in circuit."""
        backend = self.backend(method=method, device=device)
        shots = 1000
        error1 = noise.QuantumError([
            ([(IGate(), [0])], 0.8),
            ([(Reset(), [0])], 0.1),
            ([(HGate(), [0])], 0.1)])

        error2 = noise.QuantumError([
            ([(IGate(), [0])], 0.75),
            ([(Reset(), [0])], 0.1),
            ([(Reset(), [1])], 0.1),
            ([(Reset(), [0]), (Reset(), [1])], 0.05)])

        qc = QuantumCircuit(2)
        qc.h(0)
        qc.append(error1, [0])
        qc.cx(0, 1)
        qc.append(error2, [0, 1])
        target_probs = qi.DensityMatrix(qc).probabilities_dict()

        # Add measurement
        qc.measure_all()
        result = backend.run(qc, shots=shots).result()
        self.assertSuccess(result)
        probs = {key: val / shots for key, val in result.get_counts(0).items()}
        self.assertDictAlmostEqual(target_probs, probs, delta=0.1)
Ejemplo n.º 2
0
    def to_instruction(self):
        """Convert to Pauli circuit instruction."""
        from math import pi

        pauli, phase = self._to_label(self.z,
                                      self.x,
                                      self._phase[0],
                                      full_group=False,
                                      return_phase=True)
        if len(pauli) == 1:
            gate = {
                "I": IGate(),
                "X": XGate(),
                "Y": YGate(),
                "Z": ZGate()
            }[pauli]
        else:
            gate = PauliGate(pauli)
        if not phase:
            return gate
        # Add global phase
        circuit = QuantumCircuit(self.num_qubits, name=str(self))
        circuit.global_phase = -phase * pi / 2
        circuit.append(gate, range(self.num_qubits))
        return circuit.to_instruction()
Ejemplo n.º 3
0
def reset_error(prob0, prob1=0):
    r"""
    Return a single qubit reset quantum error channel.

    The error channel returned is given by the map

    .. math::

        E(ρ) = (1 - p_0 - p_1) ρ + \text{Tr}[ρ] \left(
                p_0 |0 \rangle\langle 0|
                + p_1 |1 \rangle\langle 1| \right)

    where the probability of no reset is given by :math:`1 - p_0 - p_1`.

    Args:
        prob0 (double): reset probability to :math:`|0\rangle`.
        prob1 (double): reset probability to :math:`|1\rangle`.

    Returns:
        QuantumError: the quantum error object.

    Raises:
        NoiseError: If noise parameters are invalid.
    """
    if prob0 < 0 or prob1 < 0 or prob0 > 1 or prob1 > 1 or (prob0 + prob1) > 1:
        raise NoiseError("Invalid reset probabilities.")
    noise_ops = [([(IGate(), [0])], 1 - prob0 - prob1),
                 ([(Reset(), [0])], prob0),
                 ([(Reset(), [0]), (XGate(), [0])], prob1)]
    return QuantumError(noise_ops)
Ejemplo n.º 4
0
    def from_label(cls, label):
        """Return a tensor product of single-qubit operators.

        Args:
            label (string): single-qubit operator string.

        Returns:
            Operator: The N-qubit operator.

        Raises:
            QiskitError: if the label contains invalid characters, or the
                         length of the label is larger than an explicitly
                         specified num_qubits.

        Additional Information:
            The labels correspond to the single-qubit matrices:
            'I': [[1, 0], [0, 1]]
            'X': [[0, 1], [1, 0]]
            'Y': [[0, -1j], [1j, 0]]
            'Z': [[1, 0], [0, -1]]
            'H': [[1, 1], [1, -1]] / sqrt(2)
            'S': [[1, 0], [0 , 1j]]
            'T': [[1, 0], [0, (1+1j) / sqrt(2)]]
            '0': [[1, 0], [0, 0]]
            '1': [[0, 0], [0, 1]]
            '+': [[0.5, 0.5], [0.5 , 0.5]]
            '-': [[0.5, -0.5], [-0.5 , 0.5]]
            'r': [[0.5, -0.5j], [0.5j , 0.5]]
            'l': [[0.5, 0.5j], [-0.5j , 0.5]]
        """
        # Check label is valid
        label_mats = {
            'I': IGate().to_matrix(),
            'X': XGate().to_matrix(),
            'Y': YGate().to_matrix(),
            'Z': ZGate().to_matrix(),
            'H': HGate().to_matrix(),
            'S': SGate().to_matrix(),
            'T': TGate().to_matrix(),
            '0': np.array([[1, 0], [0, 0]], dtype=complex),
            '1': np.array([[0, 0], [0, 1]], dtype=complex),
            '+': np.array([[0.5, 0.5], [0.5, 0.5]], dtype=complex),
            '-': np.array([[0.5, -0.5], [-0.5, 0.5]], dtype=complex),
            'r': np.array([[0.5, -0.5j], [0.5j, 0.5]], dtype=complex),
            'l': np.array([[0.5, 0.5j], [-0.5j, 0.5]], dtype=complex),
        }
        if re.match(r'^[IXYZHST01rl\-+]+$', label) is None:
            raise QiskitError('Label contains invalid characters.')
        # Initialize an identity matrix and apply each gate
        num_qubits = len(label)
        op = Operator(np.eye(2 ** num_qubits, dtype=complex))
        for qubit, char in enumerate(reversed(label)):
            if char != 'I':
                op = op.compose(label_mats[char], qargs=[qubit])
        return op
Ejemplo n.º 5
0
    def from_label(label):
        """Return a tensor product of single-qubit Clifford gates.

        Args:
            label (string): single-qubit operator string.

        Returns:
            Clifford: The N-qubit Clifford operator.

        Raises:
            QiskitError: if the label contains invalid characters.

        Additional Information:
            The labels correspond to the single-qubit Cliffords are

            * - Label
              - Stabilizer
              - Destabilizer
            * - ``"I"``
              - +Z
              - +X
            * - ``"X"``
              - -Z
              - +X
            * - ``"Y"``
              - -Z
              - -X
            * - ``"Z"``
              - +Z
              - -X
            * - ``"H"``
              - +X
              - +Z
            * - ``"S"``
              - +Z
              - +Y
        """
        # Check label is valid
        label_gates = {
            'I': IGate(),
            'X': XGate(),
            'Y': YGate(),
            'Z': ZGate(),
            'H': HGate(),
            'S': SGate()
        }
        if re.match(r'^[IXYZHS\-+]+$', label) is None:
            raise QiskitError('Label contains invalid characters.')
        # Initialize an identity matrix and apply each gate
        num_qubits = len(label)
        op = Clifford(np.eye(2 * num_qubits, dtype=np.bool))
        for qubit, char in enumerate(reversed(label)):
            _append_circuit(op, label_gates[char], qargs=[qubit])
        return op
Ejemplo n.º 6
0
    def test_pauli_error_1q_gate_from_string(self):
        """Test single-qubit pauli error as gate qobj from string label"""
        paulis = ['I', 'X', 'Y', 'Z']
        probs = [0.4, 0.3, 0.2, 0.1]
        actual = pauli_error(zip(paulis, probs))

        expected = QuantumError([(IGate(), 0.4), (XGate(), 0.3), (YGate(), 0.2), (ZGate(), 0.1)])
        for i in range(actual.size):
            circ, prob = actual.error_term(i)
            expected_circ, expected_prob = expected.error_term(i)
            self.assertEqual(circ, expected_circ, msg=f"Incorrect {i}-th circuit")
            self.assertAlmostEqual(prob, expected_prob, msg=f"Incorrect {i}-th probability")
Ejemplo n.º 7
0
 def to_instruction(self):
     """Convert to Pauli circuit instruction."""
     from qiskit.circuit import QuantumCircuit, QuantumRegister
     from qiskit.circuit.library.standard_gates import IGate, XGate, YGate, ZGate
     gates = {'I': IGate(), 'X': XGate(), 'Y': YGate(), 'Z': ZGate()}
     label = self.to_label()
     num_qubits = self.num_qubits
     qreg = QuantumRegister(num_qubits)
     circuit = QuantumCircuit(qreg, name='Pauli:{}'.format(label))
     for i, pauli in enumerate(reversed(label)):
         circuit.append(gates[pauli], [qreg[i]])
     return circuit.to_instruction()
Ejemplo n.º 8
0
 def test_thermal_relaxation_error_t1_equal_t2_1state(self):
     """Test qobj instructions return for t1=t2"""
     actual = thermal_relaxation_error(1, 1, 1, 1)
     expected = QuantumError([
         (IGate(), np.exp(-1)),
         ([(Reset(), [0]), (XGate(), [0])], 1 - np.exp(-1)),
     ])
     for i in range(actual.size):
         circ, prob = actual.error_term(i)
         expected_circ, expected_prob = expected.error_term(i)
         self.assertEqual(circ, expected_circ, msg=f"Incorrect {i}-th circuit")
         self.assertAlmostEqual(prob, expected_prob, msg=f"Incorrect {i}-th probability")
Ejemplo n.º 9
0
    def test_depolarizing_error_1q_gate(self):
        """Test 1-qubit depolarizing error as gate qobj"""
        p_depol = 0.3
        actual = depolarizing_error(p_depol, 1)

        expected = QuantumError([
            (IGate(), 1 - p_depol*3/4),
            (XGate(), p_depol/4),
            (YGate(), p_depol/4),
            (ZGate(), p_depol/4)
        ])
        for i in range(actual.size):
            circ, prob = actual.error_term(i)
            expected_circ, expected_prob = expected.error_term(i)
            self.assertEqual(circ, expected_circ, msg=f"Incorrect {i}-th circuit")
            self.assertAlmostEqual(prob, expected_prob, msg=f"Incorrect {i}-th probability")
Ejemplo n.º 10
0
    def test_from_dict(self):
        noise_ops_1q = [((IGate(), [0]), 0.9),
                     ((XGate(), [0]), 0.1)]

        noise_ops_2q = [((PauliGate('II'), [0, 1]), 0.9),
                     ((PauliGate('IX'), [0, 1]), 0.045),
                     ((PauliGate('XI'), [0, 1]), 0.045),
                     ((PauliGate('XX'), [0, 1]), 0.01)]

        noise_model = NoiseModel()
        with self.assertWarns(DeprecationWarning):
            noise_model.add_quantum_error(QuantumError(noise_ops_1q, 1), 'h', [0])
            noise_model.add_quantum_error(QuantumError(noise_ops_1q, 1), 'h', [1])
            noise_model.add_quantum_error(QuantumError(noise_ops_2q, 2), 'cx', [0, 1])
            noise_model.add_quantum_error(QuantumError(noise_ops_2q, 2), 'cx', [1, 0])
            deserialized = NoiseModel.from_dict(noise_model.to_dict())
            self.assertEqual(noise_model, deserialized)
Ejemplo n.º 11
0
    def test_auto_method_clifford_circuits_and_reset_noise(self):
        """Test statevector method is used for Clifford circuit"""
        # Test noise model
        noise_circs = [Reset(), IGate()]
        noise_probs = [0.5, 0.5]
        error = QuantumError(zip(noise_circs, noise_probs))
        noise_model = NoiseModel()
        noise_model.add_all_qubit_quantum_error(
            error, ['id', 'x', 'y', 'z', 'h', 's', 'sdg'])
        backend = self.backend(noise_model=noise_model)

        # Test circuits
        shots = 100
        circuits = ref_2q_clifford.cz_gate_circuits_deterministic(
            final_measure=True)
        result = backend.run(circuits, shots=shots).result()
        success = getattr(result, 'success', False)
        self.assertTrue(success)
        self.compare_result_metadata(result, circuits, 'method', 'stabilizer')
Ejemplo n.º 12
0
    def test_thermal_relaxation_error_gate(self):
        """Test qobj instructions return for t2 < t1"""
        t1, t2, time, p1 = (2, 1, 1, 0.3)
        actual = thermal_relaxation_error(t1, t2, time, p1)

        p_z = 0.5 * np.exp(-1 / t1) * (1 - np.exp(-(1 / t2 - 1 / t1) * time))
        p_reset0 = (1 - p1) * (1 - np.exp(-1 / t1))
        p_reset1 = p1 * (1 - np.exp(-1 / t1))
        expected = QuantumError([
            (IGate(), 1 - p_z - p_reset0 - p_reset1),
            (ZGate(), p_z),
            (Reset(), p_reset0),
            ([(Reset(), [0]), (XGate(), [0])], p_reset1),
        ])
        for i in range(actual.size):
            circ, prob = actual.error_term(i)
            expected_circ, expected_prob = expected.error_term(i)
            self.assertEqual(circ, expected_circ, msg=f"Incorrect {i}-th circuit")
            self.assertAlmostEqual(prob, expected_prob, msg=f"Incorrect {i}-th probability")
Ejemplo n.º 13
0
    def test_reset_2_qubit(self):
        # approximating amplitude damping using relaxation operators
        gamma = 0.23
        p = (gamma - numpy.sqrt(1 - gamma) + 1) / 2
        A0 = [[1, 0], [0, numpy.sqrt(1 - gamma)]]
        A1 = [[0, numpy.sqrt(gamma)], [0, 0]]
        error_1 = QuantumError([([(Kraus([A0, A1]), [0]), (IGate(), [1])], 1)])
        error_2 = QuantumError([([(Kraus([A0, A1]), [1]), (IGate(), [0])], 1)])

        expected_results_1 = QuantumError([([(IGate(), [0]),
                                             (IGate(), [1])], 1 - p),
                                           ([(Reset(), [0]),
                                             (IGate(), [1])], p)])
        expected_results_2 = QuantumError([([(IGate(), [1]),
                                             (IGate(), [0])], 1 - p),
                                           ([(Reset(), [1]),
                                             (IGate(), [0])], p)])

        results_1 = approximate_quantum_error(error_1, operator_string="reset")
        results_2 = approximate_quantum_error(error_2, operator_string="reset")

        self.assertErrorsAlmostEqual(results_1, expected_results_1)
        self.assertErrorsAlmostEqual(results_2, expected_results_2)
Ejemplo n.º 14
0
        if a ``dict`` is given, ``list`` is overwritten.
        The ``string`` supports only 1- or 2-qubit errors and
        its possible values are ``'pauli'``, ``'reset'``, ``'clifford'``.
        The ``'clifford'`` does not support 2-qubit errors.
    """
    def approximate(noise):
        return approximate_quantum_error(noise,
                                         operator_string=operator_string,
                                         operator_dict=operator_dict,
                                         operator_list=operator_list)

    return transform_noise_model(model, approximate)


# pauli operators
_PAULIS_Q0 = [[(IGate(), [0])], [(XGate(), [0])], [(YGate(), [0])],
              [(ZGate(), [0])]]
_PAULIS_Q1 = [[(IGate(), [1])], [(XGate(), [1])], [(YGate(), [1])],
              [(ZGate(), [1])]]
_PAULIS_Q0Q1 = [op_q0 + op_q1 for op_q0 in _PAULIS_Q0 for op_q1 in _PAULIS_Q1]
# reset operators
_RESET_Q0_TO_0 = [(Reset(), [0])]
_RESET_Q0_TO_1 = [(Reset(), [0]), (XGate(), [0])]
_RESET_Q0 = [[(IGate(), [0])], _RESET_Q0_TO_0, _RESET_Q0_TO_1]
_RESET_Q1_TO_0 = [(Reset(), [1])]
_RESET_Q1_TO_1 = [(Reset(), [1]), (XGate(), [1])]
_RESET_Q1 = [[(IGate(), [1])], _RESET_Q1_TO_0, _RESET_Q1_TO_1]
_RESET_Q0Q1 = [op_q0 + op_q1 for op_q0 in _RESET_Q0 for op_q1 in _RESET_Q1]
# preset operator table
_PRESET_OPERATOR_TABLE = {
    "pauli": {
Ejemplo n.º 15
0
def thermal_relaxation_error(t1, t2, time, excited_state_population=0):
    r"""
    Return a single-qubit thermal relaxation quantum error channel.

    Args:
        t1 (double): the :math:`T_1` relaxation time constant.
        t2 (double): the :math:`T_2` relaxation time constant.
        time (double): the gate time for relaxation error.
        excited_state_population (double): the population of :math:`|1\rangle`
                                           state at equilibrium (default: 0).

    Returns:
        QuantumError: a quantum error object for a noise model.

    Raises:
        NoiseError: If noise parameters are invalid.

    Additional information:
        * For parameters to be valid :math:`T_1` and :math:`T_2` must
          satisfy :math:`T_2 \le 2 T_1`.

        * If :math:`T_2 \le T_1` the error can be expressed as a mixed
          reset and unitary error channel.

        * If :math:`T_1 < T_2 \le 2 T_1` the error must be expressed as a
          general non-unitary Kraus error channel.
    """
    if excited_state_population < 0:
        raise NoiseError("Invalid excited state population "
                         "({} < 0).".format(excited_state_population))
    if excited_state_population > 1:
        raise NoiseError("Invalid excited state population "
                         "({} > 1).".format(excited_state_population))
    if time < 0:
        raise NoiseError("Invalid gate_time ({} < 0)".format(time))
    if t1 <= 0:
        raise NoiseError("Invalid T_1 relaxation time parameter: T_1 <= 0.")
    if t2 <= 0:
        raise NoiseError("Invalid T_2 relaxation time parameter: T_2 <= 0.")
    if t2 - 2 * t1 > 0:
        raise NoiseError(
            "Invalid T_2 relaxation time parameter: T_2 greater than 2 * T_1.")

    # T1 relaxation rate
    if t1 == np.inf:
        rate1 = 0
        p_reset = 0
    else:
        rate1 = 1 / t1
        p_reset = 1 - np.exp(-time * rate1)
    # T2 dephasing rate
    if t2 == np.inf:
        rate2 = 0
        exp_t2 = 1
    else:
        rate2 = 1 / t2
        exp_t2 = np.exp(-time * rate2)
    # Qubit state equilibrium probabilities
    p0 = 1 - excited_state_population
    p1 = excited_state_population

    if t2 > t1:
        # If T_2 > T_1 we must express this as a Kraus channel
        # We start with the Choi-matrix representation:
        chan = Choi(
            np.array([[1 - p1 * p_reset, 0, 0, exp_t2],
                      [0, p1 * p_reset, 0, 0], [0, 0, p0 * p_reset, 0],
                      [exp_t2, 0, 0, 1 - p0 * p_reset]]))
        return QuantumError(Kraus(chan))
    else:
        # If T_2 < T_1 we can express this channel as a probabilistic
        # mixture of reset operations and unitary errors:
        circuits = [[(IGate(), [0])], [(ZGate(), [0])], [(Reset(), [0])],
                    [(Reset(), [0]), (XGate(), [0])]]
        # Probability
        p_reset0 = p_reset * p0
        p_reset1 = p_reset * p1
        p_z = (1 - p_reset) * (1 - np.exp(-time * (rate2 - rate1))) / 2
        p_identity = 1 - p_z - p_reset0 - p_reset1
        probabilities = [p_identity, p_z, p_reset0, p_reset1]
        return QuantumError(zip(circuits, probabilities))
Ejemplo n.º 16
0
    def _pre_runhook(self, dag: DAGCircuit):
        super()._pre_runhook(dag)

        num_pulses = len(self._dd_sequence)

        # Check if physical circuit is given
        if len(dag.qregs) != 1 or dag.qregs.get("q", None) is None:
            raise TranspilerError("DD runs on physical circuits only.")

        # Set default spacing otherwise validate user input
        if self._spacing is None:
            mid = 1 / num_pulses
            end = mid / 2
            self._spacing = [end] + [mid] * (num_pulses - 1) + [end]
        else:
            if sum(self._spacing) != 1 or any(a < 0 for a in self._spacing):
                raise TranspilerError(
                    "The spacings must be given in terms of fractions "
                    "of the slack period and sum to 1.")

        # Check if DD sequence is identity
        if num_pulses != 1:
            if num_pulses % 2 != 0:
                raise TranspilerError(
                    "DD sequence must contain an even number of gates (or 1).")
            noop = np.eye(2)
            for gate in self._dd_sequence:
                noop = noop.dot(gate.to_matrix())
            if not matrix_equal(noop, IGate().to_matrix(), ignore_phase=True):
                raise TranspilerError(
                    "The DD sequence does not make an identity operation.")
            self._sequence_phase = np.angle(noop[0][0])

        # Precompute qubit-wise DD sequence length for performance
        for qubit in dag.qubits:
            physical_index = dag.qubits.index(qubit)
            if self._qubits and physical_index not in self._qubits:
                continue

            sequence_lengths = []
            for gate in self._dd_sequence:
                try:
                    # Check calibration.
                    gate_length = dag.calibrations[gate.name][(physical_index,
                                                               gate.params)]
                    if gate_length % self._alignment != 0:
                        # This is necessary to implement lightweight scheduling logic for this pass.
                        # Usually the pulse alignment constraint and pulse data chunk size take
                        # the same value, however, we can intentionally violate this pattern
                        # at the gate level. For example, we can create a schedule consisting of
                        # a pi-pulse of 32 dt followed by a post buffer, i.e. delay, of 4 dt
                        # on the device with 16 dt constraint. Note that the pi-pulse length
                        # is multiple of 16 dt but the gate length of 36 is not multiple of it.
                        # Such pulse gate should be excluded.
                        raise TranspilerError(
                            f"Pulse gate {gate.name} with length non-multiple of {self._alignment} "
                            f"is not acceptable in {self.__class__.__name__} pass."
                        )
                except KeyError:
                    gate_length = self._durations.get(gate, physical_index)
                sequence_lengths.append(gate_length)
                # Update gate duration. This is necessary for current timeline drawer, i.e. scheduled.
                gate.duration = gate_length
            self._dd_sequence_lengths[qubit] = sequence_lengths
Ejemplo n.º 17
0
    def _gate_gradient_dict(
            gate: Gate) -> List[Tuple[List[complex], List[Instruction]]]:
        r"""Given a parameterized gate U(theta) with derivative
        dU(theta)/dtheta = sum_ia_iU(theta)V_i.
        This function returns a:=[a_0, ...] and V=[V_0, ...]
        Suppose U takes multiple parameters, i.e., U(theta^0, ... theta^k).
        The returned coefficients and gates are ordered accordingly.
        Only parameterized Qiskit gates are supported.

        Args:
            gate: The gate for which the derivative is being computed.

           Returns:
                The coefficients and the gates used for the metric computation for each parameter of
                the respective gates.
                [([a^0], [V^0]) ..., ([a^k], [V^k])]


        Raises:
            AquaError: If the input gate is controlled by another state but '|1>^{\otimes k}'
            TypeError: If the input gate is not a supported parametrized gate.
        """

        # pylint: disable=too-many-return-statements
        if isinstance(gate, PhaseGate):
            # theta
            return [([0.5j, -0.5j], [IGate(), CZGate()])]
        if isinstance(gate, UGate):
            # theta, lambda, phi
            return [([-0.5j], [CZGate()]), ([-0.5j], [CZGate()]),
                    ([-0.5j], [CZGate()])]
        if isinstance(gate, RXGate):
            # theta
            return [([-0.5j], [CXGate()])]
        if isinstance(gate, RYGate):
            # theta
            return [([-0.5j], [CYGate()])]
        if isinstance(gate, RZGate):
            # theta
            return [([-0.5j], [CZGate()])]
        if isinstance(gate, RXXGate):
            # theta
            cxx_circ = QuantumCircuit(3)
            cxx_circ.cx(0, 1)
            cxx_circ.cx(0, 2)
            cxx = cxx_circ.to_instruction()
            return [([-0.5j], [cxx])]
        if isinstance(gate, RYYGate):
            # theta
            cyy_circ = QuantumCircuit(3)
            cyy_circ.cy(0, 1)
            cyy_circ.cy(0, 2)
            cyy = cyy_circ.to_instruction()
            return [([-0.5j], [cyy])]
        if isinstance(gate, RZZGate):
            # theta
            czz_circ = QuantumCircuit(3)
            czz_circ.cz(0, 1)
            czz_circ.cz(0, 2)
            czz = czz_circ.to_instruction()
            return [([-0.5j], [czz])]
        if isinstance(gate, RZXGate):
            # theta
            czx_circ = QuantumCircuit(3)
            czx_circ.cx(0, 2)
            czx_circ.cz(0, 1)
            czx = czx_circ.to_instruction()
            return [([-0.5j], [czx])]
        if isinstance(gate, ControlledGate):
            # TODO support arbitrary control states
            if gate.ctrl_state != 2**gate.num_ctrl_qubits - 1:
                raise AquaError(
                    'Function only support controlled gates with control state `1` on all control '
                    'qubits.')

            base_coeffs_gates = LinComb._gate_gradient_dict(gate.base_gate)
            coeffs_gates = []
            # The projectors needed for the gradient of a controlled gate are integrated by a sum
            # of gates.
            # The following line generates the decomposition gates.

            proj_gates_controlled = [[
                (-1)**p.count(ZGate()), p
            ] for p in product([IGate(), ZGate()], repeat=gate.num_ctrl_qubits)
                                     ]
            for base_coeffs, base_gates in base_coeffs_gates:  # loop over parameters
                coeffs = []
                gates = []
                for phase, proj_gates in proj_gates_controlled:
                    coeffs.extend([
                        phase * c / (2**gate.num_ctrl_qubits)
                        for c in base_coeffs
                    ])
                    for base_gate in base_gates:
                        controlled_circ = QuantumCircuit(gate.num_ctrl_qubits +
                                                         gate.num_qubits)
                        for i, proj_gate in enumerate(proj_gates):
                            if isinstance(proj_gate, ZGate):
                                controlled_circ.cz(0, i + 1)
                        if not isinstance(base_gate, IGate):
                            controlled_circ.append(base_gate, [
                                0,
                                range(gate.num_ctrl_qubits + 1,
                                      gate.num_ctrl_qubits + gate.num_qubits)
                            ])
                        gates.append(controlled_circ.to_instruction())
                c_g = (coeffs, gates)
                coeffs_gates.append(c_g)
            return coeffs_gates

        raise TypeError('Unrecognized parametrized gate, {}'.format(gate))
Ejemplo n.º 18
0
    def run(self, dag):
        """Run the DynamicalDecoupling pass on dag.

        Args:
            dag (DAGCircuit): a scheduled DAG.

        Returns:
            DAGCircuit: equivalent circuit with delays interrupted by DD,
                where possible.

        Raises:
            TranspilerError: if the circuit is not mapped on physical qubits.
        """
        if len(dag.qregs) != 1 or dag.qregs.get("q", None) is None:
            raise TranspilerError("DD runs on physical circuits only.")

        if dag.duration is None:
            raise TranspilerError("DD runs after circuit is scheduled.")

        num_pulses = len(self._dd_sequence)
        sequence_gphase = 0
        if num_pulses != 1:
            if num_pulses % 2 != 0:
                raise TranspilerError(
                    "DD sequence must contain an even number of gates (or 1).")
            noop = np.eye(2)
            for gate in self._dd_sequence:
                noop = noop.dot(gate.to_matrix())
            if not matrix_equal(noop, IGate().to_matrix(), ignore_phase=True):
                raise TranspilerError(
                    "The DD sequence does not make an identity operation.")
            sequence_gphase = np.angle(noop[0][0])

        if self._qubits is None:
            self._qubits = set(range(dag.num_qubits()))
        else:
            self._qubits = set(self._qubits)

        if self._spacing:
            if sum(self._spacing) != 1 or any(a < 0 for a in self._spacing):
                raise TranspilerError(
                    "The spacings must be given in terms of fractions "
                    "of the slack period and sum to 1.")
        else:  # default to balanced spacing
            mid = 1 / num_pulses
            end = mid / 2
            self._spacing = [end] + [mid] * (num_pulses - 1) + [end]

        new_dag = dag._copy_circuit_metadata()

        qubit_index_map = {
            qubit: index
            for index, qubit in enumerate(new_dag.qubits)
        }
        index_sequence_duration_map = {}
        for qubit in new_dag.qubits:
            physical_qubit = qubit_index_map[qubit]
            dd_sequence_duration = 0
            for gate in self._dd_sequence:
                gate.duration = self._durations.get(gate, physical_qubit)
                dd_sequence_duration += gate.duration
            index_sequence_duration_map[physical_qubit] = dd_sequence_duration

        for nd in dag.topological_op_nodes():
            if not isinstance(nd.op, Delay):
                new_dag.apply_operation_back(nd.op, nd.qargs, nd.cargs)
                continue

            dag_qubit = nd.qargs[0]
            physical_qubit = qubit_index_map[dag_qubit]
            if physical_qubit not in self._qubits:  # skip unwanted qubits
                new_dag.apply_operation_back(nd.op, nd.qargs, nd.cargs)
                continue

            pred = next(dag.predecessors(nd))
            succ = next(dag.successors(nd))
            if self._skip_reset_qubits:  # discount initial delays
                if pred.type == "in" or isinstance(pred.op, Reset):
                    new_dag.apply_operation_back(nd.op, nd.qargs, nd.cargs)
                    continue

            dd_sequence_duration = index_sequence_duration_map[physical_qubit]
            slack = nd.op.duration - dd_sequence_duration
            if slack <= 0:  # dd doesn't fit
                new_dag.apply_operation_back(nd.op, nd.qargs, nd.cargs)
                continue

            if num_pulses == 1:  # special case of using a single gate for DD
                u_inv = self._dd_sequence[0].inverse().to_matrix()
                theta, phi, lam, phase = OneQubitEulerDecomposer(
                ).angles_and_phase(u_inv)
                # absorb the inverse into the successor (from left in circuit)
                if succ.type == "op" and isinstance(succ.op, (UGate, U3Gate)):
                    theta_r, phi_r, lam_r = succ.op.params
                    succ.op.params = Optimize1qGates.compose_u3(
                        theta_r, phi_r, lam_r, theta, phi, lam)
                    sequence_gphase += phase
                # absorb the inverse into the predecessor (from right in circuit)
                elif pred.type == "op" and isinstance(pred.op,
                                                      (UGate, U3Gate)):
                    theta_l, phi_l, lam_l = pred.op.params
                    pred.op.params = Optimize1qGates.compose_u3(
                        theta, phi, lam, theta_l, phi_l, lam_l)
                    sequence_gphase += phase
                # don't do anything if there's no single-qubit gate to absorb the inverse
                else:
                    new_dag.apply_operation_back(nd.op, nd.qargs, nd.cargs)
                    continue

            # insert the actual DD sequence
            taus = [int(slack * a) for a in self._spacing]
            unused_slack = slack - sum(
                taus)  # unused, due to rounding to int multiples of dt
            middle_index = int(
                (len(taus) - 1) / 2)  # arbitrary: redistribute to middle
            taus[
                middle_index] += unused_slack  # now we add up to original delay duration

            for tau, gate in itertools.zip_longest(taus, self._dd_sequence):
                if tau > 0:
                    new_dag.apply_operation_back(Delay(tau), [dag_qubit])
                if gate is not None:
                    new_dag.apply_operation_back(gate, [dag_qubit])

            new_dag.global_phase = _mod_2pi(new_dag.global_phase +
                                            sequence_gphase)

        return new_dag
Ejemplo n.º 19
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))
Ejemplo n.º 20
0
def convert_to_target(conf_dict: dict,
                      props_dict: dict = None,
                      defs_dict: dict = None) -> Target:
    """Uses configuration, properties and pulse defaults dicts
    to construct and return Target class.
    """
    name_mapping = {
        "id": IGate(),
        "sx": SXGate(),
        "x": XGate(),
        "cx": CXGate(),
        "rz": RZGate(Parameter("λ")),
        "reset": Reset(),
    }
    custom_gates = {}
    qubit_props = None
    if props_dict:
        qubit_props = qubit_props_from_props(props_dict)
    target = Target(qubit_properties=qubit_props)
    # Parse from properties if it exsits
    if props_dict is not None:
        # Parse instructions
        gates = {}
        for gate in props_dict["gates"]:
            name = gate["gate"]
            if name in name_mapping:
                if name not in gates:
                    gates[name] = {}
            elif name not in custom_gates:
                custom_gate = Gate(name, len(gate["qubits"]), [])
                custom_gates[name] = custom_gate
                gates[name] = {}

            qubits = tuple(gate["qubits"])
            gate_props = {}
            for param in gate["parameters"]:
                if param["name"] == "gate_error":
                    gate_props["error"] = param["value"]
                if param["name"] == "gate_length":
                    gate_props["duration"] = apply_prefix(
                        param["value"], param["unit"])
            gates[name][qubits] = InstructionProperties(**gate_props)
        for gate, props in gates.items():
            if gate in name_mapping:
                inst = name_mapping.get(gate)
            else:
                inst = custom_gates[gate]
            target.add_instruction(inst, props)
        # Create measurement instructions:
        measure_props = {}
        count = 0
        for qubit in props_dict["qubits"]:
            qubit_prop = {}
            for prop in qubit:
                if prop["name"] == "readout_length":
                    qubit_prop["duration"] = apply_prefix(
                        prop["value"], prop["unit"])
                if prop["name"] == "readout_error":
                    qubit_prop["error"] = prop["value"]
            measure_props[(count, )] = InstructionProperties(**qubit_prop)
            count += 1
        target.add_instruction(Measure(), measure_props)
    # Parse from configuration because properties doesn't exist
    else:
        for gate in conf_dict["gates"]:
            name = gate["name"]
            gate_props = {tuple(x): None for x in gate["coupling_map"]}
            if name in name_mapping:
                target.add_instruction(name_mapping[name], gate_props)
            else:
                custom_gate = Gate(name, len(gate["coupling_map"][0]), [])
                target.add_instruction(custom_gate, gate_props)
        measure_props = {(n, ): None for n in range(conf_dict["n_qubits"])}
        target.add_instruction(Measure(), measure_props)
    # parse global configuration properties
    dt = conf_dict.get("dt")
    if dt:
        target.dt = dt * 1e-9
    if "timing_constraints" in conf_dict:
        target.granularity = conf_dict["timing_constraints"].get("granularity")
        target.min_length = conf_dict["timing_constraints"].get("min_length")
        target.pulse_alignment = conf_dict["timing_constraints"].get(
            "pulse_alignment")
        target.aquire_alignment = conf_dict["timing_constraints"].get(
            "acquire_alignment")
    # If pulse defaults exists use that as the source of truth
    if defs_dict is not None:
        # TODO remove the usage of PulseDefaults as it will be deprecated in the future
        pulse_defs = PulseDefaults.from_dict(defs_dict)
        inst_map = pulse_defs.instruction_schedule_map
        for inst in inst_map.instructions:
            for qarg in inst_map.qubits_with_instruction(inst):
                sched = inst_map.get(inst, qarg)
                if inst in target:
                    try:
                        qarg = tuple(qarg)
                    except TypeError:
                        qarg = (qarg, )
                    if inst == "measure":
                        for qubit in qarg:
                            target[inst][(qubit, )].calibration = sched
                    else:
                        target[inst][qarg].calibration = sched
    target.add_instruction(Delay(Parameter("t")),
                           {(bit, ): None
                            for bit in range(target.num_qubits)})
    return target
Ejemplo n.º 21
0
def convert_to_target(
    configuration: BackendConfiguration,
    properties: BackendProperties = None,
    defaults: PulseDefaults = None,
) -> Target:
    """Uses configuration, properties and pulse defaults
    to construct and return Target class.
    """
    name_mapping = {
        "id": IGate(),
        "sx": SXGate(),
        "x": XGate(),
        "cx": CXGate(),
        "rz": RZGate(Parameter("λ")),
        "reset": Reset(),
    }
    custom_gates = {}
    target = None
    # Parse from properties if it exsits
    if properties is not None:
        qubit_properties = qubit_props_list_from_props(properties=properties)
        target = Target(
            num_qubits=configuration.n_qubits, qubit_properties=qubit_properties
        )
        # Parse instructions
        gates: Dict[str, Any] = {}
        for gate in properties.gates:
            name = gate.gate
            if name in name_mapping:
                if name not in gates:
                    gates[name] = {}
            elif name not in custom_gates:
                custom_gate = Gate(name, len(gate.qubits), [])
                custom_gates[name] = custom_gate
                gates[name] = {}

            qubits = tuple(gate.qubits)
            gate_props = {}
            for param in gate.parameters:
                if param.name == "gate_error":
                    gate_props["error"] = param.value
                if param.name == "gate_length":
                    gate_props["duration"] = apply_prefix(param.value, param.unit)
            gates[name][qubits] = InstructionProperties(**gate_props)
        for gate, props in gates.items():
            if gate in name_mapping:
                inst = name_mapping.get(gate)
            else:
                inst = custom_gates[gate]
            target.add_instruction(inst, props)
        # Create measurement instructions:
        measure_props = {}
        for qubit, _ in enumerate(properties.qubits):
            measure_props[(qubit,)] = InstructionProperties(
                duration=properties.readout_length(qubit),
                error=properties.readout_error(qubit),
            )
        target.add_instruction(Measure(), measure_props)
    # Parse from configuration because properties doesn't exist
    else:
        target = Target(num_qubits=configuration.n_qubits)
        for gate in configuration.gates:
            name = gate.name
            gate_props = (
                {tuple(x): None for x in gate.coupling_map}  # type: ignore[misc]
                if hasattr(gate, "coupling_map")
                else {None: None}
            )
            gate_len = len(gate.coupling_map[0]) if hasattr(gate, "coupling_map") else 0
            if name in name_mapping:
                target.add_instruction(name_mapping[name], gate_props)
            else:
                custom_gate = Gate(name, gate_len, [])
                target.add_instruction(custom_gate, gate_props)
        target.add_instruction(Measure())
    # parse global configuration properties
    if hasattr(configuration, "dt"):
        target.dt = configuration.dt
    if hasattr(configuration, "timing_constraints"):
        target.granularity = configuration.timing_constraints.get("granularity")
        target.min_length = configuration.timing_constraints.get("min_length")
        target.pulse_alignment = configuration.timing_constraints.get("pulse_alignment")
        target.aquire_alignment = configuration.timing_constraints.get(
            "acquire_alignment"
        )
    # If a pulse defaults exists use that as the source of truth
    if defaults is not None:
        inst_map = defaults.instruction_schedule_map
        for inst in inst_map.instructions:
            for qarg in inst_map.qubits_with_instruction(inst):
                sched = inst_map.get(inst, qarg)
                if inst in target:
                    try:
                        qarg = tuple(qarg)
                    except TypeError:
                        qarg = (qarg,)
                    if inst == "measure":
                        for qubit in qarg:
                            target[inst][(qubit,)].calibration = sched
                    else:
                        target[inst][qarg].calibration = sched
    if "delay" not in target:
        target.add_instruction(
            Delay(Parameter("t")), {(bit,): None for bit in range(target.num_qubits)}
        )
    return target
Ejemplo n.º 22
0
        ('z', 'h', 'sdg'),
        ('s', 'h', 'z'),
        ('sdg', 'h', 'z'),
        ('z', 'h', 'z'),
        # u3 gates
        (
            'x', ),
        ('y', ),
        ('s', 'x'),
        ('sdg', 'x')
    ]
    return labels[j]


_CLIFFORD_GATES = [
    (IGate(), ),
    (SGate(), ),
    (SdgGate(), ),
    (ZGate(), ),
    # u2 gates
    (HGate(), ),
    (HGate(), ZGate()),
    (ZGate(), HGate()),
    (HGate(), SGate()),
    (SGate(), HGate()),
    (HGate(), SdgGate()),
    (SdgGate(), HGate()),
    (SGate(), HGate(), SGate()),
    (SdgGate(), HGate(), SGate()),
    (ZGate(), HGate(), SGate()),
    (SGate(), HGate(), SdgGate()),
Ejemplo n.º 23
0
def approximate_quantum_error(error,
                              *,
                              operator_string=None,
                              operator_dict=None,
                              operator_list=None):
    r"""
    Return a ``QuantumError`` object that approximates an error
    as a mixture of specified operators (channels).

    The approximation is done by minimizing the Hilbert-Schmidt distance
    between the process matrix of the target error channel (:math:`T`) and
    the process matrix of the output channel (:math:`S = \sum_i{p_i S_i}`),
    i.e. :math:`Tr[(T-S)^\dagger (T-S)]`,
    where
    :math:`[p_1, p_2, ..., p_n]` denote probabilities and
    :math:`[S_1, S_2, ..., S_n]` denote basis operators (channels).

    See `arXiv:1207.0046 <http://arxiv.org/abs/1207.0046>`_ for the details.

    Args:
        error (QuantumError or QuantumChannel): the error to be approximated.
            The number of qubits must be 1 or 2.
        operator_string (string): a name for a pre-made set of
            building blocks for the output channel (Default: None).
            Possible values are ``'pauli'``, ``'reset'``, ``'clifford'``.
        operator_dict (dict): a dictionary whose values are the
            building blocks for the output channel (Default: None).
            E.g. {"x": XGate(), "y": YGate()}, keys "x" and "y"
            are not used in transformation.
        operator_list (list): list of building block operators for the
            output channel (Default: None). E.g. [XGate(), YGate()]

    Returns:
        QuantumError: the approximate quantum error.

    Raises:
        NoiseError: if any invalid argument is specified or approximation failed.
        MissingOptionalLibraryError: if cvxpy is not installed.

    Note:
        The operator input precedence is: ``list`` < ``dict`` < ``string``.
        If a ``string`` is given, ``dict`` is overwritten;
        if a ``dict`` is given, ``list`` is overwritten.
        The ``string`` supports only 1- or 2-qubit errors and
        its possible values are ``'pauli'``, ``'reset'``, ``'clifford'``.
        The ``'clifford'`` does not support 2-qubit errors.
    """
    if not isinstance(error, (QuantumError, QuantumChannel)):
        warnings.warn(
            'Support of types other than QuantumError or QuantumChannel for error'
            ' to be approximated 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)
        if isinstance(error, tuple) and isinstance(error[0], np.ndarray):
            error = list(error)
        if isinstance(error, list) or \
                (isinstance(error, tuple) and isinstance(error[0], list)):
            # first case for ordinary Kraus [A_i, B_i]
            # second case for generalized Kraus ([A_i], [B_i])
            error = Kraus(error)
        else:
            raise NoiseError(
                f"Invalid input error type: {error.__class__.__name__}")

    if error.num_qubits > 2:
        raise NoiseError("Only 1-qubit and 2-qubit noises can be converted, "
                         f"{error.num_qubits}-qubit noise found in model")

    if operator_string is not None:
        valid_operator_strings = _PRESET_OPERATOR_TABLE.keys()
        operator_string = operator_string.lower()
        if operator_string not in valid_operator_strings:
            raise NoiseError(
                f"{operator_string} is not a valid operator_string. "
                f"It must be one of {valid_operator_strings}")
        try:
            operator_list = _PRESET_OPERATOR_TABLE[operator_string][
                error.num_qubits]
        except KeyError:
            raise NoiseError(
                f"Preset '{operator_string}' operators do not support the "
                f"approximation of errors with {error.num_qubits} qubits")
    if operator_dict is not None:
        _, operator_list = zip(*operator_dict.items())
    if operator_list is not None:
        if not isinstance(operator_list, Sequence):
            raise NoiseError(
                "operator_list is not a sequence: {}".format(operator_list))
        if operator_list and isinstance(operator_list[0],
                                        Sequence) and isinstance(
                                            operator_list[0][0], np.ndarray):
            warnings.warn(
                'Accepting a sequence of Kraus matrices (list of numpy arrays)'
                ' as an operator_list has been deprecated as of qiskit-aer 0.10.0'
                ' and will be removed no earlier than 3 months from that release date.'
                ' Please use a sequence of Kraus objects instead.',
                DeprecationWarning,
                stacklevel=2)
            operator_list = [Kraus(op) for op in operator_list]

        try:
            channel_list = [
                op if isinstance(op, QuantumChannel) else QuantumError([(op,
                                                                         1)])
                for op in operator_list
            ]  # preserve operator_list
        except NoiseError:
            raise NoiseError(
                f"Invalid type found in operator list: {operator_list}")

        probabilities = _transform_by_operator_list(channel_list, error)[1:]
        identity_prob = np.round(1 - sum(probabilities), 9)
        if identity_prob < 0 or identity_prob > 1:
            raise NoiseError(
                f"Channel probabilities sum to {1 - identity_prob}")
        noise_ops = [((IGate(), [0]), identity_prob)]
        for (operator, probability) in zip(operator_list, probabilities):
            noise_ops.append((operator, probability))
        return QuantumError(noise_ops)

    raise NoiseError(
        "Quantum error approximation failed - no approximating operators detected"
    )
Ejemplo n.º 24
0
def depolarizing_error(param, num_qubits, standard_gates=None):
    r"""
    Return a depolarizing quantum error channel.

    The depolarizing channel is defined as:

    .. math::

        E(ρ) = (1 - λ) ρ + λ \text{Tr}[ρ] \frac{I}{2^n}

    with :math:`0 \le λ \le 4^n / (4^n - 1)`

    where :math:`λ` is the depolarizing error param and :math`n` is the
    number of qubits.

    * If :math:`λ = 0` this is the identity channel :math:`E(ρ) = ρ`
    * If :math:`λ = 1` this is a completely depolarizing channel
      :math:`E(ρ) = I / 2^n`
    * If :math:`λ = 4^n / (4^n - 1)` this is a uniform Pauli
      error channel: :math:`E(ρ) = \sum_j P_j ρ P_j / (4^n - 1)` for
      all :math:`P_j != I`.

    Args:
        param (double): depolarizing error parameter.
        num_qubits (int): the number of qubits for the error channel.
        standard_gates (bool): DEPRECATED, if True return the operators as
                               Pauli gates. If false return as unitary gates.
                               (Default: None)

    Returns:
        QuantumError: The quantum error object.

    Raises:
        NoiseError: If noise parameters are invalid.
    """
    if not isinstance(num_qubits, int) or num_qubits < 1:
        raise NoiseError("num_qubits must be a positive integer.")
    # Check that the depolarizing parameter gives a valid CPTP
    num_terms = 4**num_qubits
    max_param = num_terms / (num_terms - 1)
    if param < 0 or param > max_param:
        raise NoiseError("Depolarizing parameter must be in between 0 "
                         "and {}.".format(max_param))

    # Rescale completely depolarizing channel error probs
    # with the identity component removed
    prob_iden = 1 - param / max_param
    prob_pauli = param / num_terms
    probs = [prob_iden] + (num_terms - 1) * [prob_pauli]

    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.',
            DeprecationWarning,
            stacklevel=2)
        circs = []
        for pauli_list in it.product(
            [IGate(), XGate(), YGate(), ZGate()], repeat=num_qubits):
            qc = QuantumCircuit(num_qubits)
            for q, pauli in enumerate(pauli_list):
                if not standard_gates:
                    pauli = UnitaryGate(pauli.to_matrix())
                qc.append(pauli, qargs=[q])
            circs.append(qc)
        return QuantumError(zip(circs, probs))

    # Generate pauli strings. The order doesn't matter as long
    # as the all identity string is first.
    paulis = [
        Pauli("".join(tup))
        for tup in it.product(['I', 'X', 'Y', 'Z'], repeat=num_qubits)
    ]
    return QuantumError(zip(paulis, probs))
Ejemplo n.º 25
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.
    """
    warnings.warn(
        'kraus2instructions 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 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
        # pylint: disable=no-member
        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([(IGate(), [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
        ]
    with warnings.catch_warnings():
        warnings.filterwarnings("ignore",
                                category=DeprecationWarning,
                                module="qiskit.providers.aer.noise.errors.errorutils")
        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)
Ejemplo n.º 26
0
def _standard_gate_instruction(instruction, ignore_phase=True):
    """Temporary function to create Instruction objects from a json string,
    which is necessary for creating a new QuantumError object from deprecated
    json-based input. Note that the type of returned object is different from
    the deprecated standard_gate_instruction.
    TODO: to be removed after deprecation period.

    Args:
        instruction (dict): A qobj instruction.
        ignore_phase (bool): Ignore global phase on unitary matrix in
                             comparison to canonical unitary.

    Returns:
        list: a list of (instructions, qubits) equivalent to in input instruction.
    """
    gate = {
        "id": IGate(),
        "x": XGate(),
        "y": YGate(),
        "z": ZGate(),
        "h": HGate(),
        "s": SGate(),
        "sdg": SdgGate(),
        "t": TGate(),
        "tdg": TdgGate(),
        "cx": CXGate(),
        "cz": CZGate(),
        "swap": SwapGate()
    }

    name = instruction.get("name", None)
    qubits = instruction["qubits"]
    if name in gate:
        return [(gate[name], qubits)]

    if name not in ["mat", "unitary", "kraus"]:
        return [instruction]

    params = instruction["params"]
    with warnings.catch_warnings():
        warnings.filterwarnings("ignore",
                                category=DeprecationWarning,
                                module="qiskit.providers.aer.noise.errors.errorutils")

        # Check for single-qubit reset Kraus
        if name == "kraus":
            if len(qubits) == 1:
                superop = SuperOp(Kraus(params))
                # Check if reset to |0>
                reset0 = reset_superop(1)
                if superop == reset0:
                    return [(Reset(), [0])]
                # Check if reset to |1>
                reset1 = reset0.compose(Operator(standard_gate_unitary('x')))
                if superop == reset1:
                    return [(Reset(), [0]), (XGate(), [0])]
            return [instruction]

        # Check single qubit gates
        mat = params[0]
        if len(qubits) == 1:
            # Check clifford gates
            for j in range(24):
                if matrix_equal(
                        mat,
                        single_qubit_clifford_matrix(j),
                        ignore_phase=ignore_phase):
                    return [(gate, [0]) for gate in _CLIFFORD_GATES[j]]
            # Check t gates
            for name in ["t", "tdg"]:
                if matrix_equal(
                        mat,
                        standard_gate_unitary(name),
                        ignore_phase=ignore_phase):
                    return [(gate[name], qubits)]
            # TODO: u1,u2,u3 decomposition
        # Check two qubit gates
        if len(qubits) == 2:
            for name in ["cx", "cz", "swap"]:
                if matrix_equal(
                        mat,
                        standard_gate_unitary(name),
                        ignore_phase=ignore_phase):
                    return [(gate[name], qubits)]
            # Check reversed CX
            if matrix_equal(
                    mat,
                    standard_gate_unitary("cx_10"),
                    ignore_phase=ignore_phase):
                return [(CXGate(), [qubits[1], qubits[0]])]
            # Check 2-qubit Pauli's
            paulis = ["id", "x", "y", "z"]
            for pauli0 in paulis:
                for pauli1 in paulis:
                    pmat = np.kron(
                        standard_gate_unitary(pauli1),
                        standard_gate_unitary(pauli0))
                    if matrix_equal(mat, pmat, ignore_phase=ignore_phase):
                        if pauli0 == "id":
                            return [(gate[pauli1], [qubits[1]])]
                        elif pauli1 == "id":
                            return [(gate[pauli0], [qubits[0]])]
                        else:
                            return [(gate[pauli0], [qubits[0]]), (gate[pauli1], [qubits[1]])]
        # Check three qubit toffoli
        if len(qubits) == 3:
            if matrix_equal(
                    mat,
                    standard_gate_unitary("ccx_012"),
                    ignore_phase=ignore_phase):
                return [(CCXGate(), qubits)]
            if matrix_equal(
                    mat,
                    standard_gate_unitary("ccx_021"),
                    ignore_phase=ignore_phase):
                return [(CCXGate(), [qubits[0], qubits[2], qubits[1]])]
            if matrix_equal(
                    mat,
                    standard_gate_unitary("ccx_120"),
                    ignore_phase=ignore_phase):
                return [(CCXGate(), [qubits[1], qubits[2], qubits[0]])]

    # Else return input in
    return [instruction]