Porți și circuite cuantice: de la NOT și CNOT la algoritmi cu Qiskit
Dacă qubitul este unitatea de informare a calculului cuantic, porți cuantice sunt operațiunile care îl transformă — echivalentul operatorilor booleeni clasici. Dar, spre deosebire de clasicele SI, SAU și NU, porțile cuantice sunt întotdeauna reversibil și operează pe amplitudini vectoriale de stare, nu pe valori binare deterministe.
În acest articol, dezvoltăm înțelegerea principalelor porți - Hadamard, X, Z, CNOT, SWAP — și le folosim pentru a construi primul algoritm cuantic real: starea Bell, care demonstrează încurcarea. Apoi rulăm circuitul pe hardware IBM real prin IBM Quantum Platformă.
Ce vei învăța
- Porți de 1 qubit: Hadamard (H), X (NU), Z, S, T și matricele lor
- Porți de 2 qubit: CNOT, CZ, SWAP și Toffoli
- Construiți circuite cu Qiskit QuantumCircuit
- Stare Bell: Circuitul fundamental care demonstrează încurcarea
- Simulare cu Qiskit Aer și vizualizarea rezultatelor
- Rulează pe hardware IBM real cu Sampler Primitive
- Transpilarea: de la circuitul logic la porțile hardware native
1 Qubit Gates: Transformări vectoriale de stat
O poartă de 1 qubit și o matrice unitară 2x2 (U^† U = I) care transformă vectorul de stare. Unitatea garantează reversibilitatea — puteți oricând „anulați” operația prin aplicarea uşă adăugată.
# Porte a 1 qubit: definizione matematica e Qiskit
import numpy as np
from qiskit import QuantumCircuit
from qiskit.quantum_info import Operator, Statevector
# Gate X (Pauli-X) — il NOT quantistico
# Matrice: [[0, 1], [1, 0]]
# Effetto: |0⟩ -> |1⟩, |1⟩ -> |0⟩
print("Gate X (NOT):")
print(Operator.from_label('X').data)
qc_x = QuantumCircuit(1)
qc_x.x(0)
print(f"X|0⟩ = {Statevector.from_instruction(qc_x).data}")
# [0.+0.j, 1.+0.j] = |1⟩
# Gate Hadamard (H) — crea superposizione
# Matrice: [[1/√2, 1/√2], [1/√2, -1/√2]]
# Effetto: |0⟩ -> |+⟩, |1⟩ -> |−⟩
print("\nGate H (Hadamard):")
qc_h = QuantumCircuit(1)
qc_h.h(0)
print(f"H|0⟩ = {Statevector.from_instruction(qc_h).data}")
# [0.707+0.j, 0.707+0.j] = |+⟩ = (|0⟩+|1⟩)/√2
# Gate Z (Pauli-Z) — phase flip
# Matrice: [[1, 0], [0, -1]]
# Effetto: |0⟩ -> |0⟩, |1⟩ -> -|1⟩ (cambia la fase di |1⟩)
print("\nGate Z (Phase Flip):")
print(f"Z|+⟩ = ?")
qc_z = QuantumCircuit(1)
qc_z.h(0) # prima crea |+⟩
qc_z.z(0) # poi applica Z
print(f"Z|+⟩ = {Statevector.from_instruction(qc_z).data}")
# [0.707+0.j, -0.707+0.j] = |−⟩
# Gate S e T — rotazioni di fase piu fini
# S = [[1,0],[0,i]]: rotazione di π/2 attorno asse Z
# T = [[1,0],[0,e^{iπ/4}]]: rotazione di π/4 attorno asse Z
qc_st = QuantumCircuit(1)
qc_st.h(0)
qc_st.s(0)
qc_st.t(0)
print(f"\nT(S|+⟩) = {Statevector.from_instruction(qc_st).data}")
2 porți Qubit: CNOT, CZ și SWAP
Porțile de 2 qubiți funcționează pe perechi de qubiți și sunt necesare pentru a crea încurcare. Cel mai important este CNOT (Controlat-NU):
# Porte a 2 qubit
from qiskit import QuantumCircuit
from qiskit.quantum_info import Statevector
# CNOT (CX): se qubit control e |1⟩, flip il qubit target
# Matrice 4x4: [[1,0,0,0],[0,1,0,0],[0,0,0,1],[0,0,1,0]]
print("CNOT truth table:")
for control_state, target_state in [(0,0), (0,1), (1,0), (1,1)]:
qc = QuantumCircuit(2)
if control_state == 1:
qc.x(0)
if target_state == 1:
qc.x(1)
qc.cx(0, 1) # qubit 0 = control, qubit 1 = target
sv = Statevector.from_instruction(qc)
result_probs = sv.probabilities_dict()
result = max(result_probs, key=result_probs.get)
print(f" CNOT|{control_state}{target_state}⟩ = |{result}⟩")
# CNOT|00⟩ = |00⟩
# CNOT|01⟩ = |01⟩
# CNOT|10⟩ = |11⟩ <- target si inverte quando control e 1
# CNOT|11⟩ = |10⟩ <- target si inverte quando control e 1
# SWAP: scambia gli stati dei due qubit
qc_swap = QuantumCircuit(2)
qc_swap.x(0) # qubit 0 = |1⟩, qubit 1 = |0⟩
qc_swap.swap(0, 1)
sv_swap = Statevector.from_instruction(qc_swap)
print(f"\nSWAP|10⟩ = {max(sv_swap.probabilities_dict(), key=sv_swap.probabilities_dict().get)}")
# SWAP|10⟩ = |01⟩
# Toffoli (CCX): CNOT con 2 qubit di controllo
# Il gate universale per computazione classica reversibile
qc_toffoli = QuantumCircuit(3)
qc_toffoli.x(0) # control 1 = |1⟩
qc_toffoli.x(1) # control 2 = |1⟩
qc_toffoli.ccx(0, 1, 2) # flip target solo se entrambi i control sono |1⟩
sv_t = Statevector.from_instruction(qc_toffoli)
print(f"Toffoli|110⟩ = {max(sv_t.probabilities_dict(), key=sv_t.probabilities_dict().get)}")
# Toffoli|110⟩ = |111⟩
The Bell State: primul circuit complet
Starea Bell este cel mai simplu circuit care demonstrează încurcarea reală. Este nevoie doar două porți — Hadamard și CNOT — și stă la baza multor algoritmi cuantici:
# Bell State: costruzione, simulazione e analisi
from qiskit import QuantumCircuit
from qiskit.quantum_info import Statevector, DensityMatrix
from qiskit_aer import AerSimulator
from qiskit import transpile
# Costruzione del circuito Bell State |Φ+⟩
qc_bell = QuantumCircuit(2, 2, name='Bell State')
# Passo 1: Hadamard su qubit 0
# Stato: (|00⟩ + |10⟩) / √2
qc_bell.h(0)
# Passo 2: CNOT con qubit 0 come control, qubit 1 come target
# Stato: (|00⟩ + |11⟩) / √2 <- Bell state!
qc_bell.cx(0, 1)
# Visualizza il circuito
print("Circuito Bell State:")
print(qc_bell.draw('text'))
print()
# Analisi statevector (senza misura)
sv = Statevector.from_instruction(qc_bell)
print(f"Statevector: {sv.data}")
# [0.707, 0, 0, 0.707] -> 50% |00⟩, 50% |11⟩
print(f"Probabilita: {sv.probabilities_dict()}")
# {'00': 0.5, '11': 0.5} <- nessuna probabilita per '01' e '10'
# Aggiungi misura e simula
qc_bell.measure([0, 1], [0, 1])
sim = AerSimulator()
compiled = transpile(qc_bell, sim)
result = sim.run(compiled, shots=10000).result()
counts = result.get_counts()
print(f"\nRisultati simulazione (10000 shots): {counts}")
# {'00': ~5000, '11': ~5000} — MAI '01' o '10'
# Verifica entanglement: calcola concurrence
dm = DensityMatrix.from_instruction(QuantumCircuit(2).compose(
QuantumCircuit(2).compose(QuantumCircuit(2))
))
# (entanglement misurabile con partial trace e reduced density matrix)
Cele 4 State Bell
Există 4 stări Bell - baza spațiului Hilbert cu 4 dimensiuni de 2 qubiți:
# Tutti e 4 i Bell States
from qiskit import QuantumCircuit
from qiskit.quantum_info import Statevector
def create_bell_state(phi_plus=True, psi=False) -> QuantumCircuit:
"""
|Φ+⟩ = (|00⟩ + |11⟩)/√2 (phi_plus=True, psi=False)
|Φ-⟩ = (|00⟩ - |11⟩)/√2 (phi_plus=False, psi=False)
|Ψ+⟩ = (|01⟩ + |10⟩)/√2 (phi_plus=True, psi=True)
|Ψ-⟩ = (|01⟩ - |10⟩)/√2 (phi_plus=False, psi=True)
"""
qc = QuantumCircuit(2)
qc.h(0)
qc.cx(0, 1)
if psi:
qc.x(1) # Flip target: |00⟩ e |11⟩ diventano |01⟩ e |10⟩
if not phi_plus:
qc.z(0) # Phase flip: + diventa -
return qc
bell_states = {
'|Φ+⟩': create_bell_state(True, False),
'|Φ-⟩': create_bell_state(False, False),
'|Ψ+⟩': create_bell_state(True, True),
'|Ψ-⟩': create_bell_state(False, True),
}
for name, qc in bell_states.items():
sv = Statevector.from_instruction(qc)
probs = {k: round(v, 3) for k, v in sv.probabilities_dict().items() if v > 0.01}
print(f"{name}: probabilita = {probs}")
Rulează pe hardware IBM real
Simulați și util de dezvoltat, dar testul real și hardware-ul fizic. Iată fluxul de lucru complet cu Qiskit v2 pentru a rula starea Bell pe un procesor IBM real:
# Esecuzione su hardware IBM reale con Qiskit v2 Primitives
from qiskit import QuantumCircuit
from qiskit_ibm_runtime import QiskitRuntimeService, SamplerV2 as Sampler
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
# Autenticazione
service = QiskitRuntimeService(channel='ibm_quantum')
# Seleziona il backend meno occupato (per account gratuito)
backend = service.least_busy(
operational=True,
simulator=False,
min_num_qubits=2
)
print(f"Backend: {backend.name}")
print(f"Qubit disponibili: {backend.num_qubits}")
print(f"Job in coda: {backend.status().pending_jobs}")
# Crea il circuito Bell State
qc = QuantumCircuit(2, 2)
qc.h(0)
qc.cx(0, 1)
qc.measure([0, 1], [0, 1])
# NUOVO in Qiskit v2: transpile ottimizzato per l'hardware specifico
# 83x piu veloce rispetto a Qiskit 0.x per circuiti complessi
pass_manager = generate_preset_pass_manager(
backend=backend,
optimization_level=1 # 0=veloce, 1=bilanciato, 2=lento ma ottimale
)
isa_circuit = pass_manager.run(qc)
print(f"\nGate originali: {qc.count_ops()}")
print(f"Gate dopo transpilation: {isa_circuit.count_ops()}")
# Il transpiler aggiunge swap gates perche l'hardware ha connettivita limitata
# Esegui con SamplerV2 Primitive
sampler = Sampler(backend)
job = sampler.run([isa_circuit], shots=4096)
print(f"\nJob ID: {job.job_id()}")
print("Attendere risultati dall'hardware quantistico...")
print("(tipicamente 1-5 minuti in coda + 30 secondi di esecuzione)")
result = job.result()
counts = result[0].data.c.get_counts()
print(f"\nRisultati hardware reale (4096 shots):")
for state, count in sorted(counts.items()):
print(f" |{state}⟩: {count} ({count/4096*100:.1f}%)")
# Output tipico su hardware reale (con noise):
# |00⟩: ~1850 (45.2%) <- meno di 50% a causa del noise
# |11⟩: ~1920 (46.9%)
# |01⟩: ~150 (3.7%) <- errori da gate noise
# |10⟩: ~176 (4.3%) <- errori da gate noise
Transpilarea: de la circuitul logic la hardware
Un circuit logic folosește porți abstracte (H, CNOT etc.), dar fiecare procesor IBM are un set de diferite porți native. Transpilerul convertește circuitul logic în instrucțiuni native a hardware-ului specific, optimizând și rutarea (qubiții fizici nu sunt toți conectate între ele).
# Analisi della transpilation
from qiskit import QuantumCircuit
from qiskit_ibm_runtime import QiskitRuntimeService
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
service = QiskitRuntimeService(channel='ibm_quantum')
backend = service.least_busy(operational=True, simulator=False)
# Circuito di esempio: 3 qubit, multiple gate
qc = QuantumCircuit(3)
qc.h(0)
qc.cx(0, 1)
qc.cx(1, 2)
qc.rx(0.5, 0)
qc.ry(1.0, 1)
# Transpilation a diversi livelli di ottimizzazione
for opt_level in [0, 1, 2]:
pm = generate_preset_pass_manager(backend=backend, optimization_level=opt_level)
transpiled = pm.run(qc)
ops = transpiled.count_ops()
total_gates = sum(ops.values())
print(f"Optimization level {opt_level}: {total_gates} gates totali, ops={ops}")
# L'IBM Heron ha come native gates: CZ, RZ, SX, X
# Il transpiler decompone ogni gate logico in queste native gate
# Ottimizzazione level 2 riduce tipicamente il circuit depth del 20-40%
Limitări importante de reținut
- Zgomot hardware: Rezultatele pe hardware real includ întotdeauna erori. Nu utilizați hardware cuantic atunci când aveți nevoie de rezultate deterministe - utilizați simulatorul pentru dezvoltare și hardware pentru validarea finală
- Limitele de adâncime a circuitului: Porțile trebuie să se completeze înainte de decoerență. Pe IBM Heron 2026, adâncimea practică maximă și ~100-200 de straturi înainte ca zgomotul să fie excesiv
- Timp la coadă: timpul de așteptare pe hardware real poate varia de la minute la ore. Planificați în consecință fluxurile de lucru de dezvoltare
- Universalitatea poarta: H + CNOT (sau Toffoli) sunt universale — oricare calcul clasic computabil și exprimabil cu aceste porți
Concluzii
Porțile cuantice sunt transformări liniare reversibile pe vectorul de stare — din punct de vedere matematic elegant și puternic din punct de vedere computațional. Starea Bell demonstrează în 2 porți ceva ce nu are echivalent clasic: încurcare. Fluxul de lucru Qiskit v2 — design de circuit, transpilare, Sampler Primitiv — și suficient de matur pentru a sprijini dezvoltarea reală.
Următorul articol folosește aceste blocuri pentru a construi primul algoritm cuantic avantaj: algoritmul lui Grover pentru căutarea pătratică O(sqrt N) pe baze de date nestructurate.







