Back to home page

EIC code displayed by LXR

 
 

    


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

0001 """Regression tests for PyMOO default algorithm inference."""
0002 
0003 from __future__ import annotations
0004 
0005 import pytest
0006 
0007 from aid2e.optimizers.base import SearchSpace
0008 from aid2e.optimizers.pymoo import PyMOOOptimizer, PyMOOOptimizerConfig
0009 from aid2e.utilities import build_optimizer_from_config
0010 from aid2e.utilities.configurations import OptimizerConfiguration
0011 from aid2e.utilities.configurations.problem_config import ProblemConfigLoader
0012 
0013 
0014 def _make_search_space() -> SearchSpace:
0015     return SearchSpace(
0016         parameters={
0017             "x": {"type": "range", "value": 0.5, "bounds": [0.0, 1.0]},
0018             "y": {"type": "range", "value": 0.5, "bounds": [0.0, 1.0]},
0019         }
0020     )
0021 
0022 
0023 def _complete_generation(optimizer: PyMOOOptimizer) -> None:
0024     candidates = optimizer.suggest_candidates()
0025     start_index = len(optimizer.get_trials()) - len(candidates)
0026     for offset, parameters in enumerate(candidates):
0027         trial_index = start_index + offset
0028         if optimizer.n_objectives == 1:
0029             metrics = {"loss": float(parameters["x"] + parameters["y"])}
0030         else:
0031             metrics = {
0032                 "f1": float(parameters["x"]),
0033                 "f2": float(parameters["y"]),
0034             }
0035         optimizer.update_with_results(
0036             trial_index=trial_index,
0037             parameters=parameters,
0038             metrics=metrics,
0039         )
0040 
0041 
0042 def test_config_resolve_algorithm_defaults() -> None:
0043     config = PyMOOOptimizerConfig(n_iterations=4)
0044     assert config.n_iterations == 4
0045     assert config.resolve_algorithm(1) == "ga"
0046     assert config.resolve_algorithm(2) == "nsga2"
0047 
0048 
0049 @pytest.mark.parametrize(
0050     ("algorithm", "n_objectives", "message"),
0051     [
0052         ("ga", 2, "single-objective"),
0053         ("nsga2", 1, "single-objective optimization"),
0054         ("nsga3", 1, "single-objective optimization"),
0055         ("moead", 1, "single-objective optimization"),
0056     ],
0057 )
0058 def test_config_rejects_incompatible_explicit_algorithms(
0059     algorithm: str,
0060     n_objectives: int,
0061     message: str,
0062 ) -> None:
0063     config = PyMOOOptimizerConfig(algorithm=algorithm)
0064     with pytest.raises(ValueError, match=message):
0065         config.resolve_algorithm(n_objectives)
0066 
0067 
0068 def test_single_objective_optimizer_defaults_to_ga_and_round_trips_state() -> None:
0069     optimizer = PyMOOOptimizer(
0070         search_space=_make_search_space(),
0071         config=PyMOOOptimizerConfig(pop_size=4, n_offsprings=4, seed=11),
0072         objective_names=["loss"],
0073         seed=11,
0074     )
0075 
0076     assert optimizer.resolved_algorithm == "ga"
0077     assert optimizer._algorithm.__class__.__name__ == "GA"
0078 
0079     _complete_generation(optimizer)
0080     _complete_generation(optimizer)
0081 
0082     results = optimizer.get_optimization_results()
0083     assert results["n_trials"] == len(optimizer.get_trials()) == 8
0084 
0085     state = optimizer.serialize_state()
0086     assert state["resolved_algorithm"] == "ga"
0087 
0088     restored = PyMOOOptimizer(
0089         search_space=_make_search_space(),
0090         config=PyMOOOptimizerConfig(pop_size=4, n_offsprings=4, seed=99),
0091         objective_names=["loss"],
0092         seed=99,
0093     )
0094     restored.load_state(state)
0095 
0096     assert restored.resolved_algorithm == "ga"
0097     assert restored.get_optimization_results()["n_trials"] == 8
0098     assert len(restored.get_trials()) == 8
0099 
0100 
0101 def test_multi_objective_optimizer_defaults_to_nsga2() -> None:
0102     optimizer = PyMOOOptimizer(
0103         search_space=_make_search_space(),
0104         config=PyMOOOptimizerConfig(pop_size=4, n_offsprings=4, seed=7),
0105         objective_names=["f1", "f2"],
0106         seed=7,
0107     )
0108 
0109     assert optimizer.resolved_algorithm == "nsga2"
0110     assert optimizer._algorithm.__class__.__name__ == "NSGA2"
0111 
0112     _complete_generation(optimizer)
0113     _complete_generation(optimizer)
0114 
0115     results = optimizer.get_optimization_results()
0116     assert results["n_trials"] == len(optimizer.get_trials()) == 8
0117     assert len(optimizer.get_pareto_front()) >= 1
0118 
0119 
0120 def test_explicit_incompatible_algorithm_fails_on_optimizer_init() -> None:
0121     with pytest.raises(ValueError, match="single-objective optimization"):
0122         PyMOOOptimizer(
0123             search_space=_make_search_space(),
0124             config=PyMOOOptimizerConfig(algorithm="nsga2", pop_size=4, seed=3),
0125             objective_names=["loss"],
0126             seed=3,
0127         )
0128 
0129 
0130 def test_runtime_builder_accepts_omitted_pymoo_algorithm(tmp_path) -> None:
0131     output_dir = tmp_path / "output"
0132     work_dir = tmp_path / "work"
0133     output_dir.mkdir()
0134     work_dir.mkdir()
0135 
0136     problem_cfg = ProblemConfigLoader.from_dict(
0137         {
0138             "name": "DTLZ2",
0139             "problem_type": "toy",
0140             "output_location": str(output_dir),
0141             "work_location": str(work_dir),
0142             "inline_design": {
0143                 "design_space": {
0144                     "design_parameters": {
0145                         "group1": {
0146                             "parameters": {
0147                                 "x": {"value": 0.5, "bounds": [0.0, 1.0]},
0148                                 "y": {"value": 0.5, "bounds": [0.0, 1.0]},
0149                             }
0150                         }
0151                     }
0152                 }
0153             },
0154             "objectives": [
0155                 {"name": "f1", "direction": "minimize"},
0156                 {"name": "f2", "direction": "minimize"},
0157             ],
0158         },
0159     )
0160     optimizer_cfg = OptimizerConfiguration(
0161         name="pymoo",
0162         type="evolutionary",
0163         parameters={
0164             "pop_size": 4,
0165             "n_offsprings": 4,
0166             "n_iterations": 2,
0167             "seed": 5,
0168         },
0169     )
0170 
0171     optimizer = build_optimizer_from_config(problem_cfg, optimizer_cfg)
0172 
0173     assert isinstance(optimizer, PyMOOOptimizer)
0174     assert optimizer.resolved_algorithm == "nsga2"
0175     assert optimizer.config.algorithm is None