特定のフォーマット済み回線を Azure Quantum に送信する方法
パッケージを使用 azure-quantum
Python して、特定の形式の回線を Azure Quantum サービスに送信する方法について説明します。 この記事では、回線を次の形式で送信する方法について説明します。
詳細については、量子回路に関するページを参照してください。
Note
Microsoft Quantum Development Kit (クラシック QDK) は、2024 年 6 月 30 日以降サポートされなくなります。 既存の QDK 開発者の場合は、量子ソリューションの開発を続けるために、新しい Azure Quantum Development Kit (Modern QDK) に移行することをお勧めします。 詳細については、「Q# コードを Modern QDK に移行する」を参照してください。
前提条件
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) は、量子プログラミング言語/フレームワークと targeted 量子計算プラットフォーム間の共通インターフェイスとして機能する中間表現です。 詳細については、「量子中間表現」を参照してください。
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 でジョブを実行する前に、それを実行するコストを見積もることができます。 QPU でジョブを実行するコストを見積もるには、
estimate_cost
メソッドを使用できます。target = workspace.get_targets(name="ionq.qpu") cost = target.estimate_cost(circuit, shots=500) print(f"Estimated cost: {cost.estimated_total}")
これにより、見積もりコストが米国ドルで出力されます。
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
!pip -q install pulser-core
量子レジスタを作成する
まず、必要なインポートを読み込みます。
import numpy as np import pulser from pprint import pprint from pulser import Pulse, Sequence, Register
PASQALのQPUは、格子内の明確に定義された位置に閉じ込められた中性原子で構成されています。 量子レジスタを定義するには、格子上に量子ビットの配列を作成します。 たとえば、次のコードでは、量子ビットの 4 x 4 平方格子が作成されます。
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)) reg = Register(qubits) reg.draw()
パルス シーケンスを書き込む
中性原子はレーザーパルスで制御されます。 Pulser SDK を使用すると、量子レジスタに適用するパルス シーケンスを作成できます。
まず、パルス シーケンスを設定し、原子の制御に使用するチャネルを宣言する必要があります。 たとえば、次のコードは 2 つのチャネル
ch0
を宣言しますch1
。from pulser.devices import Chadoq2 seq = Sequence(reg, Chadoq2) seq.declare_channel("ch0", "raman_local") print("Available channels after declaring 'ch0':") pprint(seq.available_channels) seq.declare_channel("ch1", "rydberg_local", initial_target=4) print("\nAvailable channels after declaring 'ch1':") pprint(seq.available_channels)
次の考慮事項があります。
- A
Sequence
in Pulser は、量子レジスタで実行される一連の操作です。 - このコードは、デバイス上で実行される一連の操作を
AnalogDevice
設定します。AnalogDevice
は、Fresnel1 と同等の量子コンピューターを表す Pulser の定義済みデバイスです。
- A
パルス シーケンスを作成します。 これを行うには、宣言したチャネルにパルスを作成して追加します。 たとえば、次のコードでは、単純なパルスを作成してチャネル
ch0
に追加し、複雑なパルスを作成してチャネルに追加しますch1
。seq.target(1, "ch0") # Target qubit 1 with channel "ch0" simple_pulse = Pulse.ConstantPulse(200, 2, -10, 0) seq.add(simple_pulse, "ch0") # Add the pulse to channel "ch0" seq.delay(100, "ch1") from pulser.waveforms import RampWaveform, BlackmanWaveform duration = 1000 # Create a Blackman waveform with a duration of 1000 ns and an area of pi/2 rad amp_wf = BlackmanWaveform(duration, np.pi / 2) # Create a ramp waveform with a duration of 1000 ns and a linear sweep from -20 to 20 rad/µs detuning_wf = RampWaveform(duration, -20, 20) # Create a pulse with the amplitude waveform amp_wf, the detuning waveform detuning_wf, and a phase of 0 rad. complex_pulse = Pulse(amp_wf, detuning_wf, phase=0) complex_pulse.draw() seq.add(complex_pulse, "ch1") # Add the pulse to channel "ch1"
この画像は、単純なパルスと複雑なパルスを示しています。
シーケンスを 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)
#print(json.dumps(input_data, indent=4, sort_keys=True))
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): 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=100 # Number of shots ) print(f"Queued job: {job.id}") job.wait_until_completed() print(f"Job completed with state: {job.details.status}") result = job.get_results() return result
Note
QPU で回路を実行するために必要な時間は、現在のキュー時間によって異なります。 ワークスペースの [プロバイダー] ブレードを選択すると、a target の平均キュー時間を表示できます。
プログラムを PASQAL に送信します。 たとえば、PASQAL Emu-TN targetにプログラムを送信できます。
target = workspace.get_targets(name="pasqal.sim.emu-tn") submit_job(target, seq)
{'0000000000000000': 59, '0000100000000000': 39, '0100000000000000': 1, '0100100000000000': 1}
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 でジョブを実行する前に、それを実行するコストを見積もることができます。 QPU でジョブを実行するコストを見積もるには、
estimate_cost
メソッドを使用できます。target = workspace.get_targets(name="quantinuum.qpu.h1-1") cost = target.estimate_cost(circuit, shots=500) print(f"Estimated cost: {cost.estimated_total}")
これにより、見積もりコストが H-System Quantum クレジット (HQC) で出力されます。
Note
Quantinuum targetに対してコスト見積もりを実行するには、最初に [qiskit] パラメーターを使用して azure-quantumPython パッケージを再読み込みし、Qiskit の最新バージョンがあることを確認する必要があります。 詳細については、azure-quantum Python パッケージの更新に関するページを参照してください。
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-2") 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 に送信します。
フィードバック
https://aka.ms/ContentUserFeedback」を参照してください。
以下は間もなく提供いたします。2024 年を通じて、コンテンツのフィードバック メカニズムとして GitHub の issue を段階的に廃止し、新しいフィードバック システムに置き換えます。 詳細については、「フィードバックの送信と表示