← All articles

Advanced Website Design: Build the Pareto Frontier Before You Pick the Weights

August 22, 2026 · Adam DeJans Jr.

← View the original challenge

The November 2025 Advanced Website Design challenge is a nice reminder that multi-objective optimization is not finished when somebody says, “just put weights on the objectives.”

The developer has ten candidate website features. Each feature has a cost and value, the client has a $10,000 budget, and several business rules create nonlinear-looking interactions:

The two objectives are to maximize value and minimize cost.

For ten binary decisions there are only 2^10 = 1,024 possible feature sets, so I enumerated the entire decision space rather than hiding the tradeoff behind an arbitrary weight.

The exact Pareto frontier for the Advanced Website Design challenge.

The result is more interesting than a single “optimal” answer.

Exact enumeration

Using the feature data published in the cDMN walkthrough:

FeatureCostValue
1$5,0007
2$4,0006
3$3,0005
4$2,0004
5$1,0003
6$1,0002
7$1,0001
8$1,0001
9$1,0001
10$1,0001

I evaluated all 1,024 subsets, applied the dependency, incompatibility, surcharge, discount, and budget rules, and retained 262 feasible designs.

From those 262 designs there are 12 unique nondominated cost-value outcomes:

Discounted costValueRepresentative feature setWeighted-sum supported?
$00Yes
$1,00035Yes
$2,00055, 6Yes
$3,00065, 6, 7No
$4,00075, 6, 7, 8No
$4,94085, 6, 7, 8, 9No
$5,00092, 5No
$6,000123, 4, 5No
$7,000143, 4, 5, 6Yes
$7,600153, 4, 5, 6, 7Yes
$8,550163, 4, 5, 6, 7, 8Yes
$9,690173, 4, 5, 6, 7, 8, 9Yes

The reproducibility data are available here: Pareto frontier CSV.

The maximum-value answer

The highest possible value is:

17

The cheapest designs reaching value 17 cost:

$9,690

One such design is:

{3, 4, 5, 6, 7, 8, 9}

The base cost is:

3,000 + 2,000 + 1,000 + 1,000 + 1,000 + 1,100 + 1,100
= 10,200

because features 8 and 9 receive the 10% premium surcharge.

Seven features are selected, so the 5% volume discount applies:

10,200 × 0.95 = 9,690

This exactly matches the best-value solution reported by the cDMN implementation. There are three equivalent minimum-cost value-17 designs: choose features 3–7 plus any two of 8, 9, and 10.

Why a weighted sum is not the Pareto frontier

A common approach to two objectives is:

maximize value - lambda * cost

and then vary lambda.

That is useful, but in a discrete problem it does not necessarily recover every Pareto-efficient solution.

For each of the 12 frontier points, I checked whether there exists any nonnegative lambda for which that point maximizes value - lambda × cost over all 262 feasible designs.

Only 7 of the 12 frontier outcomes are supported by some weighted sum.

The five outcomes at:

($3,000, 6)
($4,000, 7)
($4,940, 8)
($5,000, 9)
($6,000, 12)

are Pareto-efficient but unsupported. No choice of a single linear weight between value and cost makes any of them the winner.

That happens because the feasible objective set is discrete and non-convex. A weighted sum finds points on the supported convex envelope. It can jump directly over legitimate tradeoffs.

Executed enumeration

from itertools import combinations

cost = {
    1: 5000, 2: 4000, 3: 3000, 4: 2000, 5: 1000,
    6: 1000, 7: 1000, 8: 1000, 9: 1000, 10: 1000,
}

value = {
    1: 7, 2: 6, 3: 5, 4: 4, 5: 3,
    6: 2, 7: 1, 8: 1, 9: 1, 10: 1,
}


def evaluate(selected):
    selected = set(selected)

    # 3 and 4 are all-or-nothing.
    if (3 in selected) != (4 in selected):
        return None

    # 2 cannot coexist with 3 (and therefore 4).
    if 2 in selected and 3 in selected:
        return None

    premium_count = len(selected & {8, 9, 10})

    base_cost = 0.0
    for f in selected:
        multiplier = 1.1 if f in {8, 9, 10} and premium_count >= 2 else 1.0
        base_cost += cost[f] * multiplier

    discounted_cost = base_cost * 0.95 if len(selected) >= 5 else base_cost
    total_value = sum(value[f] for f in selected)

    if discounted_cost > 10000:
        return None

    return discounted_cost, total_value, tuple(sorted(selected))


feasible = []
for mask in range(1 << 10):
    selected = [f for f in range(1, 11) if mask & (1 << (f - 1))]
    result = evaluate(selected)
    if result is not None:
        feasible.append(result)

print(len(feasible))  # 262

pareto = []
for candidate in feasible:
    c, v, _ = candidate
    dominated = any(
        c2 <= c and v2 >= v and (c2 < c or v2 > v)
        for c2, v2, _ in feasible
    )
    if not dominated:
        pareto.append(candidate)

frontier = sorted(set((c, v) for c, v, _ in pareto))
print(len(frontier))  # 12
print(frontier)

Checking whether a frontier point is supported

For a frontier point (c_i, v_i) to be optimal for some weighted sum, there must be a lambda >= 0 satisfying:

v_i - lambda c_i >= v_j - lambda c_j

for every feasible outcome j.

Each comparison gives either an upper or lower bound on lambda. Intersecting all those bounds tells us whether such a weight exists.

I performed that check against all feasible outcomes. Five Pareto points have an empty multiplier interval: they are genuinely nondominated, but no linear scalarization can select them.

The decision lesson

There is nothing wrong with weighted objectives. The problem is treating a weight as if it were a neutral modeling choice.

A weight encodes a preference. In a discrete decision problem it can also quietly remove options from consideration.

When the objective tradeoff is economically meaningful, I would rather expose the Pareto frontier first and let the decision-maker see the choices:

What does another $1,000 actually buy us?
Where does marginal value flatten?
Which tradeoffs disappear if we scalarize too early?

The optimization model should help reveal the decision, not bury it inside a coefficient.


Solution and exhaustive validation by Adam DeJans Jr..

Join the discussion

Sign in with GitHub to leave a comment.