Return the Choi matrix of a Pauli channel.
The Pauli channel is defined by a set of Pauli operators weighted by a probability vector.
For a given probability vector \((p_0, \ldots, p_{4^q -1 })\), the channel is defined as
shown below. Where, \(q\) is the number of qubits.
\[
\Phi(\rho) = \sum_{i=0}^{4^q - 1} p_i P_i \rho P_i^*
\]
where \(P_i\) are Pauli operators generated by a lexicographically increasing sequence of pauli operators of
length strictly equal to \(q\), and \(p_i\) is the corresponding probability of that operator.
For example, when \(q = 2\),
\(P_{0} = I \otimes I\), \(P_{1} = I \otimes X\), \(P_{2} = I \otimes Y\), \(P_{3} = I \otimes Z\),
\(P_{4} = X \otimes I\), \(P_{5} = X \otimes Y , \ldots P_{15} = Z \otimes Z\)
If prob is a scalar, it generates a random prob-qubit Pauli channel.
The length of the probability vector (if provided) must be \(4^q\) for some
integer \(q\) (number of qubits).
Parameters:
-
prob
(int | ndarray)
–
Probability vector for Pauli operators. If scalar, generates random probabilities for \(q =\) prob
qubits. The probabilities correspond to Pauli operators in lexicographical order of length strictly equal to
\(q\), when prob is a vector.
-
return_kraus_ops
(bool, default:
False
)
–
If True, return the list of Kraus operators instead of the Choi matrix.
Returns:
-
ndarray | tuple
–
The Choi matrix of the Pauli channel, or its list of Kraus operators when
-
ndarray | tuple
–
return_kraus_ops is True.
Raises:
-
ValueError
–
If probabilities are negative or don't sum to 1.
-
ValueError
–
If length of probability vector is not 4^q for some integer q.
Examples:
Generate the Choi matrix of a random single-qubit Pauli channel:
from toqito.channels import pauli_channel
print(pauli_channel(prob=1))
[[ 0.45156796+0.j 0. +0.j 0. +0.j -0.29460065+0.j]
[ 0. +0.j 0.54843204+0.j -0.10005221+0.j 0. +0.j]
[ 0. +0.j -0.10005221+0.j 0.54843204+0.j 0. +0.j]
[-0.29460065+0.j 0. +0.j 0. +0.j 0.45156796+0.j]]
Apply a specific two-qubit Pauli channel to an input matrix via apply_channel:
import numpy as np
from toqito.channels import pauli_channel
from toqito.channel_ops import apply_channel
choi = pauli_channel(prob=np.array([0.1, 0.2, 0.3, 0.4]))
print(apply_channel(np.eye(2), choi))
[[1.+0.j 0.+0.j]
[0.+0.j 1.+0.j]]
Source code in toqito/channels/pauli_channel.py
| def pauli_channel(prob: int | np.ndarray, return_kraus_ops: bool = False) -> np.ndarray | tuple:
r"""Return the Choi matrix of a Pauli channel.
The Pauli channel is defined by a set of Pauli operators weighted by a probability vector.
For a given probability vector \((p_0, \ldots, p_{4^q -1 })\), the channel is defined as
shown below. Where, $q$ is the number of qubits.
\[
\Phi(\rho) = \sum_{i=0}^{4^q - 1} p_i P_i \rho P_i^*
\]
where \(P_i\) are Pauli operators generated by a lexicographically increasing sequence of pauli operators of
length strictly equal to \(q\), and \(p_i\) is the corresponding probability of that operator.
For example, when \(q = 2\),
\(P_{0} = I \otimes I\), \(P_{1} = I \otimes X\), \(P_{2} = I \otimes Y\), \(P_{3} = I \otimes Z\),
\(P_{4} = X \otimes I\), \(P_{5} = X \otimes Y , \ldots P_{15} = Z \otimes Z\)
If `prob` is a scalar, it generates a random `prob`-qubit Pauli channel.
The length of the probability vector (if provided) must be \(4^q\) for some
integer \(q\) (number of qubits).
Args:
prob: Probability vector for Pauli operators. If scalar, generates random probabilities for \(q =\) `prob`
qubits. The probabilities correspond to Pauli operators in lexicographical order of length strictly equal to
\(q\), when `prob` is a vector.
return_kraus_ops: If `True`, return the list of Kraus operators instead of the Choi matrix.
Returns:
The Choi matrix of the Pauli channel, or its list of Kraus operators when
`return_kraus_ops` is `True`.
Raises:
ValueError: If probabilities are negative or don't sum to 1.
ValueError: If length of probability vector is not ``4^q`` for some integer ``q``.
Examples:
Generate the Choi matrix of a random single-qubit Pauli channel:
```python exec="1" source="above" result="text"
from toqito.channels import pauli_channel
print(pauli_channel(prob=1))
```
Apply a specific two-qubit Pauli channel to an input matrix via `apply_channel`:
```python exec="1" source="above" result="text"
import numpy as np
from toqito.channels import pauli_channel
from toqito.channel_ops import apply_channel
choi = pauli_channel(prob=np.array([0.1, 0.2, 0.3, 0.4]))
print(apply_channel(np.eye(2), choi))
```
"""
if not isinstance(prob, np.ndarray):
if np.isscalar(prob):
q = prob
prob = np.random.rand(4**q)
prob /= np.sum(prob)
else:
prob = np.array(prob)
if np.any(prob < 0) or not np.isclose(np.sum(prob), 1):
raise ValueError("Probabilities must be non-negative and sum to 1.")
q = int(np.round(np.log2(len(prob)) / 2))
if len(prob) != 4**q:
raise ValueError("The length of the probability vector must be 4^q for some integer q (number of qubits).")
# The channel's Kraus operators are sqrt(prob_j) * P_j, so its Choi matrix is the Choi of this flat (completely
# positive) Kraus list. Building it with a single kraus_to_choi call avoids one conversion per Pauli (4^q calls).
kraus_operators = [
np.sqrt(prob[j]) * pauli(list(ind)) for j, ind in enumerate(itertools.product(range(4), repeat=q))
]
Phi = kraus_to_choi(kraus_operators)
if return_kraus_ops:
return kraus_operators
return Phi
|