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.
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
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 pointegro_pso(...) has the same signature. The paper's recommended instantiation
is EGRO-CMA. EGRO-PSO needs only NumPy and SciPy.
| 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).
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.
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.
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.
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.
| 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).
- Benchmark: the 29 official CEC 2017 functions F1 and F3 to F30 via
opfunu1.0.1, at d = 10, 30, and 50.opfunurenumbers the surviving functions consecutively (itsF22017is the official F3), socode/cec2017_official.pymaps official function k toopfunuindex k−1 for k ≥ 3. All indices inresults/cec2017_official_d*.jsonand 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
cmapackage 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.
- 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.
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.
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).
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.