Cookbook🔗
This page contains practical Egor parameterization recipes.
Each recipe includes:
- when to use it
- a suggested configuration
- why it helps
Notes
- Start from the closest recipe, then tune one group of parameters at a time.
- Keep random seeds fixed while comparing configurations.
- Regarding the required
xspecsparameter (i.e. the input space) used byEgor: When using continuous variables only, the simplest form is a list of[lower, upper]pairs, e.g.[[0.0, 1.0], [1.0, 10.0], [-5.0, 5.0]]. Otherwise, see the XSpecs in Python API for more complex variable types. - For complete parameter definitions, see the Python API.
Recipe 1: Cheap Low-Dimensional Objective🔗
Use when:
- dimension is low (for example 2 to 5)
- objective evaluation is cheap
- you can afford more iterations
Suggested setup:
optim = egx.Egor(
xspecs,
n_doe=30,
)
res = optim.minimize(fun, max_iters=60, seed=42)
Why it helps:
- larger DOE improves global coverage early
- cheap evaluations allow more exploration iterations
Recipe 2: Expensive Objective🔗
Use when:
- each objective call is expensive (seconds to minutes)
- you need strong information gain at each iteration
Suggested setup:
optim = egx.Egor(
xspecs,
n_doe=13,
)
res = optim.minimize(fun, max_iters=20, seed=42)
Why it helps:
- smaller DOE reduces initial expensive calls (default xdim + 1)
Recipe 3: High Dimension (d > 10)🔗
Use when:
- the number of variables is high
Suggested setup:
gp_cfg = egx.GpConfig(kpls_dim=10)
optim = egx.Egor(
xspecs,
gp_config=gp_cfg,
)
res = optim.minimize(fun, max_iters=40, seed=42)
Why it helps:
- KPLS reduces effective surrogate complexity
- improves robustness in high-dimensional GP fitting
Rule of thumb:
- around d=20, start with kpls_dim=5
- around d=100, start with kpls_dim=10
Recipe 4: Very High Dimension (d > 50)🔗
Use when:
- standard optimization stalls in very high dimension
Suggested setup:
gp_cfg = egx.GpConfig(kpls_dim=10)
optim = egx.Egor(
xspecs,
gp_config=gp_cfg,
coego_n_coop=5,
)
res = optim.minimize(fun, max_iters=60, seed=42)
Why it helps:
- CoEGO decomposes search into cooperative component groups
- better scaling behavior for very high dimension
Recipe 5: Parallel Evaluations Available🔗
Use when:
- you can evaluate multiple points concurrently
Suggested setup:
qei_cfg = egx.QEiConfig(batch=10, strategy=egx.QEiStrategy.KB, optmod=1)
optim = egx.Egor(
xspecs,
qei_config=qei_cfg,
)
res = optim.minimize(fun, max_iters=20, seed=42)
Why it helps:
- qEI proposes batches of points per iteration
- better wall-clock performance on parallel hardware
Rule of thumb:
- around d=50, try batch=5
- around d=100, try batch=10
Recipe 6: Stagnation or Poor Progress🔗
Use when:
- best objective value barely improves
- optimizer revisits similar regions
Suggested setup:
gp_cfg = egx.GpConfig(corr_spec=egx.CorrelationSpec.MATERN52)
optim = egx.Egor(
xspecs,
gp_config=gp_cfg,
trego=True,
infill_strategy=egx.InfillStrategy.WB2,
infill_optimizer=egx.InfillOptimizer.SLSQP
)
res = optim.minimize(fun, max_iters=40, seed=42)
Why it helps:
- TREGO alternates global and local trust-region behavior improves convergence
- Matern52 is often more robust on rougher landscapes
- On bad infill optimization you can try to change the infill optimizer; try
SLSQP - Default
LOG_EIoptimization on rough landscapes may be too difficult; tryWB2,WB2Sor evenEIinstead
Recipe 7: Constraint-Heavy Problems🔗
Use when:
- feasibility dominates the search difficulty
Suggested setup:
optim = egx.Egor(
xspecs,
n_cstr=n_cstr,
cstr_infill=True,
cstr_strategy=egx.ConstraintStrategy.UTB,
infill_strategy=egx.InfillStrategy.WB2,
feasible_infill_strategy=egx.FeasibleInfillStrategy.EFI_FE,
)
res = optim.minimize(fun, max_iters=30, seed=42)
Why it helps:
- UTB makes constraint handling more conservative under uncertainty
- EFI_FE increases exploration of feasible regions
Note🔗
- EFI_FE is not implemented for default infill strategies (LOG_EI), so you need to use WB2, EI or WB2S.
Recipe 8: Objective May Crash🔗
Use when:
- objective occasionally fails or returns
NaN - long runs may be interrupted and should be resumed safely
Suggested setup:
optim = egx.Egor(
xspecs,
failsafe_strategy=egx.FailsafeStrategy.VIABILITY,
)
# First run and subsequent restarts use the same outdir.
# hot_start=0 means resume from latest checkpoint if available.
res = optim.minimize(
fun,
max_iters=60,
outdir="run01",
hot_start=0,
seed=42,
)
Why it helps:
FailsafeStrategy.VIABILITYmodels failure regions and steers search away from themhot_startwith a stableoutdirlets you continue from checkpoints instead of restarting from scratch
Alternatives:
FailsafeStrategy.REJECTION: drops failed points (simplest)FailsafeStrategy.IMPUTATION: fills failed outputs with surrogate-based estimates
Recipe 9: Restart From an Existing DOE🔗
Use when:
- you already have a DOE from a previous run
- you want to continue optimization without starting from scratch
Suggested setup:
initial_doe = np.load("run01/egor_initial_doe.npy")
optim = egx.Egor(
xspecs,
doe=initial_doe,
)
res = optim.minimize(fun, max_iters=40, seed=42)
If the DOE was saved in an output directory from a previous run, you can also let Egor reload it automatically:
optim = egx.Egor(
xspecs
)
res = optim.minimize(
fun,
max_iters=40,
outdir="run01",
warm_start=True,
seed=42,
)
Why it helps:
- reuses already evaluated points instead of recomputing them
- keeps the surrogate and search history aligned with prior work
- makes long optimization runs easier to resume after interruptions
Recipe 10: Constraints Not of the Form ≤ 0🔗
Use when:
- your constraint functions return values that need different feasibility interpretations
- constraints are equality constraints (e.g.,
g(x) = 0) - constraints have lower bounds (e.g.,
g(x) ≥ bound) - constraints have interval bounds (e.g.,
lower ≤ g(x) ≤ upper)
Suggested setup:
import egobox as egx
# Example: constraint must be >= 0 (instead of default <= 0)
optim = egx.Egor(
xspecs,
cstr_specs=[egx.CstrSpec.geq(0.0)], # g(x) >= 0
)
res = optim.minimize(fun, max_iters=40, seed=42)
# Example: equality constraint g(x) = target
optim = egx.Egor(
xspecs,
cstr_specs=[egx.CstrSpec.eq(target_value)],
)
res = optim.minimize(fun, max_iters=40, seed=42)
# Example: constraint in interval [lower, upper]
optim = egx.Egor(
xspecs,
cstr_specs=[egx.CstrSpec.btw(lower_bound, upper_bound)],
)
res = optim.minimize(fun, max_iters=40, seed=42)
Why it helps:
cstr_specsdefines the semantics of each constraint beyond the default≤ 0CstrSpec.leq(bound): constraintg(x) ≤ boundCstrSpec.geq(bound): constraintg(x) ≥ boundCstrSpec.eq(value): equality constraintg(x) = valueCstrSpec.btw(lower, upper): interval constraintlower ≤ g(x) ≤ upper
Note:
- The constraint function
funshould return raw values;cstr_specsinterprets feasibility - For equality constraints, consider using a small tolerance via
cstr_tol
Recipe 11: Cheap Not Metamodelized Constraint🔗
Use when:
- you have constraints that are cheap to evaluate
- constraints depend only on input variables
x(not on expensive simulations) - you want to avoid the overhead of surrogate modeling for these constraints
Suggested setup:
import egobox as egx
# Example: constraint g(x) <= 0 that is cheap to evaluate
def cheap_constraint(x):
# Cheap computation based only on x
return x[0] ** 2 + x[1] ** 2 # Circle constraint
optim = egx.Egor(
xspecs,
)
res = optim.minimize(
fun,
fcstrs=[cheap_constraint], # List of cheap constraint functions
fcstr_specs=[egx.CstrSpec.leq(1.0)], # Constraint semantics
max_iters=40,
seed=42,
)
Why it helps:
fcstrs(function constraints) are evaluated directly, not surrogate-modeled- Avoids the complexity of modeling cheap constraints with GPs
- Reduces optimization overhead when constraints are inexpensive
fcstr_specsdefines feasibility interpretation (same ascstr_specs)
Note:
- Use
fcstrsfor cheap constraints; usecstr_specsfor expensive constraints that need surrogate modeling - The
funcallable should return(objective, *constraint_values)when using surrogate constraints, but forfcstrs, constraints are passed separately - Multiple cheap constraints can be provided as a list to
fcstrs