1. Ana sayfa
  2. İçerik planı
  3. Job sistemi · Qiskit
Qiskit · devre yürütme

Job sistemi — asenkron tutamak, durum makinesi ve sonuç okuma

Qiskit'te bir devreyi backend.run(...) ile gönderdiğinizde, karşılığında sonuç değil bir Job nesnesi alırsınız. Bu nesne, yürütmenin tutamak (handle) tarafıdır: hangi backend'e gönderildi, hangi durumda (queued / running / done / cancelled / error), ne zaman sıraya girdi, sonuçlar geldiğinde nasıl çözülecek. Yerel simülatörde bu döngü neredeyse anlık tamamlanır; gerçek donanımda ise iş kuyruğa girer, dakikalar veya saatler sonra sonuçlanır. Bu sayfa Job'un kavramsal yapısını, durum makinesini, polling ve callback disiplinini, sonuç okuma ve hata sınıflarını uzun uzun açar; ardından tamamen yerel (FakeBackend + Aer) örneklerle aynı kalıbı hesapsız uygulanabilir hale getirir. Hiçbir kod canlı IBM API'sine bağlanmaz.

  • Rol: yürütmenin asenkron tutamağı
  • Durum: queued · running · done · error
  • Yerel: Aer + FakeBackend ile dayanıklı desen

Job nedir, neyi temsil eder?

Job, Qiskit'in yürütme katmanında "şu iş istek olarak kabul edildi, sonucunu seninle nasıl paylaşacağımı bilen tutamak budur" nesnesidir. İçinde sonuç yoktur; sonuca nasıl ulaşılacağı vardır. Bu ayrım bilinçli bir tasarım kararıdır: kuantum donanımı gerçek dünyada saniyeler içinde cevap vermez. Job nesnesi, kuyruğa girip milisaniyeden saatlere uzanabilecek bir yürütmenin kimliğini, durumunu ve sonuç çözüm yolunu taşır.

Backend ile ayrım

Backend mimarisi sayfasındaki sözleşmeye göre backend.run(circuit) her zaman bir Job döndürür; geriye doğrudan bir Result dönmez. Yerel Aer'da bile bu sözleşme korunur: yürütme neredeyse anlık biter ama yine de bir Job nesnesi üzerinden okunur. Bu tutarlılık, kodun gerçek donanıma taşınmasını sağlar — algoritma mantığı aynıdır, yalnızca bekleme süresi değişir.

Bu sayfanın sınırı

Burada Runtime primitives'in (Sampler/Estimator) Job davranışı ayrı bir sayfada (runtime yürütmesi) işlenir; Job'un kuyruğa girme tarafı kuyruk mantığı sayfasındadır. Bu sayfa, geleneksel backend.run(...).result() akışı ve onun etrafında dönen disiplinleri merkez alır.

Tek cümle Job, sonucun kendisi değil; sonucu zamanı geldiğinde nasıl alacağımı bilen asenkron bir tutamaktır.

Yaşam döngüsü ve durum makinesi

Bir Job, sınırlı bir durum kümesi arasında geçer. Tipik akış: INITIALIZING (oluşturuldu) → VALIDATING (gönderim onaylanıyor) → QUEUED (kuyrukta) → RUNNING (donanımda yürütülüyor) → DONE (sonuç hazır). Yan dallar: CANCELLED (kullanıcı veya sistem iptali) ve ERROR (transpile/donanım/kanal hatası). Yerel simülatörde ara durumlar çoğu zaman görünmeden tamamlanır; kullanıcı yalnızca bitişi okur. Üretim kodu durum değişimini varsaymamalı, sorgulamalıdır.

Geri dönüşsüz ve geri dönülebilir

DONE, CANCELLED ve ERROR terminal durumlardır — sonrasında durum değişmez. QUEUED ve RUNNING geçici durumlardır. Bu yüzden kod, "henüz bitmedi" döngüsünden çıkarken terminal kümeye geçtiğinde durmalıdır; sonsuz polling yapmamalıdır.

"done" sözcüğünün tuzağı

DONE durumu yalnızca iş bitti demektir, otomatik olarak "başarılı" anlamına gelmez. Bazı arka uçlarda DONE sonrası bile job.result() beklenmedik içerik üretebilir (örneğin kalibrasyon kayması). Bu yüzden raporlamada "job tamamlandı" ile "algoritma başarılı" farklı satırlardır.

Yerel ve gerçek donanım farkı

Aynı arayüz, farklı zaman boyutları. Yerel Aer'da job.result() çağrısı, çoğu küçük devrede milisaniyeler içinde döner; teknik olarak hâlâ asenkron tutamak vardır ama beklemenin kullanıcı için bir anlamı kalmaz. Gerçek donanımda aynı çağrı, kuyruk derinliğine göre dakikalar veya saatler beklenebilir; arada güvenlik penceresi, kalibrasyon turu veya bakım penceresi geçebilir. Kodun bu iki uçta da çalışabilir olması, Qiskit'in donanım-bağımsız duruşunun pratik karşılığıdır.

Yerel-önce disiplini

Bir algoritmayı gerçek cihazda denemeden önce Aer üzerinde ve FakeBackend ile koşturmak; "Job hatası mı, devre hatası mı?" sorusunun ayrımını mümkün kılar. Bu adım atlanırsa, kuyrukta saatlerce beklenen bir işin sonunda "transpile hatasıydı" demek son derece pahalıdır.

Shot sayısı ve süre ilişkisi

Shot sayısı arttıkça yerel yürütme süresi doğrusal uzar; gerçek donanımda ise shot başına süre çoğunlukla baskın etmen değildir — kuyrukta bekleme ve hedef qubit'lere atanma asıl maliyettir. Shot mantığı sayfası bu istatistik tartışmasını derinleştirir.

Polling, callback ve bloklayan beklemeler

job.result() çağrısı tarihsel olarak bloklayıcıdır: iş bitene kadar bekler. Küçük yerel devrelerde sorun çıkarmaz; ama uzun kuyruklarda ana iş parçacığı saatlerce kilitlenebilir. Dolayısıyla üretim kodu üç desenden birini seçer: (a) polling — düzenli aralıkla job.status() sorgusu; (b) callback — backend / Runtime tarafının desteklediği bildirim hook'u; (c) arka plan — işi farklı bir thread/process'e bırakıp ana akışı serbest bırakmak.

Polling aralığı tasarımı

Çok sık polling (örneğin saniyede bir) ne donanıma ne ağa fayda sağlar; çoğu hizmet rate-limit uygulayabilir. Tipik desen üstel artan aralıktır: 1 sn → 2 sn → 4 sn → 8 sn → 30 sn üst sınırı. Yerel simülatörde polling zaten gereksizdir; örnek kodda yalnızca kalıbı görmek için kullanılır.

Bloklayan beklemeden uzaklaşmak

Jupyter ve script ortamında result() çağrısının doğrudan kullanılması anlaşılır bir başlangıçtır; fakat ölçeklendikçe arka plan veya callback ile değiştirilir. Bu kararın sorumluluğu yine kullanıcıdadır; Job arayüzü her iki yolu da destekler.

Result nesnesi ve okuma sözleşmesi

Result, Job'un terminal DONE durumunda taşıdığı veri kabıdır. İçinde (genellikle) bir veya daha fazla devre için shot sayımları, statevector snapshot'ı, yoğunluk matrisi, memory dökümü gibi alanlar bulunur. Hangi alanların dolu olduğu, devrenin ve backend'in türüne bağlıdır: qasm simülasyonu, save_statevector ve save_probabilities sayfaları bu kabuğun farklı pencerelerini gösterir.

get_counts ve devre sırası

result.get_counts() birden fazla devre varsa bir liste döndürür; tek devre varsa doğrudan sözlük. Kod, her iki şekli de düşünmelidir. Devre sırası backend.run([qc1, qc2]) çağrısındaki listeyle korunur; sonuç indeksi bu sıralamayla eşleşir.

Bit endian ve ölçüm sırası

Qiskit'in klasik kayıt yazımı genellikle sağdan sola okunur (LSB en sağda). Aynı devre farklı arka uçlarda farklı endian raporlayabilir; özellikle başka bir kütüphanenin çıktısıyla karşılaştırırken bu ayrımı yazılı olarak ifade etmek gerekir.

İptal, zaman aşımı ve retry disiplini

job.cancel() çağrısı, işi henüz QUEUED ya da RUNNING durumundayken iptal etmek için kullanılır. Bazı backend'ler iptali RUNNING aşamasında kabul etmeyebilir; iptal sonucu CANCELLED durumuna geçişin doğrulanması kodun sorumluluğundadır. Zaman aşımı için job.wait_for_final_state(timeout) gibi yardımcılar tercih edilir; iş süre tavanını aştığında kullanıcı kararı alınır (yeniden gönder, vazgeç, başka backend dene).

Retry stratejisi

Bir Job ERROR ile döndüğünde, kör retry tehlikelidir: aynı transpile hatası tekrar edebilir. Sağlıklı yaklaşım hata sınıfına bakmak — transpile hatası tekrar denenmeden düzeltilir; geçici ağ kesintisi ise kısa bir bekleyişle tekrar denenir. İterasyon başı başarısızlık logları, sonraki günde aynı tuzağa düşmemenin tek yoludur.

Idempotent gönderim

Aynı devreyi tekrar göndermek istatistiksel olarak zararsızdır (yalnızca shot toplamına eklenir) ama maliyet ve kuyruk tarafında "yeniden iş" sayılır. Üretim akışları, sonuçları fingerprint ile cache'leyerek aynı isteğin iki kez yapılmamasını sağlar.

Pratik kural İptali doğrula, retry'i sınıflandır, kısa beklemede üstel artır, uzun beklemede vazgeçme eşiği yaz. Bu üç adım, Job sisteminin "üretime hazır" kullanımının özetidir.

Hata sınıfları ve teşhis

Hata, Job'un ERROR durumuna düşmesiyle değil, çoğu zaman onunla birlikte gelen mesajla tanımlanır. Tipik aileler: (a) Transpile hatası — devre target'a uymadı, ölçülemeyen qubit hedeflendi, parametre vektörü bağlanmadı; (b) Donanım hatası — kalibrasyon kayması, kaynak yokluğu; (c) Kanal hatası — bağlantı koptu, oturum zaman aşımı; (d) Politika hatası — kota, plan limiti. Her aile farklı bir düzeltme akışı çağırır.

Stack trace okuma

Hatanın hangi paketten geldiğini görmek (qiskit / qiskit-aer / qiskit-ibm-runtime), kök nedeni daraltmanın en hızlı yoludur. Aer hatası ile RuntimeService hatası farklı dünyalardır; provider sistemi sayfasındaki disiplin burada da geçerlidir.

Yereli koru, buluttan büyüt

Hata ayıklamada yerel Aer veya FakeBackend ile devrelerinizi sağlayın; sonra aynı kodu buluta taşıyın. Hata yalnızca bulutta tekrar üretiliyorsa, sebep büyük olasılıkla ağ, kalibrasyon veya politika tarafındadır — devre değildir.

Job kimliği, kalıcılık ve loglama

Her Job, backend tarafından üretilen bir job_id taşır. Bu kimlik kalıcıdır: Python süreciniz çökse bile, daha sonra aynı id ile geri dönüp sonucu alabilirsiniz. Pratikte bu, "Notebook'um kapandı, hesaplama 3 saat sürüyordu" senaryosunun kurtarıcısıdır. Bu yüzden uzun süren her iş, gönderildiği anda id'siyle loglanmalıdır.

Geri alma akışı

Bir id'den Job nesnesini geri almak backend.retrieve_job(job_id) veya RuntimeService'in service.job(job_id) gibi çağrıları ile mümkündür. Yerel simülatörde bu API çoğu zaman gerekmez; gerçek donanımda hayat kurtarır.

Metadata ve etiketleme

Mümkünse her gönderime kısa bir etiket / tag ekleyin (algoritma adı, deney serisi, parametre hash'i). Sonradan dashboard'da aynı dönemin işlerini gruplamak hem hesap raporu hem hata ayıklama için değer üretir.

Kod laboratuvarı

Üç blok da tamamen yereldir: ağ yok, IBM hesabı yok. Birinci blok Job'un temel anatomisini (id, status, sonuç) gösterir. İkinci blok zarif polling kalıbını FakeBackend üzerinde uygular; gerçek donanımda kalıp aynıdır. Üçüncü blok, Aer üzerinde küçük bir iptal ve zaman aşımı denemesi yapar. Bulut karşılığı yalnızca yorum hattında durur.

Referans iskelet — çalıştırmayın Aşağıdaki kalıp IBM hesabı ve token gerektirir; statik sayfada çalıştırmanız önerilmez. Yalnızca akışı hatırlamak içindir.
# from qiskit_ibm_runtime import QiskitRuntimeService
# service  = QiskitRuntimeService()
# backend  = service.least_busy(simulator=False, operational=True)
# job      = backend.run(transpiled_qc, shots=1024)
# print("id:", job.job_id())
# result   = job.result()                              # bloklayıcı
# counts   = result.get_counts()
#
# # Daha sonra, başka bir oturumda:
# # job_again = service.job("")
# # counts    = job_again.result().get_counts()
job_anatomy.py Python
from qiskit import QuantumCircuit, transpile
from qiskit_aer import AerSimulator

qc = QuantumCircuit(2, 2)
qc.h(0)
qc.cx(0, 1)
qc.measure([0, 1], [0, 1])

backend = AerSimulator()
tqc = transpile(qc, backend=backend, optimization_level=2)
job = backend.run(tqc, shots=1024)

# Job: id + asenkron durum, henüz sonuç yok
print("job_id:", job.job_id())
print("status :", job.status())

result = job.result()
print("status :", job.status())
print("counts :", result.get_counts())
qiskit Aer · Job arayüzü her hedefte aynıdır UTF-8 · LF
job_polling_pattern.py Python
import time
from qiskit import QuantumCircuit, transpile
from qiskit.providers import JobStatus
from qiskit_ibm_runtime.fake_provider import FakeManilaV2

TERMINAL = {JobStatus.DONE, JobStatus.CANCELLED, JobStatus.ERROR}

def wait_with_backoff(job, *, max_total=30.0):
    """Üstel artan aralıkla durum sorgular; yerelde anında biter,
    gerçek donanımda da aynı kalıp kullanılır."""
    delay, waited = 0.25, 0.0
    while waited < max_total:
        status = job.status()
        print(f"[{waited:5.2f}s] status={status}")
        if status in TERMINAL:
            return status
        time.sleep(delay)
        waited += delay
        delay = min(delay * 2, 4.0)
    raise TimeoutError("İş süre tavanını aştı.")

qc = QuantumCircuit(2, 2)
qc.h(0)
qc.cx(0, 1)
qc.measure([0, 1], [0, 1])

backend = FakeManilaV2()
tqc = transpile(qc, backend=backend, optimization_level=2)
job = backend.run(tqc, shots=512)

final = wait_with_backoff(job, max_total=10.0)
if final == JobStatus.DONE:
    print("counts:", job.result().get_counts())
qiskit Polling · üstel artan aralık · terminal kümede dur UTF-8 · LF
job_cancel_and_timeout.py Python
from qiskit import QuantumCircuit
from qiskit.providers import JobStatus
from qiskit_aer import AerSimulator

qc = QuantumCircuit(3, 3)
qc.h(range(3))
qc.cx(0, 1)
qc.cx(1, 2)
qc.measure(range(3), range(3))

backend = AerSimulator()
job = backend.run(qc, shots=2048)

try:
    # İptal denemesi: yerel Aer çok hızlı biteceği için
    # büyük olasılıkla zaten DONE durumuna geçmiştir.
    cancelled = job.cancel()
    print("cancel() döndü:", cancelled, "status:", job.status())
except Exception as exc:
    print("Bu backend cancel'i desteklemiyor olabilir:", exc)

if job.status() == JobStatus.DONE:
    print("counts:", job.result().get_counts())
else:
    print("son durum:", job.status())
qiskit-aer cancel · destek backend'e göre değişir UTF-8 · LF

İleri okuma ve özet

Job, asenkron yürütmenin tutamağıdır: kimlik, durum, sonuç çözüm yolu. Polling üstel artar, terminal kümede durur; iptal doğrulanır; retry sınıflandırılır; uzun süren iş id'siyle loglanır. Yerel Aer ve FakeBackend ile aynı kalıbı kurarak gerçek donanıma sorunsuz taşınırsınız.

Özet Job = sonuç değil, sonucu çözmenin yolu. Durum makinesini sorgula, üstel polling kullan, iptali doğrula, retry'i sınıflandır, id'yi logla. Yerel öğrenmeden buluta taşınma kodun yeniden yazılmasını gerektirmez.