Solve Optimization Problem¶
So far, we've gone through the optimization model formulation. We shall now present the core Classiq capabilities, generation of a designated quantum solution, and execution of the generated algorithm on a quantum backend.
We present our method with a common example problem, the Max Independent Set (MIS) with
networkx.star_graph(4)
solved by QAOAMixer algorithm with a single QAOA layer
(see the problem library).
import networkx as nx
import pyomo.core as pyo
def mis(graph: nx.Graph) -> pyo.ConcreteModel:
model = pyo.ConcreteModel()
model.x = pyo.Var(graph.nodes, domain=pyo.Binary)
@model.Constraint(graph.edges)
def independent_rule(model, node1, node2):
return model.x[node1] + model.x[node2] <= 1
model.cost = pyo.Objective(expr=sum(model.x.values()), sense=pyo.maximize)
return model
The method consists of building a PYOMO model, indicating the QAOA and optimizer preferences and then using one of the following commands:
generate
- generates the quantum circuit.solve
- solves the optimization problem.get_operator
- returns the Ising hamiltonian representing the problem's objective.get_objective
- returns the PYOMO object representing the problem's objective.get_initial_point
- returns the initial parameters for a parametric ansatz.classical_solution.solve
- solves the optimization problem classically.
{
"name": "mis",
"graph": [
[0.0, 1.0, 1.0, 1.0, 1.0],
[1.0, 0.0, 0.0, 0.0, 0.0],
[1.0, 0.0, 0.0, 0.0, 0.0],
[1.0, 0.0, 0.0, 0.0, 0.0],
[1.0, 0.0, 0.0, 0.0, 0.0]
],
"qaoa_preferences" : {
"qsolver": "QAOAMixer",
"qaoa_reps": 1,
}
}
The file should include the name of the problem, and the required arguments for the model building function. In the case of MIS problem, the only argument is the underline graph in the form of adjacency matrix. Please make sure the given name is equal to the file name of the model definition.
Two extension commands are available: generate circuit
and solve problem
.
This is done by opening the Command Palette (Ctrl+Shift+P / Command+Shift+P) and choosing either the "Generate circuit for combinatorial optimization" command or "Solve combinatorial optimization" command, respectively.
First, we create the desired optimization problem as a PYOMO model: The following code snippet is a concise example of the application of the optimization engine.
import networkx as nx
graph = nx.star_graph(4)
mis_model = mis(graph)
We are now ready to send the model to the Classiq backend. This is done using the CombinatorialOptimization
package and its "synthesize" and "solve" commands.
from classiq.applications.combinatorial_optimization import (
CombinatorialOptimization,
QSolver,
QAOAPreferences,
)
qaoa_preferences = QAOAPreferences(qsolver=QSolver.QAOAMixer, qaoa_reps=3)
mis_problem = CombinatorialOptimization(
model=mis_model, qsolver_preferences=qaoa_preferences
)
Results¶
"Model Designer" command¶
The model
property exposes the functional-level model of the resulting ansatz.
Afterwards, the user can synthesize from it the explicit circuit.
model = mis_problem.get_model()
or,
model = mis_problem.model
"Synthesize" command¶
The quantum circuit is returned as GeneratedCircuit class, which contains both textual and visual representations. The textual data is available in multiple formats. The visual data consists of a static image (see figure below) and an interactive image.
qc = mis_problem.synthesize()
or,
qc = mis_problem.ansatz
"Solve problem" command¶
result = mis_problem.solve()
The results are organized in the VQESolverResult class and may be observed in several formats.
serialized output¶
print(result)
=== OPTIMAL SOLUTION ===
solution cost
--------------- ------
(0, 1, 1, 1, 1) 4
=== SOLUTION DISTRIBUTION ===
solution cost probability
--------------- ------ -------------
(0, 1, 1, 1, 1) 4 0.71
(0, 1, 1, 1, 0) 3 0.02
(0, 1, 1, 0, 1) 3 0.04
(0, 1, 0, 1, 1) 3 0.03
........ .. ....
=== OPTIMAL_PARAMETERS ===
[3.064572795460487, 0.5370940203297239, 2.869550922366777, 0.526740029882901, 3.0904332803941017, 1.417496935210913]
=== TIME ===
00:00:02.258639
Histogram¶
print(result.histogram())
Convergence Graph¶
print(result.convergence_graph)
Optimal Parameters¶
result.optimal_parameters_graph()
Operator command¶
operator = mis_problem.get_operator()
print(operator.show())
-2.500 * IIIII
+0.500 * IIIIZ
+0.500 * IIIZI
+0.500 * IIZII
+0.500 * IZIII
+0.500 * ZIIII
Objective command¶
print(mis_problem.get_objective())
x[0] + x[1] + x[2] + x[3] + x[4] + x[5]
Initial parameters command¶
initial_parameters = mis_problem.get_initial_point()
[0.22.., 3.09..., 0.95..., 2.83...]
Solve classically command¶
result = mis_problem.solve_classically()
best_cost=4.0 time=None solution=(0, 1, 1, 1, 1)