<jupyter_start><jupyter_text>Describing quantum noise: Quantum channelsIn this notebook, we demonstrate how to describe noise in the Quantum Learning Machine. DefinitionsQuantum noise is generally described mathematically by completely positive, trace-preserving maps or **quantum channels**, which we will denote $\mathcal{E}(\rho)$ in the following (with $\rho$ the density matrix).A quantum channel $\mathcal{E}$ can be represented by its **Kraus operators** $\lbrace E_k \rbrace_{k=1\dots K}$. In this Kraus or operator-sum representation, $\mathcal{E}$ acts on a given density matrix $\rho$ as follows:$$\mathcal{E}(\rho) = \sum_{k=1}^K E_k \rho E_k^\dagger $$$K$ is called the **Kraus rank** of the quantum channel. Since a quantum channel is trace preserving, the Kraus operators fulfill the relation$$\sum_k E_k^\dagger E_k = \mathbf{1} $$ Implementation in the Quantum Learning MachineIn the QLM, quantum channels and related tools are implemented in the ``qat.quops`` (for "quantum operations") module. The Kraus representation of a quantum channel is implemented by the ``QuantumChannelKraus`` object. ConstructionIn the code snippet below, we create a quantum channel with two Kraus operators $$E_0 = \sqrt{p} \mathbf{1},\;\;E_1 = \sqrt{1-p} \mathbf{X}$$where $\mathbf{X}$ is the Pauli X matrix, and $p=0.75$.<jupyter_code>import numpy as np
from qat.quops import QuantumChannelKraus

Pauli_X = np.array([[0.,1.],[1.,0.]])

my_channel = QuantumChannelKraus(kraus_operators=[np.sqrt(0.75)*np.identity(2), np.sqrt(0.25)*Pauli_X],
                                 name="my simple channel")

# let us print the Kraus operators and the Kraus rank

print("Kraus operators = ", my_channel.kraus_operators)
print("Kraus rank =", my_channel.kraus_rank())<jupyter_output><empty_output><jupyter_text>Application to a density matrixLet us apply this channel to a density matrix:<jupyter_code>density_matrix = np.zeros((2,2))
density_matrix[0,0] = 1.

result = my_channel.apply_to(density_matrix, [0])

print("result = ", result)<jupyter_output><empty_output><jupyter_text>Composition of quantum channelsOne can compose quantum channels. The cell below shows how to construct $\mathcal{E_1}\circ \mathcal{E_2}$:<jupyter_code>Pauli_Z = np.array([[1.,0.],[0.,-1.]])

my_channel_2 = QuantumChannelKraus(kraus_operators=[np.sqrt(0.5)*np.identity(2), np.sqrt(0.5)*Pauli_Z],
                                 name="my simple channel")

final_channel = my_channel*my_channel_2

print("Kraus rank of composition : ", final_channel.kraus_rank())
print("Kraus operators of composition : ", final_channel.kraus_operators)<jupyter_output><empty_output>
{'fraction_non_alphanumeric': 0.12319939347990902, 'fraction_numerical': 0.015163002274450341, 'mean_word_length': 5.283333333333333, 'pattern_counts': {'":': 0, '<': 13, '<?xml version=': 0, '>': 13, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 2, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'chain_length': '3', 'dir': 'jupyter-structured-clean-dedup', 'id': '460272', 'license': 'Apache-2.0', 'n_tokens_mistral': 847, 'n_tokens_neox': 806, 'n_words': 253, 'path': 'misc/notebooks/tutorials/noisy_simulation/quantum_channels.ipynb', 'repo_name': 'wmkirby1/CS-VQE', 'size': '2638'}