Quickstart: NSGA-II on DTLZ2#
This example minimizes the three objectives of DTLZ2 in a 12-dimensional decision space,
with all variables bounded by [0, 1]. It chooses CUDA when available and otherwise uses CPU,
sets a random seed, initializes the workflow, and performs 50 iterations. It then extracts
non-dominated solutions from the final population and computes IGD.
Complete example#
"""Optimize DTLZ2 with NSGA-II and report the current population's IGD."""
import torch
from evomo.algorithms import NSGA2
from evomo.metrics import igd
from evomo.operators.selection import non_dominate_rank
from evomo.problems.numerical import DTLZ2
from evomo.workflows import UnifiedWorkflow
def main():
device = "cuda" if torch.cuda.is_available() else "cpu"
torch.set_default_device(device)
torch.manual_seed(42)
problem = DTLZ2(d=12, m=3)
algorithm = NSGA2(
pop_size=100,
n_objs=problem.m,
lb=torch.zeros(problem.d, device=device),
ub=torch.ones(problem.d, device=device),
device=device,
)
workflow = UnifiedWorkflow(algorithm, problem, device=device)
workflow.init_step()
for _ in range(50):
workflow.step()
fitness = workflow.algorithm.fit
finite = torch.isfinite(fitness).all(dim=1)
population = workflow.algorithm.pop[finite]
fitness = fitness[finite]
if fitness.shape[0] == 0:
raise RuntimeError("No finite objective values; check the problem evaluation")
front = non_dominate_rank(fitness) == 0
pareto_solutions = population[front]
pareto_fitness = fitness[front]
reference_front = problem.pf().to(device=fitness.device, dtype=fitness.dtype)
print(f"Device: {device}")
print(f"Population: {tuple(workflow.algorithm.pop.shape)}")
print(f"Non-dominated solutions: {pareto_solutions.shape[0]}")
print(f"IGD: {igd(pareto_fitness, reference_front).item():.6f}")
if __name__ == "__main__":
main()
After installing the source checkout, run this command from the repository root:
python docs/examples/quickstart.py
Understanding the result#
workflow.algorithm.pop stores decision variables and workflow.algorithm.fit stores the
corresponding objective values. Rows of these tensors refer to the same candidate solutions.
Algorithms use minimization internally. Since this example does not change objective directions,
these values are also DTLZ2’s original objectives.
non_dominate_rank(fitness) == 0 extracts the first non-dominated front of the current set.
Filtering happens after optimization and is kept outside the compiled step.
problem.pf() supplies reference front samples. Lower IGD means the reference points are
closer on average to the resulting solution set. Hardware, versions, and random operations
may change the numerical result, so the example does not prescribe a fixed score.
The 50 iterations illustrate the interface; they do not establish convergence or algorithm performance.
Next steps#
To change algorithms, check algorithm parameters and the actual population size.
To change problems, check benchmarks and update dimensions, objective count, and valid bounds.
For feasibility handling, follow the constrained optimization example.
Before compiling the workflow, read compilation and performance.