你当前正在访问 Microsoft Azure Global Edition 技术文档网站。 如果需要访问由世纪互联运营的 Microsoft Azure 中国技术文档网站,请访问 https://docs.azure.cn。
如何将特定格式化线路提交到 Azure Quantum
了解如何使用 azure-quantum
Python 包将特定格式的线路提交到 Azure Quantum 服务。 本文介绍如何使用以下格式提交线路:
有关详细信息,请参阅量子线路。
先决条件
若要在 Azure 门户 的 Notebook 中运行线路,需要:
- 具有活动订阅的 Azure 帐户。 如果没有 Azure 帐户,请免费注册并注册 即用即付订阅。
- Azure Quantum 工作区。 有关详细信息,请参阅创建 Azure Quantum 工作区。
若要在 Visual Studio Code 中开发和运行线路,还需要:
Python安装了 Python Pip 的环境。
Azure Quantum
qsharp
azure-quantum
和ipykernel
包。python -m pip install --upgrade qsharp azure-quantum ipykernel
创建新的 Jupyter Notebook
可以在 VS Code 中创建笔记本,也可以直接在 Azure Quantum 门户中创建笔记本。
- 登录到 Azure 门户并选择在上一步骤中创建的工作区。
- 在左侧边栏选项卡中,选择“笔记本”。
- 单击“我的笔记本”,然后单击“新增”。
- 在“内核类型”中,选择“IPython”。
- 键入文件的名称,然后单击“ 创建文件”。
当你的新笔记本打开时,它会根据订阅和工作区信息自动为第一个单元格创建代码。
from azure.quantum import Workspace
workspace = Workspace (
resource_id = "", # Your resource_id
location = "" # Your workspace location (for example, "westus")
)
提交 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
target选择 QIR 线路并将其提交到 Azure Quantum。 例如,若要将 QIR 线路提交到 IonQ 模拟器 target:
target = workspace.get_targets(name="ionq.simulator") result = submit_qir_job(target, QIR_routine, "QIR routine") result
{'Histogram': ['(0, 0)', 0.5, '(1, 1)', 0.5]}
将具有提供程序特定格式的线路提交到 Azure Quantum
除了 QIR 语言(如 Q# 或 Qiskit),还可以将提供程序特定格式的量子线路提交到 Azure Quantum。 每个提供程序都有自己的表示量子线路的格式。
使用 JSON 格式将线路提交到 IonQ
使用 IonQ 支持的与语言无关的 JSON 格式创建量子线路,如 IonQ API 文档中所述。targets 例如,以下示例在三个量子比特之间创建叠加:
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)
..... {'duration': 8240356, 'histogram': {'0': 0.5, '7': 0.5}}
然后,可以使用 Matplotlib 对结果进行可视化。
import pylab as pl pl.rcParams["font.size"] = 16 hist = {format(n, "03b"): 0 for n in range(8)} hist.update({format(int(k), "03b"): v for k, v in results["histogram"].items()}) pl.bar(hist.keys(), hist.values()) pl.ylabel("Probabilities")
在 QPU 上运行作业之前,应估算运行的成本。
注意
有关最新定价详细信息,请参阅 IonQ 定价,或通过以下网页找到你的工作区并查看该工作区的“提供商”选项卡中的定价选项:aka.ms/aq/myworkspaces。
使用 Pulser SDK 将线路提交到 PASQAL
若要将线路提交到 PASQAL,可以使用 Pulser SDK 创建脉冲序列并将其提交到 PASQAL target。
安装 Pulser SDK
Pulser 是一个框架,用于为中性原子量子设备组合、模拟和执行脉冲序列。 它由 PASQAL 设计为传递,用于将量子实验提交到其量子处理器。 有关详细信息,请参阅 Pulser 文档。
若要提交脉冲序列,请先安装 Pulser SDK 包:
try:
import pulser
except ImportError:
!pip -q install pulser
创建量子寄存器
在继续操作之前,需要同时定义寄存器和布局。 寄存器指定将排列原子的位置,而布局指定捕获和构造寄存器中这些原子所需的陷阱的位置。
有关布局的详细信息,请参阅 Pulser 文档。
首先,创建一个“devices”对象来导入 PASQAL 量子计算机 targetFresnel。
from pulser_pasqal import PasqalCloud devices = PasqalCloud().fetch_available_devices() QPU = devices["FRESNEL"]
预先校准的布局
这是建议的选项,因为它将提高 QPU 的性能。
选项 1:使用预先校准的布局定义寄存器
检查 Fresnel 上可用的布局,并从此布局定义寄存器。 有关如何执行此操作的详细信息,请查看 pulser 文档。
示例:
# let's say we are interested in 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,3,5] reg = layout.define_register(*traps) # You can draw the resulting register to verify it matches your expectations reg.draw()
任意布局
如果预先校准的布局不满足试验的要求,则可以创建自定义布局。
对于任何给定的任意寄存器,中性原子 QPU 将根据布局放置陷阱,然后必须进行校准。 由于每个校准都需要时间,因此一般建议尽可能重复使用现有的校准布局
选项 2:从定义的寄存器自动派生布局
此选项允许基于指定寄存器自动生成布局。 但是,对于大型寄存器,此过程可能会产生亚最佳解决方案,因为用于创建布局的算法存在限制。
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)
选项 3:使用手动定义的布局定义寄存器
- 创建一个任意布局,其中 20 个陷阱随机定位在 2D 平面中
import numpy as np # Generating 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) # Creating the layout layout = RegisterLayout(traps, slug="random_20")
- 使用特定陷阱 ID 定义寄存器
trap_ids = [4, 8, 19, 0] reg = layout.define_register(*trap_ids, qubit_ids=["a", "b", "c", "d"]) reg.draw()
编写脉冲序列
中性原子由激光脉冲控制。 Pulser SDK 允许创建脉冲序列以应用于量子寄存器。
首先,通过声明将用于控制原子的通道来定义脉冲序列属性。 若要创建
Sequence
,需要提供实例Register
以及将执行序列的设备。 例如,以下代码声明一个通道:ch0
注意
可以使用设备
QPU = devices["FRESNEL"]
或从 Pulser 导入虚拟设备,以提高灵活性。 使用允许VirtualDevice
序列创建不受设备规范约束,使其适合在仿真器上执行。 有关详细信息,请参阅 Pulser 文档。from pulser import Sequence seq = Sequence(reg, QPU) # print the available channels for your sequence print(seq.available_channels) # Declare a channel. In this example we will be using `rydberg_global` seq.declare_channel("ch0", "rydberg_global")
向序列添加脉冲。 为此,请创建脉冲并将其添加到声明的通道。 例如,以下代码创建脉冲并将其添加到通道
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-tn
作为一种 target测试代码。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 表示形式创建量子线路。 例如,下面的示例创建 Teleportation 线路:
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]; """
或者,你可以从文件加载线路:
with open("my_teleport.qasm", "r") as f: circuit = f.read()
将线路提交到 Quantinuum target。 以下示例使用返回
Job
对象的 Quantinuum API 验证程序。target = workspace.get_targets(name="quantinuum.sim.h1-1sc") job = target.submit(circuit, shots=500)
等待该作业完成并获取结果。
results = job.get_results() print(results)
........ {'c0': ['000', '000', '000', '000', '000', '000', '000', ... ]}
然后,可以使用 Matplotlib 对结果进行可视化。
import pylab as pl pl.hist(results["c0"]) pl.ylabel("Counts") pl.xlabel("Bitstring")
查看直方图,你可能会注意到,随机数生成器每次都返回 0,因此算不上随机。 这是因为,虽然 API 验证程序可确保代码在 Quantinuum 硬件上成功运行,但每次量子测量时它也会返回 0。 对于真正的随机数生成器,需要在量子硬件上运行线路。
在 QPU 上运行作业之前,应估算运行的成本。
注意
有关最新的定价详细信息,请参阅 Azure Quantum 定价,或通过: aka.ms/aq/myworkspaces 在工作区的“提供程序”选项卡中查找工作区并查看定价选项。
使用 Quil 将线路提交到 Rigetti
提交 Quil 作业的最简单方法是使用 pyquil-for-azure-quantum 包,因为它允许你使用 pyQuil 库的工具和文档。 如果没有此包,pyQuil 可用于 构造 Quil 程序,但不能将其提交到 Azure Quantum。
你还可以手动构造 Quil 程序并直接使用 azure-quantum
包来提交它们。
首先,加载所需的导入。
from pyquil.gates import CNOT, MEASURE, H from pyquil.quil import Program from pyquil.quilbase import Declare from pyquil_for_azure_quantum import get_qpu, get_qvm
get_qvm
使用或get_qpu
函数获取与 QVM 或 QPU 的连接。qc = get_qvm() # For simulation # qc = get_qpu("Ankaa-9Q-3") for submitting to a QPU
创建 Quil 程序。 接受任何有效的 Quil 程序,但读取 必须 命名
ro
。program = Program( Declare("ro", "BIT", 2), H(0), CNOT(0, 1), MEASURE(0, ("ro", 0)), MEASURE(1, ("ro", 1)), ).wrap_in_numshots_loop(5) # Optionally pass to_native_gates=False to .compile() to skip the compilation stage result = qc.run(qc.compile(program)) data_per_shot = result.readout_data["ro"]
data_per_shot
下面是一个numpy
数组,因此可以使用numpy
方法。assert data_per_shot.shape == (5, 2) ro_data_first_shot = data_per_shot[0] assert ro_data_first_shot[0] == 1 or ro_data_first_shot[0] == 0
输出所有数据。
print("Data from 'ro' 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())