クイックリファレンス: 解
qbpp::Sol クラスは QUBO 問題の解を表します。 変数の割り当て(パックされたビット配列)とエネルギー値、求解時間を格納します。
Sol の作成
| 式 | 説明 |
|---|---|
Sol(expr) | 式 expr に対する、すべての変数が 0 の解を作成(ネイティブ整数変数は下限の値) |
変数・項・式の評価
解 sol が与えられたとき、変数値や式の結果は2つの等価な呼び出し方法で取得できます:
| 式 | 等価 | 戻り値の型 | 説明 |
|---|---|---|---|
sol(x) | x(sol) | energy_t | Var x を評価(0 または 1 を返す) |
sol.get(x) | sol(x) | coeff_t | Var x の値(0 または 1)を取得。ネイティブ整数変数なら整数値(energy_t) |
sol(vi) | vi(sol) | energy_t | 整数変数 vi を評価(整数値) |
sol(t) | t(sol) | energy_t | Term t を評価 |
sol(f) | f(sol) | energy_t | Expr f を評価 |
sol(c) | c(sol) | energy_t | 制約式 c のペナルティを評価 |
sol(arr) | arr(sol) | Array<coeff_t> | 変数(Var)の配列、ネイティブ整数変数の配列、式(Term・Expr)の配列を評価(同じ形) |
sol(x) と x(sol) は同じ結果を返します。 x(sol) 形式は配列で使いやすく、サイズ $n \times m$ の Var 配列 x の場合、 x(sol) はサイズ $n \times m$ の割り当て値(0 または 1)を含む Array<coeff_t> を返します。 式の配列(Array<Dim, Expr>)も、sol(arr) / arr(sol) で各要素を評価した同じ形の Array<coeff_t> を返します(部分配列 arr[i] にも使えます)。 値が coeff_t に収まらない要素があると例外 std::overflow_error になるので、その場合は要素ごとに arr[i](sol)(energy_t)で評価します。
解のモデル(ソルバーに渡した式)に含まれていない変数を読み出したり、そのような変数を含む式を評価したりすると、エラー(例外 std::runtime_error)になります。 replace で値を固定した変数は、固定したあとの式の解には含まれないので、Sol(f).set(sol, ml) で元の式 f の解に戻してから読み出します。 sol.has(x) で、変数がモデルに含まれているか確認できます。
変数値の設定
| 式 | 説明 |
|---|---|
sol.set(x, value) | 変数 x を value(0 または 1,ネイティブ整数変数なら整数)に設定 |
sol.flip(x) | バイナリ変数 x の値を反転(0 ↔ 1)。格納されたエネルギーは無効になる。ネイティブ整数変数には使えない |
sol.set(other) | 別の Sol からすべての変数値をコピー |
sol.set(ml) | MapList から変数値を設定 |
sol.set(other, ml) | other からコピーし、MapList ml を適用 |
MapList は (Var, 値) または (Expr, 値) (この Expr は整数変数) のペアのリストです:
qbpp::MapList ml = {{x[0], 1}, {x[1], 0}, {vi, 5}};
sol.set(ml);
set() は Sol& を返します。replace で変数を固定した式の解 sol から元の式 f の解を作るには、set(sol, ml) を使います:
auto full_sol = qbpp::Sol(f).set(sol, ml);
ml の値には ~x[1] のような式も書け、その値は sol の変数値で評価されます。 解を渡さない set(ml) では値は定数でなければならず、式を書くと std::invalid_argument 例外が投げられます。
エネルギーと評価
| 式 | 戻り値の型 | 説明 |
|---|---|---|
sol.energy() | energy_t | 格納されたエネルギー値を返す |
sol.comp_energy() | energy_t | エネルギーを返す(無効な場合のみ現在の変数値から再計算して格納) |
sol.tts() | double | 求解時間(秒) |
sol.energy() はソルバーが解を見つけた時点で格納されたエネルギー値を返します。 エネルギーの再計算は行いません。 sol.set() や sol.flip() で変数値を変更した後は、格納されたエネルギーは無効になります。 この状態で sol.energy() を呼び出すとエラーが発生します。 sol.comp_energy() を呼んでエネルギーを再計算・更新してからアクセスしてください。 エネルギーが既に有効な場合、comp_energy() は再計算せずにキャッシュ値を返します。
解からの整数の抽出
onehot_to_int() — ワンホットデコード
| 式 | 戻り値の型 | 説明 |
|---|---|---|
onehot_to_int(sol(x)) | Array<coeff_t> | 最後の軸に沿ってワンホットをデコード(デフォルト) |
onehot_to_int(sol(x), k) | Array<coeff_t> | 軸 $k$ に沿ってワンホットをデコード |
指定された軸に沿ってデコードし、次元が1つ少ない配列を返します。 1 次元の配列を渡したときは、配列ではなく coeff_t のスカラー(1 の位置)を返します(軸は指定しません)。 出力形状は入力形状から軸 $k$ を除いたもので、各要素はその軸に沿った1のインデックスです。 負のインデックスもサポートされています(例: -1 = 最後の軸)。 有効なワンホットベクトルでないスライス(ちょうど1つの1を含まない)に対しては $-1$ を返します。
詳細と例については ワンホットから整数への変換 を参照してください。
ソルバー情報
ソルバーの結果クラス(EasySolverSol、ExhaustiveSolverSol、ABS3SolverSol)は Sol を継承し、 info() で追加情報を提供します。
| 式 | 戻り値の型 | 説明 |
|---|---|---|
sol.info() | const KeyValueVector& | ソルバー情報のキーバリューペア |
sol.info().get(key) | const char* | キーに対応する値(無ければ nullptr) |
sol.sols | const std::vector<Sol>& | 収集された全解 |
sol.size() | size_t | 収集された解の数 |
sol.sols[i] | const Sol& | $i$ 番目の解にアクセス |
info() オブジェクトはソルバーのメタデータを文字列のキーバリューペアとして格納しています。 代表的なキー:
| キー | 説明 |
|---|---|
"flip_count" | 実行された変数フリップの総数(ABS3Solver では "total_flip_count") |
"var_count" | モデルの変数数 |
"term_count" | モデルの項数 |
"version" | QUBO++ のバージョン |
"cpu_name" | CPU モデル名 |
"hostname" | マシンのホスト名 |
返されるキーはソルバーによって異なります。たとえば ExhaustiveSolver は flip_count・version・cpu_name・hostname を返しません(ネイティブ整数変数を含むモデルでは var_count・term_count も返しません)。 info().get(key) はキーが無ければ nullptr を返します。 全エントリを反復処理するか、キーでアクセスできます:
for (const auto& kv : sol.info()) {
std::cout << kv.key << " = " << kv.value << std::endl;
}
std::cout << "var_count = " << sol.info().get("var_count") << std::endl;
出力
Sol オブジェクトは std::cout で出力できます:
std::cout << sol << std::endl;
変数の割り当てを含む人間が読みやすい形式で出力されます。 表示の例は クイックリファレンス: 表示 を参照してください。