跳转至

用三条本地路线求解同一个 MIS

在三节点路径图 a — b — c 上,比较 Digital QAOA、Hybrid QAOA 和 Analog QAA。固定图和解码顺序,每条路线尝试两个参数点,再用穷举答案核对选中的结果。这次练习要学会读比较报告,避免把“偶尔采到一次最优解”当成求解效果很好。

先完成图优化入门、D-A-D 实验和实验设计。使用学习环境,在仓库根目录运行。课堂版共三个节点、六次参数评估,每次采样 128 次。一次 macOS、Python 3.11 开发环境实测约需 1.5 秒,包含报告;更大问题或其他机器可能需要更长时间。

固定问题,说明比较条件

图有 (a, b) 和 (b, c) 两条边,位置分别为 (0, 0)、(6, 0)、(12, 0),单位为微米。唯一最大独立集为 {a, c},按 [a, b, c] 顺序编码为 101,大小为 2。

Digital 和 Hybrid 使用单层 QAOA,两个参数点为 (gamma_0, beta_0) = (0.16, 0.24) 和 (0.28, −0.18)。Analog 使用 QAA,两个调度为 (anneal_time, omega_max) = (0.4 us, 1 rad/us) 和 (0.7 us, 1.4 rad/us)。这些只是刻意控制规模的候选,没有经过训练,也不代表全局最优调度。参数点数量相同,不代表物理时长、模拟成本或搜索难度相同。

ProblemCompiler 把同一个图映射为三种程序。optimize(parameter_sets=...) 评估给定参数点,选择期望目标值最小的点,并列时按输入顺序选择。这个例子的目标值来自理想概率分布,counts 则是从选中分布得到的有限样本。当前流程在选参后没有额外进行独立确认采样。

运行前想一想:如果三条路线都至少采到过一次最优解,是否足以认为它们效果相同?

运行并比较报告

python3 examples/learning/projects/mis_routes.py

打开 artifacts/mis-routes/mis-zh.html。路线报告读取已完成的执行结果,按共用 Problem 目标进行比较。routes.json 保存图、完整执行结果、参数历史和模拟设置,英文报告来自同一批结果。

打开实验报告

下载原始数据(JSON)

{
  "artifacts": {
    "data": "artifacts/mis-routes/routes.json",
    "report_en": "artifacts/mis-routes/mis-en.html",
    "report_zh": "artifacts/mis-routes/mis-zh.html"
  },
  "classical_maximum_size": 2,
  "classical_optimal_bitstrings": [
    "101"
  ],
  "modes": {
    "analog": {
      "algorithm": "qaa",
      "best_observed_bitstring": "101",
      "best_observed_feasible": true,
      "counts": {
        "000": 92,
        "001": 9,
        "010": 12,
        "100": 13,
        "101": 2
      },
      "evaluation_count": 2,
      "feasible_frequency": 1.0,
      "history_shots": 256,
      "logical_order": [
        "a",
        "b",
        "c"
      ],
      "objective_value": -0.32725200794438375,
      "optimal_frequency": 0.015625,
      "parameter_sets": [
        {
          "anneal_time": 0.4,
          "omega_max": 1.0
        },
        {
          "anneal_time": 0.7,
          "omega_max": 1.4
        }
      ],
      "problem_hash": "826a555614f6b069611aa1a14214968356def14aba43be0b3f3a45c5c6ae1c5b",
      "selected_evaluation_index": 1,
      "selected_result_shots": 128
    },
    "digital": {
      "algorithm": "qaoa",
      "best_observed_bitstring": "101",
      "best_observed_feasible": true,
      "counts": {
        "000": 13,
        "001": 13,
        "010": 20,
        "011": 19,
        "100": 19,
        "101": 21,
        "110": 12,
        "111": 11
      },
      "evaluation_count": 2,
      "feasible_frequency": 0.671875,
      "history_shots": 256,
      "logical_order": [
        "a",
        "b",
        "c"
      ],
      "objective_value": -0.7202251513922284,
      "optimal_frequency": 0.1640625,
      "parameter_sets": [
        {
          "beta_0": 0.24,
          "gamma_0": 0.16
        },
        {
          "beta_0": -0.18,
          "gamma_0": 0.28
        }
      ],
      "problem_hash": "826a555614f6b069611aa1a14214968356def14aba43be0b3f3a45c5c6ae1c5b",
      "selected_evaluation_index": 1,
      "selected_result_shots": 128
    },
    "hybrid": {
      "algorithm": "qaoa",
      "best_observed_bitstring": "101",
      "best_observed_feasible": true,
      "counts": {
        "000": 13,
        "001": 13,
        "010": 20,
        "011": 19,
        "100": 19,
        "101": 21,
        "110": 12,
        "111": 11
      },
      "evaluation_count": 2,
      "feasible_frequency": 0.671875,
      "history_shots": 256,
      "logical_order": [
        "a",
        "b",
        "c"
      ],
      "objective_value": -0.7202251513922278,
      "optimal_frequency": 0.1640625,
      "parameter_sets": [
        {
          "beta_0": 0.24,
          "gamma_0": 0.16
        },
        {
          "beta_0": -0.18,
          "gamma_0": 0.28
        }
      ],
      "problem_hash": "826a555614f6b069611aa1a14214968356def14aba43be0b3f3a45c5c6ae1c5b",
      "selected_evaluation_index": 1,
      "selected_result_shots": 128
    }
  },
  "sdk_version": "1.0.8a",
  "seed": 4702,
  "shots_per_evaluation": 128
}
字段 含义
problem_hash、logical_order 检查三条路线是否使用相同数学问题和解码顺序
objective_value 选中参数点的编译目标期望值,本例越小越好
best_observed_bitstring 选中样本里观察到的最佳候选,不保证未来每次都能采到
feasible_frequency 选中样本中不违反图边约束的比例
optimal_frequency 选中样本中等于精确答案 101 的比例
history_shots 两个参数点的采样总和,默认每条路线 256 次
selected_result_shots 选中点的 128 个样本,已包含在历史预算内

整个搜索因此采样 768 次,不是 1,152 次:不能把选中点的样本再加一次。保存两种语言的报告也不会把实验运行两遍。

在这个本地理想例子里,Digital 和 Hybrid 实现等价的 QAOA 演化,概率在数值精度范围内一致;使用相同 seed 时,计数也可能相同。Analog 调度的时间演化和目标响应不同,需要结合它自己的设置,以及共同的解码问题来解释。

把状态空间差异写进结论

Analog 路线采用模拟目标的自动硬阻塞选择。在当前几何下,允许的基态包含独立集,并排除了相邻原子同时激发。因此,即使很少采到最优解,可行比例也可能为 1。这部分来自所选状态表示,不能据此判断短调度找到了更好的解。

Digital 和 Hybrid QAOA 可以采到无效位串,并由编译后的罚项处理。比较可行比例前先说明这个差异。研究比较还需决定保持哪些条件相同:搜索预算、目标约束、数值精度、物理时长,或其中几项。

阅读实现

脚本先穷举八个候选得到参考,再使用公开 Problem 编译和报告接口。可行比例与最优解频率直接从 counts 计算,你可以逐项核对。

"""Compare three local compilation routes for the same three-node MIS problem."""

from __future__ import annotations

import argparse
import json
from pathlib import Path
from typing import Any

import cascaqit
from cascaqit import LocalBackend, MockNeutralAtomTarget, SimulationOptions, visualize
from cascaqit.problems import GraphProblemIR, ProblemCompiler, decode_graph_bitstring


def experiment(
    output_dir: Path, *, shots: int = 128, seed: int = 4702
) -> dict[str, Any]:
    """Keep problem, candidates, parameter budgets and report inputs together."""
    if shots < 1:
        raise ValueError("shots must be positive.")
    output_dir.mkdir(parents=True, exist_ok=True)
    graph = GraphProblemIR.from_edges(
        problem_id="lesson.project.mis-routes",
        positions={"a": (0.0, 0.0), "b": (6.0, 0.0), "c": (12.0, 0.0)},
        edges=(("a", "b"), ("b", "c")),
    )
    candidates = [decode_graph_bitstring(graph, f"{i:03b}") for i in range(8)]
    feasible = [c for c in candidates if c["is_independent"]]
    optimum = max(c["selection_size"] for c in feasible)
    target = MockNeutralAtomTarget.local_ahs_v0_1()
    backend = LocalBackend(target=target, analog_time_steps=32)
    options = SimulationOptions(
        dtype="complex128", integrator="fixed_step_krylov", max_steps=32
    )
    parameter_sets = {
        "digital": (
            {"gamma_0": 0.16, "beta_0": 0.24},
            {"gamma_0": 0.28, "beta_0": -0.18},
        ),
        "hybrid": (
            {"gamma_0": 0.16, "beta_0": 0.24},
            {"gamma_0": 0.28, "beta_0": -0.18},
        ),
        "analog": (
            {"anneal_time": 0.4, "omega_max": 1.0},
            {"anneal_time": 0.7, "omega_max": 1.4},
        ),
    }
    executions = {}
    modes = {}
    for mode, parameters in parameter_sets.items():
        algorithm = "qaa" if mode == "analog" else "qaoa"
        compiled = ProblemCompiler().compile(
            graph, mode=mode, algorithm=algorithm, target=target
        )
        execution = compiled.optimize(
            parameter_sets=parameters,
            shots=shots,
            seed=seed,
            backend=backend,
            options=None if mode == "digital" else options,
        )
        counts = execution.result.counts
        decoded = [
            (decode_graph_bitstring(graph, bits), count)
            for bits, count in counts.items()
        ]
        feasible_shots = sum(count for item, count in decoded if item["is_independent"])
        optimal_shots = sum(
            count
            for item, count in decoded
            if item["is_independent"] and item["selection_size"] == optimum
        )
        history_shots = sum(item.shots for item in execution.parameter_history)
        modes[mode] = {
            "algorithm": algorithm,
            "problem_hash": execution.problem_hash,
            "logical_order": list(execution.logical_order),
            "parameter_sets": parameters,
            "evaluation_count": len(execution.parameter_history),
            "history_shots": history_shots,
            "selected_result_shots": sum(counts.values()),
            "selected_evaluation_index": execution.selected_evaluation_index,
            "objective_value": execution.objective_value,
            "best_observed_bitstring": execution.best_observed_candidate.bitstring,
            "best_observed_feasible": execution.best_observed_candidate.feasible,
            "counts": counts,
            "feasible_frequency": feasible_shots / shots,
            "optimal_frequency": optimal_shots / shots,
        }
        executions[mode] = execution
    artifacts = {"data": str(output_dir / "routes.json")}
    for language in ("en", "zh"):
        path = output_dir / f"mis-{language}.html"
        visualize(
            executions,
            output=path,
            language=language,
            title="MIS: Digital / Hybrid / Analog",
        )
        artifacts[f"report_{language}"] = str(path)
    summary = {
        "sdk_version": cascaqit.__version__,
        "seed": seed,
        "shots_per_evaluation": shots,
        "classical_maximum_size": optimum,
        "classical_optimal_bitstrings": [
            c["bitstring"] for c in feasible if c["selection_size"] == optimum
        ],
        "modes": modes,
        "artifacts": artifacts,
    }
    raw = {
        **summary,
        "graph": graph.to_dict(),
        "simulation_options": options.to_dict(),
        "executions": {mode: run.to_dict() for mode, run in executions.items()},
    }
    Path(artifacts["data"]).write_text(
        json.dumps(raw, indent=2) + "\n", encoding="utf-8"
    )
    return summary


def main() -> None:
    parser = argparse.ArgumentParser(description=__doc__)
    parser.add_argument("--output-dir", type=Path, default=Path("artifacts/mis-routes"))
    parser.add_argument("--shots", type=int, default=128)
    parser.add_argument("--seed", type=int, default=4702)
    args = parser.parse_args()
    print(
        json.dumps(
            experiment(args.output_dir, shots=args.shots, seed=args.seed),
            sort_keys=True,
        )
    )


if __name__ == "__main__":
    main()

下载完整脚本

写一份能复查的比较说明

  1. 从 counts 重新计算三条路线的可行比例和最优解频率。解释为什么仅看最佳位串会隐藏分布差异。
  2. 使用 --shots 1024 --seed 4703 --output-dir artifacts/mis-more-shots 重跑。哪个量是期望代价?哪些量会带有新的采样不确定度?
  3. 在本地副本中只修改 Analog 参数集,记录所有尝试过的调度和目标值。为什么仅凭两个手选参数点,还不足以比较 QAA 和 QAOA 各自能达到的最佳表现?
  4. 扩展到已有的 3×3 网格示例。运行前先列出增加后的变量数、基空间大小和数值设置,再决定预算。
核对思路

可行比例累加 000、001、010、100 和 101 的计数;最优解频率只使用 101。很少出现与经常出现的最优解,可能给出相同的最佳位串。这个理想流程的期望代价由概率得到,候选频率随 seed 和 shots 波动;增加采样不会探索新的参数点。3×3 图有九个变量,约化前的完整基空间有 512 个状态。原示例的精度和时间步设置也不同,必须记录,不能默认与这里的三节点实验相同。

研究扩展应预先确定搜索预算,保留全部参数历史,检查数值收敛性,并用独立样本确认选中候选。报告成功频率的不确定度,也要写明经典穷举或其他参考方法的适用规模。这个小型本地比较不能证明量子优势或真机性能。

English

SDK 1.0.8a · `6eff6362`