Evaluating Expressions

Evaluation using a dict

The value of an expression can simply be computed by providing an assignment of values to all variables as a dict mapping variables to values. A dict carries a list of (variable, value) pairs that together specify a complete assignment.

The following program computes the function $f(x,y,z)$ for $(x,y,z)=(0,1,1)$:

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))

In this program, the dict ml = {x: 0, y: 1, z: 1} defines the assignment $x=0$, $y=1$, $z=1$, and f(ml) returns the value of $f(0,1,1)$. This program displays the following output:

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

Alternatively, we can provide an assignment directly as a dict literal, or as a list of (variable, value) tuples:

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)]))

The dict form and the list-of-tuples form are equivalent and return the same result.

Native integer variables (qbpp.var(..., integer=(l, u))) can also be used as keys. The value must lie within the declared range; an out-of-range value raises ValueError. See Native integer variables for an example.

Expressions with constraints (qbpp.cons()) or with qbpp.abs, qbpp.relu, qbpp.max, and qbpp.min are evaluated the same way. The value is the same as with a qbpp.Sol: the objective plus the value of each constraint (weight × violation squared). Every variable that the expression uses must be in the dict, including variables that appear only inside a constraint. A missing variable raises ValueError.

Evaluation using Sol

A solution (Sol) can also be used to evaluate the value of an expression. To do this, we first construct a solution sol associated with a given expression f. The newly created solution is initialized with the all-zero assignment.

Using the sol.set(x, value) method, we can assign values to individual variables. Then, both f(sol) and sol(f) return the value of the expression f under the assignment stored in sol. Furthermore, the comp_energy() method computes and returns the same value.

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())

Note that the method comp_energy() of a solution sol computes the energy value and caches it inside the solution. In addition, a solution returned by a solver already has its energy value computed and cached. To retrieve the energy without recomputing it, you can use the energy property, as shown below:

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)

In this program, sol.energy correctly returns 0. However, after flipping the variable z, the cached energy value becomes invalid. Printing via print(sol) still succeeds, because it computes the energy of the current assignment on the fly for display (the cache stays invalid). Accessing sol.energy while the cache is invalid, however, results in a runtime error (RuntimeError) (in this example it occurs at the last print), as shown below:

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.

To resolve this issue, you must explicitly recompute the energy by calling sol.comp_energy() after modifying the solution, as follows:

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)

This program produces the following output:

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

After calling comp_energy(), the sol.energy property also returns the correct (recomputed) energy.


Back to top

Page last modified: 2026.10.18.

© 2026 Koji Nakano, Hiroshima University