Managing Optimizations and Solutions

Luna simplifies the process of creating, storing, and managing optimizations, allowing you to efficiently solve complex problems. By leveraging Luna's intuitive interface and powerful tools, you can seamlessly define optimization problems, execute them on classical, quantum, or hybrid hardware, and analyze the results. Additionally, Luna provides robust mechanisms for managing solutions, enabling you to store, visualize and refine them to achieve optimal outcomes. This streamlined approach enhances your workflow and ensures that you can focus on finding the best solutions for your problems.

Upon completing this tutorial, you will be able to:

  1. Define and Configure Optimization Problems in Luna.
  2. Effortlessly execute and manage solutions using a wide array of algorithms.

Managing Optimizations

First, you'll need to upload an optimization problem to Luna. In this example, we'll use a simple optimization in the BQM format.

from luna_sdk.schemas.optimization_formats.bqm import BQMSchema

# Define your QUBO problem as a BQM
bqm_data = {
    "linear": {"0": 4.0, "1": -2.0, "2": 6.0, "3": 2.0, "4": 5.0},
    "quadratic": {("3", "2"): -6.0, ("4", "0"): -4.0},
    "vartype": "BINARY"
}

bqm = BQMSchema(**bqm_data)

# Upload your BQM to LunaSolve
optimization = ls.optimization.create_from_bqm(name="My BQM", bqm=bqm)

Luna makes it easy to manage all your stored optimizations. You can retrieve, edit, and delete them.

# Retrieve an optimization
optimization = ls.optimization.get(optimization_id=optimization.id)

print(optimization)

Output:


id: 664e1452acaf90d0a917a4e3
name: My BQM
original_format: BQM
created_date: 2024-05-22 17:50:42.070000 (WET)
quadratic:
    ('3', '2'): -6.0
    ('4', '0'): -4.0
linear:
    2: 6.0
    3: 2.0
    0: 4.0
    4: 5.0
    1: -2.0
vartype: BQMVarType.BINARY
offset: 0.0

Furthermore, you can retrieve ally our created optimization problems:

from luna_sdk.schemas.enums.timeframe import TimeframeEnum
from luna_sdk.schemas.enums.optimization import OptFormat

all_optimizations = ls.optimization.get_all(
    timeframe=TimeframeEnum.this_week, #Timeframe when solution was created
    input_type=OptFormat.QUBO, # Format of the optimization problem
    limit=10,  #Limit of optimizations returned, default is 50.
    offset=0, #Pagination
)
# Rename an optimization
updated_optimization = ls.optimization.rename(optimization_id=optimization.id, name="My Updated BQM")

print(updated_optimization)

Output:

id: 664e1452acaf90d0a917a4e3
name: My Updated BQM
original_format: BQM
created_date: 2024-05-22 17:50:42.070000 (WET)
# Delete an optimization
ls.optimization.delete(optimization_id=optimization.id)

Managing Solutions

First, you'll need to create a solution for an optimization via Luna. We'll use the same BQM from earlier and generate a simple solution using the Simulated Annealing algorithm.

# Define your QUBO problem as a BQM
bqm_data = {
    "linear": {"0": 4.0, "1": -2.0, "2": 6.0, "3": 2.0, "4": 5.0},
    "quadratic": {("3", "2"): -6.0, ("4", "0"): -4.0},
    "vartype": "BINARY"
}

bqm = BQMSchema(**bqm_data)

# Upload your BQM to LunaSolve
optimization = ls.optimization.create_from_bqm(name="My BQM", bqm=bqm)

# Create a solution for the BQM using SA
job = ls.solution.create(
    optimization_id=optimization.id,
    solver_name="SA",
    provider="dwave"
)

Similar to optimizations, Luna provides the same features for managing solutions. Users can easily print solution details, obtaining all necessary information for their problem. Luna provides two options for solution visualization. Using print(solution) displays all problem solutions, print(solution.head) highlights 5 solutions among all the availabe possibilities. If you only need to verify that the solution process was successful and don't require all solutions to be displayed, you can use print(solution.head). This will provide the necessary information about the solution object without displaying every single solution.

# Retrieve an optimization
solution = ls.solution.get(solution_id=job.id)

print(solution)

Output:

id: 664e1574ca894b28dc87b1e0
name: Your_Solution_Name
created_date: 2024-05-22 17:55:32.998000 (WET)
runtime:
    total: 0.005857708998519229
    qpu: None
solver: SA
provider: dwave
status: DONE
optimization_name: My BQM
results:
1 results found. Displaying first 5 results.
Result 1:
    {'sample': {'0': 0.0, '1': 1.0, '2': 0.0, '3': 0.0, '4': 0.0}, 'obj_value': -2.0, 'feasible': True, 'constraints': {}}
# Retrieve all solutions available to you
all_solutions = ls.solution.get_all(
    timeframe=TimeframeEnum.this_week, #Timeframe when solution was created
    limit=10,  # Limit of solutions returned, default is 50.
    offset=0, #Pagination
)
# Delete a solution
ls.solution.delete(solution_id=job.id)

Because some algorithms return multiple possible solutions, Luna allows you to retrieve only the best result from any created solutions.

ls.solution.get_best_result(solution)

When you create a solution through our use case library, Luna also offers the option to directly access the best result within the use case representation.

import networkx as nx
from luna_sdk.schemas import TravellingSalesmanProblem

# Create an empty graph using networkx
graph = nx.Graph()

# Define the cities we want to visit
cities = ['Berlin', 'Hamburg', 'Munich']

# Add the cities as nodes in our graph
graph.add_nodes_from(cities)

# Define the distances between each city
edges = [('Berlin', 'Hamburg', 289),
         ('Berlin', 'Munich', 586),
         ('Hamburg', 'Munich', 796)]

# Add the distances as the edges in our graph
graph.add_weighted_edges_from(edges)

# Convert the networkx graph to a digestible format for Luna
graph_data = nx.to_dict_of_dicts(graph)

# Define that we want to create a TSP with the corresponding graph data
tsp = TravellingSalesmanProblem(graph=graph_data)

# Create a TSP instance and retrieve the corresponding ID
optimization = ls.optimization.create_from_use_case(name="My TSP", use_case=tsp)

# Create a solution for the TSP using SA
job = ls.solution.create(
    optimization_id=optimization.id,
    solver_name="SA",
    provider="dwave"
)

# Instead of retrieving the raw solution, get it in the format fitting for your problem formulation
use_case_solution = ls.solution.get_use_case_representation(job.id)

# Finally, get the best result directly in the use case representation
result = ls.solution.get_best_use_case_result(use_case_solution)

print(result)

Output:

representation=[['Hamburg'], ['Munich'], ['Berlin']] obj_value=-3111.0

You can cancel queued jobs using the cancel method:

cancelled_solution = ls.solution.cancel('<YOUR_SOLUTION_ID>')

Interpreting Solution Runtimes

Each solution you create via Luna contains a Runtime object, which itself has the fields total and qpu. The total field holds a value we measured ourselves: it is the time in seconds that elapsed from between calling a solver and getting back a result from the solver. The qpu field only holds a value when the solver that created the solution runs on a backend other than our own servers. It reports the runtime in seconds on the hardware of our providers.

For example, if you had created the solution in the previous section with D-Wave's quantum annealer, you could expect the solution runtime to be of the following format:

print(solution.runtime)

Output:

total=3.362961308001104 qpu=0.01587886

Depending on the provider and the specific solver, solutions may also have timing information in the solution's metadata field. These metadata differ from provider to provider, and we mostly just pass on the values we obtain from the provider. The expected timing metadata per provider are summarized below.

D-Wave

For a general overview over which runtime metadata to expect from D-Wave solvers, check out D-Wave's website.

When you create a solution with D-Wave's quantum annealer, the metadata may look like this:

print(solution.details())

Output:

<...>

--------------------------------------------------------------------------------
DWAVE META DATA:
--------------------------------------------------------------------------------
timing:
    qpu_sampling_time: 94.5
    qpu_anneal_time_per_sample: 20
    qpu_readout_time_per_sample: 53.92
    qpu_access_time: 15878.86
    qpu_access_overhead_time: 1.14
    qpu_programming_time: 15784.36
    qpu_delay_time_per_sample: 20.58
    total_post_processing_time: 1
    post_processing_overhead_time: 1
problem_id: "75464a4b-64aa-495a-bdd6-b08344b1e9f8"

Was this page helpful?