特定のフォーマット済み回線を Azure Quantum に送信する方法
パッケージを使用 azure-quantum
Python して、特定の形式の回線を Azure Quantum サービスに送信する方法について説明します。 この記事では、回線を次の形式で送信する方法について説明します。
詳細については、量子回路に関するページを参照してください。
前提条件
Azure portal のノートブックで回線を実行するには、次のものが必要です。
- アクティブなサブスクリプションが含まれる Azure アカウント。 Azure アカウントをお持ちでない場合は、無料で登録し、従量課金制サブスクリプションにサインアップ してください。
- Azure Quantum ワークスペース。 詳細については、「Azure Quantum ワークスペースを作成する」を参照してください。
Visual Studio Code で回線を開発して実行するには、次も必要です。
Python Pip がPythonインストールされている環境。
Azure Quantum Development Kit、Jupyter 拡張機能がインストールされている VS CodePython。
Azure Quantum
qsharp
、azure-quantum
およびipykernel
パッケージ。python -m pip install --upgrade qsharp azure-quantum ipykernel
新しい Jupyter Notebook を作成する
ノートブックは VS Code で作成することも、Azure Quantum ポータルで直接作成することもできます。
- Azure portal にログインし、前の手順のワークスペースを選択します。
- 左側のブレードで、[ノートブック] を選択します。
- [マイ ノートブック] をクリックし、[新規追加] をクリックします。
- [カーネルの種類] で [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} """
QIR 回線を
submit_qir_job
送信するヘルパー関数を作成します 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 回線を target 選択して 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 API ドキュメントで説明されているように、IonQ でサポートされている言語に依存しない JSON 形式を使用して量子回路を作成します。targets たとえば、次のサンプルでは、3 つの量子ビットの間に重ね合わせが作成されます。
circuit = { "qubits": 3, "circuit": [ { "gate": "h", "target": 0 }, { "gate": "cnot", "control": 0, "target": 1 }, { "gate": "cnot", "control": 0, "target": 2 }, ] }
回線を IonQ targetに送信します。 次の例では、
Job
オブジェクトを返す IonQ シミュレーターを使用します。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 でジョブを実行する前に、実行するコストを見積もる必要があります。
Note
最新の価格の詳細については、IonQ の価格に関するページを参照するか、またはワークスペースを見つけ、そのワークスペースの [プロバイダー] タブで aka.ms/aq/myworkspaces を使用して価格オプションを表示してください。
Pulser SDK を使用して PASQAL に回線を送信する
PASQAL に回線を送信するには、Pulser SDK を使用してパルス シーケンスを作成し、PASQAL targetに送信します。
Pulser SDK をインストールする
Pulser は、中性原子量子デバイスのパルス シーケンスを作成、シミュレート、実行するためのフレームワークです。 これは、量子プロセッサに量子実験を送信するためのパススルーとして PASQAL によって設計されています。 詳細については、Pulser のドキュメントを参照してください。
pulse シーケンスを送信するには、まず Pulser SDK パッケージをインストールします。
try:
import pulser
except ImportError:
!pip -q install pulser
量子レジスタを作成する
最初に、PASQAL 量子コンピューター targetFresnel をインポートする 'devices' オブジェクトを作成します。 Fresnel QPU には、量子レジスタを作成するために使用できる定義済みのトラップ レイアウトが用意されています。 量子レジスタを構成するには、量子ビットの配列を配置します。
from pulser_pasqal import PasqalCloud devices = PasqalCloud().fetch_available_devices() QPU = devices["FRESNEL"] # List all available calibrated register layouts for calibrated_register_layout in QPU.calibrated_register_layouts.keys(): print(calibrated_register_layout)
次に、量子ビット レジスタのレイアウトを定義します。 この例では、トラップ レイアウトとして a
TriangularLatticeLayout(61, 5.0µm)
を使用します。layout = QPU.calibrated_register_layouts[ "TriangularLatticeLayout(61, 5.0µm)" ] layout.draw()
次の図は、選択したレイアウトの表示を示しています。
次に、レイアウトからトラップのセットを選択する量子ビット レジスタを定義します。 この例では、レイアウトに 60 個のトラップがあり、ID を使用して 7 個のトラップを選択し、7 量子ビットの量子レジスタを定義します。
reg = layout.define_register(*[30, 21, 26, 35, 39, 34, 25]) reg.draw()
次の図は、量子ビット レジスタの最終的な表示を示しています。
パルス シーケンスを書き込む
中性原子はレーザーパルスで制御されます。 Pulser SDK を使用すると、量子レジスタに適用するパルス シーケンスを作成できます。
まず、原子の制御に使用するチャネルを宣言することで、パルス シーケンス属性を定義します。 を作成
Sequence
するには、シーケンスがRegister
実行されるデバイスと共にインスタンスを指定する必要があります。 たとえば、次のコードは 1 つのチャネルを宣言しますch0
。Note
デバイスを
QPU = devices["FRESNEL"]
使用するか、Pulser から仮想デバイスをインポートして柔軟性を高めることができます。 a を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
Note
QPU でジョブを実行するために必要な時間は、現在のキュー時刻によって異なります。 ワークスペースの [プロバイダー] ブレードを選択すると、a 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 }
高度なエミュレーター機能を調べる
PASQAL のエミュレーター には、Fresnel QPU ではまだサポートされていない高度な機能が用意されています。 事前に調整されたレイアウトの制限なしに、カスタムの方法でレジスタを整理できます。 たとえば、次のコードでは、量子ビットの 4 x 4 平方格子が作成されます。
import numpy as np
from pulser import Register, Sequence
L = 4
square = np.array([[i, j] for i in range(L) for j in range(L)], dtype=float)
square -= np.mean(square, axis=0)
square *= 5
qubits = dict(enumerate(square))
custom_reg = Register(qubits)
custom_reg.draw()
seq = Sequence(custom_reg, QPU)
カスタム レジスタを定義したら、前のセクションで説明したのとまったく同じ手順に従って、エミュレーターで指定したシーケンスを送信できます。
Note
カスタム レジスタ機能は近日中に FRESNEL で利用できるようになります。
OpenQASM を使用して Quantinuum に回線を送信する
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]; """
必要に応じて、回路をファイルから読み込むことができます。
with open("my_teleport.qasm", "r") as f: circuit = f.read()
回路を Quantinuum targetに送信します。 次の例では、
Job
オブジェクトを返す Quantinuum API 検証コントロールを使用します。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 でジョブを実行する前に、実行するコストを見積もる必要があります。
Note
最新の価格の詳細については、「Azure Quantum の価格」を参照するか、ワークスペースを見つけて、ワークスペースの [プロバイダー] タブで価格オプションを表示します。aka.ms/aq/myworkspaces。
Quil を使用してリゲッティに回線を送信する
Quil ジョブを送信する最も簡単な方法は、pyQuil ライブラリのツールとドキュメントを使用できるため、pyquil-for-azure-quantum パッケージを使用することです。 このパッケージがないと、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 プログラムを作成します。 任意の有効なキルプログラムは受け入れられますが、読み取りは名前を付
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}")
重要
1 つのジョブで複数の回路を送信することは現在サポートされていません。 回避策として、backend.run
メソッドを呼び出して各回路を非同期に送信し、その後、各ジョブの結果を取得できます。 次に例を示します。
jobs = []
for circuit in circuits:
jobs.append(backend.run(circuit, shots=N))
results = []
for job in jobs:
results.append(job.result())
関連するコンテンツ
- Qiskit を使用して回線を Azure Quantum に送信します。
- Cirq を使用して回線を Azure Quantum に送信します。