跳转至

11. 量子计算

本章介绍计算平台提供的量子线路 GPU 模拟资源,以及集群账号申请、模拟器作业提交方法。

11.1 高性能 GPU 模拟器

主要包括部署在高能所 V100 GPU 集群上的高性能量子线路 GPU 模拟器,如 qiskit GPU 模拟器(qiskit-aer),支持基于 MPI 的多 GPU 并行。

11.2 集群账号与登录

没有高能所集群账号

参考 4.1.1 统一认证账号 节注册高能所统一认证账号,再申请集群 AFS 账号,在 隶属应用 处选择 QC,其余信息根据实际情况填写。

已有高能所集群账号

如已有高能所集群账号,但不属于 QC 用户组,则需按 4.1.3 账号申请添加辅组 申请副组。 具体登录 https://ink.ihep.ac.cn,在 Home → Apply for second linux group → Secondary group apply 处选择 QC 副组。

上述申请经审核通过后,会在集群的 /hpcfs/quantum/ 目录下生成用户目录。

11.3 模拟器作业提交

软件环境

使用 module 管理 MPI 编译器、量子模拟器等软件环境:

module use /hpcfs/quantum/modulefiles
module av
可以查看不同软件及相应版本,用 module load 可加载对应软件,如 module load quantum/qiskit-2.4.1-aer-gpu 可加载 IBM qiskit 量子编程框架,其通过 qiskit-aer 后端支持高性能 GPU 模拟,并可通过 MPI 进行多 GPU 模拟,以模拟超出单卡显存的量子线路。

Slurm 作业提交

在用 Slurm 调度系统提交量子模拟作业时,partition 需用 gpu,account 需用 qc,qos 需用 qcnormal,一个 Slurm 作业脚本(脚本可以叫 submit.sh)的例子如下:

#!/bin/bash
#SBATCH --partition=gpu
#SBATCH --qos=qcnormal
#SBATCH --account=qc
#SBATCH --job-name=qv
#SBATCH --output=qv.%j.out
#SBATCH --error=qv.%j.err
#SBATCH --nodes=1
#SBATCH --gres=gpu:v100:1
#SBATCH --ntasks=1
#SBATCH --mem=40G

module purge
module use /hpcfs/quantum/modulefiles
module load quantum/qiskit-2.4.1-aer-gpu

export OMP_NUM_THREADS=1

ulimit -s unlimited

echo "Slurm job ID: $SLURM_JOB_ID"
echo "Slurm job partition: $SLURM_JOB_PARTITION"
echo "Slurm job account: $SLURM_JOB_ACCOUNT"
echo "Slurm job nodes: $SLURM_JOB_NODELIST"

python compare_cpu_gpu.py --qubits-min 10 --qubits-max 25
其中 compare_cpu_gpu.py 是测试比较不同比特数时 qiskit 的 QuantumVolume 量子线路在 CPU 及 GPU 上运行时的性能差异,该脚本内容如下:
"""Compare CPU vs Thrust GPU simulation time on Quantum Volume circuits."""

from __future__ import annotations

import argparse

from qiskit import transpile
from qiskit.circuit import QuantumCircuit
from qiskit.circuit.library import quantum_volume
from qiskit_aer import AerSimulator

OUTPUT = "compare_cpu_gpu.png"
SEED = 12345

BACKENDS = (
    ("CPU", " dc2626", "s"),
    ("GPU", " 2563eb", "o"),
)


def make_circuit(qubits: int, depth: int) -> QuantumCircuit:
    circuit = quantum_volume(qubits, depth, seed=0)
    circuit.measure_all()
    return circuit


def run_benchmark(qubits: int, device: str, *, depth: int, shots: int) -> float:
    sim = AerSimulator(method="statevector", device=device)
    circuit = transpile(make_circuit(qubits, depth), sim)
    result = sim.run(circuit, shots=shots, seed_simulator=SEED).result()

    data = result.to_dict()["results"][0]
    if data.get("status") != "DONE":
        raise RuntimeError(f"{device} failed at {qubits} qubits: {data.get('status')}")
    return float(data["time_taken"])


def plot_results(
    qubits_list: list[int],
    timings: dict[str, list[float]],
    *,
    depth: int,
    shots: int,
) -> None:
    import matplotlib.pyplot as plt
    from matplotlib.ticker import MaxNLocator, ScalarFormatter

    plt.style.use("ggplot")
    fig, ax = plt.subplots(figsize=(9, 5.5), dpi=150)
    for device, color, marker in BACKENDS:
        ax.plot(
            qubits_list,
            timings[device],
            label=device,
            color=color,
            marker=marker,
            markersize=7,
            linewidth=2,
            markerfacecolor="white",
            markeredgewidth=1.8,
            markeredgecolor=color,
        )

    ax.set_xlabel("Number of qubits", fontsize=12)
    ax.set_ylabel("Simulation time (s)", fontsize=12)
    ax.set_title(
        f"CPU vs GPU (QV depth={depth}, shots={shots})",
        fontsize=13,
        fontweight="semibold",
        pad=12,
    )
    ax.set_xticks(qubits_list)
    ax.xaxis.set_major_locator(MaxNLocator(integer=True))
    ax.yaxis.set_major_formatter(ScalarFormatter(useMathText=True))
    ax.set_yscale("log")
    ax.legend(frameon=True, fancybox=True, shadow=False, fontsize=10)
    ax.grid(True, which="both", linestyle="--", linewidth=0.6, alpha=0.7)
    fig.tight_layout()
    fig.savefig(OUTPUT, bbox_inches="tight")
    plt.close(fig)


def main() -> None:
    parser = argparse.ArgumentParser(description="Compare CPU vs GPU on QV.")
    parser.add_argument("--qubits-min", type=int, default=20)
    parser.add_argument("--qubits-max", type=int, default=27)
    parser.add_argument("--depth", type=int, default=40)
    parser.add_argument("--shots", type=int, default=2000)
    args = parser.parse_args()

    qubits_list = list(range(args.qubits_min, args.qubits_max + 1))
    timings: dict[str, list[float]] = {device: [] for device, *_ in BACKENDS}

    for qubits in qubits_list:
        for device, *_ in BACKENDS:
            elapsed = run_benchmark(
                qubits, device, depth=args.depth, shots=args.shots
            )
            timings[device].append(elapsed)
            print(f"qubits={qubits:2d}  {device}: {elapsed:.6f} s", flush=True)

    for device, *_ in BACKENDS:
        print(f"{device}: {timings[device]}", flush=True)

    plot_results(qubits_list, timings, depth=args.depth, shots=args.shots)
    print(f"Saved plot: {OUTPUT}", flush=True)


if __name__ == "__main__":
    main()
通过 sbatch submit.sh 可以将任务提交到 GPU 计算节点,使用 squeue -u $USER 可以查看当前用户的作业运行情况。

得到结果如下图所示,可以看到用 GPU 进行量子线路模拟要快许多。

qiskit-aer 模拟器 CPU 与 GPU 性能对比

图 11-1 qiskit-aer 模拟器 CPU 与 GPU 性能对比

上述脚本位于 /hpcfs/quantum/examples/qiskit 目录,可供参考(qiskit 版本为 2.4.1,qiskit-aer 版本为 0.17.2)。

常见问题(FAQ)

见常见问题。

🤖 AI 助手
💬 快速提问 ×

有问题?输入后跳转到对话页继续交流