User Defined Ansatz¶
Here we present how a combinatorial optimization problem can be solved within the VQE method and a user-defined ansatz. This method enrich the platform defined ansatze: QAOAPenalty and QAOAMixer.
Lets demonstrate on the Max Vertex Cover (MVC) problem, which is described in problem library.
import networkx as nx
import pyomo.core as pyo
def mvc(graph: nx.Graph, k: int) -> pyo.ConcreteModel:
model = pyo.ConcreteModel()
model.x = pyo.Var(graph.nodes, domain=pyo.Binary)
model.amount_constraint = pyo.Constraint(expr=sum(model.x.values()) == k)
def obj_expression(model):
# number of edges not covered
return sum((1 - model.x[i]) * (1 - model.x[j]) for i, j in graph.edges)
model.cost = pyo.Objective(rule=obj_expression, sense=pyo.minimize)
return model
mvc_model = mvc(graph=nx.star_graph(4), k=1)
Initializing the ansatz¶
Here, we define our ansatz. It consists from a Hardware efficient ansatz function
from classiq import Model
from classiq.builtin_functions import HardwareEfficientAnsatz
model = Model()
model.HardwareEfficientAnsatz(HardwareEfficientAnsatz(num_qubits=5, reps=1))
ansatz = model.synthesize()
Now we connect all the components using CombinatorialOptimization
class and send a solve
command.
The appropriate QSolver
for this case is QSovler.Custom
.
from classiq.applications.combinatorial_optimization import (
CombinatorialOptimization,
QAOAPreferences,
QSolver,
)
problem = CombinatorialOptimization(
model=mvc_model,
qsolver_preferences=QAOAPreferences(qsolver=QSolver.Custom),
ansatz=ansatz,
)
result = problem.solve()
Changing the ansatz¶
Another option is to initialize CombinatorialOptimization
class with a builtin QSolver
and then change
it to a custom ansatz.
problem = CombinatorialOptimization(
model=mvc_model,
qsolver_preferences=QAOAPreferences(qsolver=QSolver.QAOAPenalty),
)
problem.ansatz = ansatz
result = problem.solve()
Checking solutions validity¶
QAOAMixer is an example for an ansatz which assures that all the resulting solutions are valid under the problem's constraints. The user defined ansatz may have this quality, or not.
CombinatorialOptimization
lets the user to declare if the ansatz is "constraints-preserving", and will act accordingly.
Namely, for those ansatze (e.g. QAOAMixer), we will check if all the solutions are valid.
This attribute is defined using should_check_valid_solutions
field.
The default value of should_check_valid_solutions
field is set to True
only for QSolve.QAOAMixer
and for a
quantum backend which is an exact simulator. If the backend is noisy we cannot completely assure the solutions validity.
So, for example, if we first use QAOAMixer
and then switch to a user-defined ansatz which doesn't
preserve the constraints, we have to explicitly switch off the should_check_valid_solutions
field.
problem = CombinatorialOptimization(
model=mvc_model,
qsolver_preferences=QAOAPreferences(qsolver=QSolver.QAOAMixer),
)
problem.should_check_valid_solutions = False
problem.ansatz = ansatz
result = problem.solve()
Setting and getting the model¶
Custom ansatz can be defined by supplying the model
to the CombinatorialOptimization
class.
Similar to the previously described method using ansatz
.
problem = CombinatorialOptimization(
model=mvc_model,
qsolver_preferences=QAOAPreferences(qsolver=QSolver.Custom),
)
problem.model = model
result = problem.solve()
Note that while using QSolver.Custom, the field 'model' will not exist unless the user explicitly sets it.