实验运行调度管理器

Prepper 类是 Quatm 实验调度与运行系统的核心组件,负责管理实验队列、线程调度和状态跟踪。

概述

Prepper 类(位于 quatm.gui.prepper 模块)是实验调度和运行系统的核心,提供以下核心能力:

  • 任务队列管理:维护待执行的实验任务列表
  • 线程调度:基于线程池的单线程调度策略,避免并发冲突
  • 状态跟踪:通过 expDict 追踪每个实验的配置和运行状态
  • 定时轮询:周期性检查任务状态并更新 UI

核心架构

graph TD
    A[实验任务入队] --> B[queue 任务队列]
    B --> C[threadpool 线程池]
    C --> D[执行实验]
    D --> E[expDict 状态更新]
    E --> F[timer 定时刷新 UI]
    F --> G[browser 界面展示]

API 参考

构造函数

from quatm.gui.prepper import Prepper

prepper = Prepper(browser=None)
参数类型必填说明
browserobject浏览器/UI 对象,用于界面交互和状态展示

成员变量

变量类型说明
browserobject关联的 UI 浏览器对象
queueQueue实验任务队列(FIFO)
expDictdict实验配置与状态字典
threadpoolThreadPoolExecutor线程池(max_workers=1)
timerQTimerUI 定时刷新器

使用示例

基础用法

from quatm.gui.prepper import Prepper
from quatm.experiments import RabiExperiment

# 1. 初始化调度器
prepper = Prepper(browser=my_browser)

# 2. 创建实验并加入队列
exp = RabiExperiment(
    qubit="q0",
    durations=[10, 20, 50, 100],  # ns
    amplitudes=[0.1, 0.2, 0.3, 0.4]
)
prepper.queue.put(exp)

# 3. 启动调度(从队列中取出并执行)
prepper.start()

批量实验调度

# 批量提交多个实验
experiments = [
    RabiExperiment(qubit="q0", durations=range(10, 100, 10)),
    RamseyExperiment(qubit="q0", delays=range(0, 1000, 50)),
    T1Experiment(qubit="q0", wait_times=range(1, 100, 5)),
]

for exp in experiments:
    prepper.queue.put(exp)

# 调度器会按 FIFO 顺序逐个执行
prepper.start()

线程模型

Prepper 使用 max_workers=1 的线程池设计,原因如下:

  1. 硬件安全:避免多个实验同时操作同一 FPGA 控制器,防止寄存器冲突
  2. 状态一致性:确保 expDict 在单线程下读写安全,无需加锁
  3. 可预测性:实验按入队顺序严格串行执行,结果可复现
from concurrent.futures import ThreadPoolExecutor

# Prepper 内部等价于:
self.threadpool = ThreadPoolExecutor(max_workers=1)

错误处理

调度器内置了异常捕获机制,通过 traceback 模块记录详细的堆栈信息:

import traceback

def _run_experiment(self, exp):
    try:
        result = exp.run()
        self.expDict[exp.id] = {"status": "done", "result": result}
    except Exception as e:
        self.expDict[exp.id] = {
            "status": "error",
            "error": str(e),
            "traceback": traceback.format_exc()
        }

实验失败不会中断调度器——错误被记录在 expDict 中,后续任务继续执行。

扩展指南

添加调度策略

如需支持优先级调度或依赖调度,可以扩展 Prepper 类:

class PriorityPrepper(Prepper):
    """支持优先级调度的实验管理器"""

    def __init__(self, browser=None):
        super().__init__(browser)
        from queue import PriorityQueue
        self.queue = PriorityQueue()

    def submit(self, exp, priority=0):
        """提交实验并指定优先级(数值越小优先级越高)"""
        self.queue.put((priority, exp))

添加实验类型

通过实现统一的实验接口,Prepper 可以支持任意实验类型:

class BaseExperiment:
    """所有实验的基类"""

    def run(self):
        raise NotImplementedError

    @property
    def id(self):
        return id(self)

# Prepper 通过鸭子类型支持任何实现了 run() 方法的对象

常见问题

Q: 为什么线程池大小固定为 1? A: 量子物理实验中,多个实验同时操作同一硬件可能导致不可预料的结果。单线程模型保证了时序确定性。

Q: 如何查看当前队列中的任务? A: 通过 prepper.queue.qsize() 获取待执行任务数,通过 prepper.expDict 查看各实验状态。

Q: 实验执行中可以暂停吗? A: 任何进程或者子进程都可以在实验执行过程中随时暂停、编辑、删除和添加。