Skip to content

HPO Optimizers API

GAM Optimizer

GAMOptimizer

GAMOptimizer(objective_function: Callable[[dict], float], search_space: SearchSpace, settings: GAMOptSettings, known_observations: list[dict] | None = None)

Bases: BaseOptimizer

Optimizer based on Generalized Additive Models. Trained model is used to suggest next node in the search space for evaluation.

Parameters:

  • objective_function (Callable[[dict], float]) –

    Target function that will be used in every evaluation. Output of this function should be 'float', as this is the value for which algorithms try to optimize solution. Function should take dict filled with 'key: value' pairs that are 'argument: corresponding value'.

  • search_space (SearchSpace) –

    Instance containing information about nodes in the solutions space that will be evaluated during the optimization.

  • settings (GAMOptSettings) –

    Instance with settings required for configuring the optimization process.

Attributes:

  • evaluations (list[dict]) –

    Already evaluated hyperparameters combinations with corresponding score.

  • max_iterations (int) –

    Effective maximum number of evaluated patterns retained as search results and published to the event handler. This is bounded by max_evals and the number of available search-space combinations.

Source code in ai4rag/core/hpo/gam_opt.py
def __init__(
    self,
    objective_function: Callable[[dict], float],
    search_space: SearchSpace,
    settings: GAMOptSettings,
    known_observations: list[dict] | None = None,
):
    super().__init__(objective_function, search_space, settings)
    self.evaluations = []
    self._evaluated_combinations = []
    self._typed_encoders_with_columns: list[tuple[str, LabelEncoder]] = []
    self.warm_start_evaluation_count: int = 0
    self._remaining_gam_output_capacity: int | None = None
    self.current_phase = "idle"

    if known_observations:
        self._load_known_observations(known_observations)
    self._known_observation_count = len(self.evaluations)

    self._validate_fields_to_balance()
    self._validate_n_random_nodes()

    self.max_evals = min(self.settings.max_evals, self._search_space.max_combinations)
    self.max_iterations = self.settings.max_iterations

Attributes

max_iterations property writable

max_iterations: int

Get the effective maximum number of retained result patterns.

warm_start_output_count property

warm_start_output_count: int

Return one slot only when this run produced a publishable warm-start result.

Methods:

search

search() -> dict[str, Any]

Actual function performing hyperparameter optimization for the selected objective function.

Returns:

  • dict[str, Any] –

    The best set of parameters with achieved score.

Raises:

  • OptimizationError –

    When there were no successful evaluations for given constraints.

Source code in ai4rag/core/hpo/gam_opt.py
def search(self) -> dict[str, Any]:
    """
    Actual function performing hyperparameter optimization for the selected
    objective function.

    Returns
    -------
    dict[str, Any]
        The best set of parameters with achieved score.

    Raises
    ------
    OptimizationError
        When there were no successful evaluations for given constraints.
    """
    self.current_phase = "warm_start"
    self.evaluate_initial_random_nodes()

    self.current_phase = "gam"
    if len(self.evaluations) >= self.max_evals:
        logger.info(
            "All %d allowed evaluations were consumed by the warm-start phase; GAM iterations will be skipped.",
            self.max_evals,
        )
    self._remaining_gam_output_capacity = max(0, self.max_iterations - self.warm_start_output_count)
    try:
        for _ in range(self._get_gam_iterations_limit()):
            self._run_iteration()
    finally:
        self._remaining_gam_output_capacity = None

    self.current_phase = "complete"

    successful_evaluations = [evaluation for evaluation in self.evaluations if evaluation["score"] is not None]
    if not successful_evaluations:
        raise OptimizationError("Number of evaluations has reached limit. All iterations have failed.")

    # Sort in ascending order and take the last element (highest score).
    # This assumes we're maximizing the score.
    best_config_with_score = sorted(successful_evaluations, key=lambda d: d["score"])[-1]

    return best_config_with_score

compute_warm_start_effective_target

compute_warm_start_effective_target() -> int

Return the effective number of successful warm-start nodes to evaluate.

Returns:

  • int –

    The effective warm-start target for the configured strategy.

Source code in ai4rag/core/hpo/gam_opt.py
def compute_warm_start_effective_target(self) -> int:
    """Return the effective number of successful warm-start nodes to evaluate.

    Returns
    -------
    int
        The effective warm-start target for the configured strategy.
    """
    return self._compute_warm_start_effective_target()

evaluate_initial_random_nodes

evaluate_initial_random_nodes() -> None

Perform evaluation of randomly chosen nodes from the solutions space. All strategies stop at the configured maximum evaluation count. Greedy and balanced starts may therefore finish before their coverage target when the evaluation budget is smaller than the coverage requirement.

When the optimizer has been warm-started with known observations, already-successful evaluations count toward the n_random_nodes target and already-evaluated combinations are excluded from candidates.

The selection order depends on warm_start_strategy: "random" — shuffled order (no reordering). "greedy" — greedy selection maximizing string-column coverage (each value >= 2 times). "balanced" — round-robin across fields_to_balance value tuples.

Source code in ai4rag/core/hpo/gam_opt.py
def evaluate_initial_random_nodes(self) -> None:
    """
    Perform evaluation of randomly chosen nodes from the solutions space.
    All strategies stop at the configured maximum evaluation count. Greedy
    and balanced starts may therefore finish before their coverage target
    when the evaluation budget is smaller than the coverage requirement.

    When the optimizer has been warm-started with known observations,
    already-successful evaluations count toward the n_random_nodes target
    and already-evaluated combinations are excluded from candidates.

    The selection order depends on warm_start_strategy:
    "random"   — shuffled order (no reordering).
    "greedy"   — greedy selection maximizing string-column coverage (each value >= 2 times).
    "balanced" — round-robin across fields_to_balance value tuples.
    """
    successful_evaluations = sum(1 for e in self.evaluations if e["score"] is not None)
    effective_target = self._compute_warm_start_effective_target()

    if successful_evaluations >= effective_target:
        logger.info(
            "Skipping random evaluation phase: %d known successful evaluations >= warm_start_target (%d).",
            successful_evaluations,
            effective_target,
        )
        return

    if len(self.evaluations) >= self.max_evals:
        return

    combinations_local = self._prepare_warm_start_combinations(effective_target, successful_evaluations)
    discrete_cols_in_space = _get_discrete_column_values(combinations_local)
    self._evaluate_warm_start_combinations(combinations_local, effective_target, successful_evaluations)

    self._log_uncovered_values(discrete_cols_in_space, self.evaluations, effective_target)

    self.warm_start_evaluation_count = len(self.evaluations)

GAMOptSettings dataclass

GAMOptSettings(max_evals: int, random_state: int = 64, max_iterations: int | None = None, n_random_nodes: int = 4, evals_per_trial: int = 1, warm_start_strategy: Literal['random', 'greedy', 'balanced'] = 'random', fields_to_balance: list[str] | None = None)

Bases: OptimizerSettings

Settings for the GAMOptimizer. For the detailed description of parameters for Generalized Additive Models, please see pygam documentation.

Parameters:

  • max_evals (int) –

    Maximum number of objective-function evaluations performed during optimization, including warm-start and GAM evaluations.

  • max_iterations (int | None, default: None ) –

    Maximum number of evaluated RAG patterns retained and published when the search completes. It controls the warm-start/GAM output allocation, not the hard evaluation budget; use max_evals to bound objective-function calls. When omitted, it is set to the effective max_evals value. It cannot exceed an explicitly configured max_evals.

  • n_random_nodes (int, default: 4 ) –

    Number of random configurations to evaluate before starting GAM iterations.

  • evals_per_trial (int, default: 1 ) –

    Number of configurations to evaluate per GAM iteration.

  • warm_start_strategy ((random, greedy, balanced), default: "random" ) –

    Controls how the initial n_random_nodes observations are selected/ordered. "random" — shuffle the candidate list and take the first n as-is. "greedy" — greedily pick combinations so every discrete column value appears at least twice. If n_random_nodes is below the computed minimum (min_required), the warm start is auto-adjusted upward to meet coverage. One output slot is allocated per four effective warm-start evaluations; GAM fills the remaining max_iterations slots. "balanced" — round-robin across the tuple of fields_to_balance values; non-balanced discrete column values each appear at least once. Requires fields_to_balance to be set. Same auto-adjustment, and output allocation rules as "greedy".

  • fields_to_balance (list[str] | None, default: None ) –

    Field names to balance by round-robin when warm_start_strategy="balanced". Each unique value combination of these fields is guaranteed to appear at least once in the first n_random_nodes evaluations.

  • random_state (int, default: 64 ) –

    Inherited from OptimizerSettings. Controls shuffle order of initial random exploration phase. Does NOT control GAM model randomness (GAM training is deterministic).

Random Optimizer

RandomOptimizer

RandomOptimizer(objective_function: Callable[[dict], float], search_space: SearchSpace, settings: RandomOptSettings)

Bases: BaseOptimizer

Optimizer running random search on the given search space.

Source code in ai4rag/core/hpo/random_opt.py
def __init__(
    self, objective_function: Callable[[dict], float], search_space: SearchSpace, settings: RandomOptSettings
):
    super().__init__(objective_function, search_space, settings)
    self._evaluated_combinations = []

Methods:

search

search() -> dict[str, Any]

Actual function performing hyperparameter optimization for the selected objective function.

Returns:

  • dict[str, Any] –

    The best set of parameters with achieved score.

Raises:

  • OptimizationError –

    When there were no successful evaluations for given constraints.

Source code in ai4rag/core/hpo/random_opt.py
def search(self) -> dict[str, Any]:
    """
    Actual function performing hyperparameter optimization for the selected
    objective function.

    Returns
    -------
    dict[str, Any]
        The best set of parameters with achieved score.

    Raises
    ------
    OptimizationError
        When there were no successful evaluations for given constraints.
    """
    combinations = list(self._search_space.combinations)
    random.Random(self.settings.random_state).shuffle(combinations)

    for idx in range(self.settings.max_evals):
        score = self._objective_function(combinations[idx])
        self._evaluated_combinations.append(combinations[idx] | {"score": score})

    successful_evaluations = [ev for ev in self._evaluated_combinations if ev["score"] is not None]

    if not successful_evaluations:
        raise OptimizationError("Number of evaluations has reached limit. All iterations have failed.")

    best_config_with_score = sorted(successful_evaluations, key=lambda d: d["score"])[-1]

    return best_config_with_score

RandomOptSettings dataclass

RandomOptSettings(max_evals: int, random_state: int = 64)

Bases: OptimizerSettings

Settings for random optimizer.

Parameters:

  • max_evals (int) –

    Maximum number of configurations to evaluate.

  • random_state (int, default: 64 ) –

    Inherited from OptimizerSettings. Controls shuffle order of search space combinations.