Each recipe normalization set up is composed of the following components:
- Solver Aspects: A list of all variables and constraints (ingredients, totals, calculations, ratios)
- Recipe Workflow Structure: The multi-step recipe topology (which steps feed into which)
- Objective Setup: What the solver should optimize for
- Epsilon: Solver precision tolerance (default 1e-5)
- Optimize Mode: If set to *‘*Optimize On Fail’, allows the solver to slightly relax constraints when infeasible
Decision Variables
The solver creates a single variable vector of length N, where each element is either:
- An ingredient quantity within a recipe step
- A step-to-step relationship quantity treated as a free variable
Objective Function
Three objective types are available:
| Type | Formula | Meaning |
|---|---|---|
| Default | minimize Σ(xᵢ – x₀ᵢ)² | Closest feasible point to original values (least-squares) |
| Proportional | minimize Σ | xᵢ – x₀ᵢ × (x_ref / x₀_ref) |
| Custom | minimize/maximize/target a weighted-sum calculation | User-defined optimization of a specific calculation |
When Optimize Mode is set to Optimize on Fail, a slack variable ε ≥ 0 is added to the objective with a large penalty: base_objective + 1000 × |ε|. This allows slight constraint violations when the problem is otherwise infeasible.
Constraint Types
| Constraint | What It Does | Mathematical Form |
|---|---|---|
| Input Variable | Bounds on individual ingredient quantities (min, max, fixed) | xᵢ = / ≥ / ≤ value |
| Recipe Total | The sum of all compounded inputs for final steps equals a target | Σ(compounded_xᵢ) = / ≥ / ≤ total |
| Weighted Sum | A linear combination of variables hits a target | Σ(cᵢ × compounded_xᵢ) = / ≥ / ≤ target |
| Weighted Average | A ratio (numerator/denominator) hits a target, linearized to avoid division | Σ(nᵢxᵢ) – target × Σ(dᵢxᵢ) = / ≥ / ≤ 0 |
| Composite Calculation | Multi-term numerator/denominator with bias constants | (Σnum + bias_n) – target × (Σden + bias_d) = 0 |
| Ingredient Ratio | Fixed proportional ratios between ingredient pairs | cᵢ × x[i+1] – c[i+1] × xᵢ = 0 |
| Relationship Variable | Bounds on step-to-step relationship quantities | Same as input variable constraints |
When the epsilon slack variable is active, every constraint is softened by ±ε (e.g., x = value becomes value – ε ≤ x ≤ value + ε).
Post-Solve Processing
Solved values go through two cleanup steps:
- Aspect-enforced snapping: Compensates for floating-point imprecision. If a variable had an equality constraint of exactly 5.0, the solver might return 4.9999999997 — this snaps it back to 5.0. Similarly for ≥ and ≤ bounds.
- Sanitization: Rounds very small residuals to zero (e.g., 1e-20 → 0), while preserving meaningful precision.
Error Cases
| Status | Meaning |
|---|---|
| Optimal | Solution found |
| Optimal Inaccurate | Solution found but may have numerical issues (accepted only if accept_optimal_inaccurate is true) |
| infeasible | Constraints are contradictory — no solution exists |
| Other non-optimal | Generic solver failure |