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.
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.
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.
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.
# 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()
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())
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())
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())
İ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.
- Backend mimarisi — BackendV2 sözleşmesi.
- Provider sistemi — kimlik ve katalog.
- Kuyruk mantığı — bekleme ve öncelik.
- Runtime yürütmesi ve oturum mantığı — primitives ailesi.
- Fake backend kullanımı — yerel öğretim hedefi.
- qasm simülasyonu ve shot mantığı — Result okuma ve istatistik.