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)]