Channels#
Channels are implemented in Qibo as additional gates and can be accessed from
the qibo.gates module. Channels can be used on density matrices to perform
noisy simulations. Channels that inherit qibo.gates.UnitaryChannel
can also be applied to state vectors using sampling and repeated execution.
For more information on the use of channels to simulate noise we refer to
How to perform noisy simulation?
The following channels are currently implemented:
Kraus channel#
- class qibo.gates.KrausChannel(qubits, operators)[source]#
General channel defined by arbitrary Kraus operators.
Implements the following transformation:
\[\mathcal{E}(\rho ) = \sum _k A_k \rho A_k^\dagger\]where A are arbitrary Kraus operators given by the user. Note that Kraus operators set should be trace preserving, however this is not checked. Simulation of this gate requires the use of density matrices. For more information on channels and Kraus operators please check J. Preskill’s notes.
- Parameters:
qubits (int or list or tuple) – Qubits that the Kraus operators act on. Type
intandtuplewill be considered as the same qubit ids for all operators. Alistshould contain tuples of qubits corresponding to each operator. Can be[]ifoperatorsare of typeqibo.gates.Gate, otherwise adds given gates on specified qubits.operators (list) – List of Kraus operators
Akas matrices of typendarray | tf.Tensoror gatesqibo.gates.Gate.
Example
import numpy as np from qibo import Circuit, gates # initialize circuit with 3 qubits circuit = Circuit(3, density_matrix=True) # define a sqrt(0.4) * X gate a_1 = np.sqrt(0.4) * np.array([[0, 1], [1, 0]]) # define a sqrt(0.6) * CNOT gate a_2 = np.sqrt(0.6) * np.array([[1, 0, 0, 0], [0, 1, 0, 0], [0, 0, 0, 1], [0, 0, 1, 0]]) # define the channel rho -> 0.4 X{1} rho X{1} + 0.6 CNOT{0, 2} rho CNOT{0, 2} channel_1 = gates.KrausChannel([(1,), (0, 2)], [a_1, a_2]) # add channel to the circuit circuit.add(channel_1) # define the same channel using qibo.gates.Unitary a_1 = gates.Unitary(a_1, 1) a_2 = gates.Unitary(a_2, 0, 2) channel_2 = gates.KrausChannel([], [a_1, a_2]) # add channel to the circuit circuit.add(channel_2) # define the channel rho -> 0.4 X{0} rho X{0} + 0.6 CNOT{1, 2} rho CNOT{1, 2} channel_3 = gates.KrausChannel([(0,), (1, 2)], [a_1, a_2]) # add channel to the circuit circuit.add(channel_3)
- on_qubits(qubit_map)[source]#
Creates the same gate targeting different qubits.
- Parameters:
qubit_map (int) – Dictionary mapping original qubit indices to new ones.
- Returns:
A
qibo.gates.Gateobject of the original gate type targeting the given qubits.
Example
from qibo import Circuit, gates circuit = Circuit(4) # Add some CNOT gates # equivalent to gates.CNOT(2, 3) circuit.add(gates.CNOT(2, 3).on_qubits({2: 2, 3: 3})) # equivalent to gates.CNOT(3, 0) circuit.add(gates.CNOT(2, 3).on_qubits({2: 3, 3: 0})) # equivalent to gates.CNOT(1, 3) circuit.add(gates.CNOT(2, 3).on_qubits({2: 1, 3: 3})) # equivalent to gates.CNOT(2, 1) circuit.add(gates.CNOT(2, 3).on_qubits({2: 2, 3: 1})) circuit.draw()
0: ───X───── 1: ───|─o─X─ 2: ─o─|─|─o─ 3: ─X─o─X───
Unitary channel#
- class qibo.gates.UnitaryChannel(qubits, operators)[source]#
Channel that is a probabilistic sum of unitary operations.
Implements the following transformation:
\[\mathcal{E}(\rho ) = \left (1 - \sum _k p_k \right )\rho + \sum _k p_k U_k \rho U_k^\dagger\]where U are arbitrary unitary operators and p are floats between 0 and 1. Note that unlike
qibo.gates.KrausChannelwhich requires density matrices, it is possible to simulate the unitary channel using state vectors and probabilistic sampling. For more information on this approach we refer to Using repeated execution.- Parameters:
qubits (int or list or tuple) – Qubits that the unitary operators act on. Types
intandtuplewill be considered as the same qubit(s) for all unitaries. Alistshould contain tuples of qubits corresponding to each operator. Can be[]ifoperatorsare of typeqibo.gates.Gate, otherwise adds given gates on specified qubits.operators (list) – List of operators as pairs
(pk, Uk)wherepkis float probability corresponding to a unitaryUkof typendarray/tf.Tensoror gatesqibo.gates.Gate.
- on_qubits(qubit_map)[source]#
Creates the same gate targeting different qubits.
- Parameters:
qubit_map (int) – Dictionary mapping original qubit indices to new ones.
- Returns:
A
qibo.gates.Gateobject of the original gate type targeting the given qubits.
Example
from qibo import Circuit, gates circuit = Circuit(4) # Add some CNOT gates # equivalent to gates.CNOT(2, 3) circuit.add(gates.CNOT(2, 3).on_qubits({2: 2, 3: 3})) # equivalent to gates.CNOT(3, 0) circuit.add(gates.CNOT(2, 3).on_qubits({2: 3, 3: 0})) # equivalent to gates.CNOT(1, 3) circuit.add(gates.CNOT(2, 3).on_qubits({2: 1, 3: 3})) # equivalent to gates.CNOT(2, 1) circuit.add(gates.CNOT(2, 3).on_qubits({2: 2, 3: 1})) circuit.draw()
0: ───X───── 1: ───|─o─X─ 2: ─o─|─|─o─ 3: ─X─o─X───
Pauli noise channel#
- class qibo.gates.PauliNoiseChannel(qubits, operators)[source]#
Multi-qubit noise channel that applies Pauli operators with given probabilities.
Implements the following transformation:
\[\mathcal{E}(\rho ) = \left (1 - \sum _{k} p_{k} \right ) \, \rho + \sum_{k} \, p_{k} \, P_{k} \, \rho \, P_{k}\]where \(P_{k}\) is the \(k\)-th Pauli
stringand \(p_{k}\) is the probability associated to \(P_{k}\).Example
from itertools import product import numpy as np from qibo.gates.channels import PauliNoiseChannel qubits = (0, 2) nqubits = len(qubits) # excluding the Identity operator paulis = list(product(["I", "X"], repeat=nqubits))[1:] # this next line is optional paulis = [''.join(pauli) for pauli in paulis] probabilities = np.random.rand(len(paulis) + 1) probabilities /= np.sum(probabilities) #Excluding probability of Identity operator probabilities = probabilities[1:] channel = PauliNoiseChannel( qubits, list(zip(paulis, probabilities)) )
This channel can be simulated using either density matrices or state vectors and sampling with repeated execution. See How to perform noisy simulation? for more information.
- Parameters:
- on_qubits(qubit_map)[source]#
Creates the same gate targeting different qubits.
- Parameters:
qubit_map (int) – Dictionary mapping original qubit indices to new ones.
- Returns:
A
qibo.gates.Gateobject of the original gate type targeting the given qubits.
Example
from qibo import Circuit, gates circuit = Circuit(4) # Add some CNOT gates # equivalent to gates.CNOT(2, 3) circuit.add(gates.CNOT(2, 3).on_qubits({2: 2, 3: 3})) # equivalent to gates.CNOT(3, 0) circuit.add(gates.CNOT(2, 3).on_qubits({2: 3, 3: 0})) # equivalent to gates.CNOT(1, 3) circuit.add(gates.CNOT(2, 3).on_qubits({2: 1, 3: 3})) # equivalent to gates.CNOT(2, 1) circuit.add(gates.CNOT(2, 3).on_qubits({2: 2, 3: 1})) circuit.draw()
0: ───X───── 1: ───|─o─X─ 2: ─o─|─|─o─ 3: ─X─o─X───
Depolarizing channel#
- class qibo.gates.DepolarizingChannel(qubits, lam)[source]#
\(n\)-qubit Depolarizing quantum error channel,
\[\mathcal{E}(\rho ) = (1 - \lambda) \rho + \lambda \text{Tr}_q[\rho]\otimes \frac{I}{2^n}\]where \(\lambda\) is the depolarizing error parameter and \(0 \le \lambda \le 4^n / (4^n - 1)\).
If \(\lambda = 1\) this is a completely depolarizing channel \(E(\rho) = I / 2^n\)
If \(\lambda = 4^n / (4^n - 1)\) this is a uniform Pauli error channel: \(E(\rho) = \sum_j P_j \rho P_j / (4^n - 1)\) for all \(P_j \neq I\).
- Parameters:
- on_qubits(qubit_map)[source]#
Creates the same gate targeting different qubits.
- Parameters:
qubit_map (int) – Dictionary mapping original qubit indices to new ones.
- Returns:
A
qibo.gates.Gateobject of the original gate type targeting the given qubits.
Example
from qibo import Circuit, gates circuit = Circuit(4) # Add some CNOT gates # equivalent to gates.CNOT(2, 3) circuit.add(gates.CNOT(2, 3).on_qubits({2: 2, 3: 3})) # equivalent to gates.CNOT(3, 0) circuit.add(gates.CNOT(2, 3).on_qubits({2: 3, 3: 0})) # equivalent to gates.CNOT(1, 3) circuit.add(gates.CNOT(2, 3).on_qubits({2: 1, 3: 3})) # equivalent to gates.CNOT(2, 1) circuit.add(gates.CNOT(2, 3).on_qubits({2: 2, 3: 1})) circuit.draw()
0: ───X───── 1: ───|─o─X─ 2: ─o─|─|─o─ 3: ─X─o─X───
Thermal relaxation channel#
- class qibo.gates.ThermalRelaxationChannel(qubit, parameters)[source]#
Single-qubit thermal relaxation error channel.
Implements the following transformation:
If \(T_1 \geq T_2\):
\[\mathcal{E} (\rho ) = (1 - p_z - p_0 - p_1) \rho + p_z \, Z\rho Z + \mathrm{Tr}_{q}[\rho] \otimes (p_0 | 0\rangle \langle 0| + p_1|1\rangle \langle 1|)\]while if \(T_1 < T_2\):
\[\mathcal{E}(\rho ) = \mathrm{Tr}_\mathcal{X} \left[\Lambda_{\mathcal{X}\mathcal{Y}} (\rho_\mathcal{X}^T \otimes I_{\mathcal{Y}}) \right]\]with
\[\begin{split}\Lambda = \begin{pmatrix} 1 - p_1 & 0 & 0 & e^{-t / T_2} \\ 0 & p_1 & 0 & 0 \\ 0 & 0 & p_0 & 0 \\ e^{-t / T_2} & 0 & 0 & 1 - p_0 \end{pmatrix}\end{split}\]where \(p_0 = (1 - e^{-t / T_1})(1 - \eta )\), \(p_1 = (1 - e^{-t / T_1})\eta\), and \(p_z = (e^{-t / T_1} - e^{-t / T_2})/2\). Here \(\eta\) is the
excited_populationand \(t\) is thetime, both controlled by the user. This gate is based on Qiskit’s thermal relaxation error channel.- Parameters:
qubit (int) – Qubit id that the noise channel acts on.
parameters (list) – list of 3 or 4 parameters (t_1, t_2, time, excited_population=0), where t_1 (float): T1 relaxation time. Should satisfy
t_1 > 0. t_2 (float): T2 dephasing time. Should satisfyt_1 > 0andt_2 < 2 * t_1. time (float): the gate time for relaxation error. excited_population (float): the population of the excited state at equilibrium. Default is 0.
- on_qubits(qubit_map)[source]#
Creates the same gate targeting different qubits.
- Parameters:
qubit_map (int) – Dictionary mapping original qubit indices to new ones.
- Returns:
A
qibo.gates.Gateobject of the original gate type targeting the given qubits.
Example
from qibo import Circuit, gates circuit = Circuit(4) # Add some CNOT gates # equivalent to gates.CNOT(2, 3) circuit.add(gates.CNOT(2, 3).on_qubits({2: 2, 3: 3})) # equivalent to gates.CNOT(3, 0) circuit.add(gates.CNOT(2, 3).on_qubits({2: 3, 3: 0})) # equivalent to gates.CNOT(1, 3) circuit.add(gates.CNOT(2, 3).on_qubits({2: 1, 3: 3})) # equivalent to gates.CNOT(2, 1) circuit.add(gates.CNOT(2, 3).on_qubits({2: 2, 3: 1})) circuit.draw()
0: ───X───── 1: ───|─o─X─ 2: ─o─|─|─o─ 3: ─X─o─X───
Amplitude damping channel#
- class qibo.gates.AmplitudeDampingChannel(qubit, gamma)[source]#
Single-qubit amplitude damping channel in its Kraus representation, i.e.
\[\begin{split}K_{0} = \begin{pmatrix} 1 & 0 \\ 0 & \sqrt{1 - \gamma} \\ \end{pmatrix} \,\, , \,\, K_{1} = \begin{pmatrix} 0 & \sqrt{\gamma} \\ 0 & 0 \\ \end{pmatrix}\end{split}\]- Parameters:
- on_qubits(qubit_map)[source]#
Creates the same gate targeting different qubits.
- Parameters:
qubit_map (int) – Dictionary mapping original qubit indices to new ones.
- Returns:
A
qibo.gates.Gateobject of the original gate type targeting the given qubits.
Example
from qibo import Circuit, gates circuit = Circuit(4) # Add some CNOT gates # equivalent to gates.CNOT(2, 3) circuit.add(gates.CNOT(2, 3).on_qubits({2: 2, 3: 3})) # equivalent to gates.CNOT(3, 0) circuit.add(gates.CNOT(2, 3).on_qubits({2: 3, 3: 0})) # equivalent to gates.CNOT(1, 3) circuit.add(gates.CNOT(2, 3).on_qubits({2: 1, 3: 3})) # equivalent to gates.CNOT(2, 1) circuit.add(gates.CNOT(2, 3).on_qubits({2: 2, 3: 1})) circuit.draw()
0: ───X───── 1: ───|─o─X─ 2: ─o─|─|─o─ 3: ─X─o─X───
Phase damping channel#
- class qibo.gates.PhaseDampingChannel(qubit, gamma)[source]#
Single-qubit phase damping channel in its Kraus representation, i.e.
\[\begin{split}K_{0} = \begin{pmatrix} 1 & 0 \\ 0 & \sqrt{1 - \gamma} \\ \end{pmatrix} \,\, , \,\, K_{1} = \begin{pmatrix} 0 & 0 \\ 0 & \sqrt{\gamma} \\ \end{pmatrix}\end{split}\]- Parameters:
- on_qubits(qubit_map)[source]#
Creates the same gate targeting different qubits.
- Parameters:
qubit_map (int) – Dictionary mapping original qubit indices to new ones.
- Returns:
A
qibo.gates.Gateobject of the original gate type targeting the given qubits.
Example
from qibo import Circuit, gates circuit = Circuit(4) # Add some CNOT gates # equivalent to gates.CNOT(2, 3) circuit.add(gates.CNOT(2, 3).on_qubits({2: 2, 3: 3})) # equivalent to gates.CNOT(3, 0) circuit.add(gates.CNOT(2, 3).on_qubits({2: 3, 3: 0})) # equivalent to gates.CNOT(1, 3) circuit.add(gates.CNOT(2, 3).on_qubits({2: 1, 3: 3})) # equivalent to gates.CNOT(2, 1) circuit.add(gates.CNOT(2, 3).on_qubits({2: 2, 3: 1})) circuit.draw()
0: ───X───── 1: ───|─o─X─ 2: ─o─|─|─o─ 3: ─X─o─X───
Readout error channel#
- class qibo.gates.ReadoutErrorChannel(qubits, probabilities)[source]#
Readout error channel implemented as a quantum-to-classical channel.
- Parameters:
Example
For 1 qubit, the transition matrix \(P\) would be
\[\begin{split}P = \begin{pmatrix} p(0 \, | \, 0) & p(1 \, | \, 0) \\ p(0 \, | \, 1) & p(1 \, | \, 1) \end{pmatrix} \, .\end{split}\]- on_qubits(qubit_map)[source]#
Creates the same gate targeting different qubits.
- Parameters:
qubit_map (int) – Dictionary mapping original qubit indices to new ones.
- Returns:
A
qibo.gates.Gateobject of the original gate type targeting the given qubits.
Example
from qibo import Circuit, gates circuit = Circuit(4) # Add some CNOT gates # equivalent to gates.CNOT(2, 3) circuit.add(gates.CNOT(2, 3).on_qubits({2: 2, 3: 3})) # equivalent to gates.CNOT(3, 0) circuit.add(gates.CNOT(2, 3).on_qubits({2: 3, 3: 0})) # equivalent to gates.CNOT(1, 3) circuit.add(gates.CNOT(2, 3).on_qubits({2: 1, 3: 3})) # equivalent to gates.CNOT(2, 1) circuit.add(gates.CNOT(2, 3).on_qubits({2: 2, 3: 1})) circuit.draw()
0: ───X───── 1: ───|─o─X─ 2: ─o─|─|─o─ 3: ─X─o─X───
Reset channel#
- class qibo.gates.ResetChannel(qubit, probabilities)[source]#
Single-qubit reset channel.
Implements the following transformation:
\[\mathcal{E}(\rho ) = (1 - p_{0} - p_{1}) \rho + \mathrm{Tr}_{q}[\rho] \otimes (p_{0} \, |0\rangle \langle 0| + p_{1} \, |1\rangle \langle 1|),\]- Parameters:
- on_qubits(qubit_map)[source]#
Creates the same gate targeting different qubits.
- Parameters:
qubit_map (int) – Dictionary mapping original qubit indices to new ones.
- Returns:
A
qibo.gates.Gateobject of the original gate type targeting the given qubits.
Example
from qibo import Circuit, gates circuit = Circuit(4) # Add some CNOT gates # equivalent to gates.CNOT(2, 3) circuit.add(gates.CNOT(2, 3).on_qubits({2: 2, 3: 3})) # equivalent to gates.CNOT(3, 0) circuit.add(gates.CNOT(2, 3).on_qubits({2: 3, 3: 0})) # equivalent to gates.CNOT(1, 3) circuit.add(gates.CNOT(2, 3).on_qubits({2: 1, 3: 3})) # equivalent to gates.CNOT(2, 1) circuit.add(gates.CNOT(2, 3).on_qubits({2: 2, 3: 1})) circuit.draw()
0: ───X───── 1: ───|─o─X─ 2: ─o─|─|─o─ 3: ─X─o─X───