Data analysis
Using the nutrition calculator as an example: fixed nutrient boundaries, minimum daily cost. This is how the minimum principle is implemented in code.
Mapping
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.
Pool
Before the solver runs, foodsForPlan() trims the database. Only this filtered pool becomes the model:
matchesDiet): vegan, vegetarian, pescatarian, carnivore or all.matchesTagFilters): for example whole food, supplement or processed.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.
Units
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.
| Mode | Meaning of u | Scaling |
|---|---|---|
| Continuous | Number of reference units, e.g. 1.5 × 100 g = 150 g | Cost = u × foodPrice(f); nutrient = u × raw |
| Whole quantities | Whole physical units, such as 1 g, 1 ml, 1 piece or 1 drop | scale = 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.
Model
{
"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.
Continuous mode
foodsForPlan().buildModel(pool, false) with the current requirements.solveModelAsync(model) through the worker or fallback solver.Solve.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.
Whole quantities
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.
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.
Repair
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.
milp-support, round+repair or milp-deficit+repair.Infeasible model
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”.
Converting the solution
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.
Mini example
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.
In short
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.
Use the database, profile, constraints and plan builder in the nutrition calculator.
Read next