クイックリファレンス: 整数変数と制約に関する演算と関数
qbpp::Expr は通常の式・整数変数・制約式を 統一的に表現する型 です。3 つとも同じ型で、持つメタデータが違うだけです。
Expr は 3 つの「顔」を持ちます:
- 通常の式 (多項式):
x + 2*y*zなどの算術式 - 整数変数:
l <= qbpp::var_int("x") <= uなどで作ったExpr。範囲・ビット分解のメタデータを保持 - 制約式:
e == 5/lo <= e <= hiなどで作ったExpr。penalty + body のメタデータを保持
基本原則
qbpp::Exprの演算・関数はすべて、どの「顔」の Expr にも適用できる- 固有 accessor (
min_val(),body等) は対応する「顔」の Expr でのみ有効。合わない Expr で呼ぶと runtime abort(エラーメッセージ付き)- 式を変更するメンバ関数 (
+=,-=,*=,/=,sqr(),replace()) を呼ぶと、内部表現は通常の式に戻る(固有メタデータは破棄され、固有 accessor は以後呼べなくなる)- 例外:
simplify*()は固有メタデータを保ったまま、保持している式のみを簡約する。「顔」は維持されるis_varint()/is_exprexpr()で現在の「顔」を実行時に確認できる
1. 整数変数
生成
| 構文 | 戻り値 |
|---|---|
l <= qbpp::var_int("x") <= u | Expr (範囲 [l, u] の整数変数) |
l <= qbpp::var_int("x", s1, s2, ...) <= u | 整数変数の多次元配列 Array<Dim, Expr> |
使える演算・関数
| カテゴリ | 例 | 戻り値 | 備考 |
|---|---|---|---|
| 単項 | -vi | Expr | |
| 算術 (右辺 Expr) | vi + 1, vi * 2, vi - x | Expr | |
| 算術 (右辺 整数変数) | vi1 + vi2, vi1 * vi2 | Expr | |
| 比較 (== int) | vi == 5 | Expr (制約式) | 制約生成 |
| 比較 (範囲) | 2 <= vi <= 5 | Expr (制約式) | between 制約 |
| グローバル関数 | qbpp::sqr(vi), qbpp::simplify(vi), qbpp::sqr(vi - 3) | Expr | |
| 整数変数固有メタ情報 | vi.min_val(), vi.max_val() | coeff_t | read-only |
| 整数変数固有構造メンバ | vi.var_count(), vi.coeff(i), vi.get_var(i), vi[i] | 各種 | read-only |
| 配列アクセス | vi.vars(), vi.coeffs() | Array<1, ...> | read-only |
| 複合代入 | vi += 1, vi -= 1, vi *= 2, vi /= 2 | Expr& | 以後は通常の式相当(固有メタは破棄) |
| 二乗 | vi.sqr() | Expr& | 以後は通常の式相当 |
| 置換 | vi.replace(ml) | Expr& | 以後は通常の式相当 |
| in-place 簡約 | vi.simplify(), vi.simplify_as_binary(), vi.simplify_as_spin() | Expr& | 保持式のみ簡約、整数変数のメタは保たれる |
| 代入 | vi = other | Expr& | 同じ型 |
注意:
vi += 1等の式変更系を呼んだ後、静的な型は引き続きqbpp::Exprですが内部状態は通常の式相当です。vi.min_val()等の整数変数固有 accessor は runtime error になります。
2. 制約式
生成
| 構文 | 戻り値 | 意味 (penalty / body) |
|---|---|---|
f == n | Expr (制約式) | penalty = sqr(f - n), body = f |
l <= f <= u | Expr (制約式) | penalty = (f-a)(f-(a+1)) (a は slack), body = f |
ここで f は整数変数以外の式 (Var, Term, Expr, 整数変数 Expr)。
使える演算・関数
| カテゴリ | 例 | 戻り値 | 備考 |
|---|---|---|---|
| 単項 | -ee | Expr | penalty を反転 |
| 算術 (右辺 Expr) | ee + 1, ee * 2, ee + x | Expr | |
| 算術 (右辺 制約式) | ee1 + ee2, ee * ee | Expr | penalty 同士 |
| グローバル関数 | qbpp::sqr(ee), qbpp::simplify_as_binary(ee), qbpp::replace(ee, ml) | Expr | penalty に適用 |
| body 取得 | ee.body() | Expr | clone |
| 解での評価 | sol(ee) (penalty を評価), ee.body(sol) (body を評価) | coeff_t | 制約満足度の検証に使用 |
| 複合代入 | ee += 1, ee -= 1, ee *= 2, ee /= 2 | Expr& | 以後は通常の式相当(body 参照不可に) |
| 二乗 | ee.sqr() | Expr& | 以後は通常の式相当 |
| 置換 | ee.replace(ml) | Expr& | 以後は通常の式相当 |
| in-place 簡約 | ee.simplify(), ee.simplify_as_binary(), ee.simplify_as_spin() | Expr& | penalty と body を同時に簡約、制約式のまま |
| 代入 | ee = other | Expr& | 同じ型 |
注意:
ee += 1等の式変更系を呼ぶと penalty のみが更新され body は参照できなくなります。一方ee.simplify*()は penalty と body 両方に同じ rule を適用するため、制約式の整合性が保たれます。
Python との挙動の違い: Python では
+=等の複合代入は silent rebind (新しいExprに再束縛)。一方 C++ は同じ object の内部状態が通常の式相当に変わります。詳細は QUBO++ (C++) と PyQBPP (Python) の違い を参照。
3. グローバル関数: 新しい Expr を返す
整数変数・制約式を引数に取れる主要なグローバル関数。いずれも引数を変更せず、新しい qbpp::Expr を返します:
| 関数 | 戻り値 | 説明 |
|---|---|---|
qbpp::sqr(x) | Expr | x * x |
qbpp::simplify(x) | Expr | 同類項マージ |
qbpp::simplify_as_binary(x) | Expr | binary (0/1) ルールで簡約 |
qbpp::simplify_as_spin(x) | Expr | spin (±1) ルールで簡約 |
qbpp::replace(x, ml) | Expr | 変数置換 |
引数 x は Var, Term, 任意の顔の Expr のいずれでも OK (内部では Expr として扱われる)。
4. 配列版
整数変数の配列および制約式の配列も同じ性質:
- 算術では各要素が
Exprとして扱われる → 結果はArray<Dim, Expr> - in-place mutator (
+=,*=等) は使えるが、要素ごとに上記と同じく通常の式相当に戻る
// 整数変数の配列
auto x = 0 <= qbpp::var_int("x", 3) <= 7; // Array<1, Expr> の整数変数配列
auto sum = qbpp::sum(x); // Expr (各要素を Expr として合計)
// 制約式の配列 (要素ごとの制約)
auto m = qbpp::var("m", 3, 4); // Array<2, Var>
auto rows = qbpp::vector_sum(m, 0); // Array<1, Expr> (各行の和)
auto onehot = (rows == 1); // Array<1, Expr> の制約式
auto penalty = qbpp::sum(onehot); // Expr (全制約の合計)
要素ごとの body アクセスは *arr[i]。