12/9(月) 応用科学学会シンポジウムで自動運転に関する講演を担当します☆彡

21-16. 量子アニーリング(QUBO)で温度計パズル(Thermometers)を解く

やること

量子アニーリングのQUBOで設定可能な条件式まとめ(保存版)の記事ではQUBOの条件式を基本、応用A、応用B、応用Cにカテゴリしてまとめました。これまでQUBOでいくつかのパズルを解いてきましたが、応用Cの使い道がありませんでした。

ここでは応用Cの実用例としてQUBOで温度計パズル(Thermometers)を解いてみましょう。

このような問題です。

答えがこちらです。

各行、各列とも数字の数だけ赤マスが入りますが、温度計なので球部(水銀溜まり)から順にしか塗られないという制約があります。

おさらい

今回は基本と応用Cの条件設定を使います。

基本「n個の量子ビットからm個を1にする」

C1「2個の量子ビットを降順にする」(q0 ≧ q1)

C2「すべての量子ビットを降順にする」(q0 ≧ q1 ≧ q2)

その他の設定可能な条件式も気になる方は過去のまとめ記事をご参照ください。今回のC1とC2は見慣れないと思いますのでまとめ記事で予習してください。

どうやって温度計パズルを解くのか

16個の量子ビットを各マスに対応させます。結果が1なら色を塗ることになります。

まず、各行、各列とも指定の個数だけ1になるように設定します。基本の「n個の量子ビットからm個を1にする」を使います。このパターンは過去に お絵かきロジックテント・アンド・ツリーパズル でもやったのでお馴染みという感じです。

次に、各温度計に着目して、球部から降順になる設定をします。ここで言う降順とは q0 ≧ q1 ≧ q2 の意味で、[1, 1, 0] のように途中から0に切り替わる形だけでなく、[0, 0, 0]、[1, 1, 1] のようにずっと等しくてもOKです。なんと応用Cでこれをちょうど表現できるのです。

コード

条件設定の部分をよく確認してください。

from pyqubo import Binary
from dwave_qbsolv import QBSolv

#量子ビットを用意する
q00 = Binary('q00')
q01 = Binary('q01')
q02 = Binary('q02')
q03 = Binary('q03')
q04 = Binary('q04')
q05 = Binary('q05')
q06 = Binary('q06')
q07 = Binary('q07')
q08 = Binary('q08')
q09 = Binary('q09')
q10 = Binary('q10')
q11 = Binary('q11')
q12 = Binary('q12')
q13 = Binary('q13')
q14 = Binary('q14')
q15 = Binary('q15')

#各行、「4個から指定の個数だけ1になる」
H = 0
H += (q00 + q01 + q02 + q03 - 2)**2
H += (q04 + q05 + q06 + q07 - 1)**2
H += (q08 + q09 + q10 + q11 - 3)**2
H += (q12 + q13 + q14 + q15 - 1)**2

#各列、「4個から指定の個数だけ1になる」
H += (q00 + q04 + q08 + q12 - 3)**2
H += (q01 + q05 + q09 + q13 - 1)**2
H += (q02 + q06 + q10 + q14 - 1)**2
H += (q03 + q07 + q11 + q15 - 2)**2

#各温度計、球部から降順になる
H += (1 - q08) * q04
H += (1 - q04) * q00 #8→4→0の連鎖

H += (1 - q05) * q01

H += (1 - q03) * q02

H += (1 - q07) * q06

H += (1 - q11) * q10
H += (1 - q10) * q09 #11→10→9の連鎖

H += (1 - q13) * q12

H += (1 - q15) * q14


#コンパイル
model = H.compile()
qubo, offset = model.to_qubo()

#分割サンプリング
response = QBSolv().sample_qubo(qubo, num_repeats=500)

#レスポンスの確認
print('response')
for (sample, energy, occurrence) in response.data():
    print(f'Sample:{list(sample.values())} Energy:{energy} Occurrence:{occurrence}')
Sample:[1, 0, 0, 1, 1, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 0] Energy:-30.0 Occurrence:368
Sample:[1, 0, 0, 1, 1, 0, 0, 0, 1, 1, 1, 0, 0, 0, 0, 1] Energy:-29.0 Occurrence:33
Sample:[1, 1, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 0, 0, 1] Energy:-29.0 Occurrence:91
Sample:[0, 0, 1, 1, 1, 0, 0, 0, 1, 0, 0, 1, 1, 1, 0, 0] Energy:-28.0 Occurrence:1
Sample:[1, 0, 0, 0, 1, 0, 0, 0, 1, 0, 1, 1, 0, 1, 0, 1] Energy:-28.0 Occurrence:1

一番目の解を4×4に戻してやると模範解答の通りになりました。

[[1 0 0 1]
 [1 0 0 0]
 [1 0 1 1]
 [0 1 0 0]]

さいごに

ちなみにマイナーなパズルですがアクアプレース(Aquarium)というのもあるようで、おそらく今回の内容を応用して解けると思います(おそらく)。QUBOはこういったペンシルパズルと相性が良く、まだまだ楽しめそうです。

タイトルとURLをコピーしました