Japanese
言語
English
Bengali
Japanese
Spanish

注釈

このページは docs/tutorials/12_qaoa_runtime.ipynb から生成されました。

QAOA ランタイム

Qiskitランタイムは、プログラム全体をバックエンド側で実行できるようにする実行モデルです。ここでは、QiskitランタイムでQAOAアルゴリズムを実行する方法について説明します。この最初のバージョンのQAOAランタイムが利用できるようにする機能のいくつかについて説明します。

[1]:
from qiskit import IBMQ

IBMQ.load_account()

provider = IBMQ.get_provider(hub="ibm-q", group="open", project="main")

事前ステップ

まず、プログラムをロードし、その引数を調べて、どの入力が期待されているかを確認します。また、戻り値型を調査して、何が返されるかを理解します。QAOAプログラムは、プロバイダーから次のように入手します。

[2]:
program_id = "qaoa"
qaoa_program = provider.runtime.program(program_id)

ランタイムへの引数は次のように取得されます。

[3]:
print(f"Program name: {qaoa_program.name}, Program id: {qaoa_program.program_id}")
print(qaoa_program.parameters())
Program name: qaoa, Program id: qaoa
ParameterNamespace (Values):
| Name       | Value        | Type         | Required |     Description |
-------------------------------------------------------------------------
| optimizati | None         | integer     | False    | The optimization level to run if the swap strategies are not used. This value is 1 by default. This is an integer. |
| use_initia | None         | boolean     | False    | A boolean flag that, if set to True (the default is False), runs a heuristic algorithm to permute the Paulis in the cost operator to better fit the coupling map and the swap strategy. This is only needed when the optimization problem is sparse and when using swap strategies to transpile. |
| use_pulse_ | None         | boolean     | False    | A boolean on whether or not to use a pulse-efficient transpilation. This flag is set to False by default. |
| measuremen | None         | boolean     | False    | Whether to apply measurement error mitigation in form of a tensored measurement fitter to the measurements. Defaults to False. |
| reps       | None         | integer     | False    | The number of QAOA repetitions, i.e. the QAOA depth typically labeled p. This value defaults to 1. This is an integer. |
| alpha      | None         | number      | False    | The fraction of top measurement samples to be used for the expectation value (CVaR expectation). Defaults to 1, i.e. using all samples to construct the expectation value. |
| aux_operat | None         | array       | False    | A list of operators to be evaluated at the final, optimized state. This must be a List[PauliSumOp]. |
| initial_po | None         | ['array', 's| False    | Initial parameters of the ansatz. Can be an array or the string ``'random'`` to choose random initial parameters. The type must be numpy.ndarray or str. |
| shots      | None         | integer     | False    | The integer number of shots used for each circuit evaluation. Defaults to 1024. |
| operator   | None         | object      | True     | The cost Hamiltonian, consisting of Pauli I and Z operators, whose smallest eigenvalue we're trying to find. The type must be a PauliSumOp. |
| optimizer  | None         | object      | False    | The classical optimizer used to update the parameters in each iteration. Per default, SPSA with automatic calibration of the learning rate is used. The type must be a qiskit.algorithms.optimizers.Optimizer. |
| use_swap_s | None         | boolean     | False    | A boolean on whether or not to use swap strategies when transpiling. This flag is set to True by default. If this is False then the standard transpiler with the given optimization level will run. |

ここでは、以下で詳しく説明するさまざまな入力引数を確認します。

  • operator は、最小化したいコスト演算子を表すため、これまでで最も重要な引数です。QAOAの場合、これは PauliSumOp に対応します。問題の2つの決定変数の間に二次項があるたびに、最小化したい PauliSumOp に対応する ZZ 項があります。たとえば、演算子 PauliSumOp.from_list([("ZIZ", 1), ("IZZ", -1), ("ZZI", 1)]) は、ひとつのエッジの重みが負である三角形の最大カット問題に対応する場合があります。

  • optimizer は、閉ループの古典的な最適化で使用されるオプティマイザーを表します。これは、ハードウェアのノイズを処理するためにデフォルトで SPSA になります。

  • reps この整数は、QAOAレイヤーの数です。

  • initial_point は、 \(\gamma\)\(\beta\) の初期値です。これは、 \([\gamma_1, \beta_1, \gamma_2, \beta_2, ...]\) の順序で指定されたリストです。したがって、このリストの長さは、QAOAの深さの2倍にする必要があります(つまり、 reps パラメーター)。

  • shots は、各回路で収集するショット数です。

  • alpha はCVaR最適化の \(\alpha\) です [1] 。 \(\alpha\) が1より小さい場合、最適化ではショットの最良の \(\alpha\) 部分のみが保持されます。つまり、これは qiskit.opflow にある CVaRExpectation(alpha, PauliExpectation()) の使用に対応します。

  • measurement_error_mitigation は、読み出しエラー軽減を使用するかどうかを決定します。 True の場合、これを使用するアルゴリズムは TensoredMeasFitter を使用します。

  • use_swap_strategiesTrue の場合、QAOAプログラムは、QAOAの ZZ 演算子の可換性と、プログラムが実行されるバックエンドの結合マップを考慮した専用のスワップ戦略を使用して、QAOA回路をトランスパイルします。 このオプションがFalseの場合、QAOAプログラムはデフォルトで最適化レベル1の標準トランスパイラーになります。この最適化レベルは、 optimization_level 入力オプションを使用して選択できます。

  • use_pulse_efficientTrue に設定されている場合、パルス効率の高いトランスパイルが各QAOA反復で実行されます [2] 。これにより、2量子ビットのブロックが収集され、CartanのKAK分解が適用されて、回路の RZXGate 表現が取得されます。次に、スケーリングされた相互共振パルスが使用されます。 詳細については、以下の付録Aを参照してください。

  • use_initial_mappingTrue の場合、ヒューリスティックアルゴリズムが実行され、コスト演算子のパウリ演算子がデバイスの結合マップによりよく適合するように並べ替えられます。この順列は、 use_swap_strategies オプションが True に設定されている場合にのみ適用され、完全に接続されていない問題に対してのみ意味があります。

  • optimization_level これは、スワップ戦略が使用されていない場合のQiskitトランスパイラーの最適化レベルです。 デフォルト値は1です。

参考文献

[1] P. Kl. Barkoutsos, G. Nannicini, A. Robert, I. Tavernelli, and S. Woerner, Improving Variational Quantum Optimization using CVaR, Quantum 4, 256 (2020).

[2] N. Earnest, C. Tornow, and D. J. Egger, Pulse-efficient circuit transpilation for quantum applications on cross-resonance-based hardware, Phys. Rev. Research 3, 043088 (2021).

QAOAランタイムプログラムの実行

ここで、5量子ビットの例を使用してQAOAランタイムプログラムを使用する方法を示します。

ここでは、クラウド上でプログラムを直接呼び出す方法を示します。 ただし、Qiskit Optimization は、Qiskit Optimization ワークフローにシームレスに適合する QAOAClient を提供し、ローカル QAOA アルゴリズムの直接の代替として使用できます。 この QAOAClient については、このチュートリアルで以下で説明します。

[4]:
import numpy as np

from qiskit.tools import job_monitor
from qiskit.opflow import PauliSumOp, Z, I
from qiskit.algorithms.optimizers import SPSA

# Define the cost operator to run.
op = (
    (Z ^ Z ^ I ^ I ^ I)
    - (I ^ I ^ Z ^ Z ^ I)
    + (I ^ I ^ Z ^ I ^ Z)
    - (Z ^ I ^ Z ^ I ^ I)
    - (I ^ Z ^ Z ^ I ^ I)
    + (I ^ Z ^ I ^ Z ^ I)
    + (I ^ I ^ I ^ Z ^ Z)
)

# SPSA helps deal with noisy environments.
optimizer = SPSA(maxiter=100)

# We will run a depth two QAOA.
reps = 2

# The initial point for the optimization, chosen at random.
initial_point = np.random.random(2 * reps)

# The backend that will run the programm.
options = {"backend_name": "ibmq_qasm_simulator"}

# The inputs of the program as described above.
runtime_inputs = {
    "operator": op,
    "reps": reps,
    "optimizer": optimizer,
    "initial_point": initial_point,
    "shots": 2**13,
    # Set to True when running on real backends to reduce circuit
    # depth by leveraging swap strategies. If False the
    # given optimization_level (default is 1) will be used.
    "use_swap_strategies": False,
    # Set to True when optimizing sparse problems.
    "use_initial_mapping": False,
    # Set to true when using echoed-cross-resonance hardware.
    "use_pulse_efficient": False,
}

ここで、プロバイダーを使用してプログラムを実行します。

[5]:
job = provider.runtime.run(
    program_id=program_id,
    options=options,
    inputs=runtime_inputs,
)
[6]:
job_monitor(job)
Job Status: job has successfully run
[7]:
print(f"Job id: {job.job_id()}")
print(f"Job status: {job.status()}")
Job id: c9qh9mekcirf2adkkhvg
Bob status: JobStatus.DONE

ジョブが正常に実行されると、ジョブから結果を取得できます。結果オブジェクトには、最適化に関する情報が含まれています。自己整合性のために、 result["inputs"] からアクセスできるQAOAプログラムへの入力も含まれています。 "inputs" の下の値は、上記のキーを持つ辞書です。

[8]:
result = job.result()

特に興味深いのは、各反復でハードウェアで測定されたエネルギーと、返された固有状態です。この例では、最小化したコスト演算子のエネルギーが最大カット問題のインスタンスに対応すると想定しています。

[9]:
from collections import defaultdict


def op_adj_mat(op: PauliSumOp) -> np.array:
    """Extract the adjacency matrix from the op."""
    adj_mat = np.zeros((op.num_qubits, op.num_qubits))
    for pauli, coeff in op.primitive.to_list():
        idx = tuple([i for i, c in enumerate(pauli[::-1]) if c == "Z"])  # index of Z
        adj_mat[idx[0], idx[1]], adj_mat[idx[1], idx[0]] = np.real(coeff), np.real(coeff)

    return adj_mat


def get_cost(bit_str: str, adj_mat: np.array) -> float:
    """Return the cut value of the bit string."""
    n, x = len(bit_str), [int(bit) for bit in bit_str[::-1]]
    cost = 0
    for i in range(n):
        for j in range(n):
            cost += adj_mat[i, j] * x[i] * (1 - x[j])

    return cost


def get_cut_distribution(result) -> dict:
    """Extract the cut distribution from the result.

    Returns:
        A dict of cut value: probability.
    """

    adj_mat = op_adj_mat(PauliSumOp.from_list(result["inputs"]["operator"]))

    state_results = []
    for bit_str, amp in result["eigenstate"].items():
        state_results.append((bit_str, get_cost(bit_str, adj_mat), amp**2 * 100))

    vals = defaultdict(int)

    for res in state_results:
        vals[res[1]] += res[2]

    return dict(vals)
[10]:
import matplotlib.pyplot as plt

cut_vals = get_cut_distribution(result)

fig, axs = plt.subplots(1, 2, figsize=(14, 5))
axs[0].plot(result["optimizer_history"]["energy"])
axs[1].bar(list(cut_vals.keys()), list(cut_vals.values()))
axs[0].set_xlabel("Energy evaluation number")
axs[0].set_ylabel("Energy")
axs[1].set_xlabel("Cut value")
axs[1].set_ylabel("Probability")
[10]:
Text(0, 0.5, 'Probability')
../_images/tutorials_12_qaoa_runtime_17_1.png

Qiskit optimization

上記の機能は Qiskit-optimization に実装されており、 runtime モジュールを介してQAOAランタイムにアクセスできます。ここで、このモジュールを QAOAClient で使用する方法を示します。

[11]:
from qiskit_optimization.runtime import QAOAClient
from qiskit_optimization.algorithms import MinimumEigenOptimizer
from qiskit_optimization import QuadraticProgram
[12]:
qubo = QuadraticProgram()
qubo.binary_var("x")
qubo.binary_var("y")
qubo.binary_var("z")
qubo.minimize(linear=[1, -2, 3], quadratic={("x", "y"): 1, ("x", "z"): -1, ("y", "z"): 2})
print(qubo.prettyprint())
Problem name:

Minimize
  x*y - x*z + 2*y*z + x - 2*y + 3*z

Subject to
  No constraints

  Binary variables (3)
    x y z

[13]:
qaoa_mes = QAOAClient(
    provider=provider, backend=provider.get_backend("ibmq_qasm_simulator"), reps=2, alpha=0.75
)

qaoa = MinimumEigenOptimizer(qaoa_mes)
[14]:
result = qaoa.solve(qubo)
print(result.prettyprint())
objective function value: -2.0
variable values: x=0.0, y=1.0, z=0.0
status: SUCCESS

付録A:パルス効率の高いトランスパイル

QAOAランタイム・プログラムは、単一量子ビット・パルスの数を最小限に抑え、IBM Quantum Systemsのエコー・クロス・レゾナンス・ゲートに適用可能なクロス・レゾナンス・スケーリング手法を利用する、パルス効率の高いトランスパイルを活用できます。完全を期すために、ここでは実装されているパスマネージャーを示します。

[15]:
from qiskit.transpiler import PassManager
from qiskit.circuit.library.standard_gates.equivalence_library import (
    StandardEquivalenceLibrary as std_eqlib,
)
from qiskit.transpiler.passes import (
    Collect2qBlocks,
    ConsolidateBlocks,
    UnrollCustomDefinitions,
    BasisTranslator,
    Optimize1qGatesDecomposition,
)
from qiskit.transpiler.passes.calibration.builders import RZXCalibrationBuilderNoEcho
from qiskit.transpiler.passes.optimization.echo_rzx_weyl_decomposition import (
    EchoRZXWeylDecomposition,
)

from qiskit.test.mock import FakeBelem
[16]:
backend = FakeBelem()

パルス効率の高いパスは、qiskitのトランスパイラーパスを使用して実装されます。これには、KAK分解から取得した RZXGate に接続されたスケーリングされた相互共鳴ゲートへのパルス対応バックエンドの知識が必要です。 これらのスケーリングされたゲートは、キャリブレーションされたCNOTゲートのスケジュールに基づいています。

[17]:
inst_map = backend.defaults().instruction_schedule_map
channel_map = backend.configuration().qubit_channel_mapping
rzx_basis = ["rzx", "rz", "x", "sx"]

pulse_efficient = PassManager(
    [
        # Consolidate consecutive two-qubit operations.
        Collect2qBlocks(),
        ConsolidateBlocks(basis_gates=["rz", "sx", "x", "rxx"]),
        # Rewrite circuit in terms of Weyl-decomposed echoed RZX gates.
        EchoRZXWeylDecomposition(backend.defaults().instruction_schedule_map),
        # Attach scaled CR pulse schedules to the RZX gates.
        RZXCalibrationBuilderNoEcho(
            instruction_schedule_map=inst_map, qubit_channel_mapping=channel_map
        ),
        # Simplify single-qubit gates.
        UnrollCustomDefinitions(std_eqlib, rzx_basis),
        BasisTranslator(std_eqlib, rzx_basis),
        Optimize1qGatesDecomposition(rzx_basis),
    ]
)

パスを示すために、2量子ビットゲートのブロックを使用して任意の回路を構築します。

[18]:
from qiskit import QuantumCircuit

circ = QuantumCircuit(3)
circ.h([0, 1, 2])
circ.rzx(0.5, 0, 1)
circ.swap(0, 1)
circ.cx(2, 1)
circ.rz(0.4, 1)
circ.cx(2, 1)
circ.rx(1.23, 2)
circ.cx(2, 1)
circ.draw("mpl")
[18]:
../_images/tutorials_12_qaoa_runtime_29_0.png
[19]:
pulse_efficient.run(circ).draw("mpl", fold=False)
[19]:
../_images/tutorials_12_qaoa_runtime_30_0.png
[20]:
import qiskit.tools.jupyter

%qiskit_version_table
%qiskit_copyright

Version Information

Qiskit SoftwareVersion
qiskit-terra0.21.0.dev0+cffbb84
qiskit-aer0.10.4
qiskit-ibmq-provider0.19.1
qiskit-optimization0.4.0
System information
Python version3.10.4
Python compilerGCC 11.2.0
Python buildmain, Apr 2 2022 09:04:19
OSLinux
CPUs4
Memory (Gb)14.577533721923828
Fri May 06 21:31:31 2022 JST

This code is a part of Qiskit

© Copyright IBM 2017, 2022.

This code is licensed under the Apache License, Version 2.0. You may
obtain a copy of this license in the LICENSE.txt file in the root directory
of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.

Any modifications or derivative works of this code must retain this
copyright notice, and modified files need to carry a notice indicating
that they have been altered from the originals.