Skip to content

Engineering design with mixed constraints

Goal: connect decision variables to a practical objective pair, execute the model, and select or interpret a candidate without losing its original row.

Prerequisites: T02, T03, and the coverage setup. The problem reference lists the built-in evaluator; the public protocol explains its metadata.

Formulate the example

The built-in welded-beam teaching model has six decisions: weld thickness h, weld length l, beam height t, beam width b, material category and integer stiffener count. It minimizes a cost expression and a deflection proxy. This is a simplified model; a numerical optimum does not validate an engineering design.

VAMOS 1.0.1 detail: this class emits four residual columns but does not declare n_constraints. The script explicitly sets problem.n_constraints = 4 before optimization. Without that protocol metadata, the evaluator does not activate the emitted constraints. The example also refuses to treat missing G as evidence of feasibility.

Residuals follow G <= 0: shear stress minus its limit, normal stress minus its limit, deflection minus its limit, and h - b. Read the constraint reference before adapting units, constants or physical assumptions.

Run and inspect

This example uses NumPy, optimizer seed 42 and 400 evaluations, including the initial population. Feature-selection and model-tuning cases require scikit-learn; plotting adds Matplotlib.

python engineering.py
python engineering.py --output engineering-local.svg

Download the complete script.

Complete runnable script
"""Verified VAMOS 1.0.1 lesson: engineering. See coverage-setup for dependencies."""


def execute_example(problem, algorithm, config, budget, seed=42, engine="numpy"):
    import numpy as np

    from vamos import OptimizationResult, optimize

    result = optimize(problem, algorithm=algorithm, algorithm_config=config, max_evaluations=budget, seed=seed, engine=engine)
    G = result.data.get("G")
    if getattr(problem, "n_constraints", 0) and G is None:
        raise ValueError("Declared constraints are missing from the returned result.")
    feasible_rows = np.arange(len(result.F)) if G is None else np.flatnonzero(np.all(G <= 0, axis=1))
    if len(feasible_rows):
        feasible = OptimizationResult({"X": result.X[feasible_rows], "F": result.F[feasible_rows]})
        front, local = feasible.front(return_indices=True)
        front_rows = feasible_rows[local]
    else:
        front, front_rows = np.empty((0, problem.n_obj)), np.empty(0, dtype=int)
    print(f"Algorithm: {algorithm}; encoding: {problem.encoding}; backend: {engine}; seed: {seed}")
    print(f"Returned X: {result.X.shape}; F: {result.F.shape}; result mode: population")
    print(f"Feasible rows: {len(feasible_rows)}/{len(result.F)}; feasible non-dominated rows: {len(front)}")
    print(f"Evaluations: {result.data['evaluations']}")
    return {
        "problem": problem,
        "algorithm": algorithm,
        "config": config,
        "result": result,
        "feasible_rows": feasible_rows,
        "front": front,
        "front_rows": front_rows,
    }


def run_engineering(budget=400):
    import numpy as np

    from vamos.algorithms import NSGAIIConfig
    from vamos.problems import WeldedBeamDesignProblem

    problem = WeldedBeamDesignProblem()
    # This 1.0.1 class emits four G columns but omits their protocol metadata.
    problem.n_constraints = 4
    config = (
        NSGAIIConfig.builder()
        .pop_size(40)
        .offspring_size(40)
        .crossover("mixed", prob=0.9)
        .mutation("mixed", prob=1.0 / problem.n_var)
        .selection("tournament", size=2)
        .constraint_mode("feasibility")
        .result_mode("population")
        .build()
    )
    data = execute_example(problem, "nsgaii", config, budget)
    if not len(data["front_rows"]):
        raise ValueError("No feasible design; inspect the model and budget before selecting a candidate.")
    # An explicit preference: minimum cost among the feasible non-dominated rows.
    row = int(data["front_rows"][np.argmin(data["front"][:, 0])])
    data["selected_row"] = row
    print(f"Minimum-cost feasible front row: {row}")
    print("Decision (h, l, t, b, material, stiffeners):", np.round(data["result"].X[row], 6).tolist())
    print("Objectives (cost, deflection proxy):", np.round(data["result"].F[row], 6).tolist())
    print("Constraint residuals G <= 0:", np.round(data["result"].data["G"][row], 6).tolist())
    return data


def plot_example(data, output):
    from pathlib import Path

    import matplotlib

    matplotlib.use("Agg")
    import matplotlib.pyplot as plt
    import numpy as np

    output = Path(output)
    if output.exists():
        raise FileExistsError(f"Choose a new figure filename: {output}")
    if output.suffix.lower() != ".svg":
        raise ValueError("Use an .svg output filename.")
    f = data["result"].F
    with plt.rc_context({"svg.hashsalt": "vamos-coverage", "svg.fonttype": "none"}):
        fig = plt.figure(figsize=(7, 4.5), layout="constrained")
        if f.shape[1] == 3:
            ax = fig.add_subplot(projection="3d")
            ax.scatter(*f.T, c="#007f7a", s=22)
            ax.set(xlabel="f1", ylabel="f2", zlabel="f3")
            ax.set_box_aspect((1, 1, 0.85), zoom=0.85)
        else:
            ax = fig.add_subplot()
            ax.scatter(*f.T, c="#b6bec8", s=25, label="Returned population")
            if len(data["front"]):
                ax.scatter(*data["front"].T, c="#007f7a", s=30, label="Feasible non-dominated rows")
            labels = {
                "WeldedBeamDesignProblem": ("Cost (minimize)", "Deflection proxy (minimize)"),
                "FeatureSelectionProblem": ("Validation error (minimize)", "Feature count (minimize)"),
                "HyperparameterTuningProblem": ("Validation error (minimize)", "Complexity proxy (minimize)"),
            }
            xlabel, ylabel = labels.get(type(data["problem"]).__name__, ("f1 (minimize)", "f2 (minimize)"))
            ax.set(xlabel=xlabel, ylabel=ylabel)
            ax.legend()
        if data["problem"].__class__.__name__ in {"ZDT1", "ZDT1Problem"}:
            x = np.linspace(0, 1, 101)
            ax.plot(x, 1 - np.sqrt(x), "--", c="#7c8795", label="Analytical front")
            ax.legend()
        ax.set_title(f"{data['algorithm'].upper()} · {data['result'].data['evaluations']} evaluations · seed 42")
        fig.savefig(output, metadata={"Date": None}, bbox_inches="tight", pad_inches=0.25)
        plt.close(fig)
    output.write_text("\n".join(line.rstrip() for line in output.read_text(encoding="utf-8").splitlines()) + "\n", encoding="utf-8")


def lesson_cli(function, default_budget=400):
    import argparse

    parser = argparse.ArgumentParser(description="Run a bounded VAMOS example and optionally plot its actual result.")
    parser.add_argument("--budget", type=int, default=default_budget)
    parser.add_argument("--output", help="New SVG filename; requires Matplotlib")
    args = parser.parse_args()
    data = function(budget=args.budget)
    if args.output:
        plot_example(data, args.output)


if __name__ == "__main__":
    lesson_cli(run_engineering, default_budget=400)

Recorded output

Algorithm: nsgaii; encoding: mixed; backend: numpy; seed: 42
Returned X: (40, 6); F: (40, 2); result mode: population
Feasible rows: 40/40; feasible non-dominated rows: 40
Evaluations: 400
Minimum-cost feasible front row: 0
Decision (h, l, t, b, material, stiffeners): [0.783541, 4.169584, 1.760651, 3.396257, 0.0, 2.0]
Objectives (cost, deflection proxy): [15.905242, 0.059214]
Constraint residuals G <= 0: [-9827.611456, -5182.606983, -0.190786, -2.612716]

Actual engineering results from the documented protocol.

Measured with VAMOS 1.0.1; the observations and provenance retain the inputs and results.

Interpret the result and limits

The script first filters feasible rows, then computes non-dominance within that subset and maps the chosen row back to the original X, F and G. Its explicit preference is minimum cost among the feasible non-dominated rows. The printed residuals allow that selection to be checked. If no feasible row exists, selection fails instead of reporting a least-infeasible design as feasible.

Material and stiffener choices require mixed operators. A scatter plot alone would not establish feasibility, and a feasible result is only feasible for these implemented equations. No safety factor, manufacturing validation or structural certification follows from this example.

Try one controlled change

Choose minimum deflection instead of minimum cost within front_rows. Recover the same original row in all arrays and compare the design decisions. This changes the decision preference, not the optimization problem.

Next: preserve the selected run with the run lifecycle, or use T06 to compare an explicit set of configurations across seeds.