學習如何使用 qdk.azurePython 模組,將特定格式的電路提交給 Azure Quantum 服務。 本文說明如何以下列格式提交線路:
如需詳細資訊,請參閱 量子電路。
必要條件
要在 Visual Studio Code(VS Code)中開發並執行你的電路,你必須具備以下條件:
一個有有效訂閱的 Azure 帳號。 如果你沒有Azure帳號,請免費註冊並訂閱隨用付費訂閱。
Azure Quantum 工作區。 欲了解更多資訊,請參閱 Create a Azure Quantum workspace。
VS Code 安裝了 Microsoft Quantum Development Kit (QDK)、Python 以及 Jupyter 擴充功能。
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
脈衝器 是一個框架,讓你能為中性原子量子裝置創建、模擬並執行脈衝序列。 脈衝器由PASQAL設計,作為將量子實驗提交給其量子處理器的直通式。 欲了解更多資訊,請參閱 脈衝器文件。
若要提交脈衝序列,請先安裝 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量子電腦 target,Fresnel。
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個陷阱,這些陷阱隨機放置在二維平面上:
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一個實例以及執行序列的裝置。 例如,下列程式代碼會宣告一個通道: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 檔。將脈衝新增至您的序列。 要做到這一點,請建立脈衝並將其加入到您宣告的通道中。 例如,以下程式碼會產生一個脈衝並將其加入通道
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欄目中查看 A 的平均排隊時間。
將程式提交至 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 }
向 Quantinuum 提交 OpenQASM 電路
在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。 針對真正的隨機數產生器,您必須在量子硬體上執行線路。
向Rigetti提交Quil電路
要將 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())