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

如何使用 Qiskit 将线路提交到 Azure Quantum

了解如何使用 azure-quantumPython 包提交 Qiskit 量子线路。 可以使用 Visual Studio Code 中的 Azure Quantum 开发工具包(QDK)和 Jupyter Notebook(VS Code)将 Qiskit 线路提交到 Azure Quantum。 还可以使用本地稀疏模拟器测试线路。

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

先决条件

有关安装详细信息,请参阅设置 QDK 扩展

  • Azure 订阅中的 Azure Quantum 工作区。 若要创建工作区,请参阅创建 Azure Quantum 工作区

  • Python安装了 Python Pip 的环境。

  • 安装了 Azure Quantum 开发工具包Python 扩展的 VS Code。

  • qdkPython,具有 qiskit 额外包的 azure-quantumPython 包,以及 ipykernel 包。

    注意

    如果未检测到 Jupyter Python 内核 ipykernel ,VS Code 将提示你安装它。

    python -m pip install --upgrade azure-quantum[qiskit] qdk ipykernel 
    

    重要

    确保你拥有最新版本的 Qiskit。 有关详细信息,请参阅 更新 azure-quantum Python 包

创建新的 Jupyter Notebook

  1. 在 VS Code 中,打开 “视图 ”菜单,然后选择 “命令面板”。
  2. 输入并选择“ 创建:新建 Jupyter Notebook”。
  3. VS Code 检测并显示笔记本选择的 Python 版本以及虚拟 Python 环境。 如果有多个 Python 环境,则可能需要使用右上角的内核选取器选择内核。 如果未检测到任何环境,请参阅 VS Code 中的 Jupyter Notebook 以获取设置信息。

加载所需的导入项

在笔记本的第一个单元中,运行以下代码以加载所需的导入:

import azure.quantum
from azure.quantum import Workspace 
from qiskit import QuantumCircuit
from qiskit.visualization import plot_histogram
from azure.quantum.qiskit import AzureQuantumProvider

连接到 Azure Quantum 服务

若要连接到 Azure Quantum 服务,你需要使用 Azure Quantum 工作区的资源 ID 和位置。

  1. 登录到 Azure 帐户, https://portal.azure.com

  2. 选择 Azure Quantum 工作区,然后导航到 “概述”。

  3. 复制字段中的参数。

    Visual Studio Code 的屏幕截图,其中显示了如何展开 Quantum 工作区的概述窗格。

在笔记本中添加一个新单元格,并使用帐户信息创建 WorkspaceAzureQuantumProvider 对象以连接到 Azure Quantum 工作区。

workspace = Workspace(  
    resource_id = "", # Add the resourceID of your workspace
    location = "" # Add the location of your workspace (for example "westus")
    )

provider = AzureQuantumProvider(workspace)

列出所有后端

现在可以打印工作区上可用的所有量子计算后端:

print("This workspace's targets:")
for backend in provider.backends():
    print("- " + backend.name())
This workspace's targets:
- ionq.qpu
- ionq.qpu.aria-1
- ionq.simulator
- microsoft.estimator
- quantinuum.hqs-lt-s1
- quantinuum.hqs-lt-s1-apival
- quantinuum.hqs-lt-s2
- quantinuum.hqs-lt-s2-apival
- quantinuum.hqs-lt-s1-sim
- quantinuum.hqs-lt-s2-sim
- quantinuum.qpu.h2-1
- quantinuum.sim.h2-1sc
- quantinuum.sim.h2-1e
- rigetti.sim.qvm
- rigetti.qpu.ankaa-3

运行简单的线路

首先,创建要运行的简单 Qiskit 线路。

# Create a Quantum Circuit acting on the q register
circuit = QuantumCircuit(3, 3)
circuit.name = "Qiskit Sample - 3-qubit GHZ circuit"
circuit.h(0)
circuit.cx(0, 1)
circuit.cx(1, 2)
circuit.measure([0,1,2], [0, 1, 2])

# Print out the circuit
circuit.draw()
     ┌───┐          ┌─┐      
q_0: ┤ H ├──■───────┤M├──────
     └───┘┌─┴─┐     └╥┘┌─┐   
q_1: ─────┤ X ├──■───╫─┤M├───
          └───┘┌─┴─┐ ║ └╥┘┌─┐
q_2: ──────────┤ X ├─╫──╫─┤M├
               └───┘ ║  ║ └╥┘
c: 3/════════════════╩══╩══╩═
                     0  1  2 

选择一个 target 运行程序

在 IonQ 模拟器上运行

在实际硬件上运行之前,让我们在模拟器中测试线路。 用于 get_backend 创建连接到 Backend IonQ 模拟器后端的对象:

simulator_backend = provider.get_backend("ionq.simulator")

IonQ 后端支持定义的门集中的门,这些门经过编译,可在硬件上以最佳方式运行。 如果线路包含不在此列表中的入口,则需要使用 gateset Qiskit 提供的函数转译到支持的transpile函数中:

from qiskit import transpile
circuit = transpile(circuit, simulator_backend)

Transpile 函数返回一个新的线路对象,其中入口分解为指定后端支持的入口。

现在,可以通过 Azure Quantum 服务运行程序并获取结果。 以下单元格提交一个作业,该作业会运行 100 次线路:

job = simulator_backend.run(circuit, shots=8)
job_id = job.id()
print("Job id", job_id)
Job id 00000000-0000-0000-0000-000000000000

若要等待作业完成并返回结果,请运行:

result = job.result()
print(result)
Result(backend_name='ionq.simulator', backend_version='1', qobj_id='Qiskit Sample - 3-qubit GHZ circuit', job_id='00000000-0000-0000-0000-000000000000', success=True, results=[ExperimentResult(shots=8, success=True, meas_level=2, data=ExperimentResultData(counts={'000': 4, '111': 4}, memory=['000', '000', '000', '000', '111', '111', '111', '111'], probabilities={'000': 0.5, '111': 0.5}), header=QobjExperimentHeader(name='Qiskit Sample - 3-qubit GHZ circuit', num_qubits=3, metadata={}), status=JobStatus.DONE, name='Qiskit Sample - 3-qubit GHZ circuit')], date=None, status=None, header=None, error_data=None)

由于结果是 Qiskit 包的原生对象,你可以使用 Qiskit 的 result.get_countsplot_histogram 来可视化结果。 若要确保表示所有可能的位字符串标签,请将它们添加到 counts

counts = {format(n, "03b"): 0 for n in range(8)}
counts.update(result.get_counts(circuit))
print(counts)
plot_histogram(counts)
{'000': 4, '001': 0, '010': 0, '011': 0, '100': 0, '101': 0, '110': 0, '111': 4}

IonQ 模拟器上的 Qiskit 线路结果

还可以使用该 get_memory() 函数显示作业中的单个镜头数据

result.get_memory(circuit)
['000', '000', '000', '000', '111', '111', '111', '111']

注意

在 IonQ targets上,如果你提交一份具有奇数测量次数的工作,那么测量次数将向下舍入到最接近的偶数。 例如,如果指定 9 次拍摄,则结果将显示 8 次拍摄的数据。

估算作业成本

在 QPU 上运行作业之前,应估算运行作业的成本。

有关最新的定价详细信息,请参阅 IonQ 定价,或通过:aka.ms/aq/myworkspaces 在工作区的“提供商”选项卡中查找工作区并查看定价选项。

在 IonQ QPU 上运行

若要连接到实际硬件(量子处理器单元(QPU),只需提供方法的名称target"ionq.qpu.aria-1"get_backend

qpu_backend = provider.get_backend("ionq.qpu.aria-1")

提交线路以在 Azure Quantum 上运行,获取结果,然后运行 plot_histogram 以绘制结果。

注意

在 QPU 上运行线路所需的时间取决于当前队列时间。

# Submit the circuit to run on Azure Quantum
job = qpu_backend.run(circuit, shots=100)
job_id = job.id()
print("Job id", job_id)

# Get the job results (this method waits for the Job to complete):
result = job.result()
print(result)
counts = {format(n, "03b"): 0 for n in range(8)}
counts.update(result.get_counts(circuit))
print(counts)
plot_histogram(counts)
Job id 00000000-0000-0000-0000-000000000000
Job Status: job has successfully run
Result(backend_name='ionq.qpu.aria-1', backend_version='1', qobj_id='Qiskit Sample - 3-qubit GHZ circuit', job_id='00000000-0000-0000-0000-000000000000', success=True, results=[ExperimentResult(shots=1024, success=True, meas_level=2, data=ExperimentResultData(counts={'0': 505, '1': 6, '2': 1, '3': 1, '4': 1, '5': 10, '6': 11, '7': 488}, probabilities={'0': 0.4932, '1': 0.0059, '2': 0.001, '3': 0.001, '4': 0.001, '5': 0.0098, '6': 0.0117, '7': 0.4766}), header=QobjExperimentHeader(name='Qiskit Sample - 3-qubit GHZ circuit', num_qubits='3', qiskit='True'))])
{'000': 505, '001': 6, '010': 1, '011': 1, '100': 1, '101': 10, '110': 11, '111': 488}

IonQ QPU 上的 Qiskit 线路结果

重要

当前不支持在单个作业中提交多个线路。 作为一种变通方法,可以调用 backend.run 方法来异步提交每个线路,然后提取每个作业的结果。 例如:

jobs = []
for circuit in circuits:
    jobs.append(backend.run(circuit, shots=N))

results = []
for job in jobs:
    results.append(job.result())

先决条件

有关安装详细信息,请参阅设置 QDK 扩展

  • Python安装了 Python Pip 的环境。

  • 安装了 Azure Quantum 开发工具包VS Code。

  • qdk Python包含可选qiskitjupyter附加项的包。

    python pip install "qdk[qiskit,jupyter]"
    

    重要

    确保你拥有最新版本的 Qiskit。 有关详细信息,请参阅 更新 azure-quantum Python 包

运行基本线路

在 VS Code 中,打开一个新 Python 文件,通过包中的内置稀疏模拟器 qsharp 定义和运行基本线路。

# load the required imports 
from qdk import qsharp
from qsharp.interop.qiskit import QSharpBackend
from qiskit.circuit.random import random_circuit

# define and display the circuit
circuit = random_circuit(2, 2, measure=True)
print(circuit)

# run the circuit using the built-in sparse simulator
backend = QSharpBackend()
job = backend.run(circuit)
counts = job.result().get_counts()

print(counts)

若要运行程序,请选择右上角的“运行”图标,然后选择“ 运行 Python 文件”。 输出显示在新的终端窗口中。

                  ┌─────────────────────────┐┌─┐
q_0: ─■───────────┤0                        ├┤M├───
      │P(0.79983) │  (XX-YY)(1.9337,1.7385) │└╥┘┌─┐
q_1: ─■───────────┤1                        ├─╫─┤M├
                  └─────────────────────────┘ ║ └╥┘
c: 2/═════════════════════════════════════════╩══╩═
                                              0  1
{'11': 680, '00': 344}

为电路生成 QIR

在同一线路中,可以生成用于在量子硬件上运行的 QIR。

注意

若要生成 QIR,必须对所有寄存器进行测量。 如果有任何未使用的寄存器,则会引发错误。 此外,尝试使用 Unrestrictedtarget 配置文件生成 QIR 时会收到错误。 Unrestricted配置文件仅对模拟有效。 必须使用TargetProfile.BaseTargetProfile.Adaptive_RITargetProfile.Adaptive_RIF。 可以在target_profile的调用中覆盖backend.qir(...)来切换配置文件。

  1. 导入 QSharpErrorTargetProfile

    from qdk.qsharp import QSharpError, TargetProfile
    
  2. 若要生成 QIR,请修改输出:

        print(backend.qir(circuit, target_profile=TargetProfile.Adaptive_RI))
    

代码现在应如下所示:

# load the required imports 
from qdk import qsharp
from qsharp import QSharpError, TargetProfile
from qsharp.interop.qiskit import QSharpBackend
from qiskit.circuit.random import random_circuit

# define and display the circuit
circuit = random_circuit(2, 2, measure=True)
print(circuit)

# generate QIR for the circuit
print(backend.qir(circuit, target_profile=TargetProfile.Adaptive_RI))

代码的输出应如下所示:

     ┌────────────┐             ┌─┐   
q_0: ┤ Rx(2.7195) ├─■───────────┤M├───
     └──┬─────┬───┘ │U1(5.5924) └╥┘┌─┐
q_1: ───┤ Tdg ├─────■────────────╫─┤M├
        └─────┘                  ║ └╥┘
c: 2/════════════════════════════╩══╩═
                                 0  1
%Result = type opaque
%Qubit = type opaque

define void @ENTRYPOINT__main() #0 {
block_0:
  call void @__quantum__qis__rx__body(double 2.7194945105768586, %Qubit* inttoptr (i64 0 to %Qubit*))
  call void @__quantum__qis__rz__body(double 2.796204066686262, %Qubit* inttoptr (i64 0 to %Qubit*))
  call void @__quantum__qis__t__adj(%Qubit* inttoptr (i64 1 to %Qubit*))
  call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*))
  call void @__quantum__qis__rz__body(double -2.796204066686262, %Qubit* inttoptr (i64 1 to %Qubit*))
  call void @__quantum__qis__cx__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Qubit* inttoptr (i64 1 to %Qubit*))
  call void @__quantum__qis__rz__body(double 2.796204066686262, %Qubit* inttoptr (i64 1 to %Qubit*))
  call void @__quantum__qis__m__body(%Qubit* inttoptr (i64 0 to %Qubit*), %Result* inttoptr (i64 0 to %Result*))
  call void @__quantum__qis__m__body(%Qubit* inttoptr (i64 1 to %Qubit*), %Result* inttoptr (i64 1 to %Result*))
  call void @__quantum__rt__array_record_output(i64 2, i8* null)
  call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 1 to %Result*), i8* null)
  call void @__quantum__rt__result_record_output(%Result* inttoptr (i64 0 to %Result*), i8* null)
  ret void
}

declare void @__quantum__qis__rx__body(double, %Qubit*)

declare void @__quantum__qis__rz__body(double, %Qubit*)

declare void @__quantum__qis__t__adj(%Qubit*)

declare void @__quantum__qis__cx__body(%Qubit*, %Qubit*)

declare void @__quantum__qis__m__body(%Qubit*, %Result*) #1

declare void @__quantum__rt__array_record_output(i64, i8*)

declare void @__quantum__rt__result_record_output(%Result*, i8*)

attributes #0 = { "entry_point" "output_labeling_schema" "qir_profiles"="adaptive_profile" "required_num_qubits"="2" "required_num_results"="2" }
attributes #1 = { "irreversible" }

; module flags

!llvm.module.flags = !{!0, !1, !2, !3, !4, !5, !6, !7, !8, !9, !10}

!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}
!4 = !{i32 1, !"classical_ints", i1 true}
!5 = !{i32 1, !"qubit_resetting", i1 true}
!6 = !{i32 1, !"classical_floats", i1 false}
!7 = !{i32 1, !"backwards_branching", i1 false}
!8 = !{i32 1, !"classical_fixed_points", i1 false}
!9 = !{i32 1, !"user_functions", i1 false}
!10 = !{i32 1, !"multiple_target_branching", i1 false}

并非所有程序都可以在所有硬件上运行。 在这里,如果尝试处理target配置文件Base,那么会获得详细的错误信息,说明程序哪些部分不受支持。

try:
    backend.qir(qc, target_profile=TargetProfile.Base)
except QSharpError as e:
    print(e)

后续步骤