Skip to content

4. Mappatura dei Difetti Quantistici

tatopenn-cell edited this page Jul 25, 2026 · 3 revisions

4. Mappatura dei Difetti Quantistici

quantum_defect_scanner.py valuta, nodo per nodo, la coerenza $\langle X_q \rangle$ residua di una catena di 12 qubit entangled sotto una rotazione RZ di dephasing localizzata su un solo qubit alla volta — 12 tracce concorrenti in un'unica chiamata run_parametric_batch_jit().

$$\langle X_q \rangle = \text{Re}\left[\sum_i \psi_i^* \psi_{i \oplus 2^q}\right]$$

Circuito

base_ops = [['ry', q, pi/4] for q in range(N_Q)] \
         + [['rz', q, f"batch_param_{q}"] for q in range(N_Q)] \
         + [['cx', q, q + 1] for q in range(N_Q - 1)]

RY($\pi/4$) uniforme su tutti i qubit, poi una scala di gate RZ (uno per qubit, destinati a variare indipendentemente nel batch), poi una catena CX.

Il bug (stesso pattern del capitolo 3)

Lo script assumeva che i 12 gate RY($\pi/4$) — passati come float letterale — restassero fissi, mentre solo i 12 RZ (passati come placeholder stringa) variassero secondo la griglia diagonale $N_Q \times N_Q$. Ma run_parametric_batch_jit non distingue: ogni gate di rotazione, letterale o no, consuma uno slot posizionale. Il circuito ha 24 slot (12 RY + 12 RZ), la griglia ne aveva solo 12 — i valori RZ finivano sui gate RY, e i veri RZ finivano fuori indice (clippati silenziosamente da JAX).

Verificato empiricamente: confrontando l'output del batch contro un circuito singolo costruito secondo l'assunzione originale dello script, lo scarto sullo statevector arrivava a 0.66 — non rumore, un altro circuito.

Fix

griglia_parametri = np.zeros((N_Q, 2 * N_Q), dtype=np.float64)
griglia_parametri[:, :N_Q] = np.pi / 4          # colonne RY, costanti
for q in range(N_Q):
    griglia_parametri[q, N_Q + q] = 0.5          # colonne RZ, diagonale

Cosa è cambiato nei numeri pubblicati (primo giro)

mappa_difetti_silicio.csv/.png rigenerati. Prima (bacato): un unico numero "42.0735%" a un nodo di bordo, descritto come "resilienza di bordo asimmetrica" — un artefatto del bug, non fisica reale. Dopo il primo fix: coerenza $70.71%$ al primo nodo, $43.88%$ al nodo 6, esattamente $50.00%$ ovunque altrove.

Secondo bug, trovato mentre si cercava di spiegare quei numeri

Provando a derivare analiticamente perché proprio quel pattern (nodo 0 diverso, nodo 6 diverso, resto a 50%), è saltato fuori un secondo problema indipendente, di indicizzazione: DenseSVSimulator usa internamente la convenzione MSB-first ($\text{phys} = N_Q{-}1{-}\text{qubit}$, vedi _cx_numpy/apply_cx in dense_evolution/simulator.py) — il qubit q del circuito vive al bit fisico N_Q-1-q dell'array dello statevector, non al bit q direttamente.

Lo script misurava la coerenza con mask = 1 << local_qubit, usando l'indice del gate direttamente come bit fisico — leggendo quindi la coerenza di un qubit diverso da quello che aveva davvero ricevuto la dephasing in quella riga. Il pattern "70.71% / 50% / 43.88%" descritto sopra, e la spiegazione ("interazione reale tra lo strato RY e la scala CX") erano l'artefatto di questo mismatch, non fisica vera.

Verificato con un test isolato: preparando un solo qubit con un angolo RY diverso dagli altri (nessun CX, quindi ogni qubit è indipendente) e leggendo la coerenza con l'indice corretto (1 << (N_Q-1-q)), si recupera esattamente il qubit giusto; con l'indice sbagliato, no.

Il vero pattern (dopo entrambi i fix)

Molto più semplice: 11 nodi su 12 danno esattamente lo stesso valore, $43.88%$, e solo l'ultimo nodo della catena è diverso, a $62.05%$.

Non abbiamo una spiegazione analitica completa del perché esattamente un nodo si comporti diversamente. Un primo tentativo di derivazione assumeva che "il qubit di controllo di un CNOT mantiene la propria coerenza" — un'invarianza che vale per le popolazioni in base Z, non per la coerenza X (verificato con un CNOT isolato: la coerenza del controllo crolla a zero, non resta invariata). Non avendo un meccanismo verificato rigorosamente, il pattern è riportato qui come fatto empirico confermato, non come teorema dimostrato — meglio ammetterlo che inventare una spiegazione plausibile ma sbagliata.

Copertura test

tests/test_quantum_defect_scanner.py:

  • test_batch_positional_slots_ry_fixed_rz_varying — costruisce la griglia corretta a 24 colonne e verifica ogni riga del batch contro un riferimento indipendente a circuito singolo, tolleranza $10^{-10}$.
  • test_coerenza_x_reads_the_correct_msb_first_physical_bit — verifica che coerenza_x() legga il bit fisico giusto, su uno stato prodotto senza CX dove ogni qubit è indipendente.
  • test_defect_scan_gives_the_index_corrected_pattern — chiama scansiona_difetti(), la funzione reale (non una copia), e verifica il pattern "11 identici + 1 diverso".

Aggiornamento: entrambi i bug di questo capitolo hanno una controparte upstream ora corretta in dense-evolution 8.1.28: il mismatch di colonne di run_parametric_batch_jit (#6) solleva ValueError invece di clippare in silenzio, e un nome di gate non riconosciuto in run_circuit/run_circuit_jit_beast_mode/run_parametric_batch_jit (#4, lo stesso tipo di problema strutturale della lacuna GATE_IDS già menzionata sopra per cy/cp/crz/u1/p/sx) solleva ValueError invece di sparire senza traccia. I fix specifici di questo script (griglia a 24 colonne, indicizzazione MSB-first) restano necessari indipendentemente dalla versione del pacchetto.


Capitolo 3 · Home · Capitolo 5 ▶