# HUBO の QUBO への変換 :::{container} prog-cpp **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` も受け付けます(要素ごとに変換). ```{literalinclude} /../programFiles/cppPrograms/advanced/hubo_to_qubo-program1.cpp :language: cpp :caption: hubo_to_qubo-program1.cpp ``` このプログラムの出力: ```{include} /../programFiles/markDown/advanced/hubo_to_qubo.md :start-after: :end-before: ``` 3 次の項 `a*b*c` が二次の式になりました.`{r0}` は新たに導入された補助二値変数です. `reduce()` は補助変数を `{r0}`・`{r1}`・… と波括弧付きで自動命名するため,ユーザー 変数と衝突しません. ## 等価性の保証 `reduce()` は最適値を保存します.**元の**変数の任意の割当 `x` に対して,変換後の式を **補助**変数について最小化した値が,元の HUBO の値に一致します. $$ f(x) = \min_{\text{aux}} g(x, \text{aux}) $$ したがって `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$ 個の二値変数で表現),項を次のように書き換えます. $$ c\,x_1 x_2 \cdots x_d \;=\; \frac{c\,(S-a)(S-a-1)}{2}. $$ $\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$ | `{r0}`=0 の $g$ | `{r0}`=1 の $g$ | `{r0}` について $\min$ | |:---:|:---:|:---:|:---:|:---:| | 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\,x_1 x_2 \cdots x_d \;=\; c\,w\,\bigl(S - (d-1)\bigr). $$ $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 個の補助変数からなる二次式に変換されます. ```{include} /../programFiles/markDown/advanced/hubo_to_qubo.md :start-after: :end-before: ``` ## in-place 形式・配列形式 `f.reduce()` は `f` をその場で変換します.式の `Array` に対しては `qbpp::reduce(arr)`(または `arr.reduce()`)が各要素を変換し,`Array` を 返します. ::: :::{container} prog-python **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)`** は式の配列も受け付けます(要素ごとに変換). ```{literalinclude} /../programFiles/pythonPrograms/advanced/hubo_to_qubo-program1.py :language: python :caption: hubo_to_qubo-program1.py ``` このプログラムの出力: ```{include} /../programFiles/markDown/advanced/hubo_to_qubo.md :start-after: :end-before: ``` 3 次の項 `a*b*c` が二次の式になりました.`{r0}` は新たに導入された補助二値変数です. `reduce()` は補助変数を `{r0}`・`{r1}`・… と波括弧付きで自動命名するため,ユーザー 変数と衝突しません. ## 等価性の保証 `reduce()` は最適値を保存します.**元の**変数の任意の割当 `x` に対して,変換後の式を **補助**変数について最小化した値が,元の HUBO の値に一致します. $$ f(x) = \min_{\text{aux}} g(x, \text{aux}) $$ したがって `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$ 個の二値変数で表現),項を次のように書き換えます. $$ c\,x_1 x_2 \cdots x_d \;=\; \frac{c\,(S-a)(S-a-1)}{2}. $$ $\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$ | `{r0}`=0 の $g$ | `{r0}`=1 の $g$ | `{r0}` について $\min$ | |:---:|:---:|:---:|:---:|:---:| | 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\,x_1 x_2 \cdots x_d \;=\; c\,w\,\bigl(S - (d-1)\bigr). $$ $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 個の補助変数からなる二次式に変換されます. ```{include} /../programFiles/markDown/advanced/hubo_to_qubo.md :start-after: :end-before: ``` ## in-place 形式・配列形式 `f.reduce()` は `f` をその場で変換します.式の配列に対しては `qbpp.reduce(arr)`(または `arr.reduce()`)が各要素を変換します. :::