通过


你当前正在访问 Microsoft Azure Global Edition 技术文档网站。 如果需要访问由世纪互联运营的 Microsoft Azure 中国技术文档网站,请访问 https://docs.azure.cn

如何将特定格式化线路提交到Azure Quantum

了解如何使用 qdk.azurePython 模块将特定格式的线路提交到 Azure Quantum 服务。 本文介绍如何以以下格式提交电路:

有关详细信息,请参阅量子线路

先决条件

若要在 Visual Studio Code(VS Code)中开发和运行线路,必须具备以下各项:

创建新的 Jupyter 笔记本并连接到 Quantum 工作区

若要在 VS Code 中的 Jupyter 笔记本中连接到工作区,请执行以下步骤:

  1. 在 VS Code 中,打开 “视图 ”菜单,然后选择 “命令面板”。

  2. 输入 Create: New Jupyter Notebook。 将在新选项卡中打开空Jupyter Notebook文件。

  3. 在笔记本的第一个单元格中,运行以下代码。 可以在 Azure 门户中工作区的 Overview 窗格中找到资源 ID。

    from qdk.azure import Workspace
    
    workspace = Workspace (resource_id="") # Add your resource ID 
    

提交 QIR 格式的电路

量子中间表示形式(QIR)是一种中间表示形式,充当量子编程语言和目标量子计算平台之间的通用接口。 有关详细信息,请参阅量子中间表示

若要提交 QIR 格式的线路,请执行以下步骤:

  1. 创建 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}
    """
    
  2. 创建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
    
  3. 将 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 targetsIonQ API 文档

以下示例在 JSON 格式的三个量子比特之间创建叠加。

  1. 在新单元格中,创建 JSON 格式的量子线路。

    circuit = {
        "qubits": 3,
        "circuit": [
            {
            "gate": "h",
            "target": 0
            },
            {
            "gate": "cnot",
            "control": 0,
            "target": 1
            },
            {
            "gate": "cnot",
            "control": 0,
            "target": 2
            },
        ]
    }
    
  2. 将线路提交到 IonQ target。 以下示例使用 IonQ 模拟器,该模拟器返回 Job 对象。

    target = workspace.get_targets(name="ionq.simulator")
    job = target.submit(circuit)
    
  3. 作业完成后,获取结果。

    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 允许创建脉冲序列以应用于量子寄存器。

  1. 通过声明控制原子的通道来定义脉冲序列属性。 若要创建 Sequence,请提供一个 Register 实例以及将执行序列的设备。 例如,以下代码声明一个通道: ch0

    from 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 文档

  2. 向序列添加脉冲。 为此,请创建脉冲并将其添加到您所声明的通道。 例如,以下代码创建脉冲并将其添加到通道 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

  1. 设置正确的输入和输出数据格式。 例如,以下代码将输入数据格式 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。

  2. 将程序提交到 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

  1. 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()
    
  2. 将电路提交到 Quantinuum target。 以下示例将作业提交到 Quantinuum 模拟器 targets之一。

    target = workspace.get_targets(name="quantinuum.sim.h2-1sc")
    job = target.submit(circuit, shots=500)
    
  3. 等待该作业完成并获取结果。

    results = job.get_results()
    print(results)
    

注意

这些结果每次尝试都返回 000,这不是随机的。 这是因为 API 验证程序仅检查代码是否可以在 Quantinuum 硬件上运行,但为每个量子度量返回 0。 对于真正的随机数生成器,需要在量子硬件上运行线路。

将 Quil 量子电路提交到 Rigetti

若要将 Quil 作业提交到 Rigetti target,请使用 qdk.azurePython 模块。

  1. 加载所需的导入。

    from azure.quantum import Workspace
    from azure.quantum.target.rigetti import Result, Rigetti, RigettiTarget, InputParams
    
  2. 创建一个 target 对象并传递要向其提交作业的 Rigetti target 的名称。 例如,以下代码选择QVMtarget。

    target = Rigetti(workspace=workspace, name=RigettiTarget.QVM)
    
  3. 创建 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
    
  4. 可以使用读数的名称给结果编制索引。 在以下代码中,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
    
  5. 输出所有数据。

    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())