學習如何使用模組 qdk.azurePython 將特定格式的電路提交給 Azure Quantum 服務。 本文說明如何以下列格式提交線路:
如需詳細資訊,請參閱 量子電路。
必要條件
若要在 Visual Studio Code (VS Code) 中開發和執行線路,您需要:
具有有效訂用帳戶的 Azure 帳戶。 如果您沒有 Azure 帳戶,請免費註冊並註冊 隨用隨付訂用帳戶。
Azure Quantum 工作區。 如需詳細資訊,請參閱 建立 Azure Quantum 工作區。
Python已安裝 Python 和 Pip 的環境。
已安裝 Azure Quantum Development Kit 和 PythonJupyter 擴充功能的 VS Code。
qdkPython含有azure額外內容的程式庫,以及ipykernel套件。python -m pip install --upgrade "qdk[azure]" ipykernel
建立新的 Jupyter 筆記本
若要在 VS Code 中建立筆記本,請遵循下列步驟:
在 VS Code 中,開啟 [檢視] 功能表,然後選擇 [命令面板]。
輸入並選取 [建立:新增 Jupyter Notebook]。
若要連線到 Azure Quantum 服務,您的程式需要資源識別碼和 Azure Quantum 工作區的位置。
- 登入您的 Azure 帳戶、 https://portal.azure.com、
- 選取您的 Azure Quantum 工作區,然後流覽至 [ 概觀]。
- 複製欄位中的參數。
在筆記本的第一個數據格中,將值貼入下列
Workspace建構函式,以建立workspace連線到 Azure Quantum 工作區的物件。from qdk.azure import Workspace workspace = Workspace ( resource_id = "", # Add your resource_id location = "" # Add 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
除了 Q# 或 Qiskit 等 QIR 語言之外,您還可以將提供者特定格式的量子線路提交至 Azure Quantum。 每個提供者都有自己的格式來表示量子線路。
使用 JSON 格式將線路提交至 IonQ
使用 IonQ 所支援的語言無關 JSON 格式建立量子線路,如 IonQ targetsAPI 檔所述。 例如,下列範例會在三個量子位之間建立迭加:
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
import pulser_pasqal
except ImportError:
!pip -q install pulser pulser-pasqal --index-url https://pypi.org/simple
建立量子快取器
您必須先定義緩存器和版面配置,再繼續進行。 緩存器會指定將排列 Atom 的位置,而版面配置會指定擷取和結構緩存器內這些原子所需的陷阱位置。
如需版面配置的詳細資訊,請參閱 Pulser 檔。
首先,您會建立 'devices' 對象來匯入 PASQAL 量子計算機 target、 Fresnel。
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:自動從您定義的快取器衍生配置
這個選項允許根據指定的緩存器自動產生配置。 不過,對於大型緩存器,此程式可能會因為用來建立配置之演算法的限制而產生次佳的解決方案。
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)選項3:使用手動定義的版面配置定義您的快取器
建立任意配置,並隨機放置於 2D 平面中的 20 個陷阱
import numpy as np from pulser.register.register_layout import RegisterLayout # 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")- 使用特定陷阱識別碼定義您的緩存器
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。 下列範例會使用 Quantinuum API 驗證程式,其會
Job傳回 物件。target = workspace.get_targets(name="quantinuum.sim.h2-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-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_shotnumpy以下是陣列,因此您可以使用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())