Skip to content

Solve the Optimization Problem

A previous section formulated the optimization problem. This section presents the core Classiq capabilities, generates a designated quantum solution, and executes the generated algorithm on a quantum backend.

This example uses the method for a common problem: the Max Independent Set (MIS) with networkx.star_graph(4), solved by a 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 builds a PYOMO model, indicates the QAOA and optimizer preferences, and then uses one of these commands:

  • generate – generates the quantum circuit.
  • solve – solves the optimization problem.
  • get_operator – returns the Ising Hamiltonian representing the problem objective.
  • get_objective – returns the PYOMO object representing the problem 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. For an MIS problem, the only argument is the underline graph in the form of an adjacency matrix. Make sure the given name is the same as the file name of the model definition.

Two extension commands are available. Open the Command Palette (Ctrl+Shift+P / Command+Shift+P) and choose one:

  • generate circuit for the "Generate circuit for combinatorial optimization" command
  • solve problem for the "Solve combinatorial optimization" command

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

To send the model to the Classiq backend, use the CombinatorialOptimization package with 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" Command

The model property exposes the functional-level model of the resulting ansatz. Afterwards, you can synthesize the explicit circuit from it.

model = mis_problem.get_model()

or

model = mis_problem.model

"Synthesize" Command

The quantum circuit is returned as a GeneratedCircuit class, containing both textual and visual representations. The textual data is available in multiple formats. The visual data consists of a static image (see the figure below) and an interactive image.

qc = mis_problem.synthesize()

or

qc = mis_problem.ansatz

 qaoa example

"Solve" Command

result = mis_problem.solve()

The results are organized in the VQESolverResult class and you can view it 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())

 histogram

Convergence Graph

print(result.convergence_graph)

 energy_convergence

Optimal Parameters

result.optimal_parameters_graph()

 optimal_parameters

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