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.
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. Accessing sol.energy without recomputing the energy therefore results in a runtime error, as shown below:
sol = 0:{{x,1},{y,0},{z,1}}
energy = 0
RuntimeError: energy is not up to date; call comp_energy() after modifying the solution
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 = 0:{{x,1},{y,0},{z,1}}
energy = 0
sol.comp_energy() = 9
flipped sol = 9:{{x,1},{y,0},{z,0}}
flipped energy = 9
After calling comp_energy(), the sol.energy property also returns the correct (recomputed) energy.