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)

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

Preuzmi
"""
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

Preuzmi
Dojcov algoritam.py Otvori na GitHub-u
"""
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

Preuzmi
Entanglement 01.py Otvori na GitHub-u
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

Preuzmi
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

Preuzmi
Kvantna_teleportacija.py Otvori na GitHub-u
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

Preuzmi
Paulijevi gejtovi.py Otvori na GitHub-u
"""
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

Preuzmi
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

Preuzmi
QFT_Kodiranje_broja_u_fazu.py Otvori na GitHub-u
"""
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

Preuzmi
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

Preuzmi
quantum_utils_v1.py Otvori na GitHub-u
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

Preuzmi
"""
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

Preuzmi
Sorov_algoritam.py Otvori na GitHub-u
"""
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

Preuzmi
Stanja kubita.py Otvori na GitHub-u
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

Preuzmi
Tofoli gejt.py Otvori na GitHub-u
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

Preuzmi
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

Preuzmi
X into Z gate.py Otvori na GitHub-u
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

Preuzmi
Z into X gate.py Otvori na GitHub-u
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