如何將特定格式化線路提交至 Azure Quantum
瞭解如何使用 azure-quantum
Python 套件將特定格式的線路提交至 Azure Quantum 服務。 本文說明如何以下列格式提交線路:
如需詳細資訊,請參閱 量子電路。
必要條件
若要在 Azure 入口網站 的 Notebook 中執行線路,您需要:
- 具有有效訂用帳戶的 Azure 帳戶。 如果您沒有 Azure 帳戶,請免費註冊並註冊 隨用隨付訂用帳戶。
- Azure Quantum 工作區。 如需詳細資訊,請參閱 建立 Azure Quantum 工作區。
若要在 Visual Studio Code 中開發和執行線路,您也需要:
Python已安裝 Python 和 Pip 的環境。
已安裝 Azure Quantum Development Kit 和 PythonJupyter 擴充功能的 VS Code。
Azure Quantum
qsharp
、azure-quantum
和ipykernel
套件。python -m pip install --upgrade qsharp azure-quantum ipykernel
建立新的 Jupyter Notebook
您可以在 VS Code 中建立筆記本,或直接在 Azure Quantum 入口網站中建立筆記本。
- 登入 Azure 入口網站,然後從上一個步驟中選取工作區。
- 在左側刀鋒視窗中,選取 [筆記本]。
- 按一下 [我的筆記本] ,然後按一下 [新增]。
- 在 [核心類型]中,選取 [IPython]。
- 輸入檔案的名稱,然後按兩下 [ 建立檔案]。
當您的新 Notebook 開啟時,會根據您的訂用帳戶和工作區資訊自動建立第一個儲存格的程式碼。
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
除了 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
except ImportError:
!pip -q install pulser
建立量子快取器
您必須先定義緩存器和版面配置,再繼續進行。 緩存器會指定將排列 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:自動從您定義的快取器衍生配置
這個選項允許根據指定的緩存器自動產生配置。 不過,對於大型緩存器,此程式可能會因為用來建立配置之演算法的限制而產生次佳的解決方案。
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 # 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.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_qpu
函get_qvm
式來取得 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())