Skip to content

About

Echo-Guided Rescue Optimization: reproducibility package (code and CEC 2017 results) for the EGRO paper

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

26 Commits

Folders and files

Repository files navigation

EGRO: Echo-Guided Rescue Optimization

Reproducibility package for the paper "Echo-Guided Rescue Optimization: a gradient-guided hybrid metaheuristic for engineering optimization problems".

EGRO couples L-BFGS-B gradient descent with a population-based inner search. A gradient skier descends to a promising basin, an isolated rescue group then searches a region whose geometry follows from the descent trajectory, and a dimension-normalized isolation index decides whether the group has converged or has moved beyond the landing-reference level, in which case its best position seeds a fresh descent. Both instantiations draw the initial group from the same echo-shaped Gaussian, whose principal axis is aligned with the descent direction, and differ in the inner search that follows:

  • EGRO-PSO: a Clerc-Kennedy particle swarm.
  • EGRO-CMA: CMA-ES, which adapts the covariance from that starting shape.

This repository contains the implementation, the raw per-run results behind every table in the paper, and the scripts that regenerate the tables and statistics.

Using EGRO on your own problem

The two optimizers live in a single stand-alone file, egro.py, with no dependency on the benchmark code. Copy it next to your script, or install the requirements and import it from this repository.

pip install numpy scipy cma      # cma is needed by EGRO-CMA only

Minimal example

import numpy as np
from egro import egro_cma, egro_pso

def rastrigin(x):                      # any callable  f(x: ndarray) -> float
    return 10 * len(x) + np.sum(x**2 - 10 * np.cos(2 * np.pi * x))

res = egro_cma(rastrigin, bounds=(-5.12, 5.12), dim=10, max_evals=100_000, seed=0)
print(res.f, res.x)                    # best value and best point

egro_pso(...) has the same signature. The paper's recommended instantiation is EGRO-CMA. EGRO-PSO needs only NumPy and SciPy.

Inputs

argument meaning
func objective to minimize, f(x) -> float, x a NumPy array
bounds (lb, ub) applied to every coordinate (then pass dim), or one pair per coordinate, [(lb_1, ub_1), ..., (lb_d, ub_d)]
max_evals total budget of objective evaluations. Every call is counted, including the ones spent on finite-difference gradients (the paper uses 10_000 * d)
seed integer for reproducible runs
grad optional g(x) -> ndarray. When given, the skier uses it instead of finite differences and only true objective calls are charged to max_evals
maximize set True to maximize instead
callback optional callback(info: dict) called after every skier and rescue-group pair with evals, best_f, best_x, rho, case, and queue_size

Any key of egro.DEFAULTS can be overridden as a keyword argument, for example egro_cma(f, bounds, max_evals, n_per_group=30, alpha_in=0.8). The defaults are the values used for every experiment in the paper. The ones worth touching first are max_evals (more budget helps most), n_per_group (agents per rescue group, larger for very rugged landscapes), and T_g (evaluations per descent, smaller if gradients are expensive or uninformative).

Output

EGROResult with fields x, f, evals, grad_evals, n_skiers, n_case1 (groups that converged inside their region), n_case2 (groups that found something beyond the landing-reference level and re-seeded the queue), and history, a list of (evals, best_f) after each outer iteration.

Constraints and heterogeneous bounds

EGRO is a bound-constrained optimizer. Handle other constraints with a penalty inside func, as the paper does for the engineering problems:

def penalized(x):
    cost, g = my_design(x)             # g: list of normalized constraints g_i <= 0
    return cost + 1e6 * sum(max(0.0, gi) for gi in g)

res = egro_cma(penalized, bounds=[(0.1, 2.0), (0.1, 10.0), (0.1, 10.0), (0.1, 2.0)],
               max_evals=40_000, seed=0)

With per-coordinate bounds the search runs in the unit cube mapped onto the physical box, exactly as the real-world campaign of the paper did. With a scalar (lb, ub) pair it runs in the original coordinates, as the CEC campaign did.

When it helps and when it does not

The framework pays for its descents only when the objective carries usable gradient signal at the scale of the initial sampling. On landscapes that are flat or oscillatory at that scale (the Lennard-Jones potential in the paper is the documented failure case) the skiers stop almost immediately and the method degrades to a poorly seeded restart scheme. The paper proposes a cheap check, not yet validated, before committing a budget: evaluate finite-difference gradient magnitudes at a few random points and, if they are negligible, consider running the inner optimizer on its own.

examples/quickstart.py walks through the three cases above, and examples/check_equivalence.py verifies that egro.py returns results bit-identical to the campaign scripts in code/ for the same seed.

Reproducing the paper

pip install setuptools==75.8.0 numpy==2.4.4 scipy==1.17.1 opfunu==1.0.1 cma==4.4.4
python egro.py                                  # 30-second demo of both optimizers on Rastrigin
python validate.py                              # recomputes the paper's headline numbers and asserts them
python code/gen_official_tables.py results .    # CEC 2017 tables and critical-difference diagrams
python code/gen_robustness_table.py results .   # robustness table
python code/gen_lbfgsb_table.py results .       # multi-start L-BFGS-B table
python code/gen_cec2011_table.py results .      # real-world table
python code/gen_truss_table.py results .        # truss table
python code/gen_truss_convergence.py results .  # truss convergence figure (Figure 5)
python code/gradient_test_unimodal_summary.py   # exact-gradient test of Section 4.3
python code/analyze_cec2011.py                  # Holm-corrected rank-sum tests on the real-world problems
python code/engineering_problems.py             # verifies the engineering formulations against their published optima

validate.py recomputes the Friedman ranks, the Nemenyi groups, the win tallies, the robustness check, the multi-start L-BFGS-B ranks, the inner-search ablation counts, the exact-gradient test, and the real-world means straight from results/, and it re-evaluates all 210 stored truss designs with the finite-element model. It asserts the values printed in the paper, so a silent regression fails the run.

setuptools is pinned because opfunu imports pkg_resources, which was removed from setuptools 81.

What produces each result

Paper section Scripts Result files
4.2 to 4.4, CEC 2017 comparison, scalability, and robustness code/cec2017_official.py (campaign, run by the workflows in .github/workflows/), code/merge_official.py, code/gen_official_tables.py, code/gen_robustness_table.py results/cec2017_official_d{10,30,50}.json, results/official_stats.json
4.3, exact-gradient test on the unimodal functions code/gradient_test_unimodal.py (campaign), code/gradient_test_unimodal_summary.py results/gradient_test_unimodal.json
4.5, inner-search ablation code/cec2017_official.py --algos EGRO-PSO --prefix egro_pso_official (campaign, run by .github/workflows/egro_pso_official.yml), validate.py (counts) results/egro_pso_official_d{10,30,50}.json, compared with results/cec2017_official_d{10,30,50}.json
4.6, the local engine alone code/lbfgsb_baseline.py (campaign), code/gen_lbfgsb_table.py results/lbfgsb_baseline_d{10,30,50}.json
4.8, real-world problems code/cec2011_problems.py, code/run_cec2011.py (campaign), code/analyze_cec2011.py, code/gen_cec2011_table.py results/cec2011_results.json
4.9, 10-bar truss code/engineering_problems.py, code/truss_campaign_v2.py (campaign), code/gen_truss_table.py, code/gen_truss_convergence.py (Figure 5) results/engineering_results_truss_v2.json

Two campaigns are included although the paper does not report them: multi-start L-BFGS-B on the real-world problems (code/lbfgsb_realworld.py, results/lbfgsb_realworld.json) and the four classical design problems, spring, pressure vessel, welded beam, and speed reducer (code/run_engineering.py, results/engineering_results_with_truss.json).

CEC 2017 campaign

  • Benchmark: the 29 official CEC 2017 functions F1 and F3 to F30 via opfunu 1.0.1, at d = 10, 30, and 50. opfunu renumbers the surviving functions consecutively (its F22017 is the official F3), so code/cec2017_official.py maps official function k to opfunu index k−1 for k ≥ 3. All indices in results/cec2017_official_d*.json and in the paper are official.
  • Protocol: 30 runs per (function, algorithm), seed equal to the run index, and a budget of 10,000 × d evaluations counted by a shared wrapper, finite-difference gradient calls included.
  • Runs are seeded, but on some composition functions floating-point differences between CPUs can change individual runs: of 8 EGRO-PSO runs at d = 50 computed both on GitHub Actions and on Modal, 3 ended at different values. The metadata block of each results file records the runner.
  • Competitors: EGRO-CMA, CMA-ES, L-SHADE, DE, PSO, GWO, and WOA. CMA-ES uses the cma package in a single run with its default settings. The other five competitors are study-specific implementations written from the original publications. EGRO-PSO, the second instantiation, runs under the same protocol and seeds in a separate file and enters only the ablation of Section 4.5.
  • Absolute errors below 1e-8 are treated as zero before averaging and ranking, following the CEC convention.

Known defects of the opfunu 1.0.1 instances, disclosed in the paper

  • Official F18 and F30 return non-finite values at d = 10 and are excluded at that dimension, leaving 27 valid functions.
  • On a subset of functions (official F10, F12, F16, F17, F20, F22, F26, and F27 at d = 10, with similar subsets at d = 30 and 50) the stated reference optimum lies above the attainable minimum, so signed errors can be negative. Rankings are unaffected, because the reference is a per-function constant, but comparisons against results produced with the official evaluator should treat these functions with caution. Newly generated results store the best decision vector per run (xbest) to allow external re-evaluation.

The mechanism is documented for reproducibility. opfunu sets each declared optimum to 100 times its own index, hence 100 below the official value from official F3 onward, and on the affected functions the declared optimum is not the minimum of the coded surface: an independent optimizer reaches −1088.7 on official F10 against a declared 900. Because all seven algorithms optimize the same surfaces under the same budget, each declared optimum is a per-function constant and cannot reorder them. gen_robustness_table.py confirms this by repeating the ranking over only the functions with a consistent reference: the leading order is unchanged at every dimension, and EGRO-CMA improves from 2.24 to 1.97 at d = 10. Absolute error values are therefore not comparable with results produced by the competition's reference implementation.

Headline results

Mean tie-aware Friedman rank, lower is better (regenerate with gen_official_tables.py):

algorithm d = 10 (27 fns) d = 30 (29 fns) d = 50 (29 fns)
L-SHADE 1.50 1.26 1.26
EGRO-CMA 2.24 2.41 2.55
CMA-ES 4.09 2.67 2.47
DE 3.41 5.31 5.72
WOA 5.07 4.69 4.62
GWO 5.52 5.34 5.10
PSO 6.17 6.31 6.28

EGRO-CMA is not significantly different from L-SHADE at any dimension (Nemenyi, α = 0.05). It beats its own CMA-ES engine on 24 of 27 functions at d = 10 and is not significantly different from it at d = 30 and d = 50 (15 of 29 and 12 of 29 wins). Ranked in the same field, multi-start L-BFGS-B places fourth of eight at d = 10 and d = 30 and shares fourth place at d = 50, and EGRO-CMA beats it on 19 of 27, 22 of 29, and 28 of 29 functions.

EGRO-PSO, run with the same functions, budget, and seeds, beats standalone PSO on 24 of 27, 28 of 29, and 28 of 29 functions, and as an eighth entry in the field it would rank third at d = 10 and fourth at d = 30 and d = 50. EGRO-CMA has the lower mean error than EGRO-PSO on 17, 16, and 22 functions (9, 12, and 7 losses), a margin that a Wilcoxon signed-rank test over the functions does not find significant at any dimension.

The main comparison estimates gradients by forward finite differences, so each gradient costs d + 1 evaluations. Rerunning EGRO-CMA on the unimodal functions F1 and F3 with the exact gradient, each gradient call charged as one evaluation and everything else unchanged, brings the error below 1e-8 in all 30 runs at d = 10, 30, and 50, matching standalone CMA-ES. The finite-difference version of that test reproduces the values of the main comparison.

Engineering campaign

code/engineering_problems.py defines five constrained problems: the tension/compression spring, the pressure vessel, the welded beam, the speed reducer, and the 10-bar truss, which assembles and solves K·u = F per evaluation with vertical displacement limits at the four free nodes, following the benchmark formulation. verify_all() checks each formulation against its published optimum. The paper reports the truss, run by code/truss_campaign_v2.py (30 seeds, budget snapshots, best-feasible tracking at a 1e-4 tolerance, and the best design vector with its constraint residual stored per run).

Repository layout

  • egro.py: stand-alone EGRO-PSO and EGRO-CMA for use on any problem.
  • examples/: quick start and the equivalence check against the campaign code.
  • code/: algorithms (egro_cma_competition.py, egro_pso_vs_cmaes.py), campaign runners, and analysis scripts.
  • results/: the raw per-run results listed in the table above, plus legacy files from the pre-renumbering campaign (CORRECTED_*, engineering/), kept for provenance. Their EGRO-PSO entries come from an earlier version whose swarm was drawn from an isotropic Gaussian.
  • .github/workflows/: the CI matrices that produced the CEC, EGRO-PSO, and baseline campaigns.
  • validate.py: one-command reproduction of the paper's headline numbers.

About

Echo-Guided Rescue Optimization: reproducibility package (code and CEC 2017 results) for the EGRO paper

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages