File indexing completed on 2026-08-12 08:24:56
0001 """Pydantic configuration model for PyMOO-based optimizers.
0002
0003 Supported algorithms and their best use cases:
0004
0005 - ``ga``: Single-objective Genetic Algorithm.
0006 - ``nsga2``: NSGA-II — fast, well-tested, good for 2-3 objectives.
0007 - ``nsga3``: NSGA-III — structured reference directions, 3+ objectives.
0008 - ``moead``: MOEA/D — weight-decomposition, highly customisable, 3+ objectives.
0009
0010 Auto-registration with the canonical optimizer config registry happens at
0011 import time so configuration utilities can resolve ``"pymoo"`` lazily.
0012
0013 Project: AID2E v0.0.0 — AI assisted Detector Design for EIC
0014 Homepage: https://aid2e.github.io/AID2E-framework
0015 Repository: https://github.com/aid2e/AID2E-framework.git
0016 """
0017
0018 from typing import Literal, Optional
0019 from pydantic import BaseModel, Field
0020
0021 from aid2e.utilities.configurations.optimization_registry import register
0022
0023
0024 PyMOOAlgorithm = Literal["ga", "nsga2", "nsga3", "moead"]
0025
0026
0027 class PyMOOOptimizerConfig(BaseModel):
0028 """Configuration for PyMOO-based evolutionary optimizers.
0029
0030 Attributes:
0031 algorithm: Optional evolutionary algorithm identifier. If omitted,
0032 AID2E infers ``"ga"`` for single-objective problems and
0033 ``"nsga2"`` for multi-objective problems.
0034 pop_size: Population size (number of individuals per generation).
0035 n_offsprings: Number of offspring generated each generation. ``None``
0036 defaults to ``pop_size``.
0037 crossover_prob: Simulated Binary Crossover (SBX) probability.
0038 crossover_eta: SBX distribution index — larger values produce offspring
0039 closer to the parents.
0040 mutation_eta: Polynomial mutation distribution index.
0041 n_iterations: Number of generations to run when using this config in
0042 declarative/runtime-driven flows.
0043 n_partitions: Reference-direction partitions for NSGA-III and MOEA/D.
0044 The total number of reference directions grows combinatorially with
0045 this value and ``n_objectives``. Ignored for NSGA-II.
0046 seed: Random seed for reproducibility. ``None`` yields non-deterministic
0047 results.
0048 verbose: Whether PyMOO prints per-generation progress to stdout.
0049
0050 Examples:
0051 >>> config = PyMOOOptimizerConfig(
0052 ... pop_size=100,
0053 ... seed=42,
0054 ... )
0055 >>> config.algorithm is None
0056 True
0057 >>> config2 = PyMOOOptimizerConfig(algorithm="nsga3", n_partitions=12)
0058
0059 Notes:
0060 - ``ga`` is the recommended default for single-objective problems.
0061 - NSGA-II is the recommended default for 2-objective problems.
0062 - For 3+ objectives consider NSGA-III or MOEA/D — their reference
0063 direction structures are better suited to high-dimensional fronts.
0064 - ``n_partitions`` has a strong effect on runtime for NSGA-III/MOEA/D;
0065 start with 12 for 2-3 objectives and reduce for 4+ objectives.
0066 """
0067
0068 algorithm: Optional[PyMOOAlgorithm] = Field(
0069 default=None,
0070 description=(
0071 "Optional evolutionary algorithm. If omitted, AID2E infers 'ga' "
0072 "for single-objective problems and 'nsga2' for multi-objective problems."
0073 ),
0074 )
0075 pop_size: int = Field(
0076 default=100,
0077 ge=2,
0078 description="Population size — number of candidate solutions per generation.",
0079 )
0080 n_offsprings: Optional[int] = Field(
0081 default=None,
0082 ge=1,
0083 description=(
0084 "Number of offspring per generation. "
0085 "Defaults to pop_size when None."
0086 ),
0087 )
0088 crossover_prob: float = Field(
0089 default=0.9,
0090 ge=0.0,
0091 le=1.0,
0092 description="SBX crossover probability.",
0093 )
0094 crossover_eta: float = Field(
0095 default=15.0,
0096 gt=0.0,
0097 description="SBX crossover distribution index.",
0098 )
0099 mutation_eta: float = Field(
0100 default=20.0,
0101 gt=0.0,
0102 description="Polynomial mutation distribution index.",
0103 )
0104 n_iterations: int = Field(
0105 default=50,
0106 ge=1,
0107 description="Number of generations for runtime-driven optimization loops.",
0108 )
0109 n_partitions: int = Field(
0110 default=12,
0111 ge=1,
0112 description=(
0113 "Reference-direction partitions for NSGA-III and MOEA/D. "
0114 "Ignored for NSGA-II."
0115 ),
0116 )
0117 seed: Optional[int] = Field(
0118 default=None,
0119 description="Random seed. None means non-deterministic.",
0120 )
0121 verbose: bool = Field(
0122 default=False,
0123 description="Print per-generation statistics to stdout.",
0124 )
0125
0126 def resolve_algorithm(self, n_objectives: int) -> PyMOOAlgorithm:
0127 """Resolve the algorithm for the given objective count.
0128
0129 Args:
0130 n_objectives: Number of objectives in the optimization problem.
0131
0132 Returns:
0133 Concrete PyMOO algorithm identifier.
0134
0135 Raises:
0136 ValueError: If the configured explicit algorithm is incompatible
0137 with the objective count.
0138 """
0139 if n_objectives < 1:
0140 raise ValueError("n_objectives must be >= 1")
0141
0142 if self.algorithm is None:
0143 return "ga" if n_objectives == 1 else "nsga2"
0144
0145 if self.algorithm == "ga" and n_objectives != 1:
0146 raise ValueError(
0147 "PyMOO algorithm 'ga' only supports single-objective problems. "
0148 f"Received {n_objectives} objectives."
0149 )
0150
0151 if self.algorithm in {"nsga2", "nsga3", "moead"} and n_objectives == 1:
0152 raise ValueError(
0153 f"PyMOO algorithm '{self.algorithm}' requires a multi-objective "
0154 "problem. Use 'ga' or omit 'algorithm' for single-objective optimization."
0155 )
0156
0157 return self.algorithm
0158
0159
0160
0161 register("pymoo", PyMOOOptimizerConfig)