見出し画像

QAP.08:「CQM:Constrained Quadratic Models」の基本【量子コンピュータ/アニーリング@Python/D-Wave】

【はじめに】

繰り返しになるが、D-Waveでは計算させたい問題の種類にあわせて、いい感じに取り扱ってくれる「Model(計算モデルオブジェクト)」を用意している

今回はこの中の「CQM:Constrained Quadratic Models」の使い方についてざっとまとめていく。

【CQMの特徴】

「CQM」の特徴をざっくりいうと

・「0,1」に加えて「整数値」を取り扱うことができる
・制約条件をそのまま設定できる

というもの。CQMで想定している数式は以下を参照。

以上を踏まえてさっそく例題でCQMの使い方をまとめていく。

【例題】

「長さ:8の紐」で四角形を作る。
「一辺の横の長さ:x」、「一辺の縦の長さ:y」をそれぞれどれくらいにしたら四角形の面積をできるだけ大きくできるか?

つまり

・目的関数: x * y  ← できるだけ大きくしたい
・制約条件:
   2 * x + 2 * y ≦ 8 ※紐の長さの上限
   0 ≦ x ≦ 4 ※横1辺の長さの上限
   0 ≦ y ≦ 4 ※縦1辺の長さの上限

をみたす(x, y)の組み合わせをさがす、ということ。

【1】ライブラリのインストール

#ライブラリのインストール
!pip install dwave-ocean-sdk

(※再掲:ネット上を検索すると「dwave-system」というよく似たライブラリも出てくるので困惑するかもしれないが、「dwave-ocean-sdk」の「pip」時に一緒に入るので気にしなくてよい。)

【2】トークン設定


※再掲:
トークンを使ったD-Waveへのアクセスについては以下の通り

上記ドキュメント記載の通り、本来はアクセストークン漏洩防止など、セキュリティの観点から「コンフィグファイル」や「環境変数」に仕込む。

【注意】
今回は簡単なプログラムをちょっと試してみるだけなので、「Samplerオブジェクトに仕込む」というやり方で手抜きをしている。

リリースアプリ、実運用ではSamplerへのトークン埋め込みはしないこと。


■トークン設定

# Sampler埋め込み用トークン(本番では行わないこと)
# 各自登録して取得したアクセストークンを指定する
MY_DWAVE_TOKEN = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"

トークンを使った接続確認として、使用可能なSolver情報を出力してみる。

from dwave.cloud import Client

# トークンを使った接続確認
# 使用可能なSolver情報を出力する
client = Client.from_config(token=MY_DWAVE_TOKEN)
print(client.get_solvers())

【実行結果例】
[BQMSolver(id='hybrid_binary_quadratic_model_version2'), DQMSolver(id='hybrid_discrete_quadratic_model_version1'), CQMSolver(id='hybrid_constrained_quadratic_model_version1'), StructuredSolver(id='DW_2000Q_6'), StructuredSolver(id='Advantage_system4.1')]

【3】整数を取り扱える変数を用意する(dimod版)

整数を取り扱える変数」として「dimod.Integer」を使うことができる。

※これは次のように定義されている(2022/2/17時点)

def Integer(
    label: Optional[Variable] = None,
    bias: Bias = 1,
    dtype: Optional[DTypeLike] = None,
    *,
    lower_bound: int = 0,
    upper_bound: Optional[int] = None)
-> QuadraticModel:

quadratic_model.pyより

これを踏まえて「変数:x、変数:y」は次のように書ける。

■整数を取り扱える変数を用意する

import dimod

# 変数の定義
x = dimod.Integer('x',upper_bound=4) # 下限デフォルト値0以上なので省略
y = dimod.Integer('y',upper_bound=4)

▲「変数」定義の時点で、「変数単体」に「値のとる範囲」を設定することができる。

【4】目的関数:fの作成

変数が用意出来たら「目的関数:f」を用意する。ここで気をつけることは

「目的関数の値をできるだけ大きくしたい」
          ↓
量子アニーリングでは、数式の値をできるだけ小さくする値の組み合わせを探す挙動である
          ↓
「目的関数にマイナスをつけてできるだけ小さくさせる」(あとでマイナスを外す)

ということ。

■目的関数:fの作成

f = -(x*y)
print(f)

【ここまでの実行結果】
QuadraticModel({'x': -0.0, 'y': -0.0}, {('y', 'x'): -1.0}, -0.0, {'x': 'INTEGER', 'y': 'INTEGER'}, dtype='float64')

【5】CQMオブジェクトの生成と目的関数のセット

「CQMオブジェクト」を作成し、作成した「目的関数:f」をセットする。

■CQMオブジェクトの生成と目的関数の設定

# CQMオブジェクトの作成
cqm = dimod.ConstrainedQuadraticModel()

# 作成した目的関数をセットする
cqm.set_objective( f )

※CQMオブジェクトの詳細は以下参照。

【6】CQMに制約条件を設定する

CQM」では「制約条件の表記のままで設定」する。制約条件に対応する数式(ペナルティ関数)にする必要はない。

■CQMに制約条件を設定する

# CQMに制約条件を設定する
# 制約条件の表記のまま設定してOK
cqm.add_constraint(2*x + 2*y <= 8, label="my constraint")

▲今回は「add_constraint()」で設定した。

※これは「add_constraint_from_model()」「add_constraint_from_comparison()」「add_constraint_from_iterable()」のwrapper関数。

ここまでで、「CQMオブジェクト」に対して「目的関数」と「制約条件」の設定が完了した。あとはSampler経由で量子アニーリング計算をすればいい。

【7】Sampler経由で量子アニーリング計算する

※再掲:Samplerオブジェクト
「D-Wave(Ocean SDK)」では、「解きたい問題に対するパラメータを設定したModel」を「Samplerオブジェクト」に渡す。これは大きく分けて5つある。

作成した「CQM」に対応する「Sampler」は「LeapHybridCQMSampler」である。

LeapHybridCQMSampler

これを使って量子アニーリング計算をする

from dwave.system.samplers import LeapHybridCQMSampler # CQM系で使うSamplerオブジェクト

# トークンを仕込んだSamplerにmodelを投げ込んで計算する
sampler = LeapHybridCQMSampler(token=MY_DWAVE_TOKEN )
sampleset = sampler.sample_cqm(cqm)

【8】結果を確認する

「Sampler」経由で「D-Wave上のSolver」が計算した結果は「SampleSetオブジェクト」として返ってくる。

■SampleSetオブジェクトについて

print(sampleset.first)

【実行結果】
Sample(sample={'x': 2.0, 'y': 2.0}, energy=-4.0, num_occurrences=1, is_feasible=True, is_satisfied=array([ True]))

▲一辺の長さがそれぞれ(x, y) = (2, 2)、面積:4と計算された(正解)

【9】全体コード

最後に全体コードを示しておく

【例題】
・目的関数: x * y  ← できるだけ大きくしたい
・制約条件:
   2 * x + 2 * y ≦ 8 ※紐の長さの上限
   0 ≦ x ≦ 4 ※横1辺の長さの上限
   0 ≦ y ≦ 4 ※縦1辺の長さの上限

【実行環境】
D-Wave Leap + Google Colab

■事前準備:ライブラリのインストール

#ライブラリのインストール
!pip install dwave-ocean-sdk

■ここからが全体コード

import dimod
from dwave.system.samplers import LeapHybridCQMSampler # CQM系で使うSamplerオブジェクト
from dwave.cloud import Client



# Sampler埋め込み用トークン(本番では行わないこと)
# 各自登録して取得したアクセストークンを指定する
MY_DWAVE_TOKEN = "XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX"


# トークンを使った接続確認
# 使用可能なSolver情報を出力する
client = Client.from_config(token=MY_DWAVE_TOKEN)
print(client.get_solvers())


# 変数の定義
x = dimod.Integer('x',upper_bound=4) # 下限デフォルト値0以上なので省略
y = dimod.Integer('y',upper_bound=4)


# 目的関数の作成(dimodの変数利用)
f = -(x*y)
print(f)


# CQMオブジェクトの作成
cqm = dimod.ConstrainedQuadraticModel()

# 作成した目的関数をセットする
cqm.set_objective( f )


# トークンを仕込んだSamplerにmodelを投げ込んで計算する
sampler = LeapHybridCQMSampler(token=MY_DWAVE_TOKEN )
sampleset = sampler.sample_cqm(cqm)

# 結果の確認
print(sampleset.first)

【実行結果】
Sample(sample={'x': 2.0, 'y': 2.0}, energy=-4.0, num_occurrences=1, is_feasible=True, is_satisfied=array([ True]))


もっと応援したいなと思っていただけた場合、よろしければサポートをおねがいします。いただいたサポートは活動費に使わせていただきます。