Kvantni programi
Svi kvantni programi iz javnog repozitorijuma https://github.com/psimosaic/Quantum-Programs. Izaberi program sa leve strane da vidiš kompletan kod — možeš ga kopirati ili preuzeti.
Programi (18)
README
PsiMosaic – Quantum Programs
Kolekcija edukativnih kvantnih programa napisanih u Pythonu pomoću Qiskit biblioteke. Svaki program demonstrira jedan koncept kvantnog računarstva (kvantni gejtovi, superpozicija, entanglement, kvantni algoritmi) sa vizualizacijom Blohove sfere, dijagrama kola i rezultata merenja.
Sadržaj
| Tema | Fajlovi |
|---|---|
| Osnove i gejtovi | Stanja kubita.py, Paulijevi gejtovi.py, S gejt.py, Tofoli gejt.py, CP.py, X into Z gate.py, Z into X gate.py |
| Entanglement / teleportacija | Entanglement 01.py, Kvantna_teleportacija.py |
| Algoritmi | Dojcov algoritam.py (Deutsch), Sorov_algoritam.py (Shor), QPE.py (Quantum Phase Estimation) |
| Furijeova transformacija | QFT.py, iQFT.py, QFT_Kodiranje_broja_u_fazu.py |
| Vežbe | Uradi sam.py |
| Pomoćna biblioteka | quantum_utils_v1.py (vizualizacija i analiza, koriste je svi programi) |
Zahtevi
- Python 3.10 ili noviji
- Zavisnosti navedene u
requirements.txt:qiskit,qiskit-aer,numpy,matplotlib,pylatexenc(crtanje kola),seaborn(qsphere prikaz)
Instalacija
Windows (PowerShell)
# 1. Kloniraj repozitorijum
git clone <URL-repozitorijuma>
cd "GitHub-PsiMosaic-Quantum Programs"
# 2. Napravi i aktiviraj virtuelno okruženje
python -m venv .venv
.\.venv\Scripts\Activate.ps1
# 3. Instaliraj zavisnosti
pip install -r requirements.txt
Ako PowerShell blokira aktivaciju skripte, jednom pokreni:
Set-ExecutionPolicy -Scope CurrentUser -ExecutionPolicy RemoteSigned
macOS / Linux (bash)
git clone <URL-repozitorijuma>
cd "GitHub-PsiMosaic-Quantum Programs"
python3 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt
Pokretanje
Sa aktiviranim virtuelnim okruženjem, pokreni bilo koji program:
python "Stanja kubita.py"
python "Sorov_algoritam.py"
Programi otvaraju matplotlib prozore sa Blohovom sferom, dijagramom kola i histogramom merenja.
Slova u izlazu (UTF-8)
Programi ispisuju tekst sa slovima (č, ć, ψ, ⟩). Na nekim Windows terminalima
podrazumevano kodiranje konzole (cp1252) ne ume da ih prikaže, pa može da iskoči
UnicodeEncodeError. Ako se to desi, uključi UTF-8 izlaz pre pokretanja (jednom po sesiji):
$env:PYTHONUTF8 = "1"
Na macOS/Linux terminalima je UTF-8 podrazumevan i nije potrebno ništa podešavati.
Napomena o quantum_utils_v1.py
Svi programi uvoze pomoćnu biblioteku sa import quantum_utils_v1 as q. Pošto se nalazi u
istom folderu kao i programi, Python je automatski pronalazi pri pokretanju skripte — nije
potrebna dodatna konfiguracija.
Deaktivacija okruženja
deactivate
CP
"""
KONTROLISANI FAZNI GEJT (Controlled-Phase, CP)
==============================================
Ovaj program pokazuje kako radi kontrolisani fazni gejt `cp(θ, kontrola, cilj)`.
CP dodaje fazu e^{iθ} samo onom baznom stanju u kome su OBA kubita
(i kontrola i cilj) jednaka |1⟩. Na stanja |00⟩, |01⟩ i |10⟩ nema uticaja.
Zato je CP "entanglujući" gejt: kada je kontrolni kubit u superpoziciji,
faza se upisuje na način koji povezuje (entangluje) dva kubita.
"""
import math
from qiskit import QuantumCircuit
import quantum_utils_v1 as q
# Fazni ugao θ koji dodajemo: θ = 2π·(1/8) = π/4 (osmina punog kruga).
angle = 2 * math.pi / 8 # φ = 1/8 → fazni ugao = 2π/8 = π/4
# Kolo sa 2 kubita i 2 klasična bita (klasični bitovi služe za merenje na kraju).
qc = QuantumCircuit(2, 2)
qc.reset([0, 1]) # garantujemo početno stanje |00⟩
qc.x(1) # kubit 1 → |1⟩ (da bi CP uopšte imao na šta da deluje)
q.show_bloch_sphere(qc) # početno stanje
# Korak 1: kubit 0 u superpoziciju |0⟩ → (|0⟩+|1⟩)/√2
qc.h(0)
qc.barrier()
q.show_bloch_sphere(qc)
# Korak 2: kontrolisani fazni gejt, kontrola = kubit 0, cilj = kubit 1.
# Pošto je kubit 0 u superpoziciji, faza e^{iθ} pogađa samo komponentu |11⟩.
qc.cp(angle, 0, 1)
qc.barrier()
q.show_bloch_sphere(qc)
q.print_state(qc, "State:")
# Merenje oba kubita u pripadajuće klasične bitove.
qc.measure([0, 1], [0, 1])
# Prikaz dijagrama kola i statistike merenja (histogram).
q.show_qc(qc)
q.show_measurement(qc)
Dojcov algoritam
"""
DOJČOV (DEUTSCH) ALGORITAM
==========================
Prvi algoritam koji pokazuje kvantnu prednost nad klasičnim računanjem.
Problem: data je funkcija f koja jednom bitu pridružuje jedan bit. Ona je ili
- KONSTANTNA (f(0) = f(1)), ili
- BALANSIRANA (f(0) ≠ f(1)).
Pitanje je samo: koja je od te dve vrste?
Klasično moramo da pozovemo f DVA puta (i za x=0 i za x=1) da bismo bili sigurni.
Dojčov algoritam to rešava sa SAMO JEDNIM pozivom funkcije (oracle-a), koristeći
superpoziciju i "phase kickback".
Trik je u pomoćnom kubitu koji stavljamo u |−⟩: tada oracle umesto da promeni
njegovu vrednost, "vrati" informaciju o f kao FAZU na prvi kubit. Završni Adamar
tu fazu pretvara u merljiv rezultat:
- merenje 0 → funkcija je KONSTANTNA
- merenje 1 → funkcija je BALANSIRANA
Ispod su definisana dva oracle-a; menjaj koji je aktivan u KORAKU 4 da uporediš ishode.
"""
from qiskit import QuantumCircuit
import quantum_utils_v1 as q
# ============================================================================
# DEFINICIJA ORACLE FUNKCIJA (Uf: |x>|y> -> |x>|y ⊕ f(x)>)
# ============================================================================
def balanced_oracle(qc):
# f(x) = x => y ⊕ x (CNOT: kontrola x=q0, cilj y=q1)
qc.cx(0, 1)
return qc
def constant_oracle(qc):
# f(x) = 1
qc.id(1)
return qc
# ----------------------------------------------------------------------------
# KORAK 1: INICIJALIZACIJA
# ----------------------------------------------------------------------------
qc = QuantumCircuit(2, 1)
qc.barrier()
q.show_bloch_sphere(qc) # početno stanje oba kubita: |00⟩
# ----------------------------------------------------------------------------
# KORAK 2: PRIPREMA POMOĆNOG KUBITA q1 U |1⟩
# Napomena: u ket redosledu |q1 q0⟩, posle X na q1 fizičko stanje je |10⟩
# ----------------------------------------------------------------------------
qc.x(1)
qc.barrier()
q.show_bloch_sphere(qc)
# ----------------------------------------------------------------------------
# KORAK 3: H-gejt NA OBA KUBITA (priprema |+⟩ i |−⟩)
# q0: |0⟩ -> |+⟩
# q1: |1⟩ -> |−⟩
# ----------------------------------------------------------------------------
qc.h(0)
qc.h(1)
qc.barrier()
q.show_bloch_sphere(qc)
# ----------------------------------------------------------------------------
# KORAK 4: PRIMENA ORACLE-A
# ----------------------------------------------------------------------------
# qc = constant_oracle(qc) # f(x)=1
qc = balanced_oracle(qc) # f(x)=x
qc.barrier()
q.show_bloch_sphere(qc)
# ----------------------------------------------------------------------------
# KORAK 5: FINALNI H-gejt
# ----------------------------------------------------------------------------
qc.h(0)
qc.h(1)
qc.barrier()
q.show_bloch_sphere(qc)
# ----------------------------------------------------------------------------
# KORAK 6: MERENJE PRVOG KUBITA
# ----------------------------------------------------------------------------
qc.measure(0, 0)
q.show_qc(qc)
q.show_measurement(qc)
Entanglement 01
from qiskit import QuantumCircuit
import quantum_utils_v1 as q
"""
ENTANGLMENT — BELOVO STANJE
==========================================
Program kreira najpoznatije spregnuto stanje, Belov par:
|Φ+⟩ = (|00⟩ + |11⟩) / √2
Recept za Belovo stanje je svega dva gejta:
1) H na kubitu 0 → superpozicija (|0⟩+|1⟩)/√2
2) CNOT
Rezultat je stanje koje se NE može razdvojiti na zaseban opis kubita 0 i kubita 1.
Zato na Blohovoj sferi svaki pojedinačni kubit izgleda "prazno" (u centru sfere) —
informacija nije u pojedinačnim kubitima, već u njihovoj KORELACIJI. Merenjem ćemo
dobiti samo |00⟩ ili |11⟩ (oba kubita uvek isti), nikada |01⟩ ili |10⟩.
"""
qc = QuantumCircuit(2, 2)
qc.reset([0, 1])
qc.barrier()
q.show_bloch_sphere(qc) # početno stanje |00⟩
# Korak 1: kubit 0 u superpoziciju |0⟩ → (|0⟩+|1⟩)/√2
qc.h(0)
qc.barrier()
q.show_bloch_sphere(qc)
# Korak 2: CNOT spreže kubit 1 sa kubitom 0 → nastaje Belov par
qc.cx(0, 1)
qc.barrier()
q.show_bloch_sphere(qc) # pojedinačni kubiti deluju "prazno"
q.show_qsphere(qc, style="qsphere") # qsphere lepo prikazuje korelaciju |00⟩/|11⟩
q.print_state(qc, "Bell state")
# Merenje oba kubita — očekuje se samo |00⟩ ili |11⟩ (savršena korelacija).
qc.measure([0, 1], [0, 1])
# Prikaz dijagrama kola i statistike merenja.
q.show_qc(qc)
q.show_measurement(qc)
iQFT
import numpy as np
from qiskit import QuantumCircuit
import quantum_utils_v1 as q
# ============================================================================
# QUANTUM FOURIER TRANSFORM (QFT)
# ============================================================================
def qft_rotations(qc, n):
"""
Primenjuje rotacione gejte za QFT na n kubita.
"""
if n == 0:
return qc
n -= 1
qc.h(n)
qc.barrier()
q.show_bloch_sphere(qc)
for qubit in range(n):
qc.cp(np.pi/2**(n-qubit), qubit, n)
qc.barrier()
q.show_bloch_sphere(qc)
qft_rotations(qc, n)
def qft(qc, n):
"""
Kompletna implementacija Quantum Fourier Transform-a.
"""
qft_rotations(qc, n)
return qc
# ============================================================================
# INVERSE QUANTUM FOURIER TRANSFORM (IQFT)
# ============================================================================
def iqft_rotations(qc, n):
"""
Primenjuje inverzne rotacione gejte za IQFT na n kubita.
Ova funkcija radi obrnutim redosledom u odnosu na qft_rotations:
- Za svaki kubit (od najmanje značajnog prema najznačajnijem)
primenjujemo kontrolisane fazne rotacije sa negativnim uglovima
(inverzno od QFT) pa zatim Hadamard gejt.
"""
# Iterativna implementacija (prozaičnija za čitanje)
for target in range(n):
# Kontrolisane rotacije u rastućem nizu kontrolnih kubita
for control in range(target):
# Inverzni ugao: negativan od originalnog
# U originalu za ciljni kubit i, ugao je π/2^{i-control}
qc.cp(-np.pi/2**(target-control), control, target)
qc.barrier()
q.show_bloch_sphere(qc)
# Nakon inverznih kontrolisanih rotacija, primeni H
qc.h(target)
qc.barrier()
q.show_bloch_sphere(qc)
return qc
def iqft(qc, n):
"""
Kompletna implementacija Inverse Quantum Fourier Transform-a.
Napomena:
QFT = (rotations)
IQFT = (rotations)^{-1}
Dakle primenjujemo inverzne rotacije (u suprotnom redosledu),
"""
iqft_rotations(qc, n)
return qc
# ----------------------------------------------------------------------------
# KORAK 1: INICIJALIZACIJA POČETNOG STANJA |ψ₀⟩ = |011⟩ (kako je u originalnom kodu)
# ----------------------------------------------------------------------------
qc = QuantumCircuit(3, 3)
# Postavljamo kubite tako da dobijemo |011⟩ = |0⟩ (MSB) ⊗ |1⟩ ⊗ |1⟩ (LSB)
qc.x(0)
qc.x(1)
qc.barrier()
print("\n--- Početno stanje ---")
q.print_state(qc, "Stanje", show_amplitudes=True)
q.show_bloch_sphere(qc)
# ----------------------------------------------------------------------------
# KORAK 2: PRIMENA QUANTUM FOURIER TRANSFORM
# ----------------------------------------------------------------------------
qft(qc, 3)
print("\n--- Stanje nakon QFT ---")
q.print_state(qc, "Stanje", show_amplitudes=True)
q.print_probabilities(qc, "Verovatnoće")
# ----------------------------------------------------------------------------
# KORAK 2.5: PRIMENA INVERSE QFT (IQFT) koja bi trebala da vrati stanje nazad
# ----------------------------------------------------------------------------
# Primena IQFT odmah nakon QFT kao demonstracija inverzije
iqft(qc, 3)
print("\n--- Stanje nakon IQFT (trebalo bi da se vrati na početno) ---")
q.print_state(qc, "Stanje", show_amplitudes=True)
q.print_probabilities(qc, "Verovatnoće")
# ----------------------------------------------------------------------------
# KORAK 3: MERENJE
# ----------------------------------------------------------------------------
qc.measure(range(3), range(3))
q.show_qc(qc)
q.show_measurement(qc, shots=1024)
Kvantna teleportacija
import math
from qiskit import QuantumCircuit, ClassicalRegister, QuantumRegister
import quantum_utils_v1 as q
# ============================================================================
# KVANTNA TELEPORTACIJA - PROGRAM
# ============================================================================
# Cilj: teleportovati stanje |ψ> sa kubita 0 (Alisa) na kubit 2 (Bob),
# koristeći pred-deljeni Bell par između kubita 1 i 2 i dve klasične poruke.
# ---------------------------------------------------------------------------
# KREIRANJE REGISTARA
# ---------------------------------------------------------------------------
# Koristimo:
# - 3 kvantna kubita: 0 = |ψ> (stanje koje teleportujemo), 1 = Alisin Bell kubit,
# 2 = Bob-ov kubit (ciljni)
# - 3 klasična registra po 1 bitu svaki (m0, m1 za merenja Alisa; m2 za
# konačno merenje Bob-ovog kubita radi verifikacije)
qr = QuantumRegister(3, 'q')
cr0 = ClassicalRegister(1, 'm0') # rezultat merenja kubita 0 (Alisa)
cr1 = ClassicalRegister(1, 'm1') # rezultat merenja kubita 1 (Alisa)
cr2 = ClassicalRegister(1, 'm2') # rezultat završnog merenja kubita 2 (Bob)
qc = QuantumCircuit(qr, cr0, cr1, cr2)
qc.barrier()
q.show_bloch_sphere(qc) # Početno stanje |000>
# ---------------------------------------------------------------------------
# KORAK 1: KREIRANJE BELL PARA (DELJENI RESURS ALISE I BOBA)
# kubiti: 1 (Alisa), 2 (Bob)
# ---------------------------------------------------------------------------
qc.h(1) # Adamar na kubitu 1
qc.cx(1, 2) # CNOT(1->2) -> Bell par |Φ+> između 1 i 2
qc.barrier()
q.print_state(qc, "Nakon kreiranja Bell para (1-2):", True)
q.show_bloch_sphere(qc)
# ---------------------------------------------------------------------------
# KORAK 2: PRIPREMA STANJA |ψ> NA KUBITU 0 (stanje koje želimo teleportovati)
# ---------------------------------------------------------------------------
# Možeš da menjaš theta i phi da bi teleportovao drugačija stanja.
theta = math.pi / 3 # ugao polarne koordinate (primer)
phi = math.pi / 2 # fazni ugao (primer)
# State vector: [cos(theta/2), e^{i phi} sin(theta/2)]
alpha = math.cos(theta / 2)
beta = math.sin(theta / 2) * complex(math.cos(phi), math.sin(phi))
qc.initialize([alpha, beta], 0) # inicijalizujemo kubit 0 u željeno stanje
qc.barrier()
q.print_state(qc, "Nakon pripreme |ψ> na kubitu 0:", True)
q.show_bloch_sphere(qc)
# ---------------------------------------------------------------------------
# KORAK 3: ALISA: BELL-MERENJE (entanglovanje stanja |ψ> sa njenim Bell kubitom)
# Operacije: CNOT(0,1) zatim H(0), pa merenja kubita 0 i 1 (Alisa)
# ---------------------------------------------------------------------------
qc.cx(0, 1)
qc.h(0)
qc.barrier()
q.print_state(qc, "Pre merenja (posle CNOT i H):", True)
q.show_bloch_sphere(qc)
# Merenja (Alisa meri svoj kubit q1 iz kvantnog para)
qc.measure(0, cr0[0]) # m0 <= mjerenje kubita 0
qc.measure(1, cr1[0]) # m1 <= mjerenje kubita 1
qc.barrier()
q.print_state(qc, "Nakon Alisinog merenja (posle CNOT i H):", True)
q.show_bloch_sphere(qc, from_instruction=False, qubit_index=2)
q.show_measurement(qc, selected_qubits=[0, 1])
# ---------------------------------------------------------------------------
# KORAK 4: BOB: KOREKCIJE NA OSNOVU KLASIČNIH BITOVA (klasična komunikacija)
# Ako je m1 == 1 -> primeni X na kubit 2
# Ako je m0 == 1 -> primeni Z na kubit 2
# Implementirano koristeći odvojene ClassicalRegister(1) registre i c_if.
# ---------------------------------------------------------------------------
# Napomena: upotreba .c_if sa jednogbitnim classical registrom: vrednost 1 znači
# da je taj bit 1, tako da se uslovna gejt primeni ispravno.
# Uslovna primena X na Bob-ov kubit ako je Alisin drugi merni bit 1
with qc.if_test((cr1[0], 1)):
qc.x(2)
# Uslovna primena Z na Bob-ov kubit ako je Alisin prvi merni bit 1
with qc.if_test((cr0[0], 1)):
qc.z(2)
qc.barrier()
q.print_state(qc, "Nakon klasičnih korekcija (pre verifikacionog merenja):", True)
q.show_bloch_sphere(qc, from_instruction=False, qubit_index=2)
# ---------------------------------------------------------------------------
# KORAK 5: VERIFIKACIJA - MERENJE BOB-OVOG KUBITA
# Očekivanje: Bob-ov kubit (2) treba da bude u istom stanju kao inicijalno |ψ>.
# ---------------------------------------------------------------------------
qc.measure(2, cr2[0]) # Merimo kubit 2 i upisujemo u m2 (radi verifikacije)
qc.barrier()
q.show_qc(qc)
q.show_measurement(qc, marginal_counts_flag=True, selected_qubits=[2]) # Prikaži histogram rezultata
# Ispis detalja
q.print_state(qc, "Finalno stanje (posle svih koraka):", True)
q.print_probabilities(qc, "Verovatnoće finalnog stanja:")
# ============================================================================
# ZAKLJUČAK:
# - Ako sve radi kako treba, stanje koje je bilo na kubitu 0 pre teleportacije
# (parametri theta, phi) sada je na kubitu 2 (pre merenja).
# - Merenjem kubita 2 i poređenjem sa očekivanom distribucijom iz alpha,beta
# možete verifikovati uspeh teleportacije.
# ============================================================================
Paulijevi gejtovi
"""
PAULIJEVI GEJTOVI: I, X, Y, Z
=============================
Tri Paulijeva gejta (X, Y, Z) su osnovne rotacije za 180° oko odgovarajućih osa
Blohove sfere, a I je identitet (ništa ne menja):
- X (bit-flip) : |0⟩ ↔ |1⟩ — rotacija oko X ose
- Z (phase-flip) : |1⟩ → −|1⟩ — rotacija oko Z ose
- Y = iXZ : kombinuje bit- i phase-flip — rotacija oko Y ose
- I (identitet) : ostavlja stanje nepromenjeno
Da bi se efekat svakog gejta jasno video, program svaki od njih primenjuje na
šest karakterističnih stanja koja "pokrivaju" Blohovu sferu (|0⟩, |1⟩, |+⟩, |−⟩,
|i+⟩, |i−⟩) i prikazuje stanje pre i posle, na sferi i kao verovatnoće.
"""
from qiskit import QuantumCircuit
import quantum_utils_v1 as q
# ============================================================================
# POMOĆNE FUNKCIJE: priprema kanonskih stanja na Bloch sferi
# ============================================================================
def prep_state(qc: QuantumCircuit, label: str, qubit: int = 0) -> QuantumCircuit:
"""
Priprema 1-kubitna stanja koja lepo “pokrivaju” Bloch sferu:
|0>, |1>, |+>, |->, |i+>, |i->
gde:
|+> = (|0>+|1>)/sqrt(2)
|-> = (|0>-|1>)/sqrt(2)
|i+> = (|0>+i|1>)/sqrt(2)
|i-> = (|0>-i|1>)/sqrt(2)
"""
if label == "|0>":
pass
elif label == "|1>":
qc.x(qubit)
elif label == "|+>":
qc.h(qubit)
elif label == "|->":
qc.x(qubit)
qc.h(qubit)
elif label == "|i+>":
qc.h(qubit)
qc.s(qubit)
elif label == "|i->":
qc.h(qubit)
qc.sdg(qubit)
else:
raise ValueError(f"Nepoznato stanje: {label}")
return qc
def apply_pauli(qc: QuantumCircuit, gate: str, qubit: int = 0) -> QuantumCircuit:
"""Primena Pulijevih gejtova: I, X, Y, Z."""
gate = gate.upper()
if gate == "I":
qc.id(qubit)
elif gate == "X":
qc.x(qubit)
elif gate == "Y":
qc.y(qubit)
elif gate == "Z":
qc.z(qubit)
else:
raise ValueError(f"Nepoznat Pauli gejt: {gate}")
return qc
# ============================================================================
# DEMO: Pulijevi gejtovi na više ulaznih stanja + Bloch sfera posle svakog koraka
# ============================================================================
def demo_pauli_gate(gate: str, input_states):
print("\n" + "=" * 80)
print(f"DEMO: Pauli {gate} gejt")
print("=" * 80)
for st in input_states:
qc = QuantumCircuit(1) # jedan kubit je idealan za Bloch sferu
qc.barrier()
# 1) Priprema ulaznog stanja
prep_state(qc, st, 0)
qc.barrier()
print(f"\nUlazno stanje: {st}")
q.show_bloch_sphere(qc)
q.print_state(qc, "Stanje (pre gejta):", True)
q.print_probabilities(qc, "Verovatnoće (pre gejta):")
# 2) Primena Pulijevog gejta
apply_pauli(qc, gate, 0)
qc.barrier()
print(f"Primena gejta: {gate}")
q.show_bloch_sphere(qc)
q.print_state(qc, "Stanje (posle gejta):", True)
q.print_probabilities(qc, "Verovatnoće (posle gejta):")
# (Opcionalno) prikaži kolo
q.show_qc(qc)
def main():
# Kanonska stanja koja eksplicitno pokazuju:
# - X flipuje z-polove, ostavlja x osu
# - Z flipuje fazu (x osu), ostavlja z osu
# - Y kombinuje bit-flip + phase-flip (rotacija oko y ose za pi, uz globalnu fazu)
states = ["|0>", "|1>", "|+>", "|->", "|i+>", "|i->"]
for gate in ["X", "Y", "Z"]:
demo_pauli_gate(gate, states)
if __name__ == "__main__":
main()
QFT
import numpy as np
from qiskit import QuantumCircuit
import quantum_utils_v1 as q
# ============================================================================
# QUANTUM FOURIER TRANSFORM (QFT)
# ============================================================================
# QFT je kvantni analog klasične diskretne Fourier transformacije.
# Za N-dimenzionalni prostor (N = 2^n gde je n broj kubita), QFT transformiše
# bazno stanje |j⟩ u superpoziciju:
#
# |j⟩ → (1/√N) Σₖ e^(2πijk/N) |k⟩
#
# QFT je fundamentalna komponenta mnogih kvantnih algoritama, uključujući:
# - Šorov algoritam za faktorizaciju
# - Kvantna procena faze (Quantum Phase Estimation)
# - Algoritmi za rešavanje sistema linearnih jednačina
def qft_rotations(qc, n):
"""
Primenjuje rotacione gejte za QFT na n kubita.
Ova funkcija implementira jezgro QFT algoritma kroz niz Adamar gejta
i kontrolisanih faznih rotacija. Svaki kubit prolazi kroz:
1. H-gejt - kreira ravnomernu superpoziciju
2. Seriju kontrolisanih R gejta - dodaje fazne relacije
Args:
qc: QuantumCircuit na koji primenjujemo QFT
n: Broj kubita
Matematički, za kubit j primenjujemo:
- H gejt
- CR_k gejte gde je R_k = [[1, 0], [0, e^(2πi/2^k)]]
"""
if n == 0: # Bazni slučaj rekurzije
return qc
n -= 1 # Indeksiramo od 0
# Primeni H-gejt na poslednji kubit
# H transformiše |0⟩ → (|0⟩ + |1⟩)/√2 i |1⟩ → (|0⟩ - |1⟩)/√2
qc.h(n)
qc.barrier()
q.show_bloch_sphere(qc)
# Primeni kontrolisane fazne rotacije
# Svaka rotacija dodaje fazni odnos između baznih stanja
# Ugao za k-tu rotaciju: theta = π / 2^{k}, gde k zavisi od razlike indeksa.
for qubit in range(n):
# Kontrolisani-P gejt: dodaje fazu e^(iθ) kada je kontrolni kubit |1⟩
# Ugao rotacije: θ = 2π/2^(n-qubit+1) = π/2^(n-qubit)
qc.cp(np.pi/2**(n-qubit), qubit, n)
qc.barrier()
q.show_bloch_sphere(qc)
# Rekurzivno primeni na preostale kubite
qft_rotations(qc, n)
def swap_registers(qc, n):
"""
Obrće redosled kubita u QFT kolu.
QFT prirodno proizvodi izlaz u obrnutom redosledu (bit-reversal).
Ova funkcija vraća kubite u originalni redosled primenom SWAP gejta.
Args:
qc: QuantumCircuit
n: Broj kubita
Napomena:
Za n kubita potrebno je n//2 SWAP operacija.
SWAP(i, n-1-i) zamenjuje kubit i sa kubitom n-1-i.
"""
for qubit in range(n//2):
qc.swap(qubit, n-qubit-1)
return qc
def qft(qc, n):
"""
Kompletna implementacija Quantum Fourier Transform-a.
Args:
qc: QuantumCircuit
n: Broj kubita na kojima se primenjuje QFT
Returns:
QuantumCircuit sa primenjenim QFT
"""
qft_rotations(qc, n)
swap_registers(qc, n)
return qc
# ----------------------------------------------------------------------------
# KORAK 1: INICIJALIZACIJA POČETNOG STANJA |ψ₀⟩ = |110⟩
# ----------------------------------------------------------------------------
# Napomena o konvenciji indeksa:
# - Ovde koristimo konvenciju da je q[2] najviše značajan bit (MSB),
# a q[0] najmanje značajan bit (LSB).
# Dakle |011⟩ znači q[0]=1, q[1]=1, q[2]=0.
#
# Kreiranje kvantnog kola sa 3 kubita i 3 klasična bita
# Početno stanje je |000⟩
qc = QuantumCircuit(3, 3)
# Postavljamo kubite tako da dobijemo |011⟩ = |0⟩ (MSB) ⊗ |1⟩ ⊗ |1⟩ (LSB)
qc.x(0)
qc.x(1)
qc.barrier(label="Start: |011⟩")
print("\n--- Početno stanje ---")
q.print_state(qc, "Stanje", show_amplitudes=True)
q.show_bloch_sphere(qc)
# ----------------------------------------------------------------------------
# KORAK 2: PRIMENA QUANTUM FOURIER TRANSFORM
# ----------------------------------------------------------------------------
# QFT transformiše bazno stanje |j⟩ u frekventni domen.
#
# Za |011⟩ (decimalno j = 3) očekujemo:
# QFT|3⟩ = (1/√8) Σ_k e^(2π i · 3 · k / 8) |k⟩
#
# Rezultat je ravnomerna superpozicija svih baznih stanja sa
# specifičnim faznim odnosima koji kodiraju frekvenciju
qft(qc, 3)
qc.barrier(label="Posle QFT")
print("\n--- Stanje nakon QFT ---")
q.print_state(qc, "Stanje", show_amplitudes=True)
q.print_probabilities(qc, "Verovatnoće")
q.show_bloch_sphere(qc)
# ----------------------------------------------------------------------------
# KORAK 3: MERENJE
# ----------------------------------------------------------------------------
# Merenjem dobijamo rezultat koji odgovara jednom od baznih stanja iz superpoziciju.
# Verovatnoća svakog stanja je |amplituda|^2
# Amplituda za svako |k⟩ jednaka je 1/√N, pa je verovatnoća 1/N = 1/8 = 12.5%.
qc.measure(range(3), range(3))
q.show_qc(qc)
q.show_measurement(qc, shots=1024)
QFT Kodiranje broja u fazu
"""
QFT: KODIRANJE BROJA U FAZE
===========================
Ovaj program pokazuje šta Kvantna Furijeova Transformacija (QFT) zapravo radi sa
običnim brojem upisanim u kubite.
Ako kubite postavimo u bazno stanje |j⟩ (npr. |101⟩ = broj 5), QFT ga pretvara u
ravnomernu superpoziciju SVIH baznih stanja, ali sa različitim FAZAMA:
QFT|j⟩ = (1/√N) · Σ_k e^{2πi·j·k/N} · |k⟩
Verovatnoće svih stanja postaju jednake (svako 1/N), pa se broj j više ne vidi u
amplitudama — on je sada "sakriven" u brzini kojom faze rotiraju po stanjima |k⟩.
Veći broj j ⇒ brža rotacija faze. Program prolazi kroz svih 8 stanja (000…111) i
za svako prikazuje rezultat na Blohovoj sferi, da se uoči taj fazni "potpis".
Funkcija apply_qft ispod implementira QFT (i opciono inverznu iQFT) iz osnovnih
gejtova: H i kontrolisanih faznih rotacija cp().
"""
import math
from qiskit import QuantumCircuit
from typing import Iterable
import quantum_utils_v1 as q
# =============================================================================
# FUNKCIJA: Kvantna Furijeova transformacija
# =============================================================================
def apply_qft(qc, qubit_range: Iterable[int], inverse: bool = False, approximation: float = 0.0):
"""
Primeni (ili inverznu) diskretnu kvantnu Furijeovu transformaciju na dati opseg kubita.
Args:
qc: QuantumCircuit - kvantno kolo na koje se primenjuju gejtovi.
qubit_range: Iterable[int] - iterabilni niz indeksa kubita (npr. range(n) ili [0,1,2]).
Redosled je Vaš logički redosled kubita (najmanje značajan -> najmanje ili kako vi želite).
inverse: bool - ako je True, primeni inverznu QFT (iQFT). Default: False.
approximation: float - prag ispod kojeg se preskaču kontrolisane fazne rotacije (ugao u radijanima).
Koristno za aproksimativnu QFT. Default: 0.0 (ne preskače se ništa).
Povratna vrednost:
qc: QuantumCircuit - vraća isti QuantumCircuit nakon primene QFT-a.
Napomena:
- Koristi qc.h, qc.cp i qc.swap (Qiskit API).
- Ovaj kod pretpostavlja standardne Qiskit metode (qc.cp dostupno).
"""
# Normalizuj listu indeksa kubita u listu (ako je prosleđen range ili generator)
qubits = list(qubit_range)
n = len(qubits)
if n == 0:
return qc
# Ako želimo inverznu QFT, možemo primeniti QFT sa obrnutim redosledom i zameniti znak uglova
if inverse:
# Inverzna QFT: izvrši korake QFT obrnutim redosledom i sa negativnim uglovima
# 1) Swap-ovi (redosled zadržavamo isti kao i kod QFT - swap na kraju)
# 2) Za svaki i od n-1 do 0: primeni kontrolisane faze sa negativnim uglom, pa H
qc.barrier()
for i in range(n - 1, -1, -1):
target = qubits[i]
# kontrolisane rotacije (od narednih kubita prema kraju)
for j in range(i + 1, n):
control = qubits[j]
k = j - i + 0 # odn. eksponent
angle = - math.pi / (2 ** (j - i))
if abs(angle) < approximation:
continue
# cp(angle, control, target) - kontrolisani fazni gejt
qc.cp(angle, control, target)
qc.h(target)
qc.barrier()
# swap da se vrati redosled bitova (QFT obrće bit-order)
for i in range(n // 2):
qc.swap(qubits[i], qubits[n - 1 - i])
qc.barrier()
return qc
# --- Normalna QFT ---
qc.barrier()
# Za svaki cilj i od 0 do n-1:
# - primeni H na i
# - za svaki j = i+1..n-1 primeni kontrolisanu rotaciju cp(angle) sa kontrolom j i ciljem i
# (ovo enkoduje faze na "manje značajne" kubite)
for i in range(n):
target = qubits[i]
qc.h(target)
# kontrolisane rotacije sa kubitima koji su "viši" u indeksu
for j in range(i + 1, n):
control = qubits[j]
angle = math.pi / (2 ** (j - i))
if abs(angle) < approximation:
continue
qc.cp(angle, control, target)
qc.barrier()
# Swap-ovi da obrnemo redosled kubita (QFT tipično zahteva reverse)
# for i in range(n // 2):
# qc.swap(qubits[i], qubits[n - 1 - i])
# qc.barrier()
return qc
# =============================================================================
# FUNKCIJA: Enkodiranje binarnog broja u početno stanje 3 kubita
# =============================================================================
def prepare_basis_state(qc, binary_string):
"""
Postavlja 3-kubitni sistem u određeno binarno stanje npr. '101' -> |101⟩
"""
for i, bit in enumerate(binary_string):
if bit == '1':
qc.x(i)
# Napomena:
# Ako koristiš drugačiji redosled kubita u Svojim primerima,
# samo obrni indeks. Ovde je |q2 q1 q0⟩ konvencija.
return qc
# =============================================================================
# GLAVNI DEO: Prolaz kroz svih 8 binarnih stanja
# =============================================================================
states = [
"000", "001", "010", "011",
"100", "101", "110", "111"
]
for state in states:
print(f"\n=======================================")
print(f"Binarno stanje: |{state}⟩")
print(f"=======================================\n")
# Kreiramo kvantno kolo sa 3 kubita
qc = QuantumCircuit(3)
# Postavljanje sistema u početno registrovano stanje
qc = prepare_basis_state(qc, state)
qc.barrier()
# Prikaz početnog stanja na Blohovim sferama
# q.show_bloch_sphere(qc, title=f"Početno stanje |{state}⟩")
# Primena kvantne Furijeove transformacije
apply_qft(qc, range(3))
qc.barrier()
# Prikaz stanja nakon QFT-a
q.show_bloch_sphere(qc, title=f"Stanje nakon QFT za |{state}⟩")
q.print_state(qc, show_amplitudes=True)
QPE
import math
from qiskit import QuantumCircuit
from qiskit.circuit.library import QFT
import quantum_utils_v1 as q
# ============================================================================
# PARAMETRI — LAKO PODESIVO
# ============================================================================
n_count = 3 # broj mernih kubita (možeš menjati)
phi = 1 / 8 # faza koju procenjujemo (φ)
# ============================================================================
# OPIS OPERATORA
# ============================================================================
# Operator U je definisan kao:
# U|1⟩ = e^{2πi * φ}|1⟩
# pa je osnovni fazni ugao:
angle = 2 * math.pi * phi
# ============================================================================
# INICIJALIZACIJA KVANTNOG KOLA
# ============================================================================
# Imamo:
# - n_count kubita za merni registar,
# - 1 kubit za sistem (eigenstanje).
qc = QuantumCircuit(n_count + 1, n_count)
qc.barrier()
q.show_bloch_sphere(qc)
# ============================================================================
# KORAK 1: Priprema ciljnog kubita u eigenstanje |1⟩
# ============================================================================
target = n_count # indeks ciljnog kubita
qc.x(target)
qc.barrier()
q.show_bloch_sphere(qc)
# ============================================================================
# KORAK 2: H-gejt na mernim kubitima
# ============================================================================
for qubit in range(n_count):
qc.h(qubit)
qc.barrier()
q.show_bloch_sphere(qc)
# ============================================================================
# KORAK 3: Primena kontrolisanih U^(2^k) operacija
# ============================================================================
# Svaki merni kubit kontroliše eksponencijalnu primenu operatora U
for k in range(n_count):
repetitions = 2 ** k
for _ in range(repetitions):
qc.cp(angle, k, target)
qc.barrier()
q.show_bloch_sphere(qc)
# ============================================================================
# KORAK 4: Inverzna Kvantna Furijeova Transformacija (iQFT)
# ============================================================================
# Automatski koristi QFT nad mernim kubitima
qc = qc.compose(QFT(num_qubits=n_count, inverse=True), list(range(n_count)))
qc.barrier()
q.show_bloch_sphere(qc)
# ============================================================================
# KORAK 5: Merenje
# ============================================================================
for n in range(n_count):
qc.measure(n, n)
# ============================================================================
# PRIKAZI I ANALIZA
# ============================================================================
q.print_state(qc, "Konačno kvantno stanje:", True)
q.print_probabilities(qc, "Verovatnoće baznih stanja:")
q.show_qc(qc)
q.show_measurement(qc)
# ============================================================================
# OČEKIVANI ISHOD
# ============================================================================
# φ = 1/8 = 0.001₂
# Ako koristiš više mernih kubita, preciznost binarnog zapisa raste.
# Na primer:
# n_count = 3 → |001⟩
# n_count = 4 → |0010⟩
# n_count = 5 → |00100⟩
# itd.
# ============================================================================
quantum utils v1
from typing import Optional, Union, Iterable
from qiskit import QuantumCircuit, transpile
from qiskit_aer import AerSimulator
from qiskit.quantum_info import Statevector, partial_trace, DensityMatrix, Pauli
from qiskit.visualization.bloch import Bloch
from qiskit.result import marginal_counts
from qiskit.visualization import (
plot_bloch_multivector,
plot_bloch_vector,
plot_histogram,
plot_state_city,
plot_state_qsphere
)
import numpy as np
import matplotlib.pyplot as plt
# ---------- Interni helper ----------
def _get_statevector(state: Union[QuantumCircuit, Statevector]) -> Statevector:
"""
Konvertuje QuantumCircuit ili Statevector u Statevector objekat.
Args:
state: QuantumCircuit ili Statevector objekat
Returns:
Statevector objekat
Raises:
TypeError: Ako argument nije odgovarajućeg tipa
"""
if isinstance(state, Statevector):
return state
elif isinstance(state, QuantumCircuit):
sim = AerSimulator(method='statevector')
qc_copy = state.copy()
qc_copy.save_statevector()
tqc = transpile(qc_copy, sim)
result = sim.run(tqc, shots=1).result()
return result.get_statevector()
else:
raise TypeError("Argument mora da bude QuantumCircuit ili Statevector")
# ---------- Utilities ----------
def get_state(state: Union[QuantumCircuit, Statevector],
threshold: float = 1e-10) -> str:
"""
Vraća string reprezentaciju state vektora sa samo znacima (bez amplituda).
Args:
state: QuantumCircuit ili Statevector
threshold: Minimalna amplituda da bi se prikazala (default: 1e-10)
Returns:
String reprezentacija stanja
"""
sv = _get_statevector(state)
terms = []
for i, amp in enumerate(sv.data):
if np.abs(amp) > threshold:
sign = '-' if np.real(amp) < 0 else '+'
ket = f"|{format(i, f'0{sv.num_qubits}b')}>"
terms.append(f"{sign} {ket}")
if not terms:
return "|0>"
result = " ".join(terms)
# Ukloni vodeći + znak
if result.startswith('+ '):
result = result[2:]
return result.replace('+ ', ' + ').replace('- ', ' - ')
def get_full_state(state: Union[QuantumCircuit, Statevector],
threshold: float = 1e-10,
precision: int = 3) -> str:
"""
Vraća potpunu string reprezentaciju state vektora sa amplitudama.
Args:
state: QuantumCircuit ili Statevector
threshold: Minimalna amplituda da bi se prikazala
precision: Broj decimala za prikaz amplituda
Returns:
String reprezentacija stanja sa amplitudama
"""
sv = _get_statevector(state)
terms = []
for i, amp in enumerate(sv.data):
if np.abs(amp) > threshold:
# Formatiranje kompleksnog broja
real = np.real(amp)
imag = np.imag(amp)
if np.abs(imag) < threshold:
# Samo realni deo
amp_str = f"{real:.{precision}f}"
elif np.abs(real) < threshold:
# Samo imaginarni deo
if np.abs(imag - 1) < threshold:
amp_str = "i"
elif np.abs(imag + 1) < threshold:
amp_str = "-i"
else:
amp_str = f"{imag:.{precision}f}i"
else:
# Oba dela
sign = '+' if imag >= 0 else '-'
amp_str = f"{real:.{precision}f}{sign}{abs(imag):.{precision}f}i"
ket = f"|{format(i, f'0{sv.num_qubits}b')}>"
# Dodaj zagradе ako je potrebno
if '+' in amp_str or (amp_str.startswith('-') and 'i' in amp_str):
terms.append(f"({amp_str}){ket}")
else:
terms.append(f"{amp_str}{ket}")
if not terms:
return "0"
result = " + ".join(terms)
return result.replace(" + -", " - ")
def print_state(state: Union[QuantumCircuit, Statevector],
label: str = "",
show_amplitudes: bool = False,
precision: int = 3) -> None:
"""
Ispisuje kvantno stanje u konzoli.
Args:
state: QuantumCircuit ili Statevector
label: Opcioni label za prikaz
show_amplitudes: Da li prikazati amplitude (False = samo znaci)
precision: Broj decimala za amplitude
"""
sv = _get_statevector(state)
if show_amplitudes:
state_str = get_full_state(sv, precision=precision)
else:
state_str = get_state(sv)
if label:
print(f"{label} = {state_str}")
else:
print(state_str)
def print_raw_state(qc, label=""):
sv = Statevector(qc)
if label:
print(f"\n{label}:")
for i, amp in enumerate(sv.data):
if abs(amp) > 1e-10:
print(f"|{i:03b}⟩: {amp:.6f}")
def get_probabilities(state: Union[QuantumCircuit, Statevector]) -> dict:
"""
Vraća verovatnoće svih baza stanja.
Args:
state: QuantumCircuit ili Statevector
Returns:
Dictionary: {basis_state: probability}
"""
sv = _get_statevector(state)
probs = {}
for i, amp in enumerate(sv.data):
prob = np.abs(amp) ** 2
if prob > 1e-10:
basis = format(i, f'0{sv.num_qubits}b')
probs[basis] = prob
return probs
def print_probabilities(state: Union[QuantumCircuit, Statevector],
label: str = "") -> None:
"""
Ispisuje verovatnoće svih baza stanja.
Args:
state: QuantumCircuit ili Statevector
label: Opcioni label
"""
probs = get_probabilities(state)
if label:
print(f"\n{label}:")
for basis, prob in sorted(probs.items()):
print(f"|{basis}>: {prob:.4f} ({prob*100:.2f}%)")
def _to_density(state: Union[QuantumCircuit, Statevector, DensityMatrix]):
"""Pretvara stanje u gustinsku matricu."""
if isinstance(state, DensityMatrix):
return state
if isinstance(state, Statevector):
return DensityMatrix(state)
if isinstance(state, QuantumCircuit):
return DensityMatrix(Statevector.from_instruction(state))
raise TypeError("Unsupported state type.")
def matrix_power(dm: DensityMatrix, p: float) -> DensityMatrix:
"""Compute ρ^p using eigen-decomposition."""
M = dm.data
vals, vecs = np.linalg.eigh(M)
vals = np.maximum(vals, 0)
vals_p = np.diag(vals ** p)
M_p = vecs @ vals_p @ vecs.conj().T
return DensityMatrix(M_p)
def get_fidelity(
state1: Union[QuantumCircuit, Statevector, DensityMatrix],
state2: Union[QuantumCircuit, Statevector, DensityMatrix],
qubits: Iterable[int] = None
) -> float:
"""
Računa fidelity između dva kvantna stanja.
- Može porediti:
* čisto–čisto
* čisto–mešovito
* mešovito–mešovito
- Ako su stanja različite dimenzije:
* automatski radi redukciju na podskup qubita (argument qubits)
Args:
state1, state2: kvantna stanja (QC, Statevector ili DensityMatrix)
qubits: lista qubita koje poredi (npr. [2]).
Ako None — upoređuje kompletne sisteme (moraju biti iste dimenzije)
Returns:
Fidelity vrednost iz [0, 1].
"""
dm1 = _to_density(state1)
dm2 = _to_density(state2)
# Handle subsystem comparison
if dm1.num_qubits != dm2.num_qubits:
if qubits is None:
raise ValueError(
"State dimensions differ. Specify qubits=[...] for subsystem fidelity."
)
dm1 = dm1.reduce(qubits)
dm2 = dm2.reduce(qubits)
else:
if qubits is not None:
dm1 = dm1.reduce(qubits)
dm2 = dm2.reduce(qubits)
# sqrt(ρ)
sqrt_dm1 = matrix_power(dm1, 0.5)
# ρ^{1/2} σ ρ^{1/2}
product = DensityMatrix(
sqrt_dm1.data @ dm2.data @ sqrt_dm1.data
)
# eigenvalues
evals = np.linalg.eigvalsh(product.data)
evals = np.maximum(evals, 0)
# (Tr sqrt(product))^2
fidelity = (np.sum(np.sqrt(evals)))**2
return float(np.real_if_close(fidelity))
def show_bloch_sphere(qc: Union[QuantumCircuit, Statevector],
from_instruction: bool = True,
qubit_index: Optional[int] = None,
title: str = None,
figsize: tuple = (10, 5)) -> None:
"""
Prikazuje Bloch sferu za sve qubite ili za jedan odabrani qubit.
Radi za čista i mešovita stanja, i za više qubita koristi partial trace.
Args:
qc: QuantumCircuit ili Statevector
from_instruction: Ako je True, koristi Statevector.from_instruction
(matematički pristup, brže).
qubit_index: Ako je None -> prikazuje sve qubite.
Ako je broj -> prikazuje samo taj qubit.
title: Naslov figure (opciono)
figsize: Veličina figure
"""
# ---- Paulijeve matrice ----
X = np.array([[0, 1],
[1, 0]], dtype=complex)
Y = np.array([[0, -1j],
[1j, 0]], dtype=complex)
Z = np.array([[1, 0],
[0, -1]], dtype=complex)
# ---- Izračunavanje statevectora ----
if isinstance(qc, Statevector):
sv = qc
else:
sv = Statevector.from_instruction(qc) if from_instruction else _get_statevector(qc)
num_qubits = sv.num_qubits
# ---- Ako je zadat samo jedan qubit ----
if qubit_index is not None:
if qubit_index < 0 or qubit_index >= num_qubits:
raise ValueError(
f"Nevažeći qubit_index {qubit_index}. Mora biti između 0 i {num_qubits-1}."
)
# Partial trace nad svim ostalim qubitima
if num_qubits > 1:
qubits_to_trace_out = [i for i in range(num_qubits) if i != qubit_index]
rho = DensityMatrix(partial_trace(sv, qubits_to_trace_out))
else:
rho = DensityMatrix(sv)
# Izračunavanje Bloch vektora
bloch_vector = [
np.real(np.trace(rho.data @ pauli))
for pauli in (X, Y, Z)
]
# Jedna jedina Bloch sfera
fig = plt.figure(figsize=figsize)
ax = fig.add_subplot(111, projection='3d')
plot_bloch_vector(bloch_vector, title=f"q{qubit_index}", ax=ax)
if title:
plt.suptitle(title, fontsize=16)
plt.tight_layout()
plt.show()
return
# ---- Prikaz svih qubita ----
if num_qubits > 5:
print(f"Upozorenje: Previše qubita ({num_qubits}) — prikazujem samo prvih 5.")
num_qubits = 5
sv = Statevector(sv.data[:2**5])
# Kreiranje figure
fig, axes = plt.subplots(1, num_qubits, figsize=(4 * num_qubits, 4),
subplot_kw={'projection': '3d'})
if num_qubits == 1:
axes = [axes]
# ---- Iteriranje kroz qubite ----
for qubit_idx in range(num_qubits):
if num_qubits == 1:
rho = DensityMatrix(sv)
else:
qubits_to_trace_out = [i for i in range(sv.num_qubits) if i != qubit_idx]
rho = DensityMatrix(partial_trace(sv, qubits_to_trace_out))
# Bloch vektor
bloch_vector = [
np.real(np.trace(rho.data @ pauli))
for pauli in (X, Y, Z)
]
plot_bloch_vector(bloch_vector, title=f"q{qubit_idx}", ax=axes[qubit_idx])
if title:
plt.suptitle(title, fontsize=16)
plt.tight_layout()
plt.show()
def show_qsphere(state: Union[QuantumCircuit, Statevector],
style: str = "qsphere") -> None:
"""
Prikazuje stanje 2-qubit sistema vizualno.
Args:
state: QuantumCircuit ili Statevector (mora imati tačno 2 qubita)
style: Stil prikaza - 'city' ili 'qsphere'
Raises:
ValueError: Ako nema tačno 2 qubita ili stil nije validan
"""
sv = _get_statevector(state)
if sv.num_qubits != 2:
raise ValueError(f"Funkcija radi samo sa 2 qubita, a prosleđeno je {sv.num_qubits}")
if style == "city":
fig = plot_state_city(sv)
elif style == "qsphere":
fig = plot_state_qsphere(sv)
else:
raise ValueError("Style mora da bude 'city' ili 'qsphere'")
plt.tight_layout()
plt.show()
plt.close(fig)
def show_qc(qc: QuantumCircuit, figsize: tuple = (12, 6)) -> None:
"""
Prikazuje kvantno kolo.
Args:
qc: QuantumCircuit koji treba prikazati
figsize: Veličina figure
"""
fig = qc.draw(output='mpl', style='iqp', fold=-1)
if hasattr(fig, 'set_size_inches'):
fig.set_size_inches(figsize)
plt.tight_layout()
plt.show()
plt.close(fig)
def show_measurement(qc: QuantumCircuit,
shots: int = 1024,
figsize: tuple = (10, 6),
label_size: int = 10,
marginal_counts_flag: bool = False,
selected_qubits: list[int] = None) -> dict:
"""
Simulira merenje kvantnog kola i prikazuje histogram rezultata.
Args:
qc: QuantumCircuit za merenje (mora imati merenje)
shots: Broj simulacija
figsize: Veličina figure
label_size: Veličina labele na histogramu
marginal_counts_flag: Ako je True, prikazuje marginalne raspodele po kubitima
selected_qubits: Lista qubita koje želimo da prikažemo u histogramu.
Primeri:
None -> prikazuje sve
[2] -> prikazuje samo q2
[0,2] -> prikazuje samo q0 i q2
Returns:
Dictionary sa rezultatima merenja
"""
simulator = AerSimulator()
qc_copy = qc.copy()
# Ako nema classical bitova, dodaj merenje nad svim qubitima
if qc_copy.num_clbits == 0:
qc_copy.measure_all()
compiled_circuit = transpile(qc_copy, simulator)
result = simulator.run(compiled_circuit, shots=shots).result()
counts = result.get_counts()
# Ako korisnik želi samo neke qubite — filter
if selected_qubits is not None and marginal_counts_flag is False:
filtered_counts = {}
# 1. Sort descending to get MSB first (q_max...q_0)
sorted_selected_qubits = sorted(selected_qubits, reverse=True)
for bitstring, count in counts.items():
selected_bits = []
num_qubits = len(bitstring.replace(" ", "")) # Handle spaces if present
# 2. Iterate in MSB order (e.g., q=1, then q=0)
for q in sorted_selected_qubits:
bit_pos = num_qubits - 1 - q # Correct index calculation
selected_bits.append(bitstring.replace(" ", "")[bit_pos]) # Access bit
# The new_key is correctly ordered (q_1 q_0)
new_key = "".join(selected_bits)
filtered_counts[new_key] = filtered_counts.get(new_key, 0) + count
# Prikaži histogram samo selektovanih qubita
fig = plot_histogram(filtered_counts, figsize=figsize)
# Ako je tražen "marginal_counts" prikaz
elif marginal_counts_flag:
num_qubits = qc_copy.num_qubits
# Kreiramo listu svih marginalnih merenja po kubitima
# qubit_counts = [counts(q0), counts(q1), counts(q2), ...]
from qiskit.result import marginal_counts
all_marginal_counts = [marginal_counts(counts, [qubit]) for qubit in range(num_qubits)]
if selected_qubits is not None:
# IZVLAČIMO SAMO IZABRANE MARGINALNE REZULTATE:
# Selektujemo samo one rečnike iz liste koji odgovaraju indeksima u selected_qubits
selected_marginal_counts = [
all_marginal_counts[q]
for q in selected_qubits
if q < num_qubits
]
# plot_histogram prihvata listu rečnika i prikazuje ih jedan pored drugog
fig = plot_histogram(selected_marginal_counts, figsize=figsize)
else:
# Ako selected_qubits nije None, prikaži sve
fig = plot_histogram(all_marginal_counts, figsize=figsize)
# Inače prikaži sve rezultate
else:
fig = plot_histogram(counts, figsize=figsize)
# Stilizacija
ax = fig.axes[0]
ax.tick_params(axis='x', labelsize=label_size)
plt.tight_layout()
plt.show()
plt.close(fig)
return counts
def compute_orbits(a: int, N: int):
"""
Računa sve orbite permutacije y -> (a*y) mod N.
a, N : parametri modularne funkcije
povratna vrednost : lista orbita (svaka orbita je lista celih brojeva)
"""
visited = set()
orbits = []
for y in range(N):
if y in visited:
continue # već smo ga stavili u neku orbitu
# Krećemo da pratimo orbitu od y
orbit = []
x = y
while x not in visited:
visited.add(x)
orbit.append(x)
x = (a * x) % N
orbits.append(orbit)
return orbits
S gejt
"""
S-GEJT (FAZNI GEJT ZA π/2)
==========================
S-gejt je fazni gejt koji baznom stanju |1⟩ dodaje fazu i = e^{iπ/2}, dok |0⟩
ostavlja nepromenjeno (matrica diag(1, i)). Na Blohovoj sferi to je rotacija
za +90° oko Z ose.
Program demonstrira njegovo dejstvo na dva karakteristična stanja:
- |+⟩ (na +X osi) → S|+⟩ = (|0⟩ + i|1⟩)/√2 = |i⟩ (rotira na +Y osu)
- |0⟩ (severni pol) → S|0⟩ = |0⟩ (nema promene, jer je |0⟩ na osi rotacije)
Napomena: S = T², a S† (sdg) radi rotaciju za −90°.
"""
from qiskit import QuantumCircuit
from qiskit.quantum_info import Statevector
import quantum_utils_v1 as q
# =============================================================================
# POMOĆNA FUNKCIJA: Blohova sfera (koristi quantum_utils ako postoji, inače fallback)
# =============================================================================
def show_bloch(statevec, title=""):
"""
statevec: qiskit.quantum_info.Statevector ili niz kompleksnih amplituda
"""
# Pokušaj tvoj helper (najčešće postoji u tvojoj biblioteci)
if hasattr(q, "show_bloch_sphere"):
q.show_bloch_sphere(statevec, title=title)
return
# Fallback: Qiskit-ov prikaz ako tvoja funkcija nije dostupna
from qiskit.visualization import plot_bloch_multivector
fig = plot_bloch_multivector(statevec)
if title:
try:
fig.suptitle(title)
except Exception:
pass
import matplotlib.pyplot as plt
plt.show()
# =============================================================================
# DEMO: Dejstvo S-gejta na kubit na Blohovoj sferi
# =============================================================================
# Dobar primer je da krenemo iz |+> (na +X osi), jer S je Z-rotacija za pi/2,
# pa |+> prelazi u |i> (na +Y osi).
qc_before = QuantumCircuit(1)
qc_before.h(0) # |0> -> |+>
qc_after = QuantumCircuit(1)
qc_after.h(0) # |0> -> |+>
qc_after.s(0) # S: diag(1, i) => rotacija oko Z ose za +pi/2
# Stanja (statevector) pre i posle S
psi_before = Statevector.from_instruction(qc_before)
psi_after = Statevector.from_instruction(qc_after)
print("Stanje pre S (|+>):", psi_before.data)
print("Stanje posle S (S|+> ~ |i>):", psi_after.data)
# Prikaz na Blohovoj sferi
show_bloch(psi_before, title="Pre S-gejta: |+> (na +X osi)")
show_bloch(psi_after, title="Posle S-gejta: S|+> = (|0> + i|1>)/√2 = |i> (na +Y osi)")
# (Opcionalno) Ako želiš da vidiš i da S ne menja |0> na Blohovoj sferi:
qc0 = QuantumCircuit(1)
psi0_before = Statevector.from_instruction(qc0) # |0>
qc0.s(0)
psi0_after = Statevector.from_instruction(qc0) # S|0> = |0>
show_bloch(psi0_before, title="Pre S-gejta: |0> (Severni pol)")
show_bloch(psi0_after, title="Posle S-gejta: S|0> = |0> (nema promene)")
Sorov algoritam
"""
Shor-ov algoritam (demonstracija) — Qiskit implementacija sa
potpunom (matrix-based) modularnom eksponencijalizacijom.
Napomena:
- Ovaj pristup konstruiše jedinični (permutacioni) matrix za operator
U : |y> -> |(a * y) mod N> koji deluje na m = ceil(log2(N)) kubita.
"""
import math
import numpy as np
from math import gcd
from fractions import Fraction
from qiskit import QuantumCircuit
from qiskit.circuit.library import QFT, UnitaryGate
import quantum_utils_v1 as q
# ===================================================================
# PARAMETRI — LAKO PODEŠAVO
# ===================================================================
n_count = 12 # merni kubiti
N = 55
a = 3
# ===================================================================
# POMOĆNE MATEMATIČKE FUNKCIJE
# ===================================================================
def continued_fraction(x, max_denominator):
frac = Fraction(x).limit_denominator(max_denominator)
return int(frac.numerator), int(frac.denominator)
def get_order_from_measurement(meas, n_count, N):
fraction = meas / (2**n_count)
p, q = continued_fraction(fraction, N)
return q
# ===================================================================
# KOREKCIJA #1 — PRAVA PERMUTACIONA MATRICA
# ===================================================================
def build_mult_a_mod_N(a, N):
"""
|y> → |(a*y) mod N>, y < N
Za y >= N ostaje identitet.
"""
m = math.ceil(math.log2(N))
dim = 2**m
# identitet
M = np.zeros((dim, dim), dtype=complex)
# prava permutacija — SVE kolone imaju TAČNO JEDNU jedinicu
for y in range(dim):
if y < N:
new = (a * y) % N
else:
new = y # identitet izvan domena
M[new, y] = 1.0
return M, m
# ===================================================================
# KONTROLISANA modularna eksponencijacija
# ===================================================================
def apply_controlled_modexp(qc, control_qubit, target_qubits, a, N, power):
M, m = build_mult_a_mod_N(a, N)
Mpow = np.linalg.matrix_power(M, power)
gate = UnitaryGate(Mpow, label=f"U^{power}")
cgate = gate.control()
qc.append(cgate, [control_qubit] + target_qubits)
# ===================================================================
# GLAVNI KOD — KONSTRUKCIJA KOLA
# ===================================================================
def shor_order_finding_circuit(N, a, n_count):
g = gcd(a, N)
if g != 1:
raise ValueError(f"Trivijalni faktor: gcd({a},{N}) = {g}")
_, m = build_mult_a_mod_N(a, N)
qc = QuantumCircuit(n_count + m, n_count)
target_qubits = list(range(n_count, n_count + m))
qc.barrier()
# -------------------------------------------------------------------
# Izaberi početno stanje drugog registra:
# -------------------------------------------------------------------
# y = 1
qc.x(target_qubits[0])
# y = 5 = 00101 (LSB = qubit[0])
# qc.x(target_qubits[0])
# qc.x(target_qubits[2])
# y = 10 = 01010
# qc.x(target_qubits[1])
# qc.x(target_qubits[3])
qc.barrier()
# H-gejtovi na mernim kubitima
for i in range(n_count):
qc.h(i)
qc.barrier()
# primena kontrolisanih U^{2^k}
for k in range(n_count):
repetitions = 2**k
apply_controlled_modexp(
qc,
control_qubit=k,
target_qubits=target_qubits,
a=a,
N=N,
power=repetitions
)
qc.barrier()
# inverzna QFT
qc = qc.compose(QFT(num_qubits=n_count, inverse=True),
list(range(n_count)))
qc.barrier()
# merenje
for i in range(n_count):
qc.measure(i, i)
return qc, m
# ===================================================================
# POKRETANJE
# ===================================================================
if __name__ == "__main__":
# ... (prethodni deo koda: inicijalizacija, shor_circuit poziv) ...
qc, m = shor_order_finding_circuit(N, a, n_count)
# 1. Pokrećemo simulaciju da dobijemo 'counts' (rečnik {stanje: broj_pojavljivanja})
# Pretpostavljam da tvoja biblioteka 'q' ima funkciju koja vraća counts objekat
# Ako ne, koristi: counts = q.run_sim(qc) ili ekvivalent
# Ovde ću koristiti standardni qiskit poziv radi sigurnosti, prilagodi ako koristiš svoju biblioteku:
q.show_qc(qc)
counts=q.show_measurement(qc)
# --- KLJUČNI DEO: FILTRIRANJE NULE ---
# Pravimo novi rečnik koji NE SADRŽI stanje '0' (fazu 0)
counts_without_zero = {}
for bitstring, count in counts.items():
# Uklanjamo razmake ako ih Qiskit ubaci i konvertujemo u int
value = int(bitstring.replace(" ", ""), 2)
if value != 0:
counts_without_zero[bitstring] = count
print(f"\n--- ANALIZA MERENJA) ---")
if not counts_without_zero:
print("Greška: Sva merenja su bila 0. Pokreni ponovo sa više shot-ova.")
else:
# Sada tražimo MAX u podacima bez nule
best_bitstring = max(counts_without_zero, key=counts_without_zero.get)
measured_int = int(best_bitstring.replace(" ", ""), 2)
print(f"Odabrano stanje (izbačena nula): {measured_int}")
# Izračunavanje faze
phase_val = measured_int / (2**n_count)
print(f"Izmerena faza: {phase_val:.4f}")
# Verižni razlomci
r_quantum = get_order_from_measurement(measured_int, n_count, N)
print(f"Period izračunat iz kvantnog merenja: r = {r_quantum}")
# Provera
result_mod = pow(a, r_quantum, N)
if result_mod == 1:
print(f"USPEH: {a}^{r_quantum} ≡ 1 (mod {N})")
else:
print(f"REZULTAT ORBITE: {a}^{r_quantum} ≡ {result_mod} (mod {N})")
Stanja kubita
from qiskit import QuantumCircuit
import quantum_utils_v1 as q
# ============================================================================
# STANJA KUBITA: SUPERPOZICIJA, FAZNI GEJTOVI I POVRATAK U RAČUNSKU BAZU
# ============================================================================
# Tipičan obrazac kvantnog računanja u tri faze:
# 1) ULAZAK u superpoziciju — Adamar (H) na svaki kubit
# 2) RAD u superpoziciji — fazni gejtovi (Z, T) menjaju FAZE baznih stanja
# 3) IZLAZAK iz superpozicije — ponovo H, da se faze pretvore u merljive razlike
#
# Fazni gejtovi (Z = faza π, T = faza π/4) ne menjaju verovatnoće dok smo u
# superpoziciji, ali zato menjaju faze; tek završni H te "skrivene" faze pretvara
# u različite verovatnoće pri merenju. Prati Blohovu sferu posle svake faze.
# ============================================================================
# Kvantno kolo sa dva kubita i dva klasična bita.
qc = QuantumCircuit(2, 2)
qc.reset([0, 1]) # Inicijalizacija kubita 0 i 1 u stanje |0⟩
qc.barrier() # Barijera = vizuelni presek (ne menja stanje)
q.show_bloch_sphere(qc) # Blohova sfera sa trenutnim stanjem kubita
# Faza 1: ulazak u superpoziciju (oba kubita u |+⟩)
qc.h(0)
qc.h(1)
qc.barrier()
q.show_bloch_sphere(qc)
# Faza 2: rad u superpoziciji — fazni gejtovi rotiraju faze oko Z ose
qc.z(1) # Z: fazni pomak za π na kubitu 1
qc.t(0) # T: fazni pomak za π/4 na kubitu 0
qc.barrier()
q.show_bloch_sphere(qc)
# Faza 3: izlazak iz superpozicije i povratak u računsku bazu
qc.h(0)
qc.h(1)
qc.barrier()
q.show_bloch_sphere(qc)
# Merenje
qc.measure(0, 0)
qc.measure(1, 1)
qc.barrier()
q.show_qc(qc)
q.show_measurement(qc)
Tofoli gejt
from qiskit import QuantumCircuit
import quantum_utils_v1 as q
# ============================================================================
# TOFFOLI (CCX) GEJT — KVANTNO "I" (AND)
# ============================================================================
# Toffoli gejt (ccx) ima dva kontrolna kubita i jedan ciljni:
# - ciljni kubit se invertuje (X) SAMO ako su OBA kontrolna kubita |1⟩;
# - u svim ostalim slučajevima ciljni kubit ostaje nepromenjen.
#
# Ako je ciljni kubit na početku |0⟩, posle Toffolija on sadrži (kontrola1 AND
# kontrola2). Zato je Toffoli reverzibilna verzija klasičnog logičkog "I" kola
# i dovoljan je za izgradnju bilo kog klasičnog logičkog kola na kvantnom računaru.
#
# VAŽNO o redosledu bitova: Qiskit koristi "little-endian" zapis |q2 q1 q0⟩,
# gde je q0 krajnje desno. Zato kada postavimo q0=1 i q1=1 (a q2=0), funkcija
# print_state će stanje ispisati kao |011⟩, iako mi neformalno kažemo "|110⟩"
# čitajući kubite redom q0,q1,q2. To je ista stvar, samo različit smer čitanja.
# ============================================================================
# q0 i q1 su kontrolni kubiti, q2 je ciljni kubit. Treći klasični bit služi za merenje.
qc = QuantumCircuit(3, 1)
qc.barrier()
q.show_bloch_sphere(qc)
q.print_state(qc, "Početno stanje:", True)
# ============================================================================
# PRIPREMA: oba kontrolna kubita u |1⟩, ciljni ostaje |0⟩
# ============================================================================
qc.x(0)
qc.x(1)
qc.barrier()
print("\nPriprema stanja |110>")
q.show_bloch_sphere(qc)
q.print_state(qc, "Stanje:", True)
# ============================================================================
# PRIMENA TOFFOLI GEJTA
# ccx(control1, control2, target)
# ============================================================================
qc.ccx(0, 1, 2)
qc.barrier()
print("\nPosle primene Toffoli gejta")
q.show_bloch_sphere(qc)
q.print_state(qc, "Stanje:", True)
q.print_probabilities(qc, "Verovatnoće:")
# ============================================================================
# MERENJE CILJNOG KUBITA
# ============================================================================
qc.measure(2, 0)
q.show_qc(qc)
q.show_measurement(qc)
Uradi sam
from qiskit import QuantumCircuit
import quantum_utils_v1 as q
# ============================================================================
# "URADI SAM" — KVANTNO KOLO KAO MAH-CENDEROV INTERFEROMETAR
# ============================================================================
# Ova vežba prevodi optički Mah-Cenderov (Mach–Zehnder) interferometar iz knjige
# u ekvivalentno kvantno kolo nad JEDNIM kubitom. Veza komponenti i gejtova:
#
# H-gejt ↔ polupropusno ogledalo (PPO) — deli/spaja "puteve" (superpozicija)
# Z-gejt ↔ staklena pločica — uvodi faznu razliku između dva puta
# X-gejt ↔ ogledala O1 i O2 — zamenjuju gornji i donji put
#
# Cilj vežbe: prati stanje na Blohovoj sferi posle svakog elementa i pokušaj da
# unapred PREDVIDIŠ rezultat merenja, pa proveri da li se poklapa sa histogramom.
# ============================================================================
# Jedan kubit i jedan klasični bit (za merenje na kraju).
qc = QuantumCircuit(1, 1)
qc.barrier()
q.show_bloch_sphere(qc)
qc.x(0) # Početno stanje |1⟩ (foton kreće "donjim" putem)
qc.barrier()
q.show_bloch_sphere(qc)
qc.h(0) # H-gejt – prvo polupropusno ogledalo (PPO1)
qc.barrier()
q.show_bloch_sphere(qc)
qc.z(0) # Z-gejt – staklena pločica (fazni pomak)
qc.barrier()
q.show_bloch_sphere(qc)
qc.x(0) # X-gejt – ogledala O1 i O2 (zamena puteva)
qc.barrier()
q.show_bloch_sphere(qc)
qc.h(0) # H-gejt – drugo polupropusno ogledalo (PPO2)
qc.barrier()
q.show_bloch_sphere(qc)
qc.measure(0, 0)
q.show_qc(qc)
q.show_measurement(qc)
X into Z gate
from qiskit import QuantumCircuit
import quantum_utils_v1 as q
"""
PROMENA BAZE: X "sendvič" daje Z gejt
=====================================
Adamar gejt (H) zamenjuje X i Z osu na Blohovoj sferi. Zato "uokvirivanje"
jednog gejta sa dva H gejta menja njegovu osu rotacije:
H · X · H = Z tj. |ψ⟩ --H--X--H--> Z|ψ⟩
Drugim rečima, bit-flip (X) posmatran u Adamardovoj bazi ponaša se kao
phase-flip (Z). Ovaj program to demonstrira korak po korak na Blohovoj sferi.
Identitet H·X·H = Z je par sa programom "Z into X gate.py", gde važi H·Z·H = X.
"""
qc = QuantumCircuit(1, 1)
qc.reset(0) # početno stanje |0⟩
qc.h(0) # |0⟩ → |+⟩ : ovde nije deo sendviča, već priprema vidljivog stanja
qc.barrier()
q.show_bloch_sphere(qc)
# Sendvič H–X–H. Rezultat je isti kao da smo direktno primenili Z gejt.
qc.h(0)
qc.x(0)
qc.h(0)
qc.barrier()
q.show_bloch_sphere(qc) # uporedi sa dejstvom samog Z gejta
qc.measure(0, 0)
q.show_qc(qc) # prikaz dijagrama kola
Z into X gate
from qiskit import QuantumCircuit
import quantum_utils_v1 as q
"""
PROMENA BAZE: Z "sendvič" daje X gejt
=====================================
Adamar gejt (H) zamenjuje X i Z osu na Blohovoj sferi. Zato sendvič
Z gejta između dva H gejta menja njegovu osu rotacije:
H · Z · H = X tj. |ψ⟩ --H--Z--H--> X|ψ⟩
Dakle phase-flip (Z) posmatran u Adamardovoj bazi ponaša se kao bit-flip (X).
Za razliku od "X into Z gate.py", ovde svaki korak prikazujemo posebno
(H, pa Z, pa H), da bi se na Blohovoj sferi videlo kako se stanje kreće.
Ovo je par sa programom "X into Z gate.py", gde važi H·X·H = Z.
"""
qc = QuantumCircuit(1, 1)
qc.reset(0) # početno stanje |0⟩
# qc.x(0) # otkomentariši da kreneš iz |1⟩ i uporediš rezultat
qc.barrier()
q.show_bloch_sphere(qc) # stanje |0⟩ (severni pol)
# Prvi H: prebacuje nas u Adamarovu bazu |0⟩ → |+⟩
qc.h(0)
qc.barrier()
q.show_bloch_sphere(qc)
# Z u toj bazi deluje kao bit-flip: rotira |+⟩ u |−⟩
qc.z(0)
qc.barrier()
q.show_bloch_sphere(qc)
# Drugi H: vraća nas u računsku bazu. Ukupni efekat H–Z–H jednak je X gejtu.
qc.h(0)
qc.barrier()
q.show_bloch_sphere(qc)
# Merenje radi provere konačnog stanja.
qc.measure(0, 0)
q.show_qc(qc) # prikaz dijagrama kola