Data analysis

How do I calculate
the minimum principle?

Using the nutrition calculator as an example: fixed nutrient boundaries, minimum daily cost. This is how the minimum principle is implemented in code.

Simon Stark|Published 8 August 2026|Approx. 14 min read

01

The minimum principle in the nutrition calculator

The result is a set of nutrient constraints: at least a certain amount of protein, calories, iron and so on, and at most the values you choose to limit. The input is money: the sum of daily food prices. The question is therefore: which food quantities satisfy every minimum and maximum while costing the least?

That is the minimum principle, not “as healthy as possible at any price” and not “as cheap as possible without constraints”. Technically it is a linear program using javascript-lp-solver. The model is solved with solver.Solve(model), normally in the web worker plan-worker.js; under file:// the same call can run on the main thread.


02

Which foods participate

Before the solver runs, foodsForPlan() trims the database. Only this filtered pool becomes the model:

  • Diet (matchesDiet): vegan, vegetarian, pescatarian, carnivore or all.
  • Properties (matchesTagFilters): for example whole food, supplement or processed.
  • Avoidance: foods carrying the lactose or gluten tag are excluded when those filters are active.

Each food receives a variable name f0, f1, f2 and so on in array order. The index matters because the solver returns the same keys.


03

The database reference unit

Prices and nutrients use a reference defined by foodRef and UNIT_META: grams and millilitres use 100 as the reference; pieces and drops use 1. foodPrice(f) is the stored price per reference and f.nutrients[p] is the nutrient value per reference.

If the scale is wrong, the solver works with nonsense. buildModel(list, asInteger, reqMap) creates variables from these values.

ModeMeaning of uScaling
ContinuousNumber of reference units, e.g. 1.5 × 100 g = 150 gCost = u × foodPrice(f); nutrient = u × raw
Whole quantitiesWhole physical units, such as 1 g, 1 ml, 1 piece or 1 dropscale = 1 / foodRef(f); cost and nutrients use scaled values

In integer mode, the variable is integer. The current solver options are an 8-second timeout and a 0.01 tolerance.


04

What goes to the solver

{ "optimize": "cost", "opType": "min", "constraints": { "Protein (g)": { "min": 160 }, "Energy (cal)": { "min": 2200, "max": 2800 } }, "variables": { "f0": { "cost": 0.44, "Energy (cal)": 371, "Protein (g)": 13.6 } } }Only parameters with a configured minimum or maximum become constraints. Zero raw values are omitted; cost and nutrients are already scaled.

The objective is min Σ cost_i × u_i. For every nutrient with a minimum, the sum must be at least that minimum; for every maximum, it must stay below it. Variables are non-negative and integer when whole quantities are enabled.


05

The path without whole quantities

  1. Pool: foodsForPlan().
  2. Model: buildModel(pool, false) with the current requirements.
  3. Solve: solveModelAsync(model) through the worker or fallback solver.Solve.
  4. Display: Convert every positive f_i into quantity and cost.

In continuous mode, cost is u × foodPrice(f), nutrients are u × raw and display quantity is u × foodRef(f). The result is the cheapest admissible plan in continuous space, so a fraction such as 37.2 g can be returned.


06

Two phases in the code

An integer linear program over the full database would often be too slow. solveIntegerPlan(pool) therefore uses two phases. The constant INTEGER_KCAL_BUFFER = 25 lowers the energy maximum by 25 without going below the energy minimum.

  1. Phase 1: Solve a continuous plan with the buffered requirements. If that fails, solve with the real requirements. Keep foods with continuous quantity above 1e-6, at most 50 items, and solve the integer problem on that support.
  2. Phase 2: If real limits are still violated, collect nutrients below minimum or above maximum, expand the support with top candidates per deficit nutrient and relevant supplements, and solve again with the full requirements.

If no exact integer plan is found, the code rounds round(u_cont × ref) or uses ceil, then repairs the plan. The result can be marked approx: true. The buffer leaves room for phase 2 to add units without immediately breaking the energy maximum.


07

Greedy fallback when MILP is not enough

repairIntegerPlan(chosen, pool, reqMap) runs for at most 250 steps. If a minimum is too low, it adds the available unit with the best nutrient-per-euro score that does not break a maximum. If a maximum is too high, it removes a unit from the food contributing most to that nutrient.

Important: This is no longer an exact cost minimum. It is a fallback to keep the plan as close as possible to all constraints. The method can be labelled milp-support, round+repair or milp-deficit+repair.

08

Which constraint is blocking the plan?

If the continuous model is infeasible, diagnoseInfeasible builds the full model, removes one constraint at a time and solves again. If the model becomes feasible, that constraint is a suspected culprit. The UI lists the nutrients and suggests relaxing a boundary or adding foods.

This is the economic translation of “the target and the permitted pool do not fit together”.


09

From u to grams and euros

Continuous: amount = u × ref; cost = u × price; nutrient = u × raw Integer: amount = u; cost = u × price / ref; nutrient = u × raw / refDaily plan cost is the sum of item costs; nutrient totals are the sum of item contributions.

The UI's limiting factor marks the nutrient closest to one of its boundaries. That is often the next lever: expand the database or adjust the boundary.


10

The same logic as the code smoke test

Minimise A + 3B subject to 10A + 20B ≥ 20 A, B ≥ 0Solution: A = 2, B = 0, cost = 2. It meets the target more cheaply than B.

With real nutrients and many maximums, the space becomes tighter. The task remains the same: find the cheapest point that still meets every profile and requirement boundary.


11

The minimum principle in one sentence

Nutrient minimums and maximums are the target. Euros are the input. The solver, with two phases and repair for whole quantities, searches the filtered food pool for the cheapest admissible daily plan. That is the minimum principle implemented.


Try the calculation in the browser

Use the database, profile, constraints and plan builder in the nutrition calculator.

Open the nutrition calculator

Read next

Related articles