QAOA MaxSat#
Problem description#
Given \(m\) disjunctive clauses over \(n\) Boolean variables \(x_1,\dotsc,x_n\), where each clause contains at most \(l \geq 2\) literals, find a variable assignment that maximizes the number of satisfied clauses.
For example, for \(l=3\) and \(n=5\) Boolean variables \(x_1,\dotsc,x_5\) consider the clauses
$$(x_1\lor x_2\lor x_3), (\bar x_1\lor x_2\lor x_4), (\bar x_2\lor x_3\lor \bar x_5), (\bar x_3\lor x_4\lor x_5)$$
where \(\bar x_i\) denotes the negation of \(x_i\). What is sought now is an assignment of the variables with 0 (False) or 1 (True) that maximizes the number of satisfied clauses. In this case, the assignment \(x=x_1x_2x_3x_4x_5=01111\) satisfies all clauses.
Given \(n\) variables \(x_1,\dotsc,x_n\) we represent each clause as a list of integers such that the absolute values correspond to the indices of the variables and a minus sign corresponds to negation of the variable. For example, the above clauses correspond to
$$[1,2,3],\quad [-1,2,4],\quad [-2,3,-5],\quad [-3,4,5]$$
The cost function for the maximum satisfyability problem is given by
$$C(x)=\sum_{\alpha=1}^mC_{\alpha}(x)$$
where for each clause \(C_{\alpha}(x)=1\) if the corresponding clause is satisfied and \(C_{\alpha}(x)=0\) otherwise. Here,
$$C_{\alpha}(x)=1-\prod_{j>0}(1-x_j)\prod_{j<0}x_{-j}$$
Cost operator#
- create_maxsat_cost_operator(problem)[source]#
Creates the cost operator for an instance of the maximum satisfiability problem. For a given cost function
\[C(x) = \sum_{\alpha=1}^m C_{\alpha}(x)\]the cost operator is given by \(e^{-i\gamma C}\) where \(C=\sum_x C(x)\ket{x}\bra{x}\).
- Parameters:
- problemtuple(int, list[list[int]])
The number of variables, and the clauses of the maximum satisfiability problem instance.
- Returns:
- cost_operatorfunction
A function receiving a QuantumVariable and a real parameter \(\gamma\). This function performs the application of the cost operator.
Classical cost function#
- create_maxsat_cl_cost_function(problem)[source]#
Creates the classical cost function for an instance of the maximum satisfiability problem.
- Parameters:
- problemtuple(int, list[list[int]])
The number of variables, and the clauses of the maximum satisfiability problem instance.
- Returns:
- cl_cost_functionfunction
The classical cost function for the problem instance, which takes a dictionary of measurement results as input.
Helper functions#
- create_maxsat_cost_polynomials(problem)[source]#
Creates a list of polynomials representing the cost function for each clause, and a list of symbols.
- Parameters:
- problemtuple(int, list[list[int]])
The number of variables, and the clauses of the maximum satisfiability problem instance.
- Returns:
- cost_polynomialslist[sympy.Expr]
A list of the cost functions for each clause as SymPy polynomials.
- symbolslist[sympy.Symbol]
A list of SymPy symbols.
MaxSat problem#
- maxsat_problem(problem)[source]#
Creates a QAOA problem instance with appropriate phase separator, mixer, and classical cost function.
- Parameters:
- problemtuple(int, list[list[int]])
The number of variables, and the clauses of the maximum satisfiability problem instance.
- Returns:
- QAOAProblem
A QAOA problem instance for MaxSat for given a
problem
.
Example implementation#
from qrisp import QuantumVariable
from qrisp.qaoa import QAOAProblem, RX_mixer, create_maxsat_cl_cost_function, create_maxsat_cost_operator
clauses = [[1,2,-3],[1,4,-6],[4,5,6],[1,3,-4],[2,4,5],[1,3,5],[-2,-3,6]]
num_vars = 6
problem = (num_vars, clauses)
qarg = QuantumVariable(num_vars)
qaoa_max_indep_set = QAOAProblem(cost_operator=create_maxsat_cost_operator(problem),
mixer=RX_mixer,
cl_cost_function=create_maxsat_cl_cost_function(problem))
results = qaoa_max_indep_set.run(qarg=qarg, depth=5)
That’s it! Feel free to experiment with the init_type='tqa'
option in the .run
method for improved performance.
In the following, we print the 5 most likely solutions together with their cost values.
cl_cost = create_maxsat_cl_cost_function(problem)
print("5 most likely solutions")
max_five = sorted(results.items(), key=lambda item: item[1], reverse=True)[:5]
for res, prob in max_five:
print(res, prob, cl_cost({res : 1}))