Kvantno merenje (quantum measurement)¶
U prethodnoj lekciji svako kolo se završavalo merenjem, tj. trećim i poslednjim korakom svakog kvantnog procesa. Sada se fokusiramo baš na taj korak:
šta merenje daje?
kako iz njega izvlačimo informaciju o stanju?
Merenje u računskoj bazi¶
Merenje kubita u računskoj bazi vraća jedan klasičan bit:
ishod sa verovatnoćom , ili
ishod sa .
Merenje je nepovratno: superpozicija „kolabira" u izmereno bazno stanje, pa jedno kolo daje tačno jedan bit.
Zato do verovatnoća dolazimo tek ponavljanjem: isto kolo pokrenemo mnogo puta tj. kreiramo uzorake (eng. shots) i prebrojimo ishode. Za stanje
očekujemo otprilike pola-pola raspodelu zastupljenosti na svakom od računskih stanja.
Python kod¶
osnovni kod¶
import numpy as np
# Korak 1: definisanje stanja
amplitudes = np.array([1/np.sqrt(2), 1/np.sqrt(2)], dtype=complex)
# Korak 2: Dobijanje verovatnoća
probs = np.abs(amplitudes) ** 2 # [0.5, 0.5]
# Korak 3: uzorkovanje
shots = 20
basis_states = np.array([0,1]) # [0, 1]
outcomes = np.random.choice(basis_states, size=shots, p=probs)
# Korak 4: sakupi sva uzorkovanja
counts = np.bincount(outcomes, minlength=len(amplitudes))
probabilities = counts / shots
# Korak 5: prikaži
print('Pojedinačni uzorci: ',counts)
print('Verovatnoće: ',probabilities)Vizuelizacija¶
import matplotlib.pyplot as plt
# kod za procesiranje pre prikazivanje histograma
qubit_number = len(bin(len(probabilities) - 1)) - 2 if len(probabilities) > 0 else 0
bit_strings = [bin(i)[2:].zfill(qubit_number) for i in range(len(probabilities))]
# Kreiranje histogram
plt.figure(figsize=(12, 6))
plt.bar(bit_strings, probabilities)
# Ose i naziv
plt.xlabel("Bit Stringovi")
plt.ylabel("Verovatnoća")
plt.title("Histogram verovatnoća")
plt.tight_layout()
plt.show()
Slika 1:Primer vizuelizacije histograma merenja. Ovo je izlaz za 20 uzoraka na primeru stanju. Pojedinačni uzorci: [11 9], Verovatnoće: [0.55 0.45].
Ekvivalent u QisKit¶
import numpy as np
from qiskit import QuantumCircuit
from qiskit.quantum_info import Statevector, Pauli
from qiskit.primitives import StatevectorSampler
# lokalni "simulator"; kasnije ga menjamo pravim hardverom
sampler = StatevectorSampler()
# priprema stanja |+> (bez merenja)
qc = QuantumCircuit(1, 1)
qc.h(0)
qc.measure(0,0) # kubit -> klasični bit
counts = sampler.run([qc], shots=1000).result()[0].data.c.get_counts()
print("Ishodi 1000 merenja:", counts) # ~ {'0': 500, '1': 500}Kako se broj ponavljanja povećava, tako se vrednosti približavaju tačnim verovatnoćama !
Sa sledećim kodom možemo da vidimo kako!
import numpy as np
import matplotlib.pyplot as plt
# Stanje: |+> = (|0> + |1>)/sqrt(2)
amplitudes = np.array([1/np.sqrt(2), 1/np.sqrt(2)], dtype=complex)
true_probs = np.abs(amplitudes) ** 2 # [0.5, 0.5]
# Menjaj broj uzorkovanja na logaritomskoj skali (logaritamska zbog bolje vizuelizacije kasnije)
shot_counts = np.unique(np.logspace(0, 5, 60).astype(int)) # 1 ... 100000
p0, p1 = [], []
rng = np.random.default_rng()
for shots in shot_counts:
outcomes = rng.choice(2, size=shots, p=true_probs)
counts = np.bincount(outcomes, minlength=2) / shots
p0.append(counts[0])
p1.append(counts[1])
# Grafik
plt.figure(figsize=(12, 6))
plt.plot(shot_counts, p0, color="blue", marker="o", markersize=4,
linewidth=1.2, label="P(0)")
plt.plot(shot_counts, p1, color="red", marker="o", markersize=4,
linewidth=1.2, label="P(1)")
plt.axhline(0.5, color="black", linestyle="--", linewidth=1, alpha=0.7,
label="Očekivane vrednosti (0.5)")
plt.xscale("log")
plt.xlabel("Broj uzorkovanja")
plt.ylabel("Verovatnoća")
plt.title(r"Kako merenja konvergiraju?")
plt.grid(True, alpha=0.2)
plt.legend()
plt.ylim(0, 1)
plt.tight_layout()
plt.show()
Slika 2:Primer vizuelizacije konvergencije ka očekivanim verovatnoćama sa povećavanjem broja uzoraka.
Koliko je kvantna mehanika zapravo nasumična?!¶
Vratimo se na stanje . Ono nam je davalo raspodelu pola-pola, pa deluje kao da je „slučajnost" ugrađena u sam kubit. Ali to nije sasvim tačno:
Setimo se Blohove sfere. Bazna stanja u kojima čitamo rezultat, i , sede na polovima ( i ). Stanje leži na ekvatoru (duž ), tačno podjednako udaljeno od oba pola. Otud i savršeno neodlučnih –. Ako želimo deterministički ishod, dovoljno je da stanje zarotiramo tako da njegov Blohov vektor pokazuje pravo na jedan od polova.

Slika 3: (crveno, ) leži na ekvatoru: merenje u računskoj bazi je maksimalno neodlučno. Rotacijom oko -ose za vektor dovodimo na pol (), gde merenje postaje deterministično.
Rotacija oko -ose¶
Za tako nešto koristimo rotacionu kapiju , koji rotira Blohov vektor u – ravni:
Primenimo ga na :
Verovatnoća ishoda 0 je kvadrat gornje amplitude, što se lepo izrazi (koristeći trigonometrijski identitet )
Sada samo tražimo ugao koji daje . To je , tj. . Uvrstimo:
Dakle vodi , i merenje sada uvek vraća 0. Analogno, vodi (uvek 1).
Python kod (samo NumPy)¶
import numpy as np
# Korak 1: polazno stanje |+> = (|0> + |1>)/sqrt(2)
plus = np.array([1/np.sqrt(2), 1/np.sqrt(2)], dtype=complex)
# Korak 2: rotacioni gejt R_y(theta) oko y-ose
def Ry(theta):
c, s = np.cos(theta/2), np.sin(theta/2)
return np.array([[c, -s],
[s, c]], dtype=complex)
# Korak 3: ugao koji |+> vodi u |0>
theta = -np.pi/2
psi = Ry(theta) @ plus
print("Stanje posle rotacije:", np.round(psi, 6)) # [1, 0]
# Korak 4: Bornove verovatnoće
probs = np.abs(psi) ** 2
print("Verovatnoće p(0), p(1):", np.round(probs, 6)) # [1, 0]
# Korak 5: uzorkovanje — svih 1000 ishoda je 0
shots = 1000
outcomes = np.random.choice([0, 1], size=shots, p=probs)
counts = np.bincount(outcomes, minlength=2)
print("Ishodi 1000 merenja:", counts) # [1000, 0]Histogram ovog kola nema više dve stubića! Sva verovatnoća je skoncentrisana na ishodu 0:

Slika 4:Nakon primenjenog na , svih 1000 uzoraka daje ishod 0. Slučajnosti više nema.
Nasumičnost se može „otkloniti"¶
Pošto je , ugao rotacije nam je zapravo promenljiva kojom biramo bilo koju verovatnoću između 0 i 1: dve tačke su potpuno determinističke.
import numpy as np
import matplotlib.pyplot as plt
theta = np.linspace(-np.pi, np.pi, 400)
p0 = (1 - np.sin(theta)) / 2
p1 = (1 + np.sin(theta)) / 2
plt.figure(figsize=(12, 6))
plt.plot(theta, p0, color="blue", lw=2, label="p(0)")
plt.plot(theta, p1, color="red", lw=2, label="p(1)")
plt.axhline(0.5, color="gray", ls="--", lw=1, alpha=0.7)
plt.xticks([-np.pi, -np.pi/2, 0, np.pi/2, np.pi],
[r"$-\pi$", r"$-\pi/2$", "0", r"$\pi/2$", r"$\pi$"])
plt.xlabel(r"Ugao rotacije $\theta$")
plt.ylabel("Verovatnoća")
plt.title(r"Verovatnoća ishoda kao funkcija rotacije $R_y(\theta)$ na $|+\rangle$")
plt.ylim(0, 1.05); plt.grid(alpha=0.2); plt.legend()
plt.tight_layout()
plt.show()
Slika 5:Rotacijom biramo verovatnoću ishoda. Na smo u polaznom (50–50); na dobijamo , a na dobijamo .
Merenje jednog kola daje jedan bit, ali koliko je taj bit nepredvidiv zavisi od baze. Isto stanje je nasumično u računskoj bazi, a savršeno predvidivo u bazi. Primeniti kapije pre merenja u računskoj bazi je zato isto što i meriti u zarotiranoj bazi.
Vežbe¶
Rešenje
Po Bornovom pravilu je i . Pošto je stanje fiksno, verovatnoće su tačne; uzorkovanjem dobijamo procenu koja se sa više shots-a stabilizuje oko 0.75 i 0.25.
import numpy as np
# stanje |psi> = (sqrt(3)/2)|0> + (1/2)|1>
amplitudes = np.array([np.sqrt(3)/2, 1/2], dtype=complex)
# Bornove verovatnoće
probs = np.abs(amplitudes) ** 2
print("p(0), p(1):", np.round(probs, 3)) # [0.75 0.25]
# uzorkovanje 1000 puta
shots = 1000
outcomes = np.random.choice([0, 1], size=shots, p=probs)
counts = np.bincount(outcomes, minlength=2)
print("Ishodi 1000 merenja:", counts) # ~ [750, 250]
print("Relativne učestanosti:", np.round(counts/shots, 3)) # ~ [0.75, 0.25]Rešenje
Iz sledi , pa je . Uvrštavanjem u i kvadriranjem gornje amplitude dobijamo tačno .
import numpy as np
def Ry(theta):
c, s = np.cos(theta/2), np.sin(theta/2)
return np.array([[c, -s],
[s, c]], dtype=complex)
plus = np.array([1/np.sqrt(2), 1/np.sqrt(2)], dtype=complex)
# traženi ugao iz p(0) = (1 - sin(theta))/2
p0_cilj = 0.85
theta = np.arcsin(1 - 2*p0_cilj)
print("theta:", round(theta, 4), "rad =", round(np.degrees(theta), 2), "stepeni") # ~ -0.775 rad, -44.43°
# provera: p(0) posle rotacije
psi = Ry(theta) @ plus
p0 = np.abs(psi[0]) ** 2
print("Postignuto p(0):", round(float(p0), 3)) # 0.85Jovan Odavić · dzovan90 [at] gmail [dot] com · www.jovanodavic.com