Skip to content

Algorithms

Algorithms are the optimization process that Luna Bench runs against your optimization models. Each algorithm takes a Model as input and produces a Solution. Luna Bench provides two base classes to accommodate different execution paradigms: synchronous algorithms that return results immediately, and asynchronous algorithms designed for cloud or quantum backends where computation happens remotely and results must be fetched later.


Overview

An algorithm in Luna Bench is a configurable, serializable unit of computation registered via the @algorithm decorator. The decorator registers the class with the internal component registry so benchmarks can discover and instantiate algorithms by name.

Luna Bench ships with a small set of built-in algorithms for testing and common use cases, but the real power comes from writing your own custom algorithms that wrap any solver, API, or service you need to benchmark.

Aspect Synchronous Asynchronous
Base class BaseAlgorithmSync BaseAlgorithmAsync[T]
Return style Blocks until solution is ready Submits work, then polls for results
Use case Local solvers (SCIP, Gurobi, etc.) Cloud APIs, quantum backends
Key method(s) run() run_async() + fetch_result()

Synchronous Algorithms

When to use

Use a synchronous algorithm when the solver runs locally or returns results in a single blocking call.

Base class: luna_bench.custom.BaseAlgorithmSync

Synchronous algorithms implement a single abstract method:

Python
def run(self, model: Model) -> Solution

The method receives a Model instance and must return a Solution. Luna Bench handles timing, error capture, and result storage around this call.

Registration

All algorithms — synchronous and asynchronous — are registered with the @algorithm decorator from luna_bench.custom:

Python
from luna_bench.custom import algorithm

@algorithm
class MySolver(BaseAlgorithmSync):
    ...

The decorator makes the class available in the component registry so that benchmarks can reference it by name in configuration files and programmatic setups.


Asynchronous Algorithms

When to use

Use an asynchronous algorithm when computation is submitted to a remote service and results become available after a delay — for example, cloud optimization platforms or quantum hardware queues.

Base class: luna_bench.custom.BaseAlgorithmAsync[T]

The generic type parameter T represents the retrieval data — a serializable object (typically a Pydantic BaseModel) that holds whatever reference is needed to fetch results later (such as a job ID or request token).

Asynchronous algorithms require three abstract members:

Member Signature Purpose
model_type @property -> type[T] Returns the concrete type of the retrieval data for deserialization
run_async (model: Model) -> T Submits the model for solving and returns retrieval data
fetch_result (model: Model, retrieval_data: T) -> Result[Solution, str] Polls the remote service and returns the solution or an error message

The return type of fetch_result uses the Result monad from the returns library. Return Success(solution) when results are ready, or Failure("reason") when they are not yet available or an error occurred.

Background Processing with Huey

Asynchronous algorithms are executed through a Huey job queue. Luna Bench submits the run_async call as a background task, then periodically invokes fetch_result to check whether the computation has completed. This keeps the main benchmark loop non-blocking and allows multiple asynchronous jobs to run in parallel.


Built-in Algorithms

Luna Bench includes several algorithms out of the box:

ScipAlgorithm

Python
from luna_bench.algorithms import ScipAlgorithm

Wraps the SCIP mixed-integer programming solver. This is a fully-featured open-source solver suitable for benchmarking linear and mixed-integer programs.

FakeAlgorithm

Python
from luna_bench.algorithms import FakeAlgorithm

A test algorithm that sleeps for a random duration before returning a dummy solution. Useful for validating benchmark pipelines, testing timeout behavior, and developing new features without waiting for real solvers.


Writing Custom Algorithms

Custom Synchronous Algorithm

The following example shows a minimal synchronous algorithm with a configurable parameter. Because algorithms are Pydantic models under the hood, you can declare typed fields with defaults and they become part of the algorithm's serializable configuration.

Python
from luna_bench.custom import algorithm
from luna_quantum import Model, Solution


@algorithm
def my_algorithm(model: Model) -> Solution:
    # Implement your solving logic here.
    ...
    return solution
Python
from luna_bench.custom import BaseAlgorithmSync, algorithm
from luna_quantum import Model, Solution


@algorithm
class MyAlgorithm(BaseAlgorithmSync):
    max_iterations: int = 1000

    def run(self, model: Model) -> Solution:
        # Implement your solving logic here.
        # Access configuration via self.max_iterations, etc.
        ...
        return solution

Key points:

  • Subclass BaseAlgorithmSync.
  • Apply the @algorithm decorator.
  • Implement the run method.
  • Declare any solver-specific parameters as class-level fields with type annotations.

Custom Asynchronous Algorithm

The following example shows an asynchronous algorithm that submits work to a remote service and later fetches results using a job ID.

Python
from luna_bench.custom import BaseAlgorithmAsync, algorithm
from luna_quantum import Model, Solution
from returns.result import Result, Success, Failure
from pydantic import BaseModel


class JobId(BaseModel):
    id: str


@algorithm
class MyCloudAlgorithm(BaseAlgorithmAsync[JobId]):
    @property
    def model_type(self) -> type[JobId]:
        return JobId

    def run_async(self, model: Model) -> JobId:
        # Submit the model to your cloud service.
        # Return a reference object that can be used to retrieve results.
        return JobId(id="job-123")

    def fetch_result(self, model: Model, retrieval_data: JobId) -> Result[Solution, str]:
        # Poll the remote service for results.
        # Return Success(solution) when ready, or Failure("not ready") to retry later.
        return Success(solution)

Key points:

  • Subclass BaseAlgorithmAsync[T] where T is your retrieval data type (a Pydantic BaseModel).
  • Apply the @algorithm decorator.
  • Implement the model_type property to return the concrete class of T.
  • Implement run_async to submit work and return the retrieval reference.
  • Implement fetch_result to check for and return results using the Result type.

Luna Quantum Integration

Any algorithm from the luna-quantum can be added directly to a Luna Bench benchmark without any additional wrapping on your part. Internally, Luna Bench uses LunaAlgorithmWrapper to adapt the luna-quantum algorithm interface to the benchmark execution framework.

This means you can mix and match native Luna Bench algorithms with existing luna-quantum solvers in the same benchmark run.


Adding Algorithms to a Benchmark

Once you have an algorithm instance — whether built-in, custom, or from luna-quantum — add it to your benchmark with the add_algorithm method:

Python
from luna_bench.algorithms import ScipAlgorithm

# Add a built-in algorithm
bench.add_algorithm("scip", ScipAlgorithm())

# Add a custom algorithm with configuration
bench.add_algorithm("my-algo", MyAlgorithm(max_iterations=500))

# Add multiple algorithms to compare them
bench.add_algorithm("cloud-solver", MyCloudAlgorithm())

The first argument is a name that identifies the algorithm in benchmark results, reports, and logs. Choose short, descriptive names — they will appear in tables and charts. The second argument is the algorithm instance with any desired configuration applied.

You can add as many algorithms as you like. Luna Bench will run every registered algorithm against every registered model, collecting results for comparison.