QAP.04:D-Wave QUBO用行列を直接作る【量子コンピュータ/アニーリング@Python/D-Wave】
【はじめに】
前回は、D-Wave Leapの使い方をざっとまとめた。
簡単なプログラムとして「Binaryオブジェクト(dimod版)」を生成し、「目的関数に対するBQMオブジェクト」を作成した後、「Sampler」にBQMを渡すことで量子アニーリング計算をした。
※再掲:D-Waveの全体の流れ
その他に、D-Waveでは直接「QUBO用のオブジェクト(※)」を作成して目的関数とすることで、量子アニーリング計算をさせることもできる。(※実体はpythonのdictオブジェクト)
今回はこのQUBOについてざっとまとめて、簡単なプログラムを作ってみる。
【QUBOについて】
QUBOという略語の正式名称とざっくりした説明をすると以下のような感じになる。
■QUBO用行列
先ほど書いた「QUBO問題」に対して、「所定の形式に従った行列(QUBO用行列)」を用意すれば、D-Waveがいい感じに量子アニーリング計算をしてくれる。
「QUBO用の行列」は具体的にいうと「目的関数(数式)」の「係数の値を使った上三角行列」である。
例えば、例に挙げている数式の場合は、次のような行列を作ればよい。
■ 2xy + 2xz + 2yz - x - y - z + 1のQUBO用行列をつくる
与えられた式の末尾の「+1」部分を無視しているが、これは「+1」部分が定数だからである。
→ 目的関数をできるだけ小さくするための「x,y,z」の組み合わせがどうであれ常に変わらず「+1」なので気にしなくていい、ということ。
なお
・対角線上の成分を「linear」
・それ以外の成分を「quadratic」
と呼ぶ。
【ここまでのまとめ】
長くなってしまったが、結局のところ、
ということ。
これを踏まえて簡単なプログラムを作ってみる。
【例題】
「2ab+2ac+2bc - a - b - c + 1 」を最小にする(a, b, c)の組み合わせを探す
■考え方
(a, b, c)の組み合わせの違いで値の変動が出るのは
「2ab+2ac+2bc - a - b - c」
部分だけ。
つまり、「+1」部分を除いた「2ab+2ac+2bc - a - b - c」を最小にする組み合わせを考える問題だと考える。
(※「+1」部分は定数なので、a,b,cの値の組み合わせがどうであれ変わらないから無視してよい)
【1】ライブラリのインストール
#ライブラリのインストール
!pip install dwave-ocean-sdk
(※再掲:ネット上を検索すると「dwave-system」というよく似たライブラリも出てくるので困惑するかもしれないが、「dwave-ocean-sdk」の「pip」時に一緒に入るので気にしなくてよい。)
【2】トークン設定
※再掲:
トークンを使ったD-Waveへのアクセスについては以下の通り
上記ドキュメント記載の通り、本来はアクセストークン漏洩防止など、セキュリティの観点から「コンフィグファイル」や「環境変数」に仕込む。
■トークン設定
# Sampler埋め込み用トークン(本番では行わないこと)
# 各自登録して取得したアクセストークンを指定する
MY_DWAVE_TOKEN = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
トークンを使った接続確認として、使用可能なSolver情報を出力してみる。
from dwave.cloud import Client
# トークンを使った接続確認
# 使用可能なSolver情報を出力する
client = Client.from_config(token=MY_DWAVE_TOKEN)
print(client.get_solvers())
【3】QUBO用行列を作成する(目的関数の作成)
QUBO用の行列は係数に対する上三角行列を作る。
「D-Wave」では「dict」+「tuple」を使って表現する。
my_qubo = {
('x0','x1'):2,
('x0','x2'):2,
('x1','x2'):2,
('x0','x0'):-1, # tuple型で2値key(縦横の組み合わせ)を表現
('x1','x1'):-1,
('x2','x2'):-1,
}
print(my_qubo)
■dictオブジェクト
■tupleオブジェクト
【4】Sampler経由で量子アニーリング計算する
今回は「Sampler」として「DWaveSampler」を使用してみる。
作成したQUBO用行列フォーマットのデータは「sample.qubo()」に渡してコールすればよい。
from dwave.system.samplers import DWaveSampler
from dwave.system.composites import EmbeddingComposite
# samplerオブジェクト生成
sampler = EmbeddingComposite(DWaveSampler(token=MY_DWAVE_TOKEN))
# qubo用行列フォーマットを渡して、アニーリングサイクル1000回で試行する
response = sampler.sample_qubo(my_qubo, num_reads=1000)
■sample.qubo()
▲リファレンスの記載にあるが、実際は「BQM」に変換して「sample()」をコールしている。
【5】計算結果を確認する
計算結果は「SampleSetオブジェクト」として返ってくる。今回は「data()+list化」でイテレータを回して少し見やすく表示した。
for sample, energy, num_occurrences, chain_break_fraction in list(response.data()):
print(sample, "Energy: ", energy, "Occurrences: ", num_occurrences)
▲1000回の試行で
・(0,1,0):372回(正解パターン)
・(0,0,1):294回(正解パターン)
・(1,0,0):333回(正解パターン)
・(1,0,1):1回 (※)
の結果となった。(正解の組み合わせが大多数観測されている)
※何回も解探索をするので、「最適ではない解にたどり着くパターン」も観測されることもある。これが「DWaveSampler」を使った量子アニーリングの計算の特徴とも言える。
■SampleSet
【6】全体コード
【例題】
「2ab+2ac+2bc - a - b - c + 1 」を最小にする(a, b, c)の組み合わせを探す
↓
今回の問題設定上では「2ab+2ac+2bc - a - b - c 」を最小にする(a,b,c)の組み合わせを探せばOK。
【実行環境】
・D-Wave Leap + Google Colab
■事前準備:ライブラリのインストール
#ライブラリのインストール
!pip install dwave-ocean-sdk
■ここからが全体コード
from dwave.system.samplers import DWaveSampler
from dwave.system.composites import EmbeddingComposite
# Sampler埋め込み用トークン(本番では行わないこと)
# 各自登録して取得したアクセストークンを指定する
MY_DWAVE_TOKEN = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
from dwave.cloud import Client
# トークンを使った接続確認
# 使用可能なSolver情報を出力する
client = Client.from_config(token=MY_DWAVE_TOKEN)
print(client.get_solvers())
my_qubo = {
('x0','x1'):2,
('x0','x2'):2,
('x1','x2'):2,
('x0','x0'):-1, # tuple型で2値key(縦横の組み合わせ)を表現
('x1','x1'):-1,
('x2','x2'):-1,
}
print(my_qubo)
# samplerオブジェクト生成
sampler = EmbeddingComposite(DWaveSampler(token=MY_DWAVE_TOKEN))
# qubo用行列フォーマットを渡して、アニーリングサイクル1000回で試行する
response = sampler.sample_qubo(my_qubo, num_reads=1000)
# 結果の確認
for sample, energy, num_occurrences, chain_break_fraction in list(response.data()):
print(sample, "Energy: ", energy, "Occurrences: ", num_occurrences)
【おまけ】BQMをつくって後からlinearやquadraticを積んでいく書き方
BQMを先に生成して、そこにlinear成分やquadratic成分を積んでいく方法もある。
from dimod import BinaryQuadraticModel
from dwave.system.samplers import LeapHybridBQMSampler # LeapHybridSamplerのエイリアス
# Sampler埋め込み用トークン(本番では行わないこと)
# 各自登録して取得したアクセストークンを指定する
MY_DWAVE_TOKEN = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"
# トークンを使った接続確認
# 使用可能なSolver情報を出力する
client = Client.from_config(token=MY_DWAVE_TOKEN)
print(client.get_solvers())
bqm = BinaryQuadraticModel(3, 'BINARY')
print(bqm)
# 出力例:変数のラベルは0,1,2...とついていく
# BinaryQuadraticModel({0: 0.0, 1: 0.0, 2: 0.0}, {}, 0.0, 'BINARY')
# BQMではオフセットも指定可能
bqm.offset = 1.0
bqm
# linear部分
bqm.add_linear(0,-1)
bqm.add_linear(1,-1)
bqm.add_linear(2,-1)
# quadratic部分
bqm.add_quadratic(0,1,2)
bqm.add_quadratic(0,2,2)
bqm.add_quadratic(1,2,2)
# QUBOフォーマットを出力してみる
print(bqm.to_qubo())
# LeapHybridBQMSamplerの生成
sampler = LeapHybridBQMSampler(token = MY_DWAVE_TOKEN)
# Solverに投げて計算実施
sampleset = sampler.sample(bqm) #LeapHybridの場合はnum_reads指定はできない
# 結果を確認
print(sampleset)
▲今回は定数「+1」部分も含めて、(1,0,0)で最小値0をとる組み合わせが答えとして返ってきた(正解)