HUBO の QUBO への変換¶
HUBO(高次の制約なし二値最適化)の式は 3 次以上の項を含みうるのに対し, QUBO(二次の制約なし二値最適化)の式は次数が 2 以下に制限されます.
Hi-QUBO 同梱の 3 つのソルバー(EasySolver・ExhaustiveSolver・ABS3Solver)は
HUBO をそのまま扱えるため変換は不要です.しかし,外部のバックエンドの多く —
物理アニーラや Gurobi・SCIP・D-Wave などの QUBO 専用オプティマイザ — は二次の
モデルしか受け付けません.これらに渡すには,HUBO を等価な QUBO に書き換える
必要があります.
reduce() 関数はこの変換を行います.次数が 2 を超えるすべての項を,新しい
補助二値変数を導入して次数 2 以下の式に書き換えます.
reduce() 関数
reduce() はグローバル関数(非破壊)とメンバ関数(in-place)の両方が提供されます.
qbpp::reduce(f)はfと等価な次数 2 以下の新しい式を返します.単一の積項もqbpp::reduce(2*a*b*c*d)のように直接渡せます(暗黙変換).f.reduce()はfをその場で更新して返します.qbpp::reduce(a)は式のArrayも受け付けます(要素ごとに変換).
#include <qbpp/qbpp.hpp>
int main() {
auto a = qbpp::var("a");
auto b = qbpp::var("b");
auto c = qbpp::var("c");
qbpp::Expr f = a * b * c; // 3 次(cubic)の HUBO 項
qbpp::Expr g = qbpp::reduce(f); // 等価な QUBO
std::cout << "f = " << f << std::endl;
std::cout << "g = " << g << std::endl;
std::cout << "max_degree = " << g.max_degree() << std::endl;
}
このプログラムの出力:
f = a*b*c
g = {r0} +a*b +a*c -a*{r0} +b*c -b*{r0} -c*{r0}
max_degree = 2
3 次の項 a*b*c が二次の式になりました.{r0} は新たに導入された補助二値変数です.
reduce() は補助変数を {r0}・{r1}・… と波括弧付きで自動命名するため,ユーザー
変数と衝突しません.
等価性の保証
reduce() は最適値を保存します.元の変数の任意の割当 x に対して,変換後の式を
補助変数について最小化した値が,元の HUBO の値に一致します.
したがって g の大域最小値(元の変数と補助変数をあわせた全体の最小値)は f の大域
最小値に等しく,g の最小解を元の変数に射影したものは f の最小解になります.これに
より,reduce(f) を QUBO 専用バックエンドに渡し,結果を元の変数で読み取ることが安全
にできます.
変換の仕組み
reduce() は高次の各項を独立に書き換えます.次数 \(d\) の項
\(c\,x_1 x_2 \cdots x_d\) を考え,\(S = x_1 + x_2 + \cdots + x_d\) をその変数のうち
1 である個数とします.
正の係数(\(c > 0\))— Ishikawa の規則
補助整数 \(a \in [0, d-2]\) を導入し(内部的には約 \(\lfloor \log_2 (d-1) \rfloor\) 個の二値変数で表現),項を次のように書き換えます.
\(\tfrac{(S-a)(S-a-1)}{2} = \binom{S-a}{2}\) は \(S-a \in \lbrace 0, 1\rbrace\) のとき 0,それ以外では正になります.したがって \(a\) について最小化すると,元の積が再現 されます.
すべての変数が 1(\(S = d\))のとき,補助整数は最大でも \(a = d-2\) までしか取れず, \(S-a = 2\) で値 \(\binom{2}{2} = 1\) — 積に等しい.
どれかの変数が 0(\(S < d\))のとき,補助整数は \(a = S\) または \(a = S-1\) を取れて, \(\binom{0}{2} = \binom{1}{2} = 0\) — 積に等しい.
上限が \(d-2\) である(補助整数が \(d-1\) や \(d\) に届かない)ことが,すべての変数が 1 の ときに値を 1 に強制する鍵です.
上の cubic の例(\(d = 3\))では,補助整数は単一の二値変数 {r0}(範囲 \([0,1]\))です.
上で出力した変換後の式 g について {r0} を最小化すると,任意の割当で \(a\,b\,c\) が
再現されます.
\((a,b,c)\) の 1 の個数 |
\(a\,b\,c\) |
|
|
|
|---|---|---|---|---|
0 |
0 |
0 |
1 |
0 |
1 |
0 |
0 |
0 |
0 |
2 |
0 |
1 |
0 |
0 |
3 |
1 |
3 |
1 |
1 |
補助変数はオプティマイザにとって「自由」で,エネルギーを最小にする値に落ち着きます. その値において,変換後の項は元の積に等しくなります.
負の係数(\(c < 0\))— Freedman の規則
負の項は 1 個の補助二値変数 \(w\) だけで済みます.
\(c < 0\) なので,最小化は \(|c|\,w\,(S-(d-1))\) の最大化を意味します.すべての変数が 1 の とき,\(S-(d-1) = 1\) で \(w = 1\) を選ぶと \(c\) — 積に等しい.それ以外では \(S-(d-1) \le 0\) なので \(w = 0\) で 0 — やはり積に等しくなります.
補助変数の個数
reduce() は補助変数を少なく抑えます.
項 |
規則 |
補助二値変数 |
|---|---|---|
正係数・次数 \(d\) |
Ishikawa |
約 \(\lfloor \log_2 (d-1) \rfloor\) 個(\(d=3,4\) で 1,\(d=5\text{–}8\) で 2,\(d=9\text{–}16\) で 3) |
負係数・任意の次数 |
Freedman |
ちょうど 1 個 |
否定リテラル
reduce() は否定リテラルを自動的に扱います.変換後の QUBO は正リテラルのみになります.
例えば 4 次の項 ~a*b*c*d は,正リテラルと 1 個の補助変数からなる二次式に変換されます.
qbpp::Expr p = (~a) * b * c * d;
qbpp::Expr q = qbpp::reduce(p); // 正リテラル + 補助変数 1 個
in-place 形式・配列形式
f.reduce() は f をその場で変換します.式の Array に対しては
qbpp::reduce(arr)(または arr.reduce())が各要素を変換し,Array<Dim, Expr> を
返します.
HUBO(高次の制約なし二値最適化)の式は 3 次以上の項を含みうるのに対し, QUBO(二次の制約なし二値最適化)の式は次数が 2 以下に制限されます.
PyQBPP 同梱のネイティブソルバー(EasySolver・ExhaustiveSolver・ABS3Solver)は
HUBO をそのまま扱えるため変換は不要です.しかし,外部のバックエンドの多く —
物理アニーラや Gurobi・SCIP・D-Wave などの QUBO 専用オプティマイザ — は二次の
モデルしか受け付けません.これらに渡すには,HUBO を等価な QUBO に書き換える
必要があります.
reduce() 関数はこの変換を行います.次数が 2 を超えるすべての項を,新しい
補助二値変数を導入して次数 2 以下の式に書き換えます.
reduce() 関数
reduce() はグローバル関数(非破壊)とメンバ関数(in-place)の両方が提供されます.
qbpp.reduce(f)はfと等価な次数 2 以下の新しい式を返します.f.reduce()はfをその場で更新して返します(in-place 形式はExprのみ.積の項Termはグローバル形式qbpp.reduce(...)に渡します).qbpp.reduce(a)は式の配列も受け付けます(要素ごとに変換).
import pyqbpp as qbpp
a = qbpp.var("a")
b = qbpp.var("b")
c = qbpp.var("c")
f = a * b * c # 3 次(cubic)の HUBO 項
g = qbpp.reduce(f) # 等価な QUBO
print("f =", f)
print("g =", g)
print("max_degree =", g.max_degree)
このプログラムの出力:
f = a*b*c
g = {r0} +a*b +a*c -a*{r0} +b*c -b*{r0} -c*{r0}
max_degree = 2
3 次の項 a*b*c が二次の式になりました.{r0} は新たに導入された補助二値変数です.
reduce() は補助変数を {r0}・{r1}・… と波括弧付きで自動命名するため,ユーザー
変数と衝突しません.
等価性の保証
reduce() は最適値を保存します.元の変数の任意の割当 x に対して,変換後の式を
補助変数について最小化した値が,元の HUBO の値に一致します.
したがって g の大域最小値(元の変数と補助変数をあわせた全体の最小値)は f の大域
最小値に等しく,g の最小解を元の変数に射影したものは f の最小解になります.これに
より,reduce(f) を QUBO 専用バックエンドに渡し,結果を元の変数で読み取ることが安全
にできます.
変換の仕組み
reduce() は高次の各項を独立に書き換えます.次数 \(d\) の項
\(c\,x_1 x_2 \cdots x_d\) を考え,\(S = x_1 + x_2 + \cdots + x_d\) をその変数のうち
1 である個数とします.
正の係数(\(c > 0\))— Ishikawa の規則
補助整数 \(a \in [0, d-2]\) を導入し(内部的には約 \(\lfloor \log_2 (d-1) \rfloor\) 個の二値変数で表現),項を次のように書き換えます.
\(\tfrac{(S-a)(S-a-1)}{2} = \binom{S-a}{2}\) は \(S-a \in \lbrace 0, 1\rbrace\) のとき 0,それ以外では正になります.したがって \(a\) について最小化すると,元の積が再現 されます.
すべての変数が 1(\(S = d\))のとき,補助整数は最大でも \(a = d-2\) までしか取れず, \(S-a = 2\) で値 \(\binom{2}{2} = 1\) — 積に等しい.
どれかの変数が 0(\(S < d\))のとき,補助整数は \(a = S\) または \(a = S-1\) を取れて, \(\binom{0}{2} = \binom{1}{2} = 0\) — 積に等しい.
上限が \(d-2\) である(補助整数が \(d-1\) や \(d\) に届かない)ことが,すべての変数が 1 の ときに値を 1 に強制する鍵です.
上の cubic の例(\(d = 3\))では,補助整数は単一の二値変数 {r0}(範囲 \([0,1]\))です.
上で出力した変換後の式 g について {r0} を最小化すると,任意の割当で \(a\,b\,c\) が
再現されます.
\((a,b,c)\) の 1 の個数 |
\(a\,b\,c\) |
|
|
|
|---|---|---|---|---|
0 |
0 |
0 |
1 |
0 |
1 |
0 |
0 |
0 |
0 |
2 |
0 |
1 |
0 |
0 |
3 |
1 |
3 |
1 |
1 |
補助変数はオプティマイザにとって「自由」で,エネルギーを最小にする値に落ち着きます. その値において,変換後の項は元の積に等しくなります.
負の係数(\(c < 0\))— Freedman の規則
負の項は 1 個の補助二値変数 \(w\) だけで済みます.
\(c < 0\) なので,最小化は \(|c|\,w\,(S-(d-1))\) の最大化を意味します.すべての変数が 1 の とき,\(S-(d-1) = 1\) で \(w = 1\) を選ぶと \(c\) — 積に等しい.それ以外では \(S-(d-1) \le 0\) なので \(w = 0\) で 0 — やはり積に等しくなります.
補助変数の個数
reduce() は補助変数を少なく抑えます.
項 |
規則 |
補助二値変数 |
|---|---|---|
正係数・次数 \(d\) |
Ishikawa |
約 \(\lfloor \log_2 (d-1) \rfloor\) 個(\(d=3,4\) で 1,\(d=5\text{–}8\) で 2,\(d=9\text{–}16\) で 3) |
負係数・任意の次数 |
Freedman |
ちょうど 1 個 |
否定リテラル
reduce() は否定リテラルを自動的に扱います.変換後の QUBO は正リテラルのみになります.
例えば 4 次の項 ~a*b*c*d は,正リテラルと 1 個の補助変数からなる二次式に変換されます.
p = (~a) * b * c * d
q = qbpp.reduce(p) # 正リテラル + 補助変数 1 個
in-place 形式・配列形式
f.reduce() は f をその場で変換します.式の配列に対しては
qbpp.reduce(arr)(または arr.reduce())が各要素を変換します.