式の評価

辞書を使った評価

式の値は、すべての変数への値の割り当てを {変数: 値} の辞書として与えることで簡単に計算できます。 辞書は完全な割り当てを構成する (変数, 値) のペアのリストを保持します。

以下のプログラムは、$(x,y,z)=(0,1,1)$ に対して関数 $f(x,y,z)$ を計算します:

import pyqbpp as qbpp

x = qbpp.var("x")
y = qbpp.var("y")
z = qbpp.var("z")
f = qbpp.sqr(x + 2 * y + 3 * z - 3)

ml = {x: 0, y: 1, z: 1}

print("assignment =", ml)
print("f(0,1,1) =", f(ml))

このプログラムでは、辞書 ml = {x: 0, y: 1, z: 1} で $x=0$, $y=1$, $z=1$ の割り当てを定義し、 f(ml) が $f(0,1,1)$ の値を返します。 このプログラムの出力は以下の通りです:

assignment = {x: 0, y: 1, z: 1}
f(0,1,1) = 4

あるいは、辞書リテラルとして直接、または (変数, 値) のタプルのリストとして 割り当てを与えることもできます:

import pyqbpp as qbpp

x = qbpp.var("x")
y = qbpp.var("y")
z = qbpp.var("z")
f = qbpp.sqr(x + 2 * y + 3 * z - 3)

print("f(0,1,1) =", f({x: 0, y: 1, z: 1}))
print("f(0,1,1) =", f([(x, 0), (y, 1), (z, 1)]))

辞書形式とタプルのリスト形式は等価で、同じ結果を返します。

ネイティブ整数変数(qbpp.var(..., integer=(l, u)))もキーに指定できます。 値は宣言した範囲内でなければならず、範囲外の値は ValueError になります。 例はネイティブ整数変数を参照してください。

制約(qbpp.cons())や qbpp.abs・qbpp.relu・qbpp.max・qbpp.min を含む式も同じように評価できます。 値は qbpp.Sol での評価と同じで、目的関数に各制約の値(重み × 違反量の 2 乗)を足したものです。 式が使う変数は、制約の中だけに現れる変数も含めて、すべて辞書に入れる必要があります。 足りない変数があると ValueError になります。

Solを使った評価

解(Sol)を使って式の値を評価することもできます。 そのためには、まず与えられた式 f に関連付けた解 sol を構築します。 新しく作成された解は、すべてゼロの割り当てで初期化されます。

sol.set(x, value) メソッドで、個々の変数に値を割り当てられます。 そして、f(sol) と sol(f) のどちらも、sol に格納された割り当ての下での 式 f の値を返します。 さらに、comp_energy() メソッドも同じ値を計算して返します。

import pyqbpp as qbpp

x = qbpp.var("x")
y = qbpp.var("y")
z = qbpp.var("z")
f = qbpp.sqr(x + 2 * y + 3 * z - 3)
f.simplify_as_binary()

sol = qbpp.Sol(f)
sol.set(y, 1)
sol.set(z, 1)

print("f(0,1,1) =", f(sol))
print("f(0,1,1) =", sol(f))
print("f(0,1,1) =", sol.comp_energy())

解 sol のメソッド comp_energy() はエネルギー値を計算し、 解の内部にキャッシュすることに注意してください。 また、ソルバーが返す解は、既にエネルギー値が計算されキャッシュされています。 再計算せずにエネルギーを取得するには、以下に示すように energy プロパティを使用できます:

import pyqbpp as qbpp

x = qbpp.var("x")
y = qbpp.var("y")
z = qbpp.var("z")
f = qbpp.sqr(x + 2 * y + 3 * z - 4)
f.simplify_as_binary()

solver = qbpp.EasySolver(f)
sol = solver.search(target_energy=0)

print("sol =", sol)
print("energy =", sol.energy)

sol.flip(z)
print("flipped sol =", sol)
print("flipped energy =", sol.energy)

このプログラムでは、sol.energy は正しく 0 を返します。 しかし、変数 z をフリップした後、キャッシュされたエネルギー値は無効になります。 print(sol) による表示は、現在の割当のエネルギーをその場で計算して表示するので成功します (キャッシュは無効のままです)。一方、キャッシュが無効な状態で sol.energy にアクセスすると、 以下のようにランタイムエラー(RuntimeError)が発生します(この例では最後の出力行で発生):

sol = Sol(energy=0, {x: 1, y: 0, z: 1})
energy = 0
flipped sol = Sol(energy=9, {x: 1, y: 0, z: 0})
RuntimeError: Sol.energy: energy is invalid (variable changed after last computation). Call comp_energy() first.

この問題を解決するには、解を変更した後に sol.comp_energy() を呼び出して エネルギーを明示的に再計算する必要があります:

print("sol =", sol)
print("energy =", sol.energy)

sol.flip(z)
print("sol.comp_energy() =", sol.comp_energy())
print("flipped sol =", sol)
print("flipped energy =", sol.energy)

このプログラムは以下の出力を生成します:

sol = Sol(energy=0, {x: 1, y: 0, z: 1})
energy = 0
sol.comp_energy() = 9
flipped sol = Sol(energy=9, {x: 1, y: 0, z: 0})
flipped energy = 9

comp_energy() を呼び出した後は、sol.energy プロパティも正しい(再計算後の) エネルギーを返します。


Back to top

Page last modified: 2026.10.18.

© 2026 中野浩嗣, 広島大学