你当前正在访问 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
创建量子寄存器
首先,创建一个“devices”对象来导入 PASQAL 量子计算机 targetFresnel。 Fresnel QPU 提供预定义的陷阱布局,可用于创建量子寄存器。 若要配置量子寄存器,请排列量子比特数组。
from pulser_pasqal import PasqalCloud devices = PasqalCloud().fetch_available_devices() QPU = devices["FRESNEL"] # List all available calibrated register layouts for calibrated_register_layout in QPU.calibrated_register_layouts.keys(): print(calibrated_register_layout)
接下来,定义量子比特寄存器的布局。 在此示例中,你将使用 a
TriangularLatticeLayout(61, 5.0µm)
作为陷阱布局。layout = QPU.calibrated_register_layouts[ "TriangularLatticeLayout(61, 5.0µm)" ] layout.draw()
下图显示了所选布局的显示。
然后,定义量子比特寄存器,从布局中选择一组陷阱。 在此示例中,布局有 60 个陷阱,使用其 ID 选择 7 个陷阱来定义 7 个量子位的量子寄存器。
reg = layout.define_register(*[30, 21, 26, 35, 39, 34, 25]) 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 }
探索高级模拟器功能
PASQAL 的仿真器 提供弗雷内尔 QPU 尚不支持的高级功能。 你可以以自定义方式组织注册,而无需预先校准的布局中的任何限制。 例如,以下代码创建量子位的 4x4 平方格子:
import numpy as np
from pulser import Register, Sequence
L = 4
square = np.array([[i, j] for i in range(L) for j in range(L)], dtype=float)
square -= np.mean(square, axis=0)
square *= 5
qubits = dict(enumerate(square))
custom_reg = Register(qubits)
custom_reg.draw()
seq = Sequence(custom_reg, QPU)
定义自定义寄存器后,可以按照上一部分中概述的完全相同的步骤在模拟器上发送指定的序列。
注意
即将在 FRESNEL 上提供自定义注册功能。
使用 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())