Back to home page

EIC code displayed by LXR

 
 

    


File indexing completed on 2026-08-12 08:24:55

0001 """Pydantic models for Ax-based optimizer configuration."""
0002 
0003 from __future__ import annotations
0004 
0005 from typing import Any, Literal, Optional
0006 
0007 from pydantic import BaseModel, ConfigDict, Field, field_validator, model_validator
0008 
0009 from aid2e.utilities.configurations.optimization_registry import register
0010 
0011 from ._resolver import validate_generator_name
0012 
0013 
0014 class AxOptimizerConfig(BaseModel):
0015     """Configure the Ax optimizer backend for AID2E.
0016 
0017     Define the tuning parameters used by the Ax-based Bayesian optimization
0018     workflow, including initialization behavior, model-generation settings, and
0019     core iteration controls.
0020 
0021     Attributes:
0022         initialization_strategy: Choose how the initial design points are drawn
0023             before model-based generation begins. Supported values are
0024             ``"sobol"``, ``"uniform"``, and ``"center"``.
0025         generator: Specify the Ax generator enum name used for model-based
0026             candidate generation. The default is ``"BOTORCH_MODULAR"``.
0027         generator_kwargs: Provide keyword arguments for Ax ``GeneratorSpec``
0028             setup (for example, model configuration details).
0029         generator_gen_kwargs: Provide generation-time keyword arguments passed
0030             into Ax candidate generation (for example,
0031             ``model_gen_options`` budgets).
0032         objective_thresholds: Optionally map objective metric names to
0033             threshold values for multi-objective optimization.
0034         n_initial_samples: Set the number of initialization trials.
0035         n_iterations: Set the total optimization iteration budget.
0036         batch_size: Set the number of candidates proposed per iteration.
0037         seed: Set an optional random seed for reproducibility.
0038 
0039     Examples:
0040         >>> config = AxOptimizerConfig(
0041         ...     initialization_strategy="sobol",
0042         ...     generator="BOTORCH_MODULAR",
0043         ...     generator_kwargs={"fit_out_of_design": False},
0044         ...     generator_gen_kwargs={"model_gen_options": {"acqf_optimizer_kwargs": {"num_restarts": 8}}},
0045         ...     n_initial_samples=12,
0046         ...     n_iterations=60,
0047         ...     batch_size=2,
0048         ...     seed=42,
0049         ... )
0050         >>> config.generator
0051         'BOTORCH_MODULAR'
0052 
0053     Notes:
0054         Legacy fields such as ``surrogate_model`` and
0055         ``acquisition_function`` are explicitly rejected.
0056     """
0057     # TODO: Remove the extra fields once the legacy config surface is fully retired.
0058     model_config = ConfigDict(extra="forbid")
0059     initialization_strategy: Literal["sobol", "uniform", "center"] = Field(
0060         default="sobol",
0061         description=(
0062             "Initialization strategy: 'sobol' for quasi-random initialization, "
0063             "'uniform' for uniform random initialization, or 'center' for one "
0064             "center point followed by additional initialization samples."
0065         ),
0066     )
0067     generator: str = Field(
0068         default="BOTORCH_MODULAR",
0069         description=(
0070             "Ax generator enum name. This backend currently supports "
0071             "'BOTORCH_MODULAR' and treats it as the default model-based backend."
0072         ),
0073     )
0074     generator_kwargs: dict[str, Any] = Field(
0075         default_factory=dict,
0076         description=(
0077             "Keyword arguments passed to Ax's GeneratorSpec for the configured "
0078             "model-based generator. YAML-friendly string values are resolved to "
0079             "supported Ax / BoTorch classes at runtime."
0080         ),
0081     )
0082     generator_gen_kwargs: dict[str, Any] = Field(
0083         default_factory=dict,
0084         description=(
0085             "Generation-time kwargs passed through Ax into candidate generation, "
0086             "such as optimizer budgets under 'model_gen_options'."
0087         ),
0088     )
0089     objective_thresholds: Optional[dict[str, float]] = Field(
0090         default=None,
0091         description=(
0092             "Optional objective thresholds for multi-objective optimization, "
0093             "keyed by metric name."
0094         ),
0095     )
0096     n_initial_samples: int = Field(
0097         default=10,
0098         ge=1,
0099         description="Number of samples in the initialization phase.",
0100     )
0101     n_iterations: int = Field(
0102         default=50,
0103         ge=1,
0104         description="Total number of optimization iterations.",
0105     )
0106     batch_size: int = Field(
0107         default=1,
0108         ge=1,
0109         description="Number of candidates to evaluate per iteration.",
0110     )
0111     seed: Optional[int] = Field(
0112         default=None,
0113         description="Random seed for reproducibility. If None, results are non-deterministic.",
0114     )
0115 
0116     @model_validator(mode="before")
0117     @classmethod
0118     def reject_legacy_fields(cls, raw_value: Any) -> Any:
0119         """Fail fast on the retired Ax config surface."""
0120         if not isinstance(raw_value, dict):
0121             return raw_value
0122 
0123         legacy_fields = [
0124             field_name
0125             for field_name in ("surrogate_model", "acquisition_function")
0126             if field_name in raw_value
0127         ]
0128         if legacy_fields:
0129             joined = ", ".join(legacy_fields)
0130             raise ValueError(
0131                 f"AxOptimizerConfig no longer accepts legacy fields: {joined}. "
0132                 "Use 'generator', 'generator_kwargs', and 'generator_gen_kwargs' "
0133                 "instead."
0134             )
0135         return raw_value
0136 
0137     @field_validator("generator")
0138     @classmethod
0139     def normalize_generator(cls, value: str) -> str:
0140         """Normalize the configured generator to an Ax enum-style name."""
0141         return validate_generator_name(value)
0142 
0143 
0144 register("ax", AxOptimizerConfig)