Algorithms and Parameters¶
Algorithms are the core logic of the framework, transforming a DataSource into a ScenarioResult. To ensure flexibility and ease of use, the framework uses a class-based approach for algorithms and a type-safe system for their parameters.
BaseAlgorithm¶
Every algorithm in the framework must subclass BaseAlgorithm. This class provides the structure for execution, parameter management, and progress tracking.
Key Components of an Algorithm:¶
run(data): The main method where the logic resides. It takes aDataSourceand returns aScenarioResult.initialize_parameters(): A static method that returns a default instance of the algorithm’s parameter set. This is used by the GUI to generate the input form.set_progress(value): A method to report the execution status (0-100%) back to the framework.
Example of a basic algorithm:
Example
1from time import sleep
2from algomancy_scenario import BaseAlgorithm, ScenarioResult
3from algomancy_data import BaseDataSource
4
5class MyAlgorithm(BaseAlgorithm):
6 def __init__(self, params: "MyParams"):
7 super().__init__("My Custom Algorithm", params)
8
9 @staticmethod
10 def initialize_parameters():
11 return MyParams()
12
13 def run(self, data: BaseDataSource) -> ScenarioResult:
14 duration = self.params.duration
15 for i in range(duration):
16 # Report progress to the GUI
17 self.set_progress(100 * (i + 1) / duration)
18 sleep(1)
19
20 return ScenarioResult(data_id=data.id)
Parameters: BaseParameterSet¶
Algorithms are often driven by user-defined inputs. The BaseParameterSet class allows you to define these inputs in a
type-safe way that the framework can automatically render in the GUI with an appropriate input component. Each Algorithm
instance creates its own parameter set instance, which is passed to the algorithm during initialization.
Defining Parameters¶
You define a parameter set by subclassing BaseParameterSet and adding the parameters in the __init__ method.
Important
After construction, the parameter set is locked and no more parameters can be added. In other words: the
self.add_parameters method must be be called in the __init__ method. Note that the values of parameters
can still be accessed and modified.
Example
1from algomancy_utils import (BaseParameterSet,
2 IntegerParameter,
3 FloatParameter)
4
5class MyParams(BaseParameterSet):
6 def __init__(self):
7 super().__init__(name="My Algorithm Parameters")
8
9 self.add_parameters([
10 IntegerParameter(name="duration", default=10, minvalue=1, maxvalue=60),
11 FloatParameter(name="threshold", default=0.5, minvalue=0.0, maxvalue=1.0)
12 ])
13
14 @property
15 def duration(self) -> int:
16 return self["duration"]
17
18 def validate(self):
19 # Add cross-parameter validation here
20 pass
Supported Parameter Types¶
Parameter Type |
Description |
Example |
|---|---|---|
|
Whole numbers with bounds. |
|
|
Decimal numbers with bounds. |
|
|
True/False toggle. |
|
|
Plain text input. |
|
|
Single selection from a list. |
|
|
Multiple selections from a list. |
|
|
A specific point in time. |
|
|
A time range (start and end). |
|
Data Parameters¶
Alongside its own parameter set, an algorithm receives a second
BaseParameterSet that the data source declared via
initialize_data_parameters. Read it from
self.data_params inside run() to subset, filter, or slice the input data
before the main loop. The framework persists the supplied values per
scenario and pushes them onto the algorithm before run(); nothing is
applied automatically.
1def run(self, data: BaseDataSource) -> ScenarioResult:
2 sku = data.tables["sku_data"].copy()
3 if self.data_params.contains("category_filter"):
4 selected = self.data_params["category_filter"]
5 if selected:
6 sku = sku[sku["category"].isin(selected)]
7 # ...rest of the algorithm
Algorithms that don’t care about data parameters simply ignore the
attribute — it defaults to EmptyParameters(), so the .contains(...)
guard is always safe.
KPIs¶
Key Performance Indicators (KPIs) are used to evaluate the output of an algorithm.
Like algorithms, they follow a class-based pattern by subclassing BaseKPI.
Implementing a KPI¶
Each KPI must implement the compute(result) method, which extracts a numeric value from the ScenarioResult.
Example
Two things to note:
The
__init__method is used to define how the KPI is displayed and comparedThe
computemethod must be implemented, and is used to extract the metric from the result
1from algomancy_scenario import BaseKPI, ImprovementDirection
2from algomancy_utils import QUANTITIES, BaseMeasurement
3
4class TotalCostKPI(BaseKPI):
5 def __init__(self):
6 # Define how the KPI should be displayed and compared
7 super().__init__(
8 name="Total Cost",
9 better_when=ImprovementDirection.LOWER,
10 base_measurement=BaseMeasurement(QUANTITIES["money"]["$"])
11 )
12
13 def compute(self, result: ScenarioResult) -> float:
14 # Extract the metric from the result
15 return sum([trip.cost for trip in result.trips])
Threshold KPIs¶
KPIs can also include a threshold. If the computed value meets the threshold (based on better_when), the KPI is marked as a “success” (e.g., with a checkmark in the GUI).
For more details, see the API reference.