When to Use sample vs. run
Both execute a kernel many times, but they return different things. Pick by what you need back.
The difference
sample | run | |
|---|---|---|
| Returns | Histogram of measured bits | List of typed kernel return values |
| Kernel return type | None (uses mz) | Required (bool, int, tuple…) |
| Best for | Distributions, tomography (data-collection step) | Conditional logic, custom outputs |
| Current status | 参考实现可用 — runnable core API | 规范接口·暂未实现 — not in the reference build yet |
qalgora.sample is part of the runnable reference core, but qalgora.run is a
specification interface the open reference build does not ship yet. The migration snippet below shows
the intended run usage; to run code today, stay with sample.
Migrating sample → run
# sample style: read raw bitstrings
@qalgora.kernel
def k_sample():
q = qalgora.qvector(2)
h(q[0]); x.ctrl(q[0], q[1])
mz(q)
# run style: return exactly what you care about
@qalgora.kernel
def k_run() -> tuple[bool, bool]:
q = qalgora.qvector(2)
h(q[0]); x.ctrl(q[0], q[1])
return mz(q[0]), mz(q[1])
results = qalgora.run(k_run, shots_count=100) # list[tuple[bool,bool]]Custom return types
The simplest custom return is a typed tuple: list the values you care about and
run hands back one typed record per shot.
@qalgora.kernel
def measure_pair() -> tuple[int, bool]:
data = qalgora.qvector(3)
h(data[0]); x.ctrl(data[0], data[1]); x.ctrl(data[1], data[2])
return mz(data[0]) + 2 * mz(data[1]), mz(data[2]) # (syndrome, logical)
records = qalgora.run(measure_pair, shots_count=500) # list[tuple[int, bool]]More elaborate dataclass / structured records are a higher-level specification
interface — the same kernel can instead return a user-defined record, so each shot is a named
structure rather than a bare tuple, handy when a kernel measures several registers with distinct
meanings.
from dataclasses import dataclass
@dataclass
class Readout:
syndrome: int
logical: bool
@qalgora.kernel
def measure_block() -> Readout:
data = qalgora.qvector(3)
h(data[0]); x.ctrl(data[0], data[1]); x.ctrl(data[1], data[2])
return Readout(syndrome=mz(data[0]) + 2 * mz(data[1]), logical=mz(data[2]))
records = qalgora.run(measure_block, shots_count=500) # list[Readout]Postselection
Because run hands you per-shot results, you can keep only the shots that pass a
condition — the basis of heralded state preparation and many error-detection schemes.
(continues the spec-only run example above; run is not in the open
reference build yet)
kept = [r for r in records if r.syndrome == 0] # discard shots that flagged an error
print(f"kept {len(kept)}/{len(records)} shots")
shots_count so enough survive — or fold the condition into the circuit instead.
何时使用 sample 与 run
两者都会多次执行内核,但返回的内容不同。根据所需结果来选择。
区别
sample | run | |
|---|---|---|
| 返回值 | 测量比特的直方图 | 内核返回值的列表(有类型) |
| 内核返回类型 | 无(使用 mz) | 必须指定(bool、int、tuple 等) |
| 适用场景 | 概率分布、量子态层析的数据采集环节 | 条件逻辑、自定义输出 |
| 当前状态 | 参考实现可用(核心 API) | 规范接口·暂未实现 |
qalgora.sample 属于可运行的参考核心,而 qalgora.run 是开放参考实现尚未内置的规范接口。下面的迁移片段展示的是 run 的预期用法;如需现在就运行,请继续使用 sample。
从 sample 迁移到 run
# sample style: read raw bitstrings
@qalgora.kernel
def k_sample():
q = qalgora.qvector(2)
h(q[0]); x.ctrl(q[0], q[1])
mz(q)
# run style: return exactly what you care about
@qalgora.kernel
def k_run() -> tuple[bool, bool]:
q = qalgora.qvector(2)
h(q[0]); x.ctrl(q[0], q[1])
return mz(q[0]), mz(q[1])
results = qalgora.run(k_run, shots_count=100) # list[tuple[bool,bool]]自定义返回类型
最简单的自定义返回是带类型的 tuple:把你关心的值逐个列出,run 每次 shot 回传一条带类型的记录。
@qalgora.kernel
def measure_pair() -> tuple[int, bool]:
data = qalgora.qvector(3)
h(data[0]); x.ctrl(data[0], data[1]); x.ctrl(data[1], data[2])
return mz(data[0]) + 2 * mz(data[1]), mz(data[2]) # (syndrome, logical)
records = qalgora.run(measure_pair, shots_count=500) # list[tuple[int, bool]]更复杂的 dataclass/结构化记录属更高层规范接口——同一个内核也可以改为返回用户自定义记录,让每次 shot 拿到的是带名字的结构而不是裸元组,在一个内核要测量多个含义不同的寄存器时尤其方便。
from dataclasses import dataclass
@dataclass
class Readout:
syndrome: int
logical: bool
@qalgora.kernel
def measure_block() -> Readout:
data = qalgora.qvector(3)
h(data[0]); x.ctrl(data[0], data[1]); x.ctrl(data[1], data[2])
return Readout(syndrome=mz(data[0]) + 2 * mz(data[1]), logical=mz(data[2]))
records = qalgora.run(measure_block, shots_count=500) # list[Readout]后选择
由于 run 把逐 shot 的结果直接交给你,你可以只保留满足条件的那些 shot。这正是预示式态制备和许多错误检测方案的基础。
(接续上面的 run 规范接口示例;run 暂未在开放参考实现中内置)
kept = [r for r in records if r.syndrome == 0] # 丢弃标记了错误的 shot
print(f"kept {len(kept)}/{len(records)} shots")
shots_count 调大,让足够多的 shot 存活下来;或者干脆把该条件编进线路里。