最大切断要件を低減するためのパウリ相関符号化
所要時間の目安:Eagle r3 プロセッサで35分(注:これはあくまで目安です。 (実行時間は異なる場合があります。)
学習成果
- パウリ相関符号化(PCE)の理論的原理を理解する。これには、多体パウリストリングが、古典的最適化問題の多項式時間での圧縮をどのように可能にするかについても含まれる。
- PCEを実践的に導入し、近未来の量子ハードウェア上で大規模最適化課題を符号化・解く。
前提条件
背景
このチュートリアルでは、量子計算をより効率的に行うために最適化問題を量子ビットにエンコードするように設計されたアプローチであるパウリ相関エンコーディング (PCE) [1] を紹介します。 PCEは、最適化問題の古典的変数を多体パウリ行列相関にマッピングし、その結果、問題の空間要件を多項式に圧縮する。 PCEを採用することで、エンコードに必要な量子ビットの数を減らすことができ、量子ビット資源が限られている近未来の量子デバイスに特に有利となる。 さらに、PCEが本質的に不毛のプラトーを緩和し、この現象に対して超多項式の回復力を提供することが解析的に実証された。 このビルトイン機能により、量子最適化ソルバーでかつてない性能を発揮することができる。
概要
PCEアプローチは、以下の [1] の図1に示すように、主に3つのステップから構成されています:
- 最適化問題をパウリ相関空間にエンコードする。
- 量子クラシカル最適化ソルバーを使って問題を解く。
- 解を元の最適化空間にデコードする。 PCEアプローチは、パウリ相関行列を処理できるあらゆる量子最適化ソルバーに適応可能である。
[1] の図1では、PCEアプローチを説明するために、 最大切断問題が例として用いられている。 ノードを用いた最大カット問題は、パウリ相関空間に符号化されており、最適化問題を相関行列として表現しています。具体的には、 個の量子ビット間の2体パウリ行列相関 です。ノードの色は、各符号化ノードに使用されるパウリ列を示しています。 例えば、二値変数 に対応するノード1は、 の期待値によって符号化され、 は によって符号化される。 これは、問題の 個の変数を 個の量子ビットに圧縮することに相当する。 より広く言えば、 -体相関により、 の次数の多項式圧縮が可能となり、 となる。選択されたパウリ集合は、互いに可換なパウリ列の3つの部分集合から構成されており、これにより、わずか3つの測定設定だけで、すべての 相関を実験的に推定することができる。
元の最大切断(max-cut)目的関数を模倣する、パウリ期待値に基づく損失関数 が構築される。 その後、 Variational Quantum Eigensolver(VQE) などの量子・古典ハイブリッド最適化ソルバーを用いて、損失関数を最適化します。
最適化が完了すると、解は元の最適化空間にデコードされ、最適な最大カット解が得られる。
要件
このチュートリアルを始める前に、以下のものがインストールされていることを確認してください:
- Qiskit SDK v1.0 またはそれ以降、 可視化サポート付き
- Qiskit Runtime v0.22 またはそれ以降 (
pip install qiskit-ibm-runtime) - 最適化マッパー Qiskit アドオン (
pip install qiskit-addon-opt-mapper)
セットアップ
from itertools import combinations
import numpy as np
import rustworkx as rx
import networkx as nx
from scipy.optimize import minimize, OptimizeResult
from qiskit.circuit.library import efficient_su2
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
from qiskit.quantum_info import SparsePauliOp
from qiskit_ibm_runtime import EstimatorV2 as Estimator
from qiskit_ibm_runtime import QiskitRuntimeService
from qiskit_ibm_runtime import Session
from rustworkx.visualization import mpl_draw
from qiskit_aer import AerSimulator
from qiskit_addon_opt_mapper.applications import Maxcutdef make_cut_evaluator(problem):
"""Return a function that maps a 0/1 node assignment to its cut value.
The objective of the max-cut ``OptimizationProblem`` is the cut value
itself, and ``problem.objective.evaluate`` computes it for one
assignment. The swap search in Step 4 evaluates one candidate
assignment per edge, so pull the objective's coefficients out once
and evaluate them as a sparse matrix product instead.
"""
quadratic = problem.objective.quadratic.coefficients.tocsr()
linear = problem.objective.linear.to_array()
constant = problem.objective.constant
def evaluate_cut(bits):
x = np.asarray(bits, dtype=float)
return int(round(constant + linear @ x + x @ (quadratic @ x)))
return evaluate_cut小規模シミュレータの例
service = QiskitRuntimeService()
real_backend = service.least_busy(
operational=True, simulator=False, min_num_qubits=156
)
backend = AerSimulator.from_backend(real_backend)
print(f"We are using the {backend.name}")Output:
We are using the aer_simulator_from(ibm_pittsburgh)
ステップ1:古典的な入力を量子問題にマッピングする
最大切断問題
最大切断問題(max-cut problem)は、グラフ 上で定義される組み合わせ最適化問題である。ここで、 は頂点の集合であり、 は辺の集合である。 目的は、頂点を と の2つの集合に分割し、これら2つの集合間の辺の数を最大化することです。 最大切断問題の詳細については、 「量子近似最適化アルゴリズム 」のチュートリアルをご参照ください。 最大カット問題も、 「QAOAの高度な手法」 チュートリアルでは例として取り上げられています。 これらのチュートリアルでは、最大カット問題を解くためにQAOAアルゴリズムが用いられています。
グラフ → 最適化問題
まず、100個のノードを持つランダムグラフについて考えてみましょう。
num_nodes = 100 # Number of nodes in graph
seed = 42
graph = rx.undirected_gnp_random_graph(num_nodes, 0.1, seed=seed)
mpl_draw(graph)Output:
Optimization Mapper Qiskit アドオンのアプリケーション Maxcut クラスを使用して、最大カット問題をモデル化します。 各ノードにつき1つのバイナリ変数 OptimizationProblem を持つ を返します。その目的関数は、最大化すべきカット値です。 QAOAのチュートリアルでは、この問題はQUBOに変換され、変数ごとに1クビットを持つコストハミルトニアンへと変換されます。 一方、PCEでは、各二値変数をパウリ期待値の符号として符号化するため、ここでは問題が目的関数を定義・評価し、それに続いて量子ビットへの符号化が行われる。
maxcut = Maxcut(graph)
maxcut_problem = maxcut.to_optimization_problem()
evaluate_cut = make_cut_evaluator(maxcut_problem)
print(f"Binary variables: {maxcut_problem.get_num_vars()}")
print(
"Quadratic terms (one per edge):",
len(maxcut_problem.objective.quadratic.to_dict()),
)Output:
Binary variables: 100
Quadratic terms (one per edge): 508
nx_graph = nx.Graph()
nx_graph.add_nodes_from(range(num_nodes))for edge in graph.edge_list():
nx_graph.add_edge(edge[0], edge[1])curr_cut_size, partition = nx.approximation.one_exchange(nx_graph, seed=1)
print(f"Initial cut size: {curr_cut_size}")Output:
Initial cut size: 345
100ノードからなるグラフを、9つの量子ビットにわたる2体パウリ行列の相関として符号化する(詳細は後述)。 このグラフは相関行列として表現され、各ノードはパウリ文字列によって符号化されている。 パウリ弦の期待値の符号は、そのノードの分割を示している。 例えば、ノード0はパウリ列 で表されます。このパウリ列の期待値の符号は、ノード0の分割を示しています。 に対するパウリ相関符号化 (PCE)を次のように定義する
ここで、 はノード のパーティションであり、 は量子状態 上のノード を符号化したパウリ文字列の期待値である。
では、PCEを使ってグラフをハミルトニアンにエンコードしてみよう。 ノードを3つのセットに分ける: 、 。 そして、各セットのノードを、それぞれ 、 、 のパウリ文字列を使って符号化する。
すべてのノードを符号化するために必要なノード数と量子ビット数との間の関係性を導き出す必要があります。 エンコーディングにおけるすべての可能な順列を用いると、次のようになる:
この例では、 を扱うため、
したがって、特定のノード数 を表現するために必要な量子ビット数 は、次のように表されます:
なお、 という記号は天井関数を表しており、実数を次の整数に切り上げるものです。 これにより、量子ビットの数が整数になることが保証されます。
num_qubits = int(np.ceil((1 + np.sqrt(1 + (8 / 3) * num_nodes)) / 2))
list_size = num_nodes // 3
node_x = [i for i in range(list_size)]
node_y = [i for i in range(list_size, 2 * list_size)]
node_z = [i for i in range(2 * list_size, num_nodes)]
print(f"Number of qubits: {num_qubits}")
print("List 1:", node_x)
print("List 2:", node_y)
print("List 3:", node_z)Output:
Number of qubits: 9
List 1: [0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32]
List 2: [33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65]
List 3: [66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99]
def build_pauli_correlation_encoding(pauli, node_list, n, k=2):
pauli_correlation_encoding = []
for idx, c in enumerate(combinations(range(n), k)):
if idx >= len(node_list):
break
paulis = ["I"] * n
paulis[c[0]], paulis[c[1]] = pauli, pauli
pauli_correlation_encoding.append(("".join(paulis)[::-1], 1))
hamiltonian = []
for pauli, weight in pauli_correlation_encoding:
hamiltonian.append(SparsePauliOp.from_list([(pauli, weight)]))
return hamiltonian
pauli_correlation_encoding_x = build_pauli_correlation_encoding(
"X", node_x, num_qubits
)
pauli_correlation_encoding_y = build_pauli_correlation_encoding(
"Y", node_y, num_qubits
)
pauli_correlation_encoding_z = build_pauli_correlation_encoding(
"Z", node_z, num_qubits
)ステップ2:量子ハードウェア実行に向けた問題の最適化
量子回路
ここでは、状態 を でパラメータ化し、変分法を用いてこれらのパラメータ を最適化する。
このチュートリアルでは、その表現力と実装の容易さから、変分アルゴリズムに efficient_su2 ansatz を採用している。
また、このチュートリアルの後半で紹介するリラックスした損失関数も使います。
その結果、より少ない量子ビットとより浅い回路深度で大規模な問題に対処できる。
# Build the quantum circuit
qc = efficient_su2(num_qubits, su2_gates=["ry", "rz"], reps=2)
qc.draw("mpl")Output:
# Optimize the circuit
pm = generate_preset_pass_manager(optimization_level=3, backend=backend)
qc = pm.run(qc)損失関数
損失関数 については、 [1] に記載されている最大カット目的関数の緩和形を用いる。これは、 と定義される。ここで、 は辺 の重みを表し、 はノード の分割を表す。 損失関数 は次のように与えられる:
,
ここで、最大切断(max-cut)の目的関数は、ノードを符号化するパウリ文字列の期待値の滑らかな双曲線正接関数に置き換えられる。 ソルバーの性能を向上させるため、正則化項 および量子ビットの数に比例する再スケーリング係数 を導入する。
正則化項は次のように定義される:
と定義される。
ここで、 、 、 は辺の数であり、 はグラフの頂点の数である。
def loss_func_estimator(x, ansatz, hamiltonian, estimator, graph):
"""
Calculates the specified loss function for the given ansatz, Hamiltonian,
and graph.
The expectation values of each Pauli string in the Hamiltonian are first
obtained by running the ansatz on the quantum backend. These
expectation values are then passed through the nonlinear function
tanh(alpha * prod_i). The loss function is
subsequently computed from these transformed values.
"""
job = estimator.run(
[
(ansatz, hamiltonian[0], x),
(ansatz, hamiltonian[1], x),
(ansatz, hamiltonian[2], x),
]
)
result = job.result()
# calculate the loss function
node_exp_map = {}
idx = 0
for r in result:
for ev in r.data.evs:
node_exp_map[idx] = ev
idx += 1
loss = 0
alpha = num_qubits
for edge0, edge1 in graph.edge_list():
loss += np.tanh(alpha * node_exp_map[edge0]) * np.tanh(
alpha * node_exp_map[edge1]
)
regulation_term = 0
for i in range(len(graph.nodes())):
regulation_term += np.tanh(alpha * node_exp_map[i]) ** 2
regulation_term = regulation_term / len(graph.nodes())
regulation_term = regulation_term**2
beta = 1 / 2
v = len(graph.edges()) / 2 + (len(graph.nodes()) - 1) / 4
regulation_term = beta * v * regulation_term
loss = loss + regulation_term
global experiment_result
print(f"Iter {len(experiment_result)}: {loss}")
experiment_result.append({"loss": loss, "exp_map": node_exp_map})
return lossステップ3: Qiskit primitivesを使用して実行する
このチュートリアルでは、説明のために最適化ループ内で を設定 max_iter=50 します。 反復回数を増やせば、より良い結果が得られると予想されます。
pce = []
pce.append(
[op.apply_layout(qc.layout) for op in pauli_correlation_encoding_x]
)
pce.append(
[op.apply_layout(qc.layout) for op in pauli_correlation_encoding_y]
)
pce.append(
[op.apply_layout(qc.layout) for op in pauli_correlation_encoding_z]
)max_iter = 50
counter = {"i": 0}
last_x = {"value": None}
last_fun = {"value": None}
with Session(backend=backend) as session:
estimator = Estimator(mode=session)
experiment_result = []
def loss_func(x):
last_x["value"] = x.copy()
if counter["i"] + 1 > max_iter:
return last_fun["value"]
counter["i"] += 1
val = loss_func_estimator(
x, qc, [pce[0], pce[1], pce[2]], estimator, graph
)
last_fun["value"] = val
return val
np.random.seed(seed)
initial_params = np.random.rand(qc.num_parameters)
result = minimize(
loss_func, initial_params, method="COBYLA", options={"rhobeg": 1.0}
)
if counter["i"] >= max_iter:
result = OptimizeResult(
message=f"Return from COBYLA because the objective function "
f"has been evaluated {max_iter} times.",
success=False,
status=3,
fun=last_fun["value"],
x=last_x["value"],
nfev=counter["i"],
)
print(result)Output:
Iter 0: 152.06331779109445
Iter 1: 126.93132569244693
Iter 2: 55.00651109478942
Iter 3: 33.21667439735082
Iter 4: 19.167189700765054
Iter 5: 30.426875553389127
Iter 6: 13.906354614925815
Iter 7: 27.668686100172614
Iter 8: 28.194600411380772
Iter 9: 24.899677141564936
Iter 10: 22.10858041608358
Iter 11: 8.096210209030176
Iter 12: 8.761240650022913
Iter 13: 9.43332962472133
Iter 14: 7.559669820210062
Iter 15: 13.023860357384214
Iter 16: 11.953653163007987
Iter 17: 10.434221292269214
Iter 18: 4.871246922225549
Iter 19: 10.303826533395856
Iter 20: 16.40225843511339
Iter 21: 10.852469442638652
Iter 22: 9.314666066331633
Iter 23: 6.082358010011717
Iter 24: 10.982893700414476
Iter 25: 4.575556036893407
Iter 26: 6.844995727798197
Iter 27: 8.695229718011293
Iter 28: 3.730227705075576
Iter 29: 4.285479467758721
Iter 30: 4.219714169314187
Iter 31: 0.21125608131726548
Iter 32: 4.22820465672552
Iter 33: 13.67513152473414
Iter 34: 10.388838701884863
Iter 35: -0.5400153834163355
Iter 36: 5.310281742761159
Iter 37: -0.5430772019249082
Iter 38: -4.47820293096989
Iter 39: -3.4832056790567467
Iter 40: -0.5087341672330172
Iter 41: -2.488777859669888
Iter 42: 0.34945469121089356
Iter 43: -0.8544299675507805
Iter 44: -3.5902371208579344
Iter 45: 2.6609573847203216
Iter 46: -4.336467834641085
Iter 47: 0.10158504073828034
Iter 48: -1.3506458813942084
Iter 49: -2.1794894170194192
message: Return from COBYLA because the objective function has been evaluated 50 times.
success: False
status: 3
fun: -2.1794894170194192
x: [ 1.375e+00 1.951e+00 ... 9.395e-01 8.948e-01]
nfev: 50
ステップ4:後処理を行い、結果を希望の古典形式で返す
ノードの分割は、そのノードを符号化するパウリ文字列の期待値の符号を評価することによって決定される。 期待値が非負の場合は、そのノードに値 1 が割り当てられ、期待値が負の場合は 0 が割り当てられます。 この割り当てを、2つのノード集合にデコードするのが Maxcut 、この手法 interpret である。
# Decode the node assignment from the iteration with the lowest loss.
# If the expectation value is non-negative, the node is assigned 1,
# otherwise it is assigned 0.
def get_bits(experiment_result):
best_index = min(
range(len(experiment_result)),
key=lambda i: experiment_result[i]["loss"],
)
exp_map = experiment_result[best_index]["exp_map"]
bits = [1 if exp_map[i] >= 0 else 0 for i in range(len(exp_map))]
return bits, best_index
cur_bits, best_index = get_bits(experiment_result)
partition = maxcut.interpret(np.array(cur_bits))
print("Partition 0:", partition[0])
print("Partition 1:", partition[1])Output:
Partition 0: [0, 1, 3, 5, 6, 7, 9, 11, 12, 14, 15, 17, 22, 24, 25, 28, 31, 32, 33, 35, 37, 38, 39, 40, 41, 43, 49, 53, 54, 56, 59, 61, 63, 65, 67, 68, 69, 77, 78, 79, 80, 88, 92, 93, 94, 95]
Partition 1: [2, 4, 8, 10, 13, 16, 18, 19, 20, 21, 23, 26, 27, 29, 30, 34, 36, 42, 44, 45, 46, 47, 48, 50, 51, 52, 55, 57, 58, 60, 62, 64, 66, 70, 71, 72, 73, 74, 75, 76, 81, 82, 83, 84, 85, 86, 87, 89, 90, 91, 96, 97, 98, 99]
この割り当て OptimizationProblem に対して目的関数を評価することで、最大カット問題のカットサイズを計算することができます。
cut_size = evaluate_cut(cur_bits)
print(f"Cut size: {cut_size}")Output:
Cut size: 278
トレーニングが完了したら、古典的な後処理として、解を改善するために1ビットのスワップ探索を1ラウンド行う。 このプロセスでは、2つのノードのパーティションを入れ替え、カットサイズを評価する。 カットサイズが改善されれば、スワップを維持する。 このプロセスを、エッジで結ばれた可能性のあるすべてのノードのペアについて繰り返す。
# Swap the partitions and calculate the cut size
def swap_partitions(graph, cur_bits, evaluate_cut):
best_cut = 0
best_bits = []
for edge0, edge1 in graph.edge_list():
swapped_bits = cur_bits.copy()
swapped_bits[edge0], swapped_bits[edge1] = (
swapped_bits[edge1],
swapped_bits[edge0],
)
cut_size = evaluate_cut(swapped_bits)
if best_cut < cut_size:
best_cut = cut_size
best_bits = swapped_bits
return best_cut, best_bits
best_cut, best_bits = swap_partitions(graph, cur_bits, evaluate_cut)
print(best_cut, best_bits)Output:
287 [0, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1]
大規模なハードウェアの例
# -------------------------Step 1-------------------------
num_nodes = 1500 # Number of nodes in graph
graph = rx.undirected_gnp_random_graph(num_nodes, 0.1, seed=seed)
nx_graph = nx.Graph()
nx_graph.add_nodes_from(range(num_nodes))
for edge in graph.edge_list():
nx_graph.add_edge(edge[0], edge[1])
maxcut = Maxcut(graph)
maxcut_problem = maxcut.to_optimization_problem()
evaluate_cut = make_cut_evaluator(maxcut_problem)
num_qubits = int(np.ceil((1 + np.sqrt(1 + (8 / 3) * num_nodes)) / 2))
list_size = num_nodes // 3
node_x = [i for i in range(list_size)]
node_y = [i for i in range(list_size, 2 * list_size)]
node_z = [i for i in range(2 * list_size, num_nodes)]
pauli_correlation_encoding_x = build_pauli_correlation_encoding(
"X", node_x, num_qubits
)
pauli_correlation_encoding_y = build_pauli_correlation_encoding(
"Y", node_y, num_qubits
)
pauli_correlation_encoding_z = build_pauli_correlation_encoding(
"Z", node_z, num_qubits
)
print(f"We are using {num_qubits} qubits")
# -------------------------Step 2-------------------------
backend = real_backend
print(f"We are using the {backend.name}")
qc = efficient_su2(num_qubits, ["ry", "rz"], reps=2)
pm = generate_preset_pass_manager(optimization_level=3, backend=backend)
qc = pm.run(qc)
# -------------------------Step 3-------------------------
pce = []
pce.append(
[op.apply_layout(qc.layout) for op in pauli_correlation_encoding_x]
)
pce.append(
[op.apply_layout(qc.layout) for op in pauli_correlation_encoding_y]
)
pce.append(
[op.apply_layout(qc.layout) for op in pauli_correlation_encoding_z]
)
# Run the optimization using a session.
max_iter = 50
counter = {"i": 0}
with Session(backend=backend) as session:
estimator = Estimator(mode=session)
estimator.options.environment.job_tags = ["TUT_PCEFQ"]
experiment_result = []
def loss_func(x):
last_x["value"] = x.copy()
if counter["i"] + 1 > max_iter:
return last_fun["value"]
counter["i"] += 1
val = loss_func_estimator(
x, qc, [pce[0], pce[1], pce[2]], estimator, graph
)
last_fun["value"] = val
return val
np.random.seed(seed)
initial_params = np.random.rand(qc.num_parameters)
result = minimize(
loss_func, initial_params, method="COBYLA", options={"rhobeg": 1.0}
)
if counter["i"] >= max_iter:
result = OptimizeResult(
message=f"Return from COBYLA because the objective function "
f"has been evaluated {max_iter} times.",
success=False,
status=3,
fun=last_fun["value"],
x=last_x["value"],
nfev=counter["i"],
)
print(result)
# -------------------------Step 4-------------------------
cur_bits, best_index = get_bits(experiment_result)
cut_size = evaluate_cut(cur_bits)
print(f"Cut size: {cut_size}")
best_cut, best_bits = swap_partitions(graph, cur_bits, evaluate_cut)
# Print final solution
print(
f"The best max-cut value achieved for a graph with {num_nodes} nodes "
f"on {num_qubits} qubits is {best_cut}"
)
print(f"and the specific partition we obtained is {best_bits}")Output:
We are using 33 qubits
We are using the ibm_pittsburgh
Iter 0: 62192.56098951446
Iter 1: 54325.76340818571
Iter 2: 42933.215216568526
Iter 3: 35721.92960658595
Iter 4: 35947.69927402131
Iter 5: 38563.731543582166
Iter 6: 32552.97070835923
Iter 7: 34730.314616527576
Iter 8: 27032.920190652876
Iter 9: 27126.107795624957
Iter 10: 25268.853826648774
Iter 11: 23521.1489346676
Iter 12: 21658.585394613096
Iter 13: 15154.765728388778
Iter 14: 14826.985321939152
Iter 15: 16199.811009055386
Iter 16: 14328.589985020852
Iter 17: 13542.67789354434
Iter 18: 11737.341026421902
Iter 19: 10986.431644208033
Iter 20: 9780.61657788984
Iter 21: 8555.757154943312
Iter 22: 10465.489221259657
Iter 23: 8577.158776664453
Iter 24: 8950.176436175909
Iter 25: 7395.8803219317215
Iter 26: 7228.1129297581765
Iter 27: 6881.076354027118
Iter 28: 6626.8983738084835
Iter 29: 7302.433279572058
Iter 30: 6995.735525608492
Iter 31: 6176.828001182083
Iter 32: 6299.8061884979725
Iter 33: 6498.921256598082
Iter 34: 6184.684072713035
Iter 35: 5707.720358527108
Iter 36: 5414.328928670104
Iter 37: 5771.80573980932
Iter 38: 5620.64903695431
Iter 39: 6285.735849503706
Iter 40: 5894.367571492991
Iter 41: 6079.275064349491
Iter 42: 5267.448536682269
Iter 43: 4789.7281459863325
Iter 44: 5109.106057200384
Iter 45: 5060.248865782273
Iter 46: 5064.786713246322
Iter 47: 5474.433264995371
Iter 48: 4675.6138719238015
Iter 49: 5081.660754434205
message: Return from COBYLA because the objective function has been evaluated 50 times.
success: False
status: 3
fun: 5081.660754434205
x: [ 1.375e+00 1.951e+00 ... 7.260e-01 8.971e-01]
nfev: 50
Cut size: 55507
The best max-cut value achieved for a graph with 1500 nodes on 33 qubits is 55567
and the specific partition we obtained is [1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 0, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 1, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 1, 1, 0, 0, 0, 1, 1, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 0, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 1, 0, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 1, 1, 0, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 1, 0, 1, 1, 1, 0, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 0, 1, 0, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 0, 1, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 0, 0, 0, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 0, 0, 0, 1, 1, 1, 1, 0, 0, 0, 1, 0, 0, 0, 0, 0, 1, 0, 1, 0, 1, 1, 1, 0, 0, 1, 0, 1, 1, 1, 0, 0, 1, 0, 0, 0, 0, 1, 0, 1, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 0, 1, 1, 0, 0, 0, 0, 1, 0, 1, 0, 0, 0, 0, 0, 1, 1, 0, 1, 0, 0, 1, 1, 1, 0, 0, 1, 0, 1, 0, 1, 1, 0, 1, 0, 1, 1, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 0, 1, 1, 1, 1, 1]
次のステップ
この作品が興味深かった方は、以下の資料もご参照ください:
参照
[1] シオリリ, M., ボルヘス, L., パティ, T. L., ガルシア=マルティン, D., カミロ, G., Anandkumar, A., & Aolita, L. (2024). 少数の量子ビットを用いた大規模量子最適化ソルバーの実現に向けて。 arXiv プレプリント arXiv:2401.09421.
チュートリアル調査
このチュートリアルに関するご意見・ご感想をお寄せください。 あなたの洞察は、私たちのコンテンツの提供とユーザーエクスペリエンスを向上させるのに役立ちます。
© IBM Corp. 2024-2026