你当前正在访问 Microsoft Azure Global Edition 技术文档网站。 如果需要访问由世纪互联运营的 Microsoft Azure 中国技术文档网站,请访问 https://docs.azure.cn。
了解如何使用 qdk.azurePython 模块将特定格式的线路提交到 Azure Quantum 服务。 本文介绍如何以以下格式提交电路:
有关详细信息,请参阅量子线路。
先决条件
若要在 Visual Studio Code(VS Code)中开发和运行线路,必须具备以下各项:
具有活动订阅的Azure帐户。 如果没有Azure帐户,请免费注册并注册 即用即付订阅。
Azure Quantum工作区。 有关详细信息,请参阅 创建Azure Quantum工作区。
安装了 Python 环境,其中包括 Python 和 Pip。
安装了 Microsoft Quantum 开发工具包 (QDK)、Python 和 Jupyter 扩展的 VS Code。
qdkPython 库,带有azure附加功能,以及ipykernel包。python -m pip install --upgrade "qdk[azure]" ipykernel
创建新的 Jupyter 笔记本并连接到 Quantum 工作区
若要在 VS Code 中的 Jupyter 笔记本中连接到工作区,请执行以下步骤:
在 VS Code 中,打开 “视图 ”菜单,然后选择 “命令面板”。
输入 Create: New Jupyter Notebook。 将在新选项卡中打开空Jupyter Notebook文件。
在笔记本的第一个单元格中,运行以下代码。 可以在 Azure 门户中工作区的 Overview 窗格中找到资源 ID。
from qdk.azure import Workspace workspace = Workspace (resource_id="") # Add your resource ID
提交 QIR 格式的电路
量子中间表示形式(QIR)是一种中间表示形式,充当量子编程语言和目标量子计算平台之间的通用接口。 有关详细信息,请参阅量子中间表示。
若要提交 QIR 格式的线路,请执行以下步骤:
创建 QIR 线路。 例如,在新单元格中运行以下代码以创建简单的纠缠线路。
QIR_routine = """%Result = type opaque %Qubit = type opaque define void @ENTRYPOINT__main() #0 { call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 0 to %Qubit*)) call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*)) call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 2 to %Qubit*)) call void @__quantum__qis__cz__body(%Qubit* inttoptr (i64 2 to %Qubit*), %Qubit* inttoptr (i64 0 to %Qubit*)) call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 2 to %Qubit*)) call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 3 to %Qubit*)) call void @__quantum__qis__cz__body(%Qubit* inttoptr (i64 3 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*)) call void @__quantum__qis__h__body(%Qubit* inttoptr (i64 3 to %Qubit*)) call void @__quantum__qis__mz__body(%Qubit* inttoptr (i64 2 to %Qubit*), %Result* inttoptr (i64 0 to %Result*)) #1 call void @__quantum__qis__mz__body(%Qubit* inttoptr (i64 3 to %Qubit*), %Result* inttoptr (i64 1 to %Result*)) #1 call void @__quantum__rt__tuple_record_output(i64 2, i8* null) call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* null) call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 1 to %Result*), i8* null) ret void } declare void @__quantum__qis__ccx__body(%Qubit*, %Qubit*, %Qubit*) declare void @__quantum__qis__cx__body(%Qubit*, %Qubit*) declare void @__quantum__qis__cy__body(%Qubit*, %Qubit*) declare void @__quantum__qis__cz__body(%Qubit*, %Qubit*) declare void @__quantum__qis__rx__body(double, %Qubit*) declare void @__quantum__qis__rxx__body(double, %Qubit*, %Qubit*) declare void @__quantum__qis__ry__body(double, %Qubit*) declare void @__quantum__qis__ryy__body(double, %Qubit*, %Qubit*) declare void @__quantum__qis__rz__body(double, %Qubit*) declare void @__quantum__qis__rzz__body(double, %Qubit*, %Qubit*) declare void @__quantum__qis__h__body(%Qubit*) declare void @__quantum__qis__s__body(%Qubit*) declare void @__quantum__qis__s__adj(%Qubit*) declare void @__quantum__qis__t__body(%Qubit*) declare void @__quantum__qis__t__adj(%Qubit*) declare void @__quantum__qis__x__body(%Qubit*) declare void @__quantum__qis__y__body(%Qubit*) declare void @__quantum__qis__z__body(%Qubit*) declare void @__quantum__qis__swap__body(%Qubit*, %Qubit*) declare void @__quantum__qis__mz__body(%Qubit*, %Result* writeonly) #1 declare void @__quantum__rt__result_record_output(%Result*, i8*) declare void @__quantum__rt__array_record_output(i64, i8*) declare void @__quantum__rt__tuple_record_output(i64, i8*) attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="base_profile" "required_num_qubits"="4" "required_num_results"="2" } attributes #1 = { "irreversible" } ; module flags !llvm.module.flags = !{!0, !1, !2, !3} !0 = !{i32 1, !"qir_major_version", i32 1} !1 = !{i32 7, !"qir_minor_version", i32 0} !2 = !{i32 1, !"dynamic_qubit_management", i1 false} !3 = !{i32 1, !"dynamic_result_management", i1 false} """创建
submit_qir_job帮助程序函数,将 QIR 电路提交到target。 在此示例中,输入和输出数据格式qir.v1分别为和microsoft.quantum-results.v1。# Submit the job with proper input and output data formats def submit_qir_job(target, input, name, count=100): job = target.submit( input_data=input, input_data_format="qir.v1", output_data_format="microsoft.quantum-results.v1", name=name, input_params = { "entryPoint": "ENTRYPOINT__main", "arguments": [], "count": count } ) print(f"Queued job: {job.id}") job.wait_until_completed() print(f"Job completed with state: {job.details.status}") #if job.details.status == "Succeeded": result = job.get_results() return result将 QIR 线路提交到特定的 Azure Quantum target。 例如,若要将 QIR 线路提交到 IonQ 模拟器 target,请运行以下代码:
target = workspace.get_targets(name="ionq.simulator") result = submit_qir_job(target, QIR_routine, "QIR routine") result
提交格式符合提供程序要求的电路到Azure Quantum
每个Azure Quantum提供程序都有自己的格式来表示量子线路。 可以将线路以提供程序特定的格式提交到 Azure Quantum,而不是使用 QIR 语言(如 Q# 或 Qiskit)。
以 JSON 格式将线路提交到 IonQ
IonQ 使用 JSON 格式在其 targets 上运行线路。 有关详细信息,请参阅 IonQ targets 和 IonQ API 文档。
以下示例在 JSON 格式的三个量子比特之间创建叠加。
在新单元格中,创建 JSON 格式的量子线路。
circuit = { "qubits": 3, "circuit": [ { "gate": "h", "target": 0 }, { "gate": "cnot", "control": 0, "target": 1 }, { "gate": "cnot", "control": 0, "target": 2 }, ] }将线路提交到 IonQ target。 以下示例使用 IonQ 模拟器,该模拟器返回
Job对象。target = workspace.get_targets(name="ionq.simulator") job = target.submit(circuit)作业完成后,获取结果。
results = job.get_results() print(results)
以 Pulser SDK 格式将线路提交到 PASQAL
可以使用 Pulser SDK 创建脉冲序列并将其提交到 PASQAL targets。
安装 Pulser SDK
Pulser 是一个框架,可用于为中性原子量子设备创建、模拟和运行脉冲序列。 Pulser 由 PASQAL 设计为一种接口,以提交量子实验到其量子处理器。 有关详细信息,请参阅 Pulser 文档。
若要提交脉冲序列,请先安装 Pulser SDK 包:
try:
import pulser
import pulser_pasqal
except ImportError:
!pip -q install pulser pulser-pasqal --index-url https://pypi.org/simple
创建量子寄存器
定义寄存器和布局。 寄存器指定排列原子的位置,布局指定捕获和构造寄存器中原子的陷阱的位置。
有关布局的详细信息,请参阅 Pulser 文档。
创建一个devices对象以导入 PASQAL 量子计算机 targetFresnel。
from pulser_pasqal import PasqalCloud
devices = PasqalCloud().fetch_available_devices()
QPU = devices["FRESNEL"]
预先校准的布局
设备定义预先校准的布局列表。 可以从其中一种布局生成寄存器。
尽可能使用预先校准的布局,因为它们提高了 QPU 的性能。
以下示例在设备上使用第一个预先校准的布局:
# Use the first layout available on the device
layout = QPU.pre_calibrated_layouts[0]
# Select traps 1, 3 and 5 of the layout to define the register
traps = [1,v3,v5]
reg = layout.define_register(*traps)
# Draw the register to verify that it matches your expectations
reg.draw()
任意布局
当预先校准的布局不满足试验的要求时,请使用自定义布局。
对于任意给定的寄存器,中性原子量子处理单元(QPU)会根据布局放置陷阱,然后必须对这些陷阱进行校准。 由于每次校准都需要时间,因此最好尽可能重复使用现有的校准布局。
若要创建任意布局,请选择以下选项之一:
根据指定的寄存器自动生成布局。 对于大型寄存器,此过程可以生成亚最佳解决方案。 例如:
from pulser import Register qubits = { "q0": (0, 0), "q1": (0, 10), "q2": (8, 2), "q3": (1, 15), "q4": (-10, -3), "q5": (-8, 5), } reg = Register(qubits).with_automatic_layout(device)手动定义用于创建寄存器的布局。 例如,创建一个任意布局,其中包含 20 个陷阱,这些陷阱随机放置在 2D 平面中:
import numpy as np from pulser.register.register_layout import RegisterLayout # Generate random coordinates np.random.seed(301122) # Keeps results consistent between runs traps = np.random.randint(0, 30, size=(20, 2)) traps = traps - np.mean(traps, axis=0) # Create the layout layout = RegisterLayout(traps, slug="random_20") # Define your register with specific trap IDs trap_ids = [4, 8, 19, 0] reg = layout.define_register(*trap_ids, qubit_ids=["a", "b", "c", "d"]) reg.draw()
编写脉冲序列
中性原子由激光脉冲控制。 Pulser SDK 允许创建脉冲序列以应用于量子寄存器。
通过声明控制原子的通道来定义脉冲序列属性。 若要创建
Sequence,请提供一个Register实例以及将执行序列的设备。 例如,以下代码声明一个通道:ch0from pulser import Sequence seq = Sequence(reg, QPU) # Print the available channels for your sequence print(seq.available_channels) # Declare a channel. For example, `rydberg_global` seq.declare_channel("ch0", "rydberg_global")注意
可以使用设备
QPU = devices["FRESNEL"]或从 Pulser 导入虚拟设备,以提高灵活性。 使用VirtualDevice可以创建不受设备规范限制的序列,从而允许您在仿真器上运行。 有关详细信息,请参阅 Pulser 文档。向序列添加脉冲。 为此,请创建脉冲并将其添加到您所声明的通道。 例如,以下代码创建脉冲并将其添加到通道
ch0:from pulser import Pulse from pulser.waveforms import RampWaveform, BlackmanWaveform import numpy as np amp_wf = BlackmanWaveform(1000, np.pi) det_wf = RampWaveform(1000, -5, 5) pulse = Pulse(amp_wf, det_wf, 0) seq.add(pulse, "ch0") seq.draw()下图显示了脉冲序列:
将序列转换为 JSON 字符串
若要提交脉冲序列,请将 Pulser 对象转换为可用作输入数据的 JSON 字符串。
import json
# Convert the sequence to a JSON string
def prepare_input_data(seq):
input_data = {}
input_data["sequence_builder"] = json.loads(seq.to_abstract_repr())
to_send = json.dumps(input_data)
return to_send
将脉冲序列提交到 PASQAL target
设置正确的输入和输出数据格式。 例如,以下代码将输入数据格式
pasqal.pulser.v1设置为 ,输出数据格式设置为pasqal.pulser-results.v1。# Submit the job with proper input and output data formats def submit_job(target, seq, shots): job = target.submit( input_data=prepare_input_data(seq), # Take the JSON string previously defined as input data input_data_format="pasqal.pulser.v1", output_data_format="pasqal.pulser-results.v1", name="PASQAL sequence", shots=shots # Number of shots ) print(f"Queued job: {job.id}") return job注意
在 QPU 上运行作业所需的时间取决于当前的队列时间。 可以在工作区的提供程序窗格中查看target。
将程序提交到 PASQAL。 在将代码提交到实际量子硬件之前,最好在模拟器
pasqal.sim.emu-tntarget上测试代码。target = workspace.get_targets(name="pasqal.sim.emu-tn") # Change to "pasqal.qpu.fresnel" to use Fresnel QPU job = submit_job(target, seq, 10) job.wait_until_completed() print(f"Job completed with state: {job.details.status}") result = job.get_results() print(result){ "1000000": 3, "0010000": 1, "0010101": 1 }
将 OpenQASM 线路提交到 Quantinuum
以 OpenQASM 表示形式创建量子线路。 例如,以下代码创建一个传送电路:
circuit = """OPENQASM 2.0; include "qelib1.inc"; qreg q[3]; creg c0[3]; h q[0]; cx q[0], q[1]; cx q[1], q[2]; measure q[0] -> c0[0]; measure q[1] -> c0[1]; measure q[2] -> c0[2]; """或者,从 OpenQASM 文件加载线路:
with open("my_teleport.qasm", "r") as f: circuit = f.read()将电路提交到 Quantinuum target。 以下示例将作业提交到 Quantinuum 模拟器 targets之一。
target = workspace.get_targets(name="quantinuum.sim.h2-1sc") job = target.submit(circuit, shots=500)等待该作业完成并获取结果。
results = job.get_results() print(results)
注意
这些结果每次尝试都返回 000,这不是随机的。 这是因为 API 验证程序仅检查代码是否可以在 Quantinuum 硬件上运行,但为每个量子度量返回 0。 对于真正的随机数生成器,需要在量子硬件上运行线路。
将 Quil 量子电路提交到 Rigetti
若要将 Quil 作业提交到 Rigetti target,请使用 qdk.azurePython 模块。
加载所需的导入。
from azure.quantum import Workspace from azure.quantum.target.rigetti import Result, Rigetti, RigettiTarget, InputParams创建一个
target对象并传递要向其提交作业的 Rigetti target 的名称。 例如,以下代码选择QVMtarget。target = Rigetti(workspace=workspace, name=RigettiTarget.QVM)创建 Quil 程序。 为了确保您的程序被接受,您必须将读出设置为
"ro"。readout = "ro" bell_state_quil = f""" DECLARE {readout} BIT[2] H 0 CNOT 0 1 MEASURE 0 {readout}[0] MEASURE 1 {readout}[1] """ num_shots = 5 job = target.submit( input_data=bell_state_quil, name="bell state", shots=100, input_params=InputParams(skip_quilc=False) ) print(f"Job completed with state: {job.details.status}") result = Result(job) # This throws an exception if the job failed可以使用读数的名称给结果编制索引。 在以下代码中,
data_per_shot是一个长度为num_shots的列表,列表中的每个项都是另一个列表,其中包含来自该镜头的寄存器数据。data_per_shot = result[readout] ro_data_first_shot = data_per_shot[0]在这种情况下,由于寄存器的类型为 BIT,类型为整数,值为 0 或 1。
assert isinstance(ro_data_first_shot[0], int) assert ro_data_first_shot[0] == 1 or ro_data_first_shot[0] == 0输出所有数据。
print(f"Data from '{readout}' register:") for i, shot in enumerate(data_per_shot): print(f"Shot {i}: {shot}")
重要
不能在单个作业上提交多个电路。 解决方法是调用 backend.run 方法以异步方式提交每个电路,然后获取每个作业的结果。 例如:
jobs = []
for circuit in circuits:
jobs.append(backend.run(circuit, shots=N))
results = []
for job in jobs:
results.append(job.result())