| """CLI entry point for a single CEC-2022 run. |
| |
| Usage:: |
| |
| python -m ahdcma.cli.run_benchmark \\ |
| --algo ahdcma --func F5_rastrigin --dim 10 --seed 0 \\ |
| --output outputs/runs/cec2022/ |
| |
| Writes ``{output}/{run_id}/result.json`` with the final best fitness, |
| config snapshot, history summary and wall time. |
| """ |
|
|
| from __future__ import annotations |
|
|
| import argparse |
| import json |
| import time |
| from pathlib import Path |
| from typing import Any |
|
|
| import numpy as np |
| import yaml |
|
|
| from ahdcma.algorithms.ahd_cma import AHDCMA |
| from ahdcma.algorithms.base import Optimizer, SearchSpace |
| from ahdcma.algorithms.bohb import BOHB |
| from ahdcma.algorithms.cmaes_restart import BIPOPCMAES, IPOPCMAES |
| from ahdcma.algorithms.cmaes_wrapper import CMAES |
| from ahdcma.algorithms.doa import DOA |
| from ahdcma.algorithms.gego import GEGO |
| from ahdcma.algorithms.grid_search import GridSearch |
| from ahdcma.algorithms.gwo import GWO |
| from ahdcma.algorithms.hyperband import Hyperband |
| from ahdcma.algorithms.ihaho import IHAHO |
| from ahdcma.algorithms.optuna_baseline import OptunaTPE |
| from ahdcma.algorithms.pso import PSO |
| from ahdcma.algorithms.random_search import RandomSearch |
| from ahdcma.algorithms.scso import SCSO |
| from ahdcma.algorithms.woa import WOA |
| from ahdcma.fitness.cec2022 import ALL_NAMES, get_problem |
| from ahdcma.utils.logging import make_run_id, setup_logging |
| from ahdcma.utils.seed import set_global_seed |
|
|
| ALGO_REGISTRY: dict[str, type[Optimizer]] = { |
| "ahdcma": AHDCMA, |
| "doa": DOA, |
| "cmaes": CMAES, |
| "ipop": IPOPCMAES, |
| "bipop": BIPOPCMAES, |
| "pso": PSO, |
| "gwo": GWO, |
| "woa": WOA, |
| "scso": SCSO, |
| "optuna": OptunaTPE, |
| "random": RandomSearch, |
| "grid": GridSearch, |
| "hyperband": Hyperband, |
| "bohb": BOHB, |
| "gego": GEGO, |
| "ihaho": IHAHO, |
| } |
|
|
|
|
| def _load_algo_config(algo: str, *, seed: int, max_evals: int, pop: int) -> dict[str, Any]: |
| cfg_path = Path(__file__).resolve().parents[3] / "configs" / "algo" / f"{algo}.yaml" |
| with cfg_path.open() as f: |
| cfg = yaml.safe_load(f) |
| |
| cfg["seed"] = seed |
| cfg["population_size"] = pop |
| cfg["max_generations"] = max(1, max_evals // pop) |
| return dict(cfg) |
|
|
|
|
| def run_single( |
| algo: str, |
| func: str, |
| dim: int, |
| seed: int, |
| *, |
| max_evals: int = 30000, |
| pop: int = 30, |
| output_dir: Path | str = "outputs/runs/cec2022", |
| ) -> dict[str, Any]: |
| """Execute a single algo/func/dim/seed run and write result.json.""" |
| if algo not in ALGO_REGISTRY: |
| raise KeyError(f"unknown algorithm {algo!r}; choose from {sorted(ALGO_REGISTRY)}") |
| if func not in ALL_NAMES: |
| raise KeyError(f"unknown CEC-2022 function {func!r}; choose from {ALL_NAMES}") |
|
|
| set_global_seed(seed) |
| run_id = make_run_id(algo, func, seed, dim=dim) |
| out_root = Path(output_dir) / run_id |
| out_root.mkdir(parents=True, exist_ok=True) |
|
|
| log_dir = out_root / "logs" |
| setup_logging(run_id, log_dir=log_dir) |
|
|
| problem = get_problem(func, dim) |
| sp = SearchSpace( |
| dim=dim, |
| lower=np.full(dim, problem.bound[0], dtype=np.float64), |
| upper=np.full(dim, problem.bound[1], dtype=np.float64), |
| ) |
| cfg = _load_algo_config(algo, seed=seed, max_evals=max_evals, pop=pop) |
| cls = ALGO_REGISTRY[algo] |
| opt = cls(cfg, problem, sp, run_id=run_id) |
|
|
| t0 = time.time() |
| result = opt.optimize() |
| wall = time.time() - t0 |
|
|
| summary = { |
| "run_id": run_id, |
| "algo": algo, |
| "func": func, |
| "dim": dim, |
| "seed": seed, |
| "best_f": float(result.best_f), |
| "best_x": result.best_x.tolist(), |
| "wall_time": wall, |
| "n_generations": len(result.history), |
| "config": cfg, |
| "best_fitness_curve": list(result.history.best_fitness), |
| "mode_curve": list(result.history.mode_per_gen), |
| } |
| (out_root / "result.json").write_text(json.dumps(summary, indent=2)) |
| return summary |
|
|
|
|
| def main() -> None: |
| p = argparse.ArgumentParser(description="Run a single CEC-2022 evaluation.") |
| p.add_argument("--algo", required=True, choices=sorted(ALGO_REGISTRY)) |
| p.add_argument("--func", required=True, choices=ALL_NAMES) |
| p.add_argument("--dim", type=int, default=10) |
| p.add_argument("--seed", type=int, default=0) |
| p.add_argument("--max-evals", type=int, default=30000) |
| p.add_argument("--pop", type=int, default=30) |
| p.add_argument("--output", default="outputs/runs/cec2022") |
| args = p.parse_args() |
| summary = run_single( |
| args.algo, |
| args.func, |
| args.dim, |
| args.seed, |
| max_evals=args.max_evals, |
| pop=args.pop, |
| output_dir=args.output, |
| ) |
| print(json.dumps({"run_id": summary["run_id"], "best_f": summary["best_f"]})) |
|
|
|
|
| if __name__ == "__main__": |
| main() |
|
|