Reverse hot chocolate recipe for fun and profit
Reverse engineering a Piedmont hot chocolate formulation with nutritional analysis, heuristics, and constrained optimization.
Abstract
Given an ingredient list, nutrition facts, and two known ingredient percentages, can the missing formulation be estimated? This case study compares analytical reasoning with constrained least-squares optimization.
Combining my passions for tech and food science, I set out to reverse-engineer one of the most delicious hot chocolates I’ve ever tasted.
I’ve tasted excellent hot chocolate around the globe, but it’s undoubtedly in Italy, specifically in Turin in the Piedmont region, where I’ve encountered the most remarkable formulations. The Piedmont hazelnut, Tonda Gentile delle Langhe, recognized as the world’s finest, plays a crucial role in their unique hot chocolate preparation.
In my (past) daily work as a “food creative”, I’m tasked with creating optimal formulations. When challenged to reproduce the famous Piedmontese hot chocolate, I needed to approach this systematically. This article outlines two complementary methods for reverse engineering this recipe: computational optimization and analytical heuristics.
What information do we have?
Understanding the target
My research revealed that one of Turin’s famous establishments shared both their ingredient list and nutritional information online, providing an excellent starting point:
Ingredients
- High-quality whole milk
- 20% hazelnut paste
- Cane sugar
- Cocoa powder
- 2.2% dark chocolate, with at least 70% cocoa
Nutrition facts
| Nutrient | Label value |
|---|---|
| Fat | 20.4% |
| Carbohydrates | 21.9% |
| Sugars | 20.04% |
| Protein | 5.6% |
| Salt | 0.02% |
As expected, the label offers only partial quantitative information: enough to guide reconstruction, but not a full recipe.
The task now is to determine the exact proportions of the remaining ingredients.
Characterizing the components
The first step involves collecting nutritional data for each ingredient:
| Ingredient | Fat | Carbohydrates | Sugars | Protein |
|---|---|---|---|---|
| Whole milk | 3.7% | 4.7% | 0% | 3.2% |
| Hazelnut paste | 70% | 5.5% | 5.03% | 16.4% |
| Cane sugar | 0% | 100% | 100% | 0% |
| Cocoa powder | 33% | 12% | 1.3% | 23% |
| Dark chocolate (70%) | 38.5% | 39.5% | 30% | 10% |
These values connect ingredient proportions to the final nutrition profile. Each ingredient contributes fat, carbohydrates, sugars, and protein in proportion to the amount used.
From a recipe to a mathematical model
Represent the recipe as a vector of ingredient proportions:
Represent ingredient nutritional compositions as a matrix (A). Each column corresponds to an ingredient; each row corresponds to fat, carbohydrates, sugars, or protein. The nutrition label becomes target vector (b).
The forward calculation is:
If all measurements were exact and perfectly compatible, we could seek an exact equality. In practice, ingredient values are approximate, labels are rounded, manufacturers work within tolerances, and more than one formulation can produce nearly identical nutrition facts.
This motivates a least-squares objective:
The expression measures total squared difference between calculated nutrition and target nutrition. Smaller values indicate closer matches.
Constraints
Nutritional agreement alone is not sufficient. Candidate recipes must also remain valid formulations.
The percentages must sum to 100%:
No ingredient percentage can be negative:
Two quantities remain fixed by the published ingredient list:
Explore formulation constraints
Fixed: hazelnut 20% + dark chocolate 2.2% · Total: 100.00%
How optimization searches for a formulation
The optimizer explores candidate values for milk, sugar, and cocoa powder. Every candidate recipe produces a calculated nutrition profile. That profile is compared with target label, producing an error. The optimizer progressively adjusts candidate proportions to reduce error while respecting mass balance, fixed quantities, and non-negativity.
How error decreases during optimization
This process returns one formulation that best satisfies selected objective and constraints. It does not prove that formulation is unique.
Method 2: analytical heuristic approach
The second method uses manual calculations and nutritional reasoning to derive the recipe proportions. This approach is useful for understanding the underlying nutritional relationships and can be performed without sophisticated computational tools.
1. Start with known quantities
- Hazelnut paste: 20%
- Dark chocolate: 2.2%
2. Estimate sugar and cocoa contributions
Carbohydrates from hazelnut paste:
Carbohydrates from dark chocolate:
Total known carbohydrate contribution:
Remaining carbohydrates to allocate:
Based on culinary knowledge and expected character of hot chocolate, original heuristic divides this remainder between cane sugar and cocoa powder in a 75:25 ratio:
This heuristic balances sweetness and chocolate intensity while providing a direct analytical estimate.
3. Calculate milk proportion
Milk fills remaining mass:
The resulting heuristic formulation is:
- Milk: 57.87%
- Hazelnut paste: 20.00%
- Cane sugar: 14.95%
- Cocoa powder: 4.98%
- Dark chocolate: 2.20%
4. Verify nutritional values
To verify our calculations, we can check if the resulting nutritional profile matches our target:
Fat content:
Carbohydrate content:
Sugar content:
Protein content:
Our calculated values match the target nutritional profile reasonably well, with some minor variations that could be attributed to ingredient processing or differences in measurement methods.
Python implementation
Method 1: computational optimization with SciPy
The first approach uses numerical optimization to estimate the ingredient proportions that best fit the target nutrition profile. Unlike solving linear equations for an exact solution, optimization aims to minimize the error between a model and target under given constraints.
Here’s the Python implementation using SciPy’s optimization module:
import numpy as np
from scipy.optimize import minimize
# Nutritional profiles [fat, carbs, sugars, protein]
milk = [3.7, 4.7, 0.0, 3.2]
hazelnut = [70.0, 5.5, 5.03, 16.4]
sugar = [0.0, 100.0, 100.0, 0.0]
cocoa = [33.0, 12.0, 1.3, 23.0]
chocolate = [38.5, 39.5, 30.0, 10.0]
# Target nutritional profile
target = [20.4, 21.9, 20.04, 5.6]
# Known constraints
hazelnut_pct = 20.0
chocolate_pct = 2.2
# Objective function: minimize squared error between calculated and target nutrition
def objective(x):
milk_pct = x[0]
sugar_pct = x[1]
cocoa_pct = x[2]
result = [0, 0, 0, 0]
for i in range(4):
result[i] = (milk_pct * milk[i] +
hazelnut_pct * hazelnut[i] +
sugar_pct * sugar[i] +
cocoa_pct * cocoa[i] +
chocolate_pct * chocolate[i]) / 100.0
return sum((result[i] - target[i])**2 for i in range(4))
# Constraint: percentages must sum to 100%
def constraint(x):
return x[0] + x[1] + x[2] + hazelnut_pct + chocolate_pct - 100.0
# Initial guess and optimization parameters
x0 = [60.0, 15.0, 2.8]
cons = {'type': 'eq', 'fun': constraint}
bounds = [(0, 100), (0, 100), (0, 100)]
# Run optimization
result = minimize(objective, x0, method='SLSQP', bounds=bounds, constraints=cons)
# Display results
if result.success:
milk_pct = result.x[0]
sugar_pct = result.x[1]
cocoa_pct = result.x[2]
print(f"Milk: {milk_pct:.2f}%")
print(f"Hazelnut paste: {hazelnut_pct:.2f}%")
print(f"Cane sugar: {sugar_pct:.2f}%")
print(f"Cocoa powder: {cocoa_pct:.2f}%")
print(f"Dark chocolate: {chocolate_pct:.2f}%")
Running this optimization yields:
- Milk: 52.76%
- Hazelnut paste: 20.00%
- Cane sugar: 17.43%
- Cocoa powder: 7.61%
- Dark chocolate: 2.20%
How the optimization works
To better understand the computational approach, let’s break down the process:
- Define the problem. We’re looking for the percentages of milk, sugar, and cocoa powder that, together with our known percentages of hazelnut paste and dark chocolate, will produce a beverage with our target nutritional profile.
- Set up variables. We define variables for the unknown percentages.
- Create an objective function. This function takes our current guess for the percentages, calculates what the nutritional content would be, computes the squared difference between calculated and target values, and returns the sum of these squared differences.
- Add constraints. We specify that the percentages must sum to 100% and cannot be negative.
- Solve. The optimizer systematically adjusts the percentages, trying to minimize the objective function while respecting the constraints.
- Read the result. When the optimizer converges, it returns the percentages that best match our target nutritional profile.
The magic of this approach is that it can handle complex nutritional interactions and multiple constraints simultaneously, finding the optimal solution even when an exact match might not be possible.
Mathematical foundations
The optimization approach can be formalized mathematically as finding the values that minimize the sum of squared differences between our calculated nutritional values and the target values.
In plain text:
- We want to minimize: Sum of (calculated nutrient minus target nutrient)²
- Subject to: All percentages sum to 100%
- With fixed values: Hazelnut paste = 20%, Dark chocolate = 2.2%
- And constraints: All percentages must be non-negative
The beauty of this approach is that it handles cases where a perfect solution might not exist, finding instead the closest possible match to our target nutritional profile.
Comparing two methods
Interestingly, the two methods yield somewhat different results:
| Ingredient | Optimization | Heuristic |
|---|---|---|
| Whole milk | 52.76% | 57.87% |
| Hazelnut paste | 20.00% | 20.00% |
| Cane sugar | 17.43% | 14.95% |
| Cocoa powder | 7.61% | 4.98% |
| Dark chocolate | 2.20% | 2.20% |
The optimization method assigns a higher percentage to sugar and cocoa powder, while reducing the milk content. This difference illustrates an important point: when dealing with complex nutritional targets, multiple formulations may satisfy the constraints to varying degrees.
The optimization approach prioritizes the exact matching of nutritional targets, while the heuristic approach incorporates culinary intuition about typical ingredient ratios. Both approaches provide valid starting points for recipe development.
Practical implementation
Let’s implement both recipes for a 200g batch:
Optimization-Based Recipe:
- Whole milk: 105.5g
- Hazelnut paste: 40.0g
- Cane sugar: 34.9g
- Cocoa powder: 15.2g
- Dark chocolate: 4.4g
Heuristic-Based Recipe:
- Whole milk: 115.7g
- Hazelnut paste: 40.0g
- Cane sugar: 27.9g
- Cocoa powder: 12.0g
- Dark chocolate: 4.4g
Compare reconstructed formulations
Why several formulations can fit same label
This reconstruction is an inverse problem. Forward problem starts from recipe and computes nutrition. Inverse problem starts from nutrition and attempts to recover recipe.
Inverse direction is less certain. Ingredient data are approximate, label values are rounded, and different combinations can produce very similar totals. Nutritional constraints can therefore define several plausible formulations rather than one unique recipe.
Heuristic and numerical results illustrate this directly. Their compositions differ, yet both were developed from same ingredient list, known percentages, and nutritional targets. Optimization prioritizes numerical agreement. Heuristic reasoning adds assumptions about plausible culinary ratios.
A future feasible-space visualization could show all ingredient combinations that remain close to nutrition label. A second animation could display nutrition values updating while proportions change, making non-uniqueness visible rather than merely stated.
Preparation method
- Heat the milk gently until hot but not boiling.
- Incorporate the hazelnut paste, stirring continuously until fully integrated.
- Add the sugar, cocoa powder, and dark chocolate while maintaining constant agitation.
- Continue stirring until all ingredients are fully dissolved and the mixture is homogeneous.
- Serve immediately.
Conclusion
This case study demonstrates how mathematical modeling and food science can be combined to reverse engineer complex recipes. Both the computational optimization and analytical heuristic approaches yield viable recipes, though with interesting differences in composition.
The power of this dual approach lies in its complementary nature: optimization provides precision and handles complex constraints efficiently, while the heuristic method offers intuitive understanding and can be performed with basic calculations.