Source code for denia.objectives

"""Multi-objective optimization: Pareto fronts and scalarized acquisition.

Real campaigns rarely optimize one number: yield vs selectivity, performance
vs cost, strength vs weight. DENIA handles multiple targets with per-target
surrogates and ParEGO-style random Chebyshev scalarization of per-target
expected improvements, so each slot of a recommended batch probes a different
trade-off region of the Pareto front.
"""

from __future__ import annotations

from typing import List, Sequence

import numpy as np
import pandas as pd


[docs] def pareto_mask(Y: np.ndarray, maximize: Sequence[bool]) -> np.ndarray: """Boolean mask of non-dominated rows of Y (n_points x n_objectives).""" Y = np.asarray(Y, dtype=float) Z = Y.copy() for j, mx in enumerate(maximize): if not mx: Z[:, j] = -Z[:, j] n = len(Z) mask = np.ones(n, dtype=bool) finite = np.isfinite(Z).all(axis=1) mask &= finite for i in range(n): if not mask[i]: continue dominates_i = (Z >= Z[i]).all(axis=1) & (Z > Z[i]).any(axis=1) & finite if dominates_i.any(): mask[i] = False return mask
[docs] def chebyshev_scalarize(scores: np.ndarray, weights: np.ndarray, rho: float = 0.05) -> np.ndarray: """Augmented Chebyshev scalarization of per-objective scores (higher=better). scores: (n_candidates, n_objectives), already normalized to comparable scale. """ w = weights / (weights.sum() + 1e-12) weighted = scores * w[None, :] return weighted.min(axis=1) + rho * weighted.sum(axis=1)
[docs] def normalize_scores(scores: np.ndarray) -> np.ndarray: """Min-max normalize each objective's score column to [0, 1].""" s = np.asarray(scores, dtype=float) lo, hi = np.nanmin(s, axis=0), np.nanmax(s, axis=0) rng = np.where(hi - lo > 1e-12, hi - lo, 1.0) return (s - lo) / rng
[docs] def pareto_front(history: pd.DataFrame, targets: List[str], maximize: Sequence[bool]) -> pd.DataFrame: """Non-dominated subset of the campaign history over the given targets.""" Y = np.column_stack([pd.to_numeric(history[t], errors="coerce").values for t in targets]) return history.loc[pareto_mask(Y, maximize)]