|
14 | 14 |
|
15 | 15 | from __future__ import annotations
|
16 | 16 |
|
17 |
| -from qiskit.circuit import Instruction |
| 17 | +import numpy as np |
| 18 | +from qiskit.circuit import Instruction, QuantumCircuit, QuantumRegister |
| 19 | +from qiskit.quantum_info import PauliList, SparsePauliOp, SuperOp |
18 | 20 | from qiskit_ibm_runtime.utils.noise_learner_result import PauliLindbladError
|
19 | 21 |
|
20 | 22 |
|
@@ -42,3 +44,32 @@ def __init__(self, ple: PauliLindbladError):
|
42 | 44 | def ple(self) -> PauliLindbladError:
|
43 | 45 | """Returns the internal Pauli-Lindblad error object."""
|
44 | 46 | return self._ple
|
| 47 | + |
| 48 | + def _define(self) -> None: |
| 49 | + # Implementation of Instruction._define which populates the definition attribute with a |
| 50 | + # QuantumCircuit-based representation of this instruction. |
| 51 | + |
| 52 | + # This implementation is adapted from qiskit_aer.noise.PauliLindbladError |
| 53 | + chan_z = np.zeros((1, self.num_qubits), dtype=bool) |
| 54 | + chan_x = np.zeros_like(chan_z) |
| 55 | + chan_p = np.ones(1, dtype=float) |
| 56 | + for term_z, term_x, term_r in zip( |
| 57 | + self.ple.generators.z, |
| 58 | + self.ple.generators.x, |
| 59 | + self.ple.rates, |
| 60 | + ): |
| 61 | + term_p = 0.5 - 0.5 * np.exp(-2 * term_r) |
| 62 | + chan_z = np.concatenate([chan_z, np.logical_xor(chan_z, term_z)], axis=0) |
| 63 | + chan_x = np.concatenate([chan_x, chan_x ^ term_x]) |
| 64 | + chan_p = np.concatenate([(1 - term_p) * chan_p, term_p * chan_p]) |
| 65 | + |
| 66 | + error_op = SparsePauliOp(PauliList.from_symplectic(chan_z, chan_x), chan_p).simplify() |
| 67 | + chan = SuperOp(np.zeros(2 * [4**self.num_qubits])) |
| 68 | + for pauli, coeff in zip(error_op.paulis, error_op.coeffs.real): |
| 69 | + chan += coeff * SuperOp(pauli) |
| 70 | + |
| 71 | + q = QuantumRegister(self.num_qubits, "q") |
| 72 | + qc = QuantumCircuit(q, name=self.name) |
| 73 | + qc.append(chan.to_instruction(), q) |
| 74 | + |
| 75 | + self.definition = qc |
0 commit comments