Skip to content

Solve a QUBO Problem with QAGA+

This tutorial walks you through the process of solving custom Quadratic Unconstrained Binary Optimization (QUBO) problems using LunaSolve and the QAGA+ algorithm—a hybrid solver developed by Aqarios that combines classical evolutionary strategies with quantum annealing. You'll learn how to define a QUBO problem, translate it into a format compatible with LunaSolve, configure the QAGA+ solver, and retrieve high-quality solutions using quantum-enhanced optimization. Whether you're tackling real-world decision problems or experimenting with quantum workflows, this guide offers a practical starting point.


What You'll Learn

By the end of this tutorial, you will have:

✅ Formulated an optimization problem in QUBO format.
✅ Used the QAGA+ algorithm to solve your QUBO problem.
✅ Retrieved and interpreted the optimization results.

1. Preparing Your QUBO

To begin, you must convert your optimization problem into the QUBO format. QUBO problems use binary variables and their interactions to represent the optimization objective. In general you can define a QUBO as a matrix (list of lists) or as a BinaryQuadraticModel (BQM) using D-Wave’s dimod package.

Lets get started. As always, first authenticate using your Luna API key. If the key is not already set in your environment, the following section will securely prompt you for it.

Next, we define our QUBO problem. In this problem we will choose to define it as a matrix, where each entry represents the linear and quadratic coefficients of the binary variables. The QuboTranslator then converts this matrix into a model compatible with LunaSolve, specifying that the variables are binary. This translation step prepares the problem for optimization using QAGA+ or other LunaSolve compatible solvers.

from luna_quantum import LunaSolve
import getpass
import os

if "LUNA_API_KEY" not in os.environ:
    # Prompt securely for the key if not already set
    os.environ["LUNA_API_KEY"] = getpass.getpass("Enter your Luna API key: ")

LunaSolve.authenticate(os.environ["LUNA_API_KEY"])
ls = LunaSolve()
from luna_quantum import Logging
import logging

Logging.set_level(logging.WARNING)
from luna_quantum import QuboTranslator, Vtype

# Define QUBO as a matrix
qubo_matrix = [
    [4, 0, 0, 0, -2],
    [0, -2, 0, 1, 0],
    [0, 0, 6, -3, 0],
    [0, 1, -3, 2, 0],
    [-2, 0, 0, 0, 5]
]

model = QuboTranslator.to_aq(qubo_matrix, name="QUBO matrix", vtype=Vtype.Binary)

2. Solving the QUBO Problem on Quantum Hardware

The QAGA+ is a hybrid optimization algorithm inspired by genetic algorithms. It evolves a population of candidate solutions over multiple iterations using mutation and recombination, guided by a selection process that favors high-quality solutions. What sets QAGA+ apart is its use of quantum annealing, via D-Wave's quantum hardware, during the mutation phase, enhancing its ability to escape local optima and explore the solution space more effectively.

QAGA+ Parameters

When configuring QAGA+, there are a number of parameters can be tuned to control how the algorithm searches, with the following being the most important:

  • p_size (Population size): Controls the number of candidate solutions per iteration.
  • mut_rate (Mutation rate): Determines the probability of solution variation.
  • rec_rate (Recombination rate): Specifies the number of solutions used for generating new candidates.

Running the QAGA+ Solver

This code sets up and runs the QAGA+ algorithm using LunaSolve and D-Wave’s quantum hardware. First, we import the necessary components: QAGA for defining the algorithm, DWaveQpu for specifying the quantum backend, and PersonalQpuToken for authenticating access to D-Wave’s system. Learn more here how to create your Personal QPU Token.

Next, we configure the algorithm by creating a QAGA object with specific parameters. Here, we set values for the population size, the mutation and recombination rate. These values help balance exploration (trying new areas of the solution space) and exploitation (refining known good solutions).

The backend argument specifies that D-Wave’s quantum annealer will be used for the mutation phase. To enable this, you must provide a valid personal token, referenced by name using PersonalQpuToken. This token must be set up in your Luna account and grants access to quantum hardware during runtime.

Finally, calling algorithm.run(model) launches the optimization. The QAGA+ algorithm begins its iterative search. The result is returned as a Solution object containing the best solution found, along with associated metrics like energy value and performance data.

from luna_quantum.client.schemas.qpu_token.qpu_token import PersonalQpuToken
from luna_quantum.solve.parameters.algorithms import QAGA
from luna_quantum.solve.parameters.backends import DWaveQpu

algorithm = QAGA(
    p_size=40,
    mut_rate=0.3,
    rec_rate=2,
    backend=DWaveQpu(
        token=PersonalQpuToken(
            name="My D-Wave Token"
        )
    )
)

job = algorithm.run(model)

3. Retrieving Your Solution

LunaSolve runs optimizations asynchronously, meaning your job is submitted and processed in the background, freeing your system for other tasks while the quantum or hybrid backend performs the computation. To retrieve the results once the computation is complete, you use the job.result() method, which will wait until the job finishes and the results are available. IF you are interested in all the details of the Solution class, please visit the Solution documentation.

solution = job.result()
samples = solution.samples.tolist()
print(samples)

Here, job.result() returns a solution object containing all sampled solutions together with metadata of the optimization. The .samples attribute is an iterable of binary vectors, each representing a candidate solution to your QUBO problem. Converting it to a list with .tolist() makes it easier to print or post-process.

If you are interested in extracting the best solution from the set of candidates, use:

best_solution = solution.best()
best_value = best_solution.obj_value

print(f"Best solution: {best_solution}")
print(f"Objective value of best solution: {best_value}")

best_solution is the corresponding binary vector that represents the best solution found by the algorithm, while best_value gives its associated objective value, essentially the minimized energy of your QUBO model. Together, these outputs provide both the vector and quality of the solution, allowing you to interpret and apply the result directly to your optimization problem.

Fantastic! You’ve now successfully formulated, submitted, and solved a QUBO problem using LunaSolve and the QAGA+ algorithm. You're ready to apply this workflow to your own optimization challenges using quantum-enhanced methods.

Additional Optimization Formats

LunaSolve supports various optimization formats beyond QUBO. For details, check our user guide on optimization formats.