☰
Scientific Agent Skills 之 Cirq 量子实验指南:参数扫描、数据采集与 VQE/QAOA/QPE 全流程
2026/10/9 21:03:08 网站建设 项目流程

Scientific Agent Skills 之 Cirq 量子实验指南:参数扫描、数据采集与 VQE/QAOA/QPE 全流程

【免费下载链接】scientific-agent-skillsTurn any AI agent into an AI Scientist. The #1 Agent Skills library for science, used by 190,000+ scientists worldwide. 165 ready-to-use validated skills plus 100+ scientific databases covering biology, chemistry, medicine, and drug discovery. Compatible with Cursor, Claude Code, Codex, Pi, Antigravity, and the open Agent Skills standard.项目地址: https://gitcode.com/GitHub_Trending/cl/scientific-agent-skills

本文档取自 Scientific Agent Skills 仓库中 cirq 技能 的实验参考指南,系统讲解如何用 Google Cirq 框架设计、执行并分析量子实验——包括实验类的抽象、参数扫描(parameter sweep)、数据采集与结构化保存、ReCirq 风格的可复现实验组织、并行任务收集,以及 VQE、QAOA、QPE 三大经典算法的落地实现。读完本文,你将能够用一套「构建电路 → 参数扫描 → 批量采集 → 统计/可视化分析」的标准化流程,独立完成一个从想法到结论的量子实验闭环,并具备将其迁移到真实量子硬件前的全部软件侧能力。


1. 本文在仓库中的定位与运行前提

在本仓库中,cirq 技能 是面向科学智能体(AI Scientist)的文档型技能库:skills/cirq/目录下不含可执行脚本,而是由 1 份 SKILL.md 与references/目录下的六篇参考指南构成,分别覆盖电路构建(building)、仿真(simulation)、变换/编译(transformation)、硬件接入(hardware)、噪声建模(noise)与量子实验(experiments,即本文)。因此,本文中的全部示例代码都需要在读者自己的环境中运行。

运行环境要求(依据 SKILL.md 安装小节):

  • Python 3.11+;
  • Cirq 稳定版1.6.1(截至本仓库编写时的版本),建议生产/硬件运行环境固定版本号以保证可复现:
uv pip install "cirq==1.6.1"

本文示例还会用到numpy、pandas、sympy、scipy、matplotlib、networkx与 Python 标准库multiprocessing。按 SKILL.md 的定位说明,Cirq 适用于 Google Quantum AI 硬件、噪声感知电路设计、参数扫描与 ReCirq 实验模式等场景;若目标是 IBM 硬件可转向 qiskit 技能,量子机器学习请用 pennylane 技能,物理系统仿真请用 qutip 技能。


2. 实验设计:用基类统一「构建 → 运行 → 分析」三段式流程

任何严谨的量子实验都应把电路构建与数据分析解耦:电路只依赖一组可调参数,运行器负责采样,分析器只消费采样结果。实验中导出的基类QuantumExperiment正是这一模式的落地:

import cirq import numpy as np import pandas as pd class QuantumExperiment: """Base class for quantum experiments.""" def __init__(self, qubits, simulator=None): self.qubits = qubits self.simulator = simulator or cirq.Simulator() self.results = [] def build_circuit(self, **params): """Build circuit with given parameters.""" raise NotImplementedError def run(self, params_list, repetitions=1000): """Run experiment with parameter sweep.""" for params in params_list: circuit = self.build_circuit(**params) result = self.simulator.run(circuit, repetitions=repetitions) self.results.append({ 'params': params, 'result': result }) return self.results def analyze(self): """Analyze experimental results.""" raise NotImplementedError

设计要点:

  • simulator可注入:默认是cirq.Simulator()(纯态态矢量仿真),但也可以传入cirq.DensityMatrixSimulator()(含噪声的混态)、cirq.CliffordSimulator()(仅含 Clifford 门时可指数级加速)或后续要接入的硬件Sampler,参见仓库内 simulation.md 的性能与选型说明。抽象出「模拟器/采样器」这一层,就能让同一套实验代码先在本地仿真上跑通,再原样切换到量子硬件。
  • build_circuit/analyze作为抽象方法:把「每个参数点对应什么电路」和「结果如何解读」留给子类,基类只管循环调度与结果归档。
  • results累积全部数据点:每条记录同时保存参数快照与原始Result对象,便于后续做任何粒度的重分析。

一个继承示例(完整版见第 9 节的 VQE 端到端实验)只需实现build_circuit,即可自动获得整轮参数扫描能力。


3. 参数扫描:用sympy符号参数 + Sweep 组合器批量探索参数空间

3.1 参数化电路与 sweep 定义

量子变分类算法(VQE、QAOA 等)的核心是「参数化线路 + 扫描求解」。Cirq 采用sympy 符号参数机制:把sympy.Symbol直接嵌入门对象,待运行前再经参数解析器绑定具体数值。

import sympy # Define parameters theta = sympy.Symbol('theta') phi = sympy.Symbol('phi') # Create parameterized circuit def parameterized_circuit(qubits, theta, phi): return cirq.Circuit( cirq.ry(theta)(qubits[0]), cirq.rz(phi)(qubits[1]), cirq.CNOT(qubits[0], qubits[1]), cirq.measure(*qubits, key='result') ) # Define sweep sweep = cirq.Product( cirq.Linspace('theta', 0, np.pi, 20), cirq.Linspace('phi', 0, 2*np.pi, 20) ) # Run sweep circuit = parameterized_circuit(cirq.LineQubit.range(2), theta, phi) results = cirq.Simulator().run_sweep(circuit, params=sweep, repetitions=1000)

逐个拆解这段代码的工程语义:

  • 符号参数:theta、phi以sympy.Symbol形式传给cirq.ry/cirq.rz,构造出带自由参数的 GateOperation。测量门统一使用key='result'命名,后续读取结果时用它作索引。
  • cirq.Linspace('theta', 0, np.pi, 20):在区间[0, π]内等间隔取 20 个点;(key, start, stop, length)四个参数的含义一目了然,你也可以改用命名参数写法cirq.Linspace(key='theta', start=0, stop=2*np.pi, length=50)(见 simulation.md)。
  • cirq.Product:对两个一维扫描做笛卡尔积,共产生20 × 20 = 400个参数组合点,遍历全部组合。这是「扫网格、画能量景观」的标准做法。
  • run_sweep:Cirq 提供的高效批量入口,一次调用完成全部 400 点采样,返回一个Result列表,其顺序与sweep的迭代顺序一一对应。对比「手写 for 循环逐个simulator.run(...)」的做法,run_sweep能复用仿真器的内部调度,是仿真性能优化清单中的明确建议(见 simulation.md 的批处理对比)。

3.2 常见 sweep 组合器选择

在 simulation.md 中记录了两种形态各异的组合器,实验设计时需要区分:

组合器语义典型用途
cirq.Product(sweep_a, sweep_b)笛卡尔积(全组合),点数相乘网格化探索两参数空间,绘制 2D 参数景观
cirq.Zip(sweep_a, sweep_b)按位配对(并行),点数取最小θ 与 φ 成对变化、互为约束的扫描(如保持某个比值固定)
cirq.Linspace单参数等间隔扫描单个旋转角 θ 从 0 扫到 π

运行单个点或少量点时,还可以直接给run传param_resolver;但参数点一多就应改用 sweep,这正是下节数据采集的效率基础。


4. 数据采集:把扫描结果整理成结构化DataFrame

裸的Result对象难以直接做统计与存档。下面的采集函数将每次扫描的「参数值 + 测量直方图 + 采样总数」合并成一条记录,最终汇总为 pandasDataFrame并可一键导出 CSV:

def collect_experiment_data(circuit, sweep, simulator, repetitions=1000): """Collect and organize experimental data.""" data = [] results = simulator.run_sweep(circuit, params=sweep, repetitions=repetitions) for params, result in zip(sweep, results): # Extract parameters param_dict = {k: v for k, v in params.param_dict.items()} # Extract measurements counts = result.histogram(key='result') # Store in structured format data.append({ **param_dict, 'counts': counts, 'total': repetitions }) return pd.DataFrame(data) # Collect data df = collect_experiment_data(circuit, sweep, cirq.Simulator()) # Save to file df.to_csv('experiment_results.csv', index=False)

三个值得记录的工程细节:

  1. zip(sweep, results)的参数对齐:run_sweep结果与 sweep 迭代器按序对应,因此能用zip把每个结果还原到它的参数点。params.param_dict直接给出该点的{'theta': 值, 'phi': 值}字典,展开后即成为DataFrame的独立列。
  2. result.histogram(key='result'):把repetitions次采样折叠为{整数值结果: 计数}字典,是多比特测量的紧凑表示。需注意当测量多个量子比特时,整数值的比特位与测量门中量子比特的声明顺序一致,后续按位拆分时要保持同一套顺序约定。
  3. 采集与落盘分离:df同时保留counts(原始分布)与total(采样规模),既便于事后计算概率分布,也便于审计统计精度。将中间数据即时to_csv落盘,符合「昂贵的量子计算/仿真结果绝不丢失」的最佳实践。

5. ReCirq 框架:为可复现实验引入工程化结构

单文件脚本适合原型验证,但科学研究要求实验可复现、可追溯、可扩展。ReCirq 正是围绕这一目标提出的结构化实验框架(在 SKILL.md 的「When to Use」中,ReCirq 实验模式被列为该技能的核心能力之一)。其思路是把一个实验拆成职责单一、边界清晰的模块。

5.1 标准目录结构

""" Standard ReCirq experiment structure: experiment_name/ ├── __init__.py ├── experiment.py # Main experiment code ├── tasks.py # Data generation tasks ├── data_collection.py # Parallel data collection ├── analysis.py # Data analysis └── plots.py # Visualization """

每个文件的关注点被严格切分:experiment.py负责实验总流程编排,tasks.py定义单个数据生成任务(一个参数点),data_collection.py只关心怎么高效地跑完一堆任务,analysis.py与plots.py分别负责统计与出图。这样任何一个环节(如并行策略、可视化样式)的修改都不会污染实验逻辑本身。

5.2 任务化数据采集(Task-Based Data Collection)

任务化的核心是用@dataclass把一个参数点封装成自包含的任务对象——任务自带参数、知道自己怎么构建电路、怎么运行:

from dataclasses import dataclass from typing import List import cirq @dataclass class ExperimentTask: """Single task in parameter sweep.""" theta: float phi: float repetitions: int = 1000 def build_circuit(self, qubits): """Build circuit for this task.""" return cirq.Circuit( cirq.ry(self.theta)(qubits[0]), cirq.rz(self.phi)(qubits[1]), cirq.CNOT(qubits[0], qubits[1]), cirq.measure(*qubits, key='result') ) def run(self, qubits, simulator): """Execute task.""" circuit = self.build_circuit(qubits) result = simulator.run(circuit, repetitions=self.repetitions) return { 'theta': self.theta, 'phi': self.phi, 'result': result } # Create tasks tasks = [ ExperimentTask(theta=t, phi=p) for t in np.linspace(0, np.pi, 10) for p in np.linspace(0, 2*np.pi, 10) ] # Execute tasks qubits = cirq.LineQubit.range(2) simulator = cirq.Simulator() results = [task.run(qubits, simulator) for task in tasks]

任务对象自带默认值repetitions=1000、字段即元数据,这让参数记录随任务一同流转,天然规避了「跑了实验却忘了当时用的什么参数」的经典事故。

5.3 并行数据采集(Parallel Data Collection)

参数扫描中每个任务之间彼此独立,是教科书式的可并行负载。用multiprocessing.Pool即可把任务均匀分发到多个工作进程:

from multiprocessing import Pool import functools def run_task_parallel(task, qubits, simulator): """Run single task (for parallel execution).""" return task.run(qubits, simulator) def collect_data_parallel(tasks, qubits, simulator, n_workers=4): """Collect data using parallel processing.""" # Create partial function with fixed arguments run_func = functools.partial( run_task_parallel, qubits=qubits, simulator=simulator ) # Run in parallel with Pool(n_workers) as pool: results = pool.map(run_func, tasks) return results # Use parallel collection results = collect_data_parallel(tasks, qubits, cirq.Simulator(), n_workers=8)

工程要点:functools.partial把qubits、simulator这两个不变参数预绑定,使pool.map只需针对变化的任务本身迭代;with Pool(n_workers)负责进程池的生命周期管理。需要注意multiprocessing要求任务对象可序列化(picklable)——这也是为什么推荐用@dataclass普通数据类封装任务,而非携带不可序列化对象。当任务耗时很长时,可进一步考虑把中间结果分批写盘,避免单点故障导致全量重跑。


6. 三大经典算法落地:VQE、QAOA 与 QPE

6.1 变分量子本征求解器(VQE)

VQE 用「参数化线路 + 经典优化器」迭代逼近哈密顿量的基态能量,是 NISQ 时代最典型的含噪应用之一。算法分两层:量子层计算当前参数下的能量期望值(cost_function),经典层用无梯度算法更新参数:

import scipy.optimize def vqe_experiment(hamiltonian, ansatz_func, initial_params): """Run VQE to find ground state energy.""" def cost_function(params): """Energy expectation value.""" circuit = ansatz_func(params) # Measure expectation value of Hamiltonian simulator = cirq.Simulator() result = simulator.simulate(circuit) energy = hamiltonian.expectation_from_state_vector( result.final_state_vector, qubit_map={q: i for i, q in enumerate(circuit.all_qubits())} ) return energy.real # Optimize parameters result = scipy.optimize.minimize( cost_function, initial_params, method='COBYLA' ) return result # Example: H2 molecule def h2_ansatz(params, qubits): """UCC ansatz for H2.""" theta = params[0] return cirq.Circuit( cirq.X(qubits[1]), cirq.ry(theta)(qubits[0]), cirq.CNOT(qubits[0], qubits[1]) ) # Define Hamiltonian (simplified) qubits = cirq.LineQubit.range(2) hamiltonian = cirq.PauliSum.from_pauli_strings([ cirq.PauliString({qubits[0]: cirq.Z}), cirq.PauliString({qubits[1]: cirq.Z}), cirq.PauliString({qubits[0]: cirq.Z, qubits[1]: cirq.Z}) ]) # Run VQE result = vqe_experiment( hamiltonian, lambda p: h2_ansatz(p, qubits), initial_params=[0.0] ) print(f"Ground state energy: {result.fun}") print(f"Optimal parameters: {result.x}")

其中几个环节的实现依据值得展开:

  • cirq.PauliSum/cirq.PauliString:以字典{qubit: Pauli}描述任意泡利串,并组合成哈密顿量的泡利和。这里的简化 H₂ 哈密顿量取Z₀ + Z₁ + Z₀Z₁(即不含量子化学系数、用于演示算法的教学版)。
  • expectation_from_state_vector:给出态矢量后,Cirq 直接计算⟨ψ|H|ψ⟩,其中qubit_map必须把「电路实际量子比特对象」映射到「态矢量索引」。这正是第 7 节纯态期望值计算所依赖的同一 API。
  • 选择COBYLA:一种不需要梯度的约束优化算法,非常适合量子变分线路——无需计算解析梯度,且对统计噪声有较好耐受性。初始化[0.0]后,输出result.fun即基态能量、result.x为最优变分参数。

此模板与 SKILL.md 中的变分算法通用模板 完全同构:后者将cost_function进一步抽象,任何量子化学、量子多体或组合优化问题只要提供「ansatz + 代价函数」即可复用同一套优化骨架。

6.2 量子近似优化算法(QAOA)

QAOA 面向组合优化(此处为 MaxCut)。其思想是把问题的代价哈密顿量编码进线路:先对所有比特施加 Hadamard 制备均匀叠加,再交替施加「问题哈密顿量(cost)」与「混合哈密顿量(mixer)」若干层(层数即p):

import networkx as nx def qaoa_circuit(graph, params, p_layers): """QAOA circuit for MaxCut problem.""" qubits = cirq.LineQubit.range(graph.number_of_nodes()) circuit = cirq.Circuit() # Initial superposition circuit.append(cirq.H(q) for q in qubits) # QAOA layers for layer in range(p_layers): gamma = params[layer] beta = params[p_layers + layer] # Problem Hamiltonian (cost) for edge in graph.edges(): i, j = edge circuit.append(cirq.ZZPowGate(exponent=gamma)(qubits[i], qubits[j])) # Mixer Hamiltonian circuit.append(cirq.rx(2 * beta)(q) for q in qubits) circuit.append(cirq.measure(*qubits, key='result')) return circuit # Run QAOA graph = nx.cycle_graph(4) p_layers = 2 def qaoa_cost(params): """Evaluate QAOA cost function.""" circuit = qaoa_circuit(graph, params, p_layers) simulator = cirq.Simulator() result = simulator.run(circuit, repetitions=1000) # Calculate MaxCut objective total_cost = 0 counts = result.histogram(key='result') for bitstring, count in counts.items(): cost = 0 bits = [(bitstring >> i) & 1 for i in range(graph.number_of_nodes())] for edge in graph.edges(): i, j = edge if bits[i] != bits[j]: cost += 1 total_cost += cost * count return -total_cost / 1000 # Maximize cut # Optimize initial_params = np.random.random(2 * p_layers) * np.pi result = scipy.optimize.minimize(qaoa_cost, initial_params, method='COBYLA') print(f"Optimal cost: {-result.fun}") print(f"Optimal parameters: {result.x}")

实现要点拆解:

  • 每层两个参数:gamma驱动 cost 层(图上每条边施加一次ZZPowGate(exponent=gamma)的两比特相位旋转,切割奖励对应比特对取值相异),beta驱动 mixer 层(rx(2*beta)绕 X 轴的旋转,负责在解空间中游走)。层数为p时参数总数即2*p,这里p_layers=2对应 4 个优化参数。
  • 从测量直方图反推目标值:把每个整数值直方图键按比特位拆成比特串,逐边判定「两端比特是否不同」,相同+1即切开的边数;按计数加权后取负号转为最小化问题(minimize默认最小化)。注意原实现按repetitions=1000硬编码归一化;若要提高可移植性,建议改为total = sum(counts.values())后做-total_cost / total,避免采样次数改动时代码失配。
  • 测试图与初值:nx.cycle_graph(4)(四节点环)是教科书级 MaxCut 基准;初值在[0, π]内随机抽取,COBYLA会驱动其收敛到切边数最大的比特分配。

6.3 量子相位估计(QPE)

QPE 是 Shor 算法与许多量子化学协议的核心子程序,用于估算酉算子本征值的相位。其结构为「本征态制备 + 叠加 → 受控酉幂次 → 逆 QFT → 测量」:

def qpe_circuit(unitary, eigenstate_prep, n_counting_qubits): """Quantum Phase Estimation circuit.""" counting_qubits = cirq.LineQubit.range(n_counting_qubits) target_qubit = cirq.LineQubit(n_counting_qubits) circuit = cirq.Circuit() # Prepare eigenstate circuit.append(eigenstate_prep(target_qubit)) # Apply Hadamard to counting qubits circuit.append(cirq.H(q) for q in counting_qubits) # Controlled unitaries for i, q in enumerate(counting_qubits): power = 2 ** (n_counting_qubits - 1 - i) # Apply controlled-U^power for _ in range(power): circuit.append(cirq.ControlledGate(unitary)(q, target_qubit)) # Inverse QFT on counting qubits circuit.append(inverse_qft(counting_qubits)) # Measure counting qubits circuit.append(cirq.measure(*counting_qubits, key='phase')) return circuit def inverse_qft(qubits): """Inverse Quantum Fourier Transform.""" n = len(qubits) ops = [] for i in range(n // 2): ops.append(cirq.SWAP(qubits[i], qubits[n - i - 1])) for i in range(n): for j in range(i): ops.append(cirq.CZPowGate(exponent=-1/2**(i-j))(qubits[j], qubits[i])) ops.append(cirq.H(qubits[i])) return ops

关键设计逐条说明:

  • 计数寄存器与控制寄存器分离:前n_counting_qubits个LineQubit作为计数寄存器,第n_counting_qubits号比特承载本征态。
  • 受控酉的幂次展开:对第i个计数比特施加U^(2^(n-1-i))。实现上通过把cirq.ControlledGate(unitary)循环施加power次达到幂次效果;ControlledGate封装任意单比特酉后自动成为两比特受控门,控制位为q、目标位为target_qubit。
  • 逆 QFT:先翻转寄存器顺序(SWAP),再以CZPowGate(exponent=-1/2**(i-j))实现由低位到高位的条件相位,最后逐比特施加 Hadamard,得到标准逆量子傅里叶变换;该函数返回操作列表,可直接被circuit.append展开。
  • 读取相位:对计数寄存器测量后,key='phase'的直方图整数值按位还原即得相位的小数展开。配合simulate得到的相位估计可与解析值对照,完成 QPE 正确性验证。

补充约定:QPE 与逆 QFT 示例偏教学演示,未包含本征态制备的具体实现细节。真实使用时eigenstate_prep通常返回「在 target 比特上制备已知本征态」的操作(例如对相位 0.5 的本征态加X)。在把这段实验代码投放到真实硬件前,建议先用仿真对照已知本征相位,确认采样分布峰值对应正确二进制展开。


7. 数据分析:统计量、期望值与保真度估计

7.1 测量统计量分析

对任意采样结果,第一件要事是把它折叠成概率分布,并提取熵与最概然输出。熵刻画分布的确定性——最大混合分布的熵接近比特数,而坍缩到单一态的分布熵为 0:

def analyze_measurement_statistics(results): """Analyze measurement statistics.""" counts = results.histogram(key='result') total = sum(counts.values()) # Calculate probabilities probabilities = {state: count/total for state, count in counts.items()} # Shannon entropy entropy = -sum(p * np.log2(p) for p in probabilities.values() if p > 0) # Most likely outcome most_likely = max(counts.items(), key=lambda x: x[1]) return { 'probabilities': probabilities, 'entropy': entropy, 'most_likely_state': most_likely[0], 'most_likely_probability': most_likely[1] / total }

p > 0的过滤保证了log2不会遇到0*log0的未定义情形;熵的单位为 bit。这是诊断「实验是否如预期坍缩」「结果是否均匀分散(可能是误差或退相干信号)」的第一手统计工具。

7.2 期望值计算(纯态路线)

测量期望值有两种路线:采样法(用统计频率近似期望,抗噪声但不精确)与态矢量法(由最终态直接解析计算,精确但只适用于无噪声理想仿真)。下面是后者:

def calculate_expectation_value(circuit, observable, simulator): """Calculate expectation value of observable.""" # Remove measurements circuit_no_measure = cirq.Circuit( op for op in circuit.all_operations() if not (isinstance(op, cirq.GateOperation) and isinstance(op.gate, cirq.MeasurementGate)) ) result = simulator.simulate(circuit_no_measure) state_vector = result.final_state_vector # Calculate ⟨ψ|O|ψ⟩ expectation = observable.expectation_from_state_vector( state_vector, qubit_map={q: i for i, q in enumerate(circuit.all_qubits())} ) return expectation.real

两个需要留意的实现点:

  • 测量门剔除:simulate在存在测量门时会退化为采样而非给出干净态矢量,因此要先从线路中移除测量操作。这里通过circuit.all_operations()遍历线路中全部操作(注意circuit直接迭代产出的是 Moment,逐 Moment 判断isinstance(m, cirq.MeasurementGate)无法命中门实例),再剔除门类型为cirq.MeasurementGate的GateOperation。
  • qubit_map映射一致性:expectation_from_state_vector需要将各量子比特对象映射到态矢量的索引下标,这里通过enumerate(circuit.all_qubits())建立。映射一旦与仿真器的比特排序不一致,期望值就会张冠李戴,这是此类 API 使用中最常见的错误源。

7.3 保真度估计

对无噪声仿真,两纯态之间的保真度有解析形式F = |⟨ψ|φ⟩|²:

def state_fidelity(state1, state2): """Calculate fidelity between two states.""" return np.abs(np.vdot(state1, state2)) ** 2

当只有采样直方图(无态矢量)时,则退化为基于概率分布的经典(Bhattacharyya)系数平方——它衡量两个测量分布的重叠程度,在噪声环境或硬件实验中是可观测量的常用代理指标:

def process_fidelity(result1, result2): """Calculate process fidelity from measurement results.""" counts1 = result1.histogram(key='result') counts2 = result2.histogram(key='result') # Normalize to probabilities total1 = sum(counts1.values()) total2 = sum(counts2.values()) probs1 = {k: v/total1 for k, v in counts1.items()} probs2 = {k: v/total2 for k, v in counts2.items()} # Classical fidelity (Bhattacharyya coefficient) all_states = set(probs1.keys()) | set(probs2.keys()) fidelity = sum(np.sqrt(probs1.get(s, 0) * probs2.get(s, 0)) for s in all_states) ** 2 return fidelity

set(probs1.keys()) | set(probs2.keys())取两个分布支持集的并集,配以.get(s, 0)默认值,保证了分布形状不同(某状态只出现在一边)时公式依然成立。典型用途包括:理想线路 vs 含噪声线路的输出保真度、同一算法在仿真器与硬件上的结果一致性核验。


8. 可视化:参数景观、收敛曲线与测量分布

可视化是把实验从「数字」变成「洞察」的最后一公里。

参数景观:把 2D 扫描(如 VQE 中 θ-φ 网格上的能量)画成等高线/热力填充图,一眼看出能量盆地的位置与陡峭程度,用于初值选择与代价函数形态诊断:

import matplotlib.pyplot as plt def plot_parameter_landscape(theta_vals, phi_vals, energies): """Plot 2D parameter landscape.""" plt.figure(figsize=(10, 8)) plt.contourf(theta_vals, phi_vals, energies, levels=50, cmap='viridis') plt.colorbar(label='Energy') plt.xlabel('θ') plt.ylabel('φ') plt.title('Energy Landscape') plt.show()

优化收敛曲线:变分类算法(VQE/QAOA)的关键健康指标是迭代过程中的代价函数轨迹。震荡下行是正常现象,长期不降则提示学习率/参数化或线路存在问题:

def plot_optimization_convergence(optimization_history): """Plot optimization convergence.""" iterations = range(len(optimization_history)) energies = [result['energy'] for result in optimization_history] plt.figure(figsize=(10, 6)) plt.plot(iterations, energies, 'b-', linewidth=2) plt.xlabel('Iteration') plt.ylabel('Energy') plt.title('Optimization Convergence') plt.grid(True) plt.show()

测量分布:直方图直接回答「多次采样后每个结果出现多少次」,是验证线路行为与噪声影响的快速通道:

def plot_measurement_distribution(results): """Plot measurement outcome distribution.""" counts = results.histogram(key='result') plt.figure(figsize=(12, 6)) plt.bar(counts.keys(), counts.values()) plt.xlabel('Measurement Outcome') plt.ylabel('Counts') plt.title('Measurement Distribution') plt.xticks(rotation=45) plt.tight_layout() plt.show()

上述三个函数分别对应对实验不同维度的审视:landscape看参数空间、convergence看优化过程、distribution看单点采样结果,三者组合即可完整复盘一次实验。


9. 完整示例:把一切串成一条流水线

把基类、扫描、期望值、优化器与可视化统一起来,就得到一次端到端 VQE 实验。子类只关心「如何由参数建电路」「如何算能量」,运行与分析的骨架完全复用:

# Full experimental workflow class VQEExperiment(QuantumExperiment): """Complete VQE experiment.""" def __init__(self, hamiltonian, ansatz, qubits): super().__init__(qubits) self.hamiltonian = hamiltonian self.ansatz = ansatz self.history = [] def build_circuit(self, params): return self.ansatz(params, self.qubits) def cost_function(self, params): circuit = self.build_circuit(params) result = self.simulator.simulate(circuit) energy = self.hamiltonian.expectation_from_state_vector( result.final_state_vector, qubit_map={q: i for i, q in enumerate(self.qubits)} ) self.history.append({'params': params, 'energy': energy.real}) return energy.real def run(self, initial_params): result = scipy.optimize.minimize( self.cost_function, initial_params, method='COBYLA', options={'maxiter': 100} ) return result def analyze(self): # Plot convergence energies = [h['energy'] for h in self.history] plt.plot(energies) plt.xlabel('Iteration') plt.ylabel('Energy') plt.title('VQE Convergence') plt.show() return { 'final_energy': self.history[-1]['energy'], 'optimal_params': self.history[-1]['params'], 'num_iterations': len(self.history) } # Run experiment experiment = VQEExperiment(hamiltonian, h2_ansatz, qubits) result = experiment.run(initial_params=[0.0]) analysis = experiment.analyze()

注意cost_function在每次求值时会向self.history追加{'params', 'energy'}记录——这是第 8 节plot_optimization_convergence所需输入的自然来源,也让analyze()无需重算即可输出收敛曲线与最优解摘要(最终能量、最优参数、迭代次数)。maxiter=100限制迭代预算,防止长跑失控。此处的hamiltonian、h2_ansatz、qubits沿用第 6.1 节的定义即可完整运行。


10. 最佳实践清单:让实验真正可复现

原文档给出 10 条实验最佳实践,这里结合本仓库的其余参考指南逐一给出落地解读:

  1. 清晰组织实验(Structure experiments clearly):采用上文 ReCirq 的模块切分(experiment / tasks / data_collection / analysis / plots)作为目录骨架,从结构上保证可复现。
  2. 分离任务(Separate tasks):数据生成、数据采集、数据分析各自独立成模块/函数,保证任何一环可单独替换与回归测试。
  3. 系统化使用参数扫描(Use parameter sweeps):凡涉及参数研究一律用Linspace/Product/Zip+run_sweep,而不是手写 for 循环逐点运行(性能对比见 simulation.md 的批处理说明)。
  4. 保存中间结果(Save intermediate results):每完成一批采样立即to_csv/落盘。仿真尚可重跑,硬件机时与长优化迭代的代价是不可逆的。
  5. 尽量并行(Parallelize when possible):独立任务用第 5.3 节的multiprocessing.Pool分发;n_workers建议按 CPU 核数与任务耗时权衡。
  6. 记录元数据(Track metadata):记录实验条件、时间戳与软件版本(如cirq.__version__、scipy.__version__、随机种子)。结合本仓库对版本固定的一致建议(SKILL.md 安装节),生产级实验应把依赖锁到同一 Cirq release。
  7. 先在模拟器上验证(Validate on simulators):任何硬件任务前先在cirq.Simulator()/DensityMatrixSimulator()上跑通并核验正确性——这也正是本节全程使用仿真器的原因;硬件事项参见同目录的 hardware.md。
  8. 实现错误处理(Implement error handling):长时实验脚本要覆盖try/except、结果文件校验与断点续跑逻辑,防止中途失败导致全量返工。
  9. 版本化管理数据(Version control data):实验数据与产生它的代码放在同一版本库中提交,让数据与代码版本一一对应。
  10. 充分文档化(Document thoroughly):记录每个函数的目的、参数含义、默认值与已知边界条件,保证他人(或未来的自己)可以复现并延续工作。

若你的实验进一步涉及电路优化(减少深度与门数以降低噪声影响),可在投放到含噪环境前参考 transformation.md 的编译与变换管线;若要为线路加入退极化/退相干等信道、或做随机基准测试(RB、XEB)等表征实验,见 noise.md;基础门与线路的组织模式(如 GHZ、QFT 等标准型)可查 building.md。这些同属 cirq 技能 的参考文档,与本指南相互衔接,共同构成一套「设计 → 仿真 → 编译 → 噪声 → 实验 → 分析」的完整量子研究工具链。

【免费下载链接】scientific-agent-skillsTurn any AI agent into an AI Scientist. The #1 Agent Skills library for science, used by 190,000+ scientists worldwide. 165 ready-to-use validated skills plus 100+ scientific databases covering biology, chemistry, medicine, and drug discovery. Compatible with Cursor, Claude Code, Codex, Pi, Antigravity, and the open Agent Skills standard.项目地址: https://gitcode.com/GitHub_Trending/cl/scientific-agent-skills

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询