Non utilizzare il metodo di scambio, è molto inefficiente. E la risposta dell'altra persona è specifica per il cancello CNOT e, ad essere sinceri, complica troppo le cose.
Ecco un algoritmo molto semplice che risolve il problema per ogni singolo caso, non solo per la porta CNOT, per eventuali bit arbitrari.
L'algoritmo:
let sys = matrix representing the current state of the system
let n = number of qubits being simulated
let lgm = logic gate matrix of size 2^n by 2^n
let f = our logic gate transformation function
for i = 0 to (2^n) - 1:
lgm[column = i] = f(i)
sys = sys × lgg
Nei computer classici, c'è qualcosa noto come "decodificatore". Diciamo che ho solo 3 fili, in effetti, 3 bit. Ma voglio controllare 8 fili. Può essere fatto? Sì, perché 3 bit ha 8 diverse possibilità: 000, 001, 010, 011, 100, 101, 110, 111. Quindi possiamo assegnare ogni possibilità a uno dei nostri 8 fili di uscita. Questo si chiama "decodifica".
Se passo il numero 101 e abbiamo 3 bit, conosciamo 101 = 5, quindi imposterò il filo di uscita 5 su un'alta tensione e gli altri 7 fili di uscita saranno 0, che possiamo rappresentare in questo modo: decodifica (101) = [0, 0, 0, 0, 0, 1, 0, 0].
In questo algoritmo, menziono la "funzione di trasformazione" chiamata "f". Per i computer classici, la funzione di trasformazione sta semplicemente rilevando un valore di input e restituendo la versione "decodificata" del valore di output. Quindi se abbiamo 3 bit e l'output è 4, restituiremo [0, 0, 0, 0, 1, 0, 0, 0]. Quindi lo assegniamo come colonna della nostra matrice per quel valore.
Pensiamo alla "decodifica" in termini di qubit. Come possiamo decodificare i qubit | 101>?
Sappiamo che per i nostri vettori di probabilità qubit, | 0> è [1, 0] e | 1> è [0, 1]. I qubit di decodifica possono quindi essere eseguiti con il cosiddetto prodotto Kronecker.
Quindi se convertiamo ogni bit nell'equivalente del vettore di probabilità e prendiamo il prodotto Kronecker di tutti loro, otteniamo ...
|101> = |1> ⊗ |0> ⊗ |1> = [0, 1] ⊗ [1, 0] ⊗ [0, 1] = [0, 0, 0, 0, 0, 1, 0, 0]
Ecco come vorremmo decodificare i qubit. Questo algoritmo può essere applicato allo stesso qubit usando questo.
Proviamo questo algoritmo su un problema più semplice.
Supponiamo di avere un sistema con solo 2 qubit. Se vogliamo applicare il gate Hadamard a solo 1 qubit, possiamo generare un gate logico per entrambi i qubit che applica il gate Hadamard solo a un singolo qubit. Supponiamo che il singolo qubit a cui vogliamo applicarlo sia il nostro qubit più significativo e il meno significativo non sarà interessato.
Vogliamo una funzione di trasformazione che per ciascuno dei nostri possibili input, produca l'output corretto. Abbiamo due qubit, questo significa che ci sono quattro possibili output.
f(|00>) = ?
f(|01>) = ?
f(|10>) = ?
f(|11>) = ?
Sappiamo che il qubit meno significativo non sarà interessato, quindi possiamo riempirlo.
f(|00>) = ? ⊗ |0>
f(|01>) = ? ⊗ |1>
f(|10>) = ? ⊗ |0>
f(|11>) = ? ⊗ |1>
Sappiamo anche cosa fa l'Hadamard con un qubit, in modo tale che:
H(|0>) = 1/sqrt(2)|0> + 1/sqrt(2)|1>
H(|1>) = 1/sqrt(2)|0> - 1/sqrt(2)|1>
Quindi la nostra funzione di trasformazione è semplicemente:
f(|00>) = (1/sqrt(2)|0> + 1/sqrt(2)|1>) ⊗ |0>
f(|01>) = (1/sqrt(2)|0> + 1/sqrt(2)|1>) ⊗ |1>
f(|10>) = (1/sqrt(2)|0> - 1/sqrt(2)|1>) ⊗ |0>
f(|11>) = (1/sqrt(2)|0> - 1/sqrt(2)|1>) ⊗ |1>
Espandilo nel nostro modulo vettoriale di probabilità normalizzato ...
f(|00>) = [ 1/sqrt(2), 1/sqrt(2) ] ⊗ [ 1, 0 ]
f(|01>) = [ 1/sqrt(2), 1/sqrt(2) ] ⊗ [ 0, 1 ]
f(|10>) = [ 1/sqrt(2), -1/sqrt(2) ] ⊗ [ 1, 0 ]
f(|11>) = [ 1/sqrt(2), -1/sqrt(2) ] ⊗ [ 0, 1 ]
Ora risolviamo davvero questo ...
f(|00>) = [ 1/sqrt(2), 0, 1/sqrt(2), 0 ]
f(|01>) = [ 0, 1/sqrt(2), 0, 1/sqrt(2) ]
f(|10>) = [ 1/sqrt(2), 0, -1/sqrt(2), 0 ]
f(|11>) = [ 0, 1/sqrt(2), 0, -1/sqrt(2) ]
Questa è la nostra funzione di trasformazione.
La nostra matrice di gate logici, "lgm", ha dimensioni 2 ^ n per 2 ^ n dove n = numero di qubit da simulare, quindi in questo caso è 2 ^ 2 per 2 ^ 2 o 4x4. Il nostro algoritmo ci dice che per ogni colonna i, impostare la colonna uguale a f (i). Abbiamo definito la nostra funzione di trasformazione delle probabilità, in modo da poter compilare facilmente queste colonne.
lgm =
|00> |01> |10> |11>
[ 1/sqrt(2), 0, 1/sqrt(2), 0 ]
[ 0, 1/sqrt(2), 0, 1/sqrt(2) ]
[ 1/sqrt(2), 0, -1/sqrt(2), 0 ]
[ 0, 1/sqrt(2), 0, -1/sqrt(2) ]
Ora il passo finale nel nostro algoritmo è semplicemente la moltiplicazione della matrice che rappresenta l'intero sistema quantistico, sys, per questa porta logica, lgm.
E questo fa quello che vogliamo. Applicherà il gate hadamard solo al maggior qubit e lascerà solo il qubit meno significativo. Se non mi credi, puoi provarlo tu stesso e vedere che funziona.
Il motivo per cui è così potente è perché si applica a tutti i casi.
Proviamo questo algoritmo sul tuo problema.
Immagina di avere un sistema a 3 qubit e vogliamo applicare un gate CNOT a qubit [0] e qubit [2]. Se cerchi la matrice CNOT su Wikipedia, quella matrice si applica solo a un sistema a 2 qubit. Una soluzione ingenua sarebbe quella di aggiungere ad essa matrici di identità usando il prodotto Kronecker per farlo funzionare su sistemi con tre qubit. Ma questo non riesce qui: qubit [0] e qubit [2] non sono adiacenti, quindi semplicemente aggiungere matrici di identità non funzionerà.
Abbiamo potuto scambiare qubit [0] con qubit [1], applicare il cancello CNOT, poi scambiare indietro. Ma è lento. Invece, potremmo semplicemente generare un gate CNOT non adiacente per il nostro problema usando l'algoritmo sopra.
Dobbiamo prima trovare una funzione di trasformazione per ogni caso.
f(|000>) = |0> ⊗ |0> ⊗ |0>
f(|001>) = |0> ⊗ |0> ⊗ |1>
f(|010>) = |0> ⊗ |1> ⊗ |0>
f(|011>) = |0> ⊗ |1> ⊗ |1>
f(|100>) = |1> ⊗ |0> ⊗ |1>
f(|101>) = |1> ⊗ |0> ⊗ |0>
f(|110>) = |1> ⊗ |1> ⊗ |1>
f(|111>) = |1> ⊗ |1> ⊗ |0>
Se capisci la porta CNOT, puoi capire perché questa è la nostra funzione. Pensa a questo come a una tabella di verità. Poiché il nostro qubit di controllo è il qubit più significativo, qubit [2], solo quando quel qubit è | 1> verrà annullato il qubit meno significativo, qubit [0].
Espandilo nel nostro modulo vettoriale di probabilità normalizzato ...
f(|000>) = [ 1, 0 ] ⊗ [ 1, 0 ] ⊗ [ 1, 0 ]
f(|001>) = [ 1, 0 ] ⊗ [ 1, 0 ] ⊗ [ 0, 1 ]
f(|010>) = [ 1, 0 ] ⊗ [ 0, 1 ] ⊗ [ 1, 0 ]
f(|011>) = [ 1, 0 ] ⊗ [ 0, 1 ] ⊗ [ 0, 1 ]
f(|100>) = [ 0, 1 ] ⊗ [ 1, 0 ] ⊗ [ 0, 1 ]
f(|101>) = [ 0, 1 ] ⊗ [ 1, 0 ] ⊗ [ 1, 0 ]
f(|110>) = [ 0, 1 ] ⊗ [ 0, 1 ] ⊗ [ 0, 1 ]
f(|111>) = [ 0, 1 ] ⊗ [ 0, 1 ] ⊗ [ 1, 0 ]
Ora risolviamo davvero questo ...
f(|000>) = [ 1, 0, 0, 0, 0, 0, 0, 0 ]
f(|001>) = [ 0, 1, 0, 0, 0, 0, 0, 0 ]
f(|010>) = [ 0, 0, 1, 0, 0, 0, 0, 0 ]
f(|011>) = [ 0, 0, 0, 1, 0, 0, 0, 0 ]
f(|100>) = [ 0, 0, 0, 0, 0, 1, 0, 0 ]
f(|101>) = [ 0, 0, 0, 0, 1, 0, 0, 0 ]
f(|110>) = [ 0, 0, 0, 0, 0, 0, 0, 1 ]
f(|111>) = [ 0, 0, 0, 0, 0, 0, 1, 0 ]
Rendiamo queste le colonne della nostra matrice LGG.
lgm =
[ 1, 0, 0, 0, 0, 0, 0, 0 ]
[ 0, 1, 0, 0, 0, 0, 0, 0 ]
[ 0, 0, 1, 0, 0, 0, 0, 0 ]
[ 0, 0, 0, 1, 0, 0, 0, 0 ]
[ 0, 0, 0, 0, 0, 1, 0, 0 ]
[ 0, 0, 0, 0, 1, 0, 0, 0 ]
[ 0, 0, 0, 0, 0, 0, 0, 1 ]
[ 0, 0, 0, 0, 0, 0, 1, 0 ]
Ora se la matrice si moltiplica la nostra intera matrice di sistema, sys, per la nostra matrice di gate logici, lgm, il nostro risultato sarà l'effetto dell'applicazione del gate CNOT a qubit [2] e qubit [0] dove qubit [2] è il controllo qubit.