VAMOS Cookbook¶
Common recipes and patterns for using VAMOS.
Recommended path (optimize)¶
The optimize(...) API is the fastest way to run experiments in Python.
See the API decision guide in docs/guide/getting-started.md if you need explicit config objects.
from vamos import optimize
from vamos.ux.api import result_summary_text
result = optimize("zdt1", algorithm="nsgaii", max_evaluations=5000, pop_size=100, seed=0)
print(result_summary_text(result))
1. Custom Problem Definition¶
Define a problem by implementing ProblemProtocol (attributes plus evaluate):
import numpy as np
class MyProblem:
def __init__(self) -> None:
self.n_var = 2
self.n_obj = 2
self.n_constraints = 0
self.xl = np.array([0.0, 0.0])
self.xu = np.array([1.0, 1.0])
self.encoding = "real"
def evaluate(self, X: np.ndarray, out: dict[str, np.ndarray]) -> None:
f1 = X[:, 0]
f2 = (1.0 + X[:, 1]) / X[:, 0]
out["F"] = np.column_stack([f1, f2])
problem = MyProblem()
2. Handling Constraints¶
Box constraints are handled via xl and xu. For other constraints, fill out["G"]. VAMOS expects g(x) <= 0 for feasible solutions. Set n_constraints to the number of constraints if you want the count explicitly tracked.
def evaluate(self, X: np.ndarray, out: dict[str, np.ndarray]) -> None:
# ... calculate F ...
# Constraint: x[0] + x[1] <= 1.5
g1 = (X[:, 0] + X[:, 1]) - 1.5
out["G"] = g1.reshape(-1, 1)
Constraint DSL (symbolic)¶
When you want a reusable constraint evaluator, build it symbolically:
import numpy as np
from vamos.foundation.constraints.dsl import constraint_model, build_constraint_evaluator
with constraint_model(n_vars=2) as cm:
x0, x1 = cm.vars("x0", "x1")
cm.add(x0 + x1 <= 1.0)
cm.add(x0 >= 0.0)
eval_constraints = build_constraint_evaluator(cm)
G = eval_constraints(np.array([[0.2, 0.3], [0.9, 0.4]]))
Notes:
- Constants must be scalar values (including 0-d arrays like np.array(1.0)).
- Vector constants are not supported; expand them into separate constraints.
3. Visualization Callback¶
Use live_viz to see progress or save frames.
from vamos import optimize
class MyCallback:
def __call__(self, algorithm):
print(f"Gen {algorithm.n_gen}: {len(algorithm.pop)} solutions")
# Access population: algorithm.pop.get("F")
optimize("zdt1", algorithm="nsgaii", max_evaluations=2000, live_viz=MyCallback())
4. Re-using Algorithm State (Checkpointing)¶
VAMOS algorithms are stateful. You can resume() them if you manually stepped them, or pickle them (ensure backends are pickleable).
Note: Full checkpointing support is in active development.
5. Using Numba for Performance¶
Select a backend via optimize(..., engine=...).
from vamos import optimize
result = optimize("zdt1", algorithm="nsgaii", engine="numba", max_evaluations=5000)
6. Comparing Algorithms¶
Run multiple algorithms and plot their fronts together.
import matplotlib.pyplot as plt
from vamos import optimize
res_nsga2 = optimize("zdt1", algorithm="nsgaii", max_evaluations=4000)
res_moead = optimize("zdt1", algorithm="moead", max_evaluations=4000)
plt.scatter(res_nsga2.F[:, 0], res_nsga2.F[:, 1], label="NSGA-II")
plt.scatter(res_moead.F[:, 0], res_moead.F[:, 1], label="MOEA/D")
plt.legend()
plt.show()
7. Inspect Auto-Resolved Defaults¶
See which top-level settings were inferred vs provided.
from vamos import optimize
result = optimize("zdt1")
print(result.explain_defaults())
The output includes:
resolved_spec: canonical problem, algorithm, operators, backend, termination, seed, and population detailsdefault_sources: which values were inferred (auto) vs set explicitly
8. Operator Facade Access¶
Import common operators directly from vamos.engine.operators.
import numpy as np
from vamos.engine.operators import SBXCrossover, PolynomialMutation
xl = np.zeros(30)
xu = np.ones(30)
crossover = SBXCrossover(prob_crossover=0.9, eta=15.0, lower=xl, upper=xu)
mutation = PolynomialMutation(prob=1 / 30, eta=20.0, lower=xl, upper=xu)
9. Multi-Seed Studies¶
Pass a list of seeds to run a small study in one call.
from vamos import optimize
from vamos.ux.api import result_summary_text
study = optimize("zdt1", algorithm="nsgaii", max_evaluations=4000, seed=[0, 1, 2, 3])
for idx, res in enumerate(study.runs):
print(idx, result_summary_text(res))
print(study.mean("evaluations"))
10. Algorithm Config Objects (Reproducible Runs)¶
Use a config object when you want every knob explicit.
from vamos import optimize
from vamos.algorithms import NSGAIIConfig
cfg = (
NSGAIIConfig.builder()
.pop_size(100)
.offspring_size(100)
.crossover("sbx", prob=1.0, eta=20.0)
.mutation("pm", prob="1/n", eta=20.0)
.selection("tournament", size=2)
.build()
)
result = optimize("zdt1", algorithm="nsgaii", algorithm_config=cfg, max_evaluations=8000, seed=7)
11. Multiprocessing Evaluation¶
For expensive evaluations, use the multiprocessing backend explicitly.
from vamos import optimize
from vamos.foundation.eval.backends import MultiprocessingEvalBackend
backend = MultiprocessingEvalBackend(n_workers=4)
result = optimize("zdt1", algorithm="nsgaii", max_evaluations=6000, eval_strategy=backend)
12. Hypervolume-Based Early Stopping¶
Use the lower-level experiment runner when you need non-default termination logic.
from vamos.experiment.runner import run_experiment
from vamos.foundation.core.experiment_config import ExperimentConfig
from vamos.foundation.core.hv_stop import build_hv_stop_config
hv_cfg = build_hv_stop_config(hv_threshold=0.9, hv_reference_front=None, problem_key="zdt1")
hv_cfg["max_evaluations"] = 12000
metrics = run_experiment(
problem="zdt1",
algorithm="nsgaii",
engine="numpy",
config=ExperimentConfig(max_evaluations=12000, seed=3),
termination=("hv", hv_cfg),
)
13. Save and Load a Run Artifact¶
Persist a complete v1 run and load its canonical numerical result later.
from vamos import load_result, load_run, optimize, save_result
result = optimize("zdt1", algorithm="nsgaii", max_evaluations=5000)
stored = save_result(result, "results/zdt1_nsgaii")
loaded = load_result(stored.root)
run = load_run(stored.root)
print(loaded.F.shape, run.manifest.run_id)
save_result is available from the top-level vamos facade only. See
Save and load Python run artifacts for integrity,
resource-limit, and non-destructive-write behavior.
14. Select a Single Solution from the Front¶
Pick a balanced normalized-sum solution or a simple min objective.
from vamos import optimize
result = optimize("zdt1", algorithm="nsgaii", max_evaluations=5000)
choice = result.best("balanced_sum")
print(choice["F"])
15. Load a Stored Run Without Re-executing It¶
Loading stored data is intentionally distinct from reproduction. The v1 core
implemented here does not expose replay or reproduction; use load_result for
arrays and load_run for the immutable manifest and environment.
from vamos import load_run
run = load_run("results/zdt1_nsgaii", verify="all")
print(run.status, run.result.F.shape)
16. Validate a Config File (CLI)¶
Check a YAML/JSON experiment spec before running:
vamos --config configs/experiment.yaml --validate-config
17. Convert Results to a DataFrame (pandas)¶
Export results for analysis in pandas (requires the analysis extra).
from vamos import optimize
from vamos.ux.api import result_to_dataframe
result = optimize("zdt1", algorithm="nsgaii", max_evaluations=4000)
df = result_to_dataframe(result)
df.to_csv("results/zdt1_nsgaii_front.csv", index=False)
18. Combine Fronts from Multiple Runs¶
Merge fronts from multiple runs and keep the non-dominated set.
import numpy as np
from vamos import optimize
from vamos.foundation.quality_indicators.pareto import pareto_filter
study = optimize("zdt1", algorithm="nsgaii", max_evaluations=4000, seed=[0, 1, 2])
combined = np.vstack([res.F for res in study.runs if res.F is not None])
front = pareto_filter(combined, return_indices=False)
20. Compute Hypervolume (2D)¶
Compute hypervolume for 2D minimization fronts.
import numpy as np
from vamos import optimize
from vamos.foundation.quality_indicators import compute_hypervolume
result = optimize("zdt1", algorithm="nsgaii", max_evaluations=4000)
F = np.asarray(result.F)
hv = compute_hypervolume(F, ref_point=[1.1, 1.1])
print(hv)
21. Normalized HV for ZDT Problems¶
Use the built-in reference front to compute normalized hypervolume on ZDT.
import numpy as np
from vamos import optimize
from vamos.foundation.quality_indicators import compute_normalized_hv
result = optimize("zdt1", algorithm="nsgaii", max_evaluations=4000)
hv_norm = compute_normalized_hv(np.asarray(result.F), "zdt1")
print(hv_norm)