Skip to article frontmatterSkip to article content
Site not loading correctly?

This may be due to an incorrect BASE_URL configuration. See the MyST Documentation for reference.

3.8 Multiobjective Optimization and Pareto Optimality

Tradeoffs should be visible

Engineering systems rarely have one natural objective. A suspension may minimize ride response, energy, actuator mass, and cost. A design dominates another if it is no worse in every objective and strictly better in at least one. A design is Pareto optimal when no feasible design dominates it.

The Pareto front exposes the best achievable tradeoffs rather than hiding them inside one arbitrary weight selection.

Conceptual Pareto front for ride performance, energy, and cost.

Scalarization methods

A weighted sum minimizes

Jw(z)=i=1mwiJ^i(z),wi0,J_w(z)=\sum_{i=1}^{m}w_i\widehat J_i(z),\qquad w_i\ge0,

where the hats indicate scaled objectives. Scaling is essential: joules, dollars, kilograms, and acceleration cannot be compared through raw numerical magnitude. Weighted sums may miss nonconvex portions of a Pareto front.

The ϵ\epsilon-constraint method minimizes one objective while bounding the others. For example, minimize ride response subject to energy ϵE\le\epsilon_E and cost ϵC\le\epsilon_C. This form often communicates requirements more clearly.

Selecting a final design

Optimization generates candidates; it does not supply organizational values. A final selection should consider robustness, active constraints, model credibility, manufacturability, controller complexity, and the value of moving along the front. A knee point is attractive when a small further gain in one objective demands a large sacrifice in another.

import numpy as np

# Columns: ride metric, energy metric, normalized lifecycle cost.
candidates = np.array([
    [1.00, 4.8, 1.9], [1.10, 3.8, 1.5], [1.25, 2.9, 1.2],
    [1.45, 2.2, 1.0], [1.30, 3.5, 1.6], [1.70, 2.6, 1.3],
])

def nondominated_mask(values):
    keep = np.ones(len(values), dtype=bool)
    for i, point in enumerate(values):
        dominates_i = np.all(values <= point, axis=1) & np.any(values < point, axis=1)
        keep[i] = not np.any(dominates_i)
    return keep

mask = nondominated_mask(candidates)
print("Pareto candidates:\n", candidates[mask])

Activity 3.8: inspect dominance