Search
Soluzione

Progetto finale: Pauli twirling

Soluzioni

from qiskit import QuantumRegister, ClassicalRegister, QuantumCircuit
import numpy as np

Attività 1 (7 punti)

Calcola algebricamente il canel risultante dopo aver applicato il twirling su:

1) Canale bit-flip $$ \Lambda_d (\rho) = (1-p) I \rho I + p (X \rho X)$$ 2) Rotazione coerente $$ \Lambda_r (\rho) = \cos^2{\left(\frac{\theta}{2}\right)} I \rho I + \sin^2{\left(\frac{\theta}{2}\right)} X \rho X + \frac{i}{2} (\sin{(\theta)} I \rho X - \sin{(\theta)} X \rho I)$$ 3) Canale di smorzamento dell'ampiezza $$ \Lambda_a (\rho) = A_0 \rho A_0^\dagger + A_1 \rho A_1^\dagger$$ dove $A_0 = \frac{(1+\sqrt{1-p})}{2} I + \frac{(1-\sqrt{1-p})}{2} Z$ e $A_1 = \frac{\sqrt{p}}{2} (X + iY)$.

Scrivi i canali risultanti come canali di Pauli.

Soluzione

Innanzitutto calcoliamo l'azione dei canali $\mathcal{P}$ sugli operatori di Pauli. \begin{align*} \mathcal{P}_i (I) &= I \\ \mathcal{P}_0 (\sigma_i) &= I \sigma_i I = \sigma_i \\ \mathcal{P}_1 (X) &= XXX = X \\ \mathcal{P}_2 (X) &= YXY = -X \\ \mathcal{P}_3 (X) &= ZXZ = -X \\ \end{align*}

  1. Canale bit-flip. \begin{align*} \widetilde{\Lambda}_d (\rho) &= \frac{1}{4}\sum_{i=0}^3 \mathcal{P}_i \Lambda_d \mathcal{P}^\dagger_i \rho \\ &= \frac{1}{4} \left(\sum_{i=0}^3 {\left[(1-p) \mathcal{P}_i I \rho I \mathcal{P}_i^\dagger + p (\mathcal{P}_i X \rho X \mathcal{P}_i^\dagger )\right]} \right) \\ &= \frac{1}{4} \left(\sum_{i=0}^3 {\left[(1-p) \mathcal{P}_i (I) \rho \mathcal{P}_i^\dagger (I) + p (\mathcal{P}_i (X) \rho \mathcal{P}_i^\dagger (X))\right]} \right) \\ &= \frac{1}{4} \left(4 (1-p) \rho + 4 p (X \rho X) \right) \\ &= \Lambda_d(\rho) \\ \end{align*} Il canale resta immutato dal twirling.
  1. Rotazione coerente. Dal calcolo precedente si nota che sia il termine $I\rho I$ sia quello $X \rho X$ rimangono invariati sotto l’azione del twirling. Inoltre, $\mathcal{P}$ e $\mathcal{P}^\dagger$ svolgono la stessa azione, pertanto useremo solo $\mathcal{P}$. \begin{align*} \widetilde{\Lambda}_r (\rho) &= \cos^2{\left(\frac{\theta}{2}\right)} I \rho I + \sin^2{\left(\frac{\theta}{2}\right)} X \rho X + \frac{1}{4}\sum_{i=0}^3 \left[\frac{i}{2} (\sin{(\theta)} \mathcal{P}_i (I) \rho \mathcal{P}_i (X) - \sin{(\theta)} \mathcal{P}_i (X) \rho \mathcal{P}_i (I))\right]. \end{align*} Concentriamoci sull'ultimo termine della somma. \begin{align*} \frac{1}{4}\sum_{i=0}^3 \left[\frac{i}{2} (\sin{(\theta)} \mathcal{P}_i (I) \rho \mathcal{P}_i (X) - \sin{(\theta)} \mathcal{P}_i (X) \rho \mathcal{P}_i (I))\right] &= \frac{1}{4}\sum_{i=0}^3 \left[\frac{i}{2} (\sin{(\theta)} I \rho \mathcal{P}_i (X) - \sin{(\theta)} \mathcal{P}_i (X) \rho I)\right] \\ &= \frac{1}{4}\left(2\left[\frac{i}{2} (\sin{(\theta)} I \rho X - \sin{(\theta)} X \rho I)\right] - 2\left[\frac{i}{2} (\sin{(\theta)} I \rho X - \sin{(\theta)} X \rho I)\right]\right) \\ &= 0. \end{align*} Questo significa che il canale twirled è semplicemente \begin{align*} \widetilde{\Lambda}_r (\rho) &= \cos^2{\left(\frac{\theta}{2}\right)} I \rho I + \sin^2{\left(\frac{\theta}{2}\right)} X \rho X \end{align*} ed è nella forma di un canale di Pauli.
  1. Canale di smorzamento dell'ampiezza. Calcoliamo qualche altra azione del canale $\mathcal{P}$. \begin{align*} \mathcal{P}_1 (Y) &= XYX = -Y \\ \mathcal{P}_2 (Y) &= YYY = Y \\ \mathcal{P}_3 (Y) &= ZYZ = -Y \\ \mathcal{P}_1 (Z) &= XZX = -Z \\ \mathcal{P}_2 (Z) &= YZY = -Z \\ \mathcal{P}_3 (Z) &= ZZZ = Z \\ \end{align*} Calcoliamo il canale di smorzamento dell'ampiezza twirled. \begin{align*} \widetilde{\Lambda}_a (\rho) &= \frac{1}{4}\sum_{i=0}^3 {\left[\mathcal{P}_i(A_0) \rho \mathcal{P}_i(A_0^\dagger) + \mathcal{P}_i(A_1) \rho \mathcal{P}_i(A_1^\dagger)\right]}. \end{align*} Prima, calcoliamo i twirl per gli operatori di Kraus $A_0$. \begin{align*} \mathcal{P}_i(A_0) &= \frac{(1+\sqrt{1-p})}{2} \mathcal{P}_i(I) + \frac{(1-\sqrt{1-p})}{2} \mathcal{P}_i(Z) \\ &= \frac{(1+\sqrt{1-p})}{2} I + \frac{(1-\sqrt{1-p})}{2} \mathcal{P}_i(Z) \\ &= \mathcal{P}_i(A_0^\dagger) \end{align*} Il secondo termine è negativo per $i=1,2$. Se sostituiamo questo risultato nell'espressione, otteniamo \begin{align*} \frac{1}{4}\sum_{i=0}^3 {\left[\mathcal{P}_i(A_0) \rho \mathcal{P}_i(A_0^\dagger)\right]} &= \frac{1}{4} \left((1+\sqrt{1-p})^2 I \rho I + (1-\sqrt{1-p})^2 Z \rho Z + \frac{(1+\sqrt{1-p})(1-\sqrt{1-p})}{4} (2 (I \rho Z + Z \rho I) - 2(I \rho Z + Z \rho I)) \right) \\ &= \frac{1}{4} \left((1+\sqrt{1-p})^2 I \rho I + (1-\sqrt{1-p})^2 Z \rho Z\right) \\ &= \frac{1}{4} \left((1+\sqrt{1-p})^2 I \rho I + (1-\sqrt{1-p})^2 Z \rho Z\right) \\ \end{align*} Adesso calcoliamo i twirl per gli operatori di Kraus $A_1$. \begin{align*} \mathcal{P}_i(A_1) &= {\frac{\sqrt{p}}{2} (\mathcal{P}_i(X) + i \mathcal{P}_i(Y))} \\ &= (\mathcal{P}_i(A_1^\dagger))^\dagger \end{align*} Lo sostituiamo nell'espressione per ottenere \begin{align*} \frac{1}{4}\sum_{i=0}^3 {\left[\mathcal{P}_i(A_1) \rho \mathcal{P}_i(A_1^\dagger)\right]} &= \frac{1}{4}\sum_{i=0}^3\frac{p}{4} { (\mathcal{P}_i(X) + i \mathcal{P}_i(Y))} \rho { (\mathcal{P}_i(X) - i \mathcal{P}_i(Y))} \\ &= \frac{1}{4}\frac{p}{4} {(4X\rho X + 4Y\rho Y - 2(i X\rho Y + i Y\rho X) + 2(i X\rho Y + i Y\rho X))} \\ &= \frac{p}{4} (X\rho X + Y\rho Y).\\ \end{align*} Infine, sommiamo i due risultati per ottenere il canale di smorzamento dell'ampiezza twirled. \begin{align*} \widetilde{\Lambda}_a (\rho) &= \frac{1}{4} \left((1+\sqrt{1-p})^2 I \rho I + pX\rho X + pY\rho Y+ (1-\sqrt{1-p})^2 Z \rho Z\right). \end{align*} Vediamo che il canale twirled è nella froma di un canale di Pauli e tutti i termini misti $\sigma_i \rho \sigma_j$ con $i\neq j$ del canale con rumore si sono eliminati.

Attività 2 (8 punti)

Scrivi classi personalizzate per esperimenti e analisi utilizzando, ad esempio, il framework qiskit-experiments per estrarre matrici di densità dopo aver ruotato i canali di rumore tramite tomografia di stato. La classe di esperimento dovrebbe accettare come input almeno un circuito con un canale di rumore, mentre la classe di analisi dovrebbe restituire come output una matrice di densità.

Esistono molti modi possibili per implementare questa funzionalità, ma per darti un’idea da dove iniziare, ti forniamo un modello qui di seguito. La tua classe di esperimento potrebbe ereditare da BaseExperiment o TomographyExperiment e implementare i metodi __init__ e circuit per generare i circuiti necessari per il twirling di Pauli. La tua classe di analisi invece potrebbe ereditare da BaseAnalysis e implementare il metodo _run_analysis per calcolare la media dei risultati ottenuti dai diversi twirl dell’esperimento. Puoi consultare le istruzioni nella Documentazione di Qiskit-experiments per ulteriore assistenza. Qiskit-experiments è completamente open source, quindi anche dare un’occhiata al codice sorgente può essere d’aiuto.

Soluzione

from qiskit import QuantumCircuit
from copy import deepcopy
from itertools import chain
from typing import List, Optional, Sequence
from qiskit.providers.backend import Backend
from qiskit_experiments.framework import Options
from qiskit_experiments.library.tomography import TomographyExperiment, StateTomographyAnalysis

from typing import List
from qiskit_experiments.framework import (
    BaseAnalysis,
    Options,
    ExperimentData,
    AnalysisResultData
)

class TwirledStateTomographyAnalysis(BaseAnalysis):
    """Personalizza il template per la classe di analisi."""

    @classmethod
    def _default_options(cls) -> Options:
        """Imposta le opzioni d'analisi di default."""

        options = super()._default_options()
        return options

    def _add_dicts(self, d1, d2):
        """Metodo per aggiungere due dizionari.
        """        
        dtot = {key: 0 for key in set(chain(d1.keys(), d2.keys()))}
        for key, val in chain(d1.items(), d2.items()):
            dtot[key] += val
        return dtot

    def _run_analysis(
        self,
        experiment_data: ExperimentData
    ) -> List[AnalysisResultData]:
        """Esegui l'analisi."""

        # Dizionario per ottenere gli indici per la base di misura di qiskit
        meas_keys = {'Z': 0, 'X': 1, 'Y': 2}
        new_res = []
        # Media i risultati ottenuti per ogni base misura sui twirls
        for meas in ['Z', 'X', 'Y']:
            # Crea nuovi dati che possiamo passare a StateTomographyAnalysis
            new_res_object = deepcopy(experiment_data.data()[0])
            del new_res_object['job_id']
            del new_res_object['meas_level']
            del new_res_object['metadata']['twirl']
            # Aggiungi 'm_idx' per StateTomographyAnalysis e 'twirled' per noi per tenere traccia
            new_res_object['metadata'].update({'twirled': True, 'm_idx': [meas_keys[meas]]})

            # Media
            total_shots = 0
            new_counts_dict = {}
            for twirl_res in filter(lambda x: x['metadata']['m_idx'] == meas, experiment_data.data()):
                new_counts_dict = self._add_dicts(new_counts_dict, twirl_res['counts'])
                total_shots += twirl_res['shots']
            new_res_object['counts'] = new_counts_dict
            new_res_object['shots'] = total_shots
            new_res.append(new_res_object)

        # Sostituisci i dati in experiment_data con i risultati mediati 
        experiment_data._result_data = new_res
        # Passa experiment_data a StateTomographyAnalysis ed esegui
        state_tomography = StateTomographyAnalysis()
        result_tom = state_tomography._run_analysis(experiment_data)
        return result_tom[0], []

class TwirledStateTomography(TomographyExperiment):
    """Personalizza il template per la classe di esperimenti."""

    def __init__(self,
                 circuit: QuantumCircuit,
                 state_prep: QuantumCircuit,
                 twirl_qubits: Sequence[int],
                 physical_qubits: Sequence[int] = None,
                 measurement_indices: Sequence[int] = None,
                 backend: Optional[Backend] = None):
        """Inizializza l'esperimento."""
        if physical_qubits is None:
            physical_qubits = tuple(range(circuit.num_qubits))
        super().__init__(circuit=circuit,
                        backend=backend,
                        physical_qubits=physical_qubits,
                        measurement_indices=measurement_indices,
                        analysis=TwirledStateTomographyAnalysis(),
                        )
        self._twirl_qubits = twirl_qubits
        # State preparation and noise channel circuit are kept separate
        self._state_prep = state_prep

    def circuits(self) -> List[QuantumCircuit]:
        """Genera la lista di circuiti da eseguire."""
        circuits = []
        # Prepara i circuiti per la misura e il twirling
        for meas in ['Z', 'X', 'Y']:
            for pauli in ['I', 'X', 'Y', 'Z']:
                for qubit in self._twirl_qubits:
                    circ = QuantumCircuit(self.num_qubits, len(self._meas_indices))
                    # Prepara lo stato
                    circ = circ.compose(self._state_prep)
                    # Usa i metadata per dire a qiskit cosa sono i circuiti
                    circ.metadata = {'twirl': pauli, 'm_idx': meas, "clbits": [0], "cond_clbits": []}
                    
                    # Twirling e canale con rumore
                    circ.pauli(pauli, [qubit])
                    circ = circ.compose(self._circuit)
                    circ.pauli(pauli, [qubit])
                    
                    # Misure di Pauli 
                    if meas == 'X':
                        circ.h(qubit)
                    if meas == 'Y':
                        circ.sdg(qubit)
                        circ.h(qubit)
                    circ.measure(qubit, qubit)

                    circuits.append(circ)
        return circuits

    @classmethod
    def _default_experiment_options(cls) -> Options:
        """Imposta qui le opzioni di default per l'esperimento."""
        options = super()._default_experiment_options()
        return options

Attività 3 (8 punti)

Inizializza un qubit in uno stato con popolazioni e coerenze diverse da zero. Applica a tale qubit ciascuno dei canali di rumore riportati di seguito ed esegui la tomografia dello stato con e senza l’applicazione del twirling al canale. Per ciascun canale di rumore, traccia sulla stessa figura $\rho_{00}$, $\rho_{11}$, $Re(\rho_{01})$, $Im(\rho_{01})$ per i casi con e senza rotazione, per diversi valori di $p$ o $\theta$.

Canali di rumore:

  • Bit-flip
    • Canale di Pauli con $(1-p, p, 0, 0)$.
  • Rotazione coerente
    • Applica il gate RX con un angolo piccolo.
  • Canale di smorzamento di ampiezza

È possibile utilizzare i modelli di rumore implementati in qiskit_aer.noise. È inoltre consentito utilizzare le implementazioni dei canali di rumore provenienti da progetti precedenti, ma occorre tenere presente che si potrebbero verificare instabilità numeriche.

Soluzione

p_values = np.linspace(0,0.99,10)
t_values = np.linspace(0,np.pi,10)
from qiskit_aer import AerSimulator
from qiskit_experiments.library.tomography import StateTomography
# Usiamo i canali con rumore implementati in qiskit_aer.noise
from qiskit_aer.noise import amplitude_damping_error, pauli_error
import matplotlib.pyplot as plt

# Backend
backend = AerSimulator()

# Stato iniziale
qc_s = QuantumCircuit(4,1)
qc_s.u(np.pi/4, np.pi/4, 0, 0)
---------------------------------------------------------------------------
ModuleNotFoundError                       Traceback (most recent call last)
Cell In[1], line 1
----> 1 from qiskit_aer import AerSimulator
      2 from qiskit_experiments.library.tomography import StateTomography
      3 # Usiamo i canali con rumore implementati in qiskit_aer.noise

ModuleNotFoundError: No module named 'qiskit_aer'

Iniziamo con il canale bit-flip.

rhos_bf_twirled = []
for p in p_values:
    qc = QuantumCircuit(4)
    # Appendiamo al circuito il canale con rumore
    error = pauli_error([("I", 1-p), ("X", p)])
    qc.append(error, [0])
    res_t = TwirledStateTomography(qc, qc_s, [0], measurement_indices=[0]).run(backend=backend).block_for_results()
    rhos_bf_twirled.append(res_t.analysis_results('state').value)

rhos_bf = []
for p in p_values:
    qc = qc_s.copy()
    error = pauli_error([("I", 1-p), ("X", p)])
    qc.append(error, [0])
    res = StateTomography(qc, measurement_indices=[0]).run(backend=backend, shots=4096).block_for_results()
    rhos_bf.append(res.analysis_results('state').value)

tomo_rhos_bf_twirled = np.zeros((2,2,len(p_values)), dtype=complex)

for (i, res) in enumerate(rhos_bf_twirled):
    tomo_rhos_bf_twirled[:,:,i] = res

tomo_rhos_bf = np.zeros((2,2,len(p_values)), dtype=complex)

for (i, res) in enumerate(rhos_bf):
    tomo_rhos_bf[:,:,i] = res

plt.figure(figsize=(10,6))

# Risultati twirled 
plt.plot(p_values, np.real(tomo_rhos_bf_twirled[0,1,:]),"C0*", label='Re $\\rho_{01} twirled$')
plt.plot(p_values, np.imag(tomo_rhos_bf_twirled[0,1,:]),"C1*", label='Im $\\rho_{01} twirled$')
plt.plot(p_values, np.real(tomo_rhos_bf_twirled[0,0,:]),"C2x", label='$\\rho_{00} twirled$')
plt.plot(p_values, np.real(tomo_rhos_bf_twirled[1,1,:]),"C3x", label='$\\rho_{11} twirled$')

# Risultati non-twirled 
plt.plot(p_values, np.real(tomo_rhos_bf[0,1,:]),"C0--", label='Re $\\rho_{01}$')
plt.plot(p_values, np.imag(tomo_rhos_bf[0,1,:]),"C1--", label='Im $\\rho_{01}$')
plt.plot(p_values, np.real(tomo_rhos_bf[0,0,:]),"C2--", label='$\\rho_{00}$')
plt.plot(p_values, np.real(tomo_rhos_bf[1,1,:]),"C3--", label='$\\rho_{11}$')

plt.xlabel('p')
plt.ylabel('$\\rho_{xx}$')
plt.legend()

plt.title("Twirling of bit-flip channel")
Text(0.5, 1.0, 'Twirling of bit-flip channel')

Vediamo che il twirling non cambia l'azione del canale bit-flip.

Qui sotto facciamo lo stesso per la rotazione coerente.

rhos_cr_twirled = []
for t in t_values:
    qc = QuantumCircuit(4,1)
    qc.rx(t,0)
    res_t = TwirledStateTomography(qc, qc_s, [0], measurement_indices=[0]).run(backend=backend).block_for_results()
    rhos_cr_twirled.append(res_t.analysis_results('state').value)

rhos_cr = []
for t in t_values:
    qc = qc_s.copy()
    qc.rx(t,0)
    res = StateTomography(qc, measurement_indices=[0]).run(backend=backend, shots=4096).block_for_results()
    rhos_cr.append(res.analysis_results('state').value)

tomo_rhos_cr_twirled = np.zeros((2,2,len(t_values)), dtype=complex)

for (i, res) in enumerate(rhos_cr_twirled):
    tomo_rhos_cr_twirled[:,:,i] = res

tomo_rhos_cr = np.zeros((2,2,len(t_values)), dtype=complex)

for (i, res) in enumerate(rhos_cr):
    tomo_rhos_cr[:,:,i] = res

plt.figure(figsize=(10,6))

# Risultati twirled 
plt.plot(t_values, np.real(tomo_rhos_cr_twirled[0,1,:]),"C0*", label='Re $\\rho_{01} twirled$')
plt.plot(t_values, np.imag(tomo_rhos_cr_twirled[0,1,:]),"C1*", label='Im $\\rho_{01} twirled$')
plt.plot(t_values, np.real(tomo_rhos_cr_twirled[0,0,:]),"C2x", label='$\\rho_{00} twirled$')
plt.plot(t_values, np.real(tomo_rhos_cr_twirled[1,1,:]),"C3x", label='$\\rho_{11} twirled$')

# Risultati non-twirled 
plt.plot(t_values, np.real(tomo_rhos_cr[0,1,:]),"C0--", label='Re $\\rho_{01}$')
plt.plot(t_values, np.imag(tomo_rhos_cr[0,1,:]),"C1--", label='Im $\\rho_{01}$')
plt.plot(t_values, np.real(tomo_rhos_cr[0,0,:]),"C2--", label='$\\rho_{00}$')
plt.plot(t_values, np.real(tomo_rhos_cr[1,1,:]),"C3--", label='$\\rho_{11}$')

plt.xlabel(r'$\theta$')
plt.ylabel('$\\rho_{xx}$')
plt.legend()

plt.title("Twirling of coherent rotation")
Text(0.5, 1.0, 'Twirling of coherent rotation')

Il twirling cambia la fase degli elementi sulla diagonale e la componente immaginaria degli elementi fuori dalla diagonale rispetto a $\theta$.

Infine facciamo lo stesso per il canel di smorzamento dell'ampiezza

rhos_ad_twirled = []
for p in p_values:
    qc = QuantumCircuit(4)
    error = amplitude_damping_error(p)
    qc.append(error, [0])
    res_t = TwirledStateTomography(qc, qc_s, [0], measurement_indices=[0]).run(backend=backend).block_for_results()
    rhos_ad_twirled.append(res_t.analysis_results('state').value)

rhos_ad = []
for p in p_values:
    qc = qc_s.copy()
    error = amplitude_damping_error(p)
    qc.append(error, [0])
    res = StateTomography(qc, measurement_indices=[0]).run(backend=backend, shots=4096).block_for_results()
    rhos_ad.append(res.analysis_results('state').value)

tomo_rhos_ad_twirled = np.zeros((2,2,len(p_values)), dtype=complex)

for (i, res) in enumerate(rhos_ad_twirled):
    tomo_rhos_ad_twirled[:,:,i] = res

tomo_rhos_ad = np.zeros((2,2,len(p_values)), dtype=complex)

for (i, res) in enumerate(rhos_ad):
    tomo_rhos_ad[:,:,i] = res

plt.figure(figsize=(10,6))

# Risultati twirled 
plt.plot(p_values, np.real(tomo_rhos_ad_twirled[0,1,:]),"C0*", label='Re $\\rho_{01} twirled$')
plt.plot(p_values, np.imag(tomo_rhos_ad_twirled[0,1,:]),"C1*", label='Im $\\rho_{01} twirled$')
plt.plot(p_values, np.real(tomo_rhos_ad_twirled[0,0,:]),"C2x", label='$\\rho_{00} twirled$')
plt.plot(p_values, np.real(tomo_rhos_ad_twirled[1,1,:]),"C3x", label='$\\rho_{11} twirled$')

# Risultati non-twirled 
plt.plot(p_values, np.real(tomo_rhos_ad[0,1,:]),"C0--", label='Re $\\rho_{01}$')
plt.plot(p_values, np.imag(tomo_rhos_ad[0,1,:]),"C1--", label='Im $\\rho_{01}$')
plt.plot(p_values, np.real(tomo_rhos_ad[0,0,:]),"C2--", label='$\\rho_{00}$')
plt.plot(p_values, np.real(tomo_rhos_ad[1,1,:]),"C3--", label='$\\rho_{11}$')

plt.xlabel('p')
plt.ylabel('$\\rho_{xx}$')
plt.legend()

plt.title("Twirling of amplitude damping channel")
Text(0.5, 1.0, 'Twirling of amplitude damping channel')

Quando facciamo il twirling sul canale di smorzamento dell'ampiezza vediamo che la matrice densità si avvicina alla matrice identità similmente a quanto avviene nel canale di depolarizzazione.

Attività 4 (7 punti)

Infine, invertiamo o attenuiamo i canali di rumore sottoposti a twirling per estrarre la matrice di densità priva di rumore. Dopo il twirling, il canale di rumore dovrebbe assumere la forma di un canale di Pauli, che è relativamente facile da invertire se i coefficienti di rumore sono noti. Non è garantito che il canale invertito sia un canale fisico, poiché i coefficienti potrebbero essere negativi. La procedura per invertire i canali di rumore comuni è descritta in S. Mangini, et al. EPJ Quantum Technol. 9, 29 (2022).

L'inversa di un canale di Pauli generico con coefficienti $(p_{0}, p_{x}, p_{y}, p_{z})$ risulta essere $$\begin{aligned} & \Lambda^{-1}_{\boldsymbol{p}}(\Lambda) = \beta _{0} \Lambda + \beta _{1} \sigma _{x} {\Lambda} \sigma _{x} + \beta _{2} \sigma _{y} {\Lambda} \sigma _{y} + \beta _{3} \sigma _{z} {\Lambda} \sigma _{z}\quad \text{with} \\ & \beta _{0} =\frac{1}{4} \biggl(1+\frac{1}{1-2(p_{x}+p_{y})}+ \frac{1}{1-2(p_{x}+p_{z})}+\frac{1}{1-2(p_{y}+p_{z})} \biggr), \\ & \beta _{1} = \frac{1}{4} \biggl(1-\frac{1}{1-2(p_{x}+p_{y})}- \frac{1}{1-2(p_{x}+p_{z})}+\frac{1}{1-2(p_{y}+p_{z})} \biggr), \\ & \beta _{2} = \frac{1}{4} \biggl(1-\frac{1}{1-2(p_{x}+p_{y})}+ \frac{1}{1-2(p_{x}+p_{z})}-\frac{1}{1-2(p_{y}+p_{z})} \biggr), \\ & \beta _{3} = \frac{1}{4} \biggl(1+\frac{1}{1\red{-}2(p_{x}+p_{y})}- \frac{1}{1-2(p_{x}+p_{z})}-\frac{1}{1-2(p_{y}+p_{z})} \biggr) . \end{aligned}$$

Utilizzando i coefficienti dei canali di rumore ruotati dell'Attività 1, inverti ciascuna delle matrici di densità che hai trovato nell'Attività 3 (per ogni $p$ e $\theta$) e confronta $\rho_{00}$, $\rho_{11}$, $Re(\rho_{01})$, $Im(\rho_{01})$ con quelle non invertite, ad esempio in un grafico.

Suggerimento: le espressioni si semplificano notevolmente se si sostituiscono i coefficienti dell'Attività 1.

Solution

import numpy as np
from qiskit.quantum_info import Pauli

I, X, Y, Z = (Pauli(pauli).to_matrix() for pauli in ['I', 'X', 'Y', 'Z'])
rhos_bf_inverted = []

for p, rho in zip(p_values, rhos_bf_twirled):
    # coefficienti di inversione
    q0 = 1/2*(1+1/(1-2*p))
    q1 = 1/2*(1-1/(1-2*p))
    q2 = 0
    q3 = 0

    mit_rho = np.zeros((2,2), dtype=complex)
    for q, P in zip([q0,q1,q2,q3], [I, X, Y, Z]):
        mit_rho += q * P @ rho.data @ P

    rhos_bf_inverted.append(mit_rho)

tomo_rhos_bf_twirled = np.zeros((2,2,len(p_values)), dtype=complex)

for (i, res) in enumerate(rhos_bf_twirled):
    tomo_rhos_bf_twirled[:,:,i] = res

tomo_rhos_bf_inverted = np.zeros((2,2,len(p_values)), dtype=complex)

for (i, res) in enumerate(rhos_bf_inverted):
    tomo_rhos_bf_inverted[:,:,i] = res

plt.figure(figsize=(10,6))

# Risultati twirled 
plt.plot(p_values, np.real(tomo_rhos_bf_twirled[0,1,:]),"C0--", label='Re $\\rho_{01} twirled$')
plt.plot(p_values, np.imag(tomo_rhos_bf_twirled[0,1,:]),"C1--", label='Im $\\rho_{01} twirled$')
plt.plot(p_values, np.real(tomo_rhos_bf_twirled[0,0,:]),"C2--", label='$\\rho_{00} twirled$')
plt.plot(p_values, np.real(tomo_rhos_bf_twirled[1,1,:]),"C3--", label='$\\rho_{11} twirled$')

# Risultati non-twirled 
plt.plot(p_values, np.real(tomo_rhos_bf_inverted[0,1,:]),"C0*", label='Re $\\rho_{01} inverted$')
plt.plot(p_values, np.imag(tomo_rhos_bf_inverted[0,1,:]),"C1*", label='Im $\\rho_{01} inverted$')
plt.plot(p_values, np.real(tomo_rhos_bf_inverted[0,0,:]),"C2x", label='$\\rho_{00} inverted$')
plt.plot(p_values, np.real(tomo_rhos_bf_inverted[1,1,:]),"C3x", label='$\\rho_{11} inverted$')

plt.xlabel('p')
plt.ylabel('$\\rho_{xx}$')
plt.legend()

plt.title("Inversion of bit-flip channel")
Text(0.5, 1.0, 'Inversion of bit-flip channel')
rhos_cr_inverted = []

for t, rho in zip(t_values, rhos_cr_twirled):
    #  coefficienti d'inversione
    q0 = 1/2*(1+1/(1-2*np.sin(t/2)**2))
    q1 = 1/2*(1-1/(1-2*np.sin(t/2)**2))
    q2 = 0
    q3 = 0

    mit_rho = np.zeros((2,2), dtype=complex)
    for q, P in zip([q0,q1,q2,q3], [I, X, Y, Z]):
        mit_rho += q * P @ rho.data @ P

    rhos_cr_inverted.append(mit_rho)

tomo_rhos_cr_twirled = np.zeros((2,2,len(p_values)), dtype=complex)

for (i, res) in enumerate(rhos_cr_twirled):
    tomo_rhos_cr_twirled[:,:,i] = res

tomo_rhos_cr_inverted = np.zeros((2,2,len(p_values)), dtype=complex)

for (i, res) in enumerate(rhos_cr_inverted):
    tomo_rhos_cr_inverted[:,:,i] = res

plt.figure(figsize=(10,6))

# Risultati twirled 
plt.plot(t_values, np.real(tomo_rhos_cr_twirled[0,1,:]),"C0--", label='Re $\\rho_{01} twirled$')
plt.plot(t_values, np.imag(tomo_rhos_cr_twirled[0,1,:]),"C1--", label='Im $\\rho_{01} twirled$')
plt.plot(t_values, np.real(tomo_rhos_cr_twirled[0,0,:]),"C2--", label='$\\rho_{00} twirled$')
plt.plot(t_values, np.real(tomo_rhos_cr_twirled[1,1,:]),"C3--", label='$\\rho_{11} twirled$')

# Risultati non-twirled 
plt.plot(t_values, np.real(tomo_rhos_cr_inverted[0,1,:]),"C0*", label='Re $\\rho_{01} inverted$')
plt.plot(t_values, np.imag(tomo_rhos_cr_inverted[0,1,:]),"C1*", label='Im $\\rho_{01} inverted$')
plt.plot(t_values, np.real(tomo_rhos_cr_inverted[0,0,:]),"C2x", label='$\\rho_{00} inverted$')
plt.plot(t_values, np.real(tomo_rhos_cr_inverted[1,1,:]),"C3x", label='$\\rho_{11} inverted$')

plt.xlabel(r'$\theta$')
plt.ylabel('$\\rho_{xx}$')
plt.legend()

plt.title("Inversion of coherent rotation")
Text(0.5, 1.0, 'Inversion of coherent rotation')
rhos_ad_inverted = []

for p, rho in zip(p_values, rhos_ad_twirled):
    # coefficienti d'inversione
    q0 = 1/4*(1+1/np.sqrt(1-p))**2
    q1 = 1/4*(1-1/(1-p))
    q2 = 1/4*(1-1/(1-p))
    q3 = 1/4*(1-1/np.sqrt(1-p))**2

    mit_rho = np.zeros((2,2), dtype=complex)
    for q, P in zip([q0,q1,q2,q3], [I, X, Y, Z]):
        mit_rho += q * P @ rho.data @ P

    rhos_ad_inverted.append(mit_rho)

tomo_rhos_ad_twirled = np.zeros((2,2,len(p_values)), dtype=complex)

for (i, res) in enumerate(rhos_ad_twirled):
    tomo_rhos_ad_twirled[:,:,i] = res

tomo_rhos_ad_inverted = np.zeros((2,2,len(p_values)), dtype=complex)

for (i, res) in enumerate(rhos_ad_inverted):
    tomo_rhos_ad_inverted[:,:,i] = res

plt.figure(figsize=(10,6))

# Risultati twirled 
plt.plot(p_values, np.real(tomo_rhos_ad_twirled[0,1,:]),"C0--", label='Re $\\rho_{01} twirled$')
plt.plot(p_values, np.imag(tomo_rhos_ad_twirled[0,1,:]),"C1--", label='Im $\\rho_{01} twirled$')
plt.plot(p_values, np.real(tomo_rhos_ad_twirled[0,0,:]),"C2--", label='$\\rho_{00} twirled$')
plt.plot(p_values, np.real(tomo_rhos_ad_twirled[1,1,:]),"C3--", label='$\\rho_{11} twirled$')

# Risultati non-twirled 
plt.plot(p_values, np.real(tomo_rhos_ad_inverted[0,1,:]),"C0*", label='Re $\\rho_{01} inverted$')
plt.plot(p_values, np.imag(tomo_rhos_ad_inverted[0,1,:]),"C1*", label='Im $\\rho_{01} inverted$')
plt.plot(p_values, np.real(tomo_rhos_ad_inverted[0,0,:]),"C2x", label='$\\rho_{00} inverted$')
plt.plot(p_values, np.real(tomo_rhos_ad_inverted[1,1,:]),"C3x", label='$\\rho_{11} inverted$')

plt.xlabel('p')
plt.ylabel('$\\rho_{xx}$')
plt.legend()

plt.title("Inversion of amplitude damping channel")
Text(0.5, 1.0, 'Inversion of amplitude damping channel')

Possiamo notare che, per tutti i canali di rumore, siamo in grado di invertirli in modo da recuperare la matrice di densità priva di rumore. Ciò può essere fatto in modo relativamente semplice solo se il canale di rumore è nella forma di un canale di Pauli, che, per un canale di rumore generico, può essere ottenuta dopo applicato il twirling. Individuando i coefficienti di rumore presenti in un dispositivo quantistico reale, è possibile invertire anche il rumore presente in quel dispositivo! Trovare questi coefficienti può tuttavia rivelarsi impegnativo e questo problema è noto come caratterizzazione del rumore o apprendimento del rumore. In pratica, si desidera invertire il rumore per circuiti applicati su molti qubit, in particolare sui gate CNOT soggetti a rumore. Molti degli stessi principi possono essere applicati a tale caso, tuttavia i dettagli tecnici sono naturalmente più complessi. Invertire il rumore al centro di un circuito, ad esempio, richiede tecniche aggiuntive quali probabilistic error cancellation.