Decorators
feature
feature(_cls: type[T] | Callable[[Model], FeatureResult | BaseModel | object] | None = None, *, feature_id: str | None = None, feature_registry: Registry[BaseFeature] = Provide[RegistryContainer.feature_registry]) -> Callable[[type[T] | Callable[[Model], FeatureResult | BaseModel | object]], type[BaseFeature[FeatureResult]] | type[T]] | type[BaseFeature[FeatureResult]] | type[T]
feature(_cls: type[T], *, feature_id: str | None = None, feature_registry: Registry[BaseFeature] = Provide[RegistryContainer.feature_registry]) -> type[T]
Register a class or function as a feature.
The decorated class must be a subclass of the BaseFeature protocol, or a decorated
function will be automatically wrapped as a BaseFeature subclass.
Parameters:
-
_cls(type[T], default:None) –The class to be decorated. If None, returns a decorator function.
-
feature_id(str | None, default:None) –Set a custom ID for the feature. If not provided, the ID will be generated automatically from the module and class/function name. It's recommended to not set this parameter.
-
feature_registry((Registry[BaseFeature], injected), default:Provide[feature_registry]) –The registry where the feature will be registered. Injected by dependency container.
Returns:
-
Callable[[type[T]], type[T]] | type[T]–Either the decorated class/function or a decorator function.
Examples:
Decorate a class as a feature:
>>> from luna_bench.base_components import BaseFeature
>>> from luna_model import Model
>>> from pydantic import BaseModel
>>>
>>> @feature
... class MyFeature(BaseFeature):
... def run(self, model: Model) -> BaseModel:
... # Extract and return feature data
... return {"feature_value": 42}
Decorate a function as a feature:
>>> @feature
... def image_dimensions(model: Model) -> dict:
... # Extract image dimensions from model
... return {"width": 640, "height": 480}
Use a custom feature ID:
algorithm
algorithm(_cls: type[T] | Callable[[Model], Solution] | None = None, *, algorithm_id: str | None = None, algorithm_sync_registry: Registry[BaseAlgorithmSync] = Provide[RegistryContainer.algorithm_sync_registry], algorithm_async_registry: Registry[BaseAlgorithmAsync[Any]] = Provide[RegistryContainer.algorithm_async_registry]) -> Callable[[type[T] | Callable[[Model], Solution]], type[T] | type[BaseAlgorithmSync]] | type[T] | type[BaseAlgorithmSync]
Register a class or function as an algorithm.
The decorated class must be a subclass of the BaseAlgorithmSync or BaseAlgorithmAsync protocol.
When decorating a function, it must be a synchronous algorithm that takes a Model and returns a Solution.
Parameters:
-
_cls(type[T] | Callable[[Model], Solution], default:None) –The class or function to be decorated. If None, returns a decorator function.
-
algorithm_id(str | None, default:None) –Set a custom ID for the algorithm. If not provided, the ID will be generated automatically from the module and class/function name. It's recommended to not set this parameter.
-
algorithm_sync_registry((Registry[BaseAlgorithmSync], injected), default:Provide[algorithm_sync_registry]) –The registry where synchronous algorithms will be registered. Injected by dependency container.
-
algorithm_async_registry((Registry[BaseAlgorithmAsync[Any]], injected), default:Provide[algorithm_async_registry]) –The registry where asynchronous algorithms will be registered. Injected by dependency container.
Returns:
-
Callable[[type[T]], type[T]] | type[T]–Either the decorated class/function or a decorator function.
Examples:
Decorate a class as a synchronous algorithm:
>>> from luna_bench.base_components import BaseAlgorithmSync
>>> from luna_model import Model, Solution
>>>
>>> @algorithm
... class MyAlgorithm(BaseAlgorithmSync):
... def run(self, model: Model) -> Solution:
... # Run algorithm and return solution
... return Solution(...)
Decorate a function as a synchronous algorithm:
>>> @algorithm
... def simple_algorithm(model: Model) -> Solution:
... # Run algorithm and return solution
... return Solution(...)
Decorate a class as an asynchronous algorithm:
>>> from luna_bench.base_components import BaseAlgorithmAsync
>>> from pydantic import BaseModel
>>> from returns.result import Result
>>>
>>> class QuantumState(BaseModel):
... job_id: str
>>> @algorithm
... class QuantumAlgorithm(BaseAlgorithmAsync[QuantumState]):
... @property
... def model_type(self) -> type[QuantumState]:
... return QuantumState
...
... def run_async(self, model: Model) -> QuantumState:
... # Submit job and return retrieval data
... return QuantumState(job_id="123")
...
... def fetch_result(self, model: Model, retrieval_data: QuantumState) -> Result[Solution, str]:
... # Fetch result using retrieval data
... return Ok(Solution(...))
Use a custom algorithm ID:
metric
metric(_cls: type[BaseMetric[Any]] | Callable[[Solution, FeatureResultContainer], MetricResult | float | int] | list[type[BaseFeature[Any]]] | tuple[type[BaseFeature[Any]], ...] | type[BaseFeature[Any]] | None = None, *, metric_id: str | None = None, metric_registry: Registry[BaseMetric] = Provide[RegistryContainer.metric_registry]) -> Callable[[type[T] | Callable[[Solution, FeatureResultContainer], MetricResult | float | int]], type[T] | type[BaseMetric[MetricResult]]] | type[T] | type[BaseMetric[MetricResult]]
metric(_cls: type[T], *, metric_id: str | None = None, metric_registry: Registry[BaseMetric[Any]] = Provide[RegistryContainer.metric_registry]) -> type[T]
metric(_cls: Callable[[Solution, FeatureResultContainer], MetricResult | float | int], *, metric_id: str | None = None, metric_registry: Registry[BaseMetric[Any]] = Provide[RegistryContainer.metric_registry]) -> type[BaseMetric[MetricResult]]
metric(_cls: type[BaseFeature[Any]], *, metric_id: str | None = None, metric_registry: Registry[BaseMetric[Any]] = Provide[RegistryContainer.metric_registry]) -> Callable[[type[BaseMetric[Any]] | Callable[[Solution, FeatureResultContainer], MetricResult | float | int]], type[BaseMetric[Any]] | type[BaseMetric[MetricResult]]]
metric(_cls: list[type[BaseFeature[Any]]] | tuple[type[BaseFeature[Any]], ...], *, metric_id: str | None = None, metric_registry: Registry[BaseMetric[Any]] = Provide[RegistryContainer.metric_registry]) -> Callable[[type[BaseMetric[Any]] | Callable[[Solution, FeatureResultContainer], MetricResult | float | int]], type[BaseMetric[Any]] | type[BaseMetric[MetricResult]]]
Register a class or function as a metric component.
This decorator handles both class-based metrics (inheriting from BaseMetric) and
function-based metrics. When you decorate a function, it's automatically wrapped in a
BaseMetric subclass. You can also declare feature dependencies that your metric
needs to compute its result.
Parameters:
-
_cls(type[BaseMetric[Any]] | Callable[[Solution, FeatureResultContainer], MetricResult | float | int] | list[type[BaseFeature[Any]]] | tuple[type[BaseFeature[Any]], ...] | type[BaseFeature[Any]] | None, default:None) –The metric to register. Can be:
- A class inheriting from
BaseMetric(bare@metric) - A function taking (solution, feature_results) and returning float/int/MetricResult
- A single
BaseFeaturetype (becomes a dependency) - A list or tuple of
BaseFeaturetypes (dependencies)
When dependencies are passed, the decorator returns a decorator function that expects a metric class or function as its argument. In mose cases you don't need to set this field at all.
- A class inheriting from
-
metric_id(str | None, default:None) –Custom identifier for this metric in the registry. If omitted, an ID is auto-generated from the module and class/function name.
-
metric_registry(Registry[BaseMetric], default:Provide[metric_registry]) –The registry where this metric will be stored. Injected by the container. You do not need to set it.
Returns:
-
type[T] | type[BaseMetric[MetricResult]] | Callable–The registered metric class, or a decorator if dependencies were specified.
Notes
When decorating a function, the return type can be:
floatorint: automatically wrapped in aMetricResultMetricResult: returned as-is
The decorated function's signature is validated to ensure it matches the expected (solution, feature_results) parameters.
Examples:
Basic metric from a class:
>>> from luna_bench.base_components import BaseMetric
>>> from luna_model import Solution
>>> from luna_bench.base_components.data_types.feature_results import FeatureResults
>>> from luna_bench.types import MetricResult
>>>
>>> @metric
... class SolutionQuality(BaseMetric):
... def run(self, solution: Solution, feature_results: FeatureResults) -> MetricResult:
... ...
... return MetricResult(result=26.11)
Basic metric from a function:
>>> @metric
... def solve_time(solution: Solution, feature_results: FeatureResultContainer) -> float:
... return 26.11
Metric that depends on a feature:
>>> from luna_bench.base_components import BaseFeature
>>>
>>> @metric(Feature1)
... def feature_based_metric(solution: Solution, feature_results: FeatureResultContainer) -> float:
... return 26.11
Metric depending on multiple features:
plot
plot(required_components: type[BasePlot] | Callable[[BenchmarkResultContainer], None] | list[type[_REQUIRED_TYPES]] | tuple[type[_REQUIRED_TYPES], ...] | type[_REQUIRED_TYPES] | None = None, *, plot_id: str | None = None, plot_registry: Registry[BasePlot] = Provide[RegistryContainer.plot_registry]) -> Callable[[type[BasePlot] | Callable[[BenchmarkResultContainer], None]], type[BasePlot]] | type[BasePlot]
plot(required_components: type[BasePlot], *, plot_id: str | None = None, plot_registry: Registry[BasePlot] = Provide[RegistryContainer.plot_registry]) -> type[BasePlot]
plot(required_components: Callable[[BenchmarkResultContainer], None], *, plot_id: str | None = None, plot_registry: Registry[BasePlot] = Provide[RegistryContainer.plot_registry]) -> type[BasePlot]
plot(required_components: type[_REQUIRED_TYPES], *, plot_id: str | None = None, plot_registry: Registry[BasePlot] = Provide[RegistryContainer.plot_registry]) -> Callable[[type[BasePlot] | Callable[[BenchmarkResultContainer], None]], type[BasePlot]]
Register a class or function as a plot component.
This decorator registers visualization components that consume benchmark results. You can
register a class inheriting from BasePlot or wrap a function that handles the plotting
logic. Plots can declare dependencies on specific metrics or features they need to function.
Parameters:
-
required_components(type[BasePlot] | Callable[[BenchmarkResultContainer], None] | list[type[_REQUIRED_TYPES]] | tuple[type[_REQUIRED_TYPES], ...] | type[_REQUIRED_TYPES] | None, default:None) –The plot to register or its dependencies. Can be:
- A class inheriting from
BasePlot(bare@plot) - A function taking benchmark_results and returning None
- A single
BaseMetricorBaseFeaturetype (becomes a dependency) - A list or tuple of
BaseMetricand/orBaseFeaturetypes (dependencies)
When dependencies are passed, the decorator returns a decorator function that expects a plot class or function as its argument.
- A class inheriting from
-
plot_id(str | None, default:None) –Custom identifier for this plot in the registry. If omitted, an ID is auto-generated from the module and class/function name.
-
plot_registry(Registry[BasePlot], default:Provide[plot_registry]) –The registry where this plot will be stored. Injected by the container. You do not need to set it.
Returns:
-
type[BasePlot] | Callable–The registered plot class, or a decorator if dependencies were specified.
Notes
When decorating a function, it will be wrapped in a BasePlot subclass automatically.
The function receives the full BenchmarkResults object and is responsible for
extracting the data it needs to render the plot.
Examples:
Basic plot from a class:
>>> from luna_bench.custom import BasePlot, BenchmarkResults
>>>
>>> @plot
... class MyPlot(BasePlot):
... def run(self, benchmark_results: BenchmarkResults) -> None:
... # Plot or visualization the data
Basic plot from a function:
>>> @plot
... def quick_plot(benchmark_results: BenchmarkResultContainer) -> None:
... # Plot or visualization the data
Plot that depends on a specific metric:
>>> from luna_bench.metrics import Runtime
>>>
>>> @plot(Runtime)
... class RuntimeVisualization(BasePlot):
... def run(self, benchmark_results: BenchmarkResultContainer) -> None:
... # Plot or visualization the data
Plot that depends on multiple metrics: